Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

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 ''