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?
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