how to iterate csv file in ansible - ansible

i have a jinja2 template including a section that need data from a csv file
how can i read a csv file and split it into a list then iterate it in the jinja2 template? sth. like this:
{% for line in csv_data %}
{{ line[0] }} = {{ line[1] }}
{% endfor %}
in my task file, i am trying to use lookup to read the csv file into csv_data, but it seems lookup can only query and get one line not the whole file, or just the whole file in raw format
vars:
csv_data: "{{ lookup('file', 'test.csv') }}"

figured a not so good method:
{% for line in csv_data.split("\n") %}
{% set list = line.split(",") %}
{{ list[0] }}={{ list[1] }}
{% endfor %}

Related

How to trim last character when using ansible jinja loop

My template like as blow
{% if hostvars[inventory_hostname].local_zk_server_id is defined %}
zookeeperServers={% for host in {{ groups[{{ target_hosts }}] %}}
"{{ hostvars[host].inventory_hostname }}:2181,"
{% endfor %}
{% endif %}
output ishost1:2181,host2:2181,host3:2181,
How to trim last comma
There are several possible gotchas in your above template regarding variables access. Moreover, rather than trimming the last character in your string, the best solution is probably not to write it. Here is a better solution IMO in my below example fixing all the problems I'm referring to:
{% set zookeeperServers=[] %}
{% if hostvars[inventory_hostname].local_zk_server_id is defined %}
{% for host in groups[target_hosts] %}
{% zookeeperServers.append(hostvars[host].inventory_hostname + ":2181") %}
{% endfor %}
zookeeperServers="{{ zookeeperServers | join(',') }}"
{% endif %}

Jinja2 for item in list change to upper case

I am trying to generate configuration using jinja2 and ansible. ansible read csv and set facts.
{%- for item in facts_csv %}
Hello {{ item.abc}}
{% endfor %}
in the csv file "abc" has a value "mike". When i run the play book its generating "Hello mike".
Now i want to print "Hello MIKE", without changing the CSV file values?
seems item.abc|upper and item.abc.upper() did not work. Any other solutions?
The template works as expected
{%- for item in facts_csv %}
Hello {{ item.abc }}
{% endfor %}
{%- for item in facts_csv %}
Hello {{ item.abc|upper }}
{% endfor %}
gives
Hello mike
Hello MIKE

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.

unable to parse ansible variable in jinja file using if condition

I'm encountering below error while trying to pass ansible variable through if condition in jinja. (elsewhere I'm able to pass a string direclty and use the same condition in jinja and also able to read the ansible variable without any condition) Any leads please...
yaml extract
---
tasks:
- set_fact:
ansible_role: "role1"
- name: Create the Jinja2 based template
template: src=./source.j2 dest=./output.txt
source.j2
---
{% for role in roles %}
{% if {{ ansible_role }} == role.name %}
{% for item in role.tests %}
"{{ item }}"
{% endfor %}
{% endif %}
{% endfor %}
error
TASK [Create the Jinja2 based template]
***************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "AnsibleError: template error while templating string: expected token
':', got '}'. String: ---\n{% for role in roles %}\n{% if {{
ansible_role }} == role.name %}\n{% for item in role.tests
%}\ninclude_controls \"{{ item }}\"\n{% endfor %}\n{% endif %}\n{%
endfor %}\n"}
Take some time to review the jinja2 template designer documentation. Your variable name is already inside a jinja2 expression. The double curly braces has to be used outside expressions to have the content of your variable written out, not inside an expression.
Therefore, you should change:
# !! WRONG !!
{% if {{ ansible_role }} == role.name %}
to
{% if ansible_role == role.name %}
My last 2 cent: don't prepend your own variable names with ansible_ as this is used by ansible itself for a lot of internal/magic variables and might lead to confusion.
I think the problem is in this line: {% if {{ ansible_role }} == role.name %}. When you use the {% %} notation, Jinja expects python inside. So you don't need to escape the ansible_role variable. Try it without it and see if now it works.

Ansible loop over range of letters in template

I'm trying to generate an Ansible template that increments on letters alphabetically rather than numbers. Is there a function similar to range(x) that could help me?
pseudo code example
{% for letter in range(a, d) %}
{{ letter }}
{% endfor %}
expected output
a
b
c
d
Alternatively is there a way to convert a number into it's alphabetical equivalent in Ansible?
{% for i in range(6) %}
{{ convert(i) }}
{% endfor %}
UPDATE
For those who are curious, here's how I ended up applying #zigam's solution. The goal was to create xml tags with every host from a hostgroup.
In my role defaults:
ids: "ABCDEFGHIGJKLMNPQRSTUVWXYZ"
In my template:
{% for host in groups['some_group'] %}
<host-id="{{ ids[loop.index] }}" hostName="{{ host }}" port="8888" />
{% endfor %}
You can iterate over a string:
{% for letter in 'abcd' %}
{{ letter }}
{% endfor %}
If you want to iterate over a range of the alphabet:
{% set letters='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' %}
{% for letter in letters[:6] %} {# first 6 chars #}
{{ letter }}
{% endfor %}
you can use a custom filter plugin to do what you want
in filter_plugins/scratch_filter.py:
def scratch_filter(n):
return chr(n)
class FilterModule(object):
''' Number to Character filter '''
def filters(self):
return {
'scratch_filter': scratch_filter
}
in scratch-template.j2:
{% for x in range(101, 113) %}
{{ x|scratch_filter }}
{% endfor %}
in scratch_playbook.yml
---
- hosts: localhost
tasks:
- name: test loop
template:
src: "{{ playbook_dir }}/scratch-template.j2"
dest: "{{ playbook_dir }}/scratch-template-output.txt"

Resources