From 43e77543d914f59245b198531cf6d6c352d98d2e Mon Sep 17 00:00:00 2001 From: Ashwini Mhatre Date: Fri, 9 Apr 2021 18:50:17 +0530 Subject: [PATCH] Add to_xml and from_xml filter plugin (#56) Add to_xml and from_xml filter plugin Reviewed-by: https://github.com/apps/ansible-zuul --- README.md | 2 + .../add-xmltojson-and-jsontoxml-filter.yaml | 3 + docs/ansible.utils.from_xml_filter.rst | 173 ++++++++++++++++++ docs/ansible.utils.to_xml_filter.rst | 166 +++++++++++++++++ plugins/filter/from_xml.py | 145 +++++++++++++++ plugins/filter/to_xml.py | 138 ++++++++++++++ plugins/plugin_utils/from_xml.py | 52 ++++++ plugins/plugin_utils/to_xml.py | 53 ++++++ .../from_xml/tasks/include/simple.yaml | 39 ++++ .../targets/from_xml/tasks/main.yaml | 13 ++ .../targets/to_xml/tasks/include/simple.yaml | 35 ++++ .../targets/to_xml/tasks/main.yaml | 13 ++ tests/unit/plugins/filter/test_from_xml.py | 49 +++++ tests/unit/plugins/filter/test_to_xml.py | 50 +++++ 14 files changed, 931 insertions(+) create mode 100644 changelogs/fragments/add-xmltojson-and-jsontoxml-filter.yaml create mode 100644 docs/ansible.utils.from_xml_filter.rst create mode 100644 docs/ansible.utils.to_xml_filter.rst create mode 100644 plugins/filter/from_xml.py create mode 100644 plugins/filter/to_xml.py create mode 100644 plugins/plugin_utils/from_xml.py create mode 100644 plugins/plugin_utils/to_xml.py create mode 100644 tests/integration/targets/from_xml/tasks/include/simple.yaml create mode 100644 tests/integration/targets/from_xml/tasks/main.yaml create mode 100644 tests/integration/targets/to_xml/tasks/include/simple.yaml create mode 100644 tests/integration/targets/to_xml/tasks/main.yaml create mode 100644 tests/unit/plugins/filter/test_from_xml.py create mode 100644 tests/unit/plugins/filter/test_to_xml.py diff --git a/README.md b/README.md index 45001d5..f67ddfe 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,11 @@ PEP440 is the schema used to describe the versions of Ansible. ### Filter plugins Name | Description --- | --- +[ansible.utils.from_xml](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.from_xml_filter.rst)|Convert given XML string to native python dictionary. [ansible.utils.get_path](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.get_path_filter.rst)|Retrieve the value in a variable using a path [ansible.utils.index_of](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.index_of_filter.rst)|Find the indices of items in a list matching some criteria [ansible.utils.to_paths](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.to_paths_filter.rst)|Flatten a complex object into a dictionary of paths and values +[ansible.utils.to_xml](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.to_xml_filter.rst)|Convert given JSON string to XML [ansible.utils.validate](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.validate_filter.rst)|Validate data with provided criteria ### Lookup plugins diff --git a/changelogs/fragments/add-xmltojson-and-jsontoxml-filter.yaml b/changelogs/fragments/add-xmltojson-and-jsontoxml-filter.yaml new file mode 100644 index 0000000..556fde8 --- /dev/null +++ b/changelogs/fragments/add-xmltojson-and-jsontoxml-filter.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Add from_xml and to_xml fiter plugin (https://github.com/ansible-collections/ansible.utils/pull/56). diff --git a/docs/ansible.utils.from_xml_filter.rst b/docs/ansible.utils.from_xml_filter.rst new file mode 100644 index 0000000..d1d43d4 --- /dev/null +++ b/docs/ansible.utils.from_xml_filter.rst @@ -0,0 +1,173 @@ +.. _ansible.utils.from_xml_filter: + + +********************** +ansible.utils.from_xml +********************** + +**Convert given XML string to native python dictionary.** + + +Version added: 2.0.2 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This plugin converts the XML string to a native python dictionary. +- Using the parameters below- ``data|ansible.utils.from_xml`` + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsConfigurationComments
+
+ data + +
+ string + / required +
+
+ + +
The input XML string.
+
This option represents the XML value that is passed to the filter plugin in pipe format.
+
For example config_data|ansible.utils.from_xml, in this case config_data represents this option.
+
+
+ engine + +
+ string +
+
+ Default:
"xmltodict"
+
+ +
Conversion library to use within the filter plugin.
+
+
+ + + + +Examples +-------- + +.. code-block:: yaml + + #### Simple examples with out any engine. plugin will use default value as xmltodict + + tasks: + - name: convert given XML to native python dictionary + ansible.builtin.set_fact: + data: " + + " + + - debug: + msg: "{{ data|ansible.utils.from_xml }}" + + ##TASK###### + # TASK [convert given XML to native python dictionary] ***************************************************************************************************** + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:5 + # ok: [localhost] => { + # "ansible_facts": { + # "data": " " + # }, + # "changed": false + # } + # + # TASK [debug] ************************************************************************************************************************* + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:13 + # Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils + # ok: [localhost] => { + # "msg": { + # "netconf-state": { + # "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", + # "schemas": { + # "schema": null + # } + # } + # } + # } + + #### example2 with engine=xmltodict + + tasks: + - name: convert given XML to native python dictionary + ansible.builtin.set_fact: + data: " + + " + + - debug: + msg: "{{ data|ansible.utils.from_xml('xmltodict') }}" + + ##TASK###### + # TASK [convert given XML to native python dictionary] ***************************************************************************************************** + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:5 + # ok: [localhost] => { + # "ansible_facts": { + # "data": " " + # }, + # "changed": false + # } + # + # TASK [debug] ************************************************************************************************************************* + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:13 + # Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils + # ok: [localhost] => { + # "msg": { + # "netconf-state": { + # "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", + # "schemas": { + # "schema": null + # } + # } + # } + # } + + + + +Status +------ + + +Authors +~~~~~~~ + +- Ashwini Mhatre (@amhatre) + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/docs/ansible.utils.to_xml_filter.rst b/docs/ansible.utils.to_xml_filter.rst new file mode 100644 index 0000000..40d3490 --- /dev/null +++ b/docs/ansible.utils.to_xml_filter.rst @@ -0,0 +1,166 @@ +.. _ansible.utils.to_xml_filter: + + +******************** +ansible.utils.to_xml +******************** + +**Convert given JSON string to XML** + + +Version added: 2.0.2 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This plugin converts the JSON string to XML. +- Using the parameters below- ``data|ansible.utils.to_xml`` + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsConfigurationComments
+
+ data + +
+ dictionary + / required +
+
+ + +
The input JSON string .
+
This option represents the JSON value that is passed to the filter plugin in pipe format.
+
For example config_data|ansible.utils.to_xml, in this case config_data represents this option.
+
+
+ engine + +
+ string +
+
+ Default:
"xmltodict"
+
+ +
Conversion library to use within the filter plugin.
+
+
+ + + + +Examples +-------- + +.. code-block:: yaml + + #### Simple examples with out any engine. plugin will use default value as xmltodict + + - name: Define JSON data + ansible.builtin.set_fact: + data: + "interface-configurations": + "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + "interface-configuration": + - debug: + msg: "{{ data|ansible.utils.to_xml }}" + + # TASK [Define JSON data ] ************************************************************************* + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:5 + # ok: [localhost] => { + # "ansible_facts": { + # "data": { + # "interface-configurations": { + # "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg", + # "interface-configuration": null + # } + # } + # }, + # "changed": false + # } + # + # TASK [debug] *********************************************************************************************************** + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:13 + # Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils + # ok: [localhost] => { + # "msg": "\n\n\t\n" + # } + + #### example2 with engine=xmltodict + + - name: Define JSON data + ansible.builtin.set_fact: + data: + "interface-configurations": + "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + "interface-configuration": + - debug: + msg: "{{ data|ansible.utils.to_xml('xmltodict') }}" + + # TASK [Define JSON data ] ************************************************************************* + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:5 + # ok: [localhost] => { + # "ansible_facts": { + # "data": { + # "interface-configurations": { + # "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg", + # "interface-configuration": null + # } + # } + # }, + # "changed": false + # } + # TASK [debug] *********************************************************************************************************** + # task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:13 + # Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils + # ok: [localhost] => { + # "msg": "\n\n\t\n" + # } + + + + +Status +------ + + +Authors +~~~~~~~ + +- Ashwini Mhatre (@amhatre) + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/plugins/filter/from_xml.py b/plugins/filter/from_xml.py new file mode 100644 index 0000000..d2f8b70 --- /dev/null +++ b/plugins/filter/from_xml.py @@ -0,0 +1,145 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +""" +The from_xml filter plugin +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ + name: from_xml + author: Ashwini Mhatre (@amhatre) + version_added: "2.0.2" + short_description: Convert given XML string to native python dictionary. + description: + - This plugin converts the XML string to a native python dictionary. + - Using the parameters below- C(data|ansible.utils.from_xml) + options: + data: + description: + - The input XML string. + - This option represents the XML value that is passed to the filter plugin in pipe format. + - For example C(config_data|ansible.utils.from_xml), in this case C(config_data) represents this option. + type: str + required: True + engine: + description: + - Conversion library to use within the filter plugin. + type: str + default: xmltodict +""" + +EXAMPLES = r""" + +#### Simple examples with out any engine. plugin will use default value as xmltodict + +tasks: + - name: convert given XML to native python dictionary + ansible.builtin.set_fact: + data: " + + " + + - debug: + msg: "{{ data|ansible.utils.from_xml }}" + +##TASK###### +# TASK [convert given XML to native python dictionary] ***************************************************************************************************** +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:5 +# ok: [localhost] => { +# "ansible_facts": { +# "data": " " +# }, +# "changed": false +# } +# +# TASK [debug] ************************************************************************************************************************* +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:13 +# Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils +# ok: [localhost] => { +# "msg": { +# "netconf-state": { +# "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", +# "schemas": { +# "schema": null +# } +# } +# } +# } + +#### example2 with engine=xmltodict + +tasks: + - name: convert given XML to native python dictionary + ansible.builtin.set_fact: + data: " + + " + + - debug: + msg: "{{ data|ansible.utils.from_xml('xmltodict') }}" + +##TASK###### +# TASK [convert given XML to native python dictionary] ***************************************************************************************************** +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:5 +# ok: [localhost] => { +# "ansible_facts": { +# "data": " " +# }, +# "changed": false +# } +# +# TASK [debug] ************************************************************************************************************************* +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils.yaml:13 +# Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils +# ok: [localhost] => { +# "msg": { +# "netconf-state": { +# "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", +# "schemas": { +# "schema": null +# } +# } +# } +# } +""" + +from ansible.errors import AnsibleFilterError +from jinja2.filters import environmentfilter +from ansible_collections.ansible.utils.plugins.plugin_utils.from_xml import ( + from_xml, +) +from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import ( + AnsibleArgSpecValidator, +) + + +@environmentfilter +def _from_xml(*args, **kwargs): + """Convert the given data from xml to json.""" + + keys = ["data", "engine"] + data = dict(zip(keys, args[1:])) + data.update(kwargs) + aav = AnsibleArgSpecValidator( + data=data, schema=DOCUMENTATION, name="from_xml" + ) + valid, errors, updated_data = aav.validate() + if not valid: + raise AnsibleFilterError(errors) + return from_xml(**updated_data) + + +class FilterModule(object): + """ from_xml """ + + def filters(self): + + """a mapping of filter names to functions""" + return {"from_xml": _from_xml} diff --git a/plugins/filter/to_xml.py b/plugins/filter/to_xml.py new file mode 100644 index 0000000..5e27aef --- /dev/null +++ b/plugins/filter/to_xml.py @@ -0,0 +1,138 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + + +""" +The to_xml filter plugin +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ + name: to_xml + author: Ashwini Mhatre (@amhatre) + version_added: "2.0.2" + short_description: Convert given JSON string to XML + description: + - This plugin converts the JSON string to XML. + - Using the parameters below- C(data|ansible.utils.to_xml) + options: + data: + description: + - The input JSON string . + - This option represents the JSON value that is passed to the filter plugin in pipe format. + - For example C(config_data|ansible.utils.to_xml), in this case C(config_data) represents this option. + type: dict + required: True + engine: + description: + - Conversion library to use within the filter plugin. + type: str + default: xmltodict +""" + +EXAMPLES = r""" + +#### Simple examples with out any engine. plugin will use default value as xmltodict + +- name: Define JSON data + ansible.builtin.set_fact: + data: + "interface-configurations": + "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + "interface-configuration": +- debug: + msg: "{{ data|ansible.utils.to_xml }}" + +# TASK [Define JSON data ] ************************************************************************* +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:5 +# ok: [localhost] => { +# "ansible_facts": { +# "data": { +# "interface-configurations": { +# "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg", +# "interface-configuration": null +# } +# } +# }, +# "changed": false +# } +# +# TASK [debug] *********************************************************************************************************** +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:13 +# Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils +# ok: [localhost] => { +# "msg": "\n\n\t\n" +# } + +#### example2 with engine=xmltodict + +- name: Define JSON data + ansible.builtin.set_fact: + data: + "interface-configurations": + "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + "interface-configuration": +- debug: + msg: "{{ data|ansible.utils.to_xml('xmltodict') }}" + +# TASK [Define JSON data ] ************************************************************************* +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:5 +# ok: [localhost] => { +# "ansible_facts": { +# "data": { +# "interface-configurations": { +# "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg", +# "interface-configuration": null +# } +# } +# }, +# "changed": false +# } +# TASK [debug] *********************************************************************************************************** +# task path: /Users/amhatre/ansible-collections/playbooks/test_utils_json_to_xml.yaml:13 +# Loading collection ansible.utils from /Users/amhatre/ansible-collections/collections/ansible_collections/ansible/utils +# ok: [localhost] => { +# "msg": "\n\n\t\n" +# } + +""" + +from ansible.errors import AnsibleFilterError +from jinja2.filters import environmentfilter +from ansible_collections.ansible.utils.plugins.plugin_utils.to_xml import ( + to_xml, +) +from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import ( + AnsibleArgSpecValidator, +) + + +@environmentfilter +def _to_xml(*args, **kwargs): + """Convert the given data from json to xml.""" + keys = ["data", "engine"] + data = dict(zip(keys, args[1:])) + data.update(kwargs) + aav = AnsibleArgSpecValidator( + data=data, schema=DOCUMENTATION, name="to_xml" + ) + valid, errors, updated_data = aav.validate() + if not valid: + raise AnsibleFilterError(errors) + return to_xml(**updated_data) + + +class FilterModule(object): + """ to_xml """ + + def filters(self): + """a mapping of filter names to functions""" + return {"to_xml": _to_xml} diff --git a/plugins/plugin_utils/from_xml.py b/plugins/plugin_utils/from_xml.py new file mode 100644 index 0000000..c8578b4 --- /dev/null +++ b/plugins/plugin_utils/from_xml.py @@ -0,0 +1,52 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +""" +The from_xml plugin code +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import json +from ansible.errors import AnsibleFilterError + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +def _raise_error(msg): + """Raise an error message, prepend with filter name + :param msg: The message + :type msg: str + :raises: AnsibleError + """ + error = "Error when using plugin 'from_xml': {msg}".format(msg=msg) + raise AnsibleFilterError(error) + + +def from_xml(data, engine): + """Convert data which is in xml to json" + :param data: The data passed in (data|from_xml(...)) + :type data: xml + :param engine: Conversion library default=xml_to_dict + """ + if engine == "xmltodict": + if not HAS_XMLTODICT: + _raise_error("Missing required library xmltodict") + try: + res = json.dumps(xmltodict.parse(data)) + except Exception: + _raise_error("Input Xml is not valid") + return res + else: + error = "engine: {engine} is not supported ".format(engine=engine) + _raise_error(error) diff --git a/plugins/plugin_utils/to_xml.py b/plugins/plugin_utils/to_xml.py new file mode 100644 index 0000000..4c42827 --- /dev/null +++ b/plugins/plugin_utils/to_xml.py @@ -0,0 +1,53 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +""" +The to_xml plugin +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible.errors import AnsibleFilterError + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +def _raise_error(msg): + """Raise an error message, prepend with filter name + + :param msg: The message + :type msg: str + :raises: AnsibleError + """ + error = "Error when using plugin 'to_xml': {msg}".format(msg=msg) + raise AnsibleFilterError(error) + + +def to_xml(data, engine): + """Convert data which is in json to xml" + + :param data: The data passed in (data|to_xml(...)) + :type data: xml + :param engine: Conversion library default=xmltodict + """ + if engine == "xmltodict": + if not HAS_XMLTODICT: + _raise_error("Missing required library xmltodict") + try: + res = xmltodict.unparse(data, pretty=True) + except Exception: + _raise_error("Input json is not valid") + return res + else: + error = "engine: {engine} is not supported ".format(engine=engine) + _raise_error(error) diff --git a/tests/integration/targets/from_xml/tasks/include/simple.yaml b/tests/integration/targets/from_xml/tasks/include/simple.yaml new file mode 100644 index 0000000..c7135ee --- /dev/null +++ b/tests/integration/targets/from_xml/tasks/include/simple.yaml @@ -0,0 +1,39 @@ +--- +- name: Setup xml and expected json + ansible.builtin.set_fact: + data: " + + " + output: { + "netconf-state": { + "@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", + "schemas": { + "schema": null + } + } + } + +- debug: + msg: "{{ data|ansible.utils.from_xml() }}" + +- name: Integration tests with and without default engine as xmltodict and + assert: + that: "{{ output == item.test }}" + loop: + - test: "{{ data|ansible.utils.from_xml() }}" + - test: "{{ data|ansible.utils.from_xml('xmltodict') }}" + +- name: Setup invalid xml as input to ansible.utils.from_xml. + ansible.builtin.set_fact: + data: "" + +- name: validate input xml + ansible.builtin.set_fact: + _result: "{{ data|ansible.utils.from_xml() }}" + ignore_errors: true + register: result + +- assert: + that: "{{ msg in result.msg }}" + vars: + msg: "Error when using plugin 'from_xml': Input Xml is not valid" diff --git a/tests/integration/targets/from_xml/tasks/main.yaml b/tests/integration/targets/from_xml/tasks/main.yaml new file mode 100644 index 0000000..4274d75 --- /dev/null +++ b/tests/integration/targets/from_xml/tasks/main.yaml @@ -0,0 +1,13 @@ +--- +- name: Recursively find all test files + find: + file_type: file + paths: "{{ role_path }}/tasks/include" + recurse: true + use_regex: true + patterns: + - '^(?!_).+$' + register: found + +- include: "{{ item.path }}" + loop: "{{ found.files }}" diff --git a/tests/integration/targets/to_xml/tasks/include/simple.yaml b/tests/integration/targets/to_xml/tasks/include/simple.yaml new file mode 100644 index 0000000..1f7a412 --- /dev/null +++ b/tests/integration/targets/to_xml/tasks/include/simple.yaml @@ -0,0 +1,35 @@ +--- +- name: Setup xml and expected json + ansible.builtin.set_fact: + data: + "interface-configurations": + "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + "interface-configuration": + output: "\n\n\t\n" + +- debug: + msg: "{{ data|ansible.utils.to_xml() }}" + +- name: Integration tests with and without default engine as xmltodict and + assert: + that: "{{ output == item.test }}" + loop: + - test: "{{ data|ansible.utils.to_xml() }}" + - test: "{{ data|ansible.utils.to_xml('xmltodict') }}" + +- name: test for supported engine for to_xml filter + ansible.builtin.set_fact: + data: + "interface-configurations": + "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + +- name: validate input xml + ansible.builtin.set_fact: + _result: "{{ data|ansible.utils.to_xml('dicttoxml') }}" + ignore_errors: true + register: result + +- assert: + that: "{{ msg in result.msg }}" + vars: + msg: "Error when using plugin 'to_xml': engine: dicttoxml is not supported" \ No newline at end of file diff --git a/tests/integration/targets/to_xml/tasks/main.yaml b/tests/integration/targets/to_xml/tasks/main.yaml new file mode 100644 index 0000000..4274d75 --- /dev/null +++ b/tests/integration/targets/to_xml/tasks/main.yaml @@ -0,0 +1,13 @@ +--- +- name: Recursively find all test files + find: + file_type: file + paths: "{{ role_path }}/tasks/include" + recurse: true + use_regex: true + patterns: + - '^(?!_).+$' + register: found + +- include: "{{ item.path }}" + loop: "{{ found.files }}" diff --git a/tests/unit/plugins/filter/test_from_xml.py b/tests/unit/plugins/filter/test_from_xml.py new file mode 100644 index 0000000..0ff51fe --- /dev/null +++ b/tests/unit/plugins/filter/test_from_xml.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import unittest +from ansible.errors import AnsibleError +from ansible_collections.ansible.utils.plugins.filter.from_xml import from_xml + +INVALID_DATA = '' + +VALID_DATA = ( + '' + "" +) + +OUTPUT = """{"netconf-state": \ +{"@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "schemas": {"schema": null}}}""" + + +class TestFromXml(unittest.TestCase): + def setUp(self): + pass + + def test_invalid_data(self): + """Check passing invalid argspec""" + + # missing required arguments + args = [INVALID_DATA, "xmltodict"] + kwargs = {} + with self.assertRaises(AnsibleError) as error: + from_xml(*args, **kwargs) + print(str(error.exception)) + self.assertIn( + "Error when using plugin 'from_xml': Input Xml is not valid", + str(error.exception), + ) + + def test_valid_data(self): + """Check passing valid data as per criteria""" + self.maxDiff = None + args = [VALID_DATA, "xmltodict"] + result = from_xml(*args) + print(result) + self.assertEqual(result, OUTPUT) diff --git a/tests/unit/plugins/filter/test_to_xml.py b/tests/unit/plugins/filter/test_to_xml.py new file mode 100644 index 0000000..48cf7e8 --- /dev/null +++ b/tests/unit/plugins/filter/test_to_xml.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import unittest +from ansible.errors import AnsibleError +from ansible_collections.ansible.utils.plugins.filter.to_xml import to_xml + +INVALID_DATA = '' + +VALID_DATA = { + "interface-configurations": { + "@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + } +} + +OUTPUT = """ +""" + + +class TestToXml(unittest.TestCase): + def setUp(self): + pass + + def test_invalid_data(self): + """Check passing invalid argspec""" + + # missing required arguments + args = [INVALID_DATA, "xmltodict"] + kwargs = {} + with self.assertRaises(AnsibleError) as error: + to_xml(*args, **kwargs) + print(str(error.exception)) + self.assertIn( + "Error when using plugin 'to_xml': Input json is not valid", + str(error.exception), + ) + + def test_valid_data(self): + """Check passing valid data as per criteria""" + self.maxDiff = None + args = [VALID_DATA, "xmltodict"] + result = to_xml(*args) + print(result) + self.assertEqual(result, OUTPUT)