Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / vigiboardrequest.py @ ee3ae8c8

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 import Event, EventsAggregate, EventHistory, \
6
        Host, HostGroup, Service, ServiceGroup
7
from tg import tmpl_context, url, config
8
from vigiboard.model import DBSession
9
from sqlalchemy import not_, and_, asc, desc, sql
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
    class_ack = {
17
        'Acknowledged': 'Ack',
18
        'None': '',
19
        'AAClosed': 'Ack'
20
    }
21

    
22
    severity = (
23
        _('OK'),            # 0
24
        _('Suppressed'),
25
        _('Initial'),
26
        _('Maintenance'),
27
        _('Minor'),
28
        _('Major'),
29
        _('Critical'),      # 6
30
    )
31

    
32
    bouton_severity = (
33
        'OK', 'Minor', 'Minor',
34
        'Minor', 'Minor', 'Major',
35
        'Critical',
36
    )
37

    
38
    @classmethod
39
    def get_severity(cls, severity):
40
        if not severity:
41
            severity = 0
42
        return int(severity)
43

    
44

    
45
    """
46
    Classe gérant la génération de la requête finale,
47
    le préformatage des évènements et celui des historiques
48
    """
49

    
50
    def __init__(self, user):
51

    
52
        """
53
        Initialisation de toutes les variables nécessaires: Liste des groupes
54
        de l'utilisateur, les classes à appliquer suivant la sévérité, les
55
        différentes étapes de la génération de la requête et la liste des
56
        plugins appliqués.
57
        """
58

    
59
        self.user_groups = user.groups
60
        self.generaterq = False
61

    
62
        self.table = [
63
            EventsAggregate,
64
            sql.func.count(EventsAggregate.idaggregate)
65
        ]
66

    
67
        self.join = [
68
                (Event, EventsAggregate.idcause == Event.idevent),
69
                (Host, Event.hostname == Host.name),
70
                (Service, Event.servicename == Service.name),
71
                (HostGroup, Host.name == HostGroup.hostname),
72
                (ServiceGroup, Service.name == ServiceGroup.servicename),
73
            ]
74

    
75
        self.outerjoin = []
76

    
77
        self.filter = [
78
                HostGroup.groupname.in_(self.user_groups),
79
                ServiceGroup.groupname.in_(self.user_groups),
80

    
81
                # On masque les évènements marqués comme OK
82
                # (current_severity == 0) et déjà traités
83
                # (status == 'AAClosed').
84
                not_(and_(EventsAggregate.current_severity == 0,
85
                    EventsAggregate.status == u'AAClosed')),
86
                EventsAggregate.timestamp_active != None,
87
            ]
88

    
89
        self.orderby = [
90
                desc(EventsAggregate.status),   # None, Acknowledged, AAClosed
91
                desc(EventsAggregate.current_severity), # Code pour Minor, etc.
92
                asc(Event.hostname),
93
                desc(Event.timestamp),
94
            ]
95

    
96
        self.groupby = [
97
                EventsAggregate.idaggregate,
98
                EventsAggregate,
99
                Event.hostname,
100
                Event.timestamp,
101
            ]
102

    
103
        self.plugin = []
104
        self.events = []
105
        self.idevents = []
106
        self.hist = []
107
        self.req = DBSession
108
        self.context_fct = []
109

    
110
    def add_plugin(self, *argv):
111

    
112
        """
113
        Ajout d'un plugin, on lui prélève ses ajouts dans la requête
114
        """
115
        for i in argv :
116
            if isinstance(i, VigiboardRequestPlugin):
117
                if i.table :
118
                    self.add_table(*i.table)
119
                if i.join :
120
                    self.add_join(*i.join)
121
                if i.outerjoin :
122
                    self.add_outer_join(*i.outerjoin)
123
                if i.filter :
124
                    self.add_filter(*i.filter)
125
                if i.groupby :    
126
                    self.add_group_by(*i.groupby)
127
                if i.orderby :
128
                    self.add_order_by(*i.orderby)
129
                self.plugin.append(i)
130

    
131
    def generate_request(self):
132
        
133
        """
134
        Génération de la requête avec l'ensemble des données stockées
135
        et la place dans la variable rq de la classe
136
        """
137
        for plug in config.get('vigiboard_plugins', []):
138
            try:
139
                mypac = __import__(
140
                    'vigiboard.controllers.vigiboard_plugin.' +\
141
                            plug[0], globals(), locals(), [plug[1]], -1)
142
                self.add_plugin(getattr(mypac, plug[1])())
143
            except:
144
                raise
145

    
146
        # query et join ont besoin de referrence
147
        self.req = self.req.query(*self.table)
