4

Debug for the winners !

 2 years ago
source link: https://arrfab.net/posts/2013/Oct/31/debug-for-the-winners/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Debug for the winners !

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" :-)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK