Created
December 2, 2016 16:20
-
-
Save pysysops/87b8c55cc0792129352979c96bb4c8c9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
import urllib | |
import urllib2 | |
import json | |
import argparse | |
import ssl | |
import time | |
from urlparse import urlparse | |
def setup(): | |
parser = argparse.ArgumentParser( | |
description='Alert Ops is a roll up alert script for Sensu alerts.', | |
) | |
parser.add_argument('-u', '--url', type=str, | |
help='URI of Sensu API', | |
required=True) | |
parser.add_argument('-s', '--slack-url', type=str, | |
help='URL to Slack webhook', default='') | |
parser.add_argument('-c', '--channel', type=str, | |
help='The Slack channel to send alerts to', | |
default='monitoring') | |
parser.add_argument('-e', '--email', type=str, | |
help='An email address to send alerts to', default='') | |
parser.add_argument('-q', '--quiet', type=int, | |
help='Number of minutes to silence events for', | |
default=0) | |
return parser.parse_args() | |
def getdata(url): | |
ctx = ssl.create_default_context() | |
ctx.check_hostname = False | |
ctx.verify_mode = ssl.CERT_NONE | |
try: | |
req = urllib2.Request(url, None, {'Accept': 'application/json'}) | |
raw = urllib2.urlopen(req, timeout=5, context=ctx).read() | |
data = json.loads(raw) | |
return {'status': 0, 'text': data} | |
except urllib2.HTTPError, e: | |
return {'status': 2, 'text': "%s: %s" % (e, url)} | |
except urllib2.URLError, e: | |
o = urlparse(url) | |
return {'status': 2, 'text': "Network error: %s: %s" % | |
(e.reason.args[1], o.netloc)} | |
except Exception, e: | |
o = urlparse(url) | |
return {'status': 3, 'text': "%s: %s" % (e, o.netloc)} | |
def postdata(url, data={}): | |
ctx = ssl.create_default_context() | |
ctx.check_hostname = False | |
ctx.verify_mode = ssl.CERT_NONE | |
try: | |
req = urllib2.Request(url, None, {'Accept': 'application/json', | |
'Content-Type': 'application/json'}) | |
postdata = json.dumps(data) | |
raw = urllib2.urlopen(req, data=postdata, timeout=5, | |
context=ctx).read() | |
except Exception, e: | |
print e | |
def deleteresource(url): | |
ctx = ssl.create_default_context() | |
ctx.check_hostname = False | |
ctx.verify_mode = ssl.CERT_NONE | |
try: | |
req = urllib2.Request(url, None, {'Accept': 'application/json', | |
'Content-Type': 'application/json'}) | |
req.get_method = lambda: 'DELETE' | |
raw = urllib2.urlopen(req, timeout=5, context=ctx).read() | |
except Exception, e: | |
print e | |
def getstatus(code): | |
if code == 0: | |
return 'OK' | |
elif code == 1: | |
return 'WARNING' | |
elif code == 2: | |
return 'CRITICAL' | |
else: | |
return 'UNKNOWN' | |
def getcolor(code): | |
if code == 0: | |
return 'good' | |
elif code == 1: | |
return 'warning' | |
elif code == 2: | |
return 'danger' | |
else: | |
return '#dddddd' | |
def main(args): | |
# Get data from Sensu / Uchiwa API | |
event_data = getdata("%s/events" % args.url) | |
stash_data = getdata("%s/stashes" % args.url)['text'] | |
# Create some dictionaries to collect useful data in | |
events = {} | |
events['total'] = {} | |
events['acknowledged'] = {} | |
attachments = [] | |
stashes = [] | |
event_stash_paths = [] | |
if event_data['status'] == 0: | |
for event in event_data['text']: | |
# increment total events for each status | |
try: | |
events['total'][getstatus(event['check']['status'])] += 1 | |
except KeyError: | |
events['total'][getstatus(event['check']['status'])] = 1 | |
# Build an array of "event" stash paths for cleaning up stashes | |
# for alerts that have resolved | |
event_stash_paths.append("silence/%s/%s" % ( | |
str(event['client']['name']), | |
str(event['check']['name']), | |
)) | |
# If the event is not acknowledged then handle it. | |
if not event['acknowledged']: | |
# Never silence alerts that have always_alert set to true | |
if ('always_alert' not in event['check']) or ( | |
event['check']['always_alert'] is True): | |
if ('occurrences' in event['check']) and ( | |
event['occurrences'] > event['check']['occurrences']): | |
try: | |
# Build slack message | |
message = {} | |
message['fallback'] = "%s - %s: %s on %s" % ( | |
event['dc'], | |
getstatus(event['check']['status']), | |
event['check']['name'], | |
event['client']['name'], | |
) | |
message['color'] = getcolor( | |
event['check']['status']) | |
message['fields'] = [] | |
fields = {} | |
fields['title'] = message['fallback'] | |
fields['value'] = event['check']['output'].split( | |
'|')[0] | |
message['fields'].append(fields) | |
attachments.append(message) | |
if args.quiet > 0: | |
stashdata = {} | |
stashdata['content'] = {} | |
stashdata['content']['reason'] = ( | |
"Successfully alerted") | |
stashdata['content']['source'] = "alert_ops" | |
stashdata['content']['timestamp'] = int( | |
time.time()) | |
stashdata['expire'] = args.quiet * 60 | |
stashdata['dc'] = event['dc'] | |
stashdata['path'] = "silence/%s/%s" % ( | |
str(event['client']['name']), | |
str(event['check']['name']), | |
) | |
# Build an array of stashes to create on | |
# successful alerting to slack | |
stashes.append(stashdata) | |
except: | |
pass | |
else: | |
# The check is acknowledged | |
# increment acknowledged events for each status | |
try: | |
sstr = getstatus(event['check']['status']) | |
events['acknowledged'][sstr] += 1 | |
except KeyError: | |
events['acknowledged'][sstr] = 1 | |
# Alert Slack | |
slack_message = {} | |
slack_message['channel'] = "#%s" % args.channel | |
slack_message['text'] = "Sensu Alerts: %s" % str(events) | |
slack_message['attachments'] = attachments | |
# Post to slack if there's anything to alert on | |
if len(slack_message['attachments']) > 0: | |
postdata(args.slack_url, slack_message) | |
# Create stashes to silence successfully alerted. | |
for stash in stashes: | |
postdata("%s/stashes" % args.url, stash) | |
# Clean up stashes for resolved alerts | |
stash_paths = [] | |
for stash in stash_data: | |
stash_paths.append(str(stash['path'])) | |
for stash in stash_paths: | |
if stash not in event_stash_paths: | |
deleteresource("%s/stashes/%s" % (args.url, stash)) | |
print str(events) | |
return event_data['status'] | |
else: | |
# Some error occured getting the data. | |
print event_data['text'] | |
return event_data['status'] | |
if __name__ == "__main__": | |
sys.exit(main(setup())) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment