review pt 1

pull/156/head
KB-perByte 2022-04-08 23:31:45 +05:30
parent 792aa2731c
commit 595c9ee38f
No known key found for this signature in database
GPG Key ID: F49472BE1744C3D5
7 changed files with 128 additions and 123 deletions

View File

@ -10,8 +10,8 @@ The Ansible ``ansible.utils`` collection includes a variety of plugins that aid
This collection has been tested against following Ansible versions: **>=2.9.10**.
For collections that support Ansible 2.9, please ensure you update your `network_os` to use the
fully qualified collection name (for example, `cisco.ios.ios`).
For collections that support Ansible 2.9, please ensure you update your `network_os` to use the
fully qualified collection name (for example, `cisco.ios.ios`).
Plugins and modules within a collection may be tested with only specific Ansible versions.
A collection may contain metadata that identifies these versions.
PEP440 is the schema used to describe the versions of Ansible.

View File

@ -1,5 +1,3 @@
---
minor_changes:
- "'consolidate' filter plugin added."
trivial:
- Fix sanity issues and update black version.

View File

@ -19,7 +19,7 @@ Synopsis
--------
- This plugin presents collective structured data including all supplied facts grouping on common attributes mentioned.
- All other boolean parameter defaults to False unless parameters is explicitly mentioned.
- Using the parameters below- ``data_source|ansible.utils.consolidate(fail_missing_match_key=False``))
- Using the parameters below- ``data_sources|ansible.utils.consolidate(fail_missing_match_key=False``))
@ -39,7 +39,7 @@ Parameters
<tr>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>data_source</b>
<b>data_sources</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
@ -98,7 +98,7 @@ Parameters
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>prefix</b>
<b>name</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
@ -110,7 +110,7 @@ Parameters
<td>
</td>
<td>
<div>Specify the prefix with which the result set be created.</div>
<div>Specify the name with which the result set be created.</div>
</td>
</tr>
@ -126,13 +126,13 @@ Parameters
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>no</li>
<li>yes</li>
<li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
</ul>
</td>
<td>
</td>
<td>
<div>Fail if duplicate values for any key is found.</div>
<div>Fail if the match key&#x27;s value exists more than once in a given data set.</div>
</td>
</tr>
<tr>
@ -147,7 +147,7 @@ Parameters
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>no</li>
<li>yes</li>
<li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
</ul>
</td>
<td>
@ -168,13 +168,13 @@ Parameters
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>no</li>
<li>yes</li>
<li><div style="color: blue"><b>yes</b>&nbsp;&larr;</div></li>
</ul>
</td>
<td>
</td>
<td>
<div>Fail if a keys to match in not same accross all data sets.</div>
<div>Fail if the match key&#x27;s value is not found in every data source.</div>
</td>
</tr>
</table>
@ -280,25 +280,25 @@ Examples
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=False) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=False) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -344,7 +344,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -402,7 +402,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -428,7 +428,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },
@ -663,25 +663,25 @@ Examples
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=True) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=True) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -727,7 +727,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -785,7 +785,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -811,7 +811,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },
@ -916,25 +916,25 @@ Examples
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_key=True) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_key=True) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -980,7 +980,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -1038,7 +1038,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -1064,7 +1064,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },
@ -1173,25 +1173,25 @@ Examples
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_duplicate=True) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_duplicate=True) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -1237,7 +1237,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -1298,7 +1298,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -1324,7 +1324,7 @@ Examples
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },

View File

