AnsibleUndefinedVariable: 'list object' has no attribute - ansible

Yaml template
bgp:
local: 109
remote: 109
site:
- neighbor:
peer_ip: [ 172.16.110.3, 172.16.110.4 ]
vpnv4:
vrf: Site-1
network: 109.10.1.1
mask: 255.255.255.0
- neighbor:
peer_ip: [ 172.16.120.3, 172.16.120.4 ]
vpnv4:
vrf: Site-2
network: 109.10.1.1
mask: 255.255.255.0
Jinja2 template
router bgp {{ item.bgp.local }}
{% for i in item.bgp.site %}
address-family ipv4 vrf {{ i.vpnv4.vrf }}
network {{ i.vpnv4.network }} mask {{ i.vpnv4.mask }}
{% for b in item.bgp.site.neighbor.peer_ip %}
neighbor {{ b }} remote-as {{ item.bgp.remote }}
neighbor {{ b }} activate
{% endfor %}
{% endfor %}
If I remove this it works. I am suspecting an issue with "list" but no idea how to fix it.
{% for b in item.bgp.site.neighbor.peer_ip %}
neighbor {{ b }} remote-as {{ item.bgp.remote }}
neighbor {{ b }} activate
{% endfor %}

item.bgp.site is actually i of your outer loop.
Try: {% for b in i.neighbor.peer_ip %}

Related

Iterate over a YAML object using Jinja template

I have a yaml file with the following contents in it
interfaces:
'loopback:local':
-address: 0.0.0.0
prefix: 24
area: 192.168.1.2
grp: testint
intname: global
-address: 0.0.0.0
prefix: 24
area: 192.168.1.3
grp: prodint
intname: global2
My jinja template looks like this
{% for intf in interfaces %}
{{ intf }}
{% for key,value in intf.items() %}
{{ key }}
{{ value }}
{% endfor %}
{% endfor %}
When I try to access the elements from the object, I get an error saying no elements found
More likely you shoud try something like this:
vars:
interfaces:
- loopback_local:
- address: 0.0.0.0
prefix: 24
area: 192.168.1.2
grp: testint
intname: global
- address: 0.0.0.0
prefix: 24
area: 192.168.1.3
grp: prodint
intname: global2
jinjafile
{% for intf in interfaces %}
{% for i in intf.loopback_local %}
{% for key,value in i.items() %}
{{ key }}:{{ value }}
{% endfor %}
{% endfor %}
{% endfor %}

ansible jinja2: nested dictionary

I'm trying to generate the following list:
list1:127.0.0.1,127.0.0.2
list2:192.168.1.1,192.168.1.254
From this dictionary:
ip_allowed:
list1:
- 127.0.0.1
- 127.0.0.2
list2:
- 192.168.1.1
- 192.168.1.254
Using the following jinja2 Ansible template:
#ZONE HOST(S) OPTIONS
{% for hosts_key, hosts_value in ip_allowed.iteritems() %}
{% set hosts_dict = hosts_value %}
{% for item in hosts_dict %}
{{ hosts_key }} {{ item }}
{%- if loop.first %},{% endif %}
{% endfor %}
{% endfor %}
But I'm getting the following result:
#ZONE HOST(S) OPTIONS
list1 127.0.0.1, list1 127.0.0.2 list2 192.168.1.1, list2 192.168.1.254
I'm not entirely sure I got the exact format you want out of the template but you'll adapt if needed once you get the idea. Just join each ip in the value.
#ZONE HOST(S) OPTIONS
{% for allowed_item in (ip_allowed | dict2items) %}
{{ allowed_item.key }} {{ allowed_item.value | join(',') }}
{% endfor %}

taking mount name and size from ansible facts using jinja 2 for loop

