From f06d9080eb94950b9b90973b31efa03f50a6e06e Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 6 Aug 2025 20:13:11 +0530 Subject: [PATCH 1/2] Add support for body-data encoding type 'x-www-form-urlencoded' --- docs/cisco.dcnm.dcnm_rest_module.rst | 21 ++++++++++++++++ plugins/httpapi/dcnm.py | 5 ++++ plugins/module_utils/network/dcnm/dcnm.py | 2 ++ plugins/modules/dcnm_rest.py | 30 ++++++++++++++++++++--- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/docs/cisco.dcnm.dcnm_rest_module.rst b/docs/cisco.dcnm.dcnm_rest_module.rst index cf392f941..a0cd16b62 100644 --- a/docs/cisco.dcnm.dcnm_rest_module.rst +++ b/docs/cisco.dcnm.dcnm_rest_module.rst @@ -87,6 +87,21 @@ Parameters
REST API Path Endpoint
+ + +
+ urlencoded_data + +
+ raw +
+ + + + +
Dictionary data to be url-encoded for x-www-form-urlencoded type REST API call
+ +
@@ -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') }}" diff --git a/plugins/httpapi/dcnm.py b/plugins/httpapi/dcnm.py index e0b702795..be4c9e633 100644 --- a/plugins/httpapi/dcnm.py +++ b/plugins/httpapi/dcnm.py @@ -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 @@ -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) diff --git a/plugins/module_utils/network/dcnm/dcnm.py b/plugins/module_utils/network/dcnm/dcnm.py index 272c1664e..30234d1e4 100644 --- a/plugins/module_utils/network/dcnm/dcnm.py +++ b/plugins/module_utils/network/dcnm/dcnm.py @@ -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) diff --git a/plugins/modules/dcnm_rest.py b/plugins/modules/dcnm_rest.py index 103109c70..6d4b640b1 100644 --- a/plugins/modules/dcnm_rest.py +++ b/plugins/modules/dcnm_rest.py @@ -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) """ @@ -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') }}" @@ -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, @@ -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 @@ -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") From 499dd4a2d74bd6da0a2fe1a9e2306daa931bfade Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 6 Aug 2025 22:07:57 +0530 Subject: [PATCH 2/2] Fix Pep8 Error --- plugins/modules/dcnm_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dcnm_rest.py b/plugins/modules/dcnm_rest.py index 6d4b640b1..d1895f4de 100644 --- a/plugins/modules/dcnm_rest.py +++ b/plugins/modules/dcnm_rest.py @@ -113,7 +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), + urlencoded_data=dict(type="raw", required=False, default=None), ) # seed the result dict