vigiboard / vigiboard / controllers / vigiboardrequest.py @ c1ce3d6a
History | View | Annotate | Download (15.8 KB)
1 | 19e88cb8 | Thomas ANDREJAK | # -*- coding: utf-8 -*-
|
---|---|---|---|
2 | # vim:set expandtab tabstop=4 shiftwidth=4:
|
||
3 | """Gestion de la requête, des plugins et de l'affichage du Vigiboard"""
|
||
4 | |||
5 | 8484b8bd | Francois POIROTTE | from vigiboard.model import Event, EventsAggregate, EventHistory, \ |
6 | bc94248f | Francois POIROTTE | Host, HostGroup, Service, ServiceGroup |
7 | from tg import tmpl_context, url, config |
||
8 | 02503aef | Gabriel DE PERTHUIS | from vigiboard.model import DBSession |
9 | 19e88cb8 | Thomas ANDREJAK | from sqlalchemy import not_ , and_ , asc , desc |
10 | c5382d25 | Gabriel DE PERTHUIS | from tw.jquery.ui_dialog import JQueryUIDialog |
11 | 19e88cb8 | Thomas ANDREJAK | from vigiboard.widgets.edit_event import EditEventForm , SearchForm |
12 | from vigiboard.controllers.vigiboard_plugin import VigiboardRequestPlugin |
||
13 | from pylons.i18n import ugettext as _ |
||
14 | |||
15 | class VigiboardRequest(): |
||
16 | |||
17 | """
|
||
18 | Classe gérant la génération de la requête finale,
|
||
19 | le préformatage des évènements et celui des historiques
|
||
20 | """
|
||
21 | |||
22 | bc94248f | Francois POIROTTE | def __init__(self, user): |
23 | 19e88cb8 | Thomas ANDREJAK | |
24 | """
|
||
25 | Initialisation de toutes les variables nécessaires: Liste des groupes de
|
||
26 | l'utilisateur, les classes à appliquer suivant la sévérité, les
|
||
27 | différentes étapes de la génération de la requête et la liste des
|
||
28 | plugins appliqués.
|
||
29 | """
|
||
30 | |||
31 | bc94248f | Francois POIROTTE | self.user_groups = user.groups
|
32 | 19e88cb8 | Thomas ANDREJAK | |
33 | 8484b8bd | Francois POIROTTE | self.bouton_severity = (
|
34 | 'Minor', 'Minor', 'Minor', 'Minor', |
||
35 | 'Minor', 'Minor', 'Major', 'Critical' |
||
36 | ) |
||
37 | |||
38 | self.class_severity = (
|
||
39 | 'None', 'None', 'None', 'None', |
||
40 | 'None', 'Minor', 'Major', 'Critical' |
||
41 | ) |
||
42 | |||
43 | self.severity = (
|
||
44 | _('None'), # 0 |
||
45 | _('OK'),
|
||
46 | _('Suppressed'),
|
||
47 | _('Initial'),
|
||
48 | _('Maintenance'),
|
||
49 | _('Minor'),
|
||
50 | _('Major'),
|
||
51 | _('Critical'), # 7 |
||
52 | ) |
||
53 | |||
54 | self.class_ack = {
|
||
55 | 'Acknowledged': 'Ack', |
||
56 | 'None': '', |
||
57 | 'AAClosed': 'Ack' |
||
58 | } |
||
59 | 19e88cb8 | Thomas ANDREJAK | |
60 | self.generaterq = False |
||
61 | 8484b8bd | Francois POIROTTE | |
62 | self.table = [EventsAggregate]
|
||
63 | |||
64 | self.join = [
|
||
65 | (Event, EventsAggregate.idcause == Event.idevent), |
||
66 | (Host, Event.hostname == Host.name), |
||
67 | (Service, Event.servicename == Service.name), |
||
68 | (HostGroup, Host.name == HostGroup.hostname), |
||
69 | (ServiceGroup, Service.name == ServiceGroup.servicename), |
||
70 | ] |
||
71 | |||
72 | 19e88cb8 | Thomas ANDREJAK | self.outerjoin = []
|
73 | 8484b8bd | Francois POIROTTE | |
74 | self.filter = [
|
||
75 | HostGroup.groupname.in_(self.user_groups),
|
||
76 | ServiceGroup.groupname.in_(self.user_groups),
|
||
77 | not_(and_(Event.active == False,
|
||
78 | EventsAggregate.status == 'AAClosed')),
|
||
79 | EventsAggregate.timestamp_active != None#, |
||
80 | #not_(Event.timestamp_active.like('0000-00-00 00:00:00'))
|
||
81 | ] |
||
82 | |||
83 | self.orderby = [
|
||
84 | desc(EventsAggregate.status), |
||
85 | desc(Event.active), |
||
86 | desc(EventsAggregate.severity), |
||
87 | asc(Event.hostname), |
||
88 | desc(Event.timestamp), |
||
89 | ] |
||
90 | |||
91 | self.groupby = [
|
||
92 | EventsAggregate.idaggregate, |
||
93 | EventsAggregate, |
||
94 | bc94248f | Francois POIROTTE | Event.active, |
95 | Event.hostname, |
||
96 | Event.timestamp, |
||
97 | 8484b8bd | Francois POIROTTE | ] |
98 | |||
99 | 19e88cb8 | Thomas ANDREJAK | self.plugin = []
|
100 | self.events = []
|
||
101 | self.idevents = []
|
||
102 | self.hist = []
|
||
103 | self.req = DBSession
|
||
104 | 8ad24667 | Thomas ANDREJAK | self.context_fct = []
|
105 | 19e88cb8 | Thomas ANDREJAK | |
106 | def add_plugin(self, *argv): |
||
107 | bc94248f | Francois POIROTTE | |
108 | 19e88cb8 | Thomas ANDREJAK | """
|
109 | Ajout d'un plugin, on lui prélève ses ajouts dans la requête
|
||
110 | """
|
||
111 | for i in argv : |
||
112 | if isinstance(i, VigiboardRequestPlugin): |
||
113 | if i.table :
|
||
114 | self.add_table(*i.table)
|
||
115 | if i.join :
|
||
116 | self.add_join(*i.join)
|
||
117 | if i.outerjoin :
|
||
118 | self.add_outer_join(*i.outerjoin)
|
||
119 | if i.filter :
|
||
120 | self.add_filter(*i.filter)
|
||
121 | if i.groupby :
|
||
122 | self.add_group_by(*i.groupby)
|
||
123 | if i.orderby :
|
||
124 | self.add_order_by(*i.orderby)
|
||
125 | self.plugin.append(i)
|
||
126 | |||
127 | def generate_request(self): |
||
128 | |||
129 | """
|
||
130 | Génération de la requête avec l'ensemble des données stockées
|
||
131 | et la place dans la variable rq de la classe
|
||
132 | """
|
||
133 | 0f56fff9 | Francois POIROTTE | for plug in config.get('vigiboard_plugins', []): |
134 | 19e88cb8 | Thomas ANDREJAK | try:
|
135 | mypac = __import__(
|
||
136 | 'vigiboard.controllers.vigiboard_plugin.' +\
|
||
137 | plug[0],globals(), locals(), [plug[1]],-1) |
||
138 | b8500d1a | Thomas ANDREJAK | self.add_plugin(getattr(mypac, plug[1])()) |
139 | 19e88cb8 | Thomas ANDREJAK | except:
|
140 | raise
|
||
141 | bc94248f | Francois POIROTTE | |
142 | 19e88cb8 | Thomas ANDREJAK | # query et join ont besoin de referrence
|
143 | self.req = self.req.query(*self.table) |
||
144 | self.req = self.req.join(*self.join) |
||
145 | |||
146 | # le reste, non
|
||
147 | for i in self.outerjoin: |
||
148 | self.req = self.req.outerjoin(i) |
||
149 | for i in self.filter: |
||
150 | self.req = self.req.filter(i) |
||
151 | for i in self.groupby: |
||
152 | self.req = self.req.group_by(i) |
||
153 | for i in self.orderby: |
||
154 | self.req = self.req.order_by(i) |
||
155 | |||
156 | def num_rows(self): |
||
157 | |||
158 | """
|
||
159 | Retourne le nombre de lignes de la requête.
|
||
160 | Si celle-ci n'est pas encore générée, on le fait.
|
||
161 |
|
||
162 | @return: Nombre de ligne
|
||
163 | """
|
||
164 | |||
165 | if not self.generaterq : |
||
166 | self.generate_request()
|
||
167 | self.generaterq = True |
||
168 | return self.req.count() |
||
169 | |||
170 | def add_table(self, *argv): |
||
171 | |||
172 | """
|
||
173 | Ajoute une ou plusieurs tables/élément d'une table à
|
||
174 | la requête.
|
||
175 |
|
||
176 | @param argv: Liste des tables à ajouter
|
||
177 | """
|
||
178 | |||
179 | 8484b8bd | Francois POIROTTE | # On vérifie qu'il n'y a pas de doublons dans la liste finale
|
180 | # des tables.
|
||
181 | 19e88cb8 | Thomas ANDREJAK | |
182 | for i in argv : |
||
183 | for j in self.table: |
||
184 | if str(i) == str(j): |
||
185 | break
|
||
186 | self.table.append(i)
|
||
187 | |||
188 | def add_join(self, *argv): |
||
189 | |||
190 | """
|
||
191 | Ajoute une ou plusieurs jointures à
|
||
192 | la requête.
|
||
193 |
|
||
194 | @param argv: Liste des jointures à ajouter
|
||
195 | """
|
||
196 | |||
197 | 8484b8bd | Francois POIROTTE | # On vérifie qu'il n'y a pas de doublons dans la liste finale
|
198 | # des jointures.
|
||
199 | 19e88cb8 | Thomas ANDREJAK | |
200 | for i in argv: |
||
201 | for j in self.join: |
||
202 | if str(i) == str(j): |
||
203 | break
|
||
204 | self.join.append(i)
|
||
205 | |||
206 | def add_outer_join(self, *argv): |
||
207 | |||
208 | """
|
||
209 | Ajoute une ou plusieurs jointures externes à
|
||
210 | la requête.
|
||
211 |
|
||
212 | @param argv: Liste des jointures externes à ajouter
|
||
213 | """
|
||
214 | |||
215 | 8484b8bd | Francois POIROTTE | # On vérifie qu'il n'y a pas de doublons dans la liste finale
|
216 | # des jointures externes.
|
||
217 | 19e88cb8 | Thomas ANDREJAK | |
218 | for i in argv: |
||
219 | for j in self.outerjoin: |
||
220 | if str(i) == str(j): |
||
221 | break
|
||
222 | self.outerjoin.append(i)
|
||
223 | |||
224 | def add_filter(self, *argv): |
||
225 | |||
226 | """
|
||
227 | Ajoute un ou plusieurs filtres à la requête.
|
||
228 |
|
||
229 | @param argv: Liste des filtres à ajouter
|
||
230 | """
|
||
231 | |||
232 | 8484b8bd | Francois POIROTTE | # On vérifie qu'il n'y a pas de doublons dans la liste finale
|
233 | # des filtres.
|
||
234 | 19e88cb8 | Thomas ANDREJAK | |
235 | for i in argv: |
||
236 | for j in self.filter: |
||
237 | if str(i) == str(j): |
||
238 | break
|
||
239 | self.filter.append(i)
|
||
240 | |||
241 | def add_group_by(self, *argv): |
||
242 | |||
243 | """
|
||
244 | Ajoute un ou plusieurs groupements à la requête.
|
||
245 |
|
||
246 | @param argv: Liste des groupements à ajouter
|
||
247 | """
|
||
248 | |||
249 | 8484b8bd | Francois POIROTTE | # On vérifie qu'il n'y a pas de doublons dans la liste finale
|
250 | # des groupements.
|
||
251 | 19e88cb8 | Thomas ANDREJAK | |
252 | for i in argv: |
||
253 | for j in self.groupby: |
||
254 | if str(i) == str(j): |
||
255 | break
|
||
256 | self.groupby.append(i)
|
||
257 | |||
258 | def add_order_by(self, *argv): |
||
259 | |||
260 | """
|
||
261 | Ajoute un ou plusieurs orders à la requête.
|
||
262 |
|
||
263 | @param argv: Liste des ordres à ajouter
|
||
264 | """
|
||
265 | |||
266 | 8484b8bd | Francois POIROTTE | # On vérifie qu'il n'y a pas de doublons dans la liste finale
|
267 | # des ordres.
|
||
268 | 19e88cb8 | Thomas ANDREJAK | |
269 | for i in argv: |
||
270 | for j in self.orderby: |
||
271 | if str(i) == str(j): |
||
272 | break
|
||
273 | self.orderby.append(i)
|
||
274 | |||
275 | 8484b8bd | Francois POIROTTE | def format_events_img_status(self, event): |
276 | 19e88cb8 | Thomas ANDREJAK | |
277 | """
|
||
278 | Suivant l'état de l'évènement, retourne la classe à appliquer
|
||
279 | à l'image indiquant si l'évènement est pris en compte ou non.
|
||
280 |
|
||
281 | @param event: l'évènement à analyser
|
||
282 |
|
||
283 | @return: Dictionnaire représentant la classe à appliquer
|
||
284 | """
|
||
285 | |||
286 | 8484b8bd | Francois POIROTTE | if event.cause.active and event.status == 'AAClosed': |
287 | 19e88cb8 | Thomas ANDREJAK | return { 'src': url('/images/crossed.png') } |
288 | elif event.status == 'Acknowledged' : |
||
289 | return { 'src': url('/images/checked.png') } |
||
290 | else:
|
||
291 | return None |
||
292 | |||
293 | def format_events(self, first_row, last_row): |
||
294 | """
|
||
295 | Formate la réponse de la requête et y applique les plugins
|
||
296 | pour un affichage simple du résultat par Genshi.
|
||
297 | On génère une liste de liste, chaqu'une étant la description de
|
||
298 | l'affichage pour un évènement donné.
|
||
299 |
|
||
300 | @param first_row: Indice de début de la liste des évènements
|
||
301 | @param last_row: Indice de fin de la liste des évènements
|
||
302 | """
|
||
303 | |||
304 | # Si la requête n'est pas générée, on le fait
|
||
305 | if not self.generaterq : |
||
306 | self.generate_request()
|
||
307 | self.generaterq = True |
||
308 | |||
309 | # Liste des éléments pour la tête du tableau
|
||
310 | |||
311 | b8500d1a | Thomas ANDREJAK | lst_title = [ |
312 | ['',{}],
|
||
313 | [_('Date')+ '<span style="font-weight:normal">' + \ |
||
314 | a7dfbba0 | Francois POIROTTE | '<br />['+_('Duration') + ']</span>', |
315 | b8500d1a | Thomas ANDREJAK | {'style':'text-align:left'}], |
316 | a7dfbba0 | Francois POIROTTE | ['#', {'title':_('Occurrence count')}], |
317 | b8500d1a | Thomas ANDREJAK | [_('Host'), {'style':'text-align:left'}], |
318 | a7dfbba0 | Francois POIROTTE | [_('Service Type')+'<br />'+_('Service Name'), |
319 | b8500d1a | Thomas ANDREJAK | {'style':'text-align:left'}], |
320 | [_('Output'), {'style':'text-align:left'}] |
||
321 | ] |
||
322 | lst_title.extend([[plug.name, plug.style] for plug in self.plugin]) |
||
323 | a7dfbba0 | Francois POIROTTE | lst_title.extend([['['+_('TT')+']', {'title': _('Trouble Ticket')}], |
324 | b8500d1a | Thomas ANDREJAK | ['', {}]])
|
325 | 19e88cb8 | Thomas ANDREJAK | events = [lst_title] |
326 | i = 0
|
||
327 | class_tr = ['odd', 'even'] |
||
328 | ids = [] |
||
329 | for req in self.req[first_row : last_row]: |
||
330 | # Si il y a plus d'un élément dans la liste des tables,
|
||
331 | # rq devient une liste plutôt que d'être directement la
|
||
332 | # table souhaité
|
||
333 | |||
334 | 8484b8bd | Francois POIROTTE | if isinstance(req, EventsAggregate) : |
335 | 19e88cb8 | Thomas ANDREJAK | event = req |
336 | 8484b8bd | Francois POIROTTE | else:
|
337 | 19e88cb8 | Thomas ANDREJAK | event = req[0]
|
338 | 8484b8bd | Francois POIROTTE | ids.append(event.idcause) |
339 | 19e88cb8 | Thomas ANDREJAK | |
340 | # La liste pour l'évènement actuel comporte dans l'ordre :
|
||
341 | bc94248f | Francois POIROTTE | # L'évènement en lui-même
|
342 | 19e88cb8 | Thomas ANDREJAK | # La classe à appliquer sur la ligne (permet d'alterner les
|
343 | # couleurs suivant les lignes)
|
||
344 | # La classe pour la case comportant la flèche de détails
|
||
345 | bc94248f | Francois POIROTTE | # La classe pour la date, l'occurence et l'édition
|
346 | 8484b8bd | Francois POIROTTE | # L'image à afficher pour la flèche de détails
|
347 | 19e88cb8 | Thomas ANDREJAK | # Une liste (une case par plugin) de ce que le plugin souhaite
|
348 | # afficher en fonction de l'évènement
|
||
349 | |||
350 | 8484b8bd | Francois POIROTTE | if event.cause.active:
|
351 | 19e88cb8 | Thomas ANDREJAK | events.append([ |
352 | 8484b8bd | Francois POIROTTE | event, |
353 | bc94248f | Francois POIROTTE | {'class': class_tr[i % 2]}, |
354 | 8484b8bd | Francois POIROTTE | {'class' : self.bouton_severity[event.severity] + \ |
355 | self.class_ack[event.status]},
|
||
356 | {'class' : self.bouton_severity[event.severity] + \ |
||
357 | self.class_ack[event.status]},
|
||
358 | {'src' : '/images/%s2.png' % \ |
||
359 | self.bouton_severity[event.severity].upper()},
|
||
360 | self.format_events_img_status(event),
|
||
361 | [[j.__show__(req), j.style] for j in self.plugin] |
||
362 | 19e88cb8 | Thomas ANDREJAK | ]) |
363 | bc94248f | Francois POIROTTE | |
364 | 8484b8bd | Francois POIROTTE | else:
|
365 | 19e88cb8 | Thomas ANDREJAK | events.append([ |
366 | 8484b8bd | Francois POIROTTE | event, |
367 | bc94248f | Francois POIROTTE | {'class': class_tr[i % 2]}, |
368 | 8484b8bd | Francois POIROTTE | {'class' : self.bouton_severity[event.severity] + \ |
369 | self.class_ack[event.status] },
|
||
370 | {'class' : 'Cleared' + self.class_ack[event.status] }, |
||
371 | {'src' : '/images/%s2.png' % \ |
||
372 | self.bouton_severity[event.severity].upper()},
|
||
373 | self.format_events_img_status(event),
|
||
374 | [[j.__show__(req), j.style] for j in self.plugin] |
||
375 | 19e88cb8 | Thomas ANDREJAK | ]) |
376 | 8484b8bd | Francois POIROTTE | i += 1
|
377 | 19e88cb8 | Thomas ANDREJAK | |
378 | bc94248f | Francois POIROTTE | # On sauvegarde la liste précédemment créée puis on remplit
|
379 | 19e88cb8 | Thomas ANDREJAK | # le TmplContext
|
380 | self.events = events
|
||
381 | self.idevents = ids
|
||
382 | |||
383 | 8484b8bd | Francois POIROTTE | def format_history(self): |
384 | 19e88cb8 | Thomas ANDREJAK | |
385 | """
|
||
386 | Formate les historiques correspondant aux évènements sélectionnés
|
||
387 | pour un affichage simple du résultat par Genshi.
|
||
388 | 8484b8bd | Francois POIROTTE | On génère une liste de liste, chaqu'une étant la description
|
389 | de l'affichage pour un historique donné.
|
||
390 | 19e88cb8 | Thomas ANDREJAK | """
|
391 | |||
392 | history = DBSession.query(EventHistory |
||
393 | ).filter(EventHistory.idevent.in_(self.idevents)
|
||
394 | ).order_by(desc(EventHistory.timestamp) |
||
395 | ).order_by(desc(EventHistory.idhistory)) |
||
396 | if history.count() == 0: |
||
397 | 693e96f1 | Thomas ANDREJAK | self.hist = {}
|
398 | for i in self.idevents: |
||
399 | self.hist[i] = []
|
||
400 | 19e88cb8 | Thomas ANDREJAK | return
|
401 | 6892ebac | Thomas ANDREJAK | hists = {} |
402 | 19e88cb8 | Thomas ANDREJAK | i = 0
|
403 | class_tr = ['odd', 'even'] |
||
404 | 6892ebac | Thomas ANDREJAK | hist_tmp = [] |
405 | last_idevent = history[0].idevent
|
||
406 | 19e88cb8 | Thomas ANDREJAK | for hist in history : |
407 | 6892ebac | Thomas ANDREJAK | |
408 | if last_idevent != hist.idevent:
|
||
409 | hists[last_idevent] = hist_tmp |
||
410 | last_idevent = hist.idevent |
||
411 | hist_tmp = [] |
||
412 | 19e88cb8 | Thomas ANDREJAK | |
413 | # La liste pour l'historique actuel comporte dans l'ordre :
|
||
414 | # Le moment où il a été généré
|
||
415 | # Qui l'a généré
|
||
416 | # Le type d'action qui a été appliqué
|
||
417 | # La sévérité de l'action si besoin est
|
||
418 | # Le détail de l'action
|
||
419 | # La classe à appliquer à la ligne (permet d'alterner
|
||
420 | # les couleurs)
|
||
421 | # La classe de la sévérité s'il y a
|
||
422 | |||
423 | if hist.value :
|
||
424 | 6892ebac | Thomas ANDREJAK | hist_tmp.append([ |
425 | 19e88cb8 | Thomas ANDREJAK | hist.timestamp, |
426 | hist.username, |
||
427 | hist.type_action, |
||
428 | self.severity[min(int(hist.value),7)], |
||
429 | hist.text, |
||
430 | bc94248f | Francois POIROTTE | {'class': class_tr[i%2]}, |
431 | {'class': self.class_severity[min(int(hist.value),7)]} |
||
432 | 19e88cb8 | Thomas ANDREJAK | ]) |
433 | bc94248f | Francois POIROTTE | |
434 | 19e88cb8 | Thomas ANDREJAK | else:
|
435 | 6892ebac | Thomas ANDREJAK | hist_tmp.append([ |
436 | 19e88cb8 | Thomas ANDREJAK | hist.timestamp, |
437 | hist.username, |
||
438 | hist.type_action, |
||
439 | self.severity[0], |
||
440 | hist.text, |
||
441 | bc94248f | Francois POIROTTE | {'class': class_tr[i%2]}, |
442 | {'class': self.class_severity[0]} |
||
443 | 19e88cb8 | Thomas ANDREJAK | ]) |
444 | 8484b8bd | Francois POIROTTE | i += 1
|
445 | 19e88cb8 | Thomas ANDREJAK | |
446 | 6892ebac | Thomas ANDREJAK | hists[last_idevent] = hist_tmp |
447 | 19e88cb8 | Thomas ANDREJAK | self.hist = hists
|
448 | |||
449 | def generate_tmpl_context(self): |
||
450 | |||
451 | """
|
||
452 | Génère et peuple la variable tmpl_context avec les Dialogs et
|
||
453 | formulaires nécessaire au fonctionnement de Vigiboard
|
||
454 | """
|
||
455 | |||
456 | # Dialogue d'édition
|
||
457 | tmpl_context.edit_event_form = EditEventForm('edit_event_form',
|
||
458 | action=url('/update'))
|
||
459 | tmpl_context.edit_eventdialog = JQueryUIDialog(id='Edit_EventsDialog',
|
||
460 | autoOpen=False,title=_('Edit Event')) |
||
461 | |||
462 | # Dialogue de recherche
|
||
463 | tmpl_context.search_form = SearchForm('search_form',
|
||
464 | action=url('/'))
|
||
465 | tmpl_context.searchdialog = JQueryUIDialog(id='SearchDialog',
|
||
466 | bc94248f | Francois POIROTTE | autoOpen=False, title=_('Search Event')) |
467 | 19e88cb8 | Thomas ANDREJAK | |
468 | # Dialogue de détail d'un évènement
|
||
469 | tmpl_context.historydialog = JQueryUIDialog(id='HistoryDialog',
|
||
470 | bc94248f | Francois POIROTTE | autoOpen=False, title=_('History')) |
471 | 8ad24667 | Thomas ANDREJAK | |
472 | # Exécution des contexts des plugins
|
||
473 | for j in self.plugin: |
||
474 | j.context(self.context_fct)
|