hruday
hruday

Reputation: 77

loop over multiple list of dictionaries using ansible

I am trying to loop over a following variable structure using ansible and what I want is to loop over each subnet. For subnets-1 I want to get network, cidr, vlan similarly loop other keys subnets-2, subnets-3

flavor.json

{
  "subnets-1": [
    {
      "network": "test1",
      "cidr": 21,
      "vlan": 123
    },
    {
      "network": "test2",
      "cidr": 22,
      "vlan": 234
    }
  ],
  "subnets-2": [
    {
      "network": "test3",
      "cidr": 43,
      "vlan": 879
    },
    {
      "network": "test4",
      "cidr": 21,
      "vlan": "12fsd"
    },
    {
      "network": "test5",
      "cidr": "22sdf",
      "vlan": "234sdfd"
    }
  ],
  "subnets-3": [
    {
      "network": "test44",
      "cidr": "fg",
      "vlan": "dsfsd"
    }
  ]
}

I have tried something like below using with_dict

playbook.yml (ansible 2.9.7)

---
- hosts: local
  gather_facts: no  
  tasks:
  - name: lookup yaml
    set_fact:
      flavors: "{{ lookup('file','flavor.json') | from_json }}"
  - name: json out
    debug:
      msg: "{{ flavors }}"
  - name: check keys
    shell: |
      echo "s_name = {{ item.network }}"
      echo "s_cidr = {{ item.cidr }}"
      echo "s_vlan = {{ item.vlan }}"
    with_dict:
      "{{ flavors }}"

This is the error I get when playbook was executed

Error:

fatal: [192.168.110.128]: FAILED! => {
    "msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'network'\n\nThe error appears to be in '/root/fla.yml': line 19, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n  - name: check keys\n    ^ here\n"
}

Upvotes: 1

Views: 4470

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68024

Use include_vars. It's simpler compared to lookup and from_yaml, e.g.

    - include_vars:
        file: flavor.json
        name: flavors

gives

  flavors:
    subnets-1:
    - {cidr: 21, network: test1, vlan: 123}
    - {cidr: 22, network: test2, vlan: 234}
    subnets-2:
    - {cidr: 43, network: test3, vlan: 879}
    - {cidr: 21, network: test4, vlan: 12fsd}
    - {cidr: 22sdf, network: test5, vlan: 234sdfd}
    subnets-3:
    - {cidr: fg, network: test44, vlan: dsfsd}

Then convert the dictionary to list and iterate with_subelements, e.g.

    - debug:
        msg: "{{ item.0.key }}
              {{ item.1.network }}
              {{ item.1.cidr }}
              {{ item.1.vlan }}"
      with_subelements:
        - "{{ flavors|dict2items }}"
        - value

gives

  msg: subnets-1 test1 21 123
  msg: subnets-1 test2 22 234
  msg: subnets-2 test3 43 879
  msg: subnets-2 test4 21 12fsd
  msg: subnets-2 test5 22sdf 234sdfd
  msg: subnets-3 test44 fg dsfsd

Upvotes: 2

Jack
Jack

Reputation: 6158

Ansible does not do nested loops well. In this case, you know you have a maximum of three array elements. So you can loop like this:

- name: loop over flavors
  debug:
    msg: "{{ flavors[item.0][item.1] }}"
  with_nested:
    - "{{ flavors }}"
    - [ 0, 1, 2 ]
  when: flavors[item.0][item.1] is defined

Which results in something like this:

TASK [loop over flavors] *********************************************************************************************
ok: [localhost] => (item=['subnets-1', 0]) => {
    "msg": {
        "cidr": 21,
        "network": "test1",
        "vlan": 123
    }
}
ok: [localhost] => (item=['subnets-1', 1]) => {
    "msg": {
        "cidr": 22,
        "network": "test2",
        "vlan": 234
    }
}
skipping: [localhost] => (item=['subnets-1', 2]) 
ok: [localhost] => (item=['subnets-2', 0]) => {
    "msg": {
        "cidr": 43,
        "network": "test3",
        "vlan": 879
    }
}
ok: [localhost] => (item=['subnets-2', 1]) => {
    "msg": {
        "cidr": 21,
        "network": "test4",
        "vlan": "12fsd"
    }
}
ok: [localhost] => (item=['subnets-2', 2]) => {
    "msg": {
        "cidr": "22sdf",
        "network": "test5",
        "vlan": "234sdfd"
    }
}
ok: [localhost] => (item=['subnets-3', 0]) => {
    "msg": {
        "cidr": "fg",
        "network": "test44",
        "vlan": "dsfsd"
    }
}
skipping: [localhost] => (item=['subnets-3', 1]) 
skipping: [localhost] => (item=['subnets-3', 2]) 

Upvotes: 0

Related Questions