てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] restconf_get モジュールで Cisco IOS XE のインターフェース情報を取得してみる

■ はじめに

Ansible 2.8 では、RESTCONF でネットワーク機器の情報を取得したり、設定を変更したりできる RESTCONF モジュールが導入されます。(本記事執筆時現在 RC段階)

この記事では restconf_get モジュールを利用して、IOS-XE へ RESTCONF でアクセスしてインターフェース情報の取得を試してみます。(後述しますが、そのままではうまくいかなかったので、暫定対処として一部コードの修正をして試しました → Ansible 2.8.2 でバグ解消済み)


■ 環境

  • Cisco IOS-XE (16.8)
    • Cisco DevNet SandBox
      • NETCONF-YANG and RESTCONF Always-On
      • netconf-yangrestconf コマンド有効
  • Ansible 2.8.0rc1
    • 本記事執筆時現在 RC段階のため、pip install ansible==2.8.0rc1 でインストール

■ 準備

Playook 実行に必要なファイルや Playbook を作成していきます。

インベントリファイル

ホスト情報を示すインベントリファイルを作成します。

  • inventory
[ios]
iosao1 ansible_host=ios-xe-mgmt.cisco.com

変数定義ファイル

インベントリファイルで定義したグループ ios が利用する変数を定義するファイルを作成します。

  • group_vars/ios.yml
ansible_connection: httpapi
ansible_httpapi_port: 9443
ansible_network_os: restconf
ansible_user: root
ansible_password: p@ss9999
ansible_httpapi_use_ssl: yes
ansible_httpapi_validate_certs: no

変数の説明

変数が多数登場しますのでそれぞれ説明します。

  • ansible_connection
  • ansible_httpapi_port
    • 利用するポートを指定します。SSH 接続では ansible_portansible_ssh_port 変数でポート番号を指定しますが、ansible_httpapi_port であることにご注意ください。
    • デフォルトは、変数 ansible_httpapi_use_ssl の値によって変わります。yes の場合は 443no の場合は 80 です。
    • ここでは、環境の都合により 9443 を指定しています。
  • ansible_network_os
    • ネットワークOSを指定します。
    • ios_configjunos_config モジュールなどのベンダー個別モジュールを利用する場合はこの変数は iosjunos といった値を指定しますが、RESTCONF モジュールを利用する場合は、このように restconf を利用します。こうすることで、restconf という httpapi プラグインが利用されます。
    • もし、例えば ios を指定した場合は unable to load API plugin for network_os ios というエラーが表示されます。
  • ansible_user
    • ユーザー名を指定します。
  • ansible_password
    • パスワードを指定します。ここではダミーの値を記載しています。必要に応じて ansible-vault で暗号化します。
  • ansible_httpapi_use_ssl
  • ansible_httpapi_validate_certs
    • SSL/TLS 接続する場合に証明書をかどうかを指定します。デフォルトは yes です。
    • ここでは、環境の都合により no を指定しています。

Playbook

処理内容を記載する Playbook を作成します。

  • restconf.yml
- hosts: ios
  gather_facts: no

  tasks:
    - name: restconf test
      restconf_get:
        path: /data/ietf-interfaces:interfaces
      register: result

    - name: debug result
      debug:
        msg: "{{ result }}"

path は Cisco DevNet Learning Labs の「Exploring IOS XE YANG Data Models with RESTCONF」や、こちらのサンプルに出てきたものを利用します。

restconf モジュールでは、root_path としてデフォルトで /restconf が定義されているため、実際には、/restconf 以降を path に指定します。


■ 実行(失敗)

それでは、Playbook を実行します。が、エラーになってしまいます。

$ ansible-playbook -i inventory restconf.yml

(...略...)
fatal: [iosao1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "code": 406, "msg": "HTTP Error 406: Not Acceptable"}
(...略...)

HTTP Error 406: Not Acceptable とのことなので、HTTP の Acceppt フィールドまわりを調査しました。

原因調査と暫定対処

Ansible ではなく Postman で同様の GET を試すと以下の結果になりました。

  • Accept: application/yang-data+json では正常に json で情報が取得できた
  • Accept: では 406: Not Acceptable になった
  • Accept フィールド自体を指定しない場合は、XML で情報が取得できた

また、lib/ansible/module_utils/network/restconf/restconf.py を見ると、以下のようになっていました。

    accept = None
    if output == 'xml':
        accept = 'application/yang.data+xml'

    connection = Connection(module._socket_path)
    return connection.send_request(None, path=path, method='GET', accept=accept)

どうやら、Accept フィールドが空になってしまっていたようです。

暫定対処として、

    accept = None

の箇所を

    accept = 'application/yang-data+json'

に修正しました。


■ 再実行(成功)

暫定対処したところで、再度同じ Playbook を実行します。

$ ansible-playbook -i inventory restconf.yml 

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

TASK [restconf test] *************************************************************************************************************
 [WARNING]: Platform darwin on host iosao1 is using the discovered Python interpreter at /usr/bin/python, but future installation
of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information.

ok: [iosao1]

TASK [debug result] **************************************************************************************************************
ok: [iosao1] => {
    "msg": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": false, 
        "failed": false, 
        "response": {
            "ietf-interfaces:interfaces": {
                "interface": [
                    {
                        "description": "DON'T TOUCH ME", 
                        "enabled": true, 
                        "ietf-ip:ipv4": {
                            "address": [
                                {
                                    "ip": "10.10.20.48", 
                                    "netmask": "255.255.255.0"
                                }
                            ]
                        }, 
                        "ietf-ip:ipv6": {}, 
                        "name": "GigabitEthernet1", 
                        "type": "iana-if-type:ethernetCsmacd"
                    }, 
                    {
                        "enabled": true, 
                        "ietf-ip:ipv4": {}, 
                        "ietf-ip:ipv6": {}, 
                        "name": "GigabitEthernet2", 
                        "type": "iana-if-type:ethernetCsmacd"
                    }, 
                    {
                        "enabled": false, 
                        "ietf-ip:ipv4": {}, 
                        "ietf-ip:ipv6": {}, 
                        "name": "GigabitEthernet3", 
                        "type": "iana-if-type:ethernetCsmacd"
                    }
                ]
            }
        }, 
        "warnings": [
            "Platform darwin on host iosao1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information."
        ]
    }
}

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

こんどは正常にインターフェース情報を取得できました。

※ちらっと表示されている Python interpreter についての WARNING の詳細は こちらをご参照ください。


■ まとめ

Ansible 2.8 で導入される restconf_get モジュールで、Cisco IOS-XE のインターフェース情報を取得してみました。 (一部コード修正が必要だった点は、他の対応方法があるのかなどの点は現時点では不明です。)

RESTCONF が有効であれば、他のベンダーの機器にも使える雰囲気がします。ベンダーごとにモジュールを使い分けなくて良い点は便利そうだと感じました。

また、Cisco IOS-XE では、RESTCONF が使えるようになってきているようなので、今後 RESTCONF は自動化の手段の選択肢になり得るのではないかと思います。

[2019/05/27 追記] 少しに気なる issue を見つけました。

restconf httpapi plugin Media Type · Issue #56680 · ansible/ansible · GitHub

[2019/09/06 追記] Ansible 2.8.2 で修正されていることを確認しました。 ansible/CHANGELOG-v2.8.rst at stable-2.8 · ansible/ansible · GitHub

Fix media type of RESTCONF requests.

[2019/09/09 追記] 設定変更編を投稿しました。

tekunabe.hatenablog.jp