Recursive filter plugins remove_keys | replace_keys | keep_keys (#110)

Recursive filter plugins remove_keys | replace_keys | keep_keys

SUMMARY

New plugins

remove_keys
replace_keys
keep_keys


ISSUE TYPE


New Module Pull Request

COMPONENT NAME


remove_keys
replace_keys
keep_keys

ADDITIONAL INFORMATION

Reviewed-by: Ashwini Mhatre <mashu97@gmail.com>
Reviewed-by: Nilashish Chakraborty <nilashishchakraborty8@gmail.com>
Reviewed-by: None <None>
pull/134/head
Sagar Paul 2022-01-27 12:22:38 +05:30 committed by GitHub
parent ea54e97359
commit 624bc76e26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3565 additions and 0 deletions

3
.gitignore vendored
View File

@ -130,3 +130,6 @@ dmypy.json
# Pyre type checker
.pyre/
# ide
.vscode

View File

@ -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.

View File

@ -0,0 +1,5 @@
---
minor_changes:
- "'keep_keys' filter plugin added."
- "'remove_keys' filter plugin added."
- "'replace_keys' filter plugin added."

View File

@ -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
<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">raw</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
</td>
<td>
<div>This option represents a list of dictionaries or a dictionary with any level of nesting data.</div>
<div>For example <code>config_data|ansible.utils.keep_keys(target([....]</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>matching_parameter</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>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>starts_with</li>
<li>ends_with</li>
<li>regex</li>
</ul>
</td>
<td>
</td>
<td>
<div>Specify the matching configuration of target keys and data attributes.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>target</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
</td>
<td>
<div>Specify the target keys to keep in list format.</div>
</td>
</tr>
</table>
<br/>
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.

View File

@ -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
<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">raw</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
</td>
<td>
<div>This option represents a list of dictionaries or a dictionary with any level of nesting data.</div>
<div>For example <code>config_data|ansible.utils.remove_keys(target([....]</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>matching_parameter</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>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>starts_with</li>
<li>ends_with</li>
<li>regex</li>
</ul>
</td>
<td>
</td>
<td>
<div>Specify the matching configuration of target keys and data attributes.</div>
</td>
</tr>
<tr>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>target</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=string</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
</td>
<td>
<div>Specify the target keys to remove in list format.</div>
</td>
</tr>
</table>
<br/>
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.

View File

@ -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
<table border=0 cellpadding=0 class="documentation-table">
<tr>
<th colspan="2">Parameter</th>
<th>Choices/<font color="blue">Defaults</font></th>
<th>Configuration</th>
<th width="100%">Comments</th>
</tr>
<tr>
<td colspan="2">
<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">raw</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
</td>
<td>
<div>This option represents a list of dictionaries or a dictionary with any level of nesting data.</div>
<div>For example <code>config_data|ansible.utils.replace_keys(target([....]</code>)), in this case <code>config_data</code> represents this option.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>matching_parameter</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>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>starts_with</li>
<li>ends_with</li>
<li>regex</li>
</ul>
</td>
<td>
</td>
<td>
<div>Specify the matching configuration of target keys and data attributes.</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>target</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=dictionary</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
</td>
<td>
<div>Specify the target keys to replace in list of dictionaries format containing before and after key value.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>after</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>
</td>
<td>
</td>
<td>
<div>after attribute key [change to]</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>before</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>
</td>
<td>
</td>
<td>
<div>before attribute key [to change]</div>
</td>
</tr>
</table>
<br/>
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.

331
plugins/filter/keep_keys.py Normal file
View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 }}"

View File

@ -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']

View File

@ -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

View File

@ -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 }}"

View File

@ -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']

View File

@ -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

View File

@ -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 }}"

View File

@ -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']

View File

@ -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

View File

@ -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)
)

View File

@ -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)
)

View File

@ -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)
)