Home > database >  Loop through subelements of subelements
Loop through subelements of subelements

Time:11-18

I am using the following data structure in Ansible:

datacenters:
  - name: Datacenter1
    clusters:
      - name: ClusterA
        hosts:
          - 192.168.0.1
          - 192.168.0.2
      - name: ClusterB
        hosts:
          - 192.168.1.1
          - 192.168.1.2
  - name: Datacenter2
    clusters:
      - name: ClusterC
        hosts:
          - 192.168.2.1
          - 192.168.2.2

In a task, I want to iterate over each host while having access to the data of all the parent layers. If there is only one nesting level, it can easily be done with the subelements filter:

loop: '{{ datacenters | subelements(''clusters'') }}'

This will give me access to the data like this:

'Datacenter: {{ item.0.name }}, Cluster: {{ item.1.name }}'

I was hoping to be able to extend this concept like this:

loop: '{{ datacenters | subelements(''clusters'') | subelements(''hosts'') }}'

And being able to access the data like this:

'Datacenter: {{ item.0.name }}, Cluster: {{ item.1.name }}, Host: {{ item.2 }}'

But that does not work and I get the following error message instead:

Unexpected templating type error occurred on ({{ datacenters | subelements('clusters') | subelements('hosts') }}): the key hosts should point to a dictionary, got ...(the result of the first layer)

I found this question, which solves a similar problem, but relies on having distinct dict keys on all nesting levels, which I don't, because datacenters and clusters have the same name key.

So, how can I iterate over subelements of subelements in Ansible?

CodePudding user response:

A bit far-fetched but the following playbook will achieve your goal:

 ---
 - hosts: localhost
   gather_facts: false
 
   vars:
     datacenters:
     - name: Datacenter1
       clusters:
         - name: ClusterA
           hosts:
             - 192.168.0.1
             - 192.168.0.2
         - name: ClusterB
           hosts:
             - 192.168.1.1
             - 192.168.1.2
     - name: Datacenter2
       clusters:
         - name: ClusterC
           hosts:
             - 192.168.2.1
             - 192.168.2.2
 
     # Get the list of datacenters
     _dcs: "{{ datacenters | map(attribute='name') }}"
     # Get the corresponding list of clusters with subelements on hosts
     _clusters: "{{ datacenters | map(attribute='clusters') | map('subelements', 'hosts') }}"
     # Recreate a list with the sublisted hosts per clusters and create subelements on that final result
     _overall: "{{ dict(_dcs | zip(_clusters)) | dict2items(key_name='datacenter', value_name='clusters') | subelements('clusters') }}"
 
   tasks:
     - name: Loop on the result
       debug:
         msg:
           - "DC: {{ item.0.datacenter }}"
           - "Cluster: {{ item.1.0.name }}"
           - "Host: {{ item.1.1 }}"
       loop: "{{ _overall }}"
       loop_control:
         label: "{{ item.0.datacenter }} - {{ item.1.0.name }}"

This gives:

PLAY [localhost] **************************************************************************************************************************************************

TASK [Loop on the result] *****************************************************************************************************************************************
ok: [localhost] => (item=Datacenter1 - ClusterA) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterA",
        "Host: 192.168.0.1"
    ]
}
ok: [localhost] => (item=Datacenter1 - ClusterA) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterA",
        "Host: 192.168.0.2"
    ]
}
ok: [localhost] => (item=Datacenter1 - ClusterB) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterB",
        "Host: 192.168.1.1"
    ]
}
ok: [localhost] => (item=Datacenter1 - ClusterB) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterB",
        "Host: 192.168.1.2"
    ]
}
ok: [localhost] => (item=Datacenter2 - ClusterC) => {
    "msg": [
        "DC: Datacenter2",
        "Cluster: ClusterC",
        "Host: 192.168.2.1"
    ]
}
ok: [localhost] => (item=Datacenter2 - ClusterC) => {
    "msg": [
        "DC: Datacenter2",
        "Cluster: ClusterC",
        "Host: 192.168.2.2"
    ]
}

PLAY RECAP ********************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
  • Related