148
        self.req = self.req.join(*self.join)
149

    
150
        # le reste, non
151
        for i in self.outerjoin:
152
            self.req = self.req.outerjoin(i)
153
        for i in self.filter:
154
            self.req = self.req.filter(i)
155
        for i in self.groupby:
156
            self.req = self.req.group_by(i)
157
        for i in self.orderby:
158
            self.req = self.req.order_by(i)
159

    
160
    def num_rows(self):
161

    
162
        """
163
        Retourne le nombre de lignes de la requête.
164
        Si celle-ci n'est pas encore générée, on le fait.
165

166
        @return: Nombre de ligne
167
        """
168

    
169
        if not self.generaterq:
170
            self.generate_request()
171
            self.generaterq = True
172
        return self.req.count()
173

    
174
    def add_table(self, *argv):
175
        
176
        """
177
        Ajoute une ou plusieurs tables/élément d'une table à
178
        la requête.
179

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

    
192
    def add_join(self, *argv):
193
        
194
        """
195
        Ajoute une ou plusieurs jointures à
196
        la requête.
197

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

    
210
    def add_outer_join(self, *argv):
211
        
212
        """
213
        Ajoute une ou plusieurs jointures externes à
214
        la requête.
215

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

    
228
    def add_filter(self, *argv):
229

    
230
        """
231
        Ajoute un ou plusieurs filtres à la requête.
232

233
        @param argv: Liste des filtres à ajouter
234
        """
235
        
236
        # On vérifie qu'il n'y a pas de doublons dans la liste finale
237
        # des filtres.
238
        
239
        for i in argv:
240
            for j in self.filter:
241
                if str(i) == str(j):
242
                    break
243
            self.filter.append(i)
244

    
245
    def add_group_by(self, *argv):
246

    
247
        """
248
        Ajoute un ou plusieurs groupements à la requête.
249

250
        @param argv: Liste des groupements à ajouter
251
        """
252
        
253
        # On vérifie qu'il n'y a pas de doublons dans la liste finale
254
        # des groupements.
255
        
256
        for i in argv:
257
            for j in self.groupby:
258
                if str(i) == str(j):
259
                    break
260
            self.groupby.append(i)
261

    
262
    def add_order_by(self, *argv):
263

    
264
        """
265
        Ajoute un ou plusieurs orders à la requête.
266

267
        @param argv: Liste des ordres à ajouter
268
        """
269
        
270
        # On vérifie qu'il n'y a pas de doublons dans la liste finale
271
        # des ordres.
272
        
273
        for i in argv:
274
            for j in self.orderby:
275
                if str(i) == str(j):
276
                    break
277
            self.orderby.append(i)
278

    
279
    def format_events_img_status(self, event):
280
        
281
        """
282
        Suivant l'état de l'évènement, retourne la classe à appliquer
283
        à l'image indiquant si l'évènement est pris en compte ou non.
284

285
        @param event: l'évènement à analyser
286

287
        @return: Dictionnaire représentant la classe à appliquer
288
        """
289

    
290
        if event.status == 'AAClosed':
291
            return { 'src': url('/images/crossed.png') }
292
        elif event.status == 'Acknowledged' :
293
            return { 'src': url('/images/checked.png') }
294
        else:
295
            return None
296

    
297
    def format_events(self, first_row, last_row):
298
        """
299
        Formate la réponse de la requête et y applique les plugins
300
        pour un affichage simple du résultat par Genshi.
301
        On génère une liste de liste, chaqu'une étant la description de
302
        l'affichage pour un évènement donné.
303

304
        @param first_row: Indice de début de la liste des évènements
305
        @param last_row: Indice de fin de la liste des évènements
