Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / vigiboardrequest.py @ 0f56fff9

History | View | Annotate | Download (15.4 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 import Events, Host, Service, \
6
        HostGroups, ServiceGroups, EventHistory, User
7
from tg import tmpl_context, url, config, request
8
from vigiboard.model import DBSession
9
from sqlalchemy import not_ , and_ , asc , desc
10
from tw.jquery.ui_dialog import JQueryUIDialog
11
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
    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
        username = request.environ['repoze.who.identity']['repoze.who.userid']
32
        self.user_groups = User.by_user_name(username).groups
33
        self.bouton_severity = { 0: 'Minor', 1: 'Minor', 2: 'Minor',
34
                3: 'Minor', 4: 'Minor', 5: 'Minor', 6: 'Major', 7: 'Critical' }
35
        self.class_severity = { 0: 'None', 1: 'None', 2: 'None', 3: 'None',
36
                4: 'None', 5: 'Minor', 6: 'Major', 7: 'Critical' }
37
        self.severity = { 0: _('None'), 1: _('OK'), 2: _('Suppressed'),
38
                3: _('Initial'), 4: _('Maintenance'), 5: _('Minor'),
39
                6: _('Major'), 7: _('Critical') }
40

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

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

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

    
92
    def generate_request(self):
93
        
94
        """
95
        Génération de la requête avec l'ensemble des données stockées
96
        et la place dans la variable rq de la classe
97
        """
98
        for plug in config.get('vigiboard_plugins', []):
99
            try:
100
                mypac = __import__(
101
                    'vigiboard.controllers.vigiboard_plugin.' +\
102
                            plug[0],globals(), locals(), [plug[1]],-1)
103
                self.add_plugin(getattr(mypac, plug[1])())
104
            except:
105
                raise
106
        
107
        # query et join ont besoin de referrence
108
        self.req = self.req.query(*self.table)
109
        self.req = self.req.join(*self.join)
110

    
111
        # le reste, non
112
        for i in self.outerjoin:
113
            self.req = self.req.outerjoin(i)
114
        for i in self.filter:
115
            self.req = self.req.filter(i)
116
        for i in self.groupby:
117
            self.req = self.req.group_by(i)
118
        for i in self.orderby:
119
            self.req = self.req.order_by(i)
120

    
121
    def num_rows(self):
122

    
123
        """
124
        Retourne le nombre de lignes de la requête.
125
        Si celle-ci n'est pas encore générée, on le fait.
126

127
        @return: Nombre de ligne
128
        """
129

    
130
        if not self.generaterq :
131
            self.generate_request()
132
            self.generaterq = True
133
        return self.req.count()
134

    
135
    def add_table(self, *argv):
136
        
137
        """
138
        Ajoute une ou plusieurs tables/élément d'une table à
139
        la requête.
140

141
        @param argv: Liste des tables à ajouter
142
        """
143
        
144
        #On vérifi qu'il n'y a pas de doublons dans la liste des
145
        #tables finale
146
        
147
        for i in argv :
148
            for j in self.table:
149
                if str(i) == str(j):
150
                    break
151
            self.table.append(i)
152

    
153
    def add_join(self, *argv):
154
        
155
        """
156
        Ajoute une ou plusieurs jointures à
157
        la requête.
158

159
        @param argv: Liste des jointures à ajouter
160
        """
161
        
162
        #On vérifi qu'il n'y a pas de doublons dans la liste des
163
        #jointures finale
164
        
165
        for i in argv:
166
            for j in self.join:
167
                if str(i) == str(j):
168
                    break
169
            self.join.append(i)
170

    
171
    def add_outer_join(self, *argv):
172
        
173
        """
174
        Ajoute une ou plusieurs jointures externes à
175
        la requête.
176

177
        @param argv: Liste des jointures externes à ajouter
178
        """
179
        
180
        #On vérifi qu'il n'y a pas de doublons dans la liste des
181
        #jointures externes finale
182
        
183
        for i in argv:
184
            for j in self.outerjoin:
185
                if str(i) == str(j):
186
                    break
187
            self.outerjoin.append(i)    
188

    
189
    def add_filter(self, *argv):
190

    
191
        """
192
        Ajoute un ou plusieurs filtres à la requête.
193

194
        @param argv: Liste des filtres à ajouter
195
        """
196
        
197
        #On vérifi qu'il n'y a pas de doublons dans la liste des
198
        #filtres finale
199
        
200
        for i in argv:
201
            for j in self.filter:
202
                if str(i) == str(j):
203
                    break
204
            self.filter.append(i)
205

    
206
    def add_group_by(self, *argv):
207

    
208
        """
209
        Ajoute un ou plusieurs groupements à la requête.
210

211
        @param argv: Liste des groupements à ajouter
212
        """
213
        
214
        #On vérifi qu'il n'y a pas de doublons dans la liste des
215
        #groupements finale
216
        
217
        for i in argv:
218
            for j in self.groupby:
219
                if str(i) == str(j):
220
                    break
221
            self.groupby.append(i)
222

    
223
    def add_order_by(self, *argv):
224

    
225
        """
226
        Ajoute un ou plusieurs orders à la requête.
227

228
        @param argv: Liste des ordres à ajouter
229
        """
230
        
231
        #On vérifi qu'il n'y a pas de doublons dans la liste des
232
        #ordres finale
233
        
234
        for i in argv:
235
            for j in self.orderby:
236
                if str(i) == str(j):
237
                    break
238
            self.orderby.append(i)
239

    
240
    def format_events_img_statu (self, event):
241
        
242
        """
243
        Suivant l'état de l'évènement, retourne la classe à appliquer
244
        à l'image indiquant si l'évènement est pris en compte ou non.
245

246
        @param event: l'évènement à analyser
247

248
        @return: Dictionnaire représentant la classe à appliquer
249
        """
250

    
251
        if event.active and event.status == 'AAClosed':
252
            return { 'src': url('/images/crossed.png') }
253
        elif event.status == 'Acknowledged' :
254
            return { 'src': url('/images/checked.png') }
255
        else:
256
            return None
257

    
258
    def format_events(self, first_row, last_row):
259
        
260
        """
261
        Formate la réponse de la requête et y applique les plugins
262
        pour un affichage simple du résultat par Genshi.
263
        On génère une liste de liste, chaqu'une étant la description de
264
        l'affichage pour un évènement donné.
265

266
        @param first_row: Indice de début de la liste des évènements
267
        @param last_row: Indice de fin de la liste des évènements
268
        """
269
        
270
        # Si la requête n'est pas générée, on le fait
271
        if not self.generaterq :
272
            self.generate_request()
273
            self.generaterq = True
274

    
275
        # Liste des éléments pour la tête du tableau
276

    
277
        lst_title = [
278
                ['',{}],
279
                [_('Date')+ '<span style="font-weight:normal">' + \
280
                        '<br />['+_('Duration') + ']</span>',
281
                        {'style':'text-align:left'}],
282
                ['#', {'title':_('Occurrence count')}],
283
                [_('Host'), {'style':'text-align:left'}],
284
                [_('Service Type')+'<br />'+_('Service Name'),
285
                    {'style':'text-align:left'}], 
286
                [_('Output'), {'style':'text-align:left'}]
287
                ]
288
        lst_title.extend([[plug.name, plug.style] for plug in self.plugin])
289
        lst_title.extend([['['+_('TT')+']', {'title': _('Trouble Ticket')}],
290
                            ['', {}]])
291
        events = [lst_title]
292
        i = 0
293
        class_tr = ['odd', 'even']
294
        ids = []
295
        for req in self.req[first_row : last_row]:
296

    
297
            # Si il y a plus d'un élément dans la liste des tables,
298
            # rq devient une liste plutôt que d'être directement la
299
            # table souhaité
300
            
301
            if isinstance(req, Events) :
302
                event = req
303
            else :
304
                event = req[0]
305
            ids.append(event.idevent)
306

    
307
            # La liste pour l'évènement actuel comporte dans l'ordre :
308
            #   L'évènment en lui même
309
            #   La classe à appliquer sur la ligne (permet d'alterner les
310
            #       couleurs suivant les lignes)
311
            #   La classe pour la case comportant la flèche de détails
312
            #   La classe pour la date, l'occurrence et l'édition
313
            #   L'image a affiche pour la flèche de détails
314
            #   Une liste (une case par plugin) de ce que le plugin souhaite
315
            #       afficher en fonction de l'évènement
316

    
317
            if event.active :
318
                events.append([
319
                    event,
320
                    {'class': class_tr[i%2]},
321
                    {'class' : self.bouton_severity[event.severity] + \
322
                            self.class_ack[event.status]},
323
                    {'class' : self.bouton_severity[event.severity] + \
324
                            self.class_ack[event.status] },
325
                    {'src' : '/images/%s2.png' % \
326
                            self.bouton_severity[event.severity].upper()},
327
                    self.format_events_img_statu(event),
328
                    [[j.__show__(req), j.style] for j in self.plugin]
329
                    ])
330
            else :
331
                events.append([
332
                    event,
333
                    {'class': class_tr[i%2]},
334
                    {'class' : self.bouton_severity[event.severity] + \
335
                            self.class_ack[event.status] },
336
                    {'class' : 'Cleared' + self.class_ack[event.status] },
337
                    {'src' : '/images/%s2.png' % \
338
                            self.bouton_severity[event.severity].upper()},
339
                    self.format_events_img_statu(event),
340
                    [[j.__show__(req), j.style] for j in self.plugin]
341
                    ])
342
            i = i + 1
343

    
344
        # On sauvegarde la liste précédemment créée puis rempli
345
        # le TmplContext
346

    
347
        self.events = events
348
        self.idevents = ids
349

    
350
    def format_history (self):
351
        
352
        """
353
        Formate les historiques correspondant aux évènements sélectionnés
354
        pour un affichage simple du résultat par Genshi.
355
        On génère une liste de liste, chaqu'une étant la description de l'affichage pour un
356
        historique donné.
357
        """
358

    
359
        history = DBSession.query(EventHistory
360
                ).filter(EventHistory.idevent.in_(self.idevents)
361
                ).order_by(desc(EventHistory.timestamp)
362
                ).order_by(desc(EventHistory.idhistory))
363
        if history.count() == 0:
364
            self.hist = {}
365
            for i in self.idevents:
366
                self.hist[i] = []
367
            return
368
        hists = {}
369
        i = 0
370
        class_tr = ['odd', 'even']
371
        hist_tmp = []
372
        last_idevent = history[0].idevent
373
        for hist in history :
374
            
375
            if last_idevent != hist.idevent:
376
                hists[last_idevent] = hist_tmp
377
                last_idevent = hist.idevent
378
                hist_tmp = []
379

    
380
            # La liste pour l'historique actuel comporte dans l'ordre :
381
            #   Le moment où il a été généré
382
            #   Qui l'a généré
383
            #   Le type d'action qui a été appliqué
384
            #   La sévérité de l'action si besoin est
385
            #   Le détail de l'action
386
            #   La classe à appliquer à la ligne (permet d'alterner
387
            #       les couleurs)
388
            #   La classe de la sévérité s'il y a
389

    
390
            if hist.value :
391
                hist_tmp.append([
392
                    hist.timestamp,
393
                    hist.username,
394
                    hist.type_action,
395
                    self.severity[min(int(hist.value),7)],
396
                    hist.text,
397
                    {'class' : class_tr[i%2]},
398
                    {'class':self.class_severity[min(int(hist.value),7)]}
399
                ])
400
            else:
401
                hist_tmp.append([
402
                    hist.timestamp,
403
                    hist.username,
404
                    hist.type_action,
405
                    self.severity[0],
406
                    hist.text,
407
                    {'class' : class_tr[i%2]},
408
                    {'class':self.class_severity[0]}
409
                ])    
410
            i = i + 1
411
        
412
        hists[last_idevent] = hist_tmp
413
        self.hist = hists
414

    
415
    def generate_tmpl_context(self):
416
        
417
        """
418
        Génère et peuple la variable tmpl_context avec les Dialogs et
419
        formulaires nécessaire au fonctionnement de Vigiboard
420
        """
421

    
422
        # Dialogue d'édition
423
        tmpl_context.edit_event_form = EditEventForm('edit_event_form',
424
                action=url('/update'))
425
        tmpl_context.edit_eventdialog = JQueryUIDialog(id='Edit_EventsDialog',
426
                autoOpen=False,title=_('Edit Event'))
427
    
428
        # Dialogue de recherche
429
        tmpl_context.search_form = SearchForm('search_form',
430
                action=url('/'))
431
        tmpl_context.searchdialog = JQueryUIDialog(id='SearchDialog',
432
                autoOpen=False,title=_('Search Event'))
433
        
434
        # Dialogue de détail d'un évènement
435
        tmpl_context.historydialog = JQueryUIDialog(id='HistoryDialog',
436
                autoOpen=False,title=_('History'))
437

    
438
        # Exécution des contexts des plugins
439
        for j in self.plugin:
440
            j.context(self.context_fct)