1+ import getpass
12import os .path
23import time
34
45import datadog
56import yaml
67
8+ try :
9+ # Ansible v2
10+ from ansible .plugins .callback import CallbackBase
11+ from __main__ import cli
12+ except ImportError :
13+ # Ansible v1
14+ CallbackBase = object
15+ cli = None
716
8- class CallbackModule (object ):
17+
18+ class CallbackModule (CallbackBase ):
919 def __init__ (self ):
1020 # Read config and set up API client
1121 api_key , url = self ._load_conf (os .path .join (os .path .dirname (__file__ ), "datadog_callback.yml" ))
1222 datadog .initialize (api_key = api_key , api_host = url )
1323
1424 self ._playbook_name = None
1525 self ._start_time = time .time ()
26+ self ._options = None
27+ if cli :
28+ self ._options = cli .options
29+
30+ # self.playbook is either set by Ansible (v1), or by us in the `playbook_start` callback method (v2)
31+ self .playbook = None
32+ # self.play is either set by Ansible (v1), or by us in the `playbook_on_play_start` callback method (v2)
33+ self .play = None
1634
1735 # Load parameters from conf file
1836 def _load_conf (self , file_path ):
@@ -47,7 +65,6 @@ def _send_event(self, title, alert_type=None, text=None, tags=None, host=None, e
4765
4866 # Send event, aggregated with other task-level events from the same host
4967 def send_task_event (self , title , alert_type = 'info' , text = '' , tags = None , host = None ):
50- # self.play is set by ansible
5168 if getattr (self , 'play' , None ):
5269 if tags is None :
5370 tags = []
@@ -98,6 +115,23 @@ def start_timer(self):
98115 def get_elapsed_time (self ):
99116 return time .time () - self ._start_time
100117
118+ # Handle `playbook_on_start` callback, common to Ansible v1 & v2
119+ def _handle_playbook_on_start (self , playbook_file_name , inventory ):
120+ self .start_timer ()
121+
122+ # Set the playbook name from its filename
123+ self ._playbook_name , _ = os .path .splitext (
124+ os .path .basename (playbook_file_name ))
125+ inventory_name = os .path .basename (os .path .realpath (inventory ))
126+
127+ self .send_playbook_event (
128+ 'Ansible playbook "{0}" started by "{1}" against "{2}"' .format (
129+ self ._playbook_name ,
130+ getpass .getuser (),
131+ inventory_name ),
132+ event_type = 'start' ,
133+ )
134+
101135 # Default tags sent with events and metrics
102136 @property
103137 def default_tags (self ):
@@ -121,9 +155,15 @@ def format_result(res):
121155 elif not res .get ('invocation' ):
122156 event_text = msg
123157 else :
124- event_text = "$$$\n {0}[{1}]\n $$$\n " .format (res ['invocation' ]['module_name' ], res ['invocation' ]['module_args' ])
158+ invocation = res ['invocation' ]
159+ event_text = "$$$\n {0}[{1}]\n $$$\n " .format (invocation ['module_name' ], invocation .get ('module_args' , '' ))
125160 event_text += msg
126- module_name = 'module:{0}' .format (res ['invocation' ]['module_name' ])
161+ module_name = 'module:{0}' .format (invocation ['module_name' ])
162+ if 'module_stdout' in res :
163+ # On Ansible v2, details on internal failures of modules are not reported in the `msg`,
164+ # so we have to extract the info differently
165+ event_text += "$$$\n {0}\n {1}\n $$$\n " .format (
166+ res .get ('module_stdout' , '' ), res .get ('module_stderr' , '' ))
127167
128168 return event_text , module_name
129169
@@ -159,20 +199,26 @@ def runner_on_unreachable(self, host, res):
159199 host = host ,
160200 )
161201
202+ # Implementation compatible with Ansible v1 only
162203 def playbook_on_start (self ):
163- # Retrieve the playbook name from its filename
164- self ._playbook_name , _ = os .path .splitext (
165- os .path .basename (self .playbook .filename ))
166- self .start_timer ()
167- host_list = self .playbook .inventory .host_list
168- inventory = os .path .basename (os .path .realpath (host_list ))
169- self .send_playbook_event (
170- 'Ansible playbook "{0}" started by "{1}" against "{2}"' .format (
171- self ._playbook_name ,
172- self .playbook .remote_user ,
173- inventory ),
174- event_type = 'start' ,
175- )
204+ playbook_file_name = self .playbook .filename
205+ inventory = self .playbook .inventory .host_list
206+
207+ self ._handle_playbook_on_start (playbook_file_name , inventory )
208+
209+ # Implementation compatible with Ansible v2 only
210+ def v2_playbook_on_start (self , playbook ):
211+ # On Ansible v2, Ansible doesn't set `self.playbook` automatically
212+ self .playbook = playbook
213+
214+ playbook_file_name = self .playbook ._file_name
215+ inventory = self ._options .inventory
216+
217+ self ._handle_playbook_on_start (playbook_file_name , inventory )
218+
219+ def v2_playbook_on_play_start (self , play ):
220+ # On Ansible v2, Ansible doesn't set `self.play` automatically
221+ self .play = play
176222
177223 def playbook_on_stats (self , stats ):
178224 total_tasks = 0
0 commit comments