Skip to content

Commit 3081ead

Browse files
committed
Add top offenders curses command
1 parent 10ef12c commit 3081ead

3 files changed

Lines changed: 182 additions & 37 deletions

File tree

alertaclient/api.py

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,67 +41,67 @@ def __init__(self, endpoint=None, key=None, token=None, timeout=5.0, ssl_verify=
4141
HTTPConnection.debuglevel = 1
4242

4343
key = key or os.environ.get('ALERTA_API_KEY', '')
44-
self.client = HTTPClient(self.endpoint, key, token, timeout, ssl_verify, debug)
44+
self.http = HTTPClient(self.endpoint, key, token, timeout, ssl_verify, debug)
4545

4646
# Alerts
4747
def send_alert(self, **kwargs):
48-
r = self.client.post('/alert', kwargs)
48+
r = self.http.post('/alert', kwargs)
4949
return Alert.parse(r['alert'])
5050

5151
def get_alert(self, id):
52-
return Alert.parse(self.client.get('/alert/%s' % id)['alert'])
52+
return Alert.parse(self.http.get('/alert/%s' % id)['alert'])
5353

5454
def set_status(self, id, status, text):
5555
data = {
5656
'status': status,
5757
'text': text
5858
}
59-
return self.client.put('/alert/%s/status' % id, data)
59+
return self.http.put('/alert/%s/status' % id, data)
6060

6161
def tag_alert(self, id, tags):
62-
return self.client.put('/alert/%s/tag' % id, {"tags": tags})
62+
return self.http.put('/alert/%s/tag' % id, {"tags": tags})
6363

6464
def untag_alert(self, id, tags):
65-
return self.client.put('/alert/%s/untag' % id, {"tags": tags})
65+
return self.http.put('/alert/%s/untag' % id, {"tags": tags})
6666

6767
def update_attributes(self, id, attributes):
6868
data = {
6969
'attributes': attributes
7070
}
71-
return self.client.put('/alert/%s/attributes' % id, data)
71+
return self.http.put('/alert/%s/attributes' % id, data)
7272

7373
def delete_alert(self, id):
74-
return self.client.delete('/alert/%s' % id)
74+
return self.http.delete('/alert/%s' % id)
7575

7676
def search(self, query=None):
7777
return self.get_alerts(query)
7878

7979
def get_alerts(self, query=None):
80-
r = self.client.get('/alerts', query)
80+
r = self.http.get('/alerts', query)
8181
return [Alert.parse(a) for a in r['alerts']]
8282

8383
def get_history(self, query=None):
84-
r = self.client.get('/alerts/history', query)
84+
r = self.http.get('/alerts/history', query)
8585
return [RichHistory.parse(a) for a in r['history']]
8686

8787
def get_count(self, query=None):
88-
counts = self.client.get('/alerts/count', query)
88+
counts = self.http.get('/alerts/count', query)
8989
return counts['total'], counts['severityCounts'], counts['statusCounts']
9090

9191
def get_top10_count(self, query=None):
92-
counts = self.client.get('/alerts/top10/count', query)
92+
counts = self.http.get('/alerts/top10/count', query)
9393
return counts['top10']
9494

9595
def get_top10_flapping(self, query=None):
96-
counts = self.client.get('/alerts/top10/flapping', query)
96+
counts = self.http.get('/alerts/top10/flapping', query)
9797
return counts['top10']
9898

9999
def get_environments(self, query=None):
100-
counts = self.client.get('/environments', query)
100+
counts = self.http.get('/environments', query)
101101
return counts['environments']
102102

103103
def get_services(self, query=None):
104-
counts = self.client.get('/services', query)
104+
counts = self.http.get('/services', query)
105105
return counts['services']
106106

107107
# Blackouts
@@ -116,46 +116,46 @@ def create_blackout(self, environment, service=None, resource=None, event=None,
116116
'startTime': start,
117117
'duration': duration
118118
}
119-
r = self.client.post('/blackout', data)
119+
r = self.http.post('/blackout', data)
120120
return Blackout.parse(r['blackout'])
121121

122122
def get_blackouts(self, query=None):
123-
r = self.client.get('/blackouts', query)
123+
r = self.http.get('/blackouts', query)
124124
return [Blackout.parse(b) for b in r['blackouts']]
125125

126126
def delete_blackout(self, id):
127-
return self.client.delete('/blackout/%s' % id)
127+
return self.http.delete('/blackout/%s' % id)
128128

129129
# Customers
130130
def create_customer(self, customer, match):
131131
data = {
132132
'customer': customer,
133133
'match': match
134134
}
135-
r = self.client.post('/customer', data)
135+
r = self.http.post('/customer', data)
136136
return Customer.parse(r['customer'])
137137

138138
def get_customers(self, query=None):
139-
r = self.client.get('/customers', query)
139+
r = self.http.get('/customers', query)
140140
return [Customer.parse(c) for c in r['customers']]
141141

142142
def delete_customer(self, id):
143-
return self.client.delete('/customer/%s' % id)
143+
return self.http.delete('/customer/%s' % id)
144144

145145
# Heartbeats
146146
def heartbeat(self, **kwargs):
147-
r = self.client.post('/heartbeat', kwargs)
147+
r = self.http.post('/heartbeat', kwargs)
148148
return Heartbeat.parse(r['heartbeat'])
149149

150150
def get_heartbeat(self, id):
151-
return Heartbeat.parse(self.client.get('/heartbeat/%s' % id)['heartbeat'])
151+
return Heartbeat.parse(self.http.get('/heartbeat/%s' % id)['heartbeat'])
152152

153153
def get_heartbeats(self, query=None):
154-
r = self.client.get('/heartbeats', query)
154+
r = self.http.get('/heartbeats', query)
155155
return [Heartbeat.parse(hb) for hb in r['heartbeats']]
156156

157157
def delete_heartbeat(self, id):
158-
return self.client.delete('/heartbeat/%s' % id)
158+
return self.http.delete('/heartbeat/%s' % id)
159159

160160
# API Keys
161161
def create_key(self, username, scopes=None, expires=None, text=''):
@@ -166,46 +166,46 @@ def create_key(self, username, scopes=None, expires=None, text=''):
166166
}
167167
if expires:
168168
data['expireTime'] = DateTime.iso8601(expires)
169-
r = self.client.post('/key', data)
169+
r = self.http.post('/key', data)
170170
return ApiKey.parse(r['data'])
171171

