Initial commit
This commit is contained in:
commit
43d4de45df
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "lib/external/pystache"]
|
||||
path = lib/external/pystache
|
||||
url = https://github.com/defunkt/pystache.git
|
1
lib/external/pystache
vendored
Submodule
1
lib/external/pystache
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 17a5dfdcd56eb76af731d141de395a7632a905b8
|
1
lib/python/pystache
Symbolic link
1
lib/python/pystache
Symbolic link
@ -0,0 +1 @@
|
||||
../external/pystache/pystache
|
202
share/check_mk/notifications/mailstache
Executable file
202
share/check_mk/notifications/mailstache
Executable file
@ -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", "<br />")
|
||||
context["LONGSERVICEOUTPUT"] = context["LONGSERVICEOUTPUT"].replace("\\n", "<br />")
|
||||
|
||||
# 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'] = '<a href="%s">%s</a>' % (host_url, context['HOSTNAME'])
|
||||
context['HOSTLINK'] = '\nLink: %s' % host_url
|
||||
|
||||
if context['WHAT'] == 'SERVICE':
|
||||
service_url = base_url + context['SERVICEURL']
|
||||
context['LINKEDSERVICEDESC'] = '<a href="%s">%s</a>' % (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 += '<img src="cid:%s" />' % name
|
||||
|
||||
if graph_code:
|
||||
context['GRAPH_CODE'] = (
|
||||
'<tr><th colspan=2>Graphs</th></tr>'
|
||||
'<tr class="even0"><td colspan=2 class=graphs>%s</td></tr>' % 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()
|
@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,130 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{{SUBJECT}}</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #426D8A;
|
||||
padding: 5px;
|
||||
font-family: arial,helvetica,sans-serif;
|
||||
font-size: 10px;
|
||||
}
|
||||
table {
|
||||
border-spacing: 0px;
|
||||
border-collapse: collapse;
|
||||
margin: 5px 0 0 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
color: black;
|
||||
empty-cells: show;
|
||||
}
|
||||
|
||||
table th {
|
||||
font-weight: normal;
|
||||
border-right: 1px solid #ccc;
|
||||
background-color: #999;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
vertical-align: middle;
|
||||
font-size: 9pt;
|
||||
height: 14px;
|
||||
}
|
||||
table th:last-child {
|
||||
border-right-style: none;
|
||||
}
|
||||
|
||||
table tr > td {
|
||||
border-right: 1px solid #ccc;
|
||||
padding: 2px 4px;
|
||||
height: 22px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
table tr td:last-child {
|
||||
border-right-style: none;
|
||||
}
|
||||
|
||||
/*
|
||||
table a {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
table a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
*/
|
||||
|
||||
table tr td {
|
||||
padding-bottom: 4px;
|
||||
padding: 4px 5px 2px 5px;
|
||||
text-align: left;
|
||||
height: 16px;
|
||||
line-height: 14px;
|
||||
vertical-align: top;
|
||||
font-size: 9pt;
|
||||
}
|
||||
table tr td.left {
|
||||
width: 10%;
|
||||
white-space: nowrap;
|
||||
vertical-align: top;
|
||||
padding-right: 20px;
|
||||
}
|
||||
table tr.even0 td.left {
|
||||
background-color: #bbb;
|
||||
}
|
||||
table tr.odd0 td.left {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
tr.odd0 { background-color: #eee; }
|
||||
tr.even0 { background-color: #ddd; }
|
||||
|
||||
td.odd0 { background-color: #eee; }
|
||||
td.even0 { background-color: #ddd; }
|
||||
|
||||
tr.odd1 { background-color: #ffc; }
|
||||
tr.even1 { background-color: #ffa; }
|
||||
|
||||
tr.odd2 { background-color: #fcc; }
|
||||
tr.even2 { background-color: #faa; }
|
||||
|
||||
tr.odd3 { background-color: #ffe0a0; }
|
||||
tr.even3 { background-color: #ffefaf; }
|
||||
|
||||
td.state {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.state0, .stateOK, .hstate0, .hstateUP, .state0 a, .hstate0 a {
|
||||
background-color: #0b3; color: #fff;
|
||||
}
|
||||
|
||||
.state1, .stateWARNING, .state1 a, tr.groupheader td.state1 {
|
||||
background-color: #ff0; color: #000;
|
||||
}
|
||||
|
||||
.state2, .stateCRITICAL, .hstate1, .hstateDOWN, .state2 a, .hstate2 a {
|
||||
background-color: #f00; color: #fff;
|
||||
}
|
||||
|
||||
.state3, .stateUNKNOWN, .hstate2, .hstateUNREACHABLE, .state3 a, .hstate2 a {
|
||||
background-color: #f80; color: #fff;
|
||||
}
|
||||
|
||||
.statep, .statePENDING, .hstatep, .hstatePENDING, .statep a, .hstatep a {
|
||||
background-color: #888; color: #fff;
|
||||
}
|
||||
|
||||
.stated, .stateDOWNTIME, .hstated, .hstateDOWNTIME, .stated a, .hstated a {
|
||||
background-color: #0af; color: #fff;
|
||||
}
|
||||
|
||||
td.graphs {
|
||||
width: 617px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
@ -0,0 +1,36 @@
|
||||
{{> head_html}}
|
||||
<table>
|
||||
<tr class="even0">
|
||||
<th colspan=2>Object Information</th>
|
||||
</tr>
|
||||
<tr class="odd0">
|
||||
<td class=left>Name</td>
|
||||
<td>{{{LINKEDHOSTNAME}}} ({{HOSTALIAS}})</td>
|
||||
</tr>
|
||||
<tr class="even0">
|
||||
<td class=left>Address</td>
|
||||
<td>{{HOSTADDRESS}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan=2>State</th>
|
||||
</tr>
|
||||
<tr class="odd0">
|
||||
<td class=left>State</td>
|
||||
<td class="state">
|
||||
<span class="hstate{{LASTHOSTSTATE}}">{{LASTHOSTSTATE}}</span> →
|
||||
<span class="hstate{{HOSTSTATEID}}">{{HOSTSTATE}} ({{NOTIFICATIONTYPE}})</span>
|
||||
since {{LASTHOSTSTATECHANGE_REL}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="even0">
|
||||
<td class="left">Output</td>
|
||||
<td>{{HOSTOUTPUT}}</td>
|
||||
</tr>
|
||||
{{#LONGHOSTOUTPUT}}
|
||||
<tr class="odd0">
|
||||
<td class="left">Long output</td>
|
||||
<td>{{{LONGHOSTOUTPUT}}}</td>
|
||||
</tr>
|
||||
{{/LONGHOSTOUTPUT}}
|
||||
{{{GRAPH_CODE}}}
|
||||
</table>{{> foot_html}}
|
@ -0,0 +1 @@
|
||||
{{NOTIFICATIONTYPE}} host {{HOSTNAME}} is {{HOSTSTATE}}
|
@ -0,0 +1,7 @@
|
||||
Host: {{HOSTNAME}} ({{HOSTALIAS}})
|
||||
Address: {{HOSTADDRESS}}
|
||||
{{HOSTLINK}}
|
||||
State: {{LASTHOSTSTATE}} -> {{HOSTSTATE}} ({{NOTIFICATIONTYPE}}) since {{LASTHOSTSTATECHANGE_REL}}
|
||||
Output: {{HOSTOUTPUT}}
|
||||
Perfdata: {{HOSTPERFDATA}}
|
||||
{{LONGHOSTOUTPUT}}
|
@ -0,0 +1,53 @@
|
||||
{{> head_html}}
|
||||
<table>
|
||||
<tr class="even0">
|
||||
<th colspan=2>Object Information</th>
|
||||
</tr>
|
||||
<tr class="odd0">
|
||||
<td class=left>Hostname</td>
|
||||
<td>{{{LINKEDHOSTNAME}}} ({{HOSTALIAS}}){{#HOSTNOTESURL}} [<a href="{{HOSTNOTESURL}}">More info</a>]{{/HOSTNOTESURL}}</td>
|
||||
</tr>
|
||||
<tr class="even0">
|
||||
<td class=left>Address</td>
|
||||
<td>{{HOSTADDRESS}}</td>
|
||||
</tr>
|
||||
<tr class="odd0">
|
||||
<td class=left>Service description</td>
|
||||
<td>{{{LINKEDSERVICEDESC}}}{{#SERVICENOTESURL}} [<a href="{{SERVICENOTESURL}}">More info</a>]{{/SERVICENOTESURL}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan=2>State</th>
|
||||
</tr>
|
||||
<tr class="odd0">
|
||||
<td class=left>State</td>
|
||||
<td class="state">
|
||||
<span class="state{{LASTSERVICESTATE}}">{{LASTSERVICESTATE}}</span> →
|
||||
<span class="state{{SERVICESTATEID}}">{{SERVICESTATE}} ({{NOTIFICATIONTYPE}})</span>
|
||||
since {{LASTSERVICESTATECHANGE_REL}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="even0">
|
||||
<td class="left">Output</td>
|
||||
<td>{{SERVICEOUTPUT}}</td>
|
||||
</tr>
|
||||
{{#LONGSERVICEOUTPUT}}
|
||||
<tr class="odd0">
|
||||
<td class="left">Long output</td>
|
||||
<td>{{{LONGSERVICEOUTPUT}}}</td>
|
||||
</tr>
|
||||
{{/LONGSERVICEOUTPUT}}
|
||||
{{#NOTIFICATIONCOMMENT}}
|
||||
<tr>
|
||||
<th colspan="2">Comment</th>
|
||||
</tr>
|
||||
<tr class="odd0">
|
||||
<td class="left">Text</td>
|
||||
<td>{{NOTIFICATIONCOMMENT}}</td>
|
||||
</tr>
|
||||
<tr class="even0">
|
||||
<td class="left">Author</td>
|
||||
<td>{{NOTIFICATIONAUTHORALIAS}} ({{NOTIFICATIONAUTHORNAME}})</td>
|
||||
</tr>
|
||||
{{/NOTIFICATIONCOMMENT}}
|
||||
{{{GRAPH_CODE}}}
|
||||
{{> foot_html}}
|
@ -0,0 +1 @@
|
||||
{{NOTIFICATIONTYPE}} service {{HOSTNAME}}/{{SERVICEDESC}} is {{SERVICESTATE}}
|
@ -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}}
|
Reference in New Issue
Block a user