vigiboard / vigiboard / controllers / vigiboard_ctl / vigiboardrequest.py @ 20367931
History | View | Annotate | Download (14.5 KB)
1 |
# -*- 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 |
from vigiboard.model.vigiboard_bdd import * |
6 |
from tg import request, tmpl_context, url |
7 |
from vigiboard.model import DBSession |
8 |
from sqlalchemy import not_ , and_ , asc , desc, or_, sql |
9 |
from tw.jquery import JQueryUIDialog |
10 |
from vigiboard.widgets.edit_event import Edit_Event_Form , Search_Form |
11 |
from vigiboard.controllers.vigiboard_ctl.userutils import GetUserGroups |
12 |
from pylons.i18n import ugettext as _, lazy_ugettext as l_ |
13 |
|
14 |
class VigiboardRequest(): |
15 |
|
16 |
"""
|
17 |
Classe gérant la génération de la requête finale,
|
18 |
le préformatage des évènements et celui des historiques
|
19 |
"""
|
20 |
|
21 |
def __init__(self): |
22 |
|
23 |
"""
|
24 |
Initialisation de toutes les variables nécessaires: Liste des groupes de
|
25 |
l'utilisateur, les classes à appliquer suivant la sévérité, les différentes
|
26 |
étapes de la génération de la requête et la liste des plugins appliqués.
|
27 |
"""
|
28 |
|
29 |
self.user_groups = GetUserGroups()
|
30 |
self.bouton_severity = { 0 : 'Minor' , 1 : 'Minor', 2 : 'Minor', 3 : 'Minor', 4 : 'Minor', 5 : 'Minor' , 6 : 'Major' , 7 : 'Critical' } |
31 |
self.class_severity = { 0 : 'None' , 1 : 'None', 2 : 'None', 3 : 'None', 4 : 'None', 5 : 'Minor' , 6 : 'Major' , 7 : 'Critical' } |
32 |
self.severity = { 0 : _('None') , 1 : _('OK'), 2 : _('Suppressed'), 3 : _('Initial'), 4 : _('Maintenance'), 5 : _('Minor') , 6 : _('Major') , 7 : _('Critical') } |
33 |
|
34 |
self.class_ack = { 'Acknowledged' : 'Ack' , 'None' : '' , 'AAClosed' : 'Ack' } |
35 |
|
36 |
self.generaterq= False |
37 |
self.table = [Events]
|
38 |
self.join = [( Host, Events.hostname == Host.name ),
|
39 |
( Service, Events.servicename == Service.name ), |
40 |
( HostGroups , Host.name == HostGroups.hostname ), |
41 |
( ServiceGroups , Service.name == ServiceGroups.servicename )] |
42 |
self.outerjoin = []
|
43 |
self.filter = [HostGroups.groupname.in_(self.user_groups), |
44 |
ServiceGroups.groupname.in_(self.user_groups),
|
45 |
not_(and_(Events.active == False,Events.status == 'AAClosed')), |
46 |
Events.timestamp_active != None,
|
47 |
not_(Events.timestamp_active.like('0000-00-00 00:00:00'))]
|
48 |
self.orderby = [asc(Events.status),
|
49 |
desc(Events.active), |
50 |
desc(Events.severity), |
51 |
asc(Events.hostname), |
52 |
desc(Events.timestamp)] |
53 |
self.groupby = []
|
54 |
self.plugin = []
|
55 |
|
56 |
def AddPlugin(self,*argv): |
57 |
|
58 |
"""
|
59 |
Ajout d'un plugin, on lui prélève ses ajouts dans la requête
|
60 |
"""
|
61 |
for i in argv : |
62 |
if isinstance(i,VigiboardRequestPlugin): |
63 |
self.AddTable(*i.table)
|
64 |
self.AddJoin(*i.join)
|
65 |
self.AddOuterJoin(*i.outerjoin)
|
66 |
self.AddFilter(*i.filter)
|
67 |
self.AddGroupBy(*i.groupby)
|
68 |
self.AddOrderBy(*i.orderby)
|
69 |
self.plugin.append(i)
|
70 |
|
71 |
def GenerateRequest(self): |
72 |
|
73 |
"""
|
74 |
Génération de la requête avec l'ensemble des données stockées
|
75 |
et la place dans la variable rq de la classe
|
76 |
"""
|
77 |
|
78 |
# query et join ont besoin de referrence
|
79 |
self.rq = DBSession.query(*self.table) |
80 |
self.rq = self.rq.join(*self.join) |
81 |
|
82 |
# le reste, non
|
83 |
for i in self.outerjoin: |
84 |
self.rq = self.rq.outerjoin(i) |
85 |
for i in self.filter: |
86 |
self.rq = self.rq.filter(i) |
87 |
for i in self.groupby: |
88 |
self.rq = self.rq.group_by(i) |
89 |
for i in self.orderby: |
90 |
self.rq = self.rq.order_by(i) |
91 |
|
92 |
def NumRows(self): |
93 |
|
94 |
"""
|
95 |
Retourne le nombre de lignes de la requête.
|
96 |
Si celle-ci n'est pas encore générée, on le fait.
|
97 |
|
98 |
@return: Nombre de ligne
|
99 |
"""
|
100 |
|
101 |
if not self.generaterq : |
102 |
self.GenerateRequest()
|
103 |
self.generaterq = True |
104 |
return self.rq.count() |
105 |
|
106 |
def AddTable(self,*argv): |
107 |
|
108 |
"""
|
109 |
Ajoute une ou plusieurs tables/élément d'une table à
|
110 |
la requête.
|
111 |
|
112 |
@param argv: Liste des tables à ajouter
|
113 |
"""
|
114 |
|
115 |
#On vérifi qu'il n'y a pas de doublons dans la liste des
|
116 |
#tables finale
|
117 |
|
118 |
for i in argv : |
119 |
for j in self.table: |
120 |
if str(i) == str(j): |
121 |
break
|
122 |
self.table.append(i)
|
123 |
|
124 |
def AddJoin(self,*argv): |
125 |
|
126 |
"""
|
127 |
Ajoute une ou plusieurs jointures à
|
128 |
la requête.
|
129 |
|
130 |
@param argv: Liste des jointures à ajouter
|
131 |
"""
|
132 |
|
133 |
#On vérifi qu'il n'y a pas de doublons dans la liste des
|
134 |
#jointures finale
|
135 |
|
136 |
for i in argv: |
137 |
for j in self.join: |
138 |
if str(i) == str(j): |
139 |
break
|
140 |
self.join.append(i)
|
141 |
|
142 |
def AddOuterJoin(self,*argv): |
143 |
|
144 |
"""
|
145 |
Ajoute une ou plusieurs jointures externes à
|
146 |
la requête.
|
147 |
|
148 |
@param argv: Liste des jointures externes à ajouter
|
149 |
"""
|
150 |
|
151 |
#On vérifi qu'il n'y a pas de doublons dans la liste des
|
152 |
#jointures externes finale
|
153 |
|
154 |
for i in argv: |
155 |
for j in self.outerjoin: |
156 |
if str(i) == str(j): |
157 |
break
|
158 |
self.outerjoin.append(i)
|
159 |
|
160 |
def AddFilter(self,*argv): |
161 |
|
162 |
"""
|
163 |
Ajoute un ou plusieurs filtres à la requête.
|
164 |
|
165 |
@param argv: Liste des filtres à ajouter
|
166 |
"""
|
167 |
|
168 |
#On vérifi qu'il n'y a pas de doublons dans la liste des
|
169 |
#filtres finale
|
170 |
|
171 |
for i in argv: |
172 |
for j in self.filter: |
173 |
if str(i) == str(j): |
174 |
break
|
175 |
self.filter.append(i)
|
176 |
|
177 |
def AddGroupBy(self,*argv): |
178 |
|
179 |
"""
|
180 |
Ajoute un ou plusieurs groupements à la requête.
|
181 |
|
182 |
@param argv: Liste des groupements à ajouter
|
183 |
"""
|
184 |
|
185 |
#On vérifi qu'il n'y a pas de doublons dans la liste des
|
186 |
#groupements finale
|
187 |
|
188 |
for i in argv: |
189 |
for j in self.groupby: |
190 |
if str(i) == str(j): |
191 |
break
|
192 |
self.groupby.append(i)
|
193 |
|
194 |
def AddOrderBy(self,*argv): |
195 |
|
196 |
"""
|
197 |
Ajoute un ou plusieurs orders à la requête.
|
198 |
|
199 |
@param argv: Liste des ordres à ajouter
|
200 |
"""
|
201 |
|
202 |
#On vérifi qu'il n'y a pas de doublons dans la liste des
|
203 |
#ordres finale
|
204 |
|
205 |
for i in argv: |
206 |
for j in self.orderby: |
207 |
if str(i) == str(j): |
208 |
break
|
209 |
self.orderby.append(i)
|
210 |
|
211 |
def FormatEventsImgStatu (self,event): |
212 |
|
213 |
"""
|
214 |
Suivant l'état de l'évènement, retourne la classe à appliquer
|
215 |
à l'image indiquant si l'évènement est pris en compte ou non.
|
216 |
|
217 |
@param event: l'évènement à analyser
|
218 |
|
219 |
@return: Dictionnaire représentant la classe à appliquer
|
220 |
"""
|
221 |
|
222 |
if event.active and event.status == 'AAClosed': |
223 |
return { 'src': url('/images/vigiboard/crossed.png') } |
224 |
elif event.status == 'Acknowledged' : |
225 |
return { 'src': url('/images/vigiboard/checked.png') } |
226 |
else:
|
227 |
return None |
228 |
|
229 |
def FormatEvents(self,first_row,last_row): |
230 |
|
231 |
"""
|
232 |
Formate la réponse de la requête et y applique les plugins
|
233 |
pour un affichage simple du résultat par Genshi.
|
234 |
On génère une liste de liste, chaqu'une étant la description de l'affichage pour un
|
235 |
évènement donné.
|
236 |
|
237 |
@param first_row: Indice de début de la liste des évènements
|
238 |
@param last_row: Indice de fin de la liste des évènements
|
239 |
"""
|
240 |
|
241 |
# Si la requête n'est pas générée, on le fait
|
242 |
if not self.generaterq : |
243 |
self.GenerateRequest()
|
244 |
self.generaterq = True |
245 |
|
246 |
# Liste des éléments pour la tête du tableau
|
247 |
|
248 |
lst_title = ['',_('Date<br />[Duration]'),'#',_('Host'),_('Service Type<br />Service Name'),_('Output')] |
249 |
lst_title.extend([plug.name for plug in self.plugin]) |
250 |
lst_title.extend(['[T T]','']) |
251 |
|
252 |
ev = [lst_title] |
253 |
i=0
|
254 |
class_tr = ['odd','even'] |
255 |
ids = [] |
256 |
for rq in self.rq[first_row : last_row]: |
257 |
|
258 |
# Si il y a plus d'un élément dans la liste des tables, rq devient une liste plutôt
|
259 |
# que d'être directement la table souhaité
|
260 |
|
261 |
if isinstance(rq,Events) : |
262 |
event = rq |
263 |
else :
|
264 |
event = rq[0]
|
265 |
ids.append(event.idevent) |
266 |
# La liste pour l'évènement actuel comporte dans l'ordre :
|
267 |
# L'évènment en lui même
|
268 |
# La classe à appliquer sur la ligne (permet d'alterner les couleurs suivant les lignes)
|
269 |
# La classe pour la case comportant la flèche de détails
|
270 |
# La classe pour la date, l'occurrence et l'édition
|
271 |
# L'image a affiche pour la flèche de détails
|
272 |
# Une liste (une case par plugin) de ce que le plugin souhaite afficher en fonction de l'évènement
|
273 |
if event.active :
|
274 |
ev.append([ |
275 |
event, |
276 |
{'class': class_tr[i%2]}, |
277 |
{'class' : self.bouton_severity[event.severity] + self.class_ack[event.status]}, |
278 |
{'class' : self.bouton_severity[event.severity] + self.class_ack[event.status] }, |
279 |
{'src' : '/images/vigiboard/%s2.png' % self.bouton_severity[event.severity].upper()}, |
280 |
self.FormatEventsImgStatu(event),
|
281 |
[[j.__show__(rq),j.style] for j in self.plugin] |
282 |
]) |
283 |
else :
|
284 |
ev.append([ |
285 |
event, |
286 |
{'class': class_tr[i%2]}, |
287 |
{'class' : self.bouton_severity[event.severity] + self.class_ack[event.status] }, |
288 |
{'class' : 'Cleared' + self.class_ack[event.status] }, |
289 |
{'src' : '/images/vigiboard/%s2.png' % self.bouton_severity[event.severity].upper()}, |
290 |
self.FormatEventsImgStatu(event),
|
291 |
[[j.__show__(rq),j.style] for j in self.plugin] |
292 |
]) |
293 |
i=i+1
|
294 |
# On sauvegarde la liste précédemment créée puis rempli le TmplContext
|
295 |
self.events = ev
|
296 |
self.idevents = ids
|
297 |
|
298 |
def FormatHistory (self): |
299 |
|
300 |
"""
|
301 |
Formate les historiques correspondant aux évènements sélectionnés
|
302 |
pour un affichage simple du résultat par Genshi.
|
303 |
On génère une liste de liste, chaqu'une étant la description de l'affichage pour un
|
304 |
historique donné.
|
305 |
"""
|
306 |
|
307 |
history = DBSession.query(EventHistory).filter(EventHistory.idevent.in_(self.idevents)).order_by(desc(EventHistory.timestamp)).order_by(desc(EventHistory.idhistory))
|
308 |
if history.count() == 0: |
309 |
self.hist=[]
|
310 |
return
|
311 |
hist = [] |
312 |
i = 0
|
313 |
class_tr = ['odd','even'] |
314 |
hostname = self.events[1][0].hostname |
315 |
servicename = self.events[1][0].servicename |
316 |
for h in history : |
317 |
# La liste pour l'historique actuel comporte dans l'ordre :
|
318 |
# Son identifiant
|
319 |
# Son nom d'hôte
|
320 |
# Son nom de service
|
321 |
# Le moment où il a été généré
|
322 |
# Qui l'a généré
|
323 |
# Le type d'action qui a été appliqué
|
324 |
# La sévérité de l'action si besoin est
|
325 |
# Le détail de l'action
|
326 |
# La classe à appliquer à la ligne (permet d'alterner les couleurs)
|
327 |
# La classe de la sévérité s'il y a
|
328 |
if h.value :
|
329 |
hist.append([ |
330 |
h.idhistory, |
331 |
hostname, |
332 |
servicename, |
333 |
h.timestamp, |
334 |
h.username, |
335 |
h.type_action, |
336 |
self.severity[min(int(h.value),7)], |
337 |
h.text, |
338 |
{'class' : class_tr[i%2]}, |
339 |
{'class':self.class_severity[min(int(h.value),7)]} |
340 |
]) |
341 |
else:
|
342 |
hist.append([ |
343 |
h.idhistory, |
344 |
hostname, |
345 |
servicename, |
346 |
h.timestamp, |
347 |
h.username, |
348 |
h.type_action, |
349 |
self.severity[0], |
350 |
h.text, |
351 |
{'class' : class_tr[i%2]}, |
352 |
{'class':self.class_severity[0]} |
353 |
]) |
354 |
i = i+1
|
355 |
|
356 |
self.hist = hist
|
357 |
|
358 |
def GenerateTmplContext(self): |
359 |
"""
|
360 |
Génère et peuple la variable tmpl_context avec les Dialogs et formulaires
|
361 |
nécessaire au fonctionnement de Vigiboard
|
362 |
"""
|
363 |
# Dialogue d'édition
|
364 |
tmpl_context.edit_event_form = Edit_Event_Form('edit_event_form',action=url('update')) |
365 |
tmpl_context.edit_eventdialog = JQueryUIDialog(id='Edit_EventsDialog',autoOpen=False,title=_('Edit Event')) |
366 |
|
367 |
# Dialogue de recherche
|
368 |
tmpl_context.search_form = Search_Form('search_form',action=url('1')) |
369 |
tmpl_context.searchdialog = JQueryUIDialog(id='SearchDialog',autoOpen=False,title=_('Search Event')) |
370 |
|
371 |
# Dialogue de détail d'un évènement
|
372 |
tmpl_context.historydialog = JQueryUIDialog(id='HistoryDialog',autoOpen=False,title=_('History')) |
373 |
|
374 |
class VigiboardRequestPlugin(): |
375 |
|
376 |
"""
|
377 |
Classe dont les plugins utilisé dans VigiboardRequest doivent étendre.
|
378 |
"""
|
379 |
|
380 |
def __init__ (self,table=[],join=[],outerjoin=[],filter=[],groupby=[],orderby=[],name='',style={}): |
381 |
self.table = table
|
382 |
self.join = join
|
383 |
self.outerjoin = outerjoin
|
384 |
self.filter = filter |
385 |
self.orderby = orderby
|
386 |
self.name = name
|
387 |
self.groupby = groupby
|
388 |
self.style = style
|
389 |
|
390 |
def __show__ (self,event): |
391 |
|
392 |
"""
|
393 |
Permet d'éviter toutes erreurs d'affichage.
|
394 |
C'est la fonction appelé par le formateur d'évènements.
|
395 |
"""
|
396 |
|
397 |
s = self.show(event)
|
398 |
|
399 |
if s != None : |
400 |
try:
|
401 |
return str(s) |
402 |
except:
|
403 |
return _('Error') |
404 |
|
405 |
def show(self, event): |
406 |
|
407 |
"""
|
408 |
Fonction qui affichera par défaut une chaîne de
|
409 |
caractères vide dans la colonne attribué au plugin.
|
410 |
|
411 |
En général, les plugins devront redéfinir cette fonction
|
412 |
pour afficher ce qu'ils souhaitent.
|
413 |
"""
|
414 |
|
415 |
return '' |