Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / vigiboardrequest.py @ 693e96f1

History | View | Annotate | Download (15.2 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
7
from tg import tmpl_context, url, config
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.userutils import get_user_groups
13
from vigiboard.controllers.vigiboard_plugin import VigiboardRequestPlugin
14
from pylons.i18n import ugettext as _
15

    
16
class VigiboardRequest():
17
    
18
    """
19
    Classe gérant la génération de la requête finale,
20
    le préformatage des évènements et celui des historiques
21
    """
22

    
23
    def __init__(self):
24

    
25
        """
26
        Initialisation de toutes les variables nécessaires: Liste des groupes de
27
        l'utilisateur, les classes à appliquer suivant la sévérité, les
28
        différentes étapes de la génération de la requête et la liste des
29
        plugins appliqués.
30
        """
31

    
32
        self.user_groups = get_user_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['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 = [['',{}], [_('Date')+ '<span style="font-weight:normal">' + _('<br />[Duration]') + '</span>', {'style':'text-align:left'}], ['#',{'title':_('Nombre d\'occurrences')}], [_('Host'),{'style':'text-align:left'}],
278
                [_('Service Type<br />Service Name'),{'style':'text-align:left'}], [_('Output'),{'style':'text-align:left'}]]
279
        lst_title.extend([[plug.name,plug.style] for plug in self.plugin])
280
        lst_title.extend([[_('[TT]'),{'title':_('Trouble Ticket')}], ['',{}]])
281
        events = [lst_title]
282
        i = 0
283
        class_tr = ['odd', 'even']
284
        ids = []
285
        for req in self.req[first_row : last_row]:
286

    
287
            # Si il y a plus d'un élément dans la liste des tables,
288
            # rq devient une liste plutôt que d'être directement la
289
            # table souhaité
290
            
291
            if isinstance(req, Events) :
292
                event = req
293
            else :
294
                event = req[0]
295
            ids.append(event.idevent)
296

    
297
            # La liste pour l'évènement actuel comporte dans l'ordre :
298
            #   L'évènment en lui même
299
            #   La classe à appliquer sur la ligne (permet d'alterner les
300
            #       couleurs suivant les lignes)
301
            #   La classe pour la case comportant la flèche de détails
302
            #   La classe pour la date, l'occurrence et l'édition
303
            #   L'image a affiche pour la flèche de détails
304
            #   Une liste (une case par plugin) de ce que le plugin souhaite
305
            #       afficher en fonction de l'évènement
306

    
307
            if event.active :
308
                events.append([
309
                    event,
310
                    {'class': class_tr[i%2]},
311
                    {'class' : self.bouton_severity[event.severity] + \
312
                            self.class_ack[event.status]},
313
                    {'class' : self.bouton_severity[event.severity] + \
314
                            self.class_ack[event.status] },
315
                    {'src' : '/images/%s2.png' % \
316
                            self.bouton_severity[event.severity].upper()},
317
                    self.format_events_img_statu(event),
318
                    [[j.__show__(req), j.style] for j in self.plugin]
319
                    ])
320
            else :
321
                events.append([
322
                    event,
323
                    {'class': class_tr[i%2]},
324
                    {'class' : self.bouton_severity[event.severity] + \
325
                            self.class_ack[event.status] },
326
                    {'class' : 'Cleared' + self.class_ack[event.status] },
327
                    {'src' : '/images/%s2.png' % \
328
                            self.bouton_severity[event.severity].upper()},
329
                    self.format_events_img_statu(event),
330
                    [[j.__show__(req), j.style] for j in self.plugin]
331
                    ])
332
            i = i + 1
333

    
334
        # On sauvegarde la liste précédemment créée puis rempli
335
        # le TmplContext
336

    
337
        self.events = events
338
        self.idevents = ids
339

    
340
    def format_history (self):
341
        
342
        """
343
        Formate les historiques correspondant aux évènements sélectionnés
344
        pour un affichage simple du résultat par Genshi.
345
        On génère une liste de liste, chaqu'une étant la description de l'affichage pour un
346
        historique donné.
347
        """
348

    
349
        history = DBSession.query(EventHistory
350
                ).filter(EventHistory.idevent.in_(self.idevents)
351
                ).order_by(desc(EventHistory.timestamp)
352
                ).order_by(desc(EventHistory.idhistory))
353
        if history.count() == 0:
354
            self.hist = {}
355
            for i in self.idevents:
356
                self.hist[i] = []
357
            return
358
        hists = {}
359
        i = 0
360
        class_tr = ['odd', 'even']
361
        hostname = self.events[1][0].hostname
362
        servicename = self.events[1][0].servicename
363
        hist_tmp = []
364
        last_idevent = history[0].idevent
365
        for hist in history :
366
            
367
            if last_idevent != hist.idevent:
368
                hists[last_idevent] = hist_tmp
369
                last_idevent = hist.idevent
370
                hist_tmp = []
371

    
372
            # La liste pour l'historique actuel comporte dans l'ordre :
373
            #   Le moment où il a été généré
374
            #   Qui l'a généré
375
            #   Le type d'action qui a été appliqué
376
            #   La sévérité de l'action si besoin est
377
            #   Le détail de l'action
378
            #   La classe à appliquer à la ligne (permet d'alterner
379
            #       les couleurs)
380
            #   La classe de la sévérité s'il y a
381

    
382
            if hist.value :
383
                hist_tmp.append([
384
                    hist.timestamp,
385
                    hist.username,
386
                    hist.type_action,
387
                    self.severity[min(int(hist.value),7)],
388
                    hist.text,
389
                    {'class' : class_tr[i%2]},
390
                    {'class':self.class_severity[min(int(hist.value),7)]}
391
                ])
392
            else:
393
                hist_tmp.append([
394
                    hist.timestamp,
395
                    hist.username,
396
                    hist.type_action,
397
                    self.severity[0],
398
                    hist.text,
399
                    {'class' : class_tr[i%2]},
400
                    {'class':self.class_severity[0]}
401
                ])    
402
            i = i + 1
403
        
404
        hists[last_idevent] = hist_tmp
405
        self.hist = hists
406

    
407
    def generate_tmpl_context(self):
408
        
409
        """
410
        Génère et peuple la variable tmpl_context avec les Dialogs et
411
        formulaires nécessaire au fonctionnement de Vigiboard
412
        """
413

    
414
        # Dialogue d'édition
415
        tmpl_context.edit_event_form = EditEventForm('edit_event_form',
416
                action=url('/update'))
417
        tmpl_context.edit_eventdialog = JQueryUIDialog(id='Edit_EventsDialog',
418
                autoOpen=False,title=_('Edit Event'))
419
    
420
        # Dialogue de recherche
421
        tmpl_context.search_form = SearchForm('search_form',
422
                action=url('/'))
423
        tmpl_context.searchdialog = JQueryUIDialog(id='SearchDialog',
424
                autoOpen=False,title=_('Search Event'))
425
        
426
        # Dialogue de détail d'un évènement
427
        tmpl_context.historydialog = JQueryUIDialog(id='HistoryDialog',
428
                autoOpen=False,title=_('History'))
429

    
430
        # Exécution des contexts des plugins
431
        for j in self.plugin:
432
            j.context(self.context_fct)