0
0
Fork 0
mirror of https://github.com/alerta/alerta.git synced 2025-02-05 14:09:44 +00:00
alerta_alerta/alerta/app/views.py
2014-03-23 21:46:30 +00:00

398 lines
13 KiB
Python

import json
import datetime
from collections import defaultdict
from functools import wraps
from flask import request, current_app, render_template, abort
from alerta.app import app, db, notify
from alerta.app.switch import Switch
from alerta.common import config
from alerta.common import log as logging
from alerta.common.alert import Alert
from alerta.common.heartbeat import Heartbeat
from alerta.common import status_code, severity_code
from alerta.common.utils import DateEncoder
from alerta.app.utils import parse_fields, crossdomain
from alerta.common.metrics import Gauge, Counter, Timer
Version = '3.0.0'
LOG = logging.getLogger(__name__)
CONF = config.CONF
# Set-up metrics
duplicate_timer = Timer('alerts', 'duplicate', 'Duplicate alerts', 'Total time to process number of duplicate alerts')
correlate_timer = Timer('alerts', 'correlate', 'Correlated alerts', 'Total time to process number of correlated alerts')
create_new_timer = Timer('alerts', 'create_new', 'Newly created alerts', 'Total time to process number of new alerts')
delete_timer = Timer('alerts', 'deleted', 'Deleted alerts', 'Total time to process number of deleted alerts')
# Over-ride jsonify to support Date Encoding
def jsonify(*args, **kwargs):
return current_app.response_class(json.dumps(dict(*args, **kwargs), cls=DateEncoder,
indent=None if request.is_xhr else 2), mimetype='application/json')
def jsonp(func):
"""Wraps JSONified output for JSONP requests."""
@wraps(func)
def decorated_function(*args, **kwargs):
callback = request.args.get('callback', False)
if callback:
data = str(func(*args, **kwargs).data)
content = str(callback) + '(' + data + ')'
mimetype = 'application/javascript'
return current_app.response_class(content, mimetype=mimetype)
else:
return func(*args, **kwargs)
return decorated_function
@app.route('/test', methods=['OPTIONS', 'PUT', 'POST', 'DELETE', 'GET'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def test():
return jsonify(
status="ok",
method=request.method,
json=request.json,
data=request.data,
args=request.args,
app_root=app.root_path,
)
@app.route('/api', methods=['GET'])
def routes():
rules = []
for rule in app.url_map.iter_rules():
rule.methods = ','.join([r for r in rule.methods if r not in ['OPTIONS', 'HEAD']])
if rule.endpoint not in ['test', 'static']:
rules.append(rule)
return render_template('index.html', rules=rules)
@app.route('/api/alerts', methods=['GET'])
@jsonp
def get_alerts():
try:
query, sort, limit, query_time = parse_fields(request)
except Exception, e:
return jsonify(status="error", message=str(e))
fields = dict()
fields['history'] = {'$slice': CONF.history_limit}
if 'status' not in query:
query['status'] = 'open'
alerts = db.get_alerts(query=query, fields=fields, sort=sort, limit=limit)
total = db.get_count(query=query) # because total may be greater than limit
found = 0
severity_count = defaultdict(int)
status_count = defaultdict(int)
alert_response = list()
if len(alerts) > 0:
last_time = None
for alert in alerts:
body = alert.get_body()
found += 1
severity_count[body['severity']] += 1
status_count[body['status']] += 1
if not last_time:
last_time = body['lastReceiveTime']
elif body['lastReceiveTime'] > last_time:
last_time = body['lastReceiveTime']
alert_response.append(body)
return jsonify(
status="ok",
total=found,
more=total > limit,
alerts=alert_response,
severityCounts=severity_count,
statusCounts=status_count,
lastTime=last_time,
autoRefresh=Switch.get('auto-refresh-allow').is_on(),
)
else:
return jsonify(
status="ok",
message="not found",
total=0,
more=False,
alerts=[],
severityCounts=severity_count,
statusCounts=status_count,
lastTime=query_time,
autoRefresh=Switch.get('auto-refresh-allow').is_on()
)
@app.route('/api/alert', methods=['OPTIONS', 'POST'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def create_alert():
# Create a new alert
try:
incomingAlert = Alert.parse_alert(request.data)
except ValueError, e:
return jsonify(status="error", message=str(e))
try:
suppress = incomingAlert.transform_alert()
except RuntimeError, e:
# self.statsd.metric_send('alerta.alerts.error', 1)
return jsonify(status="error", message=str(e))
if suppress:
LOG.info('Suppressing alert %s', incomingAlert.get_id())
return jsonify(status="error", message="alert suppressed by transform")
if db.is_duplicate(incomingAlert):
duplicate_timer.start_timer()
alert = db.save_duplicate(incomingAlert)
duplicate_timer.stop_timer()
elif db.is_correlated(incomingAlert):
correlate_timer.start_timer()
alert = db.save_correlated(incomingAlert)
correlate_timer.stop_timer()
else:
create_new_timer.start_timer()
alert = db.save_alert(incomingAlert)
create_new_timer.stop_timer()
if alert:
notify.send(alert)
return jsonify(status="ok", id=alert.id)
else:
return jsonify(status="error", message="alert insert or update failed")
@app.route('/api/alert/<id>', methods=['OPTIONS', 'GET'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def get_alert(id):
alert = db.get_alert(id=id)
if alert:
return jsonify(status="ok", total=1, alert=alert.get_body())
else:
return jsonify(status="ok", message="not found", total=0, alert=None)
@app.route('/api/alert/<id>/status', methods=['OPTIONS', 'POST'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def set_status(id):
data = request.json
if data and 'status' in data:
alert = db.set_status(id=id, status=data['status'], text=data.get('text', ''))
else:
return jsonify(status="error", message="no data")
if alert:
notify.send(alert)
return jsonify(status="ok")
else:
return jsonify(status="error", message="failed to set alert status")
@app.route('/api/alert/<id>/tag', methods=['OPTIONS', 'POST'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def tag_alert(id):
data = request.json
if data and 'tags' in data:
response = db.tag_alert(id, data['tags'])
else:
return jsonify(status="error", message="no data")
if response:
return jsonify(status="ok")
else:
return jsonify(status="error", message="failed to tag alert")
@app.route('/api/alert/<id>', methods=['OPTIONS', 'DELETE', 'POST'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def delete_alert(id):
if request.method == 'DELETE' or (request.method == 'POST' and request.json['_method'] == 'delete'):
delete_timer.start_timer()
response = db.delete_alert(id)
delete_timer.stop_timer()
if response:
return jsonify(status="ok")
else:
return jsonify(status="error", message="failed to delete alert")
# Return severity and status counts
@app.route('/api/alert/counts', methods=['GET'])
@jsonp
def get_counts():
try:
query, _, _, query_time = parse_fields(request)
except Exception, e:
return jsonify(response={"status": "error", "message": str(e)})
counts = db.get_counts(query=query)
found = 0
severity_count = defaultdict(int)
status_count = defaultdict(int)
for count in counts:
found += 1
severity_count[count['severity']] += 1
status_count[count['status']] += 1
return jsonify(
status="ok",
total=found,
more=False,
severityCounts=severity_count,
statusCounts=status_count,
lastTime=query_time,
autoRefresh=Switch.get('auto-refresh-allow').is_on()
)
@app.route('/pagerduty', methods=['POST'])
def pagerduty():
if not request.json or not 'messages' in request.json:
abort(400)
for message in request.json['messages']:
LOG.debug('%s', json.dumps(message))
id = message['data']['incident']['incident_key']
html_url = message['data']['incident']['html_url']
incident_number = message['data']['incident']['incident_number']
incident_url = '<a href="%s">#%s</a>' % (html_url, incident_number)
LOG.info('PagerDuty incident #%s webhook for alert %s', incident_number, id)
LOG.error('previous status %s', db.get_alert(id=id).status)
if message['type'] == 'incident.trigger':
status = status_code.OPEN
user = message['data']['incident']['assigned_to_user']['name']
text = 'Incident %s assigned to %s' % (incident_url, user)
elif message['type'] == 'incident.acknowledge':
status = status_code.ACK
user = message['data']['incident']['assigned_to_user']['name']
text = 'Incident %s acknowledged by %s' % (incident_url, user)
elif message['type'] == 'incident.unacknowledge':
status = status_code.OPEN
text = 'Incident %s unacknowledged due to timeout' % incident_url
elif message['type'] == 'incident.resolve':
status = status_code.CLOSED
if message['data']['incident']['resolved_by_user']:
user = message['data']['incident']['resolved_by_user']['name']
else:
user = 'n/a'
text = 'Incident %s resolved by %s' % (incident_url, user)
elif message['type'] == 'incident.assign':
status = status_code.ASSIGN
user = message['data']['incident']['assigned_to_user']['name']
text = 'Incident %s manually assigned to %s' % (incident_url, user)
elif message['type'] == 'incident.escalate':
status = status_code.OPEN
user = message['data']['incident']['assigned_to_user']['name']
text = 'Incident %s escalated to %s' % (incident_url, user)
elif message['type'] == 'incident.delegate':
status = status_code.OPEN
user = message['data']['incident']['assigned_to_user']['name']
text = 'Incident %s reassigned due to escalation to %s' % (incident_url, user)
else:
status = status_code.UNKNOWN
text = message['type']
LOG.warn('Unknown PagerDuty message type: %s', message)
LOG.info('PagerDuty webhook %s change status to %s', message['type'], status)
pdAlert = db.update_status(id=id, status=status, text=text)
db.tag_alert(id=id, tags='incident=#%s' % incident_number)
LOG.error('returned status %s', pdAlert.status)
LOG.error('current status %s', db.get_alert(id=id).status)
# Forward alert to notify topic and logger queue
if pdAlert:
pdAlert.origin = 'pagerduty/webhook'
notify.send(pdAlert)
return jsonify(status="ok")
# Return a list of heartbeats
@app.route('/api/heartbeats', methods=['GET'])
@jsonp
def get_heartbeats():
heartbeats = db.get_heartbeats()
hb_list = list()
for hb in heartbeats:
hb_list.append(hb.get_body())
return jsonify(
status="ok",
total=len(heartbeats),
heartbeats=hb_list,
time=datetime.datetime.utcnow()
)
@app.route('/api/heartbeat', methods=['OPTIONS', 'POST'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def create_heartbeat():
try:
heartbeat = Heartbeat.parse_heartbeat(request.data)
except ValueError, e:
return jsonify(status="error", message=str(e))
heartbeat_id = db.save_heartbeat(heartbeat)
return jsonify(status="ok", id=heartbeat_id)
@app.route('/api/heartbeat/<id>', methods=['OPTIONS', 'DELETE', 'POST'])
@crossdomain(origin='*', headers=['Origin', 'X-Requested-With', 'Content-Type', 'Accept'])
@jsonp
def delete_heartbeat(id):
if request.method == 'DELETE' or (request.method == 'POST' and request.json['_method'] == 'delete'):
response = db.delete_heartbeat(id)
if response:
return jsonify(status="ok")
else:
return jsonify(status="error", message="failed to delete heartbeat")