Ansible: iterating over a nested dictionary

ansible

I have data that looks like this:

nova_flavors:
- disk: 10
  name: m1.tiny
  properties:
    disk_read_bytes_sec: 12500000
    disk_read_iops_sec: 1000
    disk_write_bytes_sec: 3125000
    disk_write_iops_sec: 250
    vif_inbound_average: 2500
    vif_inbound_burst: 3750000
    vif_inbound_peak: 12500
    vif_outbound_average: 2500
    vif_outbound_burst: 3750000
    vif_outbound_peak: 12500
  ram: 1
- disk: 10
  name: m1.small
  properties:
    disk_read_bytes_sec: 25000000
    disk_read_iops_sec: 2000
    disk_write_bytes_sec: 6250000
    disk_write_iops_sec: 500
    vif_inbound_average: 5000
    vif_inbound_burst: 7500000
    vif_inbound_peak: 25000
    vif_outbound_average: 5000
    vif_outbound_burst: 7500000
    vif_outbound_peak: 25000
  ram: 2

I need the Ansible equivalent of the following Python loop:

for flavor in nova_flavors:
  for propname, propval in flavor['properties'].items():
    # do something with (flavor['name'], propname, propval)

I was hoping I could do this:

- debug:
    msg: "{{ item }}"
  with_subelements:
    - "{{ nova_flavors }}"
    - "properties"

But that fails because properties is a dictionary, not a list. And
you can't do this either:

- debug:
    msg: "{{ item }}"
  with_subelements:
    - "{{ nova_flavors }}"
    - "properties.items()"

Any pointers?

Best Answer

Well I can suggest a semi-hacky solution where you use set_fact a couple times to construct a list of dicts that you can probably use?

- hosts: localhost
  vars:
    nova_flavors:
    - disk: 10
      name: m1.tiny
      properties:
        disk_read_bytes_sec: 12500000
        disk_read_iops_sec: 1000
        disk_write_bytes_sec: 3125000
      ram: 1
    - disk: 10
      name: m1.small
      properties:
        vif_outbound_burst: 7500000
        vif_outbound_peak: 25000
      ram: 2
  tasks:
  - set_fact:
      aslist: |
        [
        {% for item in nova_flavors %}
        {% for prop in item.properties.keys() %}
        {{ '{' }} 'name':'{{ item.name }}','propname':'{{ prop }}','propvalue':{{item.properties[prop]}} {{ '}' }},
        {% endfor %}
        {% endfor %}
        ]

  - debug:
      var: aslist

Results.

TASK [debug] *******************************************************************
ok: [localhost] => {
    "aslist": [
        {
            "name": "m1.tiny", 
            "propname": "disk_write_bytes_sec", 
            "propvalue": 3125000
        }, 
        {
            "name": "m1.tiny", 
            "propname": "disk_read_iops_sec", 
            "propvalue": 1000
        }, 
        {
            "name": "m1.tiny", 
            "propname": "disk_read_bytes_sec", 
            "propvalue": 12500000
        }, 
        {
            "name": "m1.small", 
            "propname": "vif_outbound_peak", 
            "propvalue": 25000
        }, 
        {
            "name": "m1.small", 
            "propname": "vif_outbound_burst", 
            "propvalue": 7500000
        }
    ]
}

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0   

I believe this would easily allow you to loop over the constructed lists setting the each property.