ansible.netcommon
ansible.netcommon copied to clipboard
ansible.netcommon.restconf_config: RPC POST fails (reboot)
SUMMARY
When performing a POST RPC operation to reboot a devide, the task fails.
ISSUE TYPE
- Bug Report
COMPONENT NAME
ansible.netcommon.restconf_config
ANSIBLE VERSION
ansible [core 2.14.1]
config file = /home/student01/module03-3/ansible/ansible.cfg
configured module search path = ['/home/student01/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/student01/module03-3/venv/lib/python3.9/site-packages/ansible
ansible collection location = /home/student01/.ansible/collections:/usr/share/ansible/collections
executable location = /home/student01/module03-3/venv/bin/ansible
python version = 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110] (/home/student01/module03-3/venv/bin/python3)
jinja version = 3.1.2
libyaml = True
COLLECTION VERSION
Collection Version
----------------- -------
ansible.netcommon 4.1.0
CONFIGURATION
CONFIG_FILE() = /home/student01/module03-3/ansible/ansible.cfg
HOST_KEY_CHECKING(/home/student01/module03-3/ansible/ansible.cfg) = False
OS / ENVIRONMENT
- Ansible control node: Debian 11
- Ansible host: Cisco Catalyst 8000V
STEPS TO REPRODUCE
---
- name: "RESTCONF test playbook"
hosts: "ios_xe"
gather_facts: false
vars:
ansible_connection: ansible.netcommon.httpapi
ansible_network_os: ansible.netcommon.restconf
ansible_httpapi_use_ssl: true
ansible_httpapi_validate_certs: false
ansible_httpapi_port: 443
ansible_httpapi_restconf_root: /restconf/data/
ansible_user: "{{ lookup('env', 'USER') }}"
tasks:
- name: "RESTCONF: Reboot"
ansible.netcommon.restconf_config:
method: "post"
path: "/Cisco-IOS-XE-rpc:reload"
content: |
{
"Cisco-IOS-XE-rpc:input": {
"force": true,
"reason": "Test"
}
}
EXPECTED RESULTS
The IOS-XE devices should reboot.
ACTUAL RESULTS
The IOS-XE devices do not reboot. Ansible shows an HTTP 405 error:
TASK [RESTCONF: Reboot] ***********************************************************************************************************************************************************************************************************************************
task path: /home/student01/module03-3/ansible/play_restconf_post_rpc.yml:15
<10.21.128.202> attempting to start connection
<10.21.128.202> using connection plugin ansible.netcommon.httpapi
Found ansible-connection at path /home/student01/module03-3/venv/bin/ansible-connection
<10.21.128.202> local domain socket does not exist, starting it
<10.21.128.202> control socket path is /home/student01/.ansible/pc/cbffa12376
<10.21.128.202> Loading collection ansible.netcommon from /home/student01/module03-3/venv/lib/python3.9/site-packages/ansible_collections/ansible/netcommon
<10.21.128.202> local domain socket listeners started successfully
<10.21.128.202> loaded API plugin ansible_collections.ansible.netcommon.plugins.httpapi.restconf from path /home/student01/module03-3/venv/lib/python3.9/site-packages/ansible_collections/ansible/netcommon/plugins/httpapi/restconf.py for platform type ansible.netcommon.restconf
<10.21.128.202>
<10.21.128.202> local domain socket path is /home/student01/.ansible/pc/cbffa12376
<10.21.128.202> ESTABLISH LOCAL CONNECTION FOR USER: student01
<10.21.128.202> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/student01/.ansible/tmp/ansible-local-335749i68q0if8 `"&& mkdir "` echo /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/ansible-tmp-1673590236.583306-335768-184237654076365 `" && echo ansible-tmp-1673590236.583306-335768-184237654076365="` echo /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/ansible-tmp-1673590236.583306-335768-184237654076365 `" ) && sleep 0'
Using module file /home/student01/module03-3/venv/lib/python3.9/site-packages/ansible_collections/ansible/netcommon/plugins/modules/restconf_config.py
<10.21.128.202> PUT /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/tmpjntyh1we TO /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/ansible-tmp-1673590236.583306-335768-184237654076365/AnsiballZ_restconf_config.py
<10.21.128.202> EXEC /bin/sh -c 'chmod u+x /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/ansible-tmp-1673590236.583306-335768-184237654076365/ /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/ansible-tmp-1673590236.583306-335768-184237654076365/AnsiballZ_restconf_config.py && sleep 0'
<10.21.128.202> EXEC /bin/sh -c '/home/student01/module03-3/venv/bin/python3 /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/ansible-tmp-1673590236.583306-335768-184237654076365/AnsiballZ_restconf_config.py && sleep 0'
<10.21.128.202> EXEC /bin/sh -c 'rm -f -r /home/student01/.ansible/tmp/ansible-local-335749i68q0if8/ansible-tmp-1673590236.583306-335768-184237654076365/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
File "/tmp/ansible_ansible.netcommon.restconf_config_payload_1e0lfg7y/ansible_ansible.netcommon.restconf_config_payload.zip/ansible_collections/ansible/netcommon/plugins/modules/restconf_config.py", line 172, in main
File "/tmp/ansible_ansible.netcommon.restconf_config_payload_1e0lfg7y/ansible_ansible.netcommon.restconf_config_payload.zip/ansible_collections/ansible/netcommon/plugins/module_utils/network/restconf/restconf.py", line 32, in get
return connection.send_request(
File "/tmp/ansible_ansible.netcommon.restconf_config_payload_1e0lfg7y/ansible_ansible.netcommon.restconf_config_payload.zip/ansible/module_utils/connection.py", line 200, in __rpc__
raise ConnectionError(to_text(msg, errors='surrogate_then_replace'), code=code)
fatal: [c8k_student01-1]: FAILED! => {
"changed": false,
"code": 405,
"invocation": {
"module_args": {
"content": "{\n \"Cisco-IOS-XE-rpc:input\": {\n \"force\": true,\n \"reason\": \"Test\"\n }\n}\n",
"format": "json",
"method": "post",
"path": "/Cisco-IOS-XE-rpc:reload"
}
},
"msg": "HTTP Error 405: Method Not Allowed"
}
PLAY RECAP ************************************************************************************************************************************************************************************************************************************************
c8k_student01-1 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
When using curl
everything works like expected.
Input file: rpc-reboot.json
{
"Cisco-IOS-XE-rpc:input": {
"force": true,
"reason": "Test"
}
}
curl operation:
$ curl -k -X POST -u student01 -H "Accept:application/yang-data+json" -H "Content-Type:application/yang-data+json" https://10.21.128.203/restconf/data/Cisco-IOS-XE-rpc:reload -d @rpc-reboot.json -v
Enter host password for user 'student01':
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 10.21.128.203:443...
* Connected to 10.21.128.203 (10.21.128.203) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: CN=IOS-Self-Signed-Certificate-4022925155
* start date: Jan 11 05:51:50 2023 GMT
* expire date: Jan 10 05:51:50 2033 GMT
* issuer: CN=IOS-Self-Signed-Certificate-4022925155
* SSL certificate verify result: self signed certificate (18), continuing anyway.
* Server auth using Basic with user 'student01'
> POST /restconf/data/Cisco-IOS-XE-rpc:reload HTTP/1.1
> Host: 10.21.128.203
> Authorization: Basic c3R1ZGVudDAxOjEyMzRRd2Vy
> User-Agent: curl/7.74.0
> Accept:application/yang-data+json
> Content-Type:application/yang-data+json
> Content-Length: 84
>
* upload completely sent off: 84 out of 84 bytes
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: openresty
< Date: Fri, 13 Jan 2023 05:28:05 GMT
< Content-Type: application/yang-data+json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
< Pragma: no-cache
< Content-Security-Policy: default-src 'self'; block-all-mixed-content; base-uri 'self'; frame-ancestors 'none';
< Strict-Transport-Security: max-age=15552000; includeSubDomains
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1; mode=block
<
{
"Cisco-IOS-XE-rpc:output": {
"result": "RPC request successful"
}
}
* Connection #0 to host 10.21.128.203 left intact
Can you rerun this with device interaction logging turned on and share the relevant section? The error is just what we get back from the remote server, but there's probably something strange happening such that the request is not what we expect it to be.
Hey @Qalthos , thank you for the reply. So I did that:
2023-01-31 07:09:05,905 p=26128 u=student01 n=ansible | PLAY [RESTCONF test playbook] ************************************************************************************************************************************************************
2023-01-31 07:09:06,048 p=26128 u=student01 n=ansible | TASK [RESTCONF: Reboot] ******************************************************************************************************************************************************************
2023-01-31 07:09:07,312 p=26169 u=student01 n=ansible | jsonrpc request: b'{"jsonrpc": "2.0", "method": "pop_messages", "id": "b8b20e23-d92d-43ff-8b1f-d0b71bb06a3b", "params": [[], {}]}'
2023-01-31 07:09:07,313 p=26169 u=student01 n=ansible | jsonrpc response: {"jsonrpc": "2.0", "id": "b8b20e23-d92d-43ff-8b1f-d0b71bb06a3b", "result_type": "pickle", "result": "(lp0\n(Vvvvv\np1\nVloaded API plugin ansible_collections.ansible.netcommon.plugins.httpapi.restconf from path /home/student01/module03-3/venv/lib/python3.9/site-packages/ansible_collections/ansible/netcommon/plugins/httpapi/restconf.py for platform type ansible.netcommon.restconf\np2\ntp3\na(Vlog\np4\nVplatform_type is set to ansible.netcommon.restconf\np5\ntp6\na(Vwarning\np7\nVPersistent connection logging is enabled for 10.21.128.202. This will log ALL interactions to /home/student01/module03-3/ansible/ansible.log and WILL NOT redact sensitive configuration like passwords. USE WITH CAUTION!\np8\ntp9\na."}
2023-01-31 07:09:07,329 p=26172 u=student01 n=ansible | jsonrpc request: b'{"jsonrpc": "2.0", "method": "pop_messages", "id": "8a83e873-1f78-42aa-a490-1d1267117d8d", "params": [[], {}]}'
2023-01-31 07:09:07,330 p=26172 u=student01 n=ansible | jsonrpc response: {"jsonrpc": "2.0", "id": "8a83e873-1f78-42aa-a490-1d1267117d8d", "result_type": "pickle", "result": "(lp0\n(Vvvvv\np1\nVloaded API plugin ansible_collections.ansible.netcommon.plugins.httpapi.restconf from path /home/student01/module03-3/venv/lib/python3.9/site-packages/ansible_collections/ansible/netcommon/plugins/httpapi/restconf.py for platform type ansible.netcommon.restconf\np2\ntp3\na(Vlog\np4\nVplatform_type is set to ansible.netcommon.restconf\np5\ntp6\na(Vwarning\np7\nVPersistent connection logging is enabled for 10.21.128.203. This will log ALL interactions to /home/student01/module03-3/ansible/ansible.log and WILL NOT redact sensitive configuration like passwords. USE WITH CAUTION!\np8\ntp9\na."}
2023-01-31 07:09:07,398 p=26128 u=student01 n=ansible | platform_type is set to ansible.netcommon.restconf
2023-01-31 07:09:07,400 p=26128 u=student01 n=ansible | [WARNING]: Persistent connection logging is enabled for 10.21.128.202. This will log ALL interactions to /home/student01/module03-3/ansible/ansible.log and WILL NOT redact sensitive
configuration like passwords. USE WITH CAUTION!
2023-01-31 07:09:07,423 p=26128 u=student01 n=ansible | platform_type is set to ansible.netcommon.restconf
2023-01-31 07:09:07,424 p=26128 u=student01 n=ansible | [WARNING]: Persistent connection logging is enabled for 10.21.128.203. This will log ALL interactions to /home/student01/module03-3/ansible/ansible.log and WILL NOT redact sensitive
configuration like passwords. USE WITH CAUTION!
2023-01-31 07:09:08,137 p=26169 u=student01 n=ansible | jsonrpc request: b'{"jsonrpc": "2.0", "method": "send_request", "id": "d04475c8-dbc2-4883-ac86-d9bd62fb6eb5", "params": [[null], {"path": "/Cisco-IOS-XE-rpc:reload", "method": "GET", "accept": null}]}'
2023-01-31 07:09:08,158 p=26172 u=student01 n=ansible | jsonrpc request: b'{"jsonrpc": "2.0", "method": "send_request", "id": "0458c432-99a9-4763-bcec-90d86d5b0198", "params": [[null], {"path": "/Cisco-IOS-XE-rpc:reload", "method": "GET", "accept": null}]}'
2023-01-31 07:09:08,463 p=26172 u=student01 n=ansible | jsonrpc response: {"jsonrpc": "2.0", "id": "0458c432-99a9-4763-bcec-90d86d5b0198", "error": {"code": 405, "message": "HTTP Error 405: Method Not Allowed"}}
2023-01-31 07:09:08,537 p=26128 u=student01 n=ansible | fatal: [c8k_student01-2]: FAILED! => {"changed": false, "code": 405, "msg": "HTTP Error 405: Method Not Allowed"}
2023-01-31 07:09:08,816 p=26169 u=student01 n=ansible | jsonrpc response: {"jsonrpc": "2.0", "id": "d04475c8-dbc2-4883-ac86-d9bd62fb6eb5", "error": {"code": 405, "message": "HTTP Error 405: Method Not Allowed"}}
2023-01-31 07:09:08,889 p=26128 u=student01 n=ansible | fatal: [c8k_student01-1]: FAILED! => {"changed": false, "code": 405, "msg": "HTTP Error 405: Method Not Allowed"}
2023-01-31 07:09:08,891 p=26172 u=student01 n=ansible | jsonrpc request: b'{"jsonrpc": "2.0", "method": "reset", "id": "e41c5a30-fc27-4b71-94af-a88f669feb1d", "params": [[], {}]}'
2023-01-31 07:09:08,892 p=26172 u=student01 n=ansible | jsonrpc response: {"jsonrpc": "2.0", "id": "e41c5a30-fc27-4b71-94af-a88f669feb1d", "result_type": "pickle", "result": "N."}
2023-01-31 07:09:08,893 p=26169 u=student01 n=ansible | jsonrpc request: b'{"jsonrpc": "2.0", "method": "reset", "id": "ca671859-0a49-4468-882c-cf06b8187ede", "params": [[], {}]}'
2023-01-31 07:09:08,894 p=26169 u=student01 n=ansible | jsonrpc response: {"jsonrpc": "2.0", "id": "ca671859-0a49-4468-882c-cf06b8187ede", "result_type": "pickle", "result": "N."}
2023-01-31 07:09:08,897 p=26128 u=student01 n=ansible | PLAY RECAP *******************************************************************************************************************************************************************************
2023-01-31 07:09:08,897 p=26128 u=student01 n=ansible | c8k_student01-1 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
2023-01-31 07:09:08,898 p=26128 u=student01 n=ansible | c8k_student01-2 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
2023-01-31 07:09:08,994 p=26172 u=student01 n=ansible | ESTABLISH HTTP(S) CONNECTFOR USER: student01 TO https://10.21.128.203:443
2023-01-31 07:09:08,994 p=26172 u=student01 n=ansible | send url 'https://10.21.128.203:443/restconf/data/Cisco-IOS-XE-rpc:reload' with data 'None' and kwargs '{'timeout': 30, 'validate_certs': False, 'use_proxy': True, 'headers': {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'}, 'method': 'GET', 'force_basic_auth': True, 'url_username': 'student01', 'url_password': 'automate2023'}'
2023-01-31 07:09:08,994 p=26172 u=student01 n=ansible | received response: 'b'''
2023-01-31 07:09:08,995 p=26172 u=student01 n=ansible | resetting persistent connection for socket_path /home/student01/.ansible/pc/f2d2969f3a
2023-01-31 07:09:08,995 p=26172 u=student01 n=ansible | closing http(s) connection to device
2023-01-31 07:09:08,995 p=26169 u=student01 n=ansible | ESTABLISH HTTP(S) CONNECTFOR USER: student01 TO https://10.21.128.202:443
2023-01-31 07:09:08,995 p=26169 u=student01 n=ansible | send url 'https://10.21.128.202:443/restconf/data/Cisco-IOS-XE-rpc:reload' with data 'None' and kwargs '{'timeout': 30, 'validate_certs': False, 'use_proxy': True, 'headers': {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'}, 'method': 'GET', 'force_basic_auth': True, 'url_username': 'student01', 'url_password': 'automate2023'}'
2023-01-31 07:09:08,995 p=26172 u=student01 n=ansible | reset call on connection instance
2023-01-31 07:09:08,996 p=26169 u=student01 n=ansible | received response: 'b'''
2023-01-31 07:09:08,996 p=26169 u=student01 n=ansible | resetting persistent connection for socket_path /home/student01/.ansible/pc/96e0c36405
2023-01-31 07:09:08,996 p=26169 u=student01 n=ansible | closing http(s) connection to device
2023-01-31 07:09:08,996 p=26169 u=student01 n=ansible | reset call on connection instance
2023-01-31 07:09:08,996 p=26172 u=student01 n=ansible | shutdown complete
2023-01-31 07:09:08,997 p=26169 u=student01 n=ansible | shutdown complete
So it seems like, that the module is trying to do a GET
instead of the desired POST