Ansible for loop on csv file, break out title line

1,459 views
Skip to first unread message

texas living

unread,
Nov 29, 2017, 8:52:28 AM11/29/17
to Ansible Project

I have a playbook where I am using csv file to create a vars file. The for loop iterates through the csvfile, skips the first title line then splits each line into a list. Works fine, but I was hoping to instead of skipping the title line, to pull out the names and use as the variable name instead of hard coding the names like Type, Hostname, etc. This would allow me to input csv files that have different title names and more or less names.


csv file example:

Type,Hostname,IP_Address,Gateway,DNS_Server,NTP_Server
ubuntu,lab1,10.1.1.1,10.1.1.254,192.168.1.254.192.168.1.253
centos,lab2,10.1.1.2,10.1.1.254,192.168.1.254.192.168.1.253
rhel6,lab3,10.1.1.3,10.1.1.254,192.168.1.254.192.168.1.253
rhel7,lab4,10.1.1.4,10.1.1.254,192.168.1.254.192.168.1.253


loop code:

---
servers:
{% for item in csvfile.split("\n") %}
{%   if loop.index != 1 %}
{%     set list = item.split(",") %}
  {{ list[1]|trim() }}:
    Type: {{ list[0]|trim() }}
    Hostname: {{ list[1]|trim() }}
    IP_Address: {{ list[2]|trim() }}
    Gateway: {{ list[3]|trim() }}
    DNS_Server: {{ list[4]|trim() }}
    NTP_Server: {{ list[22]|trim() }}
{%   endif %}
{% endfor %}


Question I have is, how would I pull out loop.index. 1 and use the title names as variables? Something like this:

{{ title. }}: {{ list[0]|trim() }}
{{ title. }}: {{ list[1]|trim() }}
{{ title }}: {{ list[2]|trim() }}
{{ title }}: {{ list[3]|trim() }}
{{ title }}: {{ list[4]|trim() }}
{{ title }}: {{ list[22]|trim() }}

Kai Stian Olstad

unread,
Dec 1, 2017, 11:26:35 AM12/1/17
to ansible...@googlegroups.com
On Wednesday, 29 November 2017 14.52.28 CET texas living wrote:
> loop code:
>
> ---
> servers:
> {% for item in csvfile.split("\n") %}
> {% if loop.index != 1 %}
> {% set list = item.split(",") %}
> {{ list[1]|trim() }}:
> Type: {{ list[0]|trim() }}
> Hostname: {{ list[1]|trim() }}
> IP_Address: {{ list[2]|trim() }}
> Gateway: {{ list[3]|trim() }}
> DNS_Server: {{ list[4]|trim() }}
> NTP_Server: {{ list[22]|trim() }}
> {% endif %}
> {% endfor %}
>
>
> Question I have is, how would I pull out loop.index. 1 and use the title
> names as variables? Something like this:
>
> {{ title. }}: {{ list[0]|trim() }}
> {{ title. }}: {{ list[1]|trim() }}
> {{ title }}: {{ list[2]|trim() }}
> {{ title }}: {{ list[3]|trim() }}
> {{ title }}: {{ list[4]|trim() }}
> {{ title }}: {{ list[22]|trim() }}

Can't you just do something like this?

{% if loop.index == 1 %}
{% set title = item.split(',') %}
{% endif %}

Then you can use title[0], title[1]...


--
Kai Stian Olstad

texas living

unread,
Dec 1, 2017, 12:08:15 PM12/1/17
to Ansible Project
I tried, but the title variable does not keep when looping from second row and on.

    {% for item in csvfile.split("\n") %}
    {%   if loop.index == 1 %}
    {%     set title = item.split(',') %}
    {%   else %}
    {%     set list = item.split(",") %}
    {{ list[1]|trim() }}:
    {{ title[0] }}: {{ list[0]|trim() }}
    {{ title[1] }}: {{ list[1]|trim() }}
    {{ title[2] }}: {{ list[2]|trim() }}
    {{ title[3] }}: {{ list[3]|trim() }}
    {{ title[4] }}: {{ list[4]|trim() }}
    {{ title[5] }}: {{ list[5]|trim() }}
    {%   endif %}
    {% endfor %}

The playbook output for task

    TASK [Parse CSV To YAML] ***********************************************************************************************************
    fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "AnsibleUndefinedVariable: 'title' is undefined"}

    msg: AnsibleUndefinedVariable: 'title' is undefined

Kai Stian Olstad

