# Playing with Ansible mutiple callbacks

**URL:** <https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195>\
**Category:** Ansible Project\
**Created:** [January 11, 2013, 4:44pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195 "2013-01-11T16:44:50Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tom\_Laredo](https://avatars.discourse-cdn.com/v4/letter/t/ecc23a/32.png) [@Tom\_Laredo](https://forum.ansible.com/u/Tom_Laredo)\
**Post date:** [January 11, 2013, 4:44pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/1 "2013-01-11T16:44:50Z")

</div>

Hi,

As I began to introduce my projet in another post, I’m currently experimenting a blocking issue.  
The thing that I want to do is pretty simple and I think that I am verry close to the solution but I clearly need your help 🙂

Here is the flow I want to create :  
A playbook executes a module (that I quickly wrote) which runs the sell command apt-show-versions, parse the result and then return it into JSON format.  
At the end, a callback is responsible for processing the result and then puting it into a database.

Info: this (great !) web page helped me to do it : [http://jpmens.net/2012/09/11/watching-ansible-at-work-callbacks/](http://jpmens.net/2012/09/11/watching-ansible-at-work-callbacks/)  
It almost works but I’m still have an issue (see under).

So here are the files:

ANSIBLE PLAYBOOK:

> * * *
> 
> - hosts: webservers  
> user: root  
> vars:  
> name: dpkg Tom  
> tasks:
> - name: Verify that apt-show-versions is installed and install it if it is not present  
> action: apt pkg=apt-show-versions state=present
> - name: Launch a custom dpkg-show-versions module  
> action: dpkg\_tom

ANSIBLE MODULE (in bash language because I don’t know how to do it in Python):

> # DEFINITION DE LA FONCTION PRINCIPALE
> 
> main()  
> {
> 
> # parseArguments $1
> 
> echo -n “{"changed": "False", "name": "dpkg\_tom", "packages": [”
> 
> nombre\_lignes=$(apt-show-versions --upgradeable | wc -l)  
> compteur=1
> 
> apt-show-versions --upgradeable | while read line  
> do  
> echo “$line” | sed ‘s@^(._?)/(._?) (._?) from (._?) to (.\*)@{"\1":{"distribution": "\2","status": "\3","installed\_version": "\4","available\_version": "\5"@’;
> 
> echo -ne “}}”
> 
> if [$compteur -lt $nombre\_lignes]  
> then  
> echo -ne “,”  
> fi
> 
> compteur=$[$compteur + 1]  
> done
> 
> # echo -ne “"":{”;
> 
> echo -n “]}”  
> }
> 
> ######################################################
> 
> main

Info : this module works just fine (tested without the callback).

ANSIBLE CALLBACK:

> import os  
> import time  
> import sqlite3
> 
> dbname = ‘/etc/ansible/cmdb.db’  
> TIME\_FORMAT=‘%Y-%m-%d %H:%M:%S’
> 
> try:  
> con = sqlite3.connect(dbname)  
> cur = con.cursor()  
> except:  
> pass
> 
> def log(host, data):
> 
> if type(data) == dict:  
> invocation = data.pop(‘invocation’, None)  
> if invocation.get(‘module\_name’, None) != ‘setup’:  
> return
> 
> facts = data.get(‘ansible\_facts’, None)
> 
> now = time.strftime(TIME\_FORMAT, time.localtime())
> 
> try:
> 
> # `host` is a unique index
> 
> cur.execute(“REPLACE INTO inventory (now, host, arch, dist, distvers, sys,kernel) VALUES(?,?,?,?,?,?,?);”,  
> (  
> now,  
> facts.get(‘ansible\_hostname’, None),  
> facts.get(‘ansible\_architecture’, None),  
> facts.get(‘ansible\_distribution’, None),  
> facts.get(‘ansible\_distribution\_version’, None),  
> facts.get(‘ansible\_system’, None),  
> facts.get(‘ansible\_kernel’, None),  
> ))  
> con.commit()  
> except:  
> pass
> 
> class CallbackModule(object):  
> def runner\_on\_ok(self, host, res):  
> log(host, res)

The problem that I have is the following Python error (error raised during callback execution):

> fatal: [127.0.0.1] =\> Traceback (most recent call last):  
> File “/usr/lib/pymodules/python2.6/ansible/runner/ **init**.py”, line 236, in \_executor  
> exec\_rc = self.\_executor\_internal(host)  
> File “/usr/lib/pymodules/python2.6/ansible/runner/ **init**.py”, line 292, in \_executor\_internal  
> return self.\_executor\_internal\_inner(host, self.module\_name, self.module\_args, inject, port)  
> File “/usr/lib/pymodules/python2.6/ansible/runner/ **init**.py”, line 435, in \_executor\_internal\_inner  
> self.callbacks.on\_ok(host, data)  
> File “/usr/lib/pymodules/python2.6/ansible/callbacks.py”, line 363, in on\_ok  
> super(PlaybookRunnerCallbacks, self).on\_ok(host, host\_result)  
> File “/usr/lib/pymodules/python2.6/ansible/callbacks.py”, line 191, in on\_ok  
> call\_callback\_module(‘runner\_on\_ok’, host, res)  
> File “/usr/lib/pymodules/python2.6/ansible/callbacks.py”, line 50, in call\_callback\_module  
> method(\*args, \*\*kwargs)  
> File “/usr/lib/pymodules/python2.6/ansible/callback\_plugins/inventory.py”, line 43, in runner\_on\_ok  
> log(host, res)  
> File “/usr/lib/pymodules/python2.6/ansible/callback\_plugins/inventory.py”, line 18, in log  
> if invocation.get(‘module\_name’, None) != ‘setup’:  
> AttributeError: ‘NoneType’ object has no attribute ‘get’
> 
> FATAL: all hosts have already failed – aborting

Then, I think that this issue is raised because of the lack of module\_name in the result that my module give.  
Have I to declare my custom module somewhere to make ansible know the module\_name of my module?  
Have I to put a module\_name attribute in my JSON result?  
I know that I have a verry poor level in Python and that I’m a beginner in playing with Ansible features so, here I am 🙂

Thank you for you awsome project anyway!  
Tom

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan](https://avatars.discourse-cdn.com/v4/letter/m/c4cdca/32.png) [@Michael\_DeHaan](https://forum.ansible.com/u/Michael_DeHaan)\
**Post date:** [January 11, 2013, 5:05pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/2 "2013-01-11T17:05:18Z")

</div>

Nope, it's saying there is no 'invocation' hash at all in what you  
have run. This is probably a minor quirk that should not be the  
case, but see example here:

[https://github.com/ansible/ansible/blob/devel/plugins/callbacks/log\_plays.py](https://github.com/ansible/ansible/blob/devel/plugins/callbacks/log_plays.py)

I suspect adding the if clause about 'verbose\_override' will fix you up.

---

<div class="post-metadata">

**Author:** ![Tom\_Laredo](https://avatars.discourse-cdn.com/v4/letter/t/ecc23a/32.png) [@Tom\_Laredo](https://forum.ansible.com/u/Tom_Laredo)\
**Post date:** [January 11, 2013, 5:18pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/3 "2013-01-11T17:18:28Z")

</div>

Thanks for your verry impressive reactivity which is really appreciated!  
I see the condition that you’re talking about, but I’m not sure to understand.  
Could you please tell me what you mean by “invocation hash”?  
Have I to declare something in my module?  
The “setup” module (wrote in Python) don’t causes any problem with the callback I gave below so how can I say "this callback has to work ONLY IF the name is ‘setup’ " if my module don’t have a proper name?

Thank you for your help 🙂  
Tom

---

<div class="post-metadata">

**Author:** ![Dusan\_Sovic](https://avatars.discourse-cdn.com/v4/letter/d/4491bb/32.png) [@Dusan\_Sovic](https://forum.ansible.com/u/Dusan_Sovic)\
**Post date:** [January 11, 2013, 5:37pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/4 "2013-01-11T17:37:00Z")

</div>

Hi Tom,

try something like this in your callback code (just put if invocation is None: and return…):

if type(data) == dict:  
invocation = data.pop(‘invocation’, None)  
if invocation is None:  
return  
if invocation.get(‘module\_name’, None) != ‘setup’:  
return

hope it helps you,  
Dusan

Dňa piatok, 11. januára 2013 18:18:28 UTC+1 grobs napísal(-a):

---

<div class="post-metadata">

**Author:** ![Tom\_Laredo](https://avatars.discourse-cdn.com/v4/letter/t/ecc23a/32.png) [@Tom\_Laredo](https://forum.ansible.com/u/Tom_Laredo)\
**Post date:** [January 14, 2013, 10:06am UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/5 "2013-01-14T10:06:05Z")

</div>

Thank you verry much! It works properly 🙂

Now, how can I do if I want to write another callback which is executed when my module (written in bash) return it’s result?  
Because if my module dos’nt have any name, I don’t see how I can link it to a callback in particular.

Tom

---

<div class="post-metadata">

**Author:** ![Tom\_Laredo](https://avatars.discourse-cdn.com/v4/letter/t/ecc23a/32.png) [@Tom\_Laredo](https://forum.ansible.com/u/Tom_Laredo)\
**Post date:** [January 15, 2013, 4:58pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/6 "2013-01-15T16:58:15Z")

</div>

For information, I just rewritten the module in perl to make it just a bit better.  
Here is the source code:  
8\<---------------------------------------------------------------------------------------------------------------------------

> #!/usr/bin/perl
> 
> use strict;  
> use warnings;
> 
> sub main  
> {  
> print “{"changed": "False", "packages": [”;
> 
> my $nbrLignes=`apt-show-versions | wc -l`;  
> my $compteur=0;
> 
> for(`apt-show-versions`) {  
> $compteur++;
> 
> # Formats attendus :
> 
> # zlib1g/squeeze uptodate 1:1.2.3.4.dfsg-3
> 
> # libnss3-1d/squeeze upgradeable from 3.12.8-1+squeeze5 to 3.12.8-1+squeeze6
> 
> # ansible-provisioning 0.9 installed: No available version in archive
> 
> next if not $\_ =~ /  
> ^  
> ([a-z0-9\_-]_?) # package  
> / # slash  
> (._?) # distribution  
> \s #  
> (._?) # status  
> \s #  
> (?: # groupe non capturant  
> from # from  
> \s #  
> (._?) # installed\_version  
> \s #  
> to # to  
> \s #  
> )? # fin de groupe non capturant  
> (.+$) # available\_version  
> /ix; # case Insensitive and eXtended spacing
> 
> my $package = $1;  
> my $distribution = $2;  
> my $status = $3;  
> my $installed\_version = ‘’;  
> my $available\_version = ‘’;
> 
> if(defined $4) {  
> $installed\_version = $4;  
> $available\_version = $5;  
> }  
> else {  
> $installed\_version = $5;  
> }
> 
> print “{"$package":{"distribution": "$distribution","status": "$status","installed\_version": "$installed\_version","available\_version": "$available\_version"}}”;
> 
> if($compteur \< $nbrLignes) {  
> print “,”  
> }  
> }
> 
> print “]}”;  
> }
> 
> ######################################################
> 
> main;

8\<---------------------------------------------------------------------------------------------------------------------------

This module works fine in my playbook but I don’t understand how I could give it a name to identify the module in my callback and then use the result of my module.

Any idea?

Tom

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan](https://avatars.discourse-cdn.com/v4/letter/m/c4cdca/32.png) [@Michael\_DeHaan](https://forum.ansible.com/u/Michael_DeHaan)\
**Post date:** [January 15, 2013, 5:21pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/7 "2013-01-15T17:21:43Z")

</div>

Callback modules must be written in Python, so I'm fairly confused here.

Regular ansible modules which are executed on remote hosts are  
completely different from callbacks.

---

<div class="post-metadata">

**Author:** ![Tom\_Laredo](https://avatars.discourse-cdn.com/v4/letter/t/ecc23a/32.png) [@Tom\_Laredo](https://forum.ansible.com/u/Tom_Laredo)\
**Post date:** [January 15, 2013, 5:40pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/8 "2013-01-15T17:40:58Z")

</div>

Yes, I’m ok with that I think, sorry if I’m not clear in what I say.

The source code that I gave before is the module, not the callback.  
The module read the result of apt-show-versions and then return JSON formatted result.  
I also have a callback (which is written in Python) which take the result and then put it in a database.  
As I have several modules and several callback associated, I want to make my Python callback work ony if this or this module was launched.  
To do this, I have those lines in my Python Callback to identify the module which was ran:  
8\<---------------------------------------------------------------------------------------------------------------------------

> if type(data) == dict:  
> invocation = data.pop(‘invocation’, None)  
> if invocation is None:  
> return  
> if invocation.get(‘module\_name’, None) != ‘the\_name\_of\_my\_module’:  
> return

8\<---------------------------------------------------------------------------------------------------------------------------

But as my module is written in Perl (or bash or whatever is not Python), I don’t know how to give a name to my module to verify in my callback that the modulethat was ran really is the one I want (“the\_name\_of\_my\_module” here).

Hope that my question is more understandable.

Tom

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan](https://avatars.discourse-cdn.com/v4/letter/m/c4cdca/32.png) [@Michael\_DeHaan](https://forum.ansible.com/u/Michael_DeHaan)\
**Post date:** [January 15, 2013, 6:14pm UTC](https://forum.ansible.com/t/playing-with-ansible-mutiple-callbacks/12195/9 "2013-01-15T18:14:44Z")

</div>

> Yes, I'm ok with that I think, sorry if I'm not clear in what I say.
> 
> The source code that I gave before is the module, not the callback.  
> The module read the result of apt-show-versions and then return JSON  
> formatted result.  
> I also have a callback (which is written in Python) which take the result  
> and then put it in a database.  
> As I have several modules and several callback associated, I want to make my  
> Python callback work ony if this or this module was launched.  
> To do this, I have those lines in my Python Callback to identify the module  
> which was ran:  
> 8\<---------------------------------------------------------------------------------------------------------------------------
> 
> > if type(data) == dict:  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;invocation = data.pop('invocation', None)  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if invocation is None:  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if invocation.get('module\_name', None) != 'the\_name\_of\_my\_module':  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return
> 
> 8\<---------------------------------------------------------------------------------------------------------------------------
> 
> But as my module is written in Perl (or bash or whatever is not Python), I  
> don't know how to give a name to my module to verify in my callback that the  
> modulethat was ran really is the one I want ("the\_name\_of\_my\_module" here).
> 
> Hope that my question is more understandable.

I see.

Looks like if your module is named 'foo', it doesn't matter what  
language it is, it will still appear as foo.

line 423 here

[https://github.com/ansible/ansible/blob/devel/lib/ansible/runner/\_\_init\_\_.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/runner/ __init__.py)
