We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
'slack' callback plugin not working
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY 
ansible-playbook with slack module produces an error in callback plugin

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
slack.CallbackModule

##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.6.1
  config file = /Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg
  configured module search path = [u'/Users/mikejames/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/Cellar/ansible/2.6.1/libexec/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.15 (default, Jun 17 2018, 12:46:58) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.2)]
```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->
```
DEFAULT_CALLBACK_PLUGIN_PATH(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = [u'/Users/mikejames/GitHub/ConfigurationManagement/ansible/playbooks/callback_plugins']
DEFAULT_CALLBACK_WHITELIST(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = ['profile_tasks', 'timer', 'slack']
DEFAULT_HOST_LIST(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = [u'/Users/mikejames/GitHub/ConfigurationManagement/ansible/inventory']
DEFAULT_LOG_PATH(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = /Users/mikejames/GitHub/ConfigurationManagement/ansible/logs/ansible.log
DEFAULT_ROLES_PATH(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = [u'/Users/mikejames/GitHub/ConfigurationManagement/ansible/playbooks/roles']
HOST_KEY_CHECKING(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = False
```

##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->
macOS High Sierra 10.13.6

##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->

https://gist.github.com/tightly-clutched/05a40814d3271b51a6530163e3209299
<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
I expected a result to be posted to slack channel

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
playbook execution failed 

<!--- Paste verbatim command output between quotes below -->
https://gist.github.com/tightly-clutched/05a40814d3271b51a6530163e3209299

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/plugins/callback/slack.py`
Content:
```
1 # (C) 2014-2015, Matt Martz <matt@sivel.net>
2 # (C) 2017 Ansible Project
3 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
4 
5 # Make coding more python3-ish
6 from __future__ import (absolute_import, division, print_function)
7 __metaclass__ = type
8 
9 DOCUMENTATION = '''
10     callback: slack
11     callback_type: notification
12     requirements:
13       - whitelist in configuration
14       - prettytable (python library)
15     short_description: Sends play events to a Slack channel
16     version_added: "2.1"
17     description:
18         - This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.
19         - Before 2.4 only environment variables were available for configuring this plugin
20     options:
21       webhook_url:
22         required: True
23         description: Slack Webhook URL
24         env:
25           - name: SLACK_WEBHOOK_URL
26         ini:
27           - section: callback_slack
28             key: webhook_url
29       channel:
30         default: "#ansible"
31         description: Slack room to post in.
32         env:
33           - name: SLACK_CHANNEL
34         ini:
35           - section: callback_slack
36             key: channel
37       username:
38         description: Username to post as.
39         env:
40           - name: SLACK_USERNAME
41         default: ansible
42         ini:
43           - section: callback_slack
44             key: username
45 '''
46 
47 import json
48 import os
49 import uuid
50 
51 try:
52     from __main__ import cli
53 except ImportError:
54     cli = None
55 
56 from ansible.module_utils.urls import open_url
57 from ansible.plugins.callback import CallbackBase
58 
59 try:
60     import prettytable
61     HAS_PRETTYTABLE = True
62 except ImportError:
63     HAS_PRETTYTABLE = False
64 
65 
66 class CallbackModule(CallbackBase):
67     """This is an ansible callback plugin that sends status
68     updates to a Slack channel during playbook execution.
69     """
70     CALLBACK_VERSION = 2.0
71     CALLBACK_TYPE = 'notification'
72     CALLBACK_NAME = 'slack'
73     CALLBACK_NEEDS_WHITELIST = True
74 
75     def __init__(self, display=None):
76 
77         super(CallbackModule, self).__init__(display=display)
78 
79         if not HAS_PRETTYTABLE:
80             self.disabled = True
81             self._display.warning('The `prettytable` python module is not '
82                                   'installed. Disabling the Slack callback '
83                                   'plugin.')
84 
85         self.playbook_name = None
86 
87         # This is a 6 character identifier provided with each message
88         # This makes it easier to correlate messages when there are more
89         # than 1 simultaneous playbooks running
90         self.guid = uuid.uuid4().hex[:6]
91 
92     def set_options(self, task_keys=None, var_options=None, direct=None):
93 
94         super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
95 
96         self.webhook_url = self.get_option('webhook_url')
97         self.channel = self.get_option('channel')
98         self.username = self.get_option('username')
99         self.show_invocation = (self._display.verbosity > 1)
100 
101         if self.webhook_url is None:
102             self.disabled = True
103             self._display.warning('Slack Webhook URL was not provided. The '
104                                   'Slack Webhook URL can be provided using '
105                                   'the `SLACK_WEBHOOK_URL` environment '
106                                   'variable.')
107 
108     def send_msg(self, attachments):
109         payload = {
110             'channel': self.channel,
111             'username': self.username,
112             'attachments': attachments,
113             'parse': 'none',
114             'icon_url': ('http://cdn2.hubspot.net/hub/330046/'
115                          'file-449187601-png/ansible_badge.png'),
116         }
117 
118         data = json.dumps(payload)
119         self._display.debug(data)
120         self._display.debug(self.webhook_url)
121         try:
122             response = open_url(self.webhook_url, data=data)
123             return response.read()
124         except Exception as e:
125             self._display.warning('Could not submit message to Slack: %s' %
126                                   str(e))
127 
128     def v2_playbook_on_start(self, playbook):
129         self.playbook_name = os.path.basename(playbook._file_name)
130 
131         title = [
132             '*Playbook initiated* (_%s_)' % self.guid
133         ]
134         invocation_items = []
135         if self._plugin_options and self.show_invocation:
136             tags = self.get_option('tags')
137             skip_tags = self.get_option('skip_tags')
138             extra_vars = self.get_option('extra_vars')
139             subset = self.get_option('subset')
140             inventory = os.path.basename(
141                 os.path.realpath(self.get_option('inventory'))
142             )
143 
144             invocation_items.append('Inventory:  %s' % inventory)
145             if tags and tags != 'all':
146                 invocation_items.append('Tags:       %s' % tags)
147             if skip_tags:
148                 invocation_items.append('Skip Tags:  %s' % skip_tags)
149             if subset:
150                 invocation_items.append('Limit:      %s' % subset)
151             if extra_vars:
152                 invocation_items.append('Extra Vars: %s' %
153                                         ' '.join(extra_vars))
154 
155             title.append('by *%s*' % self.get_option('remote_user'))
156 
157         title.append('\n\n*%s*' % self.playbook_name)
158         msg_items = [' '.join(title)]
159         if invocation_items:
160             msg_items.append('```\n%s\n```' % '\n'.join(invocation_items))
161 
162         msg = '\n'.join(msg_items)
163 
164         attachments = [{
165             'fallback': msg,
166             'fields': [
167                 {
168                     'value': msg
169                 }
170             ],
171             'color': 'warning',
172             'mrkdwn_in': ['text', 'fallback', 'fields'],
173         }]
174 
175         self.send_msg(attachments=attachments)
176 
177     def v2_playbook_on_play_start(self, play):
178         """Display Play start messages"""
179 
180         name = play.name or 'Play name not specified (%s)' % play._uuid
181         msg = '*Starting play* (_%s_)\n\n*%s*' % (self.guid, name)
182         attachments = [
183             {
184                 'fallback': msg,
185                 'text': msg,
186                 'color': 'warning',
187                 'mrkdwn_in': ['text', 'fallback', 'fields'],
188             }
189         ]
190         self.send_msg(attachments=attachments)
191 
192     def v2_playbook_on_stats(self, stats):
193         """Display info about playbook statistics"""
194 
195         hosts = sorted(stats.processed.keys())
196 
197         t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable',
198                                      'Failures'])
199 
200         failures = False
201         unreachable = False
202 
203         for h in hosts:
204             s = stats.summarize(h)
205 
206             if s['failures'] > 0:
207                 failures = True
208             if s['unreachable'] > 0:
209                 unreachable = True
210 
211             t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable',
212                                             'failures']])
213 
214         attachments = []
215         msg_items = [
216             '*Playbook Complete* (_%s_)' % self.guid
217         ]
218         if failures or unreachable:
219             color = 'danger'
220             msg_items.append('\n*Failed!*')
221         else:
222             color = 'good'
223             msg_items.append('\n*Success!*')
224 
225         msg_items.append('```\n%s\n```' % t)
226 
227         msg = '\n'.join(msg_items)
228 
229         attachments.append({
230             'fallback': msg,
231             'fields': [
232                 {
233                     'value': msg
234                 }
235             ],
236             'color': color,
237             'mrkdwn_in': ['text', 'fallback', 'fields']
238         })
239 
240         self.send_msg(attachments=attachments)
241
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/plugins/callback/slack.py b/lib/ansible/plugins/callback/slack.py
--- a/lib/ansible/plugins/callback/slack.py
+++ b/lib/ansible/plugins/callback/slack.py
@@ -76,6 +76,8 @@
 
         super(CallbackModule, self).__init__(display=display)
 
+        self._options = cli.options
+
         if not HAS_PRETTYTABLE:
             self.disabled = True
             self._display.warning('The `prettytable` python module is not '
@@ -132,27 +134,25 @@
             '*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 self._options and self.show_invocation:
+            tags = self._options.tags
+            skip_tags = self._options.skip_tags
+            extra_vars = self._options.extra_vars
+            subset = self._options.subset
+            inventory = [os.path.abspath(i) for i in self._options.inventory]
+
+            invocation_items.append('Inventory:  %s' % ', '.join(inventory))
+            if tags and tags != ['all']:
+                invocation_items.append('Tags:       %s' % ', '.join(tags))
             if skip_tags:
-                invocation_items.append('Skip Tags:  %s' % skip_tags)
+                invocation_items.append('Skip Tags:  %s' % ', '.join(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_option('remote_user'))
+            title.append('by *%s*' % self._options.remote_user)
 
         title.append('\n\n*%s*' % self.playbook_name)
         msg_items = [' '.join(title)]

{"golden_diff": "diff --git a/lib/ansible/plugins/callback/slack.py b/lib/ansible/plugins/callback/slack.py\n--- a/lib/ansible/plugins/callback/slack.py\n+++ b/lib/ansible/plugins/callback/slack.py\n@@ -76,6 +76,8 @@\n \n         super(CallbackModule, self).__init__(display=display)\n \n+        self._options = cli.options\n+\n         if not HAS_PRETTYTABLE:\n             self.disabled = True\n             self._display.warning('The `prettytable` python module is not '\n@@ -132,27 +134,25 @@\n             '*Playbook initiated* (_%s_)' % self.guid\n         ]\n         invocation_items = []\n-        if self._plugin_options and self.show_invocation:\n-            tags = self.get_option('tags')\n-            skip_tags = self.get_option('skip_tags')\n-            extra_vars = self.get_option('extra_vars')\n-            subset = self.get_option('subset')\n-            inventory = os.path.basename(\n-                os.path.realpath(self.get_option('inventory'))\n-            )\n-\n-            invocation_items.append('Inventory:  %s' % inventory)\n-            if tags and tags != 'all':\n-                invocation_items.append('Tags:       %s' % tags)\n+        if self._options and self.show_invocation:\n+            tags = self._options.tags\n+            skip_tags = self._options.skip_tags\n+            extra_vars = self._options.extra_vars\n+            subset = self._options.subset\n+            inventory = [os.path.abspath(i) for i in self._options.inventory]\n+\n+            invocation_items.append('Inventory:  %s' % ', '.join(inventory))\n+            if tags and tags != ['all']:\n+                invocation_items.append('Tags:       %s' % ', '.join(tags))\n             if skip_tags:\n-                invocation_items.append('Skip Tags:  %s' % skip_tags)\n+                invocation_items.append('Skip Tags:  %s' % ', '.join(skip_tags))\n             if subset:\n                 invocation_items.append('Limit:      %s' % subset)\n             if extra_vars:\n                 invocation_items.append('Extra Vars: %s' %\n                                         ' '.join(extra_vars))\n \n-            title.append('by *%s*' % self.get_option('remote_user'))\n+            title.append('by *%s*' % self._options.remote_user)\n \n         title.append('\\n\\n*%s*' % self.playbook_name)\n         msg_items = [' '.join(title)]\n", "issue": "'slack' callback plugin not working\n<!---\r\nVerify first that your issue/request is not already reported on GitHub.\r\nTHIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.\r\nAlso test if the latest release, and devel branch are affected too.\r\nALWAYS add information AFTER (OUTSIDE) these html comments.\r\nOtherwise it may end up being automatically closed by our bot. -->\r\n\r\n##### SUMMARY \r\nansible-playbook with slack module produces an error in callback plugin\r\n\r\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### COMPONENT NAME\r\nslack.CallbackModule\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste, BELOW THIS COMMENT, verbatim output from \"ansible --version\" between quotes below -->\r\n```\r\nansible 2.6.1\r\n  config file = /Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg\r\n  configured module search path = [u'/Users/mikejames/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/local/Cellar/ansible/2.6.1/libexec/lib/python2.7/site-packages/ansible\r\n  executable location = /usr/local/bin/ansible\r\n  python version = 2.7.15 (default, Jun 17 2018, 12:46:58) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.2)]\r\n```\r\n\r\n##### CONFIGURATION\r\n<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of \"ansible-config dump --only-changed\"\r\nOtherwise, mention any settings you have changed/added/removed in ansible.cfg\r\n(or using the ANSIBLE_* environment variables).-->\r\n```\r\nDEFAULT_CALLBACK_PLUGIN_PATH(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = [u'/Users/mikejames/GitHub/ConfigurationManagement/ansible/playbooks/callback_plugins']\r\nDEFAULT_CALLBACK_WHITELIST(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = ['profile_tasks', 'timer', 'slack']\r\nDEFAULT_HOST_LIST(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = [u'/Users/mikejames/GitHub/ConfigurationManagement/ansible/inventory']\r\nDEFAULT_LOG_PATH(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = /Users/mikejames/GitHub/ConfigurationManagement/ansible/logs/ansible.log\r\nDEFAULT_ROLES_PATH(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = [u'/Users/mikejames/GitHub/ConfigurationManagement/ansible/playbooks/roles']\r\nHOST_KEY_CHECKING(/Users/mikejames/GitHub/ConfigurationManagement/ansible/ansible.cfg) = False\r\n```\r\n\r\n##### OS / ENVIRONMENT\r\n<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are\r\nmanaging, or say \"N/A\" for anything that is not platform-specific.\r\nAlso mention the specific version of what you are trying to control,\r\ne.g. if this is a network bug the version of firmware on the network device.-->\r\nmacOS High Sierra 10.13.6\r\n\r\n##### STEPS TO REPRODUCE\r\n<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.\r\nFor new features, show how the feature would be used. -->\r\n\r\nhttps://gist.github.com/tightly-clutched/05a40814d3271b51a6530163e3209299\r\n<!--- You can also paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- What did you expect to happen when running the steps above? -->\r\nI expected a result to be posted to slack channel\r\n\r\n##### ACTUAL RESULTS\r\n<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->\r\nplaybook execution failed \r\n\r\n<!--- Paste verbatim command output between quotes below -->\r\nhttps://gist.github.com/tightly-clutched/05a40814d3271b51a6530163e3209299\n", "before_files": [{"content": "# (C) 2014-2015, Matt Martz <matt@sivel.net>\n# (C) 2017 Ansible Project\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\n# Make coding more python3-ish\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nDOCUMENTATION = '''\n    callback: slack\n    callback_type: notification\n    requirements:\n      - whitelist in configuration\n      - prettytable (python library)\n    short_description: Sends play events to a Slack channel\n    version_added: \"2.1\"\n    description:\n        - This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.\n        - Before 2.4 only environment variables were available for configuring this plugin\n    options:\n      webhook_url:\n        required: True\n        description: Slack Webhook URL\n        env:\n          - name: SLACK_WEBHOOK_URL\n        ini:\n          - section: callback_slack\n            key: webhook_url\n      channel:\n        default: \"#ansible\"\n        description: Slack room to post in.\n        env:\n          - name: SLACK_CHANNEL\n        ini:\n          - section: callback_slack\n            key: channel\n      username:\n        description: Username to post as.\n        env:\n          - name: SLACK_USERNAME\n        default: ansible\n        ini:\n          - section: callback_slack\n            key: username\n'''\n\nimport json\nimport os\nimport uuid\n\ntry:\n    from __main__ import cli\nexcept ImportError:\n    cli = None\n\nfrom ansible.module_utils.urls import open_url\nfrom ansible.plugins.callback import CallbackBase\n\ntry:\n    import prettytable\n    HAS_PRETTYTABLE = True\nexcept ImportError:\n    HAS_PRETTYTABLE = False\n\n\nclass CallbackModule(CallbackBase):\n    \"\"\"This is an ansible callback plugin that sends status\n    updates to a Slack channel during playbook execution.\n    \"\"\"\n    CALLBACK_VERSION = 2.0\n    CALLBACK_TYPE = 'notification'\n    CALLBACK_NAME = 'slack'\n    CALLBACK_NEEDS_WHITELIST = True\n\n    def __init__(self, display=None):\n\n        super(CallbackModule, self).__init__(display=display)\n\n        if not HAS_PRETTYTABLE:\n            self.disabled = True\n            self._display.warning('The `prettytable` python module is not '\n                                  'installed. Disabling the Slack callback '\n                                  'plugin.')\n\n        self.playbook_name = None\n\n        # This is a 6 character identifier provided with each message\n        # This makes it easier to correlate messages when there are more\n        # than 1 simultaneous playbooks running\n        self.guid = uuid.uuid4().hex[:6]\n\n    def set_options(self, task_keys=None, var_options=None, direct=None):\n\n        super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)\n\n        self.webhook_url = self.get_option('webhook_url')\n        self.channel = self.get_option('channel')\n        self.username = self.get_option('username')\n        self.show_invocation = (self._display.verbosity > 1)\n\n        if self.webhook_url is None:\n            self.disabled = True\n            self._display.warning('Slack Webhook URL was not provided. The '\n                                  'Slack Webhook URL can be provided using '\n                                  'the `SLACK_WEBHOOK_URL` environment '\n                                  'variable.')\n\n    def send_msg(self, attachments):\n        payload = {\n            'channel': self.channel,\n            'username': self.username,\n            'attachments': attachments,\n            'parse': 'none',\n            'icon_url': ('http://cdn2.hubspot.net/hub/330046/'\n                         'file-449187601-png/ansible_badge.png'),\n        }\n\n        data = json.dumps(payload)\n        self._display.debug(data)\n        self._display.debug(self.webhook_url)\n        try:\n            response = open_url(self.webhook_url, data=data)\n            return response.read()\n        except Exception as e:\n            self._display.warning('Could not submit message to Slack: %s' %\n                                  str(e))\n\n    def v2_playbook_on_start(self, playbook):\n        self.playbook_name = os.path.basename(playbook._file_name)\n\n        title = [\n            '*Playbook initiated* (_%s_)' % self.guid\n        ]\n        invocation_items = []\n        if self._plugin_options and self.show_invocation:\n            tags = self.get_option('tags')\n            skip_tags = self.get_option('skip_tags')\n            extra_vars = self.get_option('extra_vars')\n            subset = self.get_option('subset')\n            inventory = os.path.basename(\n                os.path.realpath(self.get_option('inventory'))\n            )\n\n            invocation_items.append('Inventory:  %s' % inventory)\n            if tags and tags != 'all':\n                invocation_items.append('Tags:       %s' % tags)\n            if skip_tags:\n                invocation_items.append('Skip Tags:  %s' % skip_tags)\n            if subset:\n                invocation_items.append('Limit:      %s' % subset)\n            if extra_vars:\n                invocation_items.append('Extra Vars: %s' %\n                                        ' '.join(extra_vars))\n\n            title.append('by *%s*' % self.get_option('remote_user'))\n\n        title.append('\\n\\n*%s*' % self.playbook_name)\n        msg_items = [' '.join(title)]\n        if invocation_items:\n            msg_items.append('```\\n%s\\n```' % '\\n'.join(invocation_items))\n\n        msg = '\\n'.join(msg_items)\n\n        attachments = [{\n            'fallback': msg,\n            'fields': [\n                {\n                    'value': msg\n                }\n            ],\n            'color': 'warning',\n            'mrkdwn_in': ['text', 'fallback', 'fields'],\n        }]\n\n        self.send_msg(attachments=attachments)\n\n    def v2_playbook_on_play_start(self, play):\n        \"\"\"Display Play start messages\"\"\"\n\n        name = play.name or 'Play name not specified (%s)' % play._uuid\n        msg = '*Starting play* (_%s_)\\n\\n*%s*' % (self.guid, name)\n        attachments = [\n            {\n                'fallback': msg,\n                'text': msg,\n                'color': 'warning',\n                'mrkdwn_in': ['text', 'fallback', 'fields'],\n            }\n        ]\n        self.send_msg(attachments=attachments)\n\n    def v2_playbook_on_stats(self, stats):\n        \"\"\"Display info about playbook statistics\"\"\"\n\n        hosts = sorted(stats.processed.keys())\n\n        t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable',\n                                     'Failures'])\n\n        failures = False\n        unreachable = False\n\n        for h in hosts:\n            s = stats.summarize(h)\n\n            if s['failures'] > 0:\n                failures = True\n            if s['unreachable'] > 0:\n                unreachable = True\n\n            t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable',\n                                            'failures']])\n\n        attachments = []\n        msg_items = [\n            '*Playbook Complete* (_%s_)' % self.guid\n        ]\n        if failures or unreachable:\n            color = 'danger'\n            msg_items.append('\\n*Failed!*')\n        else:\n            color = 'good'\n            msg_items.append('\\n*Success!*')\n\n        msg_items.append('```\\n%s\\n```' % t)\n\n        msg = '\\n'.join(msg_items)\n\n        attachments.append({\n            'fallback': msg,\n            'fields': [\n                {\n                    'value': msg\n                }\n            ],\n            'color': color,\n            'mrkdwn_in': ['text', 'fallback', 'fields']\n        })\n\n        self.send_msg(attachments=attachments)\n", "path": "lib/ansible/plugins/callback/slack.py"}], "after_files": [{"content": "# (C) 2014-2015, Matt Martz <matt@sivel.net>\n# (C) 2017 Ansible Project\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\n# Make coding more python3-ish\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nDOCUMENTATION = '''\n    callback: slack\n    callback_type: notification\n    requirements:\n      - whitelist in configuration\n      - prettytable (python library)\n    short_description: Sends play events to a Slack channel\n    version_added: \"2.1\"\n    description:\n        - This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.\n        - Before 2.4 only environment variables were available for configuring this plugin\n    options:\n      webhook_url:\n        required: True\n        description: Slack Webhook URL\n        env:\n          - name: SLACK_WEBHOOK_URL\n        ini:\n          - section: callback_slack\n            key: webhook_url\n      channel:\n        default: \"#ansible\"\n        description: Slack room to post in.\n        env:\n          - name: SLACK_CHANNEL\n        ini:\n          - section: callback_slack\n            key: channel\n      username:\n        description: Username to post as.\n        env:\n          - name: SLACK_USERNAME\n        default: ansible\n        ini:\n          - section: callback_slack\n            key: username\n'''\n\nimport json\nimport os\nimport uuid\n\ntry:\n    from __main__ import cli\nexcept ImportError:\n    cli = None\n\nfrom ansible.module_utils.urls import open_url\nfrom ansible.plugins.callback import CallbackBase\n\ntry:\n    import prettytable\n    HAS_PRETTYTABLE = True\nexcept ImportError:\n    HAS_PRETTYTABLE = False\n\n\nclass CallbackModule(CallbackBase):\n    \"\"\"This is an ansible callback plugin that sends status\n    updates to a Slack channel during playbook execution.\n    \"\"\"\n    CALLBACK_VERSION = 2.0\n    CALLBACK_TYPE = 'notification'\n    CALLBACK_NAME = 'slack'\n    CALLBACK_NEEDS_WHITELIST = True\n\n    def __init__(self, display=None):\n\n        super(CallbackModule, self).__init__(display=display)\n\n        self._options = cli.options\n\n        if not HAS_PRETTYTABLE:\n            self.disabled = True\n            self._display.warning('The `prettytable` python module is not '\n                                  'installed. Disabling the Slack callback '\n                                  'plugin.')\n\n        self.playbook_name = None\n\n        # This is a 6 character identifier provided with each message\n        # This makes it easier to correlate messages when there are more\n        # than 1 simultaneous playbooks running\n        self.guid = uuid.uuid4().hex[:6]\n\n    def set_options(self, task_keys=None, var_options=None, direct=None):\n\n        super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)\n\n        self.webhook_url = self.get_option('webhook_url')\n        self.channel = self.get_option('channel')\n        self.username = self.get_option('username')\n        self.show_invocation = (self._display.verbosity > 1)\n\n        if self.webhook_url is None:\n            self.disabled = True\n            self._display.warning('Slack Webhook URL was not provided. The '\n                                  'Slack Webhook URL can be provided using '\n                                  'the `SLACK_WEBHOOK_URL` environment '\n                                  'variable.')\n\n    def send_msg(self, attachments):\n        payload = {\n            'channel': self.channel,\n            'username': self.username,\n            'attachments': attachments,\n            'parse': 'none',\n            'icon_url': ('http://cdn2.hubspot.net/hub/330046/'\n                         'file-449187601-png/ansible_badge.png'),\n        }\n\n        data = json.dumps(payload)\n        self._display.debug(data)\n        self._display.debug(self.webhook_url)\n        try:\n            response = open_url(self.webhook_url, data=data)\n            return response.read()\n        except Exception as e:\n            self._display.warning('Could not submit message to Slack: %s' %\n                                  str(e))\n\n    def v2_playbook_on_start(self, playbook):\n        self.playbook_name = os.path.basename(playbook._file_name)\n\n        title = [\n            '*Playbook initiated* (_%s_)' % self.guid\n        ]\n        invocation_items = []\n        if self._options and self.show_invocation:\n            tags = self._options.tags\n            skip_tags = self._options.skip_tags\n            extra_vars = self._options.extra_vars\n            subset = self._options.subset\n            inventory = [os.path.abspath(i) for i in self._options.inventory]\n\n            invocation_items.append('Inventory:  %s' % ', '.join(inventory))\n            if tags and tags != ['all']:\n                invocation_items.append('Tags:       %s' % ', '.join(tags))\n            if skip_tags:\n                invocation_items.append('Skip Tags:  %s' % ', '.join(skip_tags))\n            if subset:\n                invocation_items.append('Limit:      %s' % subset)\n            if extra_vars:\n                invocation_items.append('Extra Vars: %s' %\n                                        ' '.join(extra_vars))\n\n            title.append('by *%s*' % self._options.remote_user)\n\n        title.append('\\n\\n*%s*' % self.playbook_name)\n        msg_items = [' '.join(title)]\n        if invocation_items:\n            msg_items.append('```\\n%s\\n```' % '\\n'.join(invocation_items))\n\n        msg = '\\n'.join(msg_items)\n\n        attachments = [{\n            'fallback': msg,\n            'fields': [\n                {\n                    'value': msg\n                }\n            ],\n            'color': 'warning',\n            'mrkdwn_in': ['text', 'fallback', 'fields'],\n        }]\n\n        self.send_msg(attachments=attachments)\n\n    def v2_playbook_on_play_start(self, play):\n        \"\"\"Display Play start messages\"\"\"\n\n        name = play.name or 'Play name not specified (%s)' % play._uuid\n        msg = '*Starting play* (_%s_)\\n\\n*%s*' % (self.guid, name)\n        attachments = [\n            {\n                'fallback': msg,\n                'text': msg,\n                'color': 'warning',\n                'mrkdwn_in': ['text', 'fallback', 'fields'],\n            }\n        ]\n        self.send_msg(attachments=attachments)\n\n    def v2_playbook_on_stats(self, stats):\n        \"\"\"Display info about playbook statistics\"\"\"\n\n        hosts = sorted(stats.processed.keys())\n\n        t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable',\n                                     'Failures'])\n\n        failures = False\n        unreachable = False\n\n        for h in hosts:\n            s = stats.summarize(h)\n\n            if s['failures'] > 0:\n                failures = True\n            if s['unreachable'] > 0:\n                unreachable = True\n\n            t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable',\n                                            'failures']])\n\n        attachments = []\n        msg_items = [\n            '*Playbook Complete* (_%s_)' % self.guid\n        ]\n        if failures or unreachable:\n            color = 'danger'\n            msg_items.append('\\n*Failed!*')\n        else:\n            color = 'good'\n            msg_items.append('\\n*Success!*')\n\n        msg_items.append('```\\n%s\\n```' % t)\n\n        msg = '\\n'.join(msg_items)\n\n        attachments.append({\n            'fallback': msg,\n            'fields': [\n                {\n                    'value': msg\n                }\n            ],\n            'color': color,\n            'mrkdwn_in': ['text', 'fallback', 'fields']\n        })\n\n        self.send_msg(attachments=attachments)\n", "path": "lib/ansible/plugins/callback/slack.py"}]}