unread,
Dec 1, 2017, 1:16:51 PM12/1/17
to ansible...@googlegroups.com
That's strange since the Jinja documentation have the following about scope:
"Please keep in mind that it is not possible to set variables inside a block and have them show up outside of it.
This also applies to loops. The only exception to that rule are if statements which do not introduce a scope."

Maybe it's a bug.


--
Kai Stian Olstad

Kai Stian Olstad

unread,
Dec 1, 2017, 2:04:57 PM12/1/17
to ansible...@googlegroups.com
As a workaround since this i a scope issue, you could set the title outside of the for loop.

{% set title = csvfile.split("\n")[0].split(',') %}
{% for item in csvfile.split("\n")[1:] %}
{% set list = item.split(",") %}
{{ list[1]|trim() }}:
{{ title[0] }}: {{ list[0]|trim() }}
{{ title[1] }}: {{ list[1]|trim() }}
{{ title[2] }}: {{ list[2]|trim() }}
{{ title[3] }}: {{ list[3]|trim() }}
{{ title[4] }}: {{ list[4]|trim() }}
{{ title[5] }}: {{ list[5]|trim() }}
{% endfor %}

--
Kai Stian Olstad

texas living

unread,
Dec 1, 2017, 3:25:53 PM12/1/17
to Ansible Project
Works perfectly! Thank you so much Kai Stian.

texas living

unread,
Dec 1, 2017, 4:31:42 PM12/1/17
to Ansible Project
I do have one more question. Say I am not sure if the last list[6] variable is defined, how would I write that? I tried the below but did not work.

{% set title = csvfile.split("\n")[0].split(',') %}
{% for item in csvfile.split("\n")[1:] %}
{%   set list = item.split(",") %}
  {{ list[1]|trim() }}:
    {{ title[0] }}: {{ list[0]|trim() }}
    {{ title[1] }}: {{ list[1]|trim() }}
    {{ title[2] }}: {{ list[2]|trim() }}
    {{ title[3] }}: {{ list[3]|trim() }}
    {{ title[4] }}: {{ list[4]|trim() }}
    {{ title[5] }}: {{ list[5]|trim() }}
{%   if {{ list[6] }} %}
    {{ title[6] }}: {{ list[6]|trim() }}

Kai Stian Olstad

unread,
Dec 1, 2017, 4:40:57 PM12/1/17
to ansible...@googlegroups.com
You can't use {{ }} inside then template code, since you are in template mode it understand that list[6] is a variable.
Try this

{% if list[6] is defined %}
{{ title[6] }}: {{ list[6]|trim() }}
{% endif %}


--
Kai Stian Olstad

texas living

unread,
Dec 1, 2017, 4:54:49 PM12/1/17
to Ansible Project
That worked, thanks again for you help.

Adolphson

unread,
Dec 2, 2017, 8:18:26 PM12/2/17
to ansible...@googlegroups.com
Sorry, one more thing. I want to identify if the title equals Hostname and both the title and list index match. I have it working below, but was hoping there is a cleaner way of doing this.  

{% set title = csvfile.split("\n")[0].split(',') %}
  {% for item in csvfile.split("\n")[1:] %}
  {% set list = item.split(",") %}

  {%   if title[0] == "Hostname" and list[0] %}
    {{ list[0]|trim() }}:
  {%   elif title[1] == "Hostname" and list[1] %}
    {{ list[1]|trim() }}:
  {%   elif title[1] == "Hostname" and list[1] %}
    {{ list[1]|trim() }}:
  {%   elif title[2] == "Hostname" and list[2] %}
    {{ list[2]|trim() }}:
  {%   elif title[3] == "Hostname" and list[3] %}
    {{ list[3]|trim() }}:
  {%   elif title[4] == "Hostname" and list[4] %}
    {{ list[4]|trim() }}:
  {%   elif title[5] == "Hostname" and list[5] %}
    {{ list[5]|trim() }}:
  {%   endif %}

  {{ title[0] }}: {{ list[0]|trim() }}
  {{ title[1] }}: {{ list[1]|trim() }}
  {{ title[2] }}: {{ list[2]|trim() }}
  {{ title[3] }}: {{ list[3]|trim() }}
  {{ title[4] }}: {{ list[4]|trim() }}
  {{ title[5] }}: {{ list[5]|trim() }}
  {% endfor %}


 

On Fri, Dec 1, 2017 at 3:54 PM, texas living <adolp...@gmail.com> wrote:
That worked, thanks again for you help.

