Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / vigiboardrequest.py @ d7d9e9eb

History | View | Annotate | Download (15.1 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':_('Occurrence')}], [_('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
        print history
354
        if history.count() == 0:
355
            self.hist = []
356
            return
357
        hists = {}
358
        i = 0
359
        class_tr = ['odd', 'even']
360
        hostname = self.events[1][0].hostname
361
        servicename = self.events[1][0].servicename
362
        hist_tmp = []
363
        last_idevent = history[0].idevent
364
        for hist in history :
365
            
366
            if last_idevent != hist.idevent:
367
                hists[last_idevent] = hist_tmp
368
                last_idevent = hist.idevent
369
                hist_tmp = []
370

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

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

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

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

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