Hi all,
I am starting to migrate my playbooks from Ansible 1.1 to Ansible 1.2
and I am getting a few problems.
To distinguish between CentOS and Ubuntu distributions, I used to set
two variables, "is_centos" and "is_ubuntu" in the following way:
is_ubuntu: "'${ansible_distribution}' == 'Ubuntu'"
is_centos: "'${ansible_distribution}' == 'CentOS'"
and then use the "only_if" statement in my tasks, like:
- action: debug msg="This is a CentOS"
only_if: $is_centos
- action: debug msg="This is an Ubuntu machine"
only_if: $is_ubuntu
and this worked as expected. Now I would like to use "when" instead of
"only_if", which is deprecated, but when I try the following:
- action: debug msg="This is a CentOS"
when: is_centos
- action: debug msg="This is an Ubuntu machine"
when: is_ubuntu
it always execute both tasks.
I've also tried to convert the "vars" section to a jinja2-like declaration:
is_ubuntu: "{{ ansible_distribution == 'Ubuntu' }}"
is_centos: "{{ ansible_distribution == 'CentOS' }}"
but still it does not work as expected.
This is the playbook I'm testing (on a Ubuntu machine):
I think the problem is related to the fact that the value of
"is_ubuntu" becomes a *string* instead of a boolean. If I use as a
conditional the following statement:
when: is_ubuntu == 'True'
it works. Also, if I set the variable explicitly as a YAML boolean:
vars:
is_ubuntu: true
is_centos: false
it works, but I don't know how to set these variables with YAML
booleans instead of strings using an expression...
Also this does not work:
- action: set_fact is_ubuntu={{ ansible_distribution == 'Ubuntu' }}
- action: set_fact is_centos={{ ansible_distribution == 'CentOS' }}
because "is_ubuntu" and "is_centos" still are evaluated as strings.
.a.
I don't know if this is related or not, but I've also found that "{{
variable }}" is treated differently from "${variable}".
In one of my playbooks I have something like:
- include: common/tasks/iptables.yml trusted_hosts=${groups.slurm_clients}
the iptables.yml file contains:
- action: template dest={{ destfile }}
src=common/templates/etc/iptables.d/rules.v4.j2 owner=root group=root
mode=0644
tags: iptables
and the template contains:
{% for host in trusted_hosts|sort %}
-A INPUT -s {{ host }} -j ACCEPT
{% endfor %}
the resulting output is:
-A INPUT -s compute001 -j ACCEPT
-A INPUT -s compute002 -j ACCEPT
but if I use {{groups.all}} instead of ${groups.all} the resulting output is:
-A INPUT -s [ -j ACCEPT
-A INPUT -s ' -j ACCEPT
-A INPUT -s c -j ACCEPT
-A INPUT -s o -j ACCEPT
-A INPUT -s m -j ACCEPT
-A INPUT -s p -j ACCEPT
-A INPUT -s u -j ACCEPT
-A INPUT -s t -j ACCEPT
-A INPUT -s e -j ACCEPT
-A INPUT -s 0 -j ACCEPT
-A INPUT -s 0 -j ACCEPT
-A INPUT -s 2 -j ACCEPT
-A INPUT -s ' -j ACCEPT
-A INPUT -s , -j ACCEPT
-A INPUT -s -j ACCEPT
-A INPUT -s ' -j ACCEPT
-A INPUT -s c -j ACCEPT
-A INPUT -s o -j ACCEPT
-A INPUT -s m -j ACCEPT
-A INPUT -s p -j ACCEPT
-A INPUT -s u -j ACCEPT
-A INPUT -s t -j ACCEPT
-A INPUT -s e -j ACCEPT
-A INPUT -s 0 -j ACCEPT
-A INPUT -s 0 -j ACCEPT
-A INPUT -s 1 -j ACCEPT
-A INPUT -s ' -j ACCEPT
-A INPUT -s ] -j ACCEPT
exactly like the "trusted_hosts" variable is treated as the string:
"['compute001', 'compute002']" instead of a list of strings.
.a.
There’s already a ticket on this, I’ll have to find it.
I strongly recommend looking into the “group_by” module for mapping configurations based on OS’es though, as the output is much nicer to not see all the ‘skipped’ steps.
groups.all is a list, you should walk over it in a loop.
There’s already a ticket on this, I’ll have to find it.
Thank you!
I strongly recommend looking into the “group_by” module for mapping configurations based on OS’es though, as the output is much nicer to not see all the ‘skipped’ steps.
I see your point, but I don’t really care about the output, and I want to migrate the syntax without changing too much. If I would have to use “group_by” for this kind of things I will have to re-structure all my playbooks
groups.all is a list, you should walk over it in a loop.
I am not sure I understand your answer. I am looping, but inside a template. I want to pass a list of hosts to a template, so that it will produce a line for each item of the list, this is something I can’t do using “with_items”.
The problem is that when I use {{ groups.all }}, in the template I get a string instead of a list, so iterating over it means iterating over all the characters of the string representation of the list…
.a.
{% for x in groups[‘all’] %}
{{ x }}
{% endfor %}
{% for x in groups['all'] %}
{{ x }}
{% endfor %}
I don't want it hardcoded in the template!
As I wrote before, this is what I want to have:
- include: common/tasks/iptables.yml trusted_hosts=${groups.slurm_clients}
where "trusted_hosts" might change (in this example is
"slurm_clients", but could be just a list of hosts).
the iptables.yml file contains:
- action: template dest={{ destfile }}
src=common/templates/etc/iptables.d/rules.v4.j2 owner=root group=root
mode=0644
tags: iptables
and the template contains:
{% for host in trusted_hosts|sort %}
-A INPUT -s {{ host }} -j ACCEPT
{% endfor %}
If I write "trusted_hosts={{groups.slurm_clients}}" the template will
receive a *string*, while if I write
"trusted_hosts=${groups.slurm_clients}" it receives a list, as
expected. Is this a bug or is the expected behavior? Is there a
difference between "${variable}" and "{{variable}}" or there should
not?
.a.
So we were intending to deprecate ${foo} but effectively it still works nicely for pass by reference, so I suspect it will be repurposed for such. In your case, it’s a matter of passing it in a dictionary rather than a string, since it can’t tell what you want the way it’s dereferenced above.
However this works today:
- include: common/tasks/main/iptables.yml
vars:
trusted_hosts: ${groups.all}
Though I really want to unify this syntax so it plays more nicely with roles, also, i.e.
- include: …
trusted_hosts: ${groups.all}
Aka shorthand
- { include: filename.yml, trusted_hosts: ${groups.all} }
The two latter forms are not implemented, but the first works for me today.
Hope this helps!
Thank you, this clarifies it. I think that for now I will continue to
use '${}' then, since it's not going to be deprecated.
.a.
It may be at some point, but in the case of passing a variable reference, it’s currently useful.