Skip to content

Commit cfa447f

Browse files
authored
[minor_change] Add support for XML file upload pre-change in nd_pcv (DCNE-150) (CiscoDevNet#77)
1 parent b2932d7 commit cfa447f

File tree

10 files changed

+362
-152
lines changed

10 files changed

+362
-152
lines changed

plugins/httpapi/nd.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,12 @@ def send_request(self, method, path, data=None):
223223
raise ConnectionError(json.dumps(self._verify_response(None, method, full_path, None)))
224224
return self._verify_response(response, method, full_path, rdata)
225225

226-
def send_file_request(self, method, path, file=None, data=None, remote_path=None, file_key="file"):
226+
def send_file_request(self, method, path, file=None, data=None, remote_path=None, file_key="file", file_ext=None):
227227
"""This method handles file download and upload operations
228228
:arg method (str): Method can be GET or POST
229229
:arg path (str): Path should be the resource path
230230
:arg file (str): The absolute file path of the target file
231+
:arg file_ext (str): The file extension, with leading dot, to be used for the file. If file has already an extension, it will be replaced
231232
:arg data (dict): Data should be the dictionary object
232233
:arg remote_path (str): Remote directory path to download/upload the file object
233234
@@ -255,6 +256,14 @@ def send_file_request(self, method, path, file=None, data=None, remote_path=None
255256
if method is not None:
256257
self.method = method
257258

259+
# If file_ext is provided, replace the file extension (if present) or add it
260+
if file_ext is not None:
261+
if not file_ext.startswith(".") or file_ext not in set(mimetypes.types_map.keys()):
262+
raise ValueError("Invalid file extension provided. Please provide a valid file extension, with leading dot")
263+
filename = os.path.splitext(os.path.basename(file))[0] + file_ext
264+
else:
265+
filename = os.path.basename(file)
266+
258267
try:
259268
# create data field
260269
data["uploadedFileName"] = os.path.basename(file)
@@ -267,11 +276,11 @@ def send_file_request(self, method, path, file=None, data=None, remote_path=None
267276
try:
268277
# create fields for MultipartEncoder
269278
if remote_path:
270-
fields = dict(rdir=remote_path, name=(os.path.basename(file), open(file, "rb"), mimetypes.guess_type(file)))
279+
fields = dict(rdir=remote_path, name=(filename, open(file, "rb"), mimetypes.guess_type(filename)))
271280
elif file_key == "importfile":
272-
fields = dict(spec=(json.dumps(data)), importfile=(os.path.basename(file), open(file, "rb"), mimetypes.guess_type(file)))
281+
fields = dict(spec=(json.dumps(data)), importfile=(filename, open(file, "rb"), mimetypes.guess_type(filename)))
273282
else:
274-
fields = dict(data=("data.json", data_str, "application/json"), file=(os.path.basename(file), open(file, "rb"), mimetypes.guess_type(file)))
283+
fields = dict(data=("data.json", data_str, "application/json"), file=(filename, open(file, "rb"), mimetypes.guess_type(filename)))
275284

276285
if not HAS_MULTIPART_ENCODER:
277286
if sys.version_info.major == 2:

plugins/module_utils/nd.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,9 @@ def __init__(self, module):
218218
self.module.warn("Enable debug output because ANSIBLE_DEBUG was set.")
219219
self.params["output_level"] = "debug"
220220

221-
def request(self, path, method=None, data=None, file=None, qs=None, prefix="", file_key="file", output_format="json", ignore_not_found_error=False):
221+
def request(
222+
self, path, method=None, data=None, file=None, qs=None, prefix="", file_key="file", output_format="json", ignore_not_found_error=False, file_ext=None
223+
):
222224
"""Generic HTTP method for ND requests."""
223225
self.path = path
224226

@@ -238,7 +240,7 @@ def request(self, path, method=None, data=None, file=None, qs=None, prefix="", f
238240
uri = uri + update_qs(qs)
239241
try:
240242
if file is not None:
241-
info = conn.send_file_request(method, uri, file, data, None, file_key)
243+
info = conn.send_file_request(method, uri, file, data, None, file_key, file_ext)
242244
else:
243245
if data:
244246
info = conn.send_request(method, uri, json.dumps(data))

plugins/module_utils/ndi.py

+11
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,17 @@ def is_json(self, myjson):
307307
return False
308308
return True
309309

310+
def is_xml(self, myxml):
311+
try:
312+
from lxml import etree
313+
314+
etree.parse(myxml)
315+
except ImportError:
316+
self.nd.fail_json(msg="Cannot use lxml etree because lxml module is not available")
317+
except etree.XMLSyntaxError:
318+
return False
319+
return True
320+
310321
def load(self, fh, chunk_size=1024):
311322
depth = 0
312323
in_str = False

plugins/modules/nd_pcv.py

+21-9
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
file:
4545
description:
4646
- Optional parameter if creating new pre-change analysis from file.
47+
- XML and JSON files are supported. If no file extension is provided, the file is assumed to be JSON.
4748
type: str
4849
manual:
4950
description:
@@ -216,17 +217,28 @@ def main():
216217
if file:
217218
if not os.path.exists(file):
218219
nd.fail_json(msg="File not found : {0}".format(file))
219-
# check whether file content is a valid json
220-
if ndi.is_json(open(file, "rb").read()) is False:
221-
extract_data = ndi.load(open(file))
220+
# Check whether the file is a valid XML file. If it's not, check if it's a valid JSON or else process it as a file from cisco.aci modules.
221+
if ndi.is_xml(open(file, "rb")):
222+
file_ext = ".xml"
222223
else:
223-
extract_data = json.loads(open(file, "rb").read())
224-
if isinstance(extract_data, list):
225-
ndi.cmap = {}
226-
tree = ndi.construct_tree(extract_data)
227-
ndi.create_structured_data(tree, file)
224+
if ndi.is_json(open(file, "rb").read()):
225+
file_ext = ".json"
226+
extract_data = json.loads(open(file, "rb").read())
227+
else:
228+
try:
229+
file_ext = ".json"
230+
extract_data = ndi.load(open(file))
231+
except BaseException:
232+
nd.fail_json(msg="Error processing the file. Check if file content is valid.")
233+
234+
if isinstance(extract_data, list):
235+
ndi.cmap = {}
236+
tree = ndi.construct_tree(extract_data)
237+
ndi.create_structured_data(tree, file)
238+
239+
# Send REST API request to create a new PCV job
228240
create_pcv_path = "{0}/{1}/fabric/{2}/prechangeAnalysis/fileChanges".format(path, insights_group, site_name)
229-
file_resp = nd.request(create_pcv_path, method="POST", file=os.path.abspath(file), data=data, prefix=ndi.prefix)
241+
file_resp = nd.request(create_pcv_path, method="POST", file=os.path.abspath(file), file_ext=file_ext, data=data, prefix=ndi.prefix)
230242
if file_resp.get("success") is True:
231243
nd.existing = file_resp.get("value")["data"]
232244
elif manual:

requirements.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
requests_toolbelt
2-
jsonpath-ng
2+
jsonpath-ng
3+
lxml
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
requests_toolbelt
2-
jsonpath-ng
2+
jsonpath-ng
3+
lxml

0 commit comments

Comments
 (0)