Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / root.py @ 228aad1c

History | View | Annotate | Download (17.5 KB)

1
# -*- coding: utf-8 -*-
2
# vim:set expandtab tabstop=4 shiftwidth=4: 
3
"""Vigiboard Controller"""
4

    
5
from tg import expose, validate, require, flash, \
6
    tmpl_context, request, config, session, redirect, url
7
from tw.forms import validators
8
from pylons.i18n import ugettext as _
9
from pylons.i18n import lazy_ugettext as l_
10
from pylons.controllers.util import abort
11
from sqlalchemy import not_, and_, asc
12
from datetime import datetime
13
import math
14

    
15
from vigiboard.model import DBSession
16
from vigiboard.model import Event, EventHistory, CorrEvent, \
17
                            Host, HostGroup, \
18
                            StateName, User, ServiceLowLevel
19
from repoze.what.predicates import Any, not_anonymous
20
from vigiboard.widgets.edit_event import edit_event_status_options
21
from vigiboard.controllers.vigiboardrequest import VigiboardRequest
22
from vigiboard.controllers.vigiboard_controller import VigiboardRootController
23
from vigilo.models.secondary_tables import HOST_GROUP_TABLE, \
24
                                            SERVICE_GROUP_TABLE
25

    
26
__all__ = ('RootController', )
27

    
28
class RootController(VigiboardRootController):
29
    """
30
    Le controller général de vigiboard
31
    """
32

    
33
    # XXX Mettre ça dans un fichier de configuration.
34
    refresh_times = (
35
        (0, l_('Never')),
36
        (30, l_('30 seconds')),
37
        (60, l_('1 minute')),
38
        (300, l_('5 minutes')),
39
        (600, l_('10 minutes')),
40
    )
41

    
42
    def process_form_errors(self, *argv, **kwargv):
43
        """
44
        Gestion des erreurs de validation : On affiche les erreurs
45
        puis on redirige vers la dernière page accédée.
46
        """
47
        for k in tmpl_context.form_errors:
48
            flash("'%s': %s" % (k, tmpl_context.form_errors[k]), 'error')
49
        if request.environ.get('HTTP_REFERER') :
50
            redirect(request.environ.get('HTTP_REFERER'
51
                ).split(request.environ.get('HTTP_HOST'))[1])
52
        else :
53
            redirect('/')
54

    
55
    @expose('vigiboard.html')
56
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
57
    def default(self, page = None, host = None, service = None, output = None,
58
            trouble_ticket=None, *argv, **krgv):
59
            
60
        """
61
        Page d'accueil de Vigiboard. Elle affiche, suivant la page demandée
62
        (page 1 par defaut), la liste des événements, rangés par ordre de prise
63
        en compte, puis de sévérité.
64
        Pour accéder à cette page, l'utilisateur doit être authentifié.
65

66
        @param page: Numéro de la page souhaitée, commence à 1
67
        @param host: Si l'utilisateur souhaite sélectionner seulement certains
68
                     événements suivant leur hôte, il peut placer une expression
69
                     ici en suivant la structure du LIKE en SQL
70
        @param service: Idem que host mais sur les services
71
        @param output: Idem que host mais sur le text explicatif
72
        @param trouble_ticket: Idem que host mais sur les tickets attribués
73
        """
74
        if page is None:
75
            page = 1
76

    
77
        try:
78
            page = int(page)
79
        except ValueError:
80
            abort(404)
81

    
82
        if page < 1:
83
            page = 1
84

    
85
        username = request.environ['repoze.who.identity']['repoze.who.userid']
86
        user = User.by_user_name(username)
87
        aggregates = VigiboardRequest(user)
88
        
89
        search = {
90
            'host': '',
91
            'service': '',
92
            'output': '',
93
            'tt': ''
94
        }
95
        # Application des filtres si nécessaire
96
        if host :
97
            search['host'] = host
98
            host = host.replace('%', '\\%').replace('_', '\\_') \
99
                    .replace('*', '%').replace('?', '_')
100
            aggregates.add_filter(Event.hostname.ilike('%%%s%%' % host))
101

    
102
        if service :
103
            search['service'] = service
104
            service = service.replace('%', '\\%').replace('_', '\\_') \
105
                    .replace('*', '%').replace('?', '_')
106
            aggregates.add_filter(Event.servicename.ilike('%%%s%%' % service))
107

    
108
        if output :
109
            search['output'] = output
110
            output = output.replace('%', '\\%').replace('_', '\\_') \
