-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsendgraph.py
executable file
·250 lines (220 loc) · 9.05 KB
/
sendgraph.py
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
241
242
243
244
245
246
247
248
249
250
#!/usr/bin/env python
"""
Adapted from:
https://gist.github.com/bkjones/1902478
Send email notifications with context (graphite graph).
This takes a recipient email address and a graphite URL and sends a
POST to <graphite host>/dashboard/email with a body that looks like
this:
sender=user%40example.com&recipients=user2%40example.com&
subject=foo&message=bar&graph_params=%7B%22
target%22%3A%22target%3DdrawAsInfinite(metric.path.in.graphite)
%22%2C%22from%22%3A%22-2hours%22%2C%22until%22%3A%22now%22%2C%22
width%22%3A600%2C%22height%22%3A250%7D
...which will cause the graphite installation to send an email.
command definition would look like this:
# 'notify-svcgraph-by-email' command definition
define command{
command_name notify-svcgraph-by-email
command_line $USER5$/sendgraph.py -u "http://$USER6$"
--interval 6hours --from_email "[email protected]"
}
service definition would look like this:
define service{
use prod_template
service_description s_dn_test
check_command check_graphite2!30min!-z ok
host_name localhost
_WARN 1
_CRIT 5
_GRAPHITE_METRIC maxSeries(stats.gauges.count.*)
action_url https://myrunbook/link
}
Updates:
- use icinga environment variables where possible
- don't bother sending notification event
- html email formatting
- decouple graphite metric from the graphite URL. This is because visually
we will often times want something different than what the check is doing
(ie 6 hour graph in the email, but generally we don't want the check to be
looking over 6hrs)
- alert thresholds printed on graph
Author: David Nguyen
"""
import requests
import logging
import sys
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
from optparse import OptionParser
import os
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
LOGGER = logging.getLogger(os.path.basename(__file__))
graphite_metric = os.getenv('ICINGA__SERVICEGRAPHITE_METRIC', None)
warn_threshold = os.getenv('ICINGA__SERVICEWARN', None)
crit_threshold = os.getenv('ICINGA__SERVICECRIT', None)
contact_email = os.getenv('ICINGA_CONTACTEMAIL', None)
notification_type = os.getenv('ICINGA_NOTIFICATIONTYPE', None)
service_state = os.getenv('ICINGA_SERVICESTATE', None)
host_alias = os.getenv('ICINGA_HOSTALIAS', None)
host_address = os.getenv('ICINGA_HOSTADDRESS', None)
service_description = os.getenv('ICINGA_SERVICEDESC', None)
date_time = os.getenv('ICINGA_LONGDATETIME', None)
action_url = os.getenv('ICINGA_SERVICEACTIONURL', None)
service_output = os.getenv('ICINGA_SERVICEOUTPUT', None)
service_duration = os.getenv('ICINGA_SERVICEDURATION', None)
notification_comment = os.getenv('ICINGA_NOTIFICATIONCOMMENT', None)
def send_graph_email(graph, subject, sender, receivers, body=None):
"""
Builds an email with the attached graph.
:param body: Text portion of the alert notification email.
:param graph: Assumed to be a PNG, currently.
:param subject: Email subject line
:param sender: an email address
:param receivers: list of email addresses to send to.
:return:
"""
LOGGER.debug("body: %s subject: %s sender: %s receivers: %s" % (
body, subject, sender, receivers))
if body is None:
body = '\n'
if graph is not None:
#Append graph to bottom of the email body
body += '<BR><BR><img src="cid:graph">'
imgpart = MIMEImage(graph, 'png')
imgpart.add_header('Content-ID', '<graph>')
imgpart.add_header(
'Content-Disposition', 'inline', filename='graph.png')
msg = MIMEMultipart('related')
msg.attach(MIMEText(body, 'html'))
if graph is not None:
msg.attach(imgpart)
msg['to'] = ', '.join(receivers)
msg['from'] = sender
msg['subject'] = subject
s = smtplib.SMTP()
try:
s.connect()
s.sendmail(sender, receivers, msg.as_string())
s.close()
except Exception as out:
LOGGER.error("Sending mail failed: %s" % out)
def get_highlight_color(notification_type, service_state):
# default
color = "fuchsia"
if notification_type == "ACKNOWLEDGEMENT":
color = "cyan"
elif notification_type == "CUSTOM":
color = "greenyellow"
elif service_state == "CRITICAL":
color = "lightcoral"
elif service_state == "WARNING":
color = "yellow"
elif service_state == "OK":
color = "springgreen"
elif service_state == "UNKNOWN":
color = "grey"
return color
def generate_email_body(bgcolor):
body = ("{date_time}<BR><BR>"
"<TABLE border='0'>"
"<TR bgcolor='{bgcolor}'><TD><B>Service State:</B></TD>"
"<TD><B>{service_state}</B></TD></TR>"
"<TR><TD>Notification Type:</TD><TD>{notification_type}</TD></TR>"
"<TR><TD>Service:</TD><TD>{service_description}</TD></TR>"
"<TR><TD>Host:</TD><TD>{host_alias}</TD></TR>"
"<TR><TD>Address:</TD><TD>{host_address}</TD></TR>"
"<TR><TD>Runbook:</TD><TD>"
"<A HREF='{action_url}'>{action_url}</A></TD></TR>"
"<TR><TD>Problem Duration:</TD><TD>{service_duration}</TD></TR>"
"<TR><TD>Comment:</TD><TD>{notification_comment}</TD></TR>"
"<TR><TD></TD><TD></TD></TR>"
"<TR><TD>Check Output:</TD><TD>{service_output}</TD></TR>"
"</TABLE>".format(date_time=date_time, bgcolor=bgcolor,
notification_type=notification_type,
service_state=service_state,
service_description=service_description,
host_alias=host_alias, host_address=host_address,
action_url=action_url, service_duration=service_duration,
notification_comment=notification_comment,
service_output=service_output))
return body
def get_graph(options):
"""
We need to handle the case where no graphite graph is being used in the
check. Return None in those cases
Returns: either response.content (png of graphite graph) or None
"""
if graphite_metric is not None:
graph_url = ("{base_url}/render?lineMode=connected&from=-{interval}&"
"width={width}&target={graphite_metric}"
"&bgcolor=FFFFFF&fgcolor=000000".format(
base_url=options.base_url,
interval=options.interval,
width=options.width,
graphite_metric=graphite_metric))
if warn_threshold is not None:
graph_url += "&target=threshold({0},'warn = {0}','yellow')".format(
warn_threshold)
if crit_threshold is not None:
graph_url += "&target=threshold({0}, 'crit = {0}','red')".format(
crit_threshold)
try:
LOGGER.debug('graph url is %s' % graph_url)
result = requests.get(graph_url, timeout=30)
except Exception, e:
LOGGER.error("Couldn't pull a graph from {0}".format(graph_url))
LOGGER.error(e)
graph = None
else:
LOGGER.debug(
"Response headers for graph request: %s", result.headers)
graph = result.content
else:
graph = None
return graph
def get_options():
parser = OptionParser()
parser.add_option('-u', '--url',
action='store',
dest='base_url',
help='Graphite Base URL http://localhost:14770')
parser.add_option('-i', '--interval',
action='store',
dest='interval',
default='6hours',
help='Graph Interval to display')
parser.add_option('-W', '--width',
action='store',
dest='width',
default='800',
help='Graph width')
parser.add_option('-f', '--from_email',
action='store',
dest='from_email',
help='From email address')
options, args = parser.parse_args()
return options
def main():
options = get_options()
bgcolor = get_highlight_color(notification_type, service_state)
body = generate_email_body(bgcolor)
# Can uncomment this for debugging - print all env vars out to see what
# icinga is doing...
#
# for key in os.environ.keys():
# body += "<BR><BR>{0}: {1}<BR>".format(key, os.environ[key])
subject = ("{notification_type} {service_state} "
"{service_description}/{host_alias}".format(
notification_type=notification_type,
service_state=service_state,
service_description=service_description, host_alias=host_alias
))
graph = get_graph(options)
send_graph_email(
graph, subject, options.from_email, contact_email.split(' '), body)
LOGGER.debug("Mail sent")
if __name__ == '__main__':
main()