4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ansibleのparse_cli_textfsmフィルターを使ったネットワークテストの自動化

Posted at

はじめに

Ansibleのparse_cli_textfsmフィルター、set_factモジュール、assertモジュールを組み合わせると、CLIコマンド出力結果を元に様々なネットワークテストができます。
今回は、Cisco IOS RouterのEIGRPネイバーテストをAnsibleで自動化してみました。

概要

parse_cli_textfsmフィルターとは

  • AnsibleのネットワークCLIフィルタープラグインの1つ。
  • TextFSMライブラリを使ってCLIコマンド出力結果をパースする。
  • 既存のTextFSMライブラリとして、networktocodeのGitHubリポジトリで公開されているNTC-Templatesがあります。Cisco IOSの場合、サポートしているshowコマンドは2018年11月時点で53種類。(ちなみにshow ip eigrp neighborsは私がプルリクエストを出したものです。)
  • インストール方法は、てくなべ (tekunabe)さんのブログが参考になると思います。

set_factモジュールとは

assertモジュールとは

  • True/False判定のためのモジュール。例えば、ある2つの変数が同じ値か、部分集合の関係になっているか、特定の値以上かを判定可能。詳細は公式ドキュメントを参照願います。

ラボ環境

以下の仮想ルータ(Cisco CSR1000V)を同一セグメントに接続し、互いにEIGRPネイバーを形成するようにしました。

機種 IPアドレス 備考
test  192.168.100.200
test2  192.168.100.201
csr1  192.168.100.202 Ansibleによるテスト対象

showコマンド出力結果は以下の通りです。

csr1#show ip eigrp neighbors
EIGRP-IPv4 Neighbors for AS(10)
H   Address                 Interface              Hold Uptime   SRTT   RTO  Q  Seq
                                                   (sec)         (ms)       Cnt Num
1   192.168.100.201         Gi1                      12 01:07:01    6   100  0  19
0   192.168.100.200         Gi1                      12 01:07:01   32   192  0  20

Playbook

例として、EIGRPの以下2点を確認するPlaybookを作成しました。
 ①想定のEIGRPネイバーが存在するか
 ②全ネイバーのUPTIMEが1分より大きいか
※変数はPlaybook内に記載しましたが、メンテナンスの観点からInventoryやVariableファイルに書いた方がいいと思います。

playbook2_eigrp.yml
---

- hosts: cisco
  gather_facts: no
  connection: local

  tasks:
    - name: run show command on remote devices
      ios_command:
        commands:
          - show ip eigrp neighbors   # show ip eigrp neighborコマンドを出力。
        provider: "{{ cli }}"
      register: result

    - name: set the actual eigrp neighbor value (all)
      set_fact:
        actual_eigrp_nei: "{{ result.stdout[0] | parse_cli_textfsm('./ntc-templates-master/templates/cisco_ios_show_ip_eigrp_neighbors.template') }}"
        # show ip eigrp neighbors用のTemplateファイルを使って出力結果をパースし、
        # リスト/辞書データとしてactual_eigrp_neiに格納。

    - name: set the actual eigrp neighbor value (address)
      set_fact:
        actual_eigrp_nei_address: "{{ actual_eigrp_nei_address + [item.ADDRESS] }}"
      loop: "{{ actual_eigrp_nei|flatten(levels=1) }}"
      # loopでactual_eigrp_nei内のリストデータを1つずつ読み込み、key=ADDRESSに格納されている
      # IPアドレスを、actual_eigrp_nei_addressにAppend。

    - name: debug
      debug:
        msg:
          - "{{ actual_eigrp_nei }}"   # actual_eigrp_neiに格納されているデータを確認
          - "{{ actual_eigrp_nei_address }}"   # actual_eigrp_nei_addressに格納されているデータを確認

    - name: assert that expected neighbors are in actual neighbors
      assert:
        that:
          - "item in actual_eigrp_nei_address"
      loop: "{{ expected_eigrp_nei_address|flatten(levels=1) }}"
      # loopでexpected_eigrp_nei_addressのリストを1つずつ読み込み、その値がactual_eigrp_nei_addressに含まれるか確認。

    - name: assert that all neighbors are up for more than 1 minute
      assert:
        that:
          - "expected_eigrp_nei_uptime < item.UPTIME"
      loop: "{{ actual_eigrp_nei|flatten(levels=1) }}"
      # loopでactual_eigrp_neiのリストを1つずつ読み込み、key=UPTIMEの値が想定時間(1分)より大きいか確認。

  vars:
    cli:
      host: "{{ inventory_hostname }}"
      username: "{{ ansible_username }}"
      password: "{{ ansible_password }}"
      authorize: true
      auth_pass: "{{ enable_secret }}"
    expected_eigrp_nei_address: ["192.168.100.200", "192.168.100.201"]   # 想定のEIGRPネイバーアドレス
    # リスト形式の空データを作成。loop処理でリスト内にデータをAppendするための元データとして必要。
    actual_eigrp_nei_address: []
    expected_eigrp_nei_uptime: 00:01:00   # 想定のEIGRP Uptime