111
                    .replace('*', '%').replace('?', '_')
112
            aggregates.add_filter(Event.message.ilike('%%%s%%' % output))
113

    
114
        if trouble_ticket :
115
            search['tt'] = trouble_ticket
116
            trouble_ticket = trouble_ticket.replace('%', '\\%') \
117
                    .replace('_', '\\_').replace('*', '%').replace('?', '_')
118
            aggregates.add_filter(CorrEvent.trouble_ticket.ilike(
119
                '%%%s%%' % trouble_ticket))
120

    
121
        # Calcul des éléments à afficher et du nombre de pages possibles
122
        total_rows = aggregates.num_rows()
123
        items_per_page = int(config['vigiboard_items_per_page'])
124

    
125
        id_first_row = items_per_page * (page-1)
126
        id_last_row = min(id_first_row + items_per_page, total_rows)
127

    
128
        aggregates.format_events(id_first_row, id_last_row)
129
        aggregates.generate_tmpl_context()
130

    
131
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
132
        if not total_rows:
133
            id_first_row = 0
134
        else:
135
            id_first_row += 1
136

    
137
        return dict(
138
                   events = aggregates.events,
139
                   rows_info = {
140
                       'id_first_row': id_first_row,
141
                       'id_last_row': id_last_row,
142
                       'total_rows': total_rows,
143
                   },
144
                   nb_pages = nb_pages,
145
                   page = page,
146
                   event_edit_status_options = edit_event_status_options,
147
                   history = [],
148
                   hist_error = False,
149
                   plugin_context = aggregates.context_fct,
150
                   search = search,
151
                   refresh_times=self.refresh_times,
152
                )
153
      
154
    @validate(validators={'idcorrevent':validators.Int(not_empty=True)},
155
            error_handler=process_form_errors)
156
    @expose('json')
157
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
158
    def history_dialog(self, idcorrevent):
159
        
160
        """
161
        JSon renvoyant les éléments pour l'affichage de la fenêtre de dialogue
162
        contenant des liens internes et externes.
163
        Pour accéder à cette page, l'utilisateur doit être authentifié.
164

165
        @param id: identifiant de l'événement
166
        """
167

    
168
        # Obtention de données sur l'événement et sur son historique
169
        username = request.environ.get('repoze.who.identity'
170
                    ).get('repoze.who.userid')
171
        user = User.by_user_name(username)
172
        user_groups = user.groups
173

    
174
#        try:
175
        event = DBSession.query(
176
                        CorrEvent.priority,
177
                        Event,
178
                 ).join(
179
                    (Event, CorrEvent.idcause == Event.idevent),
180
                    (ServiceLowLevel, Event.idsupitem == ServiceLowLevel.idservice),
181
                    (Host, Host.idhost == ServiceLowLevel.idhost),
182
                    (HOST_GROUP_TABLE, HOST_GROUP_TABLE.c.idhost == Host.idhost),
183
                    (SERVICE_GROUP_TABLE, SERVICE_GROUP_TABLE.c.idservice == \
184
                        ServiceLowLevel.idservice),
185
                 ).filter(HOST_GROUP_TABLE.c.idgroup.in_(user_groups)
186
                 ).filter(SERVICE_GROUP_TABLE.c.idgroup.in_(user_groups)
187
                 ).filter(
188
                    # On masque les événements avec l'état OK
189
                    # et traités (status == u'AAClosed').
190
                    not_(and_(
191
                        StateName.statename == u'OK',
192
                        CorrEvent.status == u'AAClosed'
193
                    ))
194
                ).filter(CorrEvent.idcorrevent == idcorrevent
195
                ).one()
196
#        except:
197
#            # XXX Raise some HTTP error.
198
#            return None
199

    
200
        history = DBSession.query(
201
                    EventHistory,
202
                 ).filter(EventHistory.idevent == event[1].idevent
203
                 ).order_by(asc(EventHistory.timestamp)
204
                 ).order_by(asc(EventHistory.type_action)).all()
205

    
206
        eventdetails = {}
207
        for edname, edlink in \
208
                config['vigiboard_links.eventdetails'].iteritems():
209

    
210
            eventdetails[edname] = edlink[1] % {
211
                    'idcorrevent': idcorrevent,
212
                    'host': event[1].supitem.host.name,
213
                    'service': event[1].supitem.servicename
214
                    }
