Recently I had to dive back into Ansible playbooks I wrote (quite) some time ago. I had to add some logic to generate different application templates based on facts/packages being installed on the managed nodes. Long story short (I'll not describe the use case here as it's quite complex), I decided that injecting directly some kind of logic in the Jinja2 templates was enough .. but not.
Let's take a very simplified example here (don't even look at the tasks but rather at the logic explained how to get there, once again this is a 'stupid' playbook) :
---
- hosts: localhost
connection: local
user: root
vars:
- myrole: httpserver
tasks:
- name: registering a variable only if myrole is httpserver
command: /bin/rpm -q --qf '%{version}' httpd
register: httpd_version
when: myrole == 'httpserver'
- name: pushing the generated template
template: src=../templates/logic.txt.j2 dest=/tmp/logic.txt
handlers:
Now let's have a look at the (very) simple logic.txt.j2 :
{% if httpd_version is defined -%}
You're using an Apache http server version : {{ httpd_version.stdout> }}
{% else %}
You're not using an http server, or not defined in the ansible> machine role
{% endif -%}
Easy, and it seems it was working when myrole was indeed httpserver :
cat /tmp/logic.txt
You're using an Apache http server version : 2.2.15
But things didn't work as expected when myrole was something else, like for example dbserver
TASK: [registering a variable only if myrole is httpserver]
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
skipping: [localhost]
TASK: [pushing the generated template]
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
fatal: [localhost] =\> {'msg': "One or more undefined variables:
'dict' object has no attribute 'stdout'", 'failed': True}
hmm, as the register: task was skipped, I was wondering why it then complained about the httpd_version.stdout as I thought that httpd_version wasn't defined .. but I was wrong : even if 'skipped' the variable exists for that host. I quickly discovered it when adding a debug task in between the other tasks in my playbook :
- debug: msg="this is http_version value {{ httpd_version }}"
Now let's see what can be wrong :
TASK: [debug msg="this is http_version value {{httpd_version}}"]
\*\*\*\*\*\*\*\*\*\*\*\*\*\*
ok: [localhost] =\> {"msg": "this is http_version value {u'skipped': True, u'changed': False}"}
Very interesting : so even when skipped, the variable httpd_version is still "registered" by the register: feature but marked as skipped.
Let's so change our "logic" in the Jinja2 template then ! :
{% if httpd_version.skipped -%}
You're not using an http server, or not defined in the ansible machine role
{% else %}
You're using an Apache http server version : {{ httpd_version.stdout }}
{% endif -%}
And now it works in all cases ..
It's a (very,very,very) simplified example, but you get the idea and using the debug module (don't forget to call ansible-playbook with -vvv to see those messages too !) can quickly show you where your issue is when having to troubleshoot something. As Patrick Debois was saying : "you gotta love Ansible for its simplicity" :-)