# # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # """ The consolidate filter plugin """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ name: consolidate author: Sagar Paul (@KB-perByte) version_added: "2.6.0" short_description: Consolidate facts together on common attributes. 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_sources|ansible.utils.consolidate(fail_missing_match_key=False))) options: 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. type: list elements: dict required: True suboptions: data: description: Specify facts data that gets consolidated. type: raw required: True match_key: description: Specify key to match on. type: str required: True 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 the match key's value is not found in every data source. type: bool default: True fail_duplicate: description: Fail if the match key's value exists more than once in a given data set. type: bool default: True """ EXAMPLES = r""" # Consolidated filter plugin example # ---------------------------------- ##play.yml tasks: - name: Define some test data ansible.builtin.set_fact: values: - name: a value: 1 - name: b value: 2 - name: c value: 3 colors: - name: a color: red - name: b color: green - name: c color: blue - name: Define some test data ansible.builtin.set_fact: base_data: - data: "{{ values }}" match_key: name name: values - data: "{{ colors }}" match_key: name name: colors - name: Consolidate the data source using the name key ansible.builtin.set_fact: consolidated: "{{ data_sources|ansible.utils.consolidate }}" vars: sizes: - name: a size: small - name: b size: medium - name: c size: large additional_data_source: - data: "{{ sizes }}" match_key: name name: sizes data_sources: "{{ base_data + additional_data_source }}" ##Output # ok: [localhost] => { # "ansible_facts": { # "consolidated": { # "a": { # "colors": { # "color": "red", # "name": "a" # }, # "sizes": { # "name": "a", # "size": "small" # }, # "values": { # "name": "a", # "value": 1 # } # }, # "b": { # "colors": { # "color": "green", # "name": "b" # }, # "sizes": { # "name": "b", # "size": "medium" # }, # "values": { # "name": "b", # "value": 2 # } # }, # "c": { # "colors": { # "color": "blue", # "name": "c" # }, # "sizes": { # "name": "c", # "size": "large" # }, # "values": { # "name": "c", # "value": 3 # } # } # } # }, # "changed": false # } name: Consolidate the data source using different keys ansible.builtin.set_fact: consolidated: "{{ data_sources|ansible.utils.consolidate }}" vars: sizes: - title: a size: small - title: b size: medium - title: c size: large additional_data_source: - data: "{{ sizes }}" match_key: title name: sizes data_sources: "{{ base_data + additional_data_source }}" ##Output # ok: [localhost] => { # "ansible_facts": { # "consolidated": { # "a": { # "colors": { # "color": "red", # "name": "a" # }, # "sizes": { # "size": "small", # "title": "a" # }, # "values": { # "name": "a", # "value": 1 # } # }, # "b": { # "colors": { # "color": "green", # "name": "b" # }, # "sizes": { # "size": "medium", # "title": "b" # }, # "values": { # "name": "b", # "value": 2 # } # }, # "c": { # "colors": { # "color": "blue", # "name": "c" # }, # "sizes": { # "size": "large", # "title": "c" # }, # "values": { # "name": "c", # "value": 3 # } # } # } # }, # "changed": false # } name: Consolidate the data source using the name key (fail_missing_match_key) ansible.builtin.set_fact: consolidated: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_key=True) }}" ignore_errors: true vars: vars: sizes: - size: small - size: medium - size: large additional_data_source: - data: "{{ sizes }}" match_key: name name: sizes data_sources: "{{ base_data + additional_data_source }}" ##Output # fatal: [localhost]: FAILED! => { # "msg": "Error when using plugin 'consolidate': 'fail_missing_match_key' # reported missing match key 'name' in data source 3 in list entry 1, # missing match key 'name' in data source 3 in list entry 2, # missing match key 'name' in data source 3 in list entry 3" # } name: Consolidate the data source using the name key (fail_missing_match_value) ansible.builtin.set_fact: consolidated: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=True) }}" ignore_errors: true vars: sizes: - name: a size: small - name: b size: medium additional_data_source: - data: "{{ sizes }}" match_key: name name: sizes data_sources: "{{ base_data + additional_data_source }}" # fatal: [localhost]: FAILED! => { # "msg": "Error when using plugin 'consolidate': 'fail_missing_match_value' # reported missing match value c in data source 3" # } name: Consolidate the data source using the name key (fail_duplicate) ansible.builtin.set_fact: consolidated: "{{ data_sources|ansible.utils.consolidate(fail_duplicate=True) }}" ignore_errors: true vars: sizes: - name: a size: small - name: a size: small additional_data_source: - data: "{{ sizes }}" match_key: name name: sizes data_sources: "{{ base_data + additional_data_source }}" # fatal: [localhost]: FAILED! => { # "msg": "Error when using plugin 'consolidate': 'fail_duplicate' # reported duplicate values in data source 3" # } ##facts.yml interfaces: - name: GigabitEthernet0/0 enabled: true duplex: auto speed: auto note: - Connected green wire - name: GigabitEthernet0/1 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: GigabitEthernet0/2 description: Configured by Ansible - Interface 2 (ADMIN DOWN) mtu: 600 enabled: false l2_interfaces: - name: GigabitEthernet0/0 - mode: access name: GigabitEthernet0/1 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" - mode: trunk name: GigabitEthernet0/2 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" encapsulation: dot1q l3_interfaces: - ipv4: - address: 192.168.0.2/24 name: GigabitEthernet0/0 - name: GigabitEthernet0/1 - name: GigabitEthernet0/2 - name: Loopback888 - name: Loopback999 ##Playbook vars_files: - "facts.yml" tasks: - name: Build the facts collection set_fact: data_sources: - data: "{{ interfaces }}" match_key: name name: interfaces - data: "{{ l2_interfaces }}" match_key: name name: l2_interfaces - data: "{{ l3_interfaces }}" match_key: name name: l3_interfaces - name: Combine all the facts based on match_keys set_fact: combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=False) }}" ##Output # ok: [localhost] => { # "ansible_facts": { # "data_sources": [ # { # "data": [ # { # "duplex": "auto", # "enabled": true, # "name": "GigabitEthernet0/0", # "note": [ # "Connected green wire" # ], # "speed": "auto" # }, # { # "description": "Configured by Ansible - Interface 1", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "GigabitEthernet0/1", # "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": "GigabitEthernet0/2" # } # ], # "match_key": "name", # "name": "interfaces" # }, # { # "data": [ # { # "name": "GigabitEthernet0/0" # }, # { # "mode": "access", # "name": "GigabitEthernet0/1", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ] # } # }, # { # "mode": "trunk", # "name": "GigabitEthernet0/2", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ], # "encapsulation": "dot1q" # } # } # ], # "match_key": "name", # "name": "l2_interfaces" # }, # { # "data": [ # { # "ipv4": [ # { # "address": "192.168.0.2/24" # } # ], # "name": "GigabitEthernet0/0" # }, # { # "name": "GigabitEthernet0/1" # }, # { # "name": "GigabitEthernet0/2" # }, # { # "name": "Loopback888" # }, # { # "name": "Loopback999" # } # ], # "match_key": "name", # "name": "l3_interfaces" # } # ] # }, # "changed": false # } # Read vars_file 'facts.yml' # TASK [Combine all the facts based on match_keys] # ok: [localhost] => { # "ansible_facts": { # "combined": { # "GigabitEthernet0/0": { # "interfaces": { # "duplex": "auto", # "enabled": true, # "name": "GigabitEthernet0/0", # "note": [ # "Connected green wire" # ], # "speed": "auto" # }, # "l2_interfaces": { # "name": "GigabitEthernet0/0" # }, # "l3_interfaces": { # "ipv4": [ # { # "address": "192.168.0.2/24" # } # ], # "name": "GigabitEthernet0/0" # } # }, # "GigabitEthernet0/1": { # "interfaces": { # "description": "Configured by Ansible - Interface 1", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "GigabitEthernet0/1", # "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 # } # ] # }, # "l2_interfaces": { # "mode": "access", # "name": "GigabitEthernet0/1", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ] # } # }, # "l3_interfaces": { # "name": "GigabitEthernet0/1" # } # }, # "GigabitEthernet0/2": { # "interfaces": { # "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)", # "enabled": false, # "mtu": 600, # "name": "GigabitEthernet0/2" # }, # "l2_interfaces": { # "mode": "trunk", # "name": "GigabitEthernet0/2", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ], # "encapsulation": "dot1q" # } # }, # "l3_interfaces": { # "name": "GigabitEthernet0/2" # } # }, # "Loopback888": { # "interfaces": {}, # "l2_interfaces": {}, # "l3_interfaces": { # "name": "Loopback888" # } # }, # "Loopback999": { # "interfaces": {}, # "l2_interfaces": {}, # "l3_interfaces": { # "name": "Loopback999" # } # } # } # }, # "changed": false # } # Failing on missing match values # ------------------------------- ##facts.yaml interfaces: - name: GigabitEthernet0/0 enabled: true duplex: auto speed: auto note: - Connected green wire - name: GigabitEthernet0/1 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: GigabitEthernet0/2 description: Configured by Ansible - Interface 2 (ADMIN DOWN) mtu: 600 enabled: false l2_interfaces: - name: GigabitEthernet0/0 - mode: access name: GigabitEthernet0/1 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" - mode: trunk name: GigabitEthernet0/2 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" encapsulation: dot1q l3_interfaces: - ipv4: - address: 192.168.0.2/24 name: GigabitEthernet0/0 - name: GigabitEthernet0/1 - name: GigabitEthernet0/2 - name: Loopback888 - name: Loopback999 ##Playbook vars_files: - "facts.yml" tasks: - name: Build the facts collection set_fact: data_sources: - data: "{{ interfaces }}" match_key: name name: interfaces - data: "{{ l2_interfaces }}" match_key: name name: l2_interfaces - data: "{{ l3_interfaces }}" match_key: name name: l3_interfaces - name: Combine all the facts based on match_keys set_fact: combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_value=True) }}" ##Output # ok: [localhost] => { # "ansible_facts": { # "data_sources": [ # { # "data": [ # { # "duplex": "auto", # "enabled": true, # "name": "GigabitEthernet0/0", # "note": [ # "Connected green wire" # ], # "speed": "auto" # }, # { # "description": "Configured by Ansible - Interface 1", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "GigabitEthernet0/1", # "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": "GigabitEthernet0/2" # } # ], # "match_key": "name", # "name": "interfaces" # }, # { # "data": [ # { # "name": "GigabitEthernet0/0" # }, # { # "mode": "access", # "name": "GigabitEthernet0/1", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ] # } # }, # { # "mode": "trunk", # "name": "GigabitEthernet0/2", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ], # "encapsulation": "dot1q" # } # } # ], # "match_key": "name", # "name": "l2_interfaces" # }, # { # "data": [ # { # "ipv4": [ # { # "address": "192.168.0.2/24" # } # ], # "name": "GigabitEthernet0/0" # }, # { # "name": "GigabitEthernet0/1" # }, # { # "name": "GigabitEthernet0/2" # }, # { # "name": "Loopback888" # }, # { # "name": "Loopback999" # } # ], # "match_key": "name", # "name": "l3_interfaces" # } # ] # }, # "changed": false # } # Read vars_file 'facts.yml' # TASK [Combine all the facts based on match_keys] # fatal: [localhost]: FAILED! => { # "msg": "Error when using plugin 'consolidate': 'fail_missing_match_value' reported Missing match value Loopback999, # Loopback888 in data source 0, Missing match value Loopback999, Loopback888 in data source 1" # } # Failing on missing match keys # ----------------------------- ##facts.yaml interfaces: - name: GigabitEthernet0/0 enabled: true duplex: auto speed: auto note: - Connected green wire - name: GigabitEthernet0/1 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: GigabitEthernet0/2 description: Configured by Ansible - Interface 2 (ADMIN DOWN) mtu: 600 enabled: false l2_interfaces: - name: GigabitEthernet0/0 - mode: access name: GigabitEthernet0/1 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" - mode: trunk name: GigabitEthernet0/2 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" encapsulation: dot1q l3_interfaces: - ipv4: - address: 192.168.0.2/24 inft_name: GigabitEthernet0/0 - inft_name: GigabitEthernet0/1 - inft_name: GigabitEthernet0/2 - inft_name: Loopback888 - inft_name: Loopback999 ##Playbook vars_files: - "facts.yml" tasks: - name: Build the facts collection set_fact: data_sources: - data: "{{ interfaces }}" match_key: name name: interfaces - data: "{{ l2_interfaces }}" match_key: name name: l2_interfaces - data: "{{ l3_interfaces }}" match_key: name name: l3_interfaces - name: Combine all the facts based on match_keys set_fact: combined: "{{ data_sources|ansible.utils.consolidate(fail_missing_match_key=True) }}" ##Output # ok: [localhost] => { # "ansible_facts": { # "data_sources": [ # { # "data": [ # { # "duplex": "auto", # "enabled": true, # "name": "GigabitEthernet0/0", # "note": [ # "Connected green wire" # ], # "speed": "auto" # }, # { # "description": "Configured by Ansible - Interface 1", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "GigabitEthernet0/1", # "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": "GigabitEthernet0/2" # } # ], # "match_key": "name", # "name": "interfaces" # }, # { # "data": [ # { # "name": "GigabitEthernet0/0" # }, # { # "mode": "access", # "name": "GigabitEthernet0/1", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ] # } # }, # { # "mode": "trunk", # "name": "GigabitEthernet0/2", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ], # "encapsulation": "dot1q" # } # } # ], # "match_key": "name", # "name": "l2_interfaces" # }, # { # "data": [ # { # "inft_name": "GigabitEthernet0/0", # "ipv4": [ # { # "address": "192.168.0.2/24" # } # ] # }, # { # "inft_name": "GigabitEthernet0/1" # }, # { # "inft_name": "GigabitEthernet0/2" # }, # { # "inft_name": "Loopback888" # }, # { # "inft_name": "Loopback999" # } # ], # "match_key": "name", # "name": "l3_interfaces" # } # ] # }, # "changed": false # } # Read vars_file 'facts.yml' # TASK [Combine all the facts based on match_keys] # fatal: [localhost]: FAILED! => { # "msg": "Error when using plugin 'consolidate': 'fail_missing_match_key' reported Missing match # key 'name' in data source 2 in list entry 0, Missing match key 'name' in data # source 2 in list entry 1, Missing match key 'name' in data source 2 in list # entry 2, Missing match key 'name' in data source 2 in list entry 3, Missing # match key 'name' in data source 2 in list entry 4" # } # Failing on duplicate values in facts # ------------------------------------ ##facts.yaml interfaces: - name: GigabitEthernet0/0 enabled: true duplex: auto speed: auto note: - Connected green wire - name: GigabitEthernet0/1 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: GigabitEthernet0/2 description: Configured by Ansible - Interface 2 (ADMIN DOWN) mtu: 600 enabled: false l2_interfaces: - name: GigabitEthernet0/0 - name: GigabitEthernet0/0 - mode: access name: GigabitEthernet0/1 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" - mode: trunk name: GigabitEthernet0/2 trunk: allowed_vlans: - "11" - "12" - "59" - "67" - "75" - "77" - "81" - "100" - 400-408 - 411-413 - "415" - "418" - "982" - "986" - "988" - "993" encapsulation: dot1q l3_interfaces: - ipv4: - address: 192.168.0.2/24 name: GigabitEthernet0/0 - name: GigabitEthernet0/1 - name: GigabitEthernet0/2 - name: Loopback888 - name: Loopback999 ##Playbook vars_files: - "facts.yml" tasks: - name: Build the facts collection set_fact: data_sources: - data: "{{ interfaces }}" match_key: name name: interfaces - data: "{{ l2_interfaces }}" match_key: name name: l2_interfaces - data: "{{ l3_interfaces }}" match_key: name name: l3_interfaces - name: Combine all the facts based on match_keys set_fact: combined: "{{ data_sources|ansible.utils.consolidate(fail_duplicate=True) }}" ##Output # ok: [localhost] => { # "ansible_facts": { # "data_sources": [ # { # "data": [ # { # "duplex": "auto", # "enabled": true, # "name": "GigabitEthernet0/0", # "note": [ # "Connected green wire" # ], # "speed": "auto" # }, # { # "description": "Configured by Ansible - Interface 1", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "GigabitEthernet0/1", # "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": "GigabitEthernet0/2" # } # ], # "match_key": "name", # "name": "interfaces" # }, # { # "data": [ # { # "name": "GigabitEthernet0/0" # }, # { # "name": "GigabitEthernet0/0" # }, # { # "mode": "access", # "name": "GigabitEthernet0/1", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ] # } # }, # { # "mode": "trunk", # "name": "GigabitEthernet0/2", # "trunk": { # "allowed_vlans": [ # "11", # "12", # "59", # "67", # "75", # "77", # "81", # "100", # "400-408", # "411-413", # "415", # "418", # "982", # "986", # "988", # "993" # ], # "encapsulation": "dot1q" # } # } # ], # "match_key": "name", # "name": "l2_interfaces" # }, # { # "data": [ # { # "ipv4": [ # { # "address": "192.168.0.2/24" # } # ], # "name": "GigabitEthernet0/0" # }, # { # "name": "GigabitEthernet0/1" # }, # { # "name": "GigabitEthernet0/2" # }, # { # "name": "Loopback888" # }, # { # "name": "Loopback999" # } # ], # "match_key": "name", # "name": "l3_interfaces" # } # ] # }, # "changed": false # } # Read vars_file 'facts.yml' # TASK [Combine all the facts based on match_keys] # fatal: [localhost]: FAILED! => { # "msg": "Error when using plugin 'consolidate': 'fail_duplicate' reported Duplicate values in data source 1" # } """ from ansible.errors import AnsibleFilterError from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import ( AnsibleArgSpecValidator, ) from ansible_collections.ansible.utils.plugins.plugin_utils.consolidate import consolidate try: from jinja2.filters import pass_environment except ImportError: from jinja2.filters import environmentfilter as pass_environment @pass_environment def _consolidate(*args, **kwargs): """Consolidate facts together on common attributes""" keys = [ "data_sources", "fail_missing_match_key", "fail_missing_match_value", "fail_duplicate", ] data = dict(zip(keys, args[1:])) data.update(kwargs) aav = AnsibleArgSpecValidator(data=data, schema=DOCUMENTATION, name="consolidate") valid, errors, updated_data = aav.validate() if not valid: raise AnsibleFilterError(errors) return consolidate(**updated_data) class FilterModule(object): """Consolidate""" def filters(self): """A mapping of filter names to functions""" return {"consolidate": _consolidate}