てくなべ (tekunabe)

ansible / network / automation

[Ansible] assert モジュールで変数の型チェックする

はじめに

Ansible の変数では様々な型が扱えますが、基本的にゆるめなため、型をチェックしたいことがあるかもしれません。

いままでは、assert モジュールと、type_debug フィルターを組みわせるチェックする方法しか思いつかなかったのが、こちらを拝見したら is string のような書き方もできることを知りました。

せっかくなので、両方のやり方をまとめます。

  • 動作確認環境


[目次]

■ 方法1: type_debug フィルターを利用する

type_debug フィルターは、変数の方を表示するフィルターです。

"{{ 'hello' | type_debug }}" であれば、 str と表示されます。この性質を利用した型チェックです。

検証 Playbook

いくつか型の種類を用意した変数のリストを assert します。ここでは str(文字列)であることをチェックします。

assert では、デバッグ的に success_msgfail_msg を利用します。

- hosts: localhost
  gather_facts: no
  connection: local

  vars:
    targets:    # チェック対象
      - hello
      - 100
      - "100"
      - -100
      - 0.9
      - [1, 2, 3]
      - {name: hoge}

  tasks:
    - name:
      assert:
        that:
          - (item | type_debug) == "str"    # ポイント
        success_msg: "{{ item }} type is {{ item | type_debug }}"
        fail_msg: "{{ item }} type is {{ item | type_debug }}, NOT str"
      loop: "{{ targets }}"

検証結果

結果です。targets で定義した値のうち、hello"100" のみ ok となりました。

$ ansible-playbook -i localhost, assert.yml 

PLAY [localhost] *************************************************************************************************

TASK [assert type] ***********************************************************************************************
ok: [localhost] => (item=hello) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": "hello",
    "msg": "hello type is str"          // hello は ok
}
failed: [localhost] (item=100) => {
    "ansible_loop_var": "item",
    "assertion": "(item | type_debug) == \"str\"",
    "changed": false,
    "evaluated_to": false,
    "item": 100,
    "msg": "100 type is int, NOT str"
}
ok: [localhost] => (item=100) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": "100",
    "msg": "100 type is str"          // "100" は ok
}
failed: [localhost] (item=-100) => {
    "ansible_loop_var": "item",
    "assertion": "(item | type_debug) == \"str\"",
    "changed": false,
    "evaluated_to": false,
    "item": -100,
    "msg": "-100 type is int, NOT str"
}
failed: [localhost] (item=0.9) => {
    "ansible_loop_var": "item",
    "assertion": "(item | type_debug) == \"str\"",
    "changed": false,
    "evaluated_to": false,
    "item": 0.9,
    "msg": "0.9 type is float, NOT str"
}
failed: [localhost] (item=[1, 2, 3]) => {
    "ansible_loop_var": "item",
    "assertion": "(item | type_debug) == \"str\"",
    "changed": false,
    "evaluated_to": false,
    "item": [
        1,
        2,
        3
    ],
    "msg": "[1, 2, 3] type is list, NOT str"
}
failed: [localhost] (item={'name': 'hoge'}) => {
    "ansible_loop_var": "item",
    "assertion": "(item | type_debug) == \"str\"",
    "changed": false,
    "evaluated_to": false,
    "item": {
        "name": "hoge"
    },
    "msg": "{'name': 'hoge'} type is dict, NOT str"
}

PLAY RECAP *******************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

2個目の値 100 も、3個目 "100" も、メッセージ上は 100 と表示され、

    "msg": "100 type is int, NOT str"

とか

    "msg": "100 type is str"

とかになるのが少しややこしいですね。


■ 方法2: is [型名] を利用する

次に、こちらで知った方法です。

検証 Playbook

チェック対象の変数は同じです。

assert で、item is string のように指定します。 str ではなく string です。

- hosts: localhost
  gather_facts: no
  connection: local

  vars:
    targets:
      - hello
      - 100
      - "100"
      - -100
      - 0.9
      - [1, 2, 3]
      - {name: hoge}

  tasks:
    - name: assert type
      assert:
        that:
          - item is string      # ポイント
        success_msg: "{{ item }} type is {{ item | type_debug }}"
        fail_msg: "{{ item }} type is {{ item | type_debug }}, NOT str"
      loop: "{{ targets }}"

検証結果

結果です。hello"100" のみ ok となりました。

