Skip to content

DCNM_REST: Adding support for Encoding Type 'x-www-form-urlencoded' #490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/cisco.dcnm.dcnm_rest_module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ Parameters
<div>REST API Path Endpoint</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>urlencoded_data</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">raw</span>
</div>
</td>
<td>
</td>
<td>
<div>Dictionary data to be url-encoded for x-www-form-urlencoded type REST API call</div>
</td>
</tr>
</table>
<br/>

Expand Down Expand Up @@ -115,6 +130,12 @@ Examples
path: /rest/top-down/fabrics/fabric1/vrfs/attachments
json_data: '[{"vrfName":"sales66_vrf1","lanAttachList":[{"fabric":"fabric1","vrfName":"sales66_vrf1","serialNumber":"FDO21392QKM","vlan":2000,"freeformConfig":"","deployment":false,"extensionValues":"","instanceValues":"{"loopbackId":"","loopbackIpAddress":"","loopbackIpV6Address":""}"}]}]'

- name: Save Robot Credentials - (urlencoded)
dcnm_rest:
method: POST
path: /rest/lanConfig/saveRobotCredentials
urlencoded_data: '{"password": "password", "username": "admin"}'

# Read payload data from file and validate a template
- set_fact:
data: "{{ lookup('file', 'validate_payload') }}"
Expand Down
5 changes: 5 additions & 0 deletions plugins/httpapi/dcnm.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class HttpApi(HttpApiBase):
def __init__(self, *args, **kwargs):
super(HttpApi, self).__init__(*args, **kwargs)
self.headers = {"Content-Type": "application/json"}
self.urlencoded_headers = {"Content-Type": "application/x-www-form-urlencoded"}
self.txt_headers = {"Content-Type": "text/plain"}
self.version = None
self.retrycount = DEFAULT_RETRY_COUNT
Expand Down Expand Up @@ -252,6 +253,10 @@ def send_request(self, method, path, json=None):
"""This method handles all DCNM REST API requests other than login"""
return self._send_request_internal(method, path, json or {}, self.headers)

def send_urlencoded_request(self, method, path, urlencoded=None):
"""This method handles all DCNM REST API urlencoded requests other than login"""
return self._send_request_internal(method, path, urlencoded or {}, self.urlencoded_headers)

def send_txt_request(self, method, path, txt=None):
"""This method handles all DCNM REST API text requests other than login"""
return self._send_request_internal(method, path, txt or "", self.txt_headers)
Expand Down
2 changes: 2 additions & 0 deletions plugins/module_utils/network/dcnm/dcnm.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ def dcnm_send(module, method, path, data=None, data_type="json"):

if data_type == "json":
return conn.send_request(method, path, data)
elif data_type == "urlencoded":
return conn.send_urlencoded_request(method, path, data)
elif data_type == "text":
return conn.send_txt_request(method, path, data)

Expand Down
30 changes: 27 additions & 3 deletions plugins/modules/dcnm_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
- json_data
required: no
type: raw
urlencoded_data:
description:
- 'Dictionary data to be url-encoded for x-www-form-urlencoded type REST API call'
required: no
type: raw
author:
- Mike Wiebe (@mikewiebe)
"""
Expand All @@ -66,6 +71,12 @@
path: /rest/top-down/fabrics/fabric1/vrfs/attachments
json_data: '[{"vrfName":"sales66_vrf1","lanAttachList":[{"fabric":"fabric1","vrfName":"sales66_vrf1","serialNumber":"FDO21392QKM","vlan":2000,"freeformConfig":"","deployment":false,"extensionValues":"","instanceValues":"{\"loopbackId\":\"\",\"loopbackIpAddress\":\"\",\"loopbackIpV6Address\":\"\"}"}]}]'

- name: Save Robot Credentials - (urlencoded)
dcnm_rest:
method: POST
path: /rest/lanConfig/saveRobotCredentials
urlencoded_data: '{"password": "password", "username": "admin"}'

# Read payload data from file and validate a template
- set_fact:
data: "{{ lookup('file', 'validate_payload') }}"
Expand All @@ -89,6 +100,7 @@
"""

import json
import urllib.parse
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.dcnm.plugins.module_utils.network.dcnm.dcnm import (
dcnm_send,
Expand All @@ -101,6 +113,7 @@ def main():
method=dict(required=True, choices=["GET", "POST", "PUT", "DELETE"]),
path=dict(required=True, type="str"),
data=dict(type="raw", required=False, default=None, aliases=["json_data"]),
urlencoded_data=dict(type="raw", required=False, default=None),
)

# seed the result dict
Expand All @@ -110,17 +123,28 @@ def main():

method = module.params["method"]
path = module.params["path"]
for key in ["json_data", "data"]:
is_urlencoded = False

for key in ["json_data", "data", "urlencoded_data"]:
data = module.params.get(key)
if data is not None:
if key == "urlencoded_data":
is_urlencoded = True
break
if data is None:
data = "{}"

# Determine if this is valid JSON or not
try:
json.loads(data)
result["response"] = dcnm_send(module, method, path, data)
json_data = json.loads(data)
if is_urlencoded:
# If the data is valid JSON but marked as urlencoded, we need to convert it
# to a URL-encoded string before sending it.
urlencoded_data = urllib.parse.urlencode(json_data)
result["response"] = dcnm_send(module, method, path, urlencoded_data, "urlencoded")
else:
# If the data is valid JSON, send it as a JSON string
result["response"] = dcnm_send(module, method, path, data)
except json.JSONDecodeError:
# Resend data as text since it's not valid JSON
result["response"] = dcnm_send(module, method, path, data, "text")
Expand Down