Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

# (C) 2014-2015, Matt Martz <matt@sivel.net> 

# (C) 2017 Ansible Project 

# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 

 

# Make coding more python3-ish 

from __future__ import (absolute_import, division, print_function) 

__metaclass__ = type 

 

DOCUMENTATION = ''' 

callback: slack 

callback_type: notification 

requirements: 

- whitelist in configuration 

- prettytable (python library) 

short_description: Sends play events to a Slack channel 

version_added: "2.1" 

description: 

- This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution. 

- Before 2.4 only environment variables were available for configuring this plugin 

options: 

webhook_url: 

required: True 

description: Slack Webhook URL 

env: 

- name: SLACK_WEBHOOK_URL 

ini: 

- section: callback_slack 

key: webhook_url 

channel: 

default: "#ansible" 

description: Slack room to post in. 

env: 

- name: SLACK_CHANNEL 

ini: 

- section: callback_slack 

key: channel 

username: 

description: Username to post as. 

env: 

- name: SLACK_USERNAME 

default: ansible 

ini: 

- section: callback_slack 

key: username 

''' 

 

import json 

import os 

import uuid 

 

try: 

from __main__ import cli 

except ImportError: 

cli = None 

 

from ansible.module_utils.urls import open_url 

from ansible.plugins.callback import CallbackBase 

 

try: 

import prettytable 

HAS_PRETTYTABLE = True 

except ImportError: 

HAS_PRETTYTABLE = False 

 

 

class CallbackModule(CallbackBase): 

"""This is an ansible callback plugin that sends status 

updates to a Slack channel during playbook execution. 

""" 

CALLBACK_VERSION = 2.0 

CALLBACK_TYPE = 'notification' 

CALLBACK_NAME = 'slack' 

CALLBACK_NEEDS_WHITELIST = True 

 

def __init__(self, display=None): 

 

super(CallbackModule, self).__init__(display=display) 

 

if not HAS_PRETTYTABLE: 

self.disabled = True 

self._display.warning('The `prettytable` python module is not ' 

'installed. Disabling the Slack callback ' 

'plugin.') 

 

self.playbook_name = None 

 

# This is a 6 character identifier provided with each message 

# This makes it easier to correlate messages when there are more 

# than 1 simultaneous playbooks running 

self.guid = uuid.uuid4().hex[:6] 

 

def set_options(self, task_keys=None, var_options=None, direct=None): 

 

super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) 

 

self.webhook_url = self.get_option('webhook_url') 

self.channel = self.get_option('channel') 

self.username = self.get_option('username') 

self.show_invocation = (self._display.verbosity > 1) 

 

if self.webhook_url is None: 

self.disabled = True 

self._display.warning('Slack Webhook URL was not provided. The ' 

'Slack Webhook URL can be provided using ' 

'the `SLACK_WEBHOOK_URL` environment ' 

'variable.') 

 

def send_msg(self, attachments): 

payload = { 

'channel': self.channel, 

'username': self.username, 

'attachments': attachments, 

'parse': 'none', 

'icon_url': ('http://cdn2.hubspot.net/hub/330046/' 

'file-449187601-png/ansible_badge.png'), 

} 

 

data = json.dumps(payload) 

self._display.debug(data) 

self._display.debug(self.webhook_url) 

try: 

response = open_url(self.webhook_url, data=data) 

return response.read() 

except Exception as e: 

self._display.warning('Could not submit message to Slack: %s' % 

str(e)) 

 

def v2_playbook_on_start(self, playbook): 

self.playbook_name = os.path.basename(playbook._file_name) 

 

title = [ 

'*Playbook initiated* (_%s_)' % self.guid 

] 

invocation_items = [] 

if self._plugin_options and self.show_invocation: 

tags = self.get_option('tags') 

skip_tags = self.get_option('skip_tags') 

extra_vars = self.get_option('extra_vars') 

subset = self.get_option('subset') 

inventory = os.path.basename( 

os.path.realpath(self.get_option('inventory')) 

) 

 

invocation_items.append('Inventory: %s' % inventory) 

if tags and tags != 'all': 

invocation_items.append('Tags: %s' % tags) 

if skip_tags: 

invocation_items.append('Skip Tags: %s' % skip_tags) 

if subset: 

invocation_items.append('Limit: %s' % subset) 

if extra_vars: 

invocation_items.append('Extra Vars: %s' % 

' '.join(extra_vars)) 

 

title.append('by *%s*' % self.get_options('remote_user')) 

 

title.append('\n\n*%s*' % self.playbook_name) 

msg_items = [' '.join(title)] 

if invocation_items: 

msg_items.append('```\n%s\n```' % '\n'.join(invocation_items)) 

 

msg = '\n'.join(msg_items) 

 

attachments = [{ 

'fallback': msg, 

'fields': [ 

{ 

'value': msg 

} 

], 

'color': 'warning', 

'mrkdwn_in': ['text', 'fallback', 'fields'], 

}] 

 

self.send_msg(attachments=attachments) 

 

def v2_playbook_on_play_start(self, play): 

"""Display Play start messages""" 

 

name = play.name or 'Play name not specified (%s)' % play._uuid 

msg = '*Starting play* (_%s_)\n\n*%s*' % (self.guid, name) 

attachments = [ 

{ 

'fallback': msg, 

'text': msg, 

'color': 'warning', 

'mrkdwn_in': ['text', 'fallback', 'fields'], 

} 

] 

self.send_msg(attachments=attachments) 

 

def v2_playbook_on_stats(self, stats): 

"""Display info about playbook statistics""" 

 

hosts = sorted(stats.processed.keys()) 

 

t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable', 

'Failures']) 

 

failures = False 

unreachable = False 

 

for h in hosts: 

s = stats.summarize(h) 

 

if s['failures'] > 0: 

failures = True 

if s['unreachable'] > 0: 

unreachable = True 

 

t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable', 

'failures']]) 

 

attachments = [] 

msg_items = [ 

'*Playbook Complete* (_%s_)' % self.guid 

] 

if failures or unreachable: 

color = 'danger' 

msg_items.append('\n*Failed!*') 

else: 

color = 'good' 

msg_items.append('\n*Success!*') 

 

msg_items.append('```\n%s\n```' % t) 

 

msg = '\n'.join(msg_items) 

 

attachments.append({ 

'fallback': msg, 

'fields': [ 

{ 

'value': msg 

} 

], 

'color': color, 

'mrkdwn_in': ['text', 'fallback', 'fields'] 

}) 

 

self.send_msg(attachments=attachments)