Add to_xml and from_xml filter plugin (#56)
Add to_xml and from_xml filter plugin Reviewed-by: https://github.com/apps/ansible-zuulpull/67/head
parent
c04eecdfc2
commit
43e77543d9
|
@ -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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
minor_changes:
|
||||
- Add from_xml and to_xml fiter plugin (https://github.com/ansible-collections/ansible.utils/pull/56).
|
|
@ -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
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th>Configuration</th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>data</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The input XML string.</div>
|
||||
<div>This option represents the XML value that is passed to the filter plugin in pipe format.</div>
|
||||
<div>For example <code>config_data|ansible.utils.from_xml</code>, in this case <code>config_data</code> represents this option.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>engine</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">"xmltodict"</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Conversion library to use within the filter plugin.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
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: "
|
||||
<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state>
|
||||
"
|
||||
|
||||
- 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": " <netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state> "
|
||||
# },
|
||||
# "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: "
|
||||
<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state>
|
||||
"
|
||||
|
||||
- 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": " <netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state> "
|
||||
# },
|
||||
# "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.
|
|
@ -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
|
||||
|
||||
<table border=0 cellpadding=0 class="documentation-table">
|
||||
<tr>
|
||||
<th colspan="1">Parameter</th>
|
||||
<th>Choices/<font color="blue">Defaults</font></th>
|
||||
<th>Configuration</th>
|
||||
<th width="100%">Comments</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>data</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">dictionary</span>
|
||||
/ <span style="color: red">required</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>The input JSON string .</div>
|
||||
<div>This option represents the JSON value that is passed to the filter plugin in pipe format.</div>
|
||||
<div>For example <code>config_data|ansible.utils.to_xml</code>, in this case <code>config_data</code> represents this option.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">
|
||||
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||
<b>engine</b>
|
||||
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||
<div style="font-size: small">
|
||||
<span style="color: purple">string</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<b>Default:</b><br/><div style="color: blue">"xmltodict"</div>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<div>Conversion library to use within the filter plugin.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
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": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<interface-configurations xmlns=\"http://cisco.com/ns/yang/
|
||||
# Cisco-IOS-XR-ifmgr-cfg\">\n\t<interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
|
||||
#### 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": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<interface-configurations xmlns=\"http://cisco.com/ns/yang/
|
||||
# Cisco-IOS-XR-ifmgr-cfg\">\n\t<interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
|
@ -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: "
|
||||
<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state>
|
||||
"
|
||||
|
||||
- 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": " <netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state> "
|
||||
# },
|
||||
# "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: "
|
||||
<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state>
|
||||
"
|
||||
|
||||
- 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": " <netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state> "
|
||||
# },
|
||||
# "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}
|
|
@ -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": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<interface-configurations xmlns=\"http://cisco.com/ns/yang/
|
||||
# Cisco-IOS-XR-ifmgr-cfg\">\n\t<interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
|
||||
#### 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": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<interface-configurations xmlns=\"http://cisco.com/ns/yang/
|
||||
# Cisco-IOS-XR-ifmgr-cfg\">\n\t<interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
# }
|
||||
|
||||
"""
|
||||
|
||||
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}
|
|
@ -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)
|
|
@ -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)
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
- name: Setup xml and expected json
|
||||
ansible.builtin.set_fact:
|
||||
data: "
|
||||
<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema/></schemas></netconf-state>
|
||||
"
|
||||
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: "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">"
|
||||
|
||||
- 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"
|
|
@ -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 }}"
|
|
@ -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: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<interface-configurations xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg\">\n\t<interface-configuration></interface-configuration>\n</interface-configurations>"
|
||||
|
||||
- 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"
|
|
@ -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 }}"
|
|
@ -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 = '<netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">'
|
||||
|
||||
VALID_DATA = (
|
||||
'<netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">'
|
||||
"<schemas><schema/></schemas></netconf-state>"
|
||||
)
|
||||
|
||||
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)
|
|
@ -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 = '<netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">'
|
||||
|
||||
VALID_DATA = {
|
||||
"interface-configurations": {
|
||||
"@xmlns": "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"
|
||||
}
|
||||
}
|
||||
|
||||
OUTPUT = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"></interface-configurations>"""
|
||||
|
||||
|
||||
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)
|
Loading…
Reference in New Issue