てくなべ (tekunabe)

ansible / network / automation / 学習の記録

[Ansible] 「つまずき Ansible 【Part25】assert で期待値確認をしたい」ふりかえり

はじめに

2021/01/09 に、YouTube Live で「つまずき Ansible 【Part25】assert で期待値確認をしたい」という配信をしました。

実際に作業しながら(ときには)エラーと戦って進めるシリーズです。

今回は、条件式を与えて期待値のチェックなどができる、assert モジュールを扱いました。

tekunabe.connpass.com

  • 環境
    • Ansible 2.9.16


動画

youtu.be

  • 0:00 イントロダクション
  • 1:50 概要
  • 6:02 簡単な使い方
  • 11:37 少し応用(ネットワーク機器の状態確認)
  • 30:20 おわりに

■ やったこと

基本的な使い方

単一の条件

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 にむけて

以下のネタを検討中です。気が向いたものをやります。

  • Ansible 2.10 関連
  • connection: local ななにか
  • ansible.cfg
  • Jinja2、フィルター
  • Windows
  • ESXi で VM作成
  • cli_parse モジュール(Part15 の続き)