Skip to content

Commit

Permalink
[minor_change] Addition of a filter plugin called aci_listify to the …
Browse files Browse the repository at this point in the history
…collection which flattens nested dictionaries
  • Loading branch information
shrsr committed Oct 31, 2023
1 parent 939924a commit 269b382
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 0 deletions.
152 changes: 152 additions & 0 deletions plugins/filter/listify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Copyright: (c) 2017, Ramses Smeyers <[email protected]>
# 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

ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"}

DOCUMENTATION = r"""
name: aci_listify
short_description: Flattens the nested dictionaries representing the ACI model data.
description:
- This filter flattens and transforms the input data into a list.
- See the Examples section below.
options:
data:
description: This option represents the ACI model data which is a list of dictionaries or a dictionary with any level of nesting data.
type: raw
required: True
keys:
description: Comma separated keys of type string denoting the ACI objects.
required: True
"""

EXAMPLES = r"""
- name: Set vars
ansible.builtin.set_fact:
data:
tenant:
- name: ansible_test
description: Created using listify
app:
- name: app_test
epg:
- name: web
bd: web_bd
- name: app
bd: app_bd
bd:
- name: bd_test
subnet:
- name: 10.10.10.1
mask: 24
scope: private
vrf: vrf_test
- name: bd_test2
subnet:
- name: 20.20.20.1
mask: 24
scope: public
vrf: vrf_test
vrf:
- name: vrf_test
- name: Create tenants
cisco.aci.aci_tenant:
host: apic
username: admin
password: SomeSecretPassword
tenant: '{{ item.tenant_name }}'
description: '{{ item.tenant_description }}'
with_items: '{{ data|cisco.aci.aci_listify("tenant") }}'
- name: Create VRFs
cisco.aci.aci_vrf:
host: apic
username: admin
password: SomeSecretPassword
tenant: '{{ item.tenant_name }}'
vrf_name: '{{ item.tenant_vrf_name }}'
with_items: '{{ data|cisco.aci.aci_listify("tenant","vrf") }}'
- name: Create BDs
cisco.aci.aci_bd:
host: apic
username: admin
password: SomeSecretPassword
tenant: '{{ item.tenant_name }}'
vrf: '{{ item.tenant_bd_vrf }}'
bd: '{{ item.tenant_bd_name }}'
enable_routing: yes
with_items: '{{ data|cisco.aci.aci_listify("tenant","bd") }}'
- name: Create BD subnets
cisco.aci.aci_bd_subnet:
host: apic
username: admin
password: SomeSecretPassword
tenant: '{{ item.tenant_name }}'
bd: '{{ item.tenant_bd_name }}'
gateway: '{{ item.tenant_bd_subnet_name }}'
mask: '{{ item.tenant_bd_subnet_mask }}'
scope: '{{ item.tenant_bd_subnet_scope }}'
with_items: '{{ data|cisco.aci.aci_listify("tenant","bd","subnet") }}'
- name: Create APs
cisco.aci.aci_ap:
host: apic
username: admin
password: SomeSecretPassword
tenant: '{{ item.tenant_name }}'
app_profile: '{{ item.tenant_app_name }}'
with_items: '{{ data|cisco.aci.aci_listify("tenant","app") }}'
- name: Create EPGs
cisco.aci.aci_epg:
host: apic
username: admin
password: SomeSecretPassword
tenant: '{{ item.tenant_name }}'
app_profile: '{{ item.tenant_app_name }}'
epg: '{{ item.tenant_app_epg_name }}'
bd: '{{ item.tenant_app_epg_bd }}'
with_items: '{{ data|cisco.aci.aci_listify("tenant","app","epg") }}'
"""


def listify(d, *keys):
return listify_worker(d, keys, 0, [], {}, "")


def listify_worker(d, keys, depth, result, cache, prefix):
prefix += keys[depth] + "_"

if keys[depth] in d:
for item in d[keys[depth]]:
cache_work = cache.copy()
if isinstance(item, dict):
for k, v in item.items():
if not isinstance(v, dict) and not isinstance(v, list):
cache_key = prefix + k
cache_value = v
cache_work[cache_key] = cache_value

if len(keys) - 1 == depth:
result.append(cache_work)
else:
for k, v in item.items():
if k == keys[depth + 1]:
if isinstance(v, dict) or isinstance(v, list):
result = listify_worker({k: v}, keys, depth + 1, result, cache_work, prefix)
return result


class FilterModule(object):
"""Ansible core jinja2 filters"""