172172
def get_keys(self, query=None):
173-
r = self.client.get('/keys', query)
173+
r = self.http.get('/keys', query)
174174
return [ApiKey.parse(k) for k in r['keys']]
175175

176176
def delete_key(self, id):
177-
return self.client.delete('/key/%s' % id)
177+
return self.http.delete('/key/%s' % id)
178178

179179
# Permissions
180180
def create_perm(self, role, scopes):
181181
data = {
182182
'match': role,
183183
'scopes': scopes
184184
}
185-
r = self.client.post('/perm', data)
185+
r = self.http.post('/perm', data)
186186
return Permission.parse(r['permission'])
187187

188188
def get_perms(self, query=None):
189-
r = self.client.get('/perms', query)
189+
r = self.http.get('/perms', query)
190190
return [Permission.parse(p) for p in r['permissions']]
191191

192192
def delete_perm(self, id):
193-
return self.client.delete('/perm/%s' % id)
193+
return self.http.delete('/perm/%s' % id)
194194

195195
# Users
196196
def login(self, username, password):
197197
data = {
198198
'username': username,
199199
'password': password
200200
}
201-
r = self.client.post('/auth/login', data)
201+
r = self.http.post('/auth/login', data)
202202
if 'token' in r:
203203
return r['token']
204204
else:
205205
raise AuthError
206206

207207
def get_users(self, query=None):
208-
r = self.client.get('/users', query)
208+
r = self.http.get('/users', query)
209209
return [User.parse(u) for u in r['users']]
210210

211211
def update_user(self, id, **kwargs):
@@ -218,20 +218,20 @@ def update_user(self, id, **kwargs):
218218
'text': kwargs.get('text'),
219219
'email_verified': kwargs.get('email_verified')
220220
}
221-
return self.client.put('/user/{}'.format(id), data)
221+
return self.http.put('/user/{}'.format(id), data)
222222

223223
def update_user_attributes(self, id, attributes):
224224
pass
225225

226226
def delete_user(self, id):
227-
return self.client.delete('/user/%s' % id)
227+
return self.http.delete('/user/%s' % id)
228228

229229
def userinfo(self):
230-
return self.client.get('/userinfo')
230+
return self.http.get('/userinfo')
231231

232232
# Management
233233
def mgmt_status(self):
234-
return self.client.get('/management/status')
234+
return self.http.get('/management/status')
235235

236236

237237
class ApiAuth(AuthBase):

alertaclient/commands/cmd_top.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
import click
3+
4+
from alertaclient.top import Screen
5+
6+
7+
@click.command('top', short_help='Show top offenders and stats')
8+
@click.pass_obj
9+
def cli(obj):
10+
"""Display alerts like unix "top" command."""
11+
client = obj['client']
12+
13+
screen = Screen(client)
14+
screen.run()

