0
0
Fork 0
mirror of https://github.com/alerta/alerta.git synced 2025-01-24 17:29:39 +00:00
alerta_alerta/htdocs/api/v1/alert-api.py
2012-10-03 23:01:11 +01:00

178 lines
6.4 KiB
Python
Executable file

#!/usr/bin/env python
########################################
#
# alerta-api.py - Alerter New Alert API
#
########################################
import os
import sys
try:
import json
except ImportError:
import simplejson as json
import time
import datetime
import stomp
import urlparse
import logging
import uuid
import re
__version__ = '1.4.1'
BROKER_LIST = [('localhost', 61613)] # list of brokers for failover
ALERT_QUEUE = '/queue/alerts'
EXPIRATION_TIME = 600 # seconds = 10 minutes
LOGFILE = '/var/log/alerta/alert-api.log'
VALID_SEVERITY = [ 'CRITICAL', 'MAJOR', 'MINOR', 'WARNING', 'NORMAL', 'INFORM', 'DEBUG' ]
VALID_ENVIRONMENT = [ 'PROD', 'REL', 'QA', 'TEST', 'CODE', 'STAGE', 'DEV', 'LWP','INFRA' ]
SEVERITY_CODE = {
# ITU RFC5674 -> Syslog RFC5424
'CRITICAL': 1, # Alert
'MAJOR': 2, # Crtical
'MINOR': 3, # Error
'WARNING': 4, # Warning
'NORMAL': 5, # Notice
'INFORM': 6, # Informational
'DEBUG': 7, # Debug
}
# Extend JSON Encoder to support ISO 8601 format dates
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.date, datetime.datetime)):
return obj.replace(microsecond=0).isoformat() + ".%03dZ" % (obj.microsecond//1000)
else:
return json.JSONEncoder.default(self, obj)
def main():
start = time.time()
logging.basicConfig(level=logging.INFO, format="%(asctime)s alert-api[%(process)d] %(levelname)s - %(message)s", filename=LOGFILE)
logging.info('Received HTTP request %s %s' % (os.environ['REQUEST_METHOD'], os.environ['REQUEST_URI']))
status = dict()
status['response'] = dict()
status['response']['status'] = None
error = 'unknown error'
for e in os.environ:
logging.debug('%s: %s', e, os.environ[e])
# Get HTTP method and any body data
method = os.environ['REQUEST_METHOD']
if method in ['PUT', 'POST']:
try:
data = json.loads(sys.stdin.read())
except ValueError, e:
data = list()
logging.warning('Failed to get data - %s', e)
error = 'failed to parse json data in body'
# Parse RESTful URI
uri = urlparse.urlsplit(os.environ['REQUEST_URI'])
form = urlparse.parse_qs(os.environ['QUERY_STRING'])
request = method + ' ' + uri.path
m = re.search(r'(PUT|POST) /alerta/api/v1/alerts/alert.json$', request)
if m:
alert = data
# Set any defaults
if 'severity' not in alert:
alert['severity'] = 'normal'
if 'group' not in alert:
alert['group'] = 'Misc'
# Check for mandatory attributes
if 'resource' not in alert:
error = 'must supply a resource'
elif 'event' not in alert:
error = 'must supply event name'
elif 'value' not in alert:
error = 'must supply event value'
elif alert['severity'].upper() not in VALID_SEVERITY:
error = 'severity must be one of %s' % ', '.join(VALID_SEVERITY)
elif not all(x in VALID_ENVIRONMENT for x in alert['environment']):
error = 'must supply one or more environments from %s' % (','.join(VALID_ENVIRONMENT))
elif 'service' not in alert:
error = 'must supply one or more service'
elif 'text' not in alert:
error = 'must supply alert text'
else:
alertid = str(uuid.uuid4()) # random UUID
createTime = datetime.datetime.utcnow()
headers = dict()
headers['type'] = "exceptionAlert"
headers['correlation-id'] = alertid
headers['persistent'] = 'true'
headers['expires'] = int(time.time() * 1000) + EXPIRATION_TIME * 1000
alert['id'] = alertid
alert['severity'] = alert['severity'].upper()
alert['severityCode'] = SEVERITY_CODE[alert['severity']]
alert['environment'] = [x.upper() for x in alert['environment']]
alert['type'] = 'exceptionAlert'
alert['summary'] = '%s - %s %s is %s on %s %s' % (','.join(alert['environment']), alert['severity'].upper(), alert['event'], alert['value'], ','.join(alert['service']), alert['resource'])
alert['createTime'] = createTime.replace(microsecond=0).isoformat() + ".%03dZ" % (createTime.microsecond//1000)
alert['origin'] = 'alert-api/%s' % os.uname()[1]
logging.info('%s : %s', alertid, json.dumps(alert))
try:
conn = stomp.Connection(BROKER_LIST)
conn.start()
conn.connect(wait=True)
except Exception, e:
print >>sys.stderr, "ERROR: Could not connect to broker - %s" % e
logging.error('Could not connect to broker %s', e)
try:
conn.send(json.dumps(alert), headers, destination=ALERT_QUEUE)
except Exception, e:
print >>sys.stderr, "ERROR: Failed to send alert to broker - %s " % e
logging.error('Failed to send alert to broker %s', e)
broker = conn.get_host_and_port()
logging.info('%s : Alert sent to %s:%s', alertid, broker[0], str(broker[1]))
conn.disconnect()
status['response']['id'] = alertid
status['response']['status'] = 'ok'
diff = time.time() - start
status['response']['time'] = "%.3f" % diff
status['response']['localTime'] = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
if status['response']['status'] == None:
logging.error('Failed request %s', request)
diff = time.time() - start
status['response']['time'] = "%.3f" % diff
status['response']['status'] = 'error'
status['response']['message'] = error
status['response']['localTime'] = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
diff = int(diff * 1000)
content = json.dumps(status, cls=DateEncoder)
if 'callback' in form:
content = '%s(%s);' % (form['callback'][0], content)
print "Content-Type: application/javascript; charset=utf-8"
print "Content-Length: %s" % len(content)
print "Expires: -1"
print "Cache-Control: no-cache"
print "Pragma: no-cache"
print ""
print content
logging.info('Request %s completed in %sms', request, diff)
if __name__ == '__main__':
main()