はじめに
2021/01/09 に、YouTube Live で「つまずき Ansible 【Part25】assert で期待値確認をしたい」という配信をしました。
実際に作業しながら(ときには)エラーと戦って進めるシリーズです。
今回は、条件式を与えて期待値のチェックなどができる、assert
モジュールを扱いました。
- 環境
- Ansible 2.9.16
動画
■ やったこと
基本的な使い方
単一の条件
pass のパターン
- name: assert assert: that: - 1 == 1
結果
TASK [assert] ******************************************** ok: [localhost] => { "changed": false, "msg": "All assertions passed" }
fail のパターン
- name: assert assert: that: - 1 == 0
結果
TASK [assert] ******************************************* fatal: [localhost]: FAILED! => { "assertion": "1 == 0", "changed": false, "evaluated_to": false, "msg": "Assertion failed" }
AND 条件
that
に複数書くと AND。
- name: assert assert: that: - 1 == 0 - 1 == 1
結果
TASK [assert] ****************************** fatal: [localhost]: FAILED! => { "assertion": "1 == 0", "changed": false, "evaluated_to": false, "msg": "Assertion failed" }
OR 条件
or を指定する。
- name: assert assert: that: - (1 == 0) or (1 == 1)
結果
TASK [assert] ************************************** ok: [localhost] => { "changed": false, "msg": "All assertions passed" }
少し応用的な使い方
Cisco ISO のネットワーク機器から収集したファクト(インターフェースの状態やLLDPネイバー情報など)が期待したどおりかを assert でチェック。
ホスト変数
まず、期待値をホスト変数として定義。
- ホスト変数ファイル
ios01.yml
--- expected: interfaces: - name: GigabitEthernet0/1 lineprotocol: up llpd_neighbors: - local_intf: GigabitEthernet0/1 host: ios02.sakana.local port: Gi0/1
- ホスト変数ファイル
ios02.yml
--- expected: interfaces: - name: GigabitEthernet0/1 lineprotocol: up llpd_neighbors: - local_intf: GigabitEthernet0/1 host: ios01.sakana.local port: Gi0/1
Playbook
以下のように fail_msg
を指定すると失敗したときのメッセージをカスタマイズできる。実際の値は何だったのか表示するときな喉に便利。success_msg
オプションもある。
--- - hosts: ios tasks: - name: assert interface lineprotocol assert: that: - ansible_facts.net_interfaces[item.name].lineprotocol == item.lineprotocol fail_msg: "実際は{{ ansible_facts.net_interfaces[item.name].lineprotocol }}" loop: "{{ expected.interfaces }}" - name: assert lldp neighbors assert: that: # 0番目の neighbor しか見てないので荒い実装です - ansible_facts.net_neighbors[item.local_intf][0].host == item.host - ansible_facts.net_neighbors[item.local_intf][0].port == item.port fail_msg: "実際は{{ ansible_facts.net_neighbors[item.local_intf][0] }}" loop: "{{ expected.llpd_neighbors }}"
実行(期待通りだったとき)
$ ansible-playbook -i inventory.ini ios_assert.yml PLAY [ios] ************************************************************************************************************* TASK [Gathering Facts] ************************************************************************************************* [WARNING]: Ignoring timeout(10) for ios_facts [WARNING]: Ignoring timeout(10) for ios_facts [WARNING]: default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards ok: [ios01] ok: [ios02] TASK [assert interface lineprotocol] *********************************************************************************** ok: [ios01] => (item={'name': 'GigabitEthernet0/1', 'lineprotocol': 'up'}) => { "ansible_loop_var": "item", "changed": false, "item": { "lineprotocol": "up", "name": "GigabitEthernet0/1" }, "msg": "All assertions passed" } ok: [ios02] => (item={'name': 'GigabitEthernet0/1', 'lineprotocol': 'up'}) => { "ansible_loop_var": "item", "changed": false, "item": { "lineprotocol": "up", "name": "GigabitEthernet0/1" }, "msg": "All assertions passed" } TASK [assert lldp neighbors] ******************************************************************************************* ok: [ios01] => (item={'local_intf': 'GigabitEthernet0/1', 'host': 'ios02.sakana.local', 'port': 'Gi0/1'}) => { "ansible_loop_var": "item", "changed": false, "item": { "host": "ios02.sakana.local", "local_intf": "GigabitEthernet0/1", "port": "Gi0/1" }, "msg": "All assertions passed" } ok: [ios02] => (item={'local_intf': 'GigabitEthernet0/1', 'host': 'ios01.sakana.local', 'port': 'Gi0/1'}) => { "ansible_loop_var": "item", "changed": false, "item": { "host": "ios01.sakana.local", "local_intf": "GigabitEthernet0/1", "port": "Gi0/1" }, "msg": "All assertions passed" } PLAY RECAP ************************************************************************************************************* ios01 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ios02 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
実行(インターフェースの状態が期待通りでなかったときの抜粋)
failed: [ios01] (item={'name': 'GigabitEthernet0/1', 'lineprotocol': 'up'}) => { "ansible_loop_var": "item", "assertion": "ansible_facts.net_interfaces[item.name].lineprotocol == item.lineprotocol", "changed": false, "evaluated_to": false, "item": { "lineprotocol": "up", "name": "GigabitEthernet0/1" }, "msg": "実際はdown" }
おわりに
assert
モジュールは、molecule の verifier を Ansible にしたときにもよく利用することになると思います。
あるいみ単純なモジュールですが、手になじませておくと便利です。
Part26 にむけて
以下のネタを検討中です。気が向いたものをやります。