Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

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)