#実行結果(成功例)

①②ともに成功し、いずれも"All assertions passed"となっています。

[<ユーザ名>@localhost ansible]$ ansible-playbook -i inventory playbook2_eigrp.yml

PLAY [cisco] ********************************************************************************

TASK [run show command on remote devices] ***************************************************
ok: [192.168.100.202]

TASK [set the actual eigrp neighbor value (all)] ********************************************
ok: [192.168.100.202]

TASK [set the actual eigrp neighbor value (address)] ****************************************
ok: [192.168.100.202] => (item={'Q_CNT': u'0', 'UPTIME': u'01:43:53', 'SEQ_NUM': u'19', 'RTO': u'100', 'SRTT': u'6', 'AS': u'10', 'ADDRESS': u'192.168.100.201', 'INTERFACE': u'Gi1', 'HOLD': u'13'})
ok: [192.168.100.202] => (item={'Q_CNT': u'0', 'UPTIME': u'01:43:53', 'SEQ_NUM': u'20', 'RTO': u'192', 'SRTT': u'32', 'AS': u'10', 'ADDRESS': u'192.168.100.200', 'INTERFACE': u'Gi1', 'HOLD': u'12'})

TASK [debug] ********************************************************************************
ok: [192.168.100.202] => {
    "msg": [
        [
            {
                "ADDRESS": "192.168.100.201", 
                "AS": "10", 
                "HOLD": "13", 
                "INTERFACE": "Gi1", 
                "Q_CNT": "0", 
                "RTO": "100", 
                "SEQ_NUM": "19", 
                "SRTT": "6", 
                "UPTIME": "01:43:53"
            }, 
            {
                "ADDRESS": "192.168.100.200", 
                "AS": "10", 
                "HOLD": "12", 
                "INTERFACE": "Gi1", 
                "Q_CNT": "0", 
                "RTO": "192", 
                "SEQ_NUM": "20", 
                "SRTT": "32", 
                "UPTIME": "01:43:53"
            }
        ], 
        [
            "192.168.100.201", 
            "192.168.100.200"
        ]
    ]
}

TASK [assert that expected neighbors are in actual neighbors] *******************************
ok: [192.168.100.202] => (item=192.168.100.200) => {
    "changed": false, 
    "item": "192.168.100.200", 
    "msg": "All assertions passed"   # 想定の"192.168.100.200"が実際の出力結果に含まれており、"All assertions passed"。
}
ok: [192.168.100.202] => (item=192.168.100.201) => {
    "changed": false, 
    "item": "192.168.100.201", 
    "msg": "All assertions passed"   # 想定の"192.168.100.201"が実際の出力結果に含まれており、"All assertions passed"。
}

