diff --git a/.gitignore b/.gitignore
index 740c811..4e54cab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -130,3 +130,6 @@ dmypy.json
# Pyre type checker
.pyre/
+
+# ide
+.vscode
diff --git a/README.md b/README.md
index 7f3162f..f76b280 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,12 @@ Name | Description
[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.hwaddr](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.hwaddr_filter.rst)|HWaddr / MAC address filters
[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.keep_keys](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.keep_keys_filter.rst)|Keep specific keys from a data recursively.
+[ansible.utils.nthhost](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.nthhost_filter.rst)|This filter returns the nth host within a network described by value.
+[ansible.utils.ipv4](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.ipv4_filter.rst)|To filter only Ipv4 addresses Ipv4 filter is used.
+[ansible.utils.param_list_compare](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.param_list_compare_filter.rst)|Generate the final param list combining/comparing base and provided parameters.
+[ansible.utils.remove_keys](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.remove_keys_filter.rst)|Remove specific keys from a data recursively.
+[ansible.utils.replace_keys](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.replace_keys_filter.rst)|Replaces specific keys with their after value from a data recursively.
[ansible.utils.macaddr](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.macaddr_filter.rst)|macaddr / MAC address filters
[ansible.utils.slaac](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.slaac_filter.rst)|This filter returns the SLAAC address within a network for a given HW/MAC address.
[ansible.utils.ipaddr](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.ipaddr_filter.rst)|This filter is designed to return the input value if a query is True, else False.
diff --git a/changelogs/fragments/recursive_filter_plugins.yaml b/changelogs/fragments/recursive_filter_plugins.yaml
new file mode 100644
index 0000000..bf942fd
--- /dev/null
+++ b/changelogs/fragments/recursive_filter_plugins.yaml
@@ -0,0 +1,5 @@
+---
+minor_changes:
+ - "'keep_keys' filter plugin added."
+ - "'remove_keys' filter plugin added."
+ - "'replace_keys' filter plugin added."
diff --git a/docs/ansible.utils.keep_keys_filter.rst b/docs/ansible.utils.keep_keys_filter.rst
new file mode 100644
index 0000000..78071cf
--- /dev/null
+++ b/docs/ansible.utils.keep_keys_filter.rst
@@ -0,0 +1,374 @@
+.. _ansible.utils.keep_keys_filter:
+
+
+***********************
+ansible.utils.keep_keys
+***********************
+
+**Keep specific keys from a data recursively.**
+
+
+Version added: 2.5.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This plugin keep only specified keys from a provided data recursively.
+- Matching parameter defaults to equals unless ``matching_parameter`` is explicitly mentioned.
+- Using the parameters below- ``data|ansible.utils.keep_keys(target([....]``))
+
+
+
+
+Parameters
+----------
+
+.. raw:: html
+
+
+
+ Parameter |
+ Choices/Defaults |
+ Configuration |
+ Comments |
+
+
+
+
+ data
+
+
+ raw
+ / required
+
+ |
+
+ |
+
+ |
+
+ This option represents a list of dictionaries or a dictionary with any level of nesting data.
+ For example config_data|ansible.utils.keep_keys(target([....] )), in this case config_data represents this option.
+ |
+
+
+
+
+ matching_parameter
+
+
+ string
+
+ |
+
+ Choices:
+ - starts_with
+ - ends_with
+ - regex
+
+ |
+
+ |
+
+ Specify the matching configuration of target keys and data attributes.
+ |
+
+
+
+
+ target
+
+
+ list
+ / elements=string
+ / required
+
+ |
+
+ |
+
+ |
+
+ Specify the target keys to keep in list format.
+ |
+
+
+
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ ##example.yaml
+ interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+ ##Playbook
+ vars_files:
+ - "example.yaml"
+ tasks:
+ - name: keep selective keys from dict/list of dict data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.keep_keys(target=['description', 'name', 'mtu', 'duplex', 'enabled', 'vifs', 'vlan_id']) }}"
+
+ ##Output
+ # TASK [keep selective keys from python dict/list of dict] ****************************************************************************************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "note": [
+ # "Connected green wire"
+ # ],
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "note": [
+ # "Connected blue wire",
+ # "Configured by Paul"
+ # ],
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "comment": "Needs reconfiguration",
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # },
+ # "changed": false
+ # }
+ # Read vars_file 'example.yaml'
+
+ # TASK [debug] *************************************************************************************************************
+ # ok: [localhost] => {
+ # "msg": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # }
+
+ ##example.yaml
+ interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+ ##Playbook
+ vars_files:
+ - "example.yaml"
+ tasks:
+ - name: keep selective keys from dict/list of dict data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.keep_keys(target=['desc', 'name'], matching_parameter= 'starts_with') }}"
+
+ ##Output
+ # TASK [keep selective keys from python dict/list of dict] **************************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "note": [
+ # "Connected green wire"
+ # ],
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "note": [
+ # "Connected blue wire",
+ # "Configured by Paul"
+ # ],
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "comment": "Needs reconfiguration",
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # },
+ # "changed": false
+ # }
+ # Read vars_file 'example.yaml'
+
+ # TASK [debug] **********************************************************************************
+ # ok: [localhost] => {
+ # "msg": [
+ # {
+ # "name": "eth0"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "name": "eth1",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100"
+ # },
+ # {
+ # "description": "Eth1 - VIF 101"
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "name": "eth2"
+ # }
+ # ]
+ # }
+
+
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Sagar Paul (@KB-perByte)
+
+
+.. 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.remove_keys_filter.rst b/docs/ansible.utils.remove_keys_filter.rst
new file mode 100644
index 0000000..2491517
--- /dev/null
+++ b/docs/ansible.utils.remove_keys_filter.rst
@@ -0,0 +1,390 @@
+.. _ansible.utils.remove_keys_filter:
+
+
+*************************
+ansible.utils.remove_keys
+*************************
+
+**Remove specific keys from a data recursively.**
+
+
+Version added: 2.5.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This plugin removes specific keys from a provided data recursively.
+- Matching parameter defaults to equals unless ``matching_parameter`` is explicitly mentioned.
+- Using the parameters below- ``data|ansible.utils.remove_keys(target([....]``))
+
+
+
+
+Parameters
+----------
+
+.. raw:: html
+
+
+
+ Parameter |
+ Choices/Defaults |
+ Configuration |
+ Comments |
+
+
+
+
+ data
+
+
+ raw
+ / required
+
+ |
+
+ |
+
+ |
+
+ This option represents a list of dictionaries or a dictionary with any level of nesting data.
+ For example config_data|ansible.utils.remove_keys(target([....] )), in this case config_data represents this option.
+ |
+
+
+
+
+ matching_parameter
+
+
+ string
+
+ |
+
+ Choices:
+ - starts_with
+ - ends_with
+ - regex
+
+ |
+
+ |
+
+ Specify the matching configuration of target keys and data attributes.
+ |
+
+
+
+
+ target
+
+
+ list
+ / elements=string
+ / required
+
+ |
+
+ |
+
+ |
+
+ Specify the target keys to remove in list format.
+ |
+
+
+
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ ##example.yaml
+ interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+ ##Playbook
+ vars_files:
+ - "example.yaml"
+ tasks:
+ - name: remove multiple keys from a provided data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.remove_keys(target=['note', 'comment']) }}"
+
+ ##Output
+ # TASK [remove multiple keys from a provided data] ***************************************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "note": [
+ # "Connected green wire"
+ # ],
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "note": [
+ # "Connected blue wire",
+ # "Configured by Paul"
+ # ],
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "comment": "Needs reconfiguration",
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # },
+ # "changed": false
+ # }
+ # Read vars_file 'example.yaml'
+
+ # TASK [debug] ********************************************
+ # ok: [localhost] => {
+ # "msg": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # }
+
+ ##example.yaml
+ interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+ ##Playbook
+ vars_files:
+ - "example.yaml"
+ tasks:
+ - name: remove multiple keys from a provided data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.remove_keys(target=['^note$', '^comment'], matching_parameter= 'regex') }}"
+
+ ##Output
+ # TASK [remove multiple keys from a provided data] ***********************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "note": [
+ # "Connected green wire"
+ # ],
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "note": [
+ # "Connected blue wire",
+ # "Configured by Paul"
+ # ],
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "comment": "Needs reconfiguration",
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # },
+ # "changed": false
+ # }
+ # Read vars_file 'example.yaml'
+
+ # TASK [debug] *****************************************
+ # ok: [localhost] => {
+ # "msg": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # }
+
+
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Sagar Paul (@KB-perByte)
+
+
+.. 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.replace_keys_filter.rst b/docs/ansible.utils.replace_keys_filter.rst
new file mode 100644
index 0000000..555acc8
--- /dev/null
+++ b/docs/ansible.utils.replace_keys_filter.rst
@@ -0,0 +1,398 @@
+.. _ansible.utils.replace_keys_filter:
+
+
+**************************
+ansible.utils.replace_keys
+**************************
+
+**Replaces specific keys with their after value from a data recursively.**
+
+
+Version added: 2.5.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This plugin replaces specific keys with their after value from a data recursively.
+- Matching parameter defaults to equals unless ``matching_parameter`` is explicitly mentioned.
+- Using the parameters below- ``data|ansible.utils.replace_keys(target([....]``))
+
+
+
+
+Parameters
+----------
+
+.. raw:: html
+
+
+
+ Parameter |
+ Choices/Defaults |
+ Configuration |
+ Comments |
+
+
+
+
+ data
+
+
+ raw
+ / required
+
+ |
+
+ |
+
+ |
+
+ This option represents a list of dictionaries or a dictionary with any level of nesting data.
+ For example config_data|ansible.utils.replace_keys(target([....] )), in this case config_data represents this option.
+ |
+
+
+
+
+ matching_parameter
+
+
+ string
+
+ |
+
+ Choices:
+ - starts_with
+ - ends_with
+ - regex
+
+ |
+
+ |
+
+ Specify the matching configuration of target keys and data attributes.
+ |
+
+
+
+
+ target
+
+
+ list
+ / elements=dictionary
+ / required
+
+ |
+
+ |
+
+ |
+
+ Specify the target keys to replace in list of dictionaries format containing before and after key value.
+ |
+
+
+ |
+
+
+ after
+
+
+ string
+
+ |
+
+ |
+
+ |
+
+ after attribute key [change to]
+ |
+
+
+ |
+
+
+ before
+
+
+ string
+
+ |
+
+ |
+
+ |
+
+ before attribute key [to change]
+ |
+
+
+
+
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ ##example.yaml
+ interfaces:
+ - interface_name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ is_enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ is_enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ is_enabled: true
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ is_enabled: false
+
+ ##Playbook
+ vars_files:
+ - "example.yaml"
+ tasks:
+ - name: replace keys with specified keys dict/list to dict
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.replace_keys(target=[{'before':'interface_name', 'after':'name'}, {'before':'is_enabled', 'after':'enabled'}]) }}"
+
+ ##Output
+ # TASK [replace keys with specified keys dict/list to dict] *************************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "interface_name": "eth0",
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "interface_name": "eth1",
+ # "is_enabled": true,
+ # "mtu": 1500,
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100",
+ # "is_enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "is_enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "interface_name": "eth2",
+ # "is_enabled": false,
+ # "mtu": 600
+ # }
+ # ]
+ # },
+ # "changed": false
+ # }
+
+ # TASK [debug] **********************************************************************
+ # ok: [localhost] => {
+ # "msg": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # }
+
+ ##example.yaml
+ interfaces:
+ - interface_name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ is_enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ is_enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ is_enabled: true
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ is_enabled: false
+
+ ##Playbook
+ vars_files:
+ - "example.yaml"
+ tasks:
+ - name: replace keys with specified keys dict/list to dict
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.replace_keys(target=[{'before':'name', 'after':'name'}, {'before':'enabled', 'after':'enabled'}],
+ matching_parameter= 'ends_with') }}"
+
+ ##Output
+ # TASK [replace keys with specified keys dict/list to dict] *********************************
+ # ok: [localhost] => {
+ # "ansible_facts": {
+ # "data": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "interface_name": "eth0",
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "interface_name": "eth1",
+ # "is_enabled": true,
+ # "mtu": 1500,
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100",
+ # "is_enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "is_enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "interface_name": "eth2",
+ # "is_enabled": false,
+ # "mtu": 600
+ # }
+ # ]
+ # },
+ # "changed": false
+ # }
+
+ # TASK [debug] ***************************************************************************
+ # ok: [localhost] => {
+ # "msg": [
+ # {
+ # "duplex": "auto",
+ # "enabled": true,
+ # "name": "eth0",
+ # "speed": "auto"
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 1",
+ # "duplex": "auto",
+ # "enabled": true,
+ # "mtu": 1500,
+ # "name": "eth1",
+ # "speed": "auto",
+ # "vifs": [
+ # {
+ # "description": "Eth1 - VIF 100",
+ # "enabled": true,
+ # "mtu": 400,
+ # "vlan_id": 100
+ # },
+ # {
+ # "description": "Eth1 - VIF 101",
+ # "enabled": true,
+ # "vlan_id": 101
+ # }
+ # ]
+ # },
+ # {
+ # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ # "enabled": false,
+ # "mtu": 600,
+ # "name": "eth2"
+ # }
+ # ]
+ # }
+
+
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Sagar Paul (@KB-perByte)
+
+
+.. 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/keep_keys.py b/plugins/filter/keep_keys.py
new file mode 100644
index 0000000..8276d87
--- /dev/null
+++ b/plugins/filter/keep_keys.py
@@ -0,0 +1,331 @@
+#
+# -*- 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 keep_keys filter plugin
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+ name: keep_keys
+ author: Sagar Paul (@KB-perByte)
+ version_added: "2.5.0"
+ short_description: Keep specific keys from a data recursively.
+ description:
+ - This plugin keep only specified keys from a provided data recursively.
+ - Matching parameter defaults to equals unless C(matching_parameter) is explicitly mentioned.
+ - Using the parameters below- C(data|ansible.utils.keep_keys(target([....])))
+ options:
+ data:
+ description:
+ - This option represents a list of dictionaries or a dictionary with any level of nesting data.
+ - For example C(config_data|ansible.utils.keep_keys(target([....]))), in this case C(config_data) represents this option.
+ type: raw
+ required: True
+ target:
+ description: Specify the target keys to keep in list format.
+ type: list
+ elements: str
+ required: True
+ matching_parameter:
+ description: Specify the matching configuration of target keys and data attributes.
+ type: str
+ choices: ["starts_with","ends_with","regex"]
+"""
+
+EXAMPLES = r"""
+
+##example.yaml
+interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+##Playbook
+vars_files:
+ - "example.yaml"
+tasks:
+ - name: keep selective keys from dict/list of dict data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.keep_keys(target=['description', 'name', 'mtu', 'duplex', 'enabled', 'vifs', 'vlan_id']) }}"
+
+##Output
+# TASK [keep selective keys from python dict/list of dict] ****************************************************************************************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "note": [
+# "Connected green wire"
+# ],
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "note": [
+# "Connected blue wire",
+# "Configured by Paul"
+# ],
+# "speed": "auto",
+# "vifs": [
+# {
+# "comment": "Needs reconfiguration",
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# },
+# "changed": false
+# }
+# Read vars_file 'example.yaml'
+
+# TASK [debug] *************************************************************************************************************
+# ok: [localhost] => {
+# "msg": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# }
+
+##example.yaml
+interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+##Playbook
+vars_files:
+ - "example.yaml"
+tasks:
+ - name: keep selective keys from dict/list of dict data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.keep_keys(target=['desc', 'name'], matching_parameter= 'starts_with') }}"
+
+##Output
+# TASK [keep selective keys from python dict/list of dict] **************************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "note": [
+# "Connected green wire"
+# ],
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "note": [
+# "Connected blue wire",
+# "Configured by Paul"
+# ],
+# "speed": "auto",
+# "vifs": [
+# {
+# "comment": "Needs reconfiguration",
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# },
+# "changed": false
+# }
+# Read vars_file 'example.yaml'
+
+# TASK [debug] **********************************************************************************
+# ok: [localhost] => {
+# "msg": [
+# {
+# "name": "eth0"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "name": "eth1",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100"
+# },
+# {
+# "description": "Eth1 - VIF 101"
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "name": "eth2"
+# }
+# ]
+# }
+"""
+
+from ansible.errors import AnsibleFilterError
+from ansible_collections.ansible.utils.plugins.plugin_utils.keep_keys import (
+ keep_keys,
+)
+from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
+ AnsibleArgSpecValidator,
+)
+
+try:
+ from jinja2.filters import pass_environment
+except ImportError:
+ from jinja2.filters import environmentfilter as pass_environment
+
+
+@pass_environment
+def _keep_keys(*args, **kwargs):
+ """keep specific keys from a data recursively"""
+
+ keys = ["data", "target", "matching_parameter"]
+ data = dict(zip(keys, args[1:]))
+ data.update(kwargs)
+ aav = AnsibleArgSpecValidator(
+ data=data, schema=DOCUMENTATION, name="keep_keys"
+ )
+ valid, errors, updated_data = aav.validate()
+ if not valid:
+ raise AnsibleFilterError(errors)
+ return keep_keys(**updated_data)
+
+
+class FilterModule(object):
+ """keep_keys"""
+
+ def filters(self):
+
+ """a mapping of filter names to functions"""
+ return {"keep_keys": _keep_keys}
diff --git a/plugins/filter/remove_keys.py b/plugins/filter/remove_keys.py
new file mode 100644
index 0000000..318f179
--- /dev/null
+++ b/plugins/filter/remove_keys.py
@@ -0,0 +1,348 @@
+#
+# -*- 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 remove_keys filter plugin
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+ name: remove_keys
+ author: Sagar Paul (@KB-perByte)
+ version_added: "2.5.0"
+ short_description: Remove specific keys from a data recursively.
+ description:
+ - This plugin removes specific keys from a provided data recursively.
+ - Matching parameter defaults to equals unless C(matching_parameter) is explicitly mentioned.
+ - Using the parameters below- C(data|ansible.utils.remove_keys(target([....])))
+ options:
+ data:
+ description:
+ - This option represents a list of dictionaries or a dictionary with any level of nesting data.
+ - For example C(config_data|ansible.utils.remove_keys(target([....]))), in this case C(config_data) represents this option.
+ type: raw
+ required: True
+ target:
+ description: Specify the target keys to remove in list format.
+ type: list
+ elements: str
+ required: True
+ matching_parameter:
+ description: Specify the matching configuration of target keys and data attributes.
+ type: str
+ choices: ["starts_with","ends_with","regex"]
+"""
+
+EXAMPLES = r"""
+
+##example.yaml
+interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+##Playbook
+vars_files:
+ - "example.yaml"
+tasks:
+ - name: remove multiple keys from a provided data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.remove_keys(target=['note', 'comment']) }}"
+
+##Output
+# TASK [remove multiple keys from a provided data] ***************************************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "note": [
+# "Connected green wire"
+# ],
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "note": [
+# "Connected blue wire",
+# "Configured by Paul"
+# ],
+# "speed": "auto",
+# "vifs": [
+# {
+# "comment": "Needs reconfiguration",
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# },
+# "changed": false
+# }
+# Read vars_file 'example.yaml'
+
+# TASK [debug] ********************************************
+# ok: [localhost] => {
+# "msg": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "speed": "auto",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# }
+
+##example.yaml
+interfaces:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ note:
+ - Connected green wire
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ note:
+ - Connected blue wire
+ - Configured by Paul
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ comment: Needs reconfiguration
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+##Playbook
+vars_files:
+ - "example.yaml"
+tasks:
+ - name: remove multiple keys from a provided data
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.remove_keys(target=['^note$', '^comment'], matching_parameter= 'regex') }}"
+
+##Output
+# TASK [remove multiple keys from a provided data] ***********************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "note": [
+# "Connected green wire"
+# ],
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "note": [
+# "Connected blue wire",
+# "Configured by Paul"
+# ],
+# "speed": "auto",
+# "vifs": [
+# {
+# "comment": "Needs reconfiguration",
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# },
+# "changed": false
+# }
+# Read vars_file 'example.yaml'
+
+# TASK [debug] *****************************************
+# ok: [localhost] => {
+# "msg": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "speed": "auto",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# }
+
+"""
+
+from ansible.errors import AnsibleFilterError
+from ansible_collections.ansible.utils.plugins.plugin_utils.remove_keys import (
+ remove_keys,
+)
+from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
+ AnsibleArgSpecValidator,
+)
+
+try:
+ from jinja2.filters import pass_environment
+except ImportError:
+ from jinja2.filters import environmentfilter as pass_environment
+
+
+@pass_environment
+def _remove_keys(*args, **kwargs):
+ """remove specific keys from a data recursively"""
+
+ keys = ["data", "target", "matching_parameter"]
+ data = dict(zip(keys, args[1:]))
+ data.update(kwargs)
+ aav = AnsibleArgSpecValidator(
+ data=data, schema=DOCUMENTATION, name="remove_keys"
+ )
+ valid, errors, updated_data = aav.validate()
+ if not valid:
+ raise AnsibleFilterError(errors)
+ return remove_keys(**updated_data)
+
+
+class FilterModule(object):
+ """remove_keys"""
+
+ def filters(self):
+
+ """a mapping of filter names to functions"""
+ return {"remove_keys": _remove_keys}
diff --git a/plugins/filter/replace_keys.py b/plugins/filter/replace_keys.py
new file mode 100644
index 0000000..edfd1fb
--- /dev/null
+++ b/plugins/filter/replace_keys.py
@@ -0,0 +1,325 @@
+#
+# -*- 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 replace_keys filter plugin
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+ name: replace_keys
+ author: Sagar Paul (@KB-perByte)
+ version_added: "2.5.0"
+ short_description: Replaces specific keys with their after value from a data recursively.
+ description:
+ - This plugin replaces specific keys with their after value from a data recursively.
+ - Matching parameter defaults to equals unless C(matching_parameter) is explicitly mentioned.
+ - Using the parameters below- C(data|ansible.utils.replace_keys(target([....])))
+ options:
+ data:
+ description:
+ - This option represents a list of dictionaries or a dictionary with any level of nesting data.
+ - For example C(config_data|ansible.utils.replace_keys(target([....]))), in this case C(config_data) represents this option.
+ type: raw
+ required: True
+ target:
+ description: Specify the target keys to replace in list of dictionaries format containing
+ before and after key value.
+ type: list
+ elements: dict
+ required: True
+ suboptions:
+ before:
+ description: before attribute key [to change]
+ type: str
+ after:
+ description: after attribute key [change to]
+ type: str
+ matching_parameter:
+ description: Specify the matching configuration of target keys and data attributes.
+ type: str
+ choices: ["starts_with","ends_with","regex"]
+"""
+
+EXAMPLES = r"""
+##example.yaml
+interfaces:
+ - interface_name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ is_enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ is_enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ is_enabled: true
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ is_enabled: false
+
+##Playbook
+vars_files:
+ - "example.yaml"
+tasks:
+ - name: replace keys with specified keys dict/list to dict
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.replace_keys(target=[{'before':'interface_name', 'after':'name'}, {'before':'is_enabled', 'after':'enabled'}]) }}"
+
+##Output
+# TASK [replace keys with specified keys dict/list to dict] *************************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "interface_name": "eth0",
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "interface_name": "eth1",
+# "is_enabled": true,
+# "mtu": 1500,
+# "speed": "auto",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100",
+# "is_enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "is_enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "interface_name": "eth2",
+# "is_enabled": false,
+# "mtu": 600
+# }
+# ]
+# },
+# "changed": false
+# }
+
+# TASK [debug] **********************************************************************
+# ok: [localhost] => {
+# "msg": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "speed": "auto",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# }
+
+##example.yaml
+interfaces:
+ - interface_name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ is_enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ is_enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ is_enabled: true
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ is_enabled: false
+
+##Playbook
+vars_files:
+ - "example.yaml"
+tasks:
+ - name: replace keys with specified keys dict/list to dict
+ ansible.builtin.set_fact:
+ data: "{{ interfaces }}"
+
+ - debug:
+ msg: "{{ data|ansible.utils.replace_keys(target=[{'before':'name', 'after':'name'}, {'before':'enabled', 'after':'enabled'}],
+ matching_parameter= 'ends_with') }}"
+
+##Output
+# TASK [replace keys with specified keys dict/list to dict] *********************************
+# ok: [localhost] => {
+# "ansible_facts": {
+# "data": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "interface_name": "eth0",
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "interface_name": "eth1",
+# "is_enabled": true,
+# "mtu": 1500,
+# "speed": "auto",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100",
+# "is_enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "is_enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "interface_name": "eth2",
+# "is_enabled": false,
+# "mtu": 600
+# }
+# ]
+# },
+# "changed": false
+# }
+
+# TASK [debug] ***************************************************************************
+# ok: [localhost] => {
+# "msg": [
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# },
+# {
+# "description": "Configured by Ansible - Interface 1",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth1",
+# "speed": "auto",
+# "vifs": [
+# {
+# "description": "Eth1 - VIF 100",
+# "enabled": true,
+# "mtu": 400,
+# "vlan_id": 100
+# },
+# {
+# "description": "Eth1 - VIF 101",
+# "enabled": true,
+# "vlan_id": 101
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# }
+# ]
+# }
+"""
+
+from ansible.errors import AnsibleFilterError
+from ansible_collections.ansible.utils.plugins.plugin_utils.replace_keys import (
+ replace_keys,
+)
+from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
+ AnsibleArgSpecValidator,
+)
+
+try:
+ from jinja2.filters import pass_environment
+except ImportError:
+ from jinja2.filters import environmentfilter as pass_environment
+
+
+@pass_environment
+def _replace_keys(*args, **kwargs):
+ """replaces specific keys with their after value from a data recursively"""
+
+ keys = ["data", "target", "matching_parameter"]
+ data = dict(zip(keys, args[1:]))
+ data.update(kwargs)
+ aav = AnsibleArgSpecValidator(
+ data=data, schema=DOCUMENTATION, name="replace_keys"
+ )
+ valid, errors, updated_data = aav.validate()
+ if not valid:
+ raise AnsibleFilterError(errors)
+ return replace_keys(**updated_data)
+
+
+class FilterModule(object):
+ """replace_keys"""
+
+ def filters(self):
+
+ """a mapping of filter names to functions"""
+ return {"replace_keys": _replace_keys}
diff --git a/plugins/plugin_utils/keep_keys.py b/plugins/plugin_utils/keep_keys.py
new file mode 100644
index 0000000..3893c05
--- /dev/null
+++ b/plugins/plugin_utils/keep_keys.py
@@ -0,0 +1,83 @@
+#
+# -*- 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 keep_keys plugin code
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from ansible.errors import AnsibleFilterError
+
+
+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 'keep_keys': {msg}".format(msg=msg)
+ raise AnsibleFilterError(error)
+
+
+def keep_keys_from_dict_n_list(data, target, matching_parameter):
+ if isinstance(data, list):
+ list_data = [
+ keep_keys_from_dict_n_list(each, target, matching_parameter)
+ for each in data
+ ]
+ return list_data
+ if isinstance(data, dict):
+ keep = {}
+ for k, val in data.items():
+ for key in target:
+ match = None
+ if not isinstance(val, (list, dict)):
+ if matching_parameter == "regex":
+ match = re.match(key, k)
+ if match:
+ keep[k] = val
+ elif matching_parameter == "starts_with":
+ if k.startswith(key):
+ keep[k], match = val, True
+ elif matching_parameter == "ends_with":
+ if k.endswith(key):
+ keep[k], match = val, True
+ else:
+ if k == key:
+ keep[k], match = val, True
+ else:
+ list_data = keep_keys_from_dict_n_list(
+ val, target, matching_parameter
+ )
+ if isinstance(list_data, list) and not match:
+ list_data = [r for r in list_data if r not in ([], {})]
+ if all(isinstance(s, str) for s in list_data):
+ continue
+ if list_data in ([], {}):
+ continue
+ keep[k] = list_data
+ return keep
+ return data
+
+
+def keep_keys(data, target, matching_parameter="equality"):
+ """keep selective keys recursively from a given data"
+ :param data: The data passed in (data|keep_keys(...))
+ :type data: raw
+ :param target: List of keys on with operation is to be performed
+ :type data: list
+ :type elements: string
+ :param matching_parameter: matching type of the target keys with data keys
+ :type data: str
+ """
+ if not isinstance(data, (list, dict)):
+ _raise_error("Input is not valid for keep operation")
+ data = keep_keys_from_dict_n_list(data, target, matching_parameter)
+ return data
diff --git a/plugins/plugin_utils/remove_keys.py b/plugins/plugin_utils/remove_keys.py
new file mode 100644
index 0000000..4fc6706
--- /dev/null
+++ b/plugins/plugin_utils/remove_keys.py
@@ -0,0 +1,83 @@
+#
+# -*- 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 remove_keys plugin code
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from ansible.errors import AnsibleFilterError
+
+
+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 'remove_keys': {msg}".format(msg=msg)
+ raise AnsibleFilterError(error)
+
+
+def remove_keys_from_dict_n_list(data, target, matching_parameter):
+ if isinstance(data, dict):
+ for key in set(target):
+ for k in list(data.keys()):
+ if matching_parameter == "regex":
+ if re.match(key, k):
+ del data[k]
+ elif matching_parameter == "starts_with":
+ if k.startswith(key):
+ del data[k]
+ elif matching_parameter == "ends_with":
+ if k.endswith(key):
+ del data[k]
+ else:
+ if k == key:
+ del data[k]
+ for k, v in data.items():
+ remove_keys_from_dict_n_list(v, target, matching_parameter)
+ elif isinstance(data, list):
+ for i in data:
+ remove_keys_from_dict_n_list(i, target, matching_parameter)
+ return data
+
+
+def clear_empty_data(data):
+ if isinstance(data, dict):
+ # for k in list(data.keys()):
+ # if not data.get(k, {}):
+ # del data[k]
+ for k, v in data.items():
+ data[k] = clear_empty_data(v)
+ if isinstance(data, list):
+ temp = []
+ for i in data:
+ if i:
+ temp.append(clear_empty_data(i))
+ return temp
+ return data
+
+
+def remove_keys(data, target, matching_parameter="equality"):
+ """Remove unwanted keys recursively from a given data"
+ :param data: The data passed in (data|remove_keys(...))
+ :type data: raw
+ :param target: List of keys on with operation is to be performed
+ :type data: list
+ :type elements: string
+ :param matching_parameter: matching type of the target keys with data keys
+ :type data: str
+ """
+ if not isinstance(data, (list, dict)):
+ _raise_error("Input is not valid for attribute removal")
+ data = remove_keys_from_dict_n_list(data, target, matching_parameter)
+ data = clear_empty_data(data)
+ return data
diff --git a/plugins/plugin_utils/replace_keys.py b/plugins/plugin_utils/replace_keys.py
new file mode 100644
index 0000000..90f4059
--- /dev/null
+++ b/plugins/plugin_utils/replace_keys.py
@@ -0,0 +1,67 @@
+#
+# -*- 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 replace_keys plugin code
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from ansible.errors import AnsibleFilterError
+
+
+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 'replace_keys': {msg}".format(msg=msg)
+ raise AnsibleFilterError(error)
+
+
+def replace_keys_from_dict_n_list(data, target, matching_parameter):
+ if isinstance(data, dict):
+ for key in target:
+ for k in list(data.keys()):
+ if matching_parameter == "regex":
+ if re.match(key.get("before"), k):
+ data[key.get("after")] = data.pop(k)
+ elif matching_parameter == "starts_with":
+ if k.startswith(key.get("before")):
+ data[key.get("after")] = data.pop(k)
+ elif matching_parameter == "ends_with":
+ if k.endswith(key.get("before")):
+ data[key.get("after")] = data.pop(k)
+ else:
+ if k == key.get("before"):
+ data[key.get("after")] = data.pop(k)
+ for k, v in data.items():
+ replace_keys_from_dict_n_list(v, target, matching_parameter)
+ elif isinstance(data, list):
+ for i in data:
+ replace_keys_from_dict_n_list(i, target, matching_parameter)
+ return data
+
+
+def replace_keys(data, target, matching_parameter="equality"):
+ """replaces specific keys with mentioned after data"
+ :param data: The data passed in (data|replace_keys(...))
+ :type data: raw
+ :param target: List of keys on with operation is to be performed
+ :type data: list
+ :type elements: string
+ :param matching_parameter: matching type of the target keys with data keys
+ :type data: list
+ :type elements: dict
+ """
+ if not isinstance(data, (list, dict)):
+ _raise_error("Input is not valid for replace operation")
+ data = replace_keys_from_dict_n_list(data, target, matching_parameter)
+ return data
diff --git a/tests/integration/targets/utils_keep_keys/tasks/main.yaml b/tests/integration/targets/utils_keep_keys/tasks/main.yaml
new file mode 100644
index 0000000..eb1a94b
--- /dev/null
+++ b/tests/integration/targets/utils_keep_keys/tasks/main.yaml
@@ -0,0 +1,14 @@
+---
+- name: Recursively find all test files
+ find:
+ file_type: file
+ paths: "{{ role_path }}/tasks"
+ recurse: false
+ use_regex: true
+ patterns:
+ - '^(?!_|main).+$'
+ delegate_to: localhost
+ register: found
+
+- include: "{{ item.path }}"
+ loop: "{{ found.files }}"
diff --git a/tests/integration/targets/utils_keep_keys/tasks/simple.yaml b/tests/integration/targets/utils_keep_keys/tasks/simple.yaml
new file mode 100644
index 0000000..72535d7
--- /dev/null
+++ b/tests/integration/targets/utils_keep_keys/tasks/simple.yaml
@@ -0,0 +1,70 @@
+---
+- name: Setup data as facts
+ ansible.builtin.set_fact:
+ data:
+ - interface_name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ is_enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ is_enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ is_enabled: true
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ is_enabled: false
+
+- debug:
+ msg: "{{ data|ansible.utils.keep_keys(target=['desc', 'interface_'], matching_parameter= 'starts_with') }}"
+ register: result
+
+- name: Assert result dicts
+ assert:
+ that:
+ - keep['starts_with'] == result['msg']
+
+- name: Setup data as facts for equivalent
+ ansible.builtin.set_fact:
+ data:
+ - interface_name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ is_enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ is_enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ is_enabled: true
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ is_enabled: false
+
+- debug:
+ msg: "{{ data|ansible.utils.keep_keys(target=['interface_name', 'is_enabled', 'vlan_id']) }}"
+ register: result
+
+- name: Assert result dicts
+ assert:
+ that:
+ - keep_default['default'] == result['msg']
diff --git a/tests/integration/targets/utils_keep_keys/vars/main.yaml b/tests/integration/targets/utils_keep_keys/vars/main.yaml
new file mode 100644
index 0000000..f72e028
--- /dev/null
+++ b/tests/integration/targets/utils_keep_keys/vars/main.yaml
@@ -0,0 +1,24 @@
+---
+keep:
+ starts_with:
+ - interface_name: eth0
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ vifs:
+ - description: Eth1 - VIF 100
+ - description: Eth1 - VIF 101
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+
+keep_default:
+ default:
+ - interface_name: eth0
+ - interface_name: eth1
+ is_enabled: true
+ vifs:
+ - vlan_id: 100
+ is_enabled: true
+ - vlan_id: 101
+ is_enabled: true
+ - interface_name: eth2
+ is_enabled: false
diff --git a/tests/integration/targets/utils_remove_keys/tasks/main.yaml b/tests/integration/targets/utils_remove_keys/tasks/main.yaml
new file mode 100644
index 0000000..eb1a94b
--- /dev/null
+++ b/tests/integration/targets/utils_remove_keys/tasks/main.yaml
@@ -0,0 +1,14 @@
+---
+- name: Recursively find all test files
+ find:
+ file_type: file
+ paths: "{{ role_path }}/tasks"
+ recurse: false
+ use_regex: true
+ patterns:
+ - '^(?!_|main).+$'
+ delegate_to: localhost
+ register: found
+
+- include: "{{ item.path }}"
+ loop: "{{ found.files }}"
diff --git a/tests/integration/targets/utils_remove_keys/tasks/simple.yaml b/tests/integration/targets/utils_remove_keys/tasks/simple.yaml
new file mode 100644
index 0000000..5e3b4c4
--- /dev/null
+++ b/tests/integration/targets/utils_remove_keys/tasks/simple.yaml
@@ -0,0 +1,39 @@
+---
+- name: Setup data as facts for remove integration test
+ ansible.builtin.set_fact:
+ data:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ comment: test interface
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ notes:
+ - note 1
+ - note 2
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+- debug:
+ msg: "{{ data|ansible.utils.remove_keys(target=['notes', 'comment']) }}"
+ register: result
+
+- name: Assert result dicts
+ assert:
+ that:
+ - remove['default'] == result['msg']
diff --git a/tests/integration/targets/utils_remove_keys/vars/main.yaml b/tests/integration/targets/utils_remove_keys/vars/main.yaml
new file mode 100644
index 0000000..7967f36
--- /dev/null
+++ b/tests/integration/targets/utils_remove_keys/vars/main.yaml
@@ -0,0 +1,25 @@
+---
+remove:
+ default:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
diff --git a/tests/integration/targets/utils_replace_keys/tasks/main.yaml b/tests/integration/targets/utils_replace_keys/tasks/main.yaml
new file mode 100644
index 0000000..eb1a94b
--- /dev/null
+++ b/tests/integration/targets/utils_replace_keys/tasks/main.yaml
@@ -0,0 +1,14 @@
+---
+- name: Recursively find all test files
+ find:
+ file_type: file
+ paths: "{{ role_path }}/tasks"
+ recurse: false
+ use_regex: true
+ patterns:
+ - '^(?!_|main).+$'
+ delegate_to: localhost
+ register: found
+
+- include: "{{ item.path }}"
+ loop: "{{ found.files }}"
diff --git a/tests/integration/targets/utils_replace_keys/tasks/simple.yaml b/tests/integration/targets/utils_replace_keys/tasks/simple.yaml
new file mode 100644
index 0000000..3e9e405
--- /dev/null
+++ b/tests/integration/targets/utils_replace_keys/tasks/simple.yaml
@@ -0,0 +1,35 @@
+---
+- name: Setup data as facts for replace integration test
+ ansible.builtin.set_fact:
+ data:
+ - interface_name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - interface_name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - interface_name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
+
+- debug:
+ msg: "{{ data|ansible.utils.replace_keys(target=[{'before':'interface_name', 'after':'name'}]) }}"
+ register: result
+
+- name: Assert result dicts
+ assert:
+ that:
+ - replace['default'] == result['msg']
diff --git a/tests/integration/targets/utils_replace_keys/vars/main.yaml b/tests/integration/targets/utils_replace_keys/vars/main.yaml
new file mode 100644
index 0000000..dd7083c
--- /dev/null
+++ b/tests/integration/targets/utils_replace_keys/vars/main.yaml
@@ -0,0 +1,25 @@
+---
+replace:
+ default:
+ - name: eth0
+ enabled: true
+ duplex: auto
+ speed: auto
+ - name: eth1
+ description: Configured by Ansible - Interface 1
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ enabled: true
+ vifs:
+ - vlan_id: 100
+ description: Eth1 - VIF 100
+ mtu: 400
+ enabled: true
+ - vlan_id: 101
+ description: Eth1 - VIF 101
+ enabled: true
+ - name: eth2
+ description: Configured by Ansible - Interface 2 (ADMIN DOWN)
+ mtu: 600
+ enabled: false
diff --git a/tests/unit/plugins/filter/test_keep_keys.py b/tests/unit/plugins/filter/test_keep_keys.py
new file mode 100644
index 0000000..33ff685
--- /dev/null
+++ b/tests/unit/plugins/filter/test_keep_keys.py
@@ -0,0 +1,233 @@
+# -*- 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)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import unittest
+from ansible.errors import AnsibleFilterError
+from ansible_collections.ansible.utils.plugins.filter.keep_keys import (
+ _keep_keys,
+)
+
+
+class TestKeepKeys(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def test_keep_filter_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["interface_name", "is_enabled"]
+ output = [
+ {"interface_name": "eth0"},
+ {
+ "is_enabled": True,
+ "vifs": [{"is_enabled": True}, {"is_enabled": True}],
+ "interface_name": "eth1",
+ },
+ {"is_enabled": True, "interface_name": "eth2"},
+ ]
+ args = ["", data, target]
+
+ result = _keep_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_keep_filter_match_starts_with_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["interface", "is_"]
+ output = [
+ {"interface_name": "eth0"},
+ {
+ "is_enabled": True,
+ "vifs": [{"is_enabled": True}, {"is_enabled": True}],
+ "interface_name": "eth1",
+ },
+ {"is_enabled": True, "interface_name": "eth2"},
+ ]
+ args = ["", data, target, "starts_with"]
+
+ result = _keep_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_keep_filter_match_ends_with_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["_name", "_enabled"]
+ output = [
+ {"interface_name": "eth0"},
+ {
+ "is_enabled": True,
+ "vifs": [{"is_enabled": True}, {"is_enabled": True}],
+ "interface_name": "eth1",
+ },
+ {"is_enabled": True, "interface_name": "eth2"},
+ ]
+ args = ["", data, target, "ends_with"]
+
+ result = _keep_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_keep_filter_match_regex_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["^interface_name$", "is_enabled"]
+ output = [
+ {"interface_name": "eth0"},
+ {
+ "is_enabled": True,
+ "vifs": [{"is_enabled": True}, {"is_enabled": True}],
+ "interface_name": "eth1",
+ },
+ {"is_enabled": True, "interface_name": "eth2"},
+ ]
+ args = ["", data, target, "regex"]
+
+ result = _keep_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_invalid_data(self):
+ self.maxDiff = None
+ target = ["a", "b"]
+ args = ["", "string data", target]
+ with self.assertRaises(AnsibleFilterError) as error:
+ _keep_keys(*args)
+ self.assertIn(
+ "Error when using plugin 'keep_keys'", str(error.exception)
+ )
diff --git a/tests/unit/plugins/filter/test_remove_keys.py b/tests/unit/plugins/filter/test_remove_keys.py
new file mode 100644
index 0000000..2b81a0f
--- /dev/null
+++ b/tests/unit/plugins/filter/test_remove_keys.py
@@ -0,0 +1,310 @@
+# -*- 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)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import unittest
+from ansible.errors import AnsibleFilterError
+from ansible_collections.ansible.utils.plugins.filter.remove_keys import (
+ _remove_keys,
+)
+
+
+class TestReplaceKeys(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def test_remove_filter_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ "extra": "remove extra",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ "comment": ["comment A", "comment B"],
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["extra", "comment"]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "is_enabled": True,
+ "vifs": [
+ {
+ "is_enabled": True,
+ "description": "Eth1 - VIF 100",
+ "vlan_id": 100,
+ "mtu": 400,
+ },
+ {
+ "is_enabled": True,
+ "description": "Eth1 - VIF 101",
+ "vlan_id": 101,
+ },
+ ],
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "mtu": 1500,
+ "interface_name": "eth1",
+ "speed": "auto",
+ },
+ {
+ "is_enabled": True,
+ "interface_name": "eth2",
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "mtu": 600,
+ },
+ ]
+ args = ["", data, target]
+
+ result = _remove_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_remove_filter_match_starts_with_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["is_"]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "vlan_id": 100,
+ "mtu": 400,
+ },
+ {"description": "Eth1 - VIF 101", "vlan_id": 101},
+ ],
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "mtu": 1500,
+ "interface_name": "eth1",
+ "speed": "auto",
+ },
+ {
+ "interface_name": "eth2",
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "mtu": 600,
+ },
+ ]
+ args = ["", data, target, "starts_with"]
+
+ result = _remove_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_replace_filter_match_ends_with_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["_enabled"]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "vlan_id": 100,
+ "mtu": 400,
+ },
+ {"description": "Eth1 - VIF 101", "vlan_id": 101},
+ ],
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "mtu": 1500,
+ "interface_name": "eth1",
+ "speed": "auto",
+ },
+ {
+ "interface_name": "eth2",
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "mtu": 600,
+ },
+ ]
+ args = ["", data, target, "ends_with"]
+
+ result = _remove_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_replace_filter_match_regex_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = ["^desc"]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "is_enabled": True,
+ "vifs": [
+ {"is_enabled": True, "vlan_id": 100, "mtu": 400},
+ {"is_enabled": True, "vlan_id": 101},
+ ],
+ "duplex": "auto",
+ "mtu": 1500,
+ "interface_name": "eth1",
+ "speed": "auto",
+ },
+ {"is_enabled": True, "interface_name": "eth2", "mtu": 600},
+ ]
+ args = ["", data, target, "regex"]
+
+ result = _remove_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_invalid_data(self):
+ self.maxDiff = None
+ target = [{"before": "pre", "after": "post"}]
+ args = ["", "string data", target]
+ with self.assertRaises(AnsibleFilterError) as error:
+ _remove_keys(*args)
+ self.assertIn(
+ "Error when using plugin 'remove_keys'", str(error.exception)
+ )
diff --git a/tests/unit/plugins/filter/test_replace_keys.py b/tests/unit/plugins/filter/test_replace_keys.py
new file mode 100644
index 0000000..bc6eda8
--- /dev/null
+++ b/tests/unit/plugins/filter/test_replace_keys.py
@@ -0,0 +1,349 @@
+# -*- 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)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import unittest
+from ansible.errors import AnsibleFilterError
+from ansible_collections.ansible.utils.plugins.filter.replace_keys import (
+ _replace_keys,
+)
+
+
+class TestReplaceKeys(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def test_replace_filter_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = [
+ {"before": "interface_name", "after": "name"},
+ {"before": "is_enabled", "after": "enabled"},
+ ]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "speed": "auto",
+ "name": "eth0",
+ },
+ {
+ "vifs": [
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 100",
+ "vlan_id": 100,
+ "mtu": 400,
+ },
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 101",
+ "vlan_id": 101,
+ },
+ ],
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "name": "eth1",
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "enabled": True,
+ "mtu": 600,
+ "name": "eth2",
+ },
+ ]
+ args = ["", data, target]
+
+ result = _replace_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_replace_filter_match_starts_with_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = [
+ {"before": "interface", "after": "name"},
+ {"before": "is", "after": "enabled"},
+ ]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "speed": "auto",
+ "name": "eth0",
+ },
+ {
+ "vifs": [
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 100",
+ "vlan_id": 100,
+ "mtu": 400,
+ },
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 101",
+ "vlan_id": 101,
+ },
+ ],
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "name": "eth1",
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "enabled": True,
+ "mtu": 600,
+ "name": "eth2",
+ },
+ ]
+ args = ["", data, target, "starts_with"]
+
+ result = _replace_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_replace_filter_match_ends_with_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = [
+ {"before": "ame", "after": "name"},
+ {"before": "enabled", "after": "enabled"},
+ ]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "speed": "auto",
+ "name": "eth0",
+ },
+ {
+ "vifs": [
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 100",
+ "vlan_id": 100,
+ "mtu": 400,
+ },
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 101",
+ "vlan_id": 101,
+ },
+ ],
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "name": "eth1",
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "enabled": True,
+ "mtu": 600,
+ "name": "eth2",
+ },
+ ]
+ args = ["", data, target, "ends_with"]
+
+ result = _replace_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_replace_filter_match_regex_plugin(self):
+ data = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "interface_name": "eth0",
+ "speed": "auto",
+ },
+ {
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "interface_name": "eth1",
+ "is_enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "vifs": [
+ {
+ "description": "Eth1 - VIF 100",
+ "is_enabled": True,
+ "mtu": 400,
+ "vlan_id": 100,
+ },
+ {
+ "description": "Eth1 - VIF 101",
+ "is_enabled": True,
+ "vlan_id": 101,
+ },
+ ],
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "interface_name": "eth2",
+ "is_enabled": True,
+ "mtu": 600,
+ },
+ ]
+ target = [
+ {"before": "^interface_name$", "after": "name"},
+ {"before": "is_enabled", "after": "enabled"},
+ ]
+ output = [
+ {
+ "duplex": "auto",
+ "enabled": True,
+ "speed": "auto",
+ "name": "eth0",
+ },
+ {
+ "vifs": [
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 100",
+ "vlan_id": 100,
+ "mtu": 400,
+ },
+ {
+ "enabled": True,
+ "description": "Eth1 - VIF 101",
+ "vlan_id": 101,
+ },
+ ],
+ "description": "Configured by Ansible - Interface 1",
+ "duplex": "auto",
+ "enabled": True,
+ "mtu": 1500,
+ "speed": "auto",
+ "name": "eth1",
+ },
+ {
+ "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)",
+ "enabled": True,
+ "mtu": 600,
+ "name": "eth2",
+ },
+ ]
+ args = ["", data, target, "regex"]
+
+ result = _replace_keys(*args)
+ self.assertEqual(result, output)
+
+ def test_invalid_data(self):
+ self.maxDiff = None
+ target = [{"before": "pre", "after": "post"}]
+ args = ["", "string data", target]
+ with self.assertRaises(AnsibleFilterError) as error:
+ _replace_keys(*args)
+ self.assertIn(
+ "Error when using plugin 'replace_keys'", str(error.exception)
+ )