はじめに
2020/10/17 に、YouTube Live で「つまずき Ansible 【Part19】モジュールのコードをデバッグしたい」という配信をしました。
実際に作業しながらエラーと戦って進めるシリーズです。
今回は、ANSIBLE_KEEP_REMOTE_FILES
の設定を有効にして Playbook を実行して残った Python のコードをもとにしてデバッグをしてみます。
やったことや、わかったことをふりかえります。
- 環境
- ansible 2.10
- python 3.6.8
動画
チャプターもつけてます。
■ やったこと
ANSIBLE_KEEP_REMOTE_FILES
を有効にして実行
利用した Playbook は以下のとおりです。
NetBox にサイトというオブジェクトを作成する API を uri
モジュールで叩きます。
本当は専用のモジュールがあるのですが、ここではよりメジャーなモジュールとして uri
を取り上げました。
--- - hosts: netbox01 gather_facts: false connection: local vars: url: http://192.168.1.145:32769/api/dcim/sites/ tasks: - name: uri: url: "{{ url }}" method: POST headers: Authorization: "Token {{ token }}" # body: "{{ lookup('file', 'body.json') }}" body: name: test1 slug: test1 status_code: - 200 - 201 body_format: json
実行コマンド
ANSIBLE_KEEP_REMOTE_FILES=1 ansible-playbook -i inventory.ini uri.yml
この場合は、一回限りの環境変数としての指定指定です。
他の設定と同様に、ansible.cfg
にも定義できます。詳細は 公式ドキュメントを参照してください。
残ったファイルの確認
先程のように ANSIBLE_KEEP_REMOTE_FILES
を有効にして Playbook を実行すると、ターゲットノードの ~/.ansible/tmp
ディレクトリ配下の、実処理を実行するための Python ファイルが残されます。
通常であれば、このファイルは実行後に削除されるのですが、それを残すのが ANSIBLE_KEEP_REMOTE_FILES
の設定ということです。
今回は、connection: local
なので、Ansible コントロールノード自身の ~/.ansible/tmp
ディレクトリ配下に残ります。
(a2100) [root@centos7 tmp]# ls -al 合計 0 drwx------. 3 root root 66 10月 17 20:10 . drwx------. 5 root root 66 10月 7 19:48 .. (a2100) [root@centos7 ansible-tmp-1602933024.2020504-20448-161437222207769]# ls -al 合計 156 drwx------. 3 root root 47 10月 17 20:14 . drwx------. 3 root root 66 10月 17 20:10 .. -rwxr--r--. 1 root root 157816 10月 17 20:10 AnsiballZ_uri.py
AnsiballZ_モジュール名.py
というファイル名で残りリます。
残ったファイルを普通に実行
AnsiballZ_uri.py
は、Ansible は不要で、通情の Python ファイルとして実行できます。
(a2100) [root@centos7 ansible-tmp-1602933024.2020504-20448-161437222207769]# python AnsiballZ_uri.py {"redirected": false, "url": "http://192.168.1.145:32769/api/dcim/sites/", "status": 201, "server": "nginx", "date": "Sat, 17 Oct 2020 11:13:19 GMT", "content_type": "application/json", "content_length": "467", "connection": "close", "location": "http://192.168.1.145:32769/api/dcim/sites/9/", "vary": "Accept, Cookie, Origin", "allow": "GET, POST, HEAD, OPTIONS", "api_version": "2.9", "x_content_type_options": "nosniff", "referrer_policy": "same-origin", "x_frame_options": "SAMEORIGIN", "p3p": "CP=\"ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV\"", "cookies_string": "", "cookies": {}, "msg": "OK (467 bytes)", "elapsed": 0, "changed": false, "json": {"id": 9, "url": "http://192.168.1.145:32769/api/dcim/sites/9/", "name": "test1", "slug": "test1", "status": {"value": "active", "label": "Active"}, "region": null, "tenant": null, "facility": "", "asn": null, "time_zone": null, "description": "", "physical_address": "", "shipping_address": "", "latitude": null, "longitude": null, "contact_name": "", "contact_phone": "", "contact_email": "", "comments": "", "tags": [], "custom_fields": {}, "created": "2020-10-17", "last_updated": "2020-10-17T11:13:19.879794Z"}, "invocation": {"module_args": {"url": "http://192.168.1.145:32769/api/dcim/sites/", "method": "POST", "headers": {"Authorization": "Token 0123456789abcdef0123456789abcdef01234567", "Content-Type": "application/json"}, "body": {"name": "test1", "slug": "test1"}, "status_code": [200, 201], "body_format": "json", "force": false, "http_agent": "ansible-httpget", "use_proxy": true, "validate_certs": true, "force_basic_auth": false, "return_content": false, "follow_redirects": "safe", "timeout": 30, "remote_src": false, "unsafe_writes": false, "url_username": null, "url_password": null, "client_cert": null, "client_key": null, "dest": null, "src": null, "creates": null, "removes": null, "unix_socket": null, "mode": null, "owner": null, "group": null, "seuser": null, "serole": null, "selevel": null, "setype": null, "attributes": null}}}
ただし、中身の大半がエンコードされているためデバッグがまともにできません。
AnsiballZ_uri.py
を普通に実行するのはた、ただの Playbook のタスクの再現用という感じです。
explode
て展開する
AnsiballZ_uri.py
内の、エンコードされた部分を展開するのが explode
オプションです。
(a2100) [root@centos7 ansible-tmp-1602933024.2020504-20448-161437222207769]# python AnsiballZ_uri.py explode Module expanded into: /root/.ansible/tmp/ansible-tmp-1602933024.2020504-20448-161437222207769/debug_dir
debug_dir
の中に、タスクのオプションを JSON 化した arg
や、モジュールのコードの本体や、それが利用するファイル一式が含まれます。
(a2100) [root@centos7 ansible-tmp-1602933024.2020504-20448-161437222207769]# tree debug_dir/ debug_dir/ ├── ansible │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-36.pyc │ ├── module_utils │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-36.pyc │ │ │ ├── _text.cpython-36.pyc │ │ │ ├── basic.cpython-36.pyc │ │ │ ├── pycompat24.cpython-36.pyc │ │ │ └── urls.cpython-36.pyc │ │ ├── _text.py │ │ ├── basic.py │ │ ├── common │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-36.pyc │ │ │ │ ├── _collections_compat.cpython-36.pyc │ │ │ │ ├── _json_compat.cpython-36.pyc │ │ │ │ ├── _utils.cpython-36.pyc │ │ │ │ ├── collections.cpython-36.pyc │ │ │ │ ├── file.cpython-36.pyc │ │ │ │ ├── parameters.cpython-36.pyc │ │ │ │ ├── process.cpython-36.pyc │ │ │ │ ├── sys_info.cpython-36.pyc │ │ │ │ ├── validation.cpython-36.pyc │ │ │ │ └── warnings.cpython-36.pyc │ │ │ ├── _collections_compat.py │ │ │ ├── _json_compat.py │ │ │ ├── _utils.py │ │ │ ├── collections.py │ │ │ ├── file.py │ │ │ ├── parameters.py │ │ │ ├── process.py │ │ │ ├── sys_info.py │ │ │ ├── text │ │ │ │ ├── __init__.py │ │ │ │ ├── __pycache__ │ │ │ │ │ ├── __init__.cpython-36.pyc │ │ │ │ │ ├── converters.cpython-36.pyc │ │ │ │ │ └── formatters.cpython-36.pyc │ │ │ │ ├── converters.py │ │ │ │ └── formatters.py │ │ │ ├── validation.py │ │ │ └── warnings.py │ │ ├── compat │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-36.pyc │ │ │ │ └── selectors.cpython-36.pyc │ │ │ ├── _selectors2.py │ │ │ └── selectors.py │ │ ├── distro │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-36.pyc │ │ │ │ └── _distro.cpython-36.pyc │ │ │ └── _distro.py │ │ ├── parsing │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-36.pyc │ │ │ │ └── convert_bool.cpython-36.pyc │ │ │ └── convert_bool.py │ │ ├── pycompat24.py │ │ ├── six │ │ │ ├── __init__.py │ │ │ └── __pycache__ │ │ │ └── __init__.cpython-36.pyc │ │ └── urls.py │ └── modules │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ └── uri.cpython-36.pyc │ └── uri.py └── args 18 directories, 60 files
args
を編集して再実行する
args
でタスクのオプションにあたる値を編集できます。今回は body
の中を編集しました。
{ "ANSIBLE_MODULE_ARGS": { "url": "http://192.168.1.145:32769/api/dcim/sites/", "method": "POST", "headers": { "Authorization": "Token 0123456789abcdef0123456789abcdef01234567" }, "body": { "name": "test3", "slug": "test3" }, ...(略)... } }
編集した args
など、debug_dir
配下のファイルを利用して再実行するには、execute
オプションを付けます。
(a2100) [root@centos7 ansible-tmp-1602933024.2020504-20448-161437222207769]# python AnsiballZ_uri.py execute ...(略)...
execute
オプションを付けないと、AnsiballZ_uri.py
単体の実行となります。
ここまでが、Ansible 固有の仕様です。
VS Code でブレークポイントをはってステップ実行する
ここからは 一般的な Python のデバッグの手法と同じです。
ここでは、モジュールの本体のファイルである debug_dir/ansible/modules/uri.py
の main()
呼び出し箇所にブレークポイントをはります。
execute
オプションを付けてデバッグを開始するために、~/.vscode/launch.json
に追記します。
{ // IntelliSense を使用して利用可能な属性を学べます。 // 既存の属性の説明をホバーして表示します。 // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Python: Current File", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal" }, // 以下追加 { "name": "Python: Current File with execute", "type": "python", "request": "launch", "program": "${file}", "args": [ "execute" ], "console": "integratedTerminal" } ] }
AnsiballZ_uri.py
を開いている状態で、先程 launch.json
に定義した定義した Python: Current File with execute
でデバッグを始めます。
処理が始まり、ブレークポイントをはった debug_dir/ansible/modules/uri.py
の main()
の呼び出し箇所で止まりました。
ここからステップ実行ができます。
たとえば、モジュールのオプションで与えた値は、モジュールのコード内でこのように変数に格納されます。
途中で変数の値を変更して、処理を続けることもできます。
VS Code によるデバッグは、公式ドキュメント などを参照にしてください。
モジュールの処理を細かく追いいたいときに便利だと思います。
Part20 にむけて
以下のネタを検討中です。気が向いたものをやります。