TASK [assert that all neighbors are up for more than 1 minute] ******************************
ok: [192.168.100.202] => (item={'Q_CNT': u'0', 'UPTIME': u'01:43:53', 'SEQ_NUM': u'19', 'RTO': u'100', 'SRTT': u'6', 'AS': u'10', 'ADDRESS': u'192.168.100.201', 'INTERFACE': u'Gi1', 'HOLD': u'13'}) => {
    "changed": false, 
    "item": {
        "ADDRESS": "192.168.100.201", 
        "AS": "10", 
        "HOLD": "13", 
        "INTERFACE": "Gi1", 
        "Q_CNT": "0", 
        "RTO": "100", 
        "SEQ_NUM": "19", 
        "SRTT": "6", 
        "UPTIME": "01:43:53"
    }, 
    "msg": "All assertions passed"   # "192.168.100.201"のUPTIMEは1分以上であり、"All assertions passed"。
}
ok: [192.168.100.202] => (item={'Q_CNT': u'0', 'UPTIME': u'01:43:53', 'SEQ_NUM': u'20', 'RTO': u'192', 'SRTT': u'32', 'AS': u'10', 'ADDRESS': u'192.168.100.200', 'INTERFACE': u'Gi1', 'HOLD': u'12'}) => {
    "changed": false, 
    "item": {
        "ADDRESS": "192.168.100.200", 
        "AS": "10", 
        "HOLD": "12", 
        "INTERFACE": "Gi1", 
        "Q_CNT": "0", 
        "RTO": "192", 
        "SEQ_NUM": "20", 
        "SRTT": "32", 
        "UPTIME": "01:43:53"
    }, 
    "msg": "All assertions passed"   # "192.168.100.200"のUPTIMEは1分以上であり、"All assertions passed"。
}

PLAY RECAP **********************************************************************************
192.168.100.202            : ok=6    changed=0    unreachable=0    failed=0   

#実行結果(失敗例)

EIGRPネイバーをリセットし、UPTIME1分前後の結果を確認しました。
期待通り、UPTIMEが1分ちょうどの時は"Assertion failed"、1分を超えた時は"All assertions passed"となっていることが分かります。

[<ユーザ名>@localhost ansible]$ ansible-playbook -i inventory playbook2_eigrp.yml -vvv
---<中略>---
TASK [assert that all neighbors are up for more than 1 minute] ******************************
task path: /home/<ユーザ名>/ansible/playbook2_eigrp.yml:58
failed: [192.168.100.202] (item={'Q_CNT': u'0', 'UPTIME': u'00:01:00', 'SEQ_NUM': u'19', 'RTO': u'100', 'SRTT': u'6', 'AS': u'10', 'ADDRESS': u'192.168.100.201', 'INTERFACE': u'Gi1', 'HOLD': u'14'}) => {
    "assertion": "expected_eigrp_nei_uptime < item.UPTIME", 
    "changed": false, 
    "evaluated_to": false, 
    "item": {
        "ADDRESS": "192.168.100.201", 
        "AS": "10", 
        "HOLD": "14", 
        "INTERFACE": "Gi1", 
        "Q_CNT": "0", 
        "RTO": "100", 
        "SEQ_NUM": "19", 
        "SRTT": "6", 
        "UPTIME": "00:01:00"
    }, 
    "msg": "Assertion failed"   # UPTIMEは1分で、想定の"1分より大きい"を満たしておらず"Assertion failed"。
}
ok: [192.168.100.202] => (item={'Q_CNT': u'0', 'UPTIME': u'00:01:01', 'SEQ_NUM': u'20', 'RTO': u'192', 'SRTT': u'32', 'AS': u'10', 'ADDRESS': u'192.168.100.200', 'INTERFACE': u'Gi1', 'HOLD': u'14'}) => {
    "changed": false, 
    "item": {
        "ADDRESS": "192.168.100.200", 
        "AS": "10", 
        "HOLD": "14", 
        "INTERFACE": "Gi1", 
        "Q_CNT": "0", 
        "RTO": "192", 
        "SEQ_NUM": "20", 
        "SRTT": "32", 
        "UPTIME": "00:01:01"
    }, 
    "msg": "All assertions passed"   # UPTIMEは1分1秒で、想定結果を満たしているため"All assertions passed"。
}
	to retry, use: --limit @/home/<ユーザ名>/ansible/playbook2_eigrp.retry

PLAY RECAP **********************************************************************************
192.168.100.202            : ok=5    changed=0    unreachable=0    failed=1 
4
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?