てくなべ

ネットワーク、自動化などの技術的なことを書いていきます。

Ansible で Cisco IOS からの ping 結果を CSV 出力する

これは Ansible Advent Calendar 2017 の18日目の記事です。

■ やりたいこと

Ansible で Cisco IOS から複数のIPアドレスに対してpingを実行し、その結果をCSVファイルに出力します。本記事では、利用する ios_ping モジュールの説明と、 Playbookのサンプルをご紹介します。 f:id:akira6592:20171216125403p:plain

ios_ping モジュールの説明

ios_pingCisco IOS の機器から ping を実行して、ロス率やRTT値など パース して返してくれるモジュールです。Ansible 2.4 で追加されました。(本記事は Ansible 2.4.2 を前提としています)

参考: ios_ping モジュール公式ドキュメント

手作業によるコマンド結果と、パースして取得できる情報の関係は以下の通りです。 f:id:akira6592:20171216130704p:plain

主なオプション

ios_ping モジュール固有のオプションは以下の通りです。

オプション名 必須/任意 デフォルト 説明 対応IOSコマンド書式 要特権(*1)
dest 必須 ping を実行する相手先のIPアドレス、ホスト名です。 ping [dest] 要特権
count 任意 5 ping を実行する回数です。本オプションと、IOSpingコマンドのタイムアウト(私の環境では2秒)を考慮し、ios_ping モジュールのタスクとしてのタイムアウト( timeout オプション: デフォルト10秒) を設定する必要があります。 ping [dest] repeat [count] 要特権
source 任意 送信元の指定です。 ping [dest] source [source] 要特権
state 任意 present 想定するping結果の指定です。 想定結果が成功なら present、失敗なら absent を指定します。成功、失敗の基準は公式ドキュメントに記載されていませんが、ios_ping.pyのコードを見ると「ロス率が100%未満→成功」「ロス率が100%→失敗」ということが分かります。 要特権
vrf 任意 default 利用するVRFの指定です。 ping vrf [vrf] [dest] 要特権
  • 要特権(*1) と書かれているオプションを利用するには、IOSへログインするユーザーに特権が必要です。手動実行と同じ事情です。
  • 認証系のオプションである provider 等は 他の ios_command 等のモジュールと共通です。
  • 全オプションは ios_ping モジュール公式ドキュメント を参照して下さい。

ping結果をCSV出力するためのPlaybookなど

・Playbook

Playbook 本体です。

---
- hosts: ios  # インベントリファイルにて10.0.0.1を定義済み
  gather_facts: no
  connection: local
  
  tasks:
    - name: ping
      ios_ping:
        dest: "{{ item }}"
        count: 5        # default
        provider: "{{ cli }}"
      with_items:
        - 192.168.1.1
        - 192.168.1.2
        - 192.168.1.3
        - 192.168.1.4
        - 192.168.1.5
        - 192.168.1.6
        - 192.168.1.7
        - 192.168.1.8
        - 192.168.1.9
        - 192.168.1.10
      ignore_errors: yes
      register: result
    
    - name: output csv file
      template:
        src: ./template_ping_result.txt
        dest: "./ping_from_{{ inventory_hostname }}_result.csv"

  vars:
    cli:
      host: "{{ inventory_hostname }}"
      timeout: 15     # (5 * 2) + margin
      username: testuser
      password: testpassword
      authorize: yes
      auth_pass: testpassword

Playbook の補足説明

  • with_items を利用して、複数の宛先へのping結果を取得するようにしています。必要に応じてパラメータ化して下しさい。
  • state オプションは省略しているため、成功(ロス率100%未満)を想定しています。もしpingが失敗した場合は、playbook中のタスクとしてfailed 扱いになります。
  • 今回は想定外の state になった場合の挙動も確認しつつ、タスクを続行したかったため、少々乱暴ですが ignore_errors: yes を指定しています。
  • count: 5(デフォルトと同じ)にしているため、IOS側のpingコマンド仕様のタイムアウト2秒を考慮すると、ロス率100%の宛先相手の場合pingが完了に10秒かかります。タスクとしての timeuout オプションのデフォルトも10秒のため、pingが完了する前にタスクとしてタイムアウトしています。この問題の対策として「count オプション × pingコマンドタイムアウト = 10秒」にさらに余裕を加えて、ios_ping モジュールの timeout オプションを 15 としています。

・テンプレート

ping結果をCSVファイルに整形するするためのテンプレートです。いろいろカスタマイズできます。

"dest","packet_loss","packets_rx","packets_tx","rtt_avg","rtt_max","rtt_min","msg"
{% for r in result['results'] %}
"{{r['item'] }}","{{ r['packet_loss'] }}",{{ r['packets_rx'] }},{{ r['packets_tx'] }},{{ r['rtt']['avg'] }},{{ r['rtt']['max']}},{{ r['rtt']['min'] }},"{{ r['msg'] | default('') }}"
{% endfor %}

テンプレートの補足説明

  • Playbook 中で 変数 results として取得した複数のping結果を、1宛先ごとにループしています。
  • msg は想定外の stete の時のみ返ってくる値です。そのため、想定通りの state の場合に msg を出力しようとするとエラーになっていまいます。エラー対策として、default フィルターを利用して、返ってこなかった場合に空文字になるようにしています。

