From 43d4de45df25b94523703ed44962034aaa16de6e Mon Sep 17 00:00:00 2001 From: Markus Birth Date: Mon, 6 Oct 2014 17:37:52 +0200 Subject: [PATCH] Initial commit --- .gitmodules | 3 + lib/external/pystache | 1 + lib/python/pystache | 1 + share/check_mk/notifications/mailstache | 202 ++++++++++++++++++ .../mailstache_templates/foot_html.mustache | 2 + .../mailstache_templates/head_html.mustache | 130 +++++++++++ .../mailstache_templates/host_html.mustache | 36 ++++ .../host_subject.mustache | 1 + .../mailstache_templates/host_txt.mustache | 7 + .../service_html.mustache | 53 +++++ .../service_subject.mustache | 1 + .../mailstache_templates/service_txt.mustache | 17 ++ 12 files changed, 454 insertions(+) create mode 100644 .gitmodules create mode 160000 lib/external/pystache create mode 120000 lib/python/pystache create mode 100755 share/check_mk/notifications/mailstache create mode 100644 share/check_mk/notifications/mailstache_templates/foot_html.mustache create mode 100644 share/check_mk/notifications/mailstache_templates/head_html.mustache create mode 100644 share/check_mk/notifications/mailstache_templates/host_html.mustache create mode 100644 share/check_mk/notifications/mailstache_templates/host_subject.mustache create mode 100644 share/check_mk/notifications/mailstache_templates/host_txt.mustache create mode 100644 share/check_mk/notifications/mailstache_templates/service_html.mustache create mode 100644 share/check_mk/notifications/mailstache_templates/service_subject.mustache create mode 100644 share/check_mk/notifications/mailstache_templates/service_txt.mustache diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..998fbea --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/external/pystache"] + path = lib/external/pystache + url = https://github.com/defunkt/pystache.git diff --git a/lib/external/pystache b/lib/external/pystache new file mode 160000 index 0000000..17a5dfd --- /dev/null +++ b/lib/external/pystache @@ -0,0 +1 @@ +Subproject commit 17a5dfdcd56eb76af731d141de395a7632a905b8 diff --git a/lib/python/pystache b/lib/python/pystache new file mode 120000 index 0000000..def8b8f --- /dev/null +++ b/lib/python/pystache @@ -0,0 +1 @@ +../external/pystache/pystache \ No newline at end of file diff --git a/share/check_mk/notifications/mailstache b/share/check_mk/notifications/mailstache new file mode 100755 index 0000000..f1c5f92 --- /dev/null +++ b/share/check_mk/notifications/mailstache @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# Templated Emails with included Graphs +# This script creates a very beautiful mail in multipart format with +# attached graphs and such neat stuff. Sweet! +# +# Argument 1: Full system path to the pnp4nagios index.php for fetching +# the graphs. Usually auto configured in OMD. +# Argument 2: HTTP-URL-Prefix to open multisite. When provided, several +# links are added to the mail. + +import os, re, sys, subprocess, pystache +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.mime.application import MIMEApplication +from email.mime.image import MIMEImage + +opt_debug = '-d' in sys.argv + +class GraphException(Exception): + pass + +def prepare_contents(context): + renderer = pystache.Renderer(search_dirs=[ + context["OMD_ROOT"] + "/local/share/check_mk/notifications/mailstache_templates", + context["OMD_ROOT"] + "/share/check_mk/notifications/mailstache_templates" + ]) + + if context['WHAT'] == 'HOST': + tmpl_subj = renderer.load_template("host_subject") + tmpl_txt = renderer.load_template("host_txt") + tmpl_html = renderer.load_template("host_html") + else: + tmpl_subj = renderer.load_template("service_subject") + tmpl_txt = renderer.load_template("service_txt") + tmpl_html = renderer.load_template("service_html") + + context["SUBJECT"] = renderer.render(tmpl_subj, context) + + # Render text + result_txt = renderer.render(tmpl_txt, context) + + # Change newlines to HTML + context["LONGHOSTOUTPUT"] = context["LONGHOSTOUTPUT"].replace("\\n", "
") + context["LONGSERVICEOUTPUT"] = context["LONGSERVICEOUTPUT"].replace("\\n", "
") + + # Render HTML + result_html = renderer.render(tmpl_html, context) + + return result_txt, result_html + +def multipart_mail(target, subject, content_txt, content_html, attach = []): + m = MIMEMultipart('related', _charset='utf-8') + + alt = MIMEMultipart('alternative') + + # The plain text part + txt = MIMEText(content_txt, 'plain', _charset='utf-8') + alt.attach(txt) + + # The html text part + html = MIMEText(content_html, 'html', _charset='utf-8') + alt.attach(html) + + m.attach(alt) + + # Add all attachments + for what, name, contents, how in attach: + if what == 'img': + part = MIMEImage(contents, name = name) + else: + part = MIMEApplication(contents, name = name) + part.add_header('Content-ID', '<%s>' % name) + # how must be inline or attachment + part.add_header('Content-Disposition', how, filename = name) + m.attach(part) + + m['Subject'] = subject + m['To'] = target + + return m + +def send_mail(m, target): + p = subprocess.Popen(["/usr/sbin/sendmail", "-i", target ], stdin = subprocess.PIPE) + p.communicate(m.as_string()) + return True + +def fetch_pnp_data(context, params): + try: + # Autodetect the path in OMD environments + path = "%s/share/pnp4nagios/htdocs/index.php" % context['OMD_ROOT'] + php_save_path = "-d session.save_path=%s/tmp/php/session" % context['OMD_ROOT'] + except: + # Non-omd environment - use plugin argument 1 + path = context.get('PARAMETER_1', '') + php_save_path = "" # Using default path + + if not os.path.exists(path): + raise GraphException('Unable to locate pnp4nagios index.php (%s)' % path) + + return os.popen('REMOTE_USER="%s" php %s %s "%s"' % (context['CONTACTNAME'], php_save_path, path, params)).read() + +def fetch_num_sources(context): + svc_desc = context['WHAT'] == 'HOST' and '_HOST_' or context['SERVICEDESC'] + infos = fetch_pnp_data(context, '/json?host=%s&srv=%s&view=0' % + (context['HOSTNAME'], svc_desc)) + if not infos.startswith('[{'): + raise GraphException('Unable to fetch graph infos, got: "%s"' % infos) + + return infos.count('source=') + +def fetch_graph(context, source, view = 1): + svc_desc = context['WHAT'] == 'HOST' and '_HOST_' or context['SERVICEDESC'] + graph = fetch_pnp_data(context, '/image?host=%s&srv=%s&view=%d&source=%d' % + (context['HOSTNAME'], svc_desc, view, source)) + + if graph[:8] != '\x89PNG\r\n\x1a\n': + raise GraphException('Unable to fetch the graph, got: "%s"' % graph) + + return graph + +def main(): + # gather all options from env + context = dict([ + (var[7:], value.decode("utf-8")) + for (var, value) + in os.environ.items() + if var.startswith("NOTIFY_")]) + + context['HOSTNOTES'] = os.environ.get("NAGIOS_HOSTNOTES") + context['HOSTNOTESURL'] = os.environ.get("NAGIOS_HOSTNOTESURL") + context['SERVICENOTES'] = os.environ.get("NAGIOS_SERVICENOTES") + context['SERVICENOTESURL'] = os.environ.get("NAGIOS_SERVICENOTESURL") + + # Fetch graphs for this object. It first tries to detect how many sources + # are available for this object. Then it loops through all sources and + # fetches retrieves the images. If a problem occurs, it is printed to + # stderr (-> notify.log) and the graph is not added to the mail. + try: + num_sources = fetch_num_sources(context) + except GraphException, e: + sys.stderr.write('Unable to fetch graph infos: %s\n' % e) + num_sources = 0 + + # If argument 2 is given, we know the base url to the installation and can add + # links to hosts and services. ubercomfortable! + if context.get('PARAMETER_2'): + base_url = context['PARAMETER_2'].rstrip('/') + host_url = base_url + context['HOSTURL'] + + context['LINKEDHOSTNAME'] = '%s' % (host_url, context['HOSTNAME']) + context['HOSTLINK'] = '\nLink: %s' % host_url + + if context['WHAT'] == 'SERVICE': + service_url = base_url + context['SERVICEURL'] + context['LINKEDSERVICEDESC'] = '%s' % (service_url, context['SERVICEDESC']) + context['SERVICELINK'] = '\nLink: %s' % service_url + else: + context['LINKEDHOSTNAME'] = context['HOSTNAME'] + context['LINKEDSERVICEDESC'] = context.get('SERVICEDESC', '') + context['HOSTLINK'] = '' + context['SERVICELINK'] = '' + + attachments = [] + graph_code = '' + for source in range(0, num_sources): + try: + content = fetch_graph(context, source) + except GraphException, e: + sys.stderr.write('Unable to fetch graph: %s\n' % e) + continue + + if context['WHAT'] == 'HOST': + svc_desc = '_HOST_' + else: + svc_desc = context['SERVICEDESC'].replace(' ', '_') + # replace forbidden windows characters < > ? " : | \ / * + for token in ["<", ">", "?", "\"", ":", "|", "\\", "/", "*"] : + svc_desc = svc_desc.replace(token, "x%s" % ord(token)) + name = '%s-%s-%d.png' % (context['HOSTNAME'], svc_desc, source) + + attachments.append(('img', name, content, 'inline')) + + context['GRAPH_%d' % source] = name + graph_code += '' % name + + if graph_code: + context['GRAPH_CODE'] = ( + 'Graphs' + '%s' % graph_code + ) + else: + context['GRAPH_CODE'] = '' + + # Prepare the mail contents (also SUBJECT) + content_txt, content_html = prepare_contents(context) + + # Create the mail and send it + m = multipart_mail(context['CONTACTEMAIL'], context['SUBJECT'], content_txt, + content_html, attachments) + send_mail(m, context['CONTACTEMAIL']) + +main() diff --git a/share/check_mk/notifications/mailstache_templates/foot_html.mustache b/share/check_mk/notifications/mailstache_templates/foot_html.mustache new file mode 100644 index 0000000..691287b --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/foot_html.mustache @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/share/check_mk/notifications/mailstache_templates/head_html.mustache b/share/check_mk/notifications/mailstache_templates/head_html.mustache new file mode 100644 index 0000000..16a637d --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/head_html.mustache @@ -0,0 +1,130 @@ + + +{{SUBJECT}} + + + \ No newline at end of file diff --git a/share/check_mk/notifications/mailstache_templates/host_html.mustache b/share/check_mk/notifications/mailstache_templates/host_html.mustache new file mode 100644 index 0000000..8ed95c8 --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/host_html.mustache @@ -0,0 +1,36 @@ +{{> head_html}} + + + + + + + + + + + + + + + + + + + + + + + +{{#LONGHOSTOUTPUT}} + + + + +{{/LONGHOSTOUTPUT}} +{{{GRAPH_CODE}}} +
Object Information
Name{{{LINKEDHOSTNAME}}} ({{HOSTALIAS}})
Address{{HOSTADDRESS}}
State
State + {{LASTHOSTSTATE}} → + {{HOSTSTATE}} ({{NOTIFICATIONTYPE}}) + since {{LASTHOSTSTATECHANGE_REL}} +
Output{{HOSTOUTPUT}}
Long output{{{LONGHOSTOUTPUT}}}
{{> foot_html}} \ No newline at end of file diff --git a/share/check_mk/notifications/mailstache_templates/host_subject.mustache b/share/check_mk/notifications/mailstache_templates/host_subject.mustache new file mode 100644 index 0000000..bf9af6e --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/host_subject.mustache @@ -0,0 +1 @@ +{{NOTIFICATIONTYPE}} host {{HOSTNAME}} is {{HOSTSTATE}} \ No newline at end of file diff --git a/share/check_mk/notifications/mailstache_templates/host_txt.mustache b/share/check_mk/notifications/mailstache_templates/host_txt.mustache new file mode 100644 index 0000000..1cfeb2b --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/host_txt.mustache @@ -0,0 +1,7 @@ +Host: {{HOSTNAME}} ({{HOSTALIAS}}) +Address: {{HOSTADDRESS}} +{{HOSTLINK}} +State: {{LASTHOSTSTATE}} -> {{HOSTSTATE}} ({{NOTIFICATIONTYPE}}) since {{LASTHOSTSTATECHANGE_REL}} +Output: {{HOSTOUTPUT}} +Perfdata: {{HOSTPERFDATA}} +{{LONGHOSTOUTPUT}} diff --git a/share/check_mk/notifications/mailstache_templates/service_html.mustache b/share/check_mk/notifications/mailstache_templates/service_html.mustache new file mode 100644 index 0000000..8a4e952 --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/service_html.mustache @@ -0,0 +1,53 @@ +{{> head_html}} + + + + + + + + + + + + + + + + + + + + + + + + + + + +{{#LONGSERVICEOUTPUT}} + + + + +{{/LONGSERVICEOUTPUT}} +{{#NOTIFICATIONCOMMENT}} + + + + + + + + + + + +{{/NOTIFICATIONCOMMENT}} +{{{GRAPH_CODE}}} +{{> foot_html}} \ No newline at end of file diff --git a/share/check_mk/notifications/mailstache_templates/service_subject.mustache b/share/check_mk/notifications/mailstache_templates/service_subject.mustache new file mode 100644 index 0000000..f4b4937 --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/service_subject.mustache @@ -0,0 +1 @@ +{{NOTIFICATIONTYPE}} service {{HOSTNAME}}/{{SERVICEDESC}} is {{SERVICESTATE}} \ No newline at end of file diff --git a/share/check_mk/notifications/mailstache_templates/service_txt.mustache b/share/check_mk/notifications/mailstache_templates/service_txt.mustache new file mode 100644 index 0000000..bec8ddf --- /dev/null +++ b/share/check_mk/notifications/mailstache_templates/service_txt.mustache @@ -0,0 +1,17 @@ +Host: {{HOSTNAME}} ({{HOSTALIAS}}) +Address: {{HOSTADDRESS}}{{HOSTLINK}} +{{#HOSTNOTESURL}}More Info: {{HOSTNOTESURL}}{{/HOSTNOTESURL}} + +Service: {{SERVICEDESC}}{{SERVICELINK}} +State: {{LASTSERVICESTATE}} -> {{SERVICESTATE}} ({{NOTIFICATIONTYPE}}) since {{LASTSERVICESTATECHANGE_REL}} +Output: {{SERVICEOUTPUT}} +Perfdata: {{SERVICEPERFDATA}} +{{LONGSERVICEOUTPUT}} +{{#NOTIFICATIONCOMMENT}} + +{{#SERVICENOTESURL}}More Info: {{SERVICENOTESURL}} + +{{/SERVICENOTESURL}} +Comment by {{NOTIFICATIONAUTHORALIAS}} ({{NOTIFICATIONAUTHORNAME}}): + {{NOTIFICATIONCOMMENT}} +{{/NOTIFICATIONCOMMENT}}
Object Information
Hostname{{{LINKEDHOSTNAME}}} ({{HOSTALIAS}}){{#HOSTNOTESURL}} [More info]{{/HOSTNOTESURL}}
Address{{HOSTADDRESS}}
Service description{{{LINKEDSERVICEDESC}}}{{#SERVICENOTESURL}} [More info]{{/SERVICENOTESURL}}
State
State + {{LASTSERVICESTATE}} → + {{SERVICESTATE}} ({{NOTIFICATIONTYPE}}) + since {{LASTSERVICESTATECHANGE_REL}} +
Output{{SERVICEOUTPUT}}
Long output{{{LONGSERVICEOUTPUT}}}
Comment
Text{{NOTIFICATIONCOMMENT}}
Author{{NOTIFICATIONAUTHORALIAS}} ({{NOTIFICATIONAUTHORNAME}})