215

    
216
        return dict(
217
                current_state = StateName.value_to_statename(
218
                                    event[1].current_state),
219
                initial_state = StateName.value_to_statename(
220
                                    event[1].initial_state),
221
                peak_state = StateName.value_to_statename(
222
                                    event[1].peak_state),
223
                idcorrevent = idcorrevent,
224
                host = event[1].supitem.host.name,
225
                service = event[1].supitem.servicename,
226
                eventdetails = eventdetails,
227
            )
228

    
229
    @validate(validators={'idcorrevent':validators.Int(not_empty=True)},
230
            error_handler=process_form_errors)
231
    @expose('vigiboard.html')
232
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
233
    def event(self, idcorrevent):
234
        """
235
        Affichage de l'historique d'un événement.
236
        Pour accéder à cette page, l'utilisateur doit être authentifié.
237

238
        @param idevent: identifiant de l'événement souhaité
239
        """
240

    
241
        username = request.environ['repoze.who.identity']['repoze.who.userid']
242
        events = VigiboardRequest(User.by_user_name(username))
243
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
244
        
245
        # Vérification que l'événement existe
246
        if events.num_rows() != 1 :
247
            flash(_('Error in DB'), 'error')
248
            redirect('/')
249
       
250
        events.format_events(0, 1)
251
        events.format_history()
252
        events.generate_tmpl_context() 
253

    
254
        return dict(
255
                    events = events.events,
256
                    rows_info = {
257
                        'id_first_row': 1,
258
                        'id_last_row': 1,
259
                        'total_rows': 1,
260
                    },
261
                    nb_pages = 1,
262
                    page = 1,
263
                    event_edit_status_options = edit_event_status_options,
264
                    history = events.hist,
265
                    hist_error = True,
266
                    plugin_context = events.context_fct,
267
                    search = {
268
                        'host': None,
269
                        'service': None,
270
                        'output': None,
271
                        'tt': None
272
                    },
273
                   refresh_times=self.refresh_times,
274
                )
275

    
276
    @validate(validators={'host':validators.NotEmpty(),
277
        'service':validators.NotEmpty()}, error_handler=process_form_errors)
278
    @expose('vigiboard.html')
279
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
280
    def host_service(self, host, service):
281
        
282
        """
283
        Affichage de l'historique de l'ensemble des événements correspondant
284
        au host et service demandé.
285
        Pour accéder à cette page, l'utilisateur doit être authentifié.
286

287
        @param host: Nom de l'hôte souhaité.
288
        @param service: Nom du service souhaité
289
        """
290

    
291
        username = request.environ['repoze.who.identity']['repoze.who.userid']
292
        events = VigiboardRequest(User.by_user_name(username))
293
        events.add_join((ServiceLowLevel, ServiceLowLevel.idservice == Event.idsupitem))
294
        events.add_join((Host, ServiceLowLevel.idhost == Host.idhost))
295
        events.add_filter(Host.name == host,
296
                ServiceLowLevel.servicename == service)
297

    
298
        # XXX On devrait avoir une autre API que ça !!!
299
        # Supprime le filtre qui empêche d'obtenir des événements fermés
300
        # (ie: ayant l'état Nagios 'OK' et le statut 'AAClosed').
301
        if len(events.filter) > 2:
302
            del events.filter[2]
303

    
304
        # Vérification qu'il y a au moins 1 événement qui correspond
305
        if events.num_rows() == 0 :
306
            redirect('/')
307

    
308
        events.format_events(0, events.num_rows())
309
        events.format_history()
310
        events.generate_tmpl_context()
311

    
312
        return dict(
313
                    events = events.events,
314
                    rows_info = {
315
                        'id_first_row': 1,
316
                        'id_last_row': 1,
317
                        'total_rows': 1,
318
                    },
319
                    nb_pages = 1,
320
                    page = 1,
321
                    event_edit_status_options = edit_event_status_options,
322
                    history = events.hist,
323
                    hist_error = True,
324
                    plugin_context = events.context_fct,
325
                    search = {
326
                        'host': None,
327
                        'service': None,
328
                        'output': None,
329
                        'tt': None
330
                    },
331
                    refresh_times=self.refresh_times,
332
                )
333

    
334
    @validate(validators={
335
        "id":validators.Regex(r'^[^,]+(,[^,]*)*,?$'),
336
#        "trouble_ticket":validators.Regex(r'^[0-9]*$'),
337
        "status":validators.OneOf(['NoChange', 'None', 'Acknowledged',
338
                'AAClosed'])
339
        }, error_handler=process_form_errors)
