Overview

Debug Ansible Playbooks Like A Pro

No Comments

With version 2.1 Ansible introduced two features that make it possible to add proper debug logging to your playbooks, and also check and troubleshoot them on the fly, while they are executed.

The Debug Strategy

Strategies change the way Ansible executes your tasks. The default strategy is linear. That is the one you are used to, and that executes all tasks one after another, always waiting until each host finished the current task before continuing with the next one. There is also a free strategy that executes the tasks on each host as fast as it can, without waiting for the other hosts to catch up.

If you set the strategy to debug, the Ansible playbook debugger will kick in whenever a task has failed, and give you the chance to view and change parameters in-flight and re-run just the failed task. Everyone who ever executed a lengthy playbook with lots of registered variables over and over again, just to debug a small task at the end of it, will immediately see the benefit of the debug strategy.

To activate a strategy you can add it to your playbook:

- hosts: localhost
  strategy: debug
    tasks:
      - ...

or, less invasively, change the strategy temporarily with an environment variable:

$ export ANSIBLE_STRATEGY=debug

and unset it when you are done debugging. This avoids adding a line to your playbook that you need to remove anyway before you commit your fix.

Now, when a task fails Ansible will spawn the debugger and present you with a prompt. Let us assume you want to write a file:

- name: Write my file
  copy:
    dest: /tmp/my
    content: |
      Hello World!
      This is my file.

and that fails for some reason. When using the debug strategy, Ansible will invoke its debugger that you can use to dissect the current variable environment of the task and change values or task parameters:

TASK [Write my file] **********************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "can not use content with a dir as dest"}
Debugger invoked
(debug) task.args
{u'dest': u'/tmp/my', u'content': u'Hello World!\nThis is my file.\n'}

The error message tells me that I tried to write to a destination that is a directory and not a file. My bad, the correct dest parameter value should have been /tmp/my/myfile. Now lets change that parameter on the fly and re-run the task:

(debug) task.args['dest']='/tmp/my/myfile'
(debug) task.args
{u'dest': '/tmp/my/myfile', u'content': u'Hello World!\nThis is my file.\n'}
(debug) redo
changed: [localhost]

The debugger not only lets you change variables and values you can also add and delete them:

(debug) task.args['owner']='myuser'
(debug) task.args
{u'dest': '/tmp/my/myfile', u'content': u'Hello World!\nThis is my file.\n', 'owner': 'myuser'}
(debug) del(task.args['owner'])
(debug) task.args
{u'dest': '/tmp/my/myfile', u'content': u'Hello World!\nThis is my file.\n'}

You can find a full list of the debugger commands here.

The Debug Module

The debug module is well known. It is the Ansible equivalent to a print line in Python code that you add now and then to see some variable values when debugging your scripts. But debug lines are ugly and you want to remove them, or replace them with proper logging, before releasing your code. With version 2.1 Ansible extended the debug module with a verbosity parameter, that transforms it from an ugly print line, that clutters your logs, with debug information on every call, into a more useful logging mechanism with an explicit debug level.

When adding a debug task and setting the the verbosity level to 2:

- name: Write file
  copy:
    dest: /tmp/my
    content: |
      Hello World!
      This is my file.
    register: write_file_result
- name: Debug write_file_result
    debug:
      var: write_file_result
      verbosity: 2

it will only be executed if you specify this verbosity (-vv) on the command line:

ansible-playbook debug-demo.yml -vv
 
[...]
 
TASK [Debug write_file_result] **********************************************
ok: [localhost] => {
    "write_file_result": {
        "changed": true,
        "checksum": "0ded82d0b36ca79392d9bd97debb6fbbb7bb93ef",
        "dest": "/tmp/my/myfile",
        "gid": 20,
        "group": "staff",
        "md5sum": "f52e35e56cd4087de0948890215f7de0",
        "mode": "0644",
        "owner": "myuser",
        "size": 30,
        "src": "/Users/myuser/.ansible/tmp/ansible-tmp-1498165508.64-230947914847899/source",
        "state": "file",
        "uid": 501
    }
}

When using verbosity level 1 (-v) Ansible will simply skip your debug task:

ansible-playbook debug-demo.yml -v
 
[...]
 
TASK [Debug write_file_result] ********************************************************************
skipping: [localhost] => {"changed": false, "skipped": true, "skipped_reason": "Verbosity threshold not met."}

Adding debug tasks not only on demand, but as a fixed part of your role will give you the ability to more quickly troubleshoot and debug. Some good points to insert debug logging are:

  1. After any task that registers a variable, or
  2. at the beginning of any included file that is parameterized or looped over.

Another good use case is to add well placed debug tasks and the verbosity level of your choice to your Jenkins build jobs, and have debugging information right in your logs.

Daniel Marks is creating and operating complex distributed software systems and infrastructures. In his current role as a Senior Cloud Engineer he builds OpenStack based clouds for codecentric and its customers.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Comment

Your email address will not be published. Required fields are marked *