@ -20,9 +20,9 @@ DOCUMENTATION = """
description:
- This plugin presents collective structured data including all supplied facts grouping on common attributes mentioned.
- All other boolean parameter defaults to False unless parameters is explicitly mentioned.
- Using the parameters below- C(data_source|ansible.utils.consolidate(fail_missing_match_key=False)))
- Using the parameters below- C(data_sources|ansible.utils.consolidate(fail_missing_match_key=False)))
options:
data_source:
data_sources:
description:
- This option represents a list of dictionaries to perform the operation on.
- For example C(facts_source|ansible.utils.consolidate(fail_missing_match_key=False))), in this case C(facts_source) represents this option.
@ -38,19 +38,22 @@ DOCUMENTATION = """
description: Specify key to match on.
type: str
required: True
prefix:
description: Specify the prefix with which the result set be created.
name:
description: Specify the name with which the result set be created.
type: str
required: True
fail_missing_match_key:
description: Fail if match_key is not found in a specific data set.
type: bool
default: True
fail_missing_match_value:
description: Fail if a keys to match in not same accross all data sets.
description: Fail if the match key's value is not found in every data source.
type: bool
default: True
fail_duplicate:
description: Fail if duplicate values for any key is found.
description: Fail if the match key's value exists more than once in a given data set.
type: bool
default: True
"""
EXAMPLES = r"""
@ -147,25 +150,25 @@ vars_files:
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=False) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=False) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -211,7 +214,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -269,7 +272,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -295,7 +298,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },
@ -530,25 +533,25 @@ vars_files:
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=True) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=True) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -594,7 +597,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -652,7 +655,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -678,7 +681,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },
@ -783,25 +786,25 @@ vars_files:
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_key=True) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_key=True) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -847,7 +850,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -905,7 +908,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -931,7 +934,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },
@ -1040,25 +1043,25 @@ vars_files:
tasks:
- name: Build the facts collection
set_fact:
data_source:
data_sources:
- data: "{{ interfaces }}"
match_key: name
prefix: interfaces
name: interfaces
- data: "{{ l2_interfaces }}"
match_key: name
prefix: l2_interfaces
name: l2_interfaces
- data: "{{ l3_interfaces }}"
match_key: name
prefix: l3_interfaces
name: l3_interfaces
- name: Combine all the facts based on match_keys
set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_duplicate=True) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_duplicate=True) }}"
##Output
# ok: [localhost] => {
# "ansible_facts": {
# "data_source": [
# "data_sources": [
# {
# "data": [
# {
@ -1104,7 +1107,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "interfaces"
# "name": "interfaces"
# },
# {
# "data": [
@ -1165,7 +1168,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l2_interfaces"
# "name": "l2_interfaces"
# },
# {
# "data": [
@ -1191,7 +1194,7 @@ tasks:
# }
# ],
# "match_key": "name",
# "prefix": "l3_interfaces"
# "name": "l3_interfaces"
# }
# ]
# },
@ -1224,7 +1227,7 @@ def _consolidate(*args, **kwargs):
"""Consolidate facts together on common attributes"""
keys = [
"data_source",
"data_sources",
"fail_missing_match_key",
"fail_missing_match_value",
"fail_duplicate",

View File

@ -33,7 +33,7 @@ def _raise_error(filter, msg):
def fail_on_filter(validator_func):
"""decorator to fail on supplied filters
"""Decorator to fail on supplied filters
Args:
validator_func (func): Function that generates failure messages
@ -43,7 +43,11 @@ def fail_on_filter(validator_func):
"""
def update_err(*args, **kwargs):
"""Filters return value or raises error as per supplied parameters
Returns:
any: Return value to the function call
"""
res, err = validator_func(*args, **kwargs)
if err.get("match_key_err"):
_raise_error(
@ -64,8 +68,8 @@ def fail_on_filter(validator_func):
def check_missing_match_key_duplicate(
data_sources, fail_missing_match_key, fail_duplicate
):
"""Checks if the match_key specified is present in all the supplied data,
also checks for duplicate data accross all the data sources
"""Check if the match_key specified is present in all the supplied data,
also check for duplicate data accross all the data sources
Args:
data_sources (list): list of dicts as data sources
@ -75,11 +79,11 @@ def check_missing_match_key_duplicate(
list: list of unique keys based on specified match_keys
"""
results, errors_match_key, errors_duplicate = [], [], []
for ds_idx, data_source in enumerate(data_sources):
for ds_idx, data_source in enumerate(data_sources, start=1):
match_key = data_source["match_key"]
ds_values = []
for dd_idx, data_dict in enumerate(data_source["data"]):
for dd_idx, data_dict in enumerate(data_source["data"], start=1):
try:
ds_values.append(data_dict[match_key])
except KeyError:
@ -117,7 +121,7 @@ def check_missing_match_values(matched_keys, fail_missing_match_value):
errors_match_values = []
all_values = set(itertools.chain.from_iterable(matched_keys))
if fail_missing_match_value:
for ds_idx, ds_values in enumerate(matched_keys):
for ds_idx, ds_values in enumerate(matched_keys, start=1):
missing_match = all_values - ds_values
if missing_match:
m_matches = ", ".join(missing_match)
@ -143,7 +147,7 @@ def consolidate_facts(data_sources, all_values):
consolidated_facts = {}
for data_source in data_sources:
match_key = data_source["match_key"]
source = data_source["prefix"]
source = data_source["name"]
data_dict = {
d[match_key]: d for d in data_source["data"] if match_key in d
}
@ -155,7 +159,7 @@ def consolidate_facts(data_sources, all_values):
def consolidate(
data_source,
data_sources,
fail_missing_match_key=False,
fail_missing_match_value=False,
fail_duplicate=False,
@ -173,8 +177,8 @@ def consolidate(
"""
key_sets = check_missing_match_key_duplicate(
data_source, fail_missing_match_key, fail_duplicate
data_sources, fail_missing_match_key, fail_duplicate
)
key_vals = check_missing_match_values(key_sets, fail_missing_match_value)
consolidated_facts = consolidate_facts(data_source, key_vals)
consolidated_facts = consolidate_facts(data_sources, key_vals)
return consolidated_facts

View File

@ -1,14 +1,14 @@
---
- name: Build the data structure
ansible.builtin.set_fact:
data_source:
data_sources:
- data:
[
{ "name": "GigabitEthernet0/1" },
{ "name": "GigabitEthernet0/2" },
]
match_key: name
prefix: acl_interfaces
name: acl_interfaces
- data:
[
{
@ -23,11 +23,11 @@
},
]
match_key: name
prefix: interfaces
name: interfaces
- name: Combine all the dictionaries based on match_keys
ansible.builtin.set_fact:
combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=False) }}"
combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=False) }}"
- name: Assert result dicts
assert:

View File

@ -19,7 +19,7 @@ class TestConsolidate(unittest.TestCase):
pass
def test_consolidate_plugin(self):
data_source = [
data_sources = [
{
"data": [
{
@ -60,7 +60,7 @@ class TestConsolidate(unittest.TestCase):
},
],
"match_key": "name",
"prefix": "interfaces",
"name": "interfaces",
},
{
"data": [
@ -116,7 +116,7 @@ class TestConsolidate(unittest.TestCase):
},
],
"match_key": "name",
"prefix": "l2_interfaces",
"name": "l2_interfaces",
},
{
"data": [
@ -130,7 +130,7 @@ class TestConsolidate(unittest.TestCase):
{"name": "Loopback999"},
],
"match_key": "name",
"prefix": "l3_interfaces",
"name": "l3_interfaces",
},
]
@ -244,13 +244,13 @@ class TestConsolidate(unittest.TestCase):
"l3_interfaces": {"name": "Loopback999"},
},
}
args = ["", data_source]
args = ["", data_sources]
result = _consolidate(*args)
self.assertEqual(result, output)
def test_fail_missing_match_key(self):
data_source = [
data_sources = [
{
"data": [
{
@ -291,7 +291,7 @@ class TestConsolidate(unittest.TestCase):
},
],
"match_key": "name",
"prefix": "interfaces",
"name": "interfaces",
},
{
"data": [
@ -347,7 +347,7 @@ class TestConsolidate(unittest.TestCase):
},
],
"match_key": "name",
"prefix": "l2_interfaces",
"name": "l2_interfaces",
},
{
"data": [
@ -361,21 +361,21 @@ class TestConsolidate(unittest.TestCase):
{"name": "Loopback999"},
],
"match_key": "name",
"prefix": "l3_interfaces",
"name": "l3_interfaces",
},
]
fail_missing_match_key = True
args = ["", data_source, fail_missing_match_key]
args = ["", data_sources, fail_missing_match_key]
with self.assertRaises(AnsibleFilterError) as error:
_consolidate(*args)
self.assertIn(
"Error when using plugin 'consolidate': 'fail_missing_match_key' reported Missing match key 'name' in data source 2 in list entry 0",
"Error when using plugin 'consolidate': 'fail_missing_match_key' reported Missing match key 'name' in data source 3 in list entry 1",
str(error.exception),
)
def test_fail_missing_match_value(self):
data_source = [
data_sources = [
{
"data": [
{
@ -416,7 +416,7 @@ class TestConsolidate(unittest.TestCase):
},
],
"match_key": "name",
"prefix": "interfaces",
"name": "interfaces",
},
{
"data": [
@ -472,7 +472,7 @@ class TestConsolidate(unittest.TestCase):
},
],
"match_key": "name",
"prefix": "l2_interfaces",
"name": "l2_interfaces",
},
{
"data": [
@ -490,7 +490,7 @@ class TestConsolidate(unittest.TestCase):
{"name": "Loopback999"},
],
"match_key": "name",
"prefix": "l3_interfaces",
"name": "l3_interfaces",
},
]
@ -499,7 +499,7 @@ class TestConsolidate(unittest.TestCase):
fail_duplicate = True
args = [
"",
data_source,
data_sources,
fail_missing_match_key,
fail_missing_match_value,
fail_duplicate,
@ -507,6 +507,6 @@ class TestConsolidate(unittest.TestCase):
with self.assertRaises(AnsibleFilterError) as error:
_consolidate(*args)
self.assertIn(
"Error when using plugin 'consolidate': 'fail_duplicate' reported Duplicate values in data source 2",
"Error when using plugin 'consolidate': 'fail_duplicate' reported Duplicate values in data source 3",
str(error.exception),
)