ansible

ansible accessing key within list of nested dictionaries


I am trying to use a loop to retrieve multiple values which are present in multiple list-nested dictionaries. Unfortunately, it seems that I cannot do so unless I explicitly define which list I want to grab. Since I intend to define hundreds of these devices, I am hoping there is something that scales better.

#playbook.yml
tasks:
   - name: Get volume details using name
      get_storage_volume:
        host: "{{ host }}"
        username: "{{ username }}"
        password: "{{ password }}"
        name: "{{ inventory_hostname }}-{{ item.volsuffix }}"
        state: "present"
      register: volume_results
      loop: "{{ volumes }}"
volumes:
  - volsuffix: data1
    volsize: 100
    mount_path: /data/fs1
    fstype: xfs
    mount_opts: noatime
    storage_protocol: fc

  - volsuffix: data2
    volsize: 100
    mount_path: /data/fs2
    fstype: xfs
    mount_opts: noatime,_netdev
    storage_protocol: iscsi

    - debug:
        msg: "{{ volume_results.results.0.attrs.serial_number }}"
        #msg: "{{ volume_results.results.1.attrs.serial_number }}"

I would like to avoid having to explicitly call list entries [0] and [1] as seen below:

TASK [debug] *********************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [node1] => {
    "msg": "abcdef12345"
}
ok: [node2] => {
    "msg": "cvbnm46807"
}

Below is what the data format looks like if I output all of "{{ volume_results.result }}" :

TASK [debug] *******************************************************************
ok: [node1] => {
    "volume_results.results": [
        {
            "ansible_loop_var": "item",
            "attrs": {
                "agent_type": "none",
                "name": "node1-data1",
                "serial_number": "abcdef12345",
                "warn_level": 0
            },
        },
        {
            "ansible_loop_var": "item",
            "attrs": {
                "agent_type": "none",
                "name": "node1-data2",
                "serial_number": "987654qwert",
                "warn_level": 0
            }
        }
    ]
}

ok: [node2] => {
    "volume_results.results": [
        {
            "ansible_loop_var": "item",
            "attrs": {
                "agent_type": "none",
                "name": "node2-data1",
                "serial_number": "cvbnm46807",
                "warn_level": 0
            },
        },
        {
            "ansible_loop_var": "item",
            "attrs": {
                "agent_type": "none",
                "name": "node2-data2",
                "serial_number": "asdfg56789",
                "warn_level": 0
            }
        }
    ]
}

Thoughts?


Solution

  • Either use map, for example,

    - debug:
        msg: "{{ volume_results.results|
                 map(attribute='attrs.serial_number') }}"
    

    ,or json_query

    - debug:
        msg: "{{ volume_results.results|
                 json_query('[].attrs.serial_number') }}"
    

    Both options give the same result

      msg:
      - abcdef12345
      - 987654qwert
    

    Q: "Produce a dictionary {node1-data1: abcdef12345, node1-data2: 987654qwert}"

    A: The below declaration

      result: "{{ dict(volume_results.results |
                       json_query('[].[attrs.name, attrs.serial_number]')) }}"
    

    gives what you want

      result:
        node1-data1: abcdef12345
        node1-data2: 987654qwert
    

    Example of a complete playbook for testing

    - hosts: localhost
    
      vars:
    
        volume_results:
          results:
            - ansible_loop_var: item
              attrs:
                agent_type: none
                name: node1-data1
                serial_number: abcdef12345
                warn_level: 0
            - ansible_loop_var: item
              attrs:
                agent_type: none
                name: node1-data2
                serial_number: 987654qwert
                warn_level: 0
    
        result: "{{ dict(volume_results.results |
                         json_query('[].[attrs.name, attrs.serial_number]')) }}"
    
      tasks:
    
        - debug:
            msg: "{{ volume_results.results |
                     map(attribute='attrs.serial_number') }}"
    
        - debug:
            msg: "{{ volume_results.results |
                     json_query('[].attrs.serial_number') }}"
    
        - debug:
            var: result