Ansible Jinja2 template for loop - ansible

I have two linux servers:
- server1: ip: 10.241.55.6, hostname: server1
- server2: ip: 10.242.55.7, hostname: server2
I have created an ansible inventory file named servers with the content bellow:
[IC]
10.241.55.6
10.241.55.7
Now I have created this jinja2 inventory template file: test.j2 with this content:
[IC]
{% for hostip in groups['IC'] %}
{% if hostip == ansible_default_ipv4.address %}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{% endif %}
{% endfor %}
And I'm running this ansible playbook:
---
- name: Generate portal inventory file
hosts: all
tasks:
- name: Generate inventory
delegate_to: localhost
template:
src: inventory/test.j2
dest: inventory/test
The command is: ansible-playbook -i inventory/servers generate-inventory.yml
The final goal is that ansible connects to each of the servers from the inventory files and then based on the jinja2 inventory template, it creates a new inventory file with this format:
[IC]
10.241.55.6 default_hostname=hostname_of_the_server_with_that_ip
and so on...
The issue here with the for loop is that all the entries are with the same server ip (while I should have an entry for each of the servers with their respective hostnames):
[IC]
10.241.55.6 default_hostname=server1
10.241.55.6 default_hostname=server2
What I'm missing here? Also if there is any other better way to achieve this please let me know.

You're using the same variable twice in the template...
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
...so of course you're getting two identical lines. It sounds like you want to access the per-host value of this variable, which means you need to access it via hostvars.
Maybe something like this:
[IC]
{% for host in groups['IC'] %}
{{ hostvars[host].ansible_default_ipv4.address }} default_hostname={{ hostvars[host].ansible_nodename }}
{% endfor %}

Related

Creating text file from jinja template with inputs from csv file

I'm trying to create a text file which goes through the CSV file and populates variables into specific fields.
playbook.yml:
- hosts: localhost
tasks:
- name: "Reading user information"
read_csv:
path: /home/test/vlans.csv
delimiter: ','
register: vlans
- debug: var=vlans
- name: Creating VLANs configuration
template:
src: vlan.conf.j2
dest: /tmp/vlan.conf
Jinja2 Template vlan.conf.j2:
{% for item in vlans %}
!
vlan {{ item.VLAN }}
name {{ item.Description }}
vn-segment {{ item.VNI }}
interface nve1
member vni {{ item.VNI }}
{% endfor %}
and this is a test vlans.csv file:
Tenant,VRF ,VLAN,VNI,Subnet,Description,Good to go
Test,,5,20005,,LAB-Checkpoint-FW-Mgmt,Yes
Test,,208,20208,,LAB-DMZ,Yes Test,,209,20209,,LAB-CSR-MGMT,Yes
Test,10000,210,20210,192.168.12.1/28,LAB-VRF-to-FW,Yes
Prod,,761,20761,,PROD-CORE,Yes
Prod,105,840,20840,172.18.33.1/24,Backups,Yes
Prod,,841,20841,,Transport,Yes
I want to end up with file like in jinja2 template and not repeating line "interface nve 1"
In your question, interface nve1 is inside a loop. It will repeat multiple times in the resulting vlan.conf file.
Use multiple loops in the jinja template to decide what is repeated and what isn't:
{% for item in vlans.list %}
vlan {{ item.VLAN }}
name {{ item.Description }}
vn-segment {{ item.VNI }}
{% endfor %}
interface nve1
{% for item in vlans.list %}
member vni {{ item.VNI }}
{% endfor %}
Note that I've referred to vlans.list in the start of the loops instead of vlans. This is correct as per documentation, but different to the example in the question so it may need adjusting.

How to use hosts in my playbook for initiating my template