def filters(self):
return {
"aci_listify": listify,
}
2 changes: 2 additions & 0 deletions tests/integration/targets/aci_filter_listify/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# No ACI simulator yet, so not enabled
# unsupported
114 changes: 114 additions & 0 deletions tests/integration/targets/aci_filter_listify/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Test code for the ACI modules
# Copyright: (c) 2023, Shreyas Srish ([email protected])
#
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

- name: Test that we have an ACI APIC host, ACI username and ACI password
fail:
msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.'
when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined

- name: Set vars
ansible.builtin.set_fact:
aci_info: &aci_info
host: "{{ aci_hostname }}"
username: "{{ aci_username }}"
password: "{{ aci_password }}"
validate_certs: '{{ aci_validate_certs | default(false) }}'
use_ssl: '{{ aci_use_ssl | default(true) }}'
use_proxy: '{{ aci_use_proxy | default(true) }}'
output_level: '{{ aci_output_level | default("info") }}'
aci_model_data:
tenant:
- name: ansible_test
description: Created using listify
app:
- name: app_test
epg:
- name: web
bd: web_bd
- name: app
bd: app_bd
bd:
- name: bd_test
subnet:
- name: 10.10.10.1
mask: 24
scope: private
vrf: vrf_test
- name: bd_test2
subnet:
- name: 20.20.20.1
mask: 24
scope: public
vrf: vrf_test
vrf:
- name: vrf_test

- name: Create tenants
cisco.aci.aci_tenant:
<<: *aci_info
tenant: '{{ item.tenant_name }}'
description: '{{ item.tenant_description }}'
with_items: '{{ aci_model_data|cisco.aci.aci_listify("tenant") }}'
register: tenant_listify

- name: Create VRFs
cisco.aci.aci_vrf:
<<: *aci_info
tenant: '{{ item.tenant_name }}'
vrf_name: '{{ item.tenant_vrf_name }}'
with_items: '{{ aci_model_data|cisco.aci.aci_listify("tenant","vrf") }}'
register: vrf_listify

- name: Create BDs
cisco.aci.aci_bd:
<<: *aci_info
tenant: '{{ item.tenant_name }}'
vrf: '{{ item.tenant_bd_vrf }}'
bd: '{{ item.tenant_bd_name }}'
enable_routing: yes
with_items: '{{ aci_model_data|cisco.aci.aci_listify("tenant","bd") }}'
register: bd_listify

- name: Create BD subnets
cisco.aci.aci_bd_subnet:
<<: *aci_info
tenant: '{{ item.tenant_name }}'
bd: '{{ item.tenant_bd_name }}'
gateway: '{{ item.tenant_bd_subnet_name }}'
mask: '{{ item.tenant_bd_subnet_mask }}'
scope: '{{ item.tenant_bd_subnet_scope }}'
with_items: '{{ aci_model_data|cisco.aci.aci_listify("tenant","bd","subnet") }}'
register: bd_subnets_listify

- name: Create APs
cisco.aci.aci_ap:
<<: *aci_info
tenant: '{{ item.tenant_name }}'
app_profile: '{{ item.tenant_app_name }}'
with_items: '{{ aci_model_data|cisco.aci.aci_listify("tenant","app") }}'
register: ap_listify

- name: Create EPGs
cisco.aci.aci_epg:
<<: *aci_info
tenant: '{{ item.tenant_name }}'
app_profile: '{{ item.tenant_app_name }}'
epg: '{{ item.tenant_app_epg_name }}'
bd: '{{ item.tenant_app_epg_bd }}'
with_items: '{{ aci_model_data|cisco.aci.aci_listify("tenant","app","epg") }}'
register: epg_listify

- name: Validate listify
assert:
that:
- tenant_listify.results.0.current.0.fvTenant.attributes.name == "ansible_test"
- vrf_listify.results.0.current.0.fvCtx.attributes.name == "vrf_test"
- bd_listify.results.0.current.0.fvBD.attributes.name == "bd_test"
- bd_listify.results.1.current.0.fvBD.attributes.name == "bd_test2"
- bd_subnets_listify.results.0.current.0.fvSubnet.attributes.ip == "10.10.10.1/24"
- bd_subnets_listify.results.1.current.0.fvSubnet.attributes.ip == "20.20.20.1/24"
- ap_listify.results.0.current.0.fvAp.attributes.name == "app_test"
- epg_listify.results.0.current.0.fvAEPg.attributes.name == "web"
- epg_listify.results.1.current.0.fvAEPg.attributes.name == "app"

0 comments on commit 269b382

Please sign in to comment.