--
You received this message because you are subscribed to a topic in the Google Groups "Ansible Project" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ansible-project/t8RUjtnZlyM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to ansible-project+unsubscribe@googlegroups.com.
To post to this group, send email to ansible-project@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/94204212-a180-455e-b604-27b71cef1c2a%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Kai Stian Olstad

unread,
Dec 3, 2017, 8:46:38 AM12/3/17
to ansible...@googlegroups.com
On 03.12.2017 02:18, Adolphson wrote:
> Sorry, one more thing. I want to identify if the title equals Hostname
> and
> both the title and list index match.

I don't understand what your mean, could you rephrase.


> I have it working below, but was
> hoping there is a cleaner way of doing this.
>
>
> {% set title = csvfile.split("\n")[0].split(',') %}
> {% for item in csvfile.split("\n")[1:] %}
> {% set list = item.split(",") %}
>
>
> {% if title[0] == "Hostname" and list[0] %}
> {{ list[0]|trim() }}:
> {% elif title[1] == "Hostname" and list[1] %}
> {{ list[1]|trim() }}:
> {% elif title[1] == "Hostname" and list[1] %}
> {{ list[1]|trim() }}:
> {% elif title[2] == "Hostname" and list[2] %}
> {{ list[2]|trim() }}:
> {% elif title[3] == "Hostname" and list[3] %}
> {{ list[3]|trim() }}:
> {% elif title[4] == "Hostname" and list[4] %}
> {{ list[4]|trim() }}:
> {% elif title[5] == "Hostname" and list[5] %}
> {{ list[5]|trim() }}:
> {% endif %}

To me, it looks like this code just print out the value correspond to
the "Hostname".
If that is the goal, then this for loop does that.

{% for element in range(list | count) %}
{% if title[element] == "Hostname" %}
{{ list[element]|trim }}:
{% endif %}
{% endfor %}


> {{ title[0] }}: {{ list[0]|trim() }}
> {{ title[1] }}: {{ list[1]|trim() }}
> {{ title[2] }}: {{ list[2]|trim() }}
> {{ title[3] }}: {{ list[3]|trim() }}
> {{ title[4] }}: {{ list[4]|trim() }}
> {{ title[5] }}: {{ list[5]|trim() }}

This section can be replaced with this for loop, it's dynamic so it will
work with any number for column in the csv file.

{% for element in range(list | count) %}
{{ title[element] }}: {{ list[element] | trim }}
{% endfor %}


--
Kai Stian Olstad

texas living

unread,
Dec 3, 2017, 9:45:21 AM12/3/17
to Ansible Project
Thanks Kai Stian, that is exactly what I was looking for.

texas living

unread,
Dec 7, 2017, 8:34:21 PM12/7/17
to Ansible Project
Kai Stian, I hope you can help with one more question. I searched but could not find out how to insert an ansible variable into the django loop.

---
{% set title = csvfile.split("\n")[0].split(',') %}
{% for item in csvfile.split("\n")[1:] %}
{%   if "{{inventory_hostname}}" in item %}
{%     set list = item.split(",") %}
{%     for element in range(list | count) %}
{{ title[element]|trim }}: {{ list[element]|trim }}       
{%     endfor %}

texas living

unread,
Dec 7, 2017, 8:42:11 PM12/7/17
to Ansible Project
I found out how. I created a variable for the {{ inventory_hostname }} first.

vars:
  vsrx: "{{ inventory_hostname }}"

{% set title = csvfile.split("\n")[0].split(',') %}
{% for item in csvfile.split("\n")[1:] %}
{%   if vsrx in item %}
{%     set list = item.split(",") %}
{%     for element in range(list | count) %}
{{ title[element]|trim }}: {{ list[element]|trim }}
{%     endfor %}

Kai Stian Olstad

unread,
Dec 8, 2017, 2:57:26 AM12/8/17
to ansible...@googlegroups.com
On Friday, 8 December 2017 02.42.11 CET texas living wrote:
> I found out how. I created a variable for the {{ inventory_hostname }}
> first.
>
> vars:
> vsrx: "{{ inventory_hostname }}"
>
> {% set title = csvfile.split("\n")[0].split(',') %}
> {% for item in csvfile.split("\n")[1:] %}
> *{% if vsrx in item %}*

You don't need to do that, just use inventory_hostname directly, it's already a variable.

{% if inventory_hostname in item %}


--
Kai Stian Olstad
Reply all
Reply to author
Forward
0 new messages