I have a playbook to initiate a conf to servers that requires hostname. I want to pass my hosts that define in playbook as group so as to loop the variables in my jinja2 template. And I don't want to set the hosts in vars(because that was already define in playbook, I don't know the reason for re-define)
For example:
host file:
[test_servers]
t1
t2
t3
[test2_servers]
t2
t4
playbook:
- hosts: test_servers
tasks:
- name: generate my conf
template:
src: templates/temp.conf.j2
dest: "test.conf"
force: True
vars:
hosts: test_servers # So far I need to declare the var here duplicately, I've group my server in host file and I just want to use the current group.
temp.conf
....
{% for host in groups[hosts] %}
Entry.{{ loop.index }} = {{ host }}
{% endfor %}
....
I wonder if there is a better way to pass the hosts that set in playbook to my jinja2 template so I can re-use the playbook for different hosts. For example, I just need to reset the playbook hosts to test2, no need to rewrite the vars.
There is no special variable with the name of the group, but there is ansible_play_hosts_all
List of all the hosts that were targeted by the play
Remove vars and use ansible_play_hosts_all in the template
- hosts: test_servers
tasks:
- name: generate my conf
template:
src: templates/temp.conf.j2
dest: "test.conf"
force: True
{% for host in ansible_play_hosts_all %}
Entry.{{ loop.index }} = {{ host }}
{% endfor %}

ansible - get host by variable

I would like to filter hosts by a variable set on them. For example:
I have a group of hosts, one is master, the rest are slaves. On the master the variable replica_type: master is set. I would now like to get the master server dynamically.
My working aproach is:
- set_fact:
master_server_string: >-
{% for server in groups.my_servers %}
{% if hostvars[server]['replica_type']=='master' -%}
{{ server }}
{% endif %}
{% endfor %}
- set_fact:
master_server: "{{ master_server_string|trim }}"
Is there a way to perform the same with a filter (or at least in a single task)?
e.g. something like
"{{ server for server in groups.my_servers if hostvars[server]['replica_type'] == 'master' }}"
I believe you are looking for the group_by module.
https://docs.ansible.com/ansible/latest/modules/group_by_module.html
- group_by:
key: {{ replica_type }}
You can pull a value out of a group of hosts by a property by looping the group and checking for a matching value like this too:
- set_fact:
master_server: "{{ item }}"
with_items: "{{ groups.my_servers }}"
when: hostvars[item].replica_type == 'master'

How to get all inventory variables for hosts in same group (except the current host)?

Suppose we have an ansible inventory like this:
[somegroup]
host1 secondaryIp=1.0.0.0
host2 secondaryIp=1.0.0.1
host3 secondaryIp=1.0.0.2
While running a task (specifically template module) is there a way to get the list of secondaryIp's for "all other hosts" in the group [somegroup] ??
I tried searching ansible filters: http://docs.ansible.com/ansible/latest/playbooks_filters.html#list-filters
My only working idea was to do this in inventory:
[somegroup]
host1 secondaryIp=1.0.0.0 otherIps="1.0.0.1,1.0.0.2"
host2 secondaryIp=1.0.0.1 otherIps="1.0.0.0,1.0.0.2"
host3 secondaryIp=1.0.0.2 otherIps="1.0.0.0,1.0.0.1"
You can get a list of hosts in the group using groups['somegroup'] and access their variables using hostvars. To exclude the host ansible is currently running on you just need to check if the current host in the list equals inventory_hostname. Here's how it works in practice.
In a template
{% for host in groups['somegroup'] %}
{% if host != inventory_hostname %}
{{ hostvars[host]['secondaryIp'] }}
{% endif %}
{% endfor %}
In a task
- name: debug
debug:
msg: "{{ hostvars[item]['secondaryIp'] }}"
when: item != inventory_hostname
with_items: "{{ groups['somegroup'] }}"

Jinja2 in Ansible Playbook

how can i loop in an ansible playbook over gathered facts?
I tried:
...
haproxy_backends:
- name: 'backend'
servers:
{% for host in groups['app-servers'] %}
- name: "{{ hostvars[host]['ansible_hostname'] }}"
ip: "{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}"
{% endfor %}
But this doesn't work, it results in a Syntax Error. Is it even possible to use jinja in a playbook?
I use a ansible galaxy role (info.haproxy) and i don't want to change the provided templates.
No you can't do this.
This has to be done in a template, something like :
template/haproxy.cfg.j2 :
...
{% for host in groups['app-servers'] %}
backend {{ hostvars[host]['ansible_hostname'] }}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}:1234 check inter 5000 slowstart 2m
{% endfor %}
...
and use :
tasks:
- name: Deploy haproxy config
template: src=templatepath/haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg
You get the idea, YMMV.
Good luck.

Resources