i hope you guys can help me.
I want to copy following template with ansible:
IFACE_EXTERNAL="{{ ansible_default_ipv4.interface }}"
IP_EXTERNAL="{{ ansible_default_ipv4.address }}"
IFACE6_EXTERNAL="{{ ansible_default_ipv6.interface }}"
IP6_EXTERNAL="{{ ansible_default_ipv6.address }}"
{% if ansible_interfaces[2] is defined %}
{% set count = 2 %}
{% for iface in ansible_interfaces %}
{% if "tun0" in iface %}
IFACE_INTERNAL_1="{{ iface }}"
{% endif %}
{% if "ens" in iface %}
IFACE_INTERNAL_{{count}}="{{ iface }}"
{% set count = count + 1 %}
{% endif %}
{% if hostvars[inventory_hostname]['ansible_%s'|format(iface)]['ipv4'] is defined %}
IP_INTERNAL_{{loop.index}}="{{ hostvars[inventory_hostname]['ansible_%s'|format(iface)]['ipv4']['address'] }}"
{% endif %}
{% endfor %}
{% endif %}
Now, if i copy this template with ansible, i get following error: "AnsibleUndefinedVariable: 'dict object' has no attribute 'interface'".
I tried for an hour but don't get a solution.
Does anyone know where the error is?
You wrote a very complicated jinja template. Now, live and suffer with it.
It's really bad idea to have a complicated access patterns into dynamic variables within jinja, as you have 0 (zero) debug tools there.
The proper way is to have all computation to be done at ansible level:
tasks:
- debug: var=some_var1
- debug: var=some_var2
- template:
src: foo.j2
dest: foo
vars:
some_var1: '{{...}}'
some_var2: '{{...}}'
By using external (ansible) vars you can debug them. If you use computation in Jinja you need to be 100% sure data are there. How can you be sure that facts are there, if default set of network variables is dependent on where there is default gateway or not on the target system?
To avoid such errors you must use something similar to {{ ansible_default_ipv6.interface | default('foo') }} as there is no guarantee that ansible_default_ipv6 object has the interface attribute.
If you do not do this, you will get templating errors. The other option is to use if conditions like '{% if interface' in ansible_default_ipv6 %}....
Related
I have a for loop in Django template. After that, I check for coincidences. But in some cases, there are might be 3 coincidences. I need to show only the first coincidence. Now, my code returns the name for 3 times, because, there are 3 coincidences
{% for ip in ips %}
{% if d.name == ip.name %}
<strong>{{ d.name}} </strong>
{% endif %}
{% endfor %}
SOLUTION
It is impossible to break forloop in django template, so I decided to change in views.py through queryset distinction of similar names
ips = Point.objects.defer('point').order_by('name').distinct('name')
I don't recommend doing this in Django Template , but in views itself. But if you can't then you can use {{ forloop|break }}.
Something like this :
{% for ip in ips %}
{% if d.name == ip.name %}
{{ forloop|break }}
<strong>{{ d.name}} </strong>
{% endif %}
{% endfor %}
Check the small snippet example here...
Before showing a text I have to evaluate it.
Instead of
{% if value %} text_true {% else %} text_false {% endif %}
Are there anything like:
{{ text_true if value else text_false }}
For the example in your question, you can use the yesno filter
{{ value|yesno:'text_true,text_false' }}
Not with this exact syntax, but there are the default and default_if_none template filters. Those may be what you're looking for.
https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#default
I am trying to iterate a list to populate a variable to be used to set the value of a hidden field. See my code example below. I am able to iterate the list and concatenate the variable however, when I go to assign the contents of the variable to the hidden input value, there is nothing there. What is the proper method of doing this?
{% set hdnfiles = '' %}
{% if tr.files is not none and tr.files|length > 0 %}
{% for file in tr.files %}
{% if hdnfiles|length > 0 %}
{% set hdnfiles = hdnfiles ~ ";" ~ file %}
{% else %}
{% set hdnfiles = file %}
{% endif %}
{% endfor %}
{% endif %}
<input type="hidden" id="filesHidden" name="filesHidden" value="{{ hdnfiles }}"/>
set will not override the value on an outer scope. Scoping in Jinja works differently than in Python; control structures (such as if and for) have a different scope than their surrounding code (where the initial set was done). Way back in 2011, Jinja's author stated:
I will keep it in mind for the new compiler backend, but in the current one that is not possible to achieve for performance reasons.
Consider building this string in Python before rendering the template, rather that building it in the template.
I've got an Ansible inventory file a bit like this:
[es-masters]
host1.my-network.com
[es-slaves]
host2.my-network.com
host3.my-network.com
[es:children]
es-masters
es-slaves
I also have a Jinja2 template file that needs a certain value set to "true" if a host belongs to the "es-masters" group.
I'm sure that there's a simple way of doing it but after some Googling and reading the documentation, I've drawn a blank.
I'm looking for something simple and programmatic like this to go in the Jinja2 template:
{% if hostvars[host][group] == "es-masters" %}
node_master=true
{% else %}
node_master=false
{% endif %}
Any ideas?
You do it the other way around.
You check if the identifier (hostname or IP or whatever is in your inventory) is in the defined group. Not if the group is in the hostvars.
{% if ansible_fqdn in groups['es-masters'] %}
node_master=true
{% else %}
node_master=false
{% endif %}
But, what you better should do is this:
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
If your inventory does not identify hosts with ansible_fqdn, ansible_hostname, etc., you can also use group_names to check if the current host has "es-masters" as one of its groups.
{% if 'es-masters' in group_names %}
node_master=true
{% else %}
node_master=false
{% endif %}
See https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#accessing-information-about-other-hosts-with-magic-variables
To avoid error with non existing group you should check first if the group exists:
{% if 'es-masters' in group_names and ansible_fqdn in groups['es-masters'] %}
node_master=true
{% else %}
node_master=false
{% endif %}
{% if ansible_fqdn in groups['es-masters'] %}
{% set node_master=true %}
{% else %}
{% set node_master=false %}
{% endif %}
maybe like this? change the var which named node_master, rather than use a txt
I would like to make a sequence of variables within a for loop such as name0, name1, .... How do I do that? Thanks.
{% for i in '1234567890' %}
{% if name{{forloop.counter0}} %}
...
{% endif %}
...
{{name{{forloop.counter0}}}}
...
{% endfor %}
is as simple as
{{ name }}{{ forloop.counter0 }}
for the if, you should use the "with" statement:
{% with name|add:forloop.counter0 as if_test %}
{% if if_test %}
... <!-- do whatever you need to do here -->
all this must be inside your for loop
As you can see, the Django templating language tries hard to keep you from doing what you're trying to do, encouraging you to do your data processing in your view code, instead of your templates. For your example, in your view code, you might try doing:
context['names'] = [name for name in names[:10]]
...instead of creating individual variables for each name.
Then in your template:
{% for name in names %}
{% if name %}
...
{% endif %}
...
{{name}}
...
{% endfor %}
As far as I can tell, that would have the same effect as your code, but you would be doing your aggregation of the names in the view, instead of the template. If I'm reading the intent of your code wrongly, please provide more context, but it doesn't seem like you're doing anything that requires template logic.