Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / vigiboard_ctl / vigiboardrequest.py @ 9c0dbb6a

History | View | Annotate | Download (15.6 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 Events, Host, Service, \
6
        HostGroups, ServiceGroups, EventHistory
7
from tg import tmpl_context, url
8
from vigiboard.model import DBSession
9
from sqlalchemy import not_ , and_ , asc , desc
10
from tw.jquery import JQueryUIDialog
11
from vigiboard.widgets.edit_event import EditEventForm , SearchForm
12
from vigiboard.controllers.vigiboard_ctl.userutils import get_user_groups
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
    def __init__(self):
23

    
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
        self.user_groups = get_user_groups()
32
        self.bouton_severity = { 0: 'Minor', 1: 'Minor', 2: 'Minor',
33
                3: 'Minor', 4: 'Minor', 5: 'Minor', 6: 'Major', 7: 'Critical' }
34
        self.class_severity = { 0: 'None', 1: 'None', 2: 'None', 3: 'None',
35
                4: 'None', 5: 'Minor', 6: 'Major', 7: 'Critical' }
36
        self.severity = { 0: _('None'), 1: _('OK'), 2: _('Suppressed'),
37
                3: _('Initial'), 4: _('Maintenance'), 5: _('Minor'),
38
                6: _('Major'), 7: _('Critical') }
39

    
40
        self.class_ack = {'Acknowledged': 'Ack', 'None': '', 'AAClosed': 'Ack'}
41

    
42
        self.generaterq = False
43
        self.table = [Events]
44
        self.join = [( Host, Events.hostname == Host.name ),
45
                ( Service, Events.servicename == Service.name ),
46
                ( HostGroups , Host.name == HostGroups.hostname ),
47
                ( ServiceGroups , Service.name == ServiceGroups.servicename )
48
                ]
49
        self.outerjoin = []
50
        self.filter = [HostGroups.groupname.in_(self.user_groups),
51
                 ServiceGroups.groupname.in_(self.user_groups),
52
                 not_(and_(Events.active == False,
53
                     Events.status == 'AAClosed')),
54
                 Events.timestamp_active != None#,
55
                 #not_(Events.timestamp_active.like('0000-00-00 00:00:00'))
56
                 ]
57
        self.orderby = [desc(Events.status),
58
                                desc(Events.active),
59
                                desc(Events.severity),
60
                                asc(Events.hostname),
61
                                desc(Events.timestamp)]
62
        self.groupby = []
63
        self.plugin = []
64
        self.events = []
65
        self.idevents = []
66
        self.hist = []
67
        self.req = DBSession
68

    
69
    def add_plugin(self, *argv):
70
        
71
        """
72
        Ajout d'un plugin, on lui prélève ses ajouts dans la requête
73
        """
74
        for i in argv :
75
            if isinstance(i, VigiboardRequestPlugin):
76
                if i.table :
77
                    self.add_table(*i.table)
78
                if i.join :
79
                    self.add_join(*i.join)
80
                if i.outerjoin :
81
                    self.add_outer_join(*i.outerjoin)
82
                if i.filter :
83
                    self.add_filter(*i.filter)
84
                if i.groupby :    
85
                    self.add_group_by(*i.groupby)
86
                if i.orderby :
87
                    self.add_order_by(*i.orderby)
88
                self.plugin.append(i)
89

    
90
    def generate_request(self):
91
        
92
        """
93
        Génération de la requête avec l'ensemble des données stockées
94
        et la place dans la variable rq de la classe
95
        """
96
        
97
        # query et join ont besoin de referrence
98
        self.req = self.req.query(*self.table)
99
        self.req = self.req.join(*self.join)
100

    
101
        # le reste, non
102
        for i in self.outerjoin:
103
            self.req = self.req.outerjoin(i)
104
        for i in self.filter:
105
            self.req = self.req.filter(i)
106
        for i in self.groupby:
107
            self.req = self.req.group_by(i)
108
        for i in self.orderby:
109
            self.req = self.req.order_by(i)
110

    
111
    def num_rows(self):
112

    
113
        """
114
        Retourne le nombre de lignes de la requête.
115
        Si celle-ci n'est pas encore générée, on le fait.
116

117
        @return: Nombre de ligne
118
        """
119

    
120
        if not self.generaterq :
121
            self.generate_request()
122
            self.generaterq = True
123
        return self.req.count()
124

    
125
    def add_table(self, *argv):
126
        
127
        """
128
        Ajoute une ou plusieurs tables/élément d'une table à
129
        la requête.
130

131
        @param argv: Liste des tables à ajouter
132
        """
133
        
134
        #On vérifi qu'il n'y a pas de doublons dans la liste des
135
        #tables finale
136
        
137
        for i in argv :
138
            for j in self.table:
139
                if str(i) == str(j):
140
                    break
141
            self.table.append(i)
142

    
143
    def add_join(self, *argv):
144
        
145
        """
146
        Ajoute une ou plusieurs jointures à
147
        la requête.
148

149
        @param argv: Liste des jointures à ajouter
150
        """
151
        
152
        #On vérifi qu'il n'y a pas de doublons dans la liste des
153
        #jointures finale
154
        
155
        for i in argv:
156
            for j in self.join:
157
                if str(i) == str(j):
158
                    break
159
            self.join.append(i)
160

    
161
    def add_outer_join(self, *argv):
162
        
163
        """
164
        Ajoute une ou plusieurs jointures externes à
165
        la requête.
166

167
        @param argv: Liste des jointures externes à ajouter
168
        """
169
        
170
        #On vérifi qu'il n'y a pas de doublons dans la liste des
171
        #jointures externes finale
172
        
173
        for i in argv:
174
            for j in self.outerjoin:
175
                if str(i) == str(j):
176
                    break
177
            self.outerjoin.append(i)    
178

    
179
    def add_filter(self, *argv):
180

    
181
        """
182
        Ajoute un ou plusieurs filtres à la requête.
183

184
        @param argv: Liste des filtres à ajouter
185
        """
186
        
187
        #On vérifi qu'il n'y a pas de doublons dans la liste des
188
        #filtres finale
189
        
190
        for i in argv:
191
            for j in self.filter:
192
                if str(i) == str(j):
193
                    break
194
            self.filter.append(i)
195

    
196
    def add_group_by(self, *argv):
197

    
198
        """
199
        Ajoute un ou plusieurs groupements à la requête.
200

201
        @param argv: Liste des groupements à ajouter
202
        """
203
        
204
        #On vérifi qu'il n'y a pas de doublons dans la liste des
205
        #groupements finale
206
        
207
        for i in argv:
208
            for j in self.groupby:
209
                if str(i) == str(j):
210
                    break
211
            self.groupby.append(i)
212

    
213
    def add_order_by(self, *argv):
214

    
215
        """
216
        Ajoute un ou plusieurs orders à la requête.
217

218
        @param argv: Liste des ordres à ajouter
219
        """
220
        
221
        #On vérifi qu'il n'y a pas de doublons dans la liste des
222
        #ordres finale
223
        
224
        for i in argv:
225
            for j in self.orderby:
226
                if str(i) == str(j):
227
                    break
228
            self.orderby.append(i)
229

    
230
    def format_events_img_statu (self, event):
231
        
232
        """
233
        Suivant l'état de l'évènement, retourne la classe à appliquer
234
        à l'image indiquant si l'évènement est pris en compte ou non.
235

236
        @param event: l'évènement à analyser
237

238
        @return: Dictionnaire représentant la classe à appliquer
239
        """
240

    
241
        if event.active and event.status == 'AAClosed':
242
            return { 'src': url('/images/vigiboard/crossed.png') }
243
        elif event.status == 'Acknowledged' :
244
            return { 'src': url('/images/vigiboard/checked.png') }
245
        else:
246
            return None
247

    
248
    def format_events(self, first_row, last_row):
249
        
250
        """
251
        Formate la réponse de la requête et y applique les plugins
252
        pour un affichage simple du résultat par Genshi.
253
        On génère une liste de liste, chaqu'une étant la description de
254
        l'affichage pour un évènement donné.
255

256
        @param first_row: Indice de début de la liste des évènements
257
        @param last_row: Indice de fin de la liste des évènements
258
        """
259
        
260
        # Si la requête n'est pas générée, on le fait
261
        if not self.generaterq :
262
            self.generate_request()
263
            self.generaterq = True
264

    
265
        # Liste des éléments pour la tête du tableau
266

    
267
        lst_title = ['', _('Date<br />[Duration]'), '#', _('Host'),
268
                _('Service Type<br />Service Name'), _('Output')]
269
        lst_title.extend([plug.name for plug in self.plugin])
270
        lst_title.extend(['[T T]', ''])
271
        
272
        events = [lst_title]
273
        i = 0
274
        class_tr = ['odd', 'even']
275
        ids = []
276
        for req in self.req[first_row : last_row]:
277

    
278
            # Si il y a plus d'un élément dans la liste des tables,
279
            # rq devient une liste plutôt que d'être directement la
280
            # table souhaité
281
            
282
            if isinstance(req, Events) :
283
                event = req
284
            else :
285
                event = req[0]
286
            ids.append(event.idevent)
287

    
288
            # La liste pour l'évènement actuel comporte dans l'ordre :
289
            #   L'évènment en lui même
290
            #   La classe à appliquer sur la ligne (permet d'alterner les
291
            #       couleurs suivant les lignes)
292
            #   La classe pour la case comportant la flèche de détails
293
            #   La classe pour la date, l'occurrence et l'édition
294
            #   L'image a affiche pour la flèche de détails
295
            #   Une liste (une case par plugin) de ce que le plugin souhaite
296
            #       afficher en fonction de l'évènement
297

    
298
            if event.active :
299
                events.append([
300
                    event,
301
                    {'class': class_tr[i%2]},
302
                    {'class' : self.bouton_severity[event.severity] + \
303
                            self.class_ack[event.status]},
304
                    {'class' : self.bouton_severity[event.severity] + \
305
                            self.class_ack[event.status] },
306
                    {'src' : '/images/vigiboard/%s2.png' % \
307
                            self.bouton_severity[event.severity].upper()},
308
                    self.format_events_img_statu(event),
309
                    [[j.__show__(req), j.style] for j in self.plugin]
310
                    ])
311
            else :
312
                events.append([
313
                    event,
314
                    {'class': class_tr[i%2]},
315
                    {'class' : self.bouton_severity[event.severity] + \
316
                            self.class_ack[event.status] },
317
                    {'class' : 'Cleared' + self.class_ack[event.status] },
318
                    {'src' : '/images/vigiboard/%s2.png' % \
319
                            self.bouton_severity[event.severity].upper()},
320
                    self.format_events_img_statu(event),
321
                    [[j.__show__(req), j.style] for j in self.plugin]
322
                    ])
323
            i = i + 1
324

    
325
        # On sauvegarde la liste précédemment créée puis rempli
326
        # le TmplContext
327

    
328
        self.events = events
329
        self.idevents = ids
330

    
331
    def format_history (self):
332
        
333
        """
334
        Formate les historiques correspondant aux évènements sélectionnés
335
        pour un affichage simple du résultat par Genshi.
336
        On génère une liste de liste, chaqu'une étant la description de l'affichage pour un
337
        historique donné.
338
        """
339

    
340
        history = DBSession.query(EventHistory
341
                ).filter(EventHistory.idevent.in_(self.idevents)
342
                ).order_by(desc(EventHistory.timestamp)
343
                ).order_by(desc(EventHistory.idhistory))
344

    
345
        if history.count() == 0:
346
            self.hist = []
347
            return
348
        hists = []
349
        i = 0
350
        class_tr = ['odd', 'even']
351
        hostname = self.events[1][0].hostname
352
        servicename = self.events[1][0].servicename
353

    
354
        for hist in history :
355

    
356
            # La liste pour l'historique actuel comporte dans l'ordre :
357
            #   Son identifiant
358
            #   Son nom d'hôte
359
            #   Son nom de service
360
            #   Le moment où il a été généré
361
            #   Qui l'a généré
362
            #   Le type d'action qui a été appliqué
363
            #   La sévérité de l'action si besoin est
364
            #   Le détail de l'action
365
            #   La classe à appliquer à la ligne (permet d'alterner
366
            #       les couleurs)
367
            #   La classe de la sévérité s'il y a
368

    
369
            if hist.value :
370
                hists.append([
371
                    hist.idhistory,
372
                    hostname,
373
                    servicename,
374
                    hist.timestamp,
375
                    hist.username,
376
                    hist.type_action,
377
                    self.severity[min(int(hist.value),7)],
378
                    hist.text,
379
                    {'class' : class_tr[i%2]},
380
                    {'class':self.class_severity[min(int(hist.value),7)]}
381
                ])
382
            else:
383
                hists.append([
384
                    hist.idhistory,
385
                    hostname,
386
                    servicename,
387
                    hist.timestamp,
388
                    hist.username,
389
                    hist.type_action,
390
                    self.severity[0],
391
                    hist.text,
392
                    {'class' : class_tr[i%2]},
393
                    {'class':self.class_severity[0]}
394
                ])    
395
            i = i + 1
396
        
397
        self.hist = hists
398

    
399
    def generate_tmpl_context(self):
400
        
401
        """
402
        Génère et peuple la variable tmpl_context avec les Dialogs et
403
        formulaires nécessaire au fonctionnement de Vigiboard
404
        """
405

    
406
        # Dialogue d'édition
407
        tmpl_context.edit_event_form = EditEventForm('edit_event_form',
408
                action=url('/vigiboard/update'))
409
        tmpl_context.edit_eventdialog = JQueryUIDialog(id='Edit_EventsDialog',
410
                autoOpen=False,title=_('Edit Event'))
411
    
412
        # Dialogue de recherche
413
        tmpl_context.search_form = SearchForm('search_form',
414
                action=url('/vigiboard'))
415
        tmpl_context.searchdialog = JQueryUIDialog(id='SearchDialog',
416
                autoOpen=False,title=_('Search Event'))
417
        
418
        # Dialogue de détail d'un évènement
419
        tmpl_context.historydialog = JQueryUIDialog(id='HistoryDialog',
420
                autoOpen=False,title=_('History'))
421

    
422
class VigiboardRequestPlugin():
423

    
424
    """
425
    Classe dont les plugins utilisé dans VigiboardRequest doivent étendre.
426
    """
427
    
428
    def __init__ (self, table = None, join = None, outerjoin = None,
429
            filters = None, groupby = None, orderby = None, name = '',
430
            style = None):
431

    
432
        self.table = table
433
        self.join = join
434
        self.outerjoin = outerjoin
435
        self.filter = filters
436
        self.orderby = orderby
437
        self.name = name
438
        self.groupby = groupby
439
        self.style = style
440

    
441
    def __show__ (self, event):
442
        
443
        """
444
        Permet d'éviter toutes erreurs d'affichage.
445
        C'est la fonction appelé par le formateur d'évènements.
446
        """
447

    
448
        show = self.show(event)
449
        
450
        if show != None :
451
            try:
452
                return str(show)
453
            except:
454
                return _('Error')
455
    
456
    def show(self, event):
457
        
458
        """
459
        Fonction qui affichera par défaut une chaîne de
460
        caractères vide dans la colonne attribué au plugin.
461

462
        En général, les plugins devront redéfinir cette fonction
463
        pour afficher ce qu'ils souhaitent.
464
        """
465
        
466
        return ''