Home > Enterprise >  Looping through a list of dictionaries in Ansible
Looping through a list of dictionaries in Ansible

Time:08-30

I've used basic loops before, nice and easy. However, now I have more of a challenge.

The task:

- name: Configure BGP
  fortios_router_bgp:
  vdom: "FG-traffic"
  router_bgp:
    as: " {{ as }}"
    router_id: "{{ router_id }}"
    neighbor:
      -
        holdtime_timer: "{{ holdtime_timer }}"
        interface: "{{ item.interface }}"
        ip: "{{ item.ip }}"
        keep_alive_timer: "{{ keep_alive_timer }}"
        prefix_list_in: "{{ item.prefix_list_in }}"
        remote_as: "{{ item.remote_as }}"
        route_map_in: "{{ item.route_map_in }}"
        update_source: "{{ item.update_source }}"
  loop: "{{ bgp_neighbors }}"
  tags: bgp

The list of dictionaries/hashes, the variable:

bgp_neighbors:
  - { interface: "INT_A", ip: "{{ netid_bgp }}.12", prefix_list_in: "default-route-in", remote_as: "30000", route_map_in: "SET_PREF", update_source: "INT_A" }
  - { interface: "INT_B", ip: "{{ netid_bgp }}.14", prefix_list_in: "default-route-in", remote_as: "30000", route_map_in: "SET_PREF", update_source: "INT_B" }
  - { interface: "INT_DR_A", ip: "{{ netid_bgp }}.16", prefix_list_in: "default-route-in", remote_as: "30000", route_map_in: "SET_PREF", update_source: "INT_DR_A" }
  - { interface: "INT_DR_B", ip: "{{ netid_bgp }}.18", prefix_list_in: "default-route-in", remote_as: "30000", route_map_in: "SET_PREF", update_source: "INT_DR_B" }

The objective is to create a list of four neighbors. What I get, of course, is a list of one, the last dictionary in the list.

I understand the loop is working correctly, that this module runs four times based on the list of four dictionaries, overwriting the key value pairs each time.

I understand that I need to process the keys in the dictionary list seperately although I'm struggling to work this out.

Now that I've started to do more loops I see that is going to be a common requirement.

This is the long hand version that I am trying to replace - ignore the variable names, I haven repopulated them with the actual values:

- name: Configure BGP
  fortios_router_bgp:
  vdom: "FG-traffic"
  router_bgp:
    as: " {{ as }}"
    router_id: "{{ router_id }}"
    neighbor:
      -
        holdtime_timer: "{{ holdtime_timer }}"
        interface: "{{ item.interface }}"
        ip: "{{ item.ip }}"
        keep_alive_timer: "{{ keep_alive_timer }}"
        prefix_list_in: "{{ item.prefix_list_in }}"
        remote_as: "{{ item.remote_as }}"
        route_map_in: "{{ item.route_map_in }}"
        update_source: "{{ item.update_source }}"
      -
        holdtime_timer: "{{ holdtime_timer }}"
        interface: "{{ item.interface }}"
        ip: "{{ item.ip }}"
        keep_alive_timer: "{{ keep_alive_timer }}"
        prefix_list_in: "{{ item.prefix_list_in }}"
        remote_as: "{{ item.remote_as }}"
        route_map_in: "{{ item.route_map_in }}"
        update_source: "{{ item.update_source }}"
      -
        holdtime_timer: "{{ holdtime_timer }}"
        interface: "{{ item.interface }}"
        ip: "{{ item.ip }}"
        keep_alive_timer: "{{ keep_alive_timer }}"
        prefix_list_in: "{{ item.prefix_list_in }}"
        remote_as: "{{ item.remote_as }}"
        route_map_in: "{{ item.route_map_in }}"
        update_source: "{{ item.update_source }}"
      -
        holdtime_timer: "{{ holdtime_timer }}"
        interface: "{{ item.interface }}"
        ip: "{{ item.ip }}"
        keep_alive_timer: "{{ keep_alive_timer }}"
        prefix_list_in: "{{ item.prefix_list_in }}"
        remote_as: "{{ item.remote_as }}"
        route_map_in: "{{ item.route_map_in }}"
        update_source: "{{ item.update_source }}"
  tags: bgp

Thanks!

CodePudding user response:

Create a template

shell> cat templates/neighbor.j2
{% for i in bgp_neighbors %}
- holdtime_timer: "{{ holdtime_timer }}"
  interface: "{{ i.interface }}"
  ip: "{{ i.ip }}"
  keep_alive_timer: "{{ keep_alive_timer }}"
  prefix_list_in: "{{ i.prefix_list_in }}"
  remote_as: "{{ i.remote_as }}"
  route_map_in: "{{ i.route_map_in }}"
  update_source: "{{ i.update_source }}"
{% endfor %}

Put the declarations into the vars

    netid_bgp: 10.1.0
    holdtime_timer: value of holdtime timer
    keep_alive_timer: value of keep alive timer
    _neighbor: "{{ lookup('template', 'templates/neighbor.j2') }}"
    neighbor: "{{ _neighbor|from_yaml }}"

Then the tasks below

    - set_fact:
        neighbor: "{{ neighbor }}"
    - debug:
        var: neighbor

give the list

neighbor:
  - holdtime_timer: value of holdtime timer
    interface: INT_A
    ip: 10.1.0.12
    keep_alive_timer: value of keep alive timer
    prefix_list_in: default-route-in
    remote_as: '30000'
    route_map_in: SET_PREF
    update_source: INT_A
  - holdtime_timer: value of holdtime timer
    interface: INT_B
    ip: 10.1.0.14
    keep_alive_timer: value of keep alive timer
    prefix_list_in: default-route-in
    remote_as: '30000'
    route_map_in: SET_PREF
    update_source: INT_B
  - holdtime_timer: value of holdtime timer
    interface: INT_DR_A
    ip: 10.1.0.16
    keep_alive_timer: value of keep alive timer
    prefix_list_in: default-route-in
    remote_as: '30000'
    route_map_in: SET_PREF
    update_source: INT_DR_A
  - holdtime_timer: value of holdtime timer
    interface: INT_DR_B
    ip: 10.1.0.18
    keep_alive_timer: value of keep alive timer
    prefix_list_in: default-route-in
    remote_as: '30000'
    route_map_in: SET_PREF
    update_source: INT_DR_B

Use the variable in the module

- name: Configure BGP
  fortios_router_bgp:
  vdom: "FG-traffic"
  router_bgp:
    as: " {{ as }}"
    router_id: "{{ router_id }}"
    neighbor: "{{ neighbor }}"
...
  • Related