List of IP in Ansible templates

I was developing a simple Ansible role for deploying elasticsearch today and I encountered a problem with Ansible’s Jinja2 template. Basically, in the configuration file for elasticsearch, I need something like

discovery.zen.ping.unicast.hosts: ["192.168.0.1", "192.168.0.2", "192.168.0.3"]

At first, I cannot extract IP addresses for all Ansible hosts. Later I figured out a way to do that, but I don’t know how to turn a list of IPs into a string with the IPs all quoted. Luckily, I found a solution to that too and everything is working in the end. This blog documents how to output a list of IP in the above format using Ansible’s template engine, Jinja2.

The map function

The map function is a concept in functional programming. The idea is that we want to apply the same function to every item in a given list. For example, we could apply a lowercase function to the list ["A", "B", "C"] and we will get back a new list ["a", "b", "c"]. If you are familiar with Python, you can think of it as the list comprehension in Python. In our case, we could use the map function in Jinja2 like this

{{ letters|map("lower") }}

Besides applying functions, the map function in Jinja2 can also be used to access an attribute for the objects in a list. Therefore, we could extract the IP addresses of all hosts in inventory with

{{ hostvars.values()|map(attribute='ansible_host') }}

And the inventory file looks like

ubuntu1 ansible_host=192.168.0.1
ubuntu2 ansible_host=192.168.0.2

Notice that we used the values method on hostvars because hostvars is a dictionary and we want to iterate over its values.

The join function

Now that we have the list of IP addresses, we could use the join function that comes with Jinja2 to join a list of items into a string. Since we want a comma delimited string, we could do

[{{ hostvars.values()|map(attribute='ansible_host')|join(', ') }}]

However, you should notice that if we do this, we would get something like

[192.168.0.1, 192.168.0.2, 192.168.0.3]

We still need to add double quotes for all the IP addresses! One way of doing that is to use a custom filter. We could define a filter which converts an input like 192.168.0.1 into "192.168.0.1". However, there is also an quick and easy way of achieving the same effect

["{{ hostvars.values()|map(attribute='ansible_host')|join('", "') }}"]

Now, we first add the quotes on the left of the 1st IP and on the right of the last IP. Then, we join by ", " instead of ,. If we execute this template, we would be able to get what we want

["192.168.0.1", "192.168.0.2", "192.168.0.3"]

Conclusion

There you have it. With a map function and a join function in Jinja2, we could now successfully generate a list of quoted IP addresses. I hope that you learnt some tips and tricks of Jinja2 in this blog post! If you want to learn more about the concepts I covered here, please refer to the following links.

References

 
comments powered by Disqus