I am losing my hope to solve this simple task...
All I need is take parameters "size_total" and "mount" from this loop:
{% for host in groups['test'] %}
{{ hostvars[host]['ansible_facts']['mounts'] }}
{% endfor %}
above gives me this:
[{'mount': '/', 'device': '/dev/mapper/rhel_rhel84-root', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'size_total': 18238930944, 'size_available': 14193164288, 'block_size': 4096, 'block_total': 4452864, 'block_available': 3465128, 'block_used': 987736, 'inode_total': 8910848, 'inode_available': 8802255, 'inode_used': 108593, 'uuid': 'f47a2833-0a96-42cc-aafa-172e34efff8a'}, {'mount': '/boot', 'device': '/dev/vda1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'size_total': 1063256064, 'size_available': 852586496, 'block_size': 4096, 'block_total': 259584, 'block_available': 208151, 'block_used': 51433, 'inode_total': 524288, 'inode_available': 523979, 'inode_used': 309, 'uuid': '3126f0dc-4e4d-457e-8102-1f6f7dadc43c'}]
Question: How can I access values from that dictionary ? Is it dictionary or list of dictionaries ?
Thanks in advance!
EDIT:
Thank you #Vladimir Botka.
Now situation looks like this:
I have slightly prettified your example ( with human readable values) and I have values twice, like this. Is there a way to do it so that values are inserted into destination file only once ? I assume some if filter with list will be used here.
mount: / free space: 3.77 GB
mount: /boot free space: 200.91 MB
mount: / free space: 3.89 GB
mount: /boot free space: 200.91 MB
EDIT 2:
I have managed to fix repeating output of file system. In case if someone need it, nested loops solved the issue:
{% set slots = [] %}
{% set size = [] %}
{% set size2 = [] %}
{% for host in groups['test'] %}
{% for i in hostvars[host]['ansible_facts']['mounts'] if i not in slots %}
{{ slots.append(i.mount) }}
{{ size.append((i.size_total - i.size_available )|human_readable ) }}
{{ size2.append(i.size_total|human_readable ) }}
{% endfor %}
{% endfor %}
Filesystem: {{ slots[0] }} Size: {{ size2[0] }} / {{ size[0] }}
Filesystem: {{ slots[1] }} Size: {{ size2[1] }} / {{ size[1] }}
Try this
{% for host in groups['test'] %}
{% for i in hostvars[host]['ansible_facts']['mounts'] %}
mount: {{ i.mount }} size_total: {{ i.size_total }}
{% endfor %}
{% endfor %}

Jinja2 syntax error: expected token 'end of print statement'

The below Jinja2 template is used by Ansible to generate a named zone configuration file. It fails during playbook execution (using template module) but the error is a bit cryptic to me so I didn't manage to fix it.
{# This template defines a named zone based on the dictionnary of the containers metadata #}
# IN SOA {{ net_search_domain }}. admin.{{ net_search_domain }}. (
{{ ansible_date_time['epoch'] }} ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
600 ) ; ttl
{{ ansible_hostname }} IN A {{ ansible_{{net_int_dmz_untrusted}}['ipv4']['address'] }}
{{ ansible_hostname }} IN AAAA {{ ansible_{{net_int_dmz_untrusted}}['ipv6']['address'] }}
{% for items in net_containers %}
{{ item.value.rproxy_be }} IN A {{ item.value.ipv4_container }}
{{ item.value.rproxy_be }} IN AAAA {{ item.value.ipv6_container }}
{% if {{ item.value.rproxy_fe }} != {{ item.value.rproxy_be }} %}
{{ item.value.rproxy_fe }} IN A {{ ansible_{{net_int_dmz_untrusted}}['ipv4']['address'] }}
{{ item.value.rproxy_fe }} IN AAAA {{ ansible_{{net_int_dmz_untrusted}}['ipv6']['address'] }}
{% endif %}
{% endfor %}
rtr IN AAAA xxxx:yyyy:zzzz:wwww:208:a2ff:fe0e:d127
printer IN AAAA xxxx:yyyy:zzzz:wwww:3205:5CFF:FE7C:6240
The error at Ansible run time:
failed: [192.168.11.6] (item={'file': '/home/nicolas/Documents/Configurations/Ansible/server/roles/containers/templates/named_domain.j2', 'target': '/srv/docker/dns/example.net'}) => {"changed": false, "item": {"file": "/home/nicolas/Documents/Configurations/Ansible/server/roles/containers/templates/named_domain.j2", "target": "/srv/docker/dns/example.net"},
"msg": "AnsibleError: template error while templating string: expected token 'end of print statement', got '{'. String: {# This template defines a named zone based on the dictionnary of the containers metadata #}
\n# IN SOA {{ net_search_domain }}. admin.{{ net_search_domain }}. (\n {{ ansible_date_time['epoch'] }} ; serial\n 3600 ; refresh
\n 1800 ; retry\n 604800 ; expire\n 600 ) ; ttl\n\n{{ ansible_hostname }} IN A {{ ansible_{{net_int_dmz_untrusted}}['ipv4']['address'] }}
\n{{ ansible_hostname }} IN AAAA {{ ansible_{{net_int_dmz_untrusted}}['ipv6']['address'] }}\n\n{% for items in net_containers %}\n{{ item.value.rproxy_be }} IN A {{ item.value.ipv4_container }}
\n{{ item.value.rproxy_be }} IN AAAA {{ item.value.ipv6_container }}\n{% if {{ item.value.rproxy_fe }} != {{ item.value.rproxy_be }} %}\n{{ item.value.rproxy_fe }} IN A {{ ansible_{{net_int_dmz_untrusted}}['ipv4']['address'] }}\n{{ item.value.rproxy_fe }} IN AAAA {{ ansible_{{net_int_dmz_untrusted}}['ipv6']['address'] }}
\n{% endif %}\n{% endfor %}\n\nrtr IN AAAA xxxx:yyyy:zzzz:wwww:208:a2ff:fe0e:d127\nprinter IN AAAA xxxx:yyyy:zzzz:wwww:5CFF:FE7C:6240\n\n\n"}
Adding an answer here as the first one solves half of the error.
The below Jinja2 template is the final one which provides the expected output:
# IN SOA {{ net_search_domain }}. admin.{{ net_search_domain }}. (
{{ ansible_date_time['epoch'] }} ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
600 ) ; ttl
{% set addr4 = hostvars[inventory_hostname]['ansible_default_ipv4']['address'] %}
{% set addr6 = hostvars[inventory_hostname]['ansible_default_ipv6']['address'] %}
{{ ansible_hostname }} IN A {{ addr4 }}
{{ ansible_hostname }} IN AAAA {{ addr6 }}
{% for item in net_containers %}
{{ net_containers[item].rproxy_be }} IN A {{ net_containers[item].ipv4_container }}
{{ net_containers[item].rproxy_be }} IN AAAA {{ net_containers[item].ipv6_container }}
{% if net_containers[item].rproxy_be != net_containers[item].rproxy_fe %}
{{ net_containers[item].rproxy_fe }} IN A {{ addr4 }}
{{ net_containers[item].rproxy_fe }} IN AAAA {{ addr6 }}
{% endif %}
{% endfor %}
{{ ansible_hostname }} IN A {{ ansible_{{net_int_dmz_untrusted}}['ipv4']['address'] }}
You can't use nested expressions like that, as Jinja does not do recursive evaluation. To do what you're trying to do, treat the vars as a dict and look up the key that way:
{{ ansible_hostname }} IN A {{ vars["ansible_"+net_int_dmz_untrusted]['ipv4']['address'] }}
Separately, although you didn't ask this, you really will be much happier assigning those expressions to variables, instead of copy-pasting it all over the place:
{# This template defines a named zone based on the dictionnary of the containers metadata #}
{% set the_addr4 = vars["ansible_"+net_int_dmz_untrusted]['ipv4']['address'] %}
{% set the_addr6 = vars["ansible_"+net_int_dmz_untrusted]['ipv6']['address'] %}
{{ ansible_hostname }} IN A {{ the_addr4 }}
{{ ansible_hostname }} IN AAAA {{ the_addr6 }}

refer variable in host file from jinja2

I am using jinja2 template for haproxy configuration and I have variable declared in inventory file. how do I refer this variable in template. i have paste below script but it is not working.
My Inventory:-
[as]
10.0.0.1 asRole=batch
10.0.0.2 asRole=batch
10.0.0.3 asRole=transaction
10.0.0.4 asRole=transaction
[hap]
10.0.0.6
[hap] is the ip for the haproxy and my ansible is to set the haproxy configuration on [hap] node based on the [as] node.
My Jinja2 tempalte:-
{% for host in groups['as'] %}
{% if "transaction" in "{{ asRole }} %}
server {{ hostvars[host]['inventory_hostname'] }} {{ host }}:{{ httpd_port }}check
{% endif %}
{% endfor %}
Br,
Tanmoy
Be careful with the asRole Var:
{% for host in groups['as'] %}
{% if "transaction" in asRole %}
server {{ hostvars[host]['inventory_hostname'] }} {{ host }}:{{ httpd_port }}check
{% endif %}
{% endfor %}

Resources