Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / vigiboard_ctl / vigiboardrequest.py @ 35cea70e

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
        self.orderby = [asc(Events.status),
57
                                desc(Events.active),
58
                                desc(Events.severity),
59
                                asc(Events.hostname),
60
                                desc(Events.timestamp)]
61
        self.groupby = []
62
        self.plugin = []
63
        self.events = []
64
        self.idevents = []
65
        self.hist = []
66
        self.req = DBSession
67

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

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

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

    
110
    def num_rows(self):
111

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

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

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

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

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

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

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

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

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

    
178
    def add_filter(self, *argv):
179

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

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

    
195
    def add_group_by(self, *argv):
196

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

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

    
212
    def add_order_by(self, *argv):
213

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
353
        for hist in history :
354

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

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

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

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

    
421
class VigiboardRequestPlugin():
422

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

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

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

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

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