Ansible facts of all hosts to template - ansible

I'm trying to write a dhcpd.conf template for my server to set static leases that is looking like this atm:
`
{% for item in groups.all %}
{% set short_name = item.split('.') %}
host {{ item }} {
hardware ethernet {{ ansible_eth0.macadress }};
fixed-address {{ hostvars[item]['ipaddress'] }};
}
{% endfor %}
`
'ipaddress' is a variable set in the inventory file
After running the playbook everything works fine, but the value for 'ansible_eth0.macaddress' is always the same and not like expected the macaddress matching to the right host.
Does anyone have an idea how i can make the loop working like i want to?

Without the tasks you use this jinja2 template i can just guess. Maybe u are fine with_subelements.

Related

How can one generate a dict in Jinja/Ansible as a var?

I need to dynamically generate a dict and assign it to an Ansible var so that the same data is available to every host and does not need to be regenerated on each one. This will be looped over by a task and template. It is possible to build lists by nesting Jinja in a var definition like so:
var_list: >
[
{% set data = [] %}
{% for host in groups['all'] %}
{{ data.append(host) }}
{% endfor %}
{{ data }}
]
However, I am not able to do so with dicts:
var_dict: >
{
{% set data = dict() %}
{% for host in groups['all'] %}
{{ data | combine({host: []}) }}
{% endfor %}
{{ data }}
}
This results in a giant, garbled string: {u'host.subdom.dom.tld': []} {}...
What I am expecting is a var set to a true dict object that can have its components referenced individually (like {{ var_dict.host1[0] }}), with a structure similar to the following JSON:
{
"host1": [],
"host2": []
}
Is there a way to cause this to expand into a proper dict when assigned to the variable? Or is there perhaps another way to generate a dict from a loop without that awful set_fact hackery?
Either of your templates is broken (the first one produces nested lists, the second one prints data in loop with this expression {{ data | combine({host: []}) }}, the value of data remains empty until the end).
Jinja2 is a templating engine, you don't really need to create data structures to print them later. You can form your output directly.
For the list:
var_list: >
[ {% for host in groups['all'] %}'{{ host }}',{% endfor %} ]
For the dictionary:
var_dict: >
{ {% for host in groups['all'] %}'{{ host }}': [],{% endfor %} }
As you expect the values to be interpreted by Ansible, you don't need to pay attention to the trailing coma, otherwise there is loop.last.

Using Jinja2 Expression within Jinja2 Statement

Let's say I have the following Jinja2 variables:
'dev_ami' = 'ami-123456'
'dev_located_ami' = 'ami-123456'
'prod_ami' = 'ami-654321'
'prod_located_ami' = 'ami-654321'
I would like to set a condition upon when the 'dev_ami' variable is equal to the 'dev_located_ami' variable. This would easily be done as shown in the following statement:
{% if dev_ami == dev_located_ami %}
... do some stuff
{% else %}
... do some other stuff
{% endif %}
But I would like to dynamically compare amis based on the deployment environment contained in a list ['dev','prod', etc...]. The following contains a templating error since there is an expression within a statement as such - {% {{ .. }} %}:
{% for env_type in ['dev','prod'] %}
{% if {{ env_type }}_ami == {{ env_type }}_located_ami %}
... do stuff
{% else %}
... do other stuff
{% endif %}
{% endfor %}
I have tried to set variables to represent the expressions I would like in the following code but unfortunately they are compiled literally as 'dev_ami' and 'dev_located_ami' whereas I would like them compiled to their corresponding variable values 'ami-123456' and 'ami-123456':
{% for env_type in ['dev','prod'] %}
{% set ami = "%s_ami"|format(env_type) %}
{% set located_ami = "%s_located_ami"|format(env_type) %}
{% if ami == located_ami %}
... do stuff
{% else %}
... do other stuff
{% endif %}
{% endfor %}
I have checked through various filters and so far have had no success. Would appreciate advice on getting this specific implementation to work properly. Thank you in advance.
I think you might be approaching the problem with the wrong datastructure in mind.
Dynamically generating variable names in order to compare amis in different environemts sounds like a massive overkill to me. Are you familiar with lists & dictionaries?
Try to start from something like this (pseudocode):
dict = { environments:
prod:
ami1: foo
ami1_located: foo
dev:
ami1: bar
ami1_located:baz
}
for env in dict[environments]:
if env[ami1] == env[ami1_located]:
[...]

