■ はじめに
Ansible 2.8 では、RESTCONF でネットワーク機器の情報を取得したり、設定を変更したりできる RESTCONF モジュールが導入されます。(本記事執筆時現在 RC段階)
この記事では restconf_get
モジュールを利用して、IOS-XE へ RESTCONF でアクセスしてインターフェース情報の取得を試してみます。(後述しますが、そのままではうまくいかなかったので、暫定対処として一部コードの修正をして試しました → Ansible 2.8.2 でバグ解消済み)
■ 環境
- Cisco IOS-XE (16.8)
- Ansible 2.8.0rc1
- 本記事執筆時現在 RC段階のため、
pip install ansible==2.8.0rc1
でインストール
■ 準備
Playook 実行に必要なファイルや Playbook を作成していきます。
インベントリファイル
ホスト情報を示すインベントリファイルを作成します。
[ios]
iosao1 ansible_host=ios-xe-mgmt.cisco.com
変数定義ファイル
インベントリファイルで定義したグループ ios
が利用する変数を定義するファイルを作成します。
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_port
や ansible_ssh_port
変数でポート番号を指定しますが、ansible_httpapi_port
であることにご注意ください。
- デフォルトは、変数
ansible_httpapi_use_ssl
の値によって変わります。yes
の場合は 443
、no
の場合は 80
です。
- ここでは、環境の都合により
9443
を指定しています。
- ansible_network_os
- ネットワークOSを指定します。
ios_config
や junos_config
モジュールなどのベンダー個別モジュールを利用する場合はこの変数は ios
や junos
といった値を指定しますが、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
Playbook
処理内容を記載する Playbook を作成します。
- 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