0
0
Fork 0
mirror of https://github.com/alerta/alerta.git synced 2025-01-24 17:29:39 +00:00
alerta_alerta/experimental/alert-console.py
2012-12-13 09:11:45 +00:00

345 lines
11 KiB
Python
Executable file

#!/usr/bin/env python
########################################
#
# alert-console.py - Alert Console
#
########################################
import os
import sys
import time
import curses
try:
import json
except ImportError:
import simplejson as json
try:
import stomp
except ImportError:
print >>sys.stderr, 'ERROR: You need to install the stomp python module'
sys.exit(1)
import signal
import datetime
import pytz
__title__ = 'Alert Console'
__program__ = 'alert-console'
__version__ = '1.0.2'
BROKER_LIST = [('monitoring.guprod.gnl', 61613),('localhost', 61613)] # list of brokers for failover
NOTIFY_TOPIC = '/topic/notify'
# Minimum screen size
SCREEN_MIN_X = 80
SCREEN_MIN_Y = 24
SCREEN_NODE_TABLE_START = 2
SCREEN_NODE_LIST_START = 2
ALERT_TABLE_COLUMNS = (
{'name': 'lastReceiveId', 'label': 'alertid', 'width': 8, 'align': 'right'},
{'name': 'severity', 'label': 'severity', 'width': 9, 'align': 'right'},
{'name': 'status', 'label': 'status', 'width': 7, 'align': 'right'},
{'name': 'lastReceiveTime', 'label': 'time', 'width': 19, 'align': 'center'},
{'name': 'duplicateCount', 'label': 'dupl.', 'width': 5, 'align': 'right'},
{'name': 'environment', 'label': 'env', 'width': 8, 'align': 'center'},
{'name': 'service', 'label': 'service', 'width': 12, 'align': 'center'},
{'name': 'resource', 'label': 'resource', 'width': 20, 'align': 'center'},
{'name': 'group', 'label': 'group', 'width': 10, 'align': 'center'},
{'name': 'event', 'label': 'event', 'width': 15, 'align': 'center'},
{'name': 'value', 'label': 'value', 'width': 12, 'align': 'center'},
{'name': 'text', 'label': 'text', 'width': 54, 'align': 'left'},
)
TOTAL_WIDTH = float(8 + 9 + 7 + 19 + 5 + 40 + 10 + 15 + 12 + 54)
SCREEN_REDRAW_INTERVAL = 0.1
alerts = list()
class MessageHandler(object):
global alerts
def on_error(self, headers, body):
pass
def on_message(self, headers, body):
alert = dict()
alert = json.loads(body)
alerts.append(alert)
def on_disconnected(self):
global conn
print >>sys.err, 'Connection lost. Attempting auto-reconnect to %s', NOTIFY_TOPIC
conn.start()
conn.connect(wait=True)
# conn.subscribe(destination=NOTIFY_TOPIC, ack='auto', headers={'selector': "repeat = 'false'"})
conn.subscribe(destination=NOTIFY_TOPIC, ack='auto', headers={})
class Screen():
global alerts
def __init__(self):
self.screen = self._get_screen()
self.min_y = 0
self.min_x = 0
self.cursor_pos = -1
self.hide_repeat = False
def run(self):
while True:
self._redraw()
event = self.screen.getch()
if 0 < event < 256:
self._handle_key_event(chr(event))
elif event in [curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE, curses.KEY_NPAGE]:
self._handle_movement_key(event)
else:
self._handle_event(event)
time.sleep(SCREEN_REDRAW_INTERVAL)
def _get_screen(self):
screen = curses.initscr()
curses.noecho()
try:
curses.curs_set(0)
except Exception:
pass
if curses.has_colors():
curses.start_color()
curses.use_default_colors()
curses.init_pair(1, curses.COLOR_WHITE, -1)
curses.init_pair(2, curses.COLOR_GREEN, -1)
curses.init_pair(3, curses.COLOR_BLUE, -1)
curses.init_pair(4, curses.COLOR_RED, -1)
screen.keypad(1)
screen.nodelay(1)
curses.def_prog_mode()
return screen
def _draw_header(self):
time = datetime.datetime.strftime(datetime.datetime.now(), '%H:%M:%S %d/%m/%y')
self.addstr(self.min_y, 'center', __title__, curses.A_BOLD)
self.addstr(self.min_y, self.min_x, BROKER_LIST[0][0], curses.A_BOLD)
self.addstr(self.min_y, 'right', time, curses.A_BOLD)
self.screen.hline(self.min_y + 1, self.min_x, '_', self.max_x)
def _draw_table_header(self):
columns = []
for column in ALERT_TABLE_COLUMNS:
name, width, align = column['label'].upper(), column['width'], column['align']
column_string = self._get_table_column_string(name, width, align)
columns.append(column_string)
columns = '' . join(columns)
self.addstr(self.min_y + SCREEN_NODE_TABLE_START, self.min_x, columns, curses.A_REVERSE)
def _get_table_column_string(self, text, width, align):
# width = int(self.max_x * (width / 100.0))
width = int(self.max_x * (width / TOTAL_WIDTH))
if align == 'center':
column_string = text.center(width)
elif align == 'left':
column_string = text.ljust(width)
else:
column_string = text.rjust(width)
return column_string
def _draw_alert_list(self):
# Draws the node list in the bottom part of the screen
self.table_lines = []
index = 0
for alert in alerts:
if self.hide_repeat and alert['repeat']:
continue
coord_y = (self.min_y + SCREEN_NODE_LIST_START) + (index + 2)
index += 1
if coord_y < self.max_y - 1:
columns = []
for column in ALERT_TABLE_COLUMNS:
width, align = column['width'], column['align']
if column['name'] == 'lastReceiveTime':
lastReceiveTime = datetime.datetime.strptime(alert['lastReceiveTime'], '%Y-%m-%dT%H:%M:%S.%fZ')
lastReceiveTime = lastReceiveTime.replace(tzinfo=pytz.utc)
value = lastReceiveTime.strftime('%T %d/%m/%y')
elif column['name'] == 'lastReceiveId':
value = alert['lastReceiveId'][0:8]
elif column['name'] == 'environment':
value = ','.join(alert['environment'])
elif column['name'] == 'service':
value = ','.join(alert['service'])
else:
value = str(alert[column['name']])
column_string = self._get_table_column_string(value, width, align)
columns.append(column_string)
columns = '' . join(columns)
self.table_lines.append(columns)
self.addstr(coord_y, self.min_x, columns)
def _draw_footer(self):
self.addstr(self.max_y, self.min_x,
'Updated: now',
curses.A_BOLD)
self.addstr(self.max_y, 'center',
'Hide Repeats: %s' % (self.hide_repeat),
curses.A_BOLD)
self.addstr(self.max_y, 'right',
'Alerts: %d' %
(len(alerts)),
curses.A_BOLD)
def _redraw(self):
self._update_max_size()
self._check_min_screen_size()
self.screen.clear()
self._draw_header()
self._draw_table_header()
self._draw_alert_list()
#self._draw_selection()
self._draw_footer()
#self._draw_node_data()
#self.screen.refresh()
curses.doupdate()
def _update_max_size(self):
max_y, max_x = self.screen.getmaxyx()
self._max_y = max_y - 2
self._max_x = max_x
def _check_min_screen_size(self):
if self.max_x < SCREEN_MIN_X or \
self.max_y < SCREEN_MIN_Y:
raise RuntimeError('Minimum screen size must be %sx%s lines' % (SCREEN_MIN_X, SCREEN_MIN_Y))
def _reset(self):
# Resets the screen
self.screen.keypad(0)
curses.echo()
curses.nocbreak()
curses.endwin()
def _clean(self):
self.screen.erase()
# Event handlers
def _handle_key_event(self, key):
if key in 'u':
#self._update_node_metrics(update_now = True)
pass
elif key in 'rR':
self.hide_repeat = not self.hide_repeat
elif key in 'qQ':
exit_handler()
def _handle_movement_key(self, key):
# Highlight the corresponding node in the list
self.max_cursor_pos = len(self.table_lines) - 1
if key == curses.KEY_UP:
if self.cursor_pos > self.min_cursor_pos:
self.cursor_pos -= 1
elif key == curses.KEY_DOWN:
if self.cursor_pos < self.max_cursor_pos:
self.cursor_pos += 1
elif key == curses.KEY_PPAGE:
self.cursor_pos = 0
elif key == curses.KEY_NPAGE:
self.cursor_pos = self.max_cursor_pos
self._update_node_metrics(update_now = True)
def _handle_event(self, event):
if event == curses.KEY_RESIZE:
# Redraw the screen on resize
self._redraw()
# Helper methods
def addstr(self, y, x, str, attr = 0):
if x == 'center':
x = self._get_center_offset(self.max_x, len(str))
elif x == 'right':
x = self._get_right_offset(self.max_x, len(str))
if y == 'center':
y = self._get_center_offset(self.max_y, len(str))
elif y == 'right':
y = self._get_right_offset(self.max_y, len(str))
self.screen.addstr(y, x, str, attr)
# Properties
@property
def max_y(self):
return self._max_y
@property
def max_x(self):
return self._max_x
def _get_center_offset(self, max_offset, string_length):
return ((max_offset / 2) - string_length / 2)
def _get_right_offset(self, max_offset, string_length):
return (max_offset - string_length)
def exit_handler(*args):
screen._reset()
conn.disconnect()
sys.exit(0)
# Register exit signals
signal.signal(signal.SIGTERM, exit_handler)
signal.signal(signal.SIGINT, exit_handler)
def main():
global conn, screen
# Connect to message broker
try:
conn = stomp.Connection(BROKER_LIST)
conn.set_listener('', MessageHandler())
conn.start()
conn.connect(wait=True)
# conn.subscribe(destination=NOTIFY_TOPIC, ack='auto', headers={'selector': "repeat = 'false'"})
conn.subscribe(destination=NOTIFY_TOPIC, ack='auto', headers={})
except Exception, e:
logging.error('Stomp connection error: %s', e)
screen = Screen()
try:
screen.run()
except RuntimeError, e:
screen._reset()
print e
conn.disconnect()
sys.exit(1)
except Exception, e:
screen._reset()
print e
conn.disconnect()
sys.exit(1)
if __name__ == '__main__':
main()