alertaclient/top.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
2+
import curses
3+
import sys
4+
import time
5+
from curses import wrapper
6+
from datetime import datetime
7+
8+
from alertaclient.models.alert import Alert
9+
from alertaclient.utils import DateTime
10+
11+
12+
class Screen(object):
13+
14+
ALIGN_RIGHT = 'R'
15+
ALIGN_CENTRE = 'C'
16+
17+
def __init__(self, client):
18+
self.client = client
19+
20+
self.lines = None
21+
self.cols = None
22+
23+
def run(self):
24+
wrapper(self.main)
25+
26+
def main(self, stdscr):
27+
self.screen = stdscr
28+
29+
curses.use_default_colors()
30+
31+
curses.init_pair(1, curses.COLOR_RED, -1)
32+
curses.init_pair(2, curses.COLOR_MAGENTA, -1)
33+
curses.init_pair(3, curses.COLOR_YELLOW, -1)
34+
curses.init_pair(4, curses.COLOR_BLUE, -1)
35+
curses.init_pair(5, curses.COLOR_CYAN, -1)
36+
curses.init_pair(6, curses.COLOR_GREEN, -1)
37+
curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK)
38+
39+
COLOR_RED = curses.color_pair(1)
40+
COLOR_MAGENTA = curses.color_pair(2)
41+
COLOR_YELLOW = curses.color_pair(3)
42+
COLOR_BLUE = curses.color_pair(4)
43+
COLOR_CYAN = curses.color_pair(5)
44+
COLOR_GREEN = curses.color_pair(6)
45+
COLOR_BLACK = curses.color_pair(7)
46+
47+
self.SEVERITY_MAP = {
48+
'critical': ["Crit", COLOR_RED],
49+
'major': ["Majr", COLOR_MAGENTA],
50+
'minor': ["Minr", COLOR_YELLOW],
51+
'warning': ["Warn", COLOR_BLUE],
52+
'indeterminate': ["Ind ", COLOR_CYAN],
53+
'cleared': ["Clr", COLOR_GREEN],
54+
'normal': ["Norm", COLOR_GREEN],
55+
'inform': ["Info", COLOR_GREEN],
56+
'ok': ["Ok", COLOR_GREEN],
57+
'debug': ["Dbug", COLOR_BLACK],
58+
'auth': ["Sec", COLOR_BLACK],
59+
'unknown': ["Unkn", COLOR_BLACK]
60+
}
61+
62+
self.screen.keypad(1)
63+
self.screen.nodelay(1)
64+
65+
while True:
66+
self.update()
67+
event = self.screen.getch()
68+
if 0 < event < 256:
69+
self._key_press(chr(event))
70+
else:
71+
if event == curses.KEY_RESIZE:
72+
self.update()
73+
time.sleep(2)
74+
75+
def update(self):
76+
self.lines, self.cols = self.screen.getmaxyx()
77+
self.screen.clear()
78+
79+
now = datetime.utcnow()
80+
status = self.client.mgmt_status()
81+
version = status['version']
82+
83+
# draw header
84+
self._addstr(0, 0, self.client.endpoint, curses.A_BOLD)
85+
self._addstr(0, 'C', 'alerta {}'.format(version), curses.A_BOLD)
86+
self._addstr(0, 'R', '{}'.format(now.strftime('%H:%M:%S %d/%m/%y')), curses.A_BOLD)
87+
88+
# draw bars
89+
90+
# draw alerts
91+
self._addstr(2, 1, 'Sev. Time Dupl. Env. Service Resource Event Value ', curses.A_UNDERLINE)
92+
93+
def short_sev(severity):
94+
return self.SEVERITY_MAP[severity][0]
95+
96+
def color(severity):
97+
return self.SEVERITY_MAP[severity][1]
98+
99+
r = self.client.http.get('/alerts')
100+
alerts = [Alert.parse(a) for a in r['alerts']]
101+
last_time = DateTime.parse(r['lastTime'])
102+
for i, alert in enumerate(alerts):
103+
self._addstr(i+3, 1, '{0:<4} {1} {2:5d} {3:<12} {4:<12} {5:<12.12} {6:<12.12} {7:<6.6}'.format(
104+
short_sev(alert.severity),
105+
alert.last_receive_time.strftime('%H:%M:%S'),
106+
alert.duplicate_count,
107+
alert.environment,
108+
','.join(alert.service),
109+
alert.resource,
110+
alert.event,
111+
alert.value
112+
), color(alert.severity))
113+
114+
# draw footer
115+
self._addstr(self.lines - 1, 0, 'Last Update: {}'.format(last_time.strftime('%H:%M:%S')), curses.A_BOLD)
116+
self._addstr(self.lines - 1, 'C', '{} - {}'.format(r['status'], r.get('message', 'no errors')), curses.A_BOLD)
117+
self._addstr(self.lines - 1, 'R', 'Count: {}'.format(r['total']), curses.A_BOLD)
118+
119+
self.screen.refresh()
120+
121+
def _addstr(self, y, x, str, attr=0):
122+
if x == self.ALIGN_RIGHT:
123+
x = self.cols - len(str) - 1
124+
if x == self.ALIGN_CENTRE:
125+
x = int((self.cols / 2) - len(str) / 2)
126+
127+
self.screen.addstr(y, x, str, attr)
128+
129+
def _key_press(self, key):
130+
if key in 'qQ':
131+
sys.exit(0)

0 commit comments

Comments
 (0)