diff --git a/README.md b/README.md index 0c1a552..a4b529c 100644 --- a/README.md +++ b/README.md @@ -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. @@ -24,6 +24,7 @@ PEP440 is the schema used to describe the versions of Ansible. Name | Description --- | --- [ansible.utils.cidr_merge](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.cidr_merge_filter.rst)|This filter can be used to merge subnets or individual addresses. +[ansible.utils.consolidate](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.consolidate_filter.rst)|Consolidate facts together on common attributes. [ansible.utils.from_xml](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.from_xml_filter.rst)|Convert given XML string to native python dictionary. [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 diff --git a/docs/ansible.utils.consolidate_filter.rst b/docs/ansible.utils.consolidate_filter.rst new file mode 100644 index 0000000..cba46fd --- /dev/null +++ b/docs/ansible.utils.consolidate_filter.rst @@ -0,0 +1,1344 @@ +.. _ansible.utils.consolidate_filter: + + +************************* +ansible.utils.consolidate +************************* + +**Consolidate facts together on common attributes.** + + +Version added: 2.5.2 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This plugin presents a 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``)) + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsConfigurationComments
+
+ data_source + +
+ list + / elements=dictionary +
+
+ + +
This option represents a list of dictionaries to perform the operation on.
+
For example facts_source|ansible.utils.consolidate(fail_missing_match_key=False)), in this case facts_source represents this option.
+
+
+ data + +
+ raw +
+
+ + +
Specify facts data that gets consolidated.
+
+
+ match_key + +
+ string +
+
+ + +
Specify key to match on.
+
+
+ prefix + +
+ string +
+
+ + +
Specify the prefix with which the result set be created.
+
+
+ fail_duplicate + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+ +
Fail if duplicate values for any key is found.
+
+
+ fail_missing_match_key + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+ +
Fail if match_key is not found in a specific data set.
+
+
+ fail_missing_match_value + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+ +
Fail if a keys to match in not same accross all data sets.
+
+
+ + + + +Examples +-------- + +.. code-block:: yaml + + # Consolidated facts example + # ------------ + + ##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_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces + + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=False) }}" + + ##Output + # ok: [localhost] => { + # "ansible_facts": { + # "data_source": [ + # { + # "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", + # "prefix": "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", + # "prefix": "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", + # "prefix": "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_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces + + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=True) }}" + + ##Output + ok: [localhost] => { + "ansible_facts": { + "data_source": [ + { + "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", + "prefix": "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", + "prefix": "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", + "prefix": "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_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces + + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_key=True) }}" + + ##Output + # ok: [localhost] => { + # "ansible_facts": { + # "data_source": [ + # { + # "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", + # "prefix": "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", + # "prefix": "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", + # "prefix": "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_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces + + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_duplicate=True) }}" + + ##Output + ok: [localhost] => { + "ansible_facts": { + "data_source": [ + { + "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", + "prefix": "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", + "prefix": "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", + "prefix": "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" + } + + + + +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. diff --git a/plugins/filter/consolidate.py b/plugins/filter/consolidate.py index da301e8..01bb170 100644 --- a/plugins/filter/consolidate.py +++ b/plugins/filter/consolidate.py @@ -15,51 +15,54 @@ __metaclass__ = type DOCUMENTATION = """ name: consolidate author: Sagar Paul (@KB-perByte) - version_added: "2.5.0" - short_description: Keep specific keys from a data recursively. + version_added: "2.5.2" + short_description: Consolidate facts together on common attributes. 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([....]))) + - This plugin presents a 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))) options: data_source: 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. + - 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 suboptions: data: - description: Specify the target keys to keep in list format. + description: Specify facts data that gets consolidated. type: raw match_key: - description: Specify the target keys to keep in list format. + description: Specify key to match on. type: str prefix: - description: Specify the target keys to keep in list format. + description: Specify the prefix with which the result set be created. type: str fail_missing_match_key: - description: Specify the target keys to keep in list format. + description: Fail if match_key is not found in a specific data set. type: bool fail_missing_match_value: - description: Specify the target keys to keep in list format. + description: Fail if a keys to match in not same accross all data sets. type: bool fail_duplicate: - description: Specify the matching configuration of target keys and data attributes. + description: Fail if duplicate values for any key is found. type: bool """ EXAMPLES = r""" -##example.yaml +# Consolidated facts example +# ------------ + +##facts.yml interfaces: - - name: eth0 + - name: GigabitEthernet0/0 enabled: true duplex: auto speed: auto note: - Connected green wire - - name: eth1 + - name: GigabitEthernet0/1 description: Configured by Ansible - Interface 1 mtu: 1500 speed: auto @@ -69,128 +72,380 @@ interfaces: - 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 + - 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: - - "example.yaml" -tasks: - - name: keep selective keys from dict/list of dict data - ansible.builtin.set_fact: - data: "{{ interfaces }}" + vars_files: + - "facts.yml" + tasks: + - name: Build the facts collection + set_fact: + data_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces - - debug: - msg: "{{ data|ansible.utils.keep_keys(target=['description', 'name', 'mtu', 'duplex', 'enabled', 'vifs', 'vlan_id']) }}" + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=False) }}" ##Output -# TASK [keep selective keys from python dict/list of dict] **************************************************************************************** # ok: [localhost] => { # "ansible_facts": { -# "data": [ +# "data_source": [ # { -# "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": [ +# "data": [ # { -# "comment": "Needs reconfiguration", -# "description": "Eth1 - VIF 100", +# "duplex": "auto", # "enabled": true, -# "mtu": 400, -# "vlan_id": 100 +# "name": "GigabitEthernet0/0", +# "note": [ +# "Connected green wire" +# ], +# "speed": "auto" # }, # { -# "description": "Eth1 - VIF 101", +# "description": "Configured by Ansible - Interface 1", +# "duplex": "auto", # "enabled": true, -# "vlan_id": 101 +# "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", +# "prefix": "interfaces" # }, # { -# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)", -# "enabled": false, -# "mtu": 600, -# "name": "eth2" +# "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", +# "prefix": "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", +# "prefix": "l3_interfaces" # } # ] # }, # "changed": false # } -# Read vars_file 'example.yaml' +# Read vars_file 'facts.yml' -# TASK [debug] ************************************************************************************************************* +# TASK [Combine all the facts based on match_keys] **************************************************************************************************************** # 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", +# "ansible_facts": { +# "combined": { +# "GigabitEthernet0/0": { +# "interfaces": { +# "duplex": "auto", # "enabled": true, -# "mtu": 400, -# "vlan_id": 100 +# "name": "GigabitEthernet0/0", +# "note": [ +# "Connected green wire" +# ], +# "speed": "auto" # }, -# { -# "description": "Eth1 - VIF 101", -# "enabled": true, -# "vlan_id": 101 +# "l2_interfaces": { +# "name": "GigabitEthernet0/0" +# }, +# "l3_interfaces": { +# "ipv4": [ +# { +# "address": "192.168.0.2/24" +# } +# ], +# "name": "GigabitEthernet0/0" # } -# ] -# }, -# { -# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)", -# "enabled": false, -# "mtu": 600, -# "name": "eth2" +# }, +# "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 # } -##example.yaml +# Failing on missing match values +# ------------------------------- + +##facts.yaml interfaces: - - name: eth0 + - name: GigabitEthernet0/0 enabled: true duplex: auto speed: auto note: - Connected green wire - - name: eth1 + - name: GigabitEthernet0/1 description: Configured by Ansible - Interface 1 mtu: 1500 speed: auto @@ -200,106 +455,744 @@ interfaces: - 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 + - 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: - - "example.yaml" -tasks: - - name: keep selective keys from dict/list of dict data - ansible.builtin.set_fact: - data: "{{ interfaces }}" + vars_files: + - "facts.yml" + tasks: + - name: Build the facts collection + set_fact: + data_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces - - debug: - msg: "{{ data|ansible.utils.keep_keys(target=['desc', 'name'], matching_parameter= 'starts_with') }}" + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_value=True) }}" + +##Output +ok: [localhost] => { + "ansible_facts": { + "data_source": [ + { + "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", + "prefix": "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", + "prefix": "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", + "prefix": "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_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces + + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_missing_match_key=True) }}" ##Output -# TASK [keep selective keys from python dict/list of dict] ************************** # ok: [localhost] => { # "ansible_facts": { -# "data": [ +# "data_source": [ # { -# "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": [ +# "data": [ # { -# "comment": "Needs reconfiguration", -# "description": "Eth1 - VIF 100", +# "duplex": "auto", # "enabled": true, -# "mtu": 400, -# "vlan_id": 100 +# "name": "GigabitEthernet0/0", +# "note": [ +# "Connected green wire" +# ], +# "speed": "auto" # }, # { -# "description": "Eth1 - VIF 101", +# "description": "Configured by Ansible - Interface 1", +# "duplex": "auto", # "enabled": true, -# "vlan_id": 101 +# "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", +# "prefix": "interfaces" # }, # { -# "description": "Configured by Ansible - Interface 2 (ADMIN DOWN)", -# "enabled": false, -# "mtu": 600, -# "name": "eth2" +# "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", +# "prefix": "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", +# "prefix": "l3_interfaces" # } # ] # }, # "changed": false # } -# Read vars_file 'example.yaml' +# Read vars_file 'facts.yml' -# 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" -# } -# ] +# 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_source: + - data: "{{ interfaces }}" + match_key: name + prefix: interfaces + - data: "{{ l2_interfaces }}" + match_key: name + prefix: l2_interfaces + - data: "{{ l3_interfaces }}" + match_key: name + prefix: l3_interfaces + + - name: Combine all the facts based on match_keys + set_fact: + combined: "{{ data_source|ansible.utils.consolidate(fail_duplicate=True) }}" + +##Output +ok: [localhost] => { + "ansible_facts": { + "data_source": [ + { + "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", + "prefix": "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", + "prefix": "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", + "prefix": "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 @@ -315,10 +1208,10 @@ try: except ImportError: from jinja2.filters import environmentfilter as pass_environment -import debugpy +# import debugpy -debugpy.listen(3000) -debugpy.wait_for_client() +# debugpy.listen(3000) +# debugpy.wait_for_client() @pass_environment @@ -333,9 +1226,7 @@ def _consolidate(*args, **kwargs): ] data = dict(zip(keys, args[1:])) data.update(kwargs) - aav = AnsibleArgSpecValidator( - data=data, schema=DOCUMENTATION, name="consolidate" - ) + aav = AnsibleArgSpecValidator(data=data, schema=DOCUMENTATION, name="consolidate") valid, errors, updated_data = aav.validate() if not valid: raise AnsibleFilterError(errors)