$ ansible-playbook -i localhost, assert.yml 

PLAY [localhost] ****************************************************************************************************

TASK [assert type] **************************************************************************************************
ok: [localhost] => (item=hello) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": "hello",
    "msg": "All assertions passed"          // hello は ok
}
failed: [localhost] (item=100) => {
    "ansible_loop_var": "item",
    "assertion": "item is string",
    "changed": false,
    "evaluated_to": false,
    "item": 100,
    "msg": "Assertion failed"
}
ok: [localhost] => (item=100) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": "100",
    "msg": "All assertions passed"          // "100" は ok
}
failed: [localhost] (item=-100) => {
    "ansible_loop_var": "item",
    "assertion": "item is string",
    "changed": false,
    "evaluated_to": false,
    "item": -100,
    "msg": "Assertion failed"
}
failed: [localhost] (item=0.9) => {
    "ansible_loop_var": "item",
    "assertion": "item is string",
    "changed": false,
    "evaluated_to": false,
    "item": 0.9,
    "msg": "Assertion failed"
}
failed: [localhost] (item=[1, 2, 3]) => {
    "ansible_loop_var": "item",
    "assertion": "item is string",
    "changed": false,
    "evaluated_to": false,
    "item": [
        1,
        2,
        3
    ],
    "msg": "Assertion failed"
}
failed: [localhost] (item={'name': 'hoge'}) => {
    "ansible_loop_var": "item",
    "assertion": "item is string",
    "changed": false,
    "evaluated_to": false,
    "item": {
        "name": "hoge"
    },
    "msg": "Assertion failed"
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   


■ 型の名前のまとめ

各値の type_debug フィルター適用後の値と、is [型名]ok となる型名をまとめます。

type debug 結果 is [型名]
hello str string
100 int number
"100" str string
-100 int number
0.9 float number
[1, 2, 3] list sequence
{name: hoge} dict sequence / mapping

試した限り、{name: hoge} については、 is sequence でも is mapping でも assertok となったのが意外でした。

is [型名] の方は、Jinja2 の List of Builtin Tests によるもののようです。@satoru_satoh さん、情報ありがとうございます!

また、Jinja2 2.11 では number 以外にも、floatinteger も利用できるようです。


おわりに

型をあまり意識しなくていいことがメリットなこともありますが、つまずくこともあるかもしれません。

型のチェックが必要な時に、参考になれば幸いです。

参考

[Ansible] CSV ファイルを YAML ファイルに変換する

はじめに

以前の記事で、CSV ファイルをリストやディクショナリとして読み込む read_csv モジュールをご紹介しました。

tekunabe.hatenablog.jp

to_nice_yaml フィルターと組み合わせると、CSV ファイルを YAML ファイルに変換できます。

この記事では、簡単なサンプルでご紹介します。

  • 動作確認環境
    • Ansible 2.9.6

利用する CSV ファイル

今回はこんな CSV ファイルを利用します。1行目がヘッダーで、read_csv モジュールで読み込むと、ディクショナリのキーになります。

name,age,hobby
taro,50,baseball
jiro,40,swimming
saburo,30,ansible


Playbook サンプル

その1: ディクショナリのリストとして出力

CSV 1行を1つリストとして出力します。(説明が難しいのでサンプルをご参照ください・・)

- hosts: testsv
  gather_facts: no

  tasks:
    - name: read users.csv
      read_csv:
        path: users.csv   # CSV ファイル名
      register: users
      delegate_to: localhost  # ここを省略するとリモートのファイルを参照

    - name: convert to yaml
      copy:
        content: "{{ users.list | to_nice_yaml(indent=2) }}" # ポイント
        dest: users.yml   # 出力先 YAML ファイル名

こうなります。

- age: '50'
  hobby: baseball
  name: taro
- age: '40'
  hobby: swimming
  name: jiro
- age: '30'
  hobby: ansible
  name: saburo

その2 ディクショナリのディクショナリとして出力

リストではなく、指定したキーのディクショナリのディクショナリを出力します。

- hosts: testsv
  gather_facts: no

  tasks:
    - name: read users.csv
      read_csv:
        path: users.csv   # CSV ファイル名
        key: name         # ポイント1 
      register: users
      delegate_to: localhost  # ここを省略するとリモートのファイルを参照

    - name: debug csv
      copy:
        content: "{{ users.dict | to_nice_yaml(indent=2) }}"  # ポイント2 users.list ではなく users.dict
        dest: users.yml

read_csvkey で指定したフィールドをキーとした、ネストされたディクショナリになります。

jiro:
  age: '40'
  hobby: swimming
  name: jiro
saburo:
  age: '30'
  hobby: ansible
  name: saburo
taro:
  age: '50'
  hobby: baseball
  name: taro

その3 ディクショナリのディクショナリとして出力

その2をベースに、トップになにかルート要素的なキーを入れたいときの方法です。

- hosts: testsv
  gather_facts: no

  tasks:
    - name: read users.csv
      read_csv:
        path: users.csv   # CSV ファイル名
        key: name         # ポイント1 
      register: users
      delegate_to: localhost  # ここを省略するとリモートのファイルを参照

    - name: debug csv
      copy:
        content: "{{ root | to_nice_yaml(indent=2) }}" # ポイント2 users.list ではなく users.dict
        dest: users.yml
      vars:
        root:     
          users: "{{ users.list }}"   # ポイント3 トップのキーを仕込む

トップに users が入った形になります。

users:
  jiro:
    age: '40'
    hobby: swimming
    name: jiro
  saburo:
    age: '30'
    hobby: ansible
    name: saburo
  taro:
    age: '50'
    hobby: baseball
    name: taro


おわりに

read_csv モジュールも、to_nice_yaml フィルター も便利ですね。

もっと柔軟にするには、 read_csv で読み込んだ後に、template モジュールで整えるのもいいかもしれません。

[Ansible] どこにどういうキーワードを指定できるかは公式ドキュメントの Playbook Keywords で分かる

他人の書いた Playbook を見て「そのキーワード(ディレクティブ)ってそこにも書けるの!?」と思うような経験はありませんでしょうか。

例えば、以下の Playbook をご覧ください。

- name: ios test         # (1)
  hosts: ios
  gather_facts: false
  tags:                  # (2)
    - ios

  tasks:
    - name: show version
      ios_command:
        commands: "{{ commands }}"
      register: result_version
      vars:               # (3)
        commands:
          - show version

もしかしたら (1)〜(3) あたりは、見かけることが多くないかもしれません。

どこに何を指定できるのかは、公式ドキュメントの Playbook Keywords というページで紹介されています。

docs.ansible.com

例えば、上記の Playbook の

  • (1) は Play 単位の name
  • (2) は Play 単位の tags
  • (3) は Task 単位の vars

として紹介されています。

モジュールのオプションはモジュールの説明ページを見れば良いのですが、その他の一般的な部分は、見かけたサンプルのかきっぷりによりがちな気がします。一度 Playbook Keywords を見ると、新しい発見があるかもしれません。

なお、余談ですが、 (3) の Task 単位の vars変数優先度のリストで、task vars (only for the task) として掲載されています。割と強めです。

[Ansible] リストの中から正規表現でマッチする要素を取り出す select('match', regexp)

はじめに

Ansible が内部的に利用している Jinja2 には select という、リストに対して様々な操作をするフィルターがあります。

ドキュメントの例には載っていませんが、match というキーワードを利用すると、正規表現をつかった抽出ができます。と、同僚に教わりました(感謝)。

この記事では、かんたんなサンプルを元に、リストの中から正規表現でマッチする要素を取り出す例をご紹介します。

  • 動作確認環境
    • Ansible 2.9.6

サンプル

リスト test_list の各要素に対して、正規表現 .+[2-4].+ にマッチする要素のみに抽出するサンプルです。

Playbook

- hosts: localhost
  gather_facts: false

  vars:
    test_list:  # 抽出対象のリスト
      - xxx1xxx
      - xxx2xxx
      - xxx3xxx
      - xxx4xxx
      - xxx5xxx

  tasks:
    - name: list match
      debug:
        msg: "{{ test_list | select('match', '.+[2-4].+') | list }}"  # ポイント

実行例

Playbook を実行します。

$ ansible-playbook -i localhost, match.yml 

PLAY [localhost] **************************************************************************************************

TASK [list match] *************************************************************************************************
ok: [localhost] => {
    "msg": [
        "xxx2xxx",
        "xxx3xxx",
        "xxx4xxx"
    ]
}

PLAY RECAP ********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

対象リストの各要素の中から、.+[2-4].+ にマッチする 3つがリストとして抽出できました。

参考

qiita.com

Cisco DevNet Associate Exam v1.0 (DEVASC 200-901) に合格しました

はじめに

2020/02/24 にシスコ認定技術者試験の制度が大きく改定されました。

それに伴い新しく始まった、ネットワーク自動化、プログラマビリティに関する資格である DevNet Associate Exam v1.0 (DEVASC 200-901) を受験して合格しました。

この記事では私の勉強方法や感想をまとめます。


■ 勉強方法

試験範囲はこちらで確認できます。(PDF)

https://www.cisco.com/c/dam/global/ja_jp/training-events/training-certifications/exam-topics/200-901-DEVASC.pdf

PythonAPI、開発手法、自動化、セキュリティ、Git、Ansible(!)、ネットワーク基礎などなど、なかなか範囲は広いです。

勉強を始める前

勉強を始める前はこんな状態でした。目安として関係しそうな資格などまとめます。

利用した教材

2019年12月下旬から、勉強を少しずつはじめました

DevNet Associate Fundamentals Course (有料・公式)

developer.cisco.com

テキストと、ハンズオン環境があります。これが一番おすすめです。 受験日一週間前くらい始まったようで、急いでやりました。

ハンズオン環境もとても良かったです。ブラウザだけで実施できて、左に説明、右にエディタやターミナルという構成です。

最初の方はコードやコマンドを手打ちしてましたが、途中で時間がなくなってきたので、コピペ機能を使ってしまいました。Google 翻訳も途中からフル活用してしまいました・・

ただ、コピペした場合でも、そのコードが何をしているのかは読み取れるようにしました。

選択問題も用意されていたので一通りときました。

振り返ってみると、これをちゃんとやるのが一番良かったのかなと思います。

Full Stack Networker (有料)

www.fullstacknetworker.com

テキストと、簡単なハンズオン環境があります。

一通りなんとなく読んだりしました。 サンプルコード(PythonYAML)のインデントがおかしく、変なところで集中力が削がれてしまいました・・。

選択問題も用意されていたので一通りときました。

CBT Nuggets (有料)

www.cbtnuggets.com

動画と書き起こしテキストのコンテンツです。

動画を見る時間をとれずに結局ほとんど利用しませんでした。


■ 感想

思っていたより難しく感じました。英語力のほうが心配だったのですが、それよりも内容のほうを心配すべきでした。

途中、落ちたと思ったので、リベンジに向けての弱点補強のポイントを整理してました。

英語圏以外に居住していて母国語が英語でない場合は、試験時間の延長措置があるのですが、結果として時間は余りました。 操作ミスかなにかで、後で見直そうとチェックつけたつもりが、そのまま終了してしまいました。

あれやっておけばよかったと思ったのは、もっと手を動かすことです。

「ネットワーク自動化ってどんな知識がが必要なのか?」という問に対して、この試験範囲が参考になりそうです。

シスコ製品固有の話もありますが、API の具体例としては何かしらあったほうが良いのだろうと捉えています。

今後

しばらくは資格勉強はお休みです。

予定はないですが、DCAUTODCCOR の組み合わせで CCNP Data Center あたりを狙うのが私にはあってるような気がします。

禁止していた Ansible を摂取します。

参考

他の方の受験記

私の勉強記など

[Ansible] Junos の interface の状態を debug で表示する(display json 編)

はじめに

以前の記事で、gather_facts を利用して Junos のインターフェースの状態を取得して debug で表示するという Playbook をご紹介しました。

tekunabe.hatenablog.jp

今回は普通に show interfaces terse コマンドを利用します。ただし、せっかく JSON で表示してくれるバージョンの Junos であれば、display json を使うのも手です。

この記事では、show interfaces terse | display json コマンド相当の処理を利用します。

動作確認環境


Playbook

Playbook はこんな感じです。

- name: Junos Module TEST
  hosts: junos
  gather_facts: no      # ポイント(1)
  vars:
    ansible_python_interpreter: /usr/bin/python3
  tasks:
    - name: show
      junos_command:
        commands:
          - show interfaces terse 
        display: json               # ポイント(2)
      register: result

    - name: debug
      debug:
        msg: "admin_status : {{ admin_status }} / oper_status : {{ oper_status }}"
      loop: "{{ result.stdout[0]['interface-information'][0]['physical-interface'] }}"  # ポイント(3)
      vars:
        admin_status: "{{ item['admin-status'][0]['data'] }}"      # ポイント(4)
        oper_status: "{{ item['oper-status'][0]['data'] }}"

ポイント・相違点

ポイント、オリジナルとの相違点は以下のとおりです。

ポイント(1)

gather_facts ではなく、show コマンドを利用するため、無効にします。

ポイント(2)

junos_command モジュールを利用して、show interfaces terse コマンドを実行します。

json で結果を取得したいので、display: json を指定します。

ポイント(3)

show interfaces terse コマンドの結果を json で取得して、物理インターフェースの情報がある構造を指定します。 ここまで指定すると、各インターフェースの情報がリストで格納されているので、witth_dict ではなく普通に loop を使います。

機種は設定によっては、ここの指定方法が変わるかもしれません。

なお、json の全内容は以下の通りです。(1000行以上あるのご注意)

クリック指定開く

{
    "interface-information": [
        {
            "physical-interface": [
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "logical-interface": [
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "vpls"
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "lc-0/0/0.32769"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        }
                    ],
                    "name": [
                        {
                            "data": "lc-0/0/0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "logical-interface": [
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ]
                                },
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet6"
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "pfe-0/0/0.16383"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        }
                    ],
                    "name": [
                        {
                            "data": "pfe-0/0/0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "logical-interface": [
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "pfh-0/0/0.16383"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        },
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "pfh-0/0/0.16384"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        }
                    ],
                    "name": [
                        {
                            "data": "pfh-0/0/0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/1"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/2"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/3"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/4"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/5"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/6"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ge-0/0/7"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "down"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "cbp0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "demux0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "dsc"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "logical-interface": [
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ],
                                    "interface-address": [
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "10.0.0.4/8"
                                                }
                                            ]
                                        },
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "128.0.0.1/2"
                                                }
                                            ]
                                        },
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "128.0.0.4/2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet6"
                                        }
                                    ],
                                    "interface-address": [
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "fe80::250:56ff:fea2:f74b/64"
                                                }
                                            ]
                                        },
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "fec0::a:0:0:4/64"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                {
                                    "address-family-name": [
                                        {
                                            "data": "tnp"
                                        }
                                    ],
                                    "interface-address": [
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "0x4"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "em1.0"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        }
                    ],
                    "name": [
                        {
                            "data": "em1"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "esi"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti1"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti2"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti3"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti4"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti5"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti6"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "fti7"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "logical-interface": [
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ],
                                    "interface-address": [
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "100.123.1.0/16"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "fxp0.0"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        }
                    ],
                    "name": [
                        {
                            "data": "fxp0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "gre"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "ipip"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "irb"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "logical-interface": [
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ],
                                    "interface-address": [
                                        {
                                            "ifa-local": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "128.0.0.127/2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "jsrv.1"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        }
                    ],
                    "name": [
                        {
                            "data": "jsrv"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "logical-interface": [
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ],
                                    "interface-address": [
                                        {
                                            "ifa-destination": [
                                                {
                                                    "attributes": {
                                                        "junos:emit": "emit"
                                                    },
                                                    "data": "0/0"
                                                }
                                            ],
                                            "ifa-local": [
                                                {
                                                    "data": "127.0.0.1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "lo0.16384"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        },
                        {
                            "address-family": [
                                {
                                    "address-family-name": [
                                        {
                                            "data": "inet"
                                        }
                                    ]
                                }
                            ],
                            "admin-status": [
                                {
                                    "data": "up"
                                }
                            ],
                            "filter-information": [
                                {}
                            ],
                            "name": [
                                {
                                    "data": "lo0.16385"
                                }
                            ],
                            "oper-status": [
                                {
                                    "data": "up"
                                }
                            ]
                        }
                    ],
                    "name": [
                        {
                            "data": "lo0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "lsi"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "mtun"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "pimd"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "pime"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "pip0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "pp0"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "rbeb"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "tap"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                },
                {
                    "admin-status": [
                        {
                            "data": "up"
                        }
                    ],
                    "name": [
                        {
                            "data": "vtep"
                        }
                    ],
                    "oper-status": [
                        {
                            "data": "up"
                        }
                    ]
                }
            ]
        }
    ]
}

ポイント(4)

json の構造に沿って、インターフェース名と各状態をタスク変数(ループごとに更新される)に格納します。

実行結果

Playbook の実行結果です。

$ ansible-playbook -i hosts fact_test2.yml

PLAY [Junos Module TEST] *************************************************************************************************************

TASK [show] **************************************************************************************************************************
ok: [vmx]

TASK [debug] *************************************************************************************************************************
ok: [vmx] => (item={'name': [{'data': 'ge-0/0/0'}], 'admin-status': [{'data': 'up'}], 'oper-status': [{'data': 'down'}]}) => {
    "msg": "admin_status : up / oper_status : down"
}
ok: [vmx] => (item={'name': [{'data': 'lc-0/0/0'}], 'admin-status': [{'data': 'up'}], 'oper-status': [{'data': 'up'}], 'logical-interface': [{'name': [{'data': 'lc-0/0/0.32769'}], 'admin-status': [{'data': 'up'}], 'oper-status': [{'data': 'up'}], 'filter-information': [{}], 'address-family': [{'address-family-name': [{'data': 'vpls'}]}]}]}) => {
    "msg": "admin_status : up / oper_status : up"
}
ok: [vmx] => (item={'name': [{'data': 'pfe-0/0/0'}], 'admin-status': [{'data': 'up'}], 'oper-status': [{'data': 'up'}], 'logical-interface': [{'name': [{'data': 'pfe-0/0/0.16383'}], 'admin-status': [{'data': 'up'}], 'oper-status': [{'data': 'up'}], 'filter-information': [{}], 'address-family': [{'address-family-name': [{'data': 'inet'}]}, {'address-family-name': [{'data': 'inet6'}]}]}]}) => {
    "msg": "admin_status : up / oper_status : up"
}

...(略)...

PLAY RECAP ********************************************************************************************************
vmx                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

補足

ループごとに表示される変数の値を調整したい場合は、loop_controllabel を使うと便利です。

# ...(略)
    - name: debug
      debug:
        msg: "admin_status : {{ admin_status }} / oper_status : {{ oper_status }}"
      loop: "{{ result.stdout[0]['interface-information'][0]['physical-interface'] }}" 
      vars:
        admin_status: "{{ item['admin-status'][0]['data'] }}"
        oper_status: "{{ item['oper-status'][0]['data'] }}"
      loop_control:           # ポイント
        label: "{{ item['name'][0]['data'] }}"      # ポイント

結果

ok: [vmx] => (item=ge-0/0/0) => {
    "msg": "admin_status : up / oper_status : down"
}


おわりに

状態を取得するだけでも色々な方法があることが分かりますね。

他にも TextFSM をパーサーを利用する方法もあります。

参考

qiita.com tekunabe.hatenablog.jp

[Ansible] Junos の interface の状態を debug で表示する(gather_facts 編)

はじめに

以下の記事で、Junos のインターフェースの状態を取得して debug で表示するという Playbook が紹介されていました。

qiita.com

面白いネタだなと思って、拝見しました。特に、以下のディクショナリをループする書き方が参考になりました。

with_items: "{{ int_results.keys() | list }}"

自分でしたら、with_dict を使うところだったので、なるほどと思いました。

そんなことを思ったので、この記事では「Playbook紹介2~interfaceの状態確認~」の別の書き方をした Playbook をご紹介します。

この記事の書き方のほうが良いと主張するつもりはまったくなく「こんな書き方もできるんだなぁ〜」程度に思っていただければと思います。

動作確認環境


Playbook

Playbook はこんな感じです。

- name: Junos Module TEST
  hosts: junos
  gather_facts: yes   # ポイント(1)

  vars:
    ansible_python_interpreter: /usr/bin/python3
  tasks:
    - name: debug
      debug:
        msg: "admin_status : {{ admin_status }} / oper_status : {{ oper_status }}"
      with_dict: "{{ ansible_net_interfaces }}"  # ポイント(2)
      vars:
        admin_status: "{{ item.value['admin-status'] }}"    # ポイント(3)
        oper_status: "{{ item.value['oper-status']}}"

ポイント・相違点

ポイント、オリジナルとの相違点は以下のとおりです。

ポイント(1)

オリジナルでは gather_facts: no としていましたが、yes にしました。これは、Ansible 2.9 から gather_facts でネットワーク機器の facts を収集する仕様に変わったためです。代わりに、junos_facts モジュールを使うタスクは定義していません。gather_facts: yes によって暗黙的に junos_facts モジュールが利用されます。

この方式のデメリットは、Ansible コントロールノード自身の fact も収集してしまう点です。

なお、デフォルトが gather_facts: yes なので、gather_facts 自体を指定しなくても構いません。

ポイント(2)

オリジナルでは、

      with_items: "{{ int_results.keys() | list }}"

としていましたが、

      with_dict: "{{ ansible_net_interfaces }}"

にしました。

with_dict は、ディクショナリのキーの数だけループする書き方です。ループ中の item.key にキーが、item.value に値が入ります。

ループで回すディクショナリの変数は、ansible_net_interfaces にしています。 オリジナルでは、ansible_facts.net_interfaces を一旦 set_fact で別の変数 int_results に格納していますが、ansible_net_interfaces という変数も同じ内容です。set_fact をなくして、いきなり ansible_net_interfaces を指定するようにしました。このあたりは好みの問題かもしれません。

ポイント(3)

ポイント(2)で with_dict を利用した関係で、ディクショナリの値の階層の指定を item.value['admin-status'] のようにしています。

なお、admin-status のように - が含まれるキーの指定は [ ] で囲う必要があります。item.value.admin-status のように、ドット表記にしてしまうと、途中の -演算子とみなされて admin というキーを参照した結果、そんなキーないよというエラーにってしまいます。

実行結果

Playbook の実行結果です。

$ ansible-playbook -i hosts fact_test2.yml

PLAY [Junos Module TEST] ******************************************************************************************

TASK [Gathering Facts] ********************************************************************************************
[WARNING]: Ignoring timeout(10) for junos_facts

ok: [junos]

TASK [debug] ******************************************************************************************************
ok: [junos] => (item={'key': 'ge-0/0/0', 'value': {'oper-status': 'down', 'admin-status': 'up', 'speed': '1000mbps', 'macaddress': '00:50:56:a2:45:a2', 'mtu': '1514', 'type': None}}) => {
    "msg": "admin_status : up / oper_status : down"
}
ok: [junos] => (item={'key': 'lc-0/0/0', 'value': {'oper-status': 'up', 'admin-status': 'up', 'speed': '800mbps', 'macaddress': 'Unspecified', 'mtu': '0', 'type': 'Unspecified'}}) => {
    "msg": "admin_status : up / oper_status : up"
}
ok: [junos] => (item={'key': 'pfe-0/0/0', 'value': {'oper-status': 'up', 'admin-status': 'up', 'speed': '800mbps', 'macaddress': 'Unspecified', 'mtu': '0', 'type': 'Unspecified'}}) => {
    "msg": "admin_status : up / oper_status : up"

...(略)...

PLAY RECAP ********************************************************************************************************
vmx                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

なお、[WARNING]: Ignoring timeout(10) for junos_facts という警告については、以下の記事を参照してください。

[Ansible] "[WARNING]: Ignoring timeout(10) for ios_facts" の正体とタイムアウトの設定方法 - てくなべ (tekunabe)

補足

実行結果でご覧いただいたとおり、ループのたびにディクショナリの内容が表示されるのが少々うるさいですね。デバッグのときには役に立つのですが。

ok: [junos] => (item={'key': 'ge-0/0/0', 'value': {'oper-status': 'down', 'admin-status': 'up', 'speed': '1000mbps', 'macaddress': '00:50:56:a2:45:a2', 'mtu': '1514', 'type': None}}) => {
    "msg": "admin_status : up / oper_status : down"
}
...(略)...

この出力を調整したい場合は、loop_controllabel を使うと便利です。

    - name: debug
      debug:
        msg: "admin_status : {{ admin_status }} / oper_status : {{ oper_status }}"
      with_dict: "{{ ansible_net_interfaces }}"
      vars:
        admin_status: "{{ item.value['admin-status'] }}"
        oper_status: "{{ item.value['oper-status']}}"
      loop_control:                 # ポイント
        label: "{{ item.key }}"     # ポイント

このように出力を調整することで、以下のように、オリジナルと同じログになります。

ok: [junos] => (item=ge-0/0/0) => {
    "msg": "admin_status : up / oper_status : down"
}
...(略)...


おわりに

タスクとしては1つにまとめるかたちにしてみました。とはいえ、タスクが少なければいいというわけではありません。 わかりやすさ、シンプルさ、メンテンナンスしやすさ、は意外と両立させるのは難しいかもしれません。

参考

show interfaces terse | display json コマンド相当の処理を利用する場合はこちら。

tekunabe.hatenablog.jp