sambo9
sambo9

Reputation: 153

Ansible variable dictionary key not substituted

I know that similar questions have been asked before, but I still can't get the results that I want in my scenario. I have the following code snippet within my group_vars folder. There are two BGP neighbors (1.1.1.1 and 1.1.1.2), which are listed at the group level. However, I want to be able to list one ore more additional neighbors, which I want to override at the host_vars level. The substitution for variable {{ bgp_neighbor_01 }} does not work because as I understand, Ansible does not perform variable substitution for the dictionary keys. Is there a way to keep my first two IP addresses at the group level, but also define additional IP addresses at the host level? Thank you in advance.

  bgp_neighbors:
    1.1.1.1:
      peer_group: EVPN-OVERLAY-PEERS
      description: SPINE1_EVPN_ADDRESS_FAMILY
    1.1.1.2:
      peer_group: EVPN-OVERLAY-PEERS
      description: SPINE2_EVPN_ADDRESS_FAMILY
    '{{ bgp_neighbor_01 }}':              <-- this variable does not get substituted 
      peer_group: '{{ peer_group_name }}'
      description: '{{ description }}'

This is how I resolved my issue based on suggestion provided by Vladimir. I created the following dictionary variable in my group_vars file.

evpn_bgp_neighbors: 
  1.1.1.1:
      peer_group: EVPN-OVERLAY-PEERS
      description: SPINE1_EVPN_ADDRESS_FAMILY
  1.1.1.2:
      peer_group: EVPN-OVERLAY-PEERS
      description: SPINE2_EVPN_ADDRESS_FAMILY

Then, in each of my individual host files, I created host specific bgp neighbors dictionary, and then I used combine filter to combine group level and host level dictionaries.

underlay_bgp_neighbors:
  192.168.1.0:
    peer_group: IPv4-UNDERLAY-PEERS
    description: SPINE1_IPv4_ADDRESS_FAMILY 
  192.168.1.128:
    peer_group: IPv4-UNDERLAY-PEERS
    description: SPINE2_IPv4_ADDRESS_FAMILY

my_bgp_neighbors: "{{ evpn_bgp_neighbors | combine(underlay_bgp_neighbors) }}"

Finally, back in my group_vars file, I referenced the combined dictionary:

router_bgp:
  neighbors: "{{ my_bgp_neighbors }}"

I did not need to make any modifications to my playbook. I only made changes to group_vars and host_vars files.

Upvotes: 0

Views: 938

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68384

Q: "Keep my first two IP addresses at the group level, but also define additional IP addresses at the host level"

A: Combine the dictionary within the host_vars. For example, given the inventory

shell> cat hosts
[group1]
srv1

and the group_vars

shell> cat group_vars/group1.yml 
bgp_neighbors:
  1.1.1.1:
    peer_group: EVPN-OVERLAY-PEERS
    description: SPINE1_EVPN_ADDRESS_FAMILY
  1.1.1.2:
    peer_group: EVPN-OVERLAY-PEERS
    description: SPINE2_EVPN_ADDRESS_FAMILY

combine the dictionary bgp_neighbors with bgp_neighbor_01 and create the variable my_bgp_neighbors within the host_vars

shell> cat host_vars/srv1.yml
bgp_neighbor_01:
  peer_group: '{{ peer_group_name }}'
  description: '{{ description }}'
my_bgp_neighbors: '{{ bgp_neighbors|default({})|
                      combine({"bgp_neighbor_01": bgp_neighbor_01}) }}'

Then the playbook

shell> cat playbook.yml
- hosts: srv1
  vars:
    peer_group_name: my-peer-group-name
    description: my-description
  tasks:
    - debug:
        var: my_bgp_neighbors

gives

ok: [srv1] => 
  my_bgp_neighbors:
    1.1.1.1:
      description: SPINE1_EVPN_ADDRESS_FAMILY
      peer_group: EVPN-OVERLAY-PEERS
    1.1.1.2:
      description: SPINE2_EVPN_ADDRESS_FAMILY
      peer_group: EVPN-OVERLAY-PEERS
    bgp_neighbor_01:
      description: my-description
      peer_group: my-peer-group-name

Unfortunately, it's not possible to combine dictionaries recursively within host_vars

shell> cat host_vars/srv1.yml
bgp_neighbor_01:
  peer_group: '{{ peer_group_name }}'
  description: '{{ description }}'
bgp_neighbors: '{{ bgp_neighbors|default({})|
                   combine({"bgp_neighbor_01": bgp_neighbor_01}) }}'

would crash

Error was a <class ''ansible.errors.AnsibleError''>, original message: recursive loop detected in template string: {{ bgp_neighbors|default({})| combine({"bgp_neighbor_01": bgp_neighbor_01}) }}'

Upvotes: 0

wowbagger
wowbagger

Reputation: 630

Why not change the group_vars to

  bgp_neighbour:
    - neighbour:
      ip: 1.1.1.1:
      peer_group: EVPN-OVERLAY-PEERS
      description: SPINE1_EVPN_ADDRESS_FAMILY
    - neighbour: 
      ip: 1.1.1.2:
      peer_group: EVPN-OVERLAY-PEERS
      description: SPINE2_EVPN_ADDRESS_FAMILY
    - neighbour:
      ip: '{{ bgp_neighbor_01 | default('empty')}}'             
      peer_group: '{{ peer_group_name }}'
      description: '{{ description }}'

and loop over that in a for loop and stop when ip is empty? Also check https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable

Upvotes: 1

Related Questions