306
        """
307
        
308
        # Si la requête n'est pas générée, on le fait
309
        if not self.generaterq :
310
            self.generate_request()
311
            self.generaterq = True
312

    
313
        # Liste des éléments pour la tête du tableau
314

    
315
        lst_title = [
316
                ['',{}],
317
                [_('Date')+ '<span style="font-weight:normal">' + \
318
                        '<br />['+_('Duration') + ']</span>',
319
                        {'style':'text-align:left'}],
320
                ['#', {'title':_('Occurrence count')}],
321
                [_('Host'), {'style':'text-align:left'}],
322
                [_('Service Type')+'<br />'+_('Service Name'),
323
                    {'style':'text-align:left'}], 
324
                [_('Output'), {'style':'text-align:left'}]
325
                ]
326
        lst_title.extend([[plug.name, plug.style] for plug in self.plugin])
327
        lst_title.extend([['[' + _('TT') + ']', {'title': _('Trouble Ticket')}],
328
                            ['', {}]])
329
        events = [lst_title]
330
        i = 0
331
        class_tr = ['odd', 'even']
332
        ids = []
333
        for req in self.req[first_row : last_row]:
334
            # Si il y a plus d'un élément dans la liste des tables,
335
            # req devient une liste plutôt que d'être directement la
336
            # table souhaitée
337

    
338
            if isinstance(req, EventsAggregate):
339
                event = req
340
            else:
341
                event = req[0]
342
            ids.append(event.idcause)
343

    
344
            # La liste pour l'évènement actuel comporte dans l'ordre :
345
            #   L'évènement en lui-même
346
            #   La classe à appliquer sur la ligne (permet d'alterner les
347
            #       couleurs suivant les lignes)
348
            #   La classe pour la case comportant la flèche de détails
349
            #   La classe pour la date, l'occurence et l'édition
350
            #   L'image à afficher pour la flèche de détails
351
            #   Une liste (une case par plugin) de ce que le plugin souhaite
352
            #       afficher en fonction de l'évènement
353

    
354
            current_severity = self.get_severity(event.current_severity)
355
            initial_severity = self.get_severity(event.initial_severity)
356

    
357
            events.append([
358
                    event,
359
                    {'class': class_tr[i % 2]},
360
                    {'class': self.bouton_severity[initial_severity] + \
361
                        self.class_ack[event.status]},
362
                    {'class': self.bouton_severity[current_severity] + \
363
                        self.class_ack[event.status]},
364
                    {'src': '/images/%s2.png' % \
365
                        self.bouton_severity[current_severity].upper()},
366
                    self.format_events_img_status(event),
367
                    [[j.__show__(event), j.style] for j in self.plugin]
368
                ])
369
            i += 1
370

    
371
        # On sauvegarde la liste précédemment créée puis on remplit
372
        # le TmplContext
373
        self.events = events
374
        self.idevents = ids
375

    
376
    def format_history(self):
377
        
378
        """
379
        Formate les historiques correspondant aux évènements sélectionnés
380
        pour un affichage simple du résultat par Genshi.
381
        On génère une liste de liste, chaqu'une étant la description
382
        de l'affichage pour un historique donné.
383
        """
384

    
385
        history = DBSession.query(
386
                    EventHistory,
387
                ).filter(EventHistory.idevent.in_(self.idevents)
388
                ).order_by(desc(EventHistory.timestamp)
389
                ).order_by(desc(EventHistory.idhistory))
390
        if history.count() == 0:
391
            self.hist = {}
392
            for i in self.idevents:
393
                self.hist[i] = []
394
            return
395

    
396
        hists = {}
397
        i = 0
398
        class_tr = ['odd', 'even']
399

    
400
        for hist in history:
401
            if not hist.idevent in hists:
402
                hists[hist.idevent] = []
403

    
404
            # La liste pour l'historique actuel comporte dans l'ordre :
405
            #   Le moment où il a été généré
406
            #   Qui l'a généré
407
            #   Le type d'action qui a été appliqué
408
            #   La valeur de l'action
409
            #   Le détail de l'action
410
            #   La classe à appliquer à la ligne
411
            #       (permet d'alterner les couleurs)
412

    
413
            hists[hist.idevent].append([
414
                hist.timestamp,
415
                hist.username,
416
                hist.type_action,
417
                hist.value,
418
                hist.text,
419
                {'class': class_tr[i % 2]},
420
            ])
421
            i += 1
422
        
423
        self.hist = hists
424

    
425
    def generate_tmpl_context(self):
426
        
427
        """
428
        Génère et peuple la variable tmpl_context avec les Dialogs et
429
        formulaires nécessaire au fonctionnement de Vigiboard
430
        """
431

    
432
        # Dialogue d'édition
433
        tmpl_context.edit_event_form = EditEventForm('edit_event_form',
434
                action=url('/update'))
435
        tmpl_context.edit_eventdialog = JQueryUIDialog(id='Edit_EventsDialog',
436
                autoOpen=False,title=_('Edit Event'))
437
    
438
        # Dialogue de recherche
439
        tmpl_context.search_form = SearchForm('search_form',
440
                action=url('/'))
441
        tmpl_context.searchdialog = JQueryUIDialog(id='SearchDialog',
442
                autoOpen=False, title=_('Search Event'))
443
        
444
        # Dialogue de détail d'un évènement
445
        tmpl_context.historydialog = JQueryUIDialog(id='HistoryDialog',
446
                autoOpen=False, title=_('History'))
447

    
448
        # Exécution des contexts des plugins
449
        for j in self.plugin:
450
            j.context(self.context_fct)
451