Jinja2 Template and Dictionary issue

Can anyone help me with the below, maybe my approach is wrong. What I’m trying to do is to create from a template a keepalived file based on the elements in a dictionary.

I’ve included the code below, when its ran the error that appears is

msg": “AnsibleUndefinedVariable: ‘dict object’ has no attribute ‘hostname’”

The error appears to in the second loop in the jinja2 template, where i try to extract the elements from the sub lst in the dictionary.

ANSIBLE Dictionary

keepalived_virtual_ip:

  • vip_details_1:

haproxy_vip_name: “AddressCleaner_Development”

ip: “1.0.1.2”

front_end_port: 80

haproxy_backend_servers:

  • servers_1.1:

hostname: system12

address: 2.0.0.1

  • servers_1.2:

hostname: system13

address: 2.0.0.2

  • vip_details_2:

haproxy_vip_name: “AddressCleaner_Cert”

ip: “3.0.0.114”

front_end_port: 80

haproxy_backend_servers:

  • servers_2.1:

hostname: system21

address: 2.0.0.3

  • servers_2.2:

hostname: system22

address: 2.0.0.4

keepalived_virtual_ip:

  • vip_details_1:

haproxy_vip_name: “AddressCleaner_Development”

ip: “3.0.0.115”

front_end_port: 80

haproxy_backend_servers:

  • servers_1.1:

hostname: lonldpostal01

address: 3.0.0.87

  • servers_1.2:

hostname: lonldpostal02

address: 3.0.0.100

  • vip_details_2:

haproxy_vip_name: “AddressCleaner_Cert”

ip: “3.0.0.114”

front_end_port: 80

haproxy_backend_servers:

  • servers_2.1:

hostname: lonlcpostal01

address: 3.0.0.96

  • servers_2.2:

hostname: lonlcpostal02

address: 3.0.0.104

HA PROXY TEMPLATE

{% for item in keepalived_virtual_ip %}

listen {{ item.haproxy_vip_name }} {{ item.ip }}:{{ item.front_end_port }}

mode {{ haproxy_mode }}

balance {{ haproxy_vip_algo }}

default_backend {{ item.haproxy_vip_name }}_backend

{% endfor %}

{% for item in keepalived_virtual_ip %}

backend {{ item.haproxy_vip_name }}_backend

mode tcp

balance source

{% for backend in item.haproxy_backend_servers %}

server {{ backend.hostname }} {{ backend.address }}:{{ haproxy_vip_port }} maxconn 9000 send-proxy check

{% endfor %}

{% endfor %}

keepalived_virtual_ip:
  - vip_details_1:
    haproxy_vip_name: "AddressCleaner_Development"
    ip: "1.0.1.2"
    front_end_port: 80
    haproxy_backend_servers:
       - servers_1.1:
          hostname: system12
          address: 2.0.0.1
       - servers_1.2:
          hostname: system13
          address: 2.0.0.2

It's not allowed to have . (dot) in variable name only letters, numbers and underscores is allowed so servers_1.1: is invalid

{% for item in keepalived_virtual_ip %}
backend {{ item.haproxy_vip_name }}_backend
    mode tcp
    balance source
    {% for backend in item.haproxy_backend_servers %}
       server {{ backend.hostname }} {{ backend.address }}:{{ haproxy_vip_port }} maxconn 9000 send-proxy check
    {% endfor %}
{% endfor %}
  
Lets say that servers_1.1: is servers_1_1:, the same for server_1.2

item.haproxy_backend_server is a list of dictonary, the hostname is not backend.hostname but
backend.servers_1_1.hostname and backend.servers_1_2.hostname

Could i have instead of :

Servers_1.1
Servers_1.2
Servers_2.1
Servers_2.2
Servers_3.1
Servers_3.2

Call each list

servers

The reason being i dont want to have to hard code names in the jinja2 template of the elements in the lists of backends, depending on the size of the dictionary i might shink or add hosts/backends and i want the template to dynamically pick these additions or substractions without a code change in the template file.

The easiest to do is make haproxy_backend_servers a list of dictionaries and not a list of dictionary.

So if you can change you haproxy_backend_servers to

     haproxy_backend_servers:
       - name: servers_1.1
         hostname: system12
         address: 2.0.0.1
       - name: servers_1.2:
         hostname: system13
         address: 2.0.0.2

You code will work as is since you can call backend.hostname and not backend.<something>.hostname