resource_list_compare filter plugin added (#89)

resource_list_compare filter plugin added

Signed-off-by: Rohit Thakur rohitthakur2590@outlook.com
SUMMARY

resolves: #88

Test Coverage
plugins/filter/param_list_compare.py	 	95%
tests/unit/plugins/filter/test_param_list_compare.py	 	100%
ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ADDITIONAL INFORMATION

Reviewed-by: Ganesh Nalawade <None>
Reviewed-by: Bradley A. Thornton <bthornto@redhat.com>
Reviewed-by: Rohit Thakur <rohitthakur2590@outlook.com>
Reviewed-by: None <None>
pull/91/head
Rohit Thakur 2021-08-12 10:45:44 +05:30 committed by GitHub
parent 61a826c997
commit 2f071599d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 360 additions and 0 deletions

View File

@ -0,0 +1,3 @@
---
minor_changes:
- Add new plugin param_list_compare that generates the final param list after comparing base and provided/target param list.

View File

@ -0,0 +1,195 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
name: param_list_compare
author: Rohit Thakur (@rohitthakur2590)
version_added: "2.4.0"
short_description: Generate the final param list combining/comparing base and provided parameters.
description:
- Generate the final list of parameters after comparing with base list and provided/target list of params/bangs.
options:
base:
description: Specify the base list.
type: list
elements: str
target:
description: Specify the target list.
type: list
elements: str
"""
EXAMPLES = r"""
- set_fact:
base: ['1','2','3','4','5']
- set_fact:
target: ['!all','2','4']
- name: Get final list of parameters
register: result
set_fact:
final_params: "{{ base|param_list_compare(target) }}"
# TASK [Target list] **********************************************************
# ok: [localhost] => {
# "msg": {
# "actionable": [
# "2",
# "4"
# ],
# "unsupported": []
# }
# }
# Network Specific Example
# -----------
- set_fact:
ios_resources:
- "acl_interfaces"
- "acls"
- "bgp_address_family"
- "bgp_global"
- "interfaces"
- "l2_interfaces"
- "l3_interfaces"
- "lacp"
- "lacp_interfaces"
- "lag_interfaces"
- "lldp_global"
- "lldp_interfaces"
- "logging_global"
- "ospf_interfaces"
- "ospfv2"
- "ospfv3"
- "prefix_lists"
- "route_maps"
- "static_routes"
- "vlans"
- set_fact:
target_resources:
- '!all'
- 'vlan'
- 'bgp_global'
- name: Get final list of target resources/params
register: result
set_fact:
network_resources: "{{ ios_resources|param_list_compare(target_resources) }}"
- name: Target list of network resources
debug:
msg: "{{ network_resources }}"
# TASK [Target list of network resources] *******************************************************************************************************************
# ok: [localhost] => {
# "msg": {
# "actionable": [
# "bgp_global",
# "vlans"
# ],
# "unsupported": []
# }
# }
- name: Get final list of target resources/params
register: result
set_fact:
network_resources: "{{ ios_resources|param_list_compare(target=['vla', 'ntp_global', 'logging_global']) }}"
- name: Target list of network resources
debug:
msg: "{{ network_resources }}"
# TASK [Target list of network resources] ************************************************
# ok: [localhost] => {
# "msg": {
# "actionable": [
# "logging_global"
# ],
# "unsupported": [
# "vla",
# "ntp_global"
# ]
# }
# }
"""
RETURN = """
actionable:
description: list of combined params
type: list
unsupported:
description: list of unsupported params
type: list
"""
from ansible.errors import AnsibleFilterError
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
check_argspec,
)
ARGSPEC_CONDITIONALS = {}
def param_list_compare(*args, **kwargs):
params = ["base", "target"]
data = dict(zip(params, args))
data.update(kwargs)
if len(data) < 2:
raise AnsibleFilterError(
"Missing either 'base' or 'other value in filter input,"
"refer 'ansible.utils.param_list_compare' filter plugin documentation for details"
)
valid, argspec_result, updated_params = check_argspec(
DOCUMENTATION,
"param_list_compare filter",
schema_conditionals=ARGSPEC_CONDITIONALS,
**data
)
if not valid:
raise AnsibleFilterError(
"{argspec_result} with errors: {argspec_errors}".format(
argspec_result=argspec_result.get("msg"),
argspec_errors=argspec_result.get("errors"),
)
)
base = data["base"]
other = data["target"]
combined = []
alls = [x for x in other if x == "all"]
bangs = [x[1:] for x in other if x.startswith("!")]
rbangs = [x for x in other if x.startswith("!")]
remain = [
x for x in other if x not in alls and x not in rbangs and x in base
]
unsupported = [
x for x in other if x not in alls and x not in rbangs and x not in base
]
if alls:
combined = base
for entry in bangs:
if entry in combined:
combined.remove(entry)
for entry in remain:
if entry not in combined:
combined.append(entry)
combined.sort()
output = {"actionable": combined, "unsupported": unsupported}
return output
class FilterModule(object):
""" param_list_compare """
def filters(self):
"""a mapping of filter names to functions"""
return {"param_list_compare": param_list_compare}

View File

@ -0,0 +1,56 @@
---
- debug:
msg: "START param_list_compare integration tests on connection={{ ansible_connection }}"
- name: Setup supported resource module list json
ansible.builtin.set_fact:
network_resources:
modules:
- 'acl_interfaces'
- 'acls'
- 'bgp_address_family'
- 'bgp_global'
- 'interfaces'
- 'l2_interfaces'
- 'l3_interfaces'
- 'lacp'
- 'lacp_interfaces'
- 'lag_interfaces'
- 'lldp_global'
- 'lldp_interfaces'
- 'logging_global'
- 'ospf_interfaces'
- 'ospfv2'
- 'ospfv3'
- 'prefix_lists'
- 'route_maps'
- 'static_routes'
- 'vlans'
- name: Setup target resources with bangs
ansible.builtin.set_fact:
provided_resources:
- '!all'
- '!acl_interfaces'
- 'acls'
- 'bgp_address_family'
- 'bgp_global'
- name: Setup target resources with bangs
ansible.builtin.set_fact:
expected_network_resources:
- 'acls'
- 'bgp_address_family'
- 'bgp_global'
- name: Get the final list of resources
set_fact:
final_network_resources: "{{ network_resources['modules']|ansible.utils.param_list_compare(provided_resources) }}"
- name: Assert final network resources
assert:
that:
- "{{ expected_network_resources | symmetric_difference(final_network_resources['actionable']) |length\
\ == 0 }}"
- debug:
msg: "END param_list_compare integration tests on connection={{ ansible_connection }}"

View File

@ -0,0 +1,13 @@
---
- name: Recursively find all test files
find:
file_type: file
paths: "{{ role_path }}/tasks/include"
recurse: true
use_regex: true
patterns:
- '^(?!_).+$'
register: found
- include: "{{ item.path }}"
loop: "{{ found.files }}"

View File

@ -0,0 +1,93 @@
# -*- 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.param_list_compare import (
param_list_compare,
)
class TestParam_list_compare_merge(unittest.TestCase):
def test_valid_data(self):
"""Check passing valid data as per criteria"""
base = ["interfaces", "l2_interfaces", "l3_interfaces"]
other = ["all"]
args = [base, other]
kwargs = {}
result = param_list_compare(*args, **kwargs)
self.assertEqual(result["actionable"], base)
def test_valid_data_same_contents(self):
"""Check passing valid data as per criteria"""
base = ["interfaces", "l2_interfaces", "l3_interfaces"]
other = ["interfaces", "l2_interfaces", "l3_interfaces"]
args = [base, other]
kwargs = {}
result = param_list_compare(*args, **kwargs)
self.assertEqual(result["actionable"], base)
def test_valid_data_with_not_bang(self):
"""Check passing valid data as per criteria"""
base = ["interfaces", "l2_interfaces", "l3_interfaces"]
other = ["!l2_interfaces", "all"]
args = [base, other]
expected = ["interfaces", "l3_interfaces"]
kwargs = {}
result = param_list_compare(*args, **kwargs)
self.assertEqual(result["actionable"], expected)
def test_invalid_args_length_data(self):
"""Check passing valid data as per criteria"""
base = {}
args = [base]
kwargs = {}
with self.assertRaises(AnsibleFilterError) as error:
param_list_compare(*args, **kwargs)
self.assertIn(
"Missing either 'base' or 'other value in filter input",
str(error.exception),
)
def test_invalid_base_type_data(self):
"""Check passing valid data as per criteria"""
base = {}
other = ["all"]
args = [base, other]
kwargs = {}
with self.assertRaises(AnsibleFilterError) as error:
param_list_compare(*args, **kwargs)
self.assertIn("cannot be converted to a list", str(error.exception))
def test_invalid_other_type_data(self):
"""Check passing valid data as per criteria"""
base = ["interfaces"]
other = {"all": None}
args = [base, other]
kwargs = {}
with self.assertRaises(AnsibleFilterError) as error:
param_list_compare(*args, **kwargs)
self.assertIn("cannot be converted to a list", str(error.exception))
def test_invalid_unsupported_bang(self):
"""Check passing valid data as per criteria"""
base = ["interfaces"]
other = ["every"]
args = [base, other]
kwargs = {}
result = param_list_compare(*args, **kwargs)
self.assertEqual(result["unsupported"], other)