■ 実行その1(全OK)

すべての宛先のping結果が想定通りの state の場合です。

・playbookの実行

[vagrant@centos7 vagrant]$ ansible-playbook -i hosts ping.yml

PLAY [ios] *********************************************************************

TASK [ping] ********************************************************************
ok: [10.0.0.1] => (item=192.168.1.1)
ok: [10.0.0.1] => (item=192.168.1.2)
ok: [10.0.0.1] => (item=192.168.1.3)
ok: [10.0.0.1] => (item=192.168.1.4)
ok: [10.0.0.1] => (item=192.168.1.5)
ok: [10.0.0.1] => (item=192.168.1.6)
ok: [10.0.0.1] => (item=192.168.1.7)
ok: [10.0.0.1] => (item=192.168.1.8)
ok: [10.0.0.1] => (item=192.168.1.9)
ok: [10.0.0.1] => (item=192.168.1.10)

TASK [output csv file] *********************************************************
changed: [10.0.0.1]

PLAY RECAP *********************************************************************
10.0.0.1               : ok=2    changed=1    unreachable=0    failed=0

CSV出力結果

[vagrant@centos7 vagrant]$ cat ping_from_10.0.0.1_result.csv
"dest","packet_loss","packets_rx","packets_tx","rtt_avg","rtt_max","rtt_min","msg"
"192.168.1.1","0%",5,5,2,9,1,""
"192.168.1.2","0%",5,5,2,9,1,""
"192.168.1.3","0%",5,5,8,9,8,""
"192.168.1.4","0%",5,5,7,9,1,""
"192.168.1.5","0%",5,5,6,9,1,""
"192.168.1.6","0%",5,5,8,9,8,""
"192.168.1.7","0%",5,5,8,9,8,""
"192.168.1.8","0%",5,5,7,9,1,""
"192.168.1.9","0%",5,5,7,9,1,""
"192.168.1.10","0%",5,5,8,9,8,""

■ 実行その2(一部NG)

一部宛先のping結果が想定外の state の場合です。

・Playbook実行

[vagrant@centos7 vagrant]$ ansible-playbook -i hosts ping.yml

PLAY [ios] *********************************************************************

TASK [ping] ********************************************************************
ok: [10.0.0.1] => (item=192.168.1.1)
failed: [10.0.0.1] (item=192.168.1.2) => {"changed": false, "commands": ["ping 192.168.1.99 repeat 5"], "item": "192.168.1.99", "msg": "Ping failed unexpectedly", "packet_loss": "100%", "packets_rx": 0, "packets_tx": 5, "rtt": {"avg": null, "max": null, "min": null}}
ok: [10.0.0.1] => (item=192.168.1.3)
ok: [10.0.0.1] => (item=192.168.1.4)
ok: [10.0.0.1] => (item=192.168.1.5)
ok: [10.0.0.1] => (item=192.168.1.6)
ok: [10.0.0.1] => (item=192.168.1.7)
ok: [10.0.0.1] => (item=192.168.1.8)
ok: [10.0.0.1] => (item=192.168.1.9)
ok: [10.0.0.1] => (item=192.168.1.10)
...ignoring

TASK [temp] ********************************************************************
changed: [10.0.0.1]

PLAY RECAP *********************************************************************
10.0.0.1               : ok=2    changed=1    unreachable=0    failed=0

想定外だった宛先に対するpingのタスクは failed になります。

CSV出力結果

以下の通り、疎通が取れる想定だった宛先に対して、ロス率100%だった場合は、msgPing failed unexpectedly となります。

[vagrant@centos7 vagrant]$ cat ping_from_10.0.0.1_result.csv
"dest","packet_loss","packets_rx","packets_tx","rtt_avg","rtt_max","rtt_min","msg"
"192.168.1.1","0%",5,5,2,9,1,""
"192.168.1.2",”100%",0,5,,,,"Ping failed unexpectedly"
"192.168.1.3","0%",5,5,8,9,8,""
"192.168.1.4","0%",5,5,7,9,1,""
"192.168.1.5","0%",5,5,6,9,1,""
"192.168.1.6","0%",5,5,8,9,8,""
"192.168.1.7","0%",5,5,8,9,8,""
"192.168.1.8","0%",5,5,7,9,1,""
"192.168.1.9","0%",5,5,7,9,1,""
"192.168.1.10","0%",5,5,8,9,8,""

なお、疎通が取れない想定だった宛先に対して、ロス率100%未満 の場合は、msgPing succeeded unexpectedly となります。

■ まとめ

Ansible の ios_ping モジュールと template モジュール利用して、Cisco IOS からの ping 結果を CSV 出力してみました。 counttimeout の関係や、「何をもって成功/失敗とするか」あたりがポイントです。 また、今回はping実行ホスト1台につき1つのCSVファイルを生成しましたが、作り方次第では1つのファイルにまとめることも可能かと思います。 もし、今回のような内容をしたい場合に、本記事が参考になれば幸いです。