Module: check_mk
Branch: master
Commit: e95ca3af09f75b525ff0fa41c093c815f5e056d0
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=e95ca3af09f75b…
Author: Óscar Nájera <on(a)mathias-kettner.de>
Date: Mon Sep 17 15:59:36 2018 +0200
Notification script that messages to slack webhook
- Deals with host state changes and service state changes
- Config WATO GUI
- Split contexts so that multiple users can be mentioned in the message
- Referenced Host and services are hyperlinked to Check_MK
Change-Id: Iffaa4a9b00365bbcdcbc9d4394f2eefb3d519bc4
---
.werks/6639 | 21 +++++
cmk/gui/plugins/wato/notifications.py | 47 ++++++++++++
cmk_base/notify.py | 2 +-
notifications/slack | 139 ++++++++++++++++++++++++++++++++++
4 files changed, 208 insertions(+), 1 deletion(-)
diff --git a/.werks/6639 b/.werks/6639
new file mode 100644
index 0000000..0227c3e
--- /dev/null
+++ b/.werks/6639
@@ -0,0 +1,21 @@
+Title: slack: New notification plugin for slack
+Level: 1
+Component: notifications
+Compatible: compat
+Edition: cre
+Version: 1.6.0i1
+Date: 1537357015
+Class: feature
+
+It is now possible to for Check_MK to send notifications about state
+changes of hosts and services into a Slack channel.
+
+Slack needs to be first configured to recieve messages from Check_MK. This
+can be done by setting up a Webhook <a
+href=\"https://my.slack.com/services/new/incoming-webhook/\"
+target=\"_blank\"> here </a>, where you specify which channel wil
recieve
+the notifications. In Check_MK, under WATO->Notifications, create a new
+notification rule. And select under Notification Method Slack. Copy the URL
+that Slack gives you after completing the configuration into the Slack
+Webhook-URL field. Optionally if you want sent messages to include
+hyperlinks to Check_MK, enable the field URL prefix for links to Check_MK.
diff --git a/cmk/gui/plugins/wato/notifications.py
b/cmk/gui/plugins/wato/notifications.py
old mode 100644
new mode 100755
index b014aa8..19f362f
--- a/cmk/gui/plugins/wato/notifications.py
+++ b/cmk/gui/plugins/wato/notifications.py
@@ -196,6 +196,53 @@ register_notification_parameters(
)
)
+register_notification_parameters(
+ "slack",
+ Dictionary(
+ optional_keys=["url_prefix"],
+ elements=[
+ ("webhook_url",
+ TextAscii(
+ title=_("Slack Webhook-URL"),
+ size=80,
+ allow_empty=False,
+ help=_(
+ "URL from Slack Webhook. Setup one <a
href=\"https://my.slack.com/services/new/incoming-webhook/\"
target=\"_blank\"> here </a>"),
+
regex="^https://hooks.slack.com/services/.+"s/.+",
+ regex_error=_("The Webhook-URL must begin with "
+
"<tt>https://hooks.slack.com/services/</tt>"),
+ )
+ ),
+ ("url_prefix",
+ Transform(CascadingDropdown(
+ style="dropdown",
+ title=_("URL prefix for links to Check_MK"),
+ help=_("If you use <b>Automatic HTTP/s</b> the URL
prefix for "
+ "host and service links within the notification mail
"
+ "is filled automatically. "
+ "If you specify an URL prefix here, then several parts of
the "
+ "slack message are armed with hyperlinks to your Check_MK
GUI. In both cases "
+ "the recipient of the message can directly visit the host
or "
+ "service in question in Check_MK. Specify an absolute URL
including "
+ "the <tt>.../check_mk/</tt>"),
+ choices=[
+ ("automatic_http", _("Automatic HTTP")),
+ ("automatic_https", _("Automatic HTTPs")),
+ ("manual", _("Specify URL prefix"),
TextAscii(
+ regex="^(http|https)://.*/check_mk/$",
+ regex_error=_("The URL must begin with
<tt>http</tt> or "
+ "<tt>https</tt> and end with
<tt>/check_mk/</tt>."),
+ size=64,
+ default_value="http://" + socket.gethostname() +
"/" + (
+ config.omd_site() and config.omd_site() + "/"
or "") + "check_mk/",
+ )),
+ ],
+ ), forth=transform_forth_html_mail_url_prefix,
+ back=transform_back_html_mail_url_prefix)
+ ),
+ ]
+ )
+)
register_notification_parameters(
"asciimail",
diff --git a/cmk_base/notify.py b/cmk_base/notify.py
old mode 100644
new mode 100755
index 28f6ce8..2b1e745
--- a/cmk_base/notify.py
+++ b/cmk_base/notify.py
@@ -526,7 +526,7 @@ def notify_rulebased(raw_context, analyse=False):
plugin_context = create_plugin_context(raw_context, params)
rbn_add_contact_information(plugin_context, contacts)
- split_contexts = (plugin not in ["", "mail",
"asciimail"] or
+ split_contexts = (plugin not in ["", "mail",
"asciimail", "slack"] or
# params can be a list (e.g. for custom notificatios)
params.get("disable_multiplexing") or
bulk)
diff --git a/notifications/slack b/notifications/slack
new file mode 100755
index 0000000..2410324
--- /dev/null
+++ b/notifications/slack
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# Slack
+
+# +------------------------------------------------------------------+
+# | ____ _ _ __ __ _ __ |
+# | / ___| |__ ___ ___| | __ | \/ | |/ / |
+# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
+# | | |___| | | | __/ (__| < | | | | . \ |
+# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
+# | |
+# | Copyright Mathias Kettner 2018 mk(a)mathias-kettner.de |
+# +------------------------------------------------------------------+
+#
+# This file is part of Check_MK.
+# The official homepage is at
http://mathias-kettner.de/check_mk.
+#
+# check_mk is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation in version 2. check_mk is distributed
+# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
+# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU General Public License for more de-
+# tails. You should have received a copy of the GNU General Public
+# License along with GNU Make; see the file COPYING. If not, write
+# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301 USA.
+r"""
+Send notification messages to slack
+===================================
+
+Use a slack webhook to send notification messages
+"""
+
+from typing import Dict # pylint: disable=unused-import
+import os
+import sys
+import requests
+
+COLORS = {
+ "CRITICAL": "#EE0000",
+ "DOWN": "#EE0000",
+ "WARNING": "#FFDD00",
+ "OK": "#00CC00",
+ "UP": "#00CC00",
+ "UNKNOWN": "#CCCCCC",
+ "UNREACHABLE": "#CCCCCC",
+}
+
+
+def extend_context_with_link_urls(context):
+ if context.get("PARAMETER_URL_PREFIX"):
+ url_prefix = context["PARAMETER_URL_PREFIX"]
+ elif context.get("PARAMETER_URL_PREFIX_MANUAL"):
+ url_prefix = context["PARAMETER_URL_PREFIX_MANUAL"]
+ elif context.get("PARAMETER_URL_PREFIX_AUTOMATIC") == "http":
+ url_prefix = "http://%s/%s" % (context["MONITORING_HOST"],
context["OMD_SITE"])
+ elif context.get("PARAMETER_URL_PREFIX_AUTOMATIC") == "https":
+ url_prefix = "https://%s/%s" % (context["MONITORING_HOST"],
context["OMD_SITE"])
+ else:
+ url_prefix = None
+
+ if url_prefix:
+ base_url = url_prefix.rstrip('/')
+ if base_url.endswith("/check_mk"):
+ base_url = base_url[:-9]
+ host_url = base_url + context['HOSTURL']
+
+ context['LINKEDHOSTNAME'] = '<%s|%s>' % (host_url,
context['HOSTNAME'])
+
+ if context['WHAT'] == 'SERVICE':
+ service_url = base_url + context['SERVICEURL']
+ context['LINKEDSERVICEDESC'] = '<%s|%s>' %
(service_url, context['SERVICEDESC'])
+
+ else:
+ context['LINKEDHOSTNAME'] = context['HOSTNAME']
+ context['LINKEDSERVICEDESC'] = context.get('SERVICEDESC',
'')
+
+
+def construct_message(context):
+ # type: (Dict) -> Dict
+ """Build the message for slack"""
+
+ extend_context_with_link_urls(context)
+
+ if context.get('SERVICESTATE', None):
+ color = COLORS.get(context["SERVICESTATE"])
+ title = "Service {NOTIFICATIONTYPE} notification".format(**context)
+ text = "Host: {LINKEDHOSTNAME} (IP: {HOSTADDRESS})\nService:
{LINKEDSERVICEDESC}\nState: {SERVICESTATE}".format(
+ **context)
+ output = context["SERVICEOUTPUT"]
+ else:
+ color = COLORS.get(context["HOSTSTATE"])
+ title = "Host {NOTIFICATIONTYPE} notification".format(**context)
+ text = "Host: {LINKEDHOSTNAME} (IP: {HOSTADDRESS})\nState:
{HOSTSTATE}".format(**context)
+ output = context["HOSTOUTPUT"]
+
+ return {
+ "attachments": [
+ {
+ "color": color,
+ "title": title,
+ "text": text,
+ },
+ {
+ "color":
+ color,
+ "title":
+ "Additional Info",
+ "text":
+ output + "\nPlease take a look: " + ", ".join(
+ map("(a){}".formatot;.format,
context["CONTACTNAME"].split(','))),
+ "footer":
+ "Check_MK notification: {LONGDATETIME}".format(**context),
+ },
+ ]
+ }
+
+
+def main():
+ context = {
+ var[7:]: value.decode("utf-8")
+ for (var, value) in os.environ.items()
+ if var.startswith("NOTIFY_")
+ }
+
+ url = context.get("PARAMETER_WEBHOOK_URL")
+
+ r = requests.post(url=url, json=construct_message(context))
+
+ if r.status_code == 200:
+ sys.exit(0)
+ else:
+ sys.stderr.write(
+ "Failed to send notification. Status: %i, Response: %s\n" %
(r.status_code, r.text))
+ sys.exit(2)
+
+
+if __name__ == '__main__':
+ main()