はじめに
Ansible Tower / AWX では、Playbook の実行ログがサーバー内に自動的に保存されます。 いつだれが実行してどうなったかを追跡するのに便利です。
そのログを、ファイルとしてダウンロードすることもできます。
数個ログだったらよいのですが、ワークフロー内にいくつもジョブがあり、ひとつひとつ実行結果画面を開いてダウンロード、開いてダウンロード、を繰り返すのは少しつらいかもしれません。
この記事では、ワークフロー実行ID を指定すると、そのワークフロー内の一連のジョブの実行ログをファイルとして保存する Playbook を紹介します。
ただし、試作レベルです。たとえば、ワークフローからワークフローを呼ぶケースには対応していません。
- 検証環境
- AWX 11.0
- Ansible 2.9.6
Playbook
さっそくですが、Playbook は以下のとおりです。
実現できそうな tower モジュールがなかったので、uri
モジュールを利用します。
--- - hosts: tower gather_facts: false connection: local vars: wfjid: 600 # 対象のワークフロージョブID tower_username: testuser tower_password: testpass # 必要に応じて暗号化などする validate_certs: no tasks: # (1) ワークフロー内のノードを取得 - name: get workflow nodes uri: url: "https://{{ ansible_host }}/api/v2/workflow_jobs/{{ wfjid }}/workflow_nodes/" method: GET url_username: "{{ tower_username }}" url_password: "{{ tower_password }}" force_basic_auth: true validate_certs: false register: res_wf_nodes # (2) ワークフロージョブ内の各ジョブの実行ログを取得 - name: get job stdout uri: url: "https://{{ ansible_host }}/api/v2/jobs/{{ item.job }}/stdout/?format=txt" method: GET return_content: true url_username: "{{ tower_username }}" url_password: "{{ tower_password }}" force_basic_auth: true validate_certs: false register: res_job_stdouts loop: "{{ res_wf_nodes.json.results }}" loop_control: label: "{{ item.job }} - {{ item.summary_fields.unified_job_template.name }}" when: - item.job != None # 実行されなかったジョブはスキップ - item.summary_fields.job.type == 'job' # 他の workflow_approval などはスキップ # (3) ワークフロージョブIDからワークフロー名を取得(ディレクトリ名に利用) - name: get workflow name by id uri: url: "https://{{ ansible_host }}/api/v2/workflow_jobs/{{ wfjid }}/" method: GET url_username: "{{ tower_username }}" url_password: "{{ tower_password }}" force_basic_auth: true validate_certs: false register: res_wfj # (4) ワークフロージョブID ごとのログファイル保存用ディレクトリを作成 - name: make directory file: path: "log/wf_{{ wfjid }}_{{ res_wfj.json.name }}" state: directory register: res_dir # (5) ワークフロージョブ内の各ジョブの実行ログをファイルとして保存 - name: save job log to file copy: content: "{{ item.content }}" dest: "{{ res_dir.path }}/job_{{ item.item.job }}_{{ jt_name }}_{{ summary.job.status }}.txt" # vars: summary: "{{ item.item.summary_fields }}" jt_name: "{{ summary.unified_job_template.name }}" loop: "{{ res_job_stdouts.results }}" loop_control: label: "{{ item.item.job }} - {{ jt_name }}" when: - item.item.job != None # 実行されなかったジョブはスキップ - item.item.summary_fields.job.type == 'job' # 他の workflow_approval などはスキップ
解説
Playbook 内の各タスクを簡単に解説します。
vars 部分
wfjid: 600
で対象のワークフロージョブIDを指定しています。
今回は埋め込んでしまっていますが、可変にするために vars_prompt
や、extra_vars
を利用するといいかもしれません。
(1) ワークフロー内のノードを取得
uri
モジュールで、指定されたワークフロージョブ内のノードを取得します。
- APIリファレンス
GET /api/v2/workflow_jobs/{id}/workflow_nodes/ List Workflow Job Nodes for a Workflow Job
(2) ワークフロー内の各ジョブの実行ログを取得
各ジョブの実行ログを取得します。GUI で実行ログをダウンロードする場合は format=txt_download
ですが、ここでは format=txt
とします。
- APIリファレンス
GET /api/v2/jobs/{id}/stdout/ Retrieve Job Stdout
(3) ワークフロージョブIDからワークフロー名を取得(ディレクトリ名に利用)
あとでワークフロージョブIDごとにディレクトリを作成しますが、その際にワークフロー名もつけたかったので、このタイミングで取得します。
- APIリファレンス
GET /api/v2/workflow_jobs/{id}/ Retrieve a Workflow Job
(4) ワークフロージョブID ごとのログファイル保存用ディレクトリを作成
わかりやすいように、log/ワークフローID_ワークフロー名
でディレクトリを作成します。
(5) ワークフロージョブ内の各ジョブの実行ログをファイルとして保存
実際に実行ログをファイルとして保存します。
GUI で実行ログをダウンロードすると、job_ジョブID.txt
というファイル名になりますが、ここでは、job_ジョブID_ジョブ名_ステータス.txt
とします。
ステータスには、success
や canceled
など、そのジョブの状態が入ります。
■ 実行
今回対象のワークフロージョブは以下のものです。中央の jt_show9
は jt_show1
が失敗したときのみ実行するジョブなのでスキップされてます。
Playbook を実行します。
$ ansible-playbook -i inventory.ini log_downlod.yml PLAY [tower] ****************************************************************************************************** TASK [get workflow nodes] ***************************************************************************************** ok: [awx1] TASK [get job stdout] ********************************************************************************************* ok: [awx1] => (item=601 - jt_show1) ok: [awx1] => (item=606 - jt_show4) ok: [awx1] => (item=603 - jt_show2) skipping: [awx1] => (item= - jt_show9) ok: [awx1] => (item=604 - jt_show3) TASK [get workflow name by id] ************************************************************************************ ok: [awx1] TASK [make directory] ********************************************************************************************* changed: [awx1] TASK [save job log to file] *************************************************************************************** changed: [awx1] => (item=601 - jt_show1) changed: [awx1] => (item=606 - jt_show4) changed: [awx1] => (item=603 - jt_show2) skipping: [awx1] => (item= - jt_show9) changed: [awx1] => (item=604 - jt_show3) PLAY RECAP ******************************************************************************************************** awx1 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
確認
以下のように実行ログが保存されます。
$ tree log log └── wf_600_wf_show ├── job_601_jt_show1_successful.txt ├── job_603_jt_show2_successful.txt ├── job_604_jt_show3_successful.txt └── job_606_jt_show4_successful.txt
結果から以下のことが分かります。
- 今回の対象のワークフロージョブID
600
のワークフロー名はwf_show
- ワークフロー内には以下のジョブが含まれ、すべて成功した
jt_show9
のジョブは実行されずにスキップされた- 条件的に実行されないノードのため
代表して一つ、ログの中身を確認します。見慣れた Playbook 実行ログです。
$ cat log/wf_600_wf_show/job_601_jt_show1_successful.txt SSH ********: PLAY [ios] ********************************************************************* TASK [show verion] ************************************************************* ok: [iosao_latest] ok: [iosao] ok: [iosao_latest] => { "msg": { "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "changed": false, "failed": false, "stdout": [ "Cisco IOS XE Software, Version 16.11.01a\\nCisco IOS Software [Gibraltar], ...(略)..." ], "stdout_lines": [ [ "Cisco IOS XE Software, Version 16.11.01a", ...(略)... "", "Configuration register is 0x2102" ] ] } } ok: [iosao] => { ...(略)... } } iosao : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 iosao_latest : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
■ おわりに
少々手間ではありますが、専用のモジュールがなくても、API 経由でやりたいことができました。