Search code examples
ansibleyamljinja2

How to list subelements from nested dict


I'm trying to use a specific collection who gather facts on a server, with a specific structure that I cannot change. I am using Ansible 2.10, but I could use 2.14 if needed (but not preferred).

The structure consists of nested dictionaries and NO lists. Some keys have specific name that are consistent (eg. vtds, device) and some that can change depending on the server (eg. all children of vtds).

My goal is to be able to list all devices in that structure.

Here is a sample playbook I did to examplify my problem. What I cannot find a way to do are the 2 last tasks.

---
- name: Test Dict
  host: all
  gather_facts: false
  vars:
    - mapping:
        vscsi:
          vhost1:
            info: azerty
            vtds:
              AZER_TY_125:
                status: UP
                device: disk1
              AZER_TY_456:
                status: UP
                device: disk2
          vhost2:
            info: azerty
            vtds:
              QWER_TY_123:
                status: UP
                device: disk2
              QWER_TY_456:
                status: DOWN
                device: disk3
          vhost3:
            info: azerty
            vtds:
              QWER_TI_123:
                status: DOWN
                device: disk4
  tasks:
    - name: Printing Overarching Dict
      ansible.builtin.debug:
        msg: 'Type: {{mapping | type debug }}, Value: {{ mapping}}'
    - name: Looping Over vhosts
      ansible.builtin.debug:
        msg: 'VHOST: {{1 item.key }}, type of value: {{item.value | type debug }}'
      loop: '{{mapping.vscsi | dict2items }}'
    - name: Listing all device
      ansible.builtin.debug:
        msg: 'Devices: {{ item.value | ? }}'
      loop: '{{ mapping.vscsi | dict2items | ??? }}'

    - name: Listing all device and their vhosts

      ansible.builtin.debug:

        msg: 'Vhost: {{ item.key }}, Devices: {{ item.value | #extract the 2nd element of vtds#| map(attribute='device') | list }}'

      loop: '{{ mapping.vscsi | dict2items }}'

The first tasks : Generate a list of all devices.

The second tasks : Generate a list of all devices and their associated vhost (or a list of all vhosts with their devices). What I'm missing here, is a way to extract the second element of the vtds dicts as this is an element I cannot know the name of beforehand.

Trying to use the subelements('value.vtds') doesn't work as is to get a list of devices as vtds is a dict and subelements expects a list. Maybe modifying the structure and registering it in a task before looping over it could work but I don't know how to proceed. Most of the similar questions I found on SO don't include the "unknown key" part which bothers me.

Thank you in advance for any leads or answers.


Solution

  • For example,

      devices: "{{ mapping|json_query('*.*.vtds.*.device')|
                           flatten|
                           unique }}"
    

    gives

      devices:
      - disk1
      - disk2
      - disk3
      - disk4