Skip to content

Instantly share code, notes, and snippets.

@pysysops
Created December 2, 2016 16:20
Show Gist options
  • Save pysysops/87b8c55cc0792129352979c96bb4c8c9 to your computer and use it in GitHub Desktop.
Save pysysops/87b8c55cc0792129352979c96bb4c8c9 to your computer and use it in GitHub Desktop.
#!/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