Short version of if var is defined for ansible templates - ansible

Jinja2 in Ansible templates allows this type of expression in templates:
{% if foobar is defined %} foo_bar = {{foobar}} {% endif %}
{% if barfoo is defined %} bar_foo = {{barfoo}} {% endif %}
etc.
Is there any shorter version to say 'do not print this line if its variable is not defined?
Something like foo_bar = {{foobar|skip_this_line_if_undefined}}?

You can use the default(omit) filter. For details have a look at the documentation.

You could use a macro.
{% macro line(key, value) -%}
{% if not value|none %}{{ key }} = {{ value }}{% endif %}
{%- endmacro %}
Then just call the macro for every key/value pair.
{{ line('foo_bar', foobar) }}
{{ line('bar_foo', barfoo) }}
Could be problematic in edge cases though. If foobar or barfoo are not defined it probably will raise an error. In the macro, value in any case would be defined, so the condition is defined doesn't make sense any more. But if null/none actually is a valid value for any of the variables, you hit the wall...
A bit longer but probably water proof:
{% macro line(key, value) -%}
{% if value != omit %}{{ key }} = {{ value }}{% endif %}
{%- endmacro %}
{{ line('foo_bar', foobar|default(omit)) }}
{{ line('bar_foo', barfoo|default(omit)) }}

Related

Ansible template mix up order of elements

I have a problem with may came up with a new Ansible version, as it worked before:
I'm passing this block to the ansible template
- monitoring-test-blackbox_exporter:
source: "{{ consul_template_template_dir }}/blackbox_exporter.ctmpl"
destination: "/etc/prometheus/file_sd/blackbox_exporter.json"
create_dest_dirs: true
command_timeout: "60s"
error_on_missing_key: false
grafana_link: "xtkCtBkiz"
This is the template:
# Template configuration
{% for ctmpl in consul_template_templates_config_node %}
# {{ ctmpl | first }}
template {
{% for option, value in ctmpl.items() %}
{% if value is sameas true %}
{{ option }} = true
{% elif value is sameas false %}
{{ option }} = false
{% elif value is string %}
{{ option }} = "{{ value|string }}"
{% elif value is number %}
{{ option }} = {{ value|int }}
{% endif %}
{% endfor %}
ctmpl | first always worked before to filter out first element monitoring-test-blackbox_exporter this is important as we use it later in the template configuration.
I tried several things with sort and select attributes neither of them worked. Does anyone have an idea to get it working again?

Using Twig to generate Markdown, how to indent blocks in twig?

I have very few hours of use with Twig so I probably missed an important tip; please forgive me if this is a trivial question.
I'm using Twig with PHP for the generation of markdown files.
My twig file contains one or more {% for %}...{% endfor %} block and inside a for-loop, a few {% if %}...{% endif %} and ... I can't make any indentation in my Twig otherwise the spaces are also present in my output.
A very stupid example: https://twigfiddle.com/fb6nzq (use the Show raw result to make sure to see the spaces before the word true).
If I don't indent my {% if %}...{% endif %}, I got the correct result (https://twigfiddle.com/fb6nzq/2) but I don't have anymore indentation of blocks in my template.
In my real world twig file, I can have multiple {% endif %} like below and it becomes unreadable.
{% for (variable) %}
{% if (condition) %}
{% if (condition) %}
{% if (condition) %}
{% endif %}
{% endif %}
{% endif %}
{% endfor%}
So ... do you know if there is a wonderful trick to keep an indentation in your code but without having an impact on the output?
Desired twig template:
{% for (variable) %}
{% if (condition) %}
{% if (condition) %}
{% if (condition) %}
{% endif %}
{% endif %}
{% endif %}
{% endfor%}
You can use a dash - on any opening or closing twig expression where:
a dash on the closing expression would do a trim on the left.
{% if true -%}
a dash on the opening expression would do a trim on the right
{%- if true %}
a dash on the both the opening and closing expression would do a trim on the both sides
{%- if true -%}
Mind that: this is acting as a PHP trim, so that means that it will also trim your line feeds!
Here is an example:
{% for i in 1..5 %}
{% if true %}
{% if true %}
{%- if true %}
foo
{%- endif %}
{%- endif %}
{% endif %}
{% endfor %}
That renders:
foo
foo
foo
foo
foo
This is testable here: https://twigfiddle.com/1awhzk
Also note: that there is a spaceless tag to achieve those kind of things.

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 %}

assignment of undefined variables in Jinja2

I got this piece of YAML and i want jinja2 to assign and create item.menu, if it is not previously defined.
data:
- name: bar
menu: blah
- name: foo
This is my code, the error-output ist "template error while templating string: expected token 'end of statement block'"
{% for item in data %}
{% if item.menu is not defined %}
{% set item.menu=item.name %}
{% endif %}
{% endfor %}
Any Help about what I did wrong would be much apretiated :)
Greetings, Hendrik
You're question is not clear but here is my answer. I'll make everything explicit :
{% for item in data %}
{% if item.menu is not defined %}
{{ item.nameĀ }}
{% else %}
{{ item.menu }}
{% endif %}
{% endfor %}

Variable in Jinja2 For Loop

We're trying to come up with a way to use ansible facts within jinja2 For Loops.
For example, I want to get all servers that belong to my memcached group as well as a group based on release (something like tag_release_dev or tag_release_prod). When I try to use {{ tt_release }} within the For Loop it evaluates {{ tt_release }} rather than the value of the variable. Is there a way to use a variable within the loop definition?
{% for host in groups["tag_function_mem"] | intersect(groups["tag_release_{{ tt_release }}"]) %}
{{ host }}:11211
{%- if not loop.last %},{% endif %}
{%- if loop.last %}"{% endif %}
{% endfor %}
{% endif %}
it evaluates {{ tt_release }} rather than the value of the variable.
This is because you already are inside a expression. You can not nest expressions - and you don't need to.
What you want is to concatenate the string "tag_release_" and the variable tt_release. In Jinja2 concatenation is done with a +.
{% for host in groups["tag_function_mem"] | intersect(groups["tag_release_" + tt_release]) %}

Resources