Ansible: Include a file if it exists and do nothing if it doesn't

I have a Jinja2 template in Ansible and I need to include some files conditionally.
Basically, I have a for loop that includes "subparts" that might or might not be present. The following sample demonstrates the basic code that would work if all tools actually had a 'pbr.j2' file, but it crashes because Ansible cannot find some of the files:
{% for tool in tools %}
{% set template = 'tools/' + tool.name + '/pbr.j2' %}
{% include template %}
{% endfor %}
I have also tried the exists and is_file filters, but I always get "false" even when the file exists. Here is a sample code of what I have tried:
{% for tool in tools %}
{% set template = 'tools/' + tool.name + '/pbr.j2' %}
{% if template|exists %}
{% include template %}
{% endif %}
{% endfor %}
The result of this last sample is that no file gets included. If I replace exists with is_file, I get the same behavior.
What do I need to change to accomplish what I want?
I have found a solution to my problem: using ignore missing in the include directive.
In this case, I would use:
{% for tool in tools %}
{% set template = 'tools/' + tool.name + '/pbr.j2' %}
{% include template ignore missing %}
{% endfor %}
Ansible will ignore missing files and only the existing files from that loop will get included.
You are trying to use Ansible filters (exists, is_file) in a Jinja2 template. This works for templates processed by Ansible templating engine, but not for templates processed by the template module.
Jinja2 does not have capability to check the existence of a file, so you need to move your logic to Ansible.
For example: create a find task to search for directories in tools/, and provide an intersection of find-results list with tools list to Jinja2.

Change variable in Ansible template based on group with children?

I found Change variable in Ansible template based on group. However how do I extend the answer in groups with children?
If I extend the group vars file in the link above to
[host0]
host0.my-network.com
[host1]
host1.my-network.com
[es-masters:children]
host0
host1
How can I extend the above post's answer (shown below) to make it work with the group vars file above? Simply putting the FQDN in the jinja2 statement does not work.
{% if ansible_fqdn in groups['es-masters'] %}
node_master=true
{% else %}
node_master=false
{% endif %}
What you should do is:
Provide default in template
# role_name/templates/template.j2
node_master={{ role_name_node_master | default(true) }}
Than override in group_vars
# group_vars/es-masters.yml
role_name_node_master: false

Jinja2 if/else matching both

I'm trying to use an if/else statement in an ansible template to populate values based on my hostname. I've written a module that parses the hostname and adds the values as facts. I'm able to use these facts in my playbooks but am having trouble getting an if/else statement to use them correctly. I end up with both the if and else matching so both values end up in my file.
jinja2 template
{% for host in groups['tag_Function_logfe'] %}
{% if hostvars[host]['tt_number'] == "01" %}
is_master = true
{% else %}
is_master = false
{% endif %}
{% endfor %}
Ansible Facts:
ok: [node1] => {"ansible_facts": {"tt_number": "01"}, "changed": false, "msg": "Additional facts added successfully"}
ok: [node2] => {"ansible_facts": {"tt_number": "02"}, "changed": false, "msg": "Additional facts added successfully"}
results:
is_master = true
is_master = false
I don’t have the full picture of what you’re trying to do overall, but Ansible and Jinja seem to behave as documented in this case so there might be a misunderstanding elsewhere.
You have a template that Ansible would render into a configuration file on every host. In this template, a for loop iterates over hosts in given group and declares is_master for each of those hosts. That would result in multiple definitions in every instance of the template, equal to the number of hosts in the “tag_Function_logfe“ group.
That doesn’t seem to make sense to me, since I’d assume there would be only one is_master definition per host.
Perhaps what you wanted to do instead is having this in the template:
{% if inventory_hostname in groups['tag_Function_logfe'] %}
{% if tt_number == "01" %}
is_master = true
{% else %}
is_master = false
{% endif %}
{% endif %}
The inventory_hostname points to the host Ansible would be working with at the moment. We apparently want to check that this host belongs to the “tag_Function_logfe” group, and if it doesn’t we don’t define is_master at all.
Then we want to determine what value is_master should have based on tt_number. Note that we don’t need to use hostvars because we access tt_number’s value only for current host where the template is rendered into the configuration file.
It’s probably possible to further eliminate the group membership test, but that’s harder to tell without looking at the rest of the configuration.

Resources