340
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
341
    def update(self,**krgv):
342
        
343
        """
344
        Mise à jour d'un événement suivant les arguments passés.
345
        Cela peut être un changement de ticket ou un changement de statut.
346
        
347
        @param krgv['id']: Le ou les identifiants des événements à traiter
348
        @param krgv['tt']: Nouveau numéro du ticket associé.
349
        @param krgv['status']: Nouveau status de/des événements.
350
        """
351
        
352
        # Si l'utilisateur édite plusieurs événements à la fois,
353
        # il nous faut chacun des identifiants
354

    
355
        if krgv['id'] is None:
356
            flash(_('No event has been selected'), 'warning')
357
            raise redirect(request.environ.get('HTTP_REFERER', url('/')))
358

    
359
        ids = krgv['id'].split(',')
360
       
361
        if len(ids) > 1 :
362
            ids = ids[:-1]
363
        
364
        username = request.environ['repoze.who.identity']['repoze.who.userid']
365
        events = VigiboardRequest(User.by_user_name(username))
366
        events.add_filter(CorrEvent.idcorrevent.in_(ids))
367
        
368
        # Vérification que au moins un des identifiants existe et est éditable
369
        if events.num_rows() <= 0 :
370
            flash(_('No access to this event'), 'error')
371
            redirect('/')
372
        
373
        # Modification des événements et création d'un historique
374
        # pour chacun d'eux.
375
        username = request.environ['repoze.who.identity']['repoze.who.userid']
376

    
377
        for req in events.req:
378
            if isinstance(req, CorrEvent):
379
                event = req
380
            else:
381
                event = req[0]
382

    
383
            if krgv['trouble_ticket'] != '' :
384
                history = EventHistory(
385
                        type_action="Ticket change",
386
                        idevent=event.idcause,
387
                        value=krgv['trouble_ticket'],
388
                        text=_("Changed trouble ticket from '%s' to '%s'") % (
389
                            event.trouble_ticket, krgv['trouble_ticket']
390
                        ),
391
                        username=username,
392
                        timestamp=datetime.now(),
393
                    )
394
                DBSession.add(history)   
395
                event.trouble_ticket = krgv['trouble_ticket']
396

    
397
            if krgv['status'] != 'NoChange' :
398
                history = EventHistory(
399
                        type_action="Acknowlegement change state",
400
                        idevent=event.idcause,
401
                        value=krgv['status'],
402
                        text=_("Changed acknowledgement status from '%s' to '%s'") % (
403
                            event.status, krgv['status']
404
                        ),
405
                        username=username,
406
                        timestamp=datetime.now(),
407
                    )
408
                DBSession.add(history)
409
                event.status = krgv['status']
410

    
411
        DBSession.flush()
412
        flash(_('Updated successfully'))
413
        redirect(request.environ.get('HTTP_REFERER', url('/')))
414

    
415

    
416
    @validate(validators={"plugin_name":validators.OneOf(
417
        [i for [i, j] in config.get('vigiboard_plugins', [])])},
418
                error_handler = process_form_errors)
419
    @expose('json')
420
    def get_plugin_value(self, plugin_name, *arg, **krgv):
421
        """
422
        Permet aux plugins de pouvoir récupérer des valeurs Json
423
        """
424
        plugins = config['vigiboard_plugins']
425
        if plugins is None:
426
            return
427

    
428
        plugin = [i for i in plugins if i[0] == plugin_name][0]
429
        try:
430
            mypac = __import__(
431
                'vigiboard.controllers.vigiboard_plugin.' + plugin[0],
432
                globals(), locals(), [plugin[1]], -1)
433
            plug = getattr(mypac, plugin[1])()
434
            return plug.controller(*arg, **krgv)
435
        except:
436
            raise
437
    
438
#    @validate(validators= {"fontsize": validators.Int()},
439
#                    error_handler = process_form_errors)
440
    @expose('json')
441
    def set_fontsize(self, fontsize):
442
        """
443
        Save font size
444
        """
445
        session['fontsize'] = fontsize
446
        session.save()
447
        return dict(ret= 'ok')
448

    
449
    @validate(validators= {"refresh": validators.Int()},
450
            error_handler = process_form_errors)
451
    @expose('json')
452
    def set_refresh(self, refresh):
453
        """
454
        Save refresh time
455
        """
456
        session['refresh'] = refresh
457
        session.save()
458
        return dict(ret= 'ok')
459