Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / root.py @ 2b740fc8

History | View | Annotate | Download (30 KB)

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

    
5
from datetime import datetime
6
from time import mktime
7
import math
8

    
9
from tg.exceptions import HTTPNotFound, HTTPInternalServerError
10
from tg import expose, validate, require, flash, \
11
    tmpl_context, request, config, session, redirect
12
from tw.forms import validators
13
from pylons.i18n import ugettext as _
14
from pylons.i18n import lazy_ugettext as l_
15
from sqlalchemy import asc
16
from sqlalchemy.sql import func
17
from repoze.what.predicates import Any, All, in_group, \
18
                                    has_permission, not_anonymous
19
from formencode import validators, schema
20

    
21
from vigilo.models.session import DBSession
22
from vigilo.models.tables import Event, EventHistory, CorrEvent, Host, \
23
                                    SupItem, SupItemGroup, LowLevelService, \
24
                                    StateName
25
from vigilo.models.functions import sql_escape_like
26
from vigilo.models.tables.secondary_tables import EVENTSAGGREGATE_TABLE
27

    
28
from vigilo.turbogears.controllers.autocomplete import AutoCompleteController
29
from vigilo.turbogears.helpers import get_current_user
30

    
31
from vigiboard.controllers.vigiboardrequest import VigiboardRequest
32
from vigiboard.controllers.vigiboard_controller import VigiboardRootController
33
from vigiboard.widgets.edit_event import edit_event_status_options
34

    
35
__all__ = ('RootController', 'get_last_modification_timestamp', 
36
           'date_to_timestamp')
37

    
38
# pylint: disable-msg=R0201
39
class RootController(VigiboardRootController):
40
    """
41
    Le controller général de vigiboard
42
    """
43
    autocomplete = AutoCompleteController()
44

    
45
    # Prédicat pour la restriction de l'accès aux interfaces.
46
    # L'utilisateur doit avoir la permission "vigiboard-access"
47
    # ou appartenir au groupe "managers" pour accéder à VigiBoard.
48
    access_restriction = All(
49
        not_anonymous(msg=l_("You need to be authenticated")),
50
        Any(in_group('managers'),
51
            has_permission('vigiboard-access'),
52
            msg=l_("You don't have access to VigiBoard"))
53
    )
54

    
55
    def process_form_errors(self, *argv, **kwargv):
56
        """
57
        Gestion des erreurs de validation : On affiche les erreurs
58
        puis on redirige vers la dernière page accédée.
59
        """
60
        for k in tmpl_context.form_errors:
61
            flash("'%s': %s" % (k, tmpl_context.form_errors[k]), 'error')
62
        redirect(request.environ.get('HTTP_REFERER', '/'))
63

    
64
    @expose('json')
65
    def handle_validation_errors_json(self, *args, **kwargs):
66
        kwargs['errors'] = tmpl_context.form_errors
67
        return dict(kwargs)
68
    
69
    class DefaultSchema(schema.Schema):
70
        """Schéma de validation de la méthode default."""
71
        page = validators.Int(min=1, if_missing=1, if_invalid=1)
72
        supitemgroup = validators.String(if_missing=None)
73
        host = validators.String(if_missing=None)
74
        service = validators.String(if_missing=None)
75
        output = validators.String(if_missing=None)
76
        trouble_ticket = validators.String(if_missing=None)
77
        from_date = validators.String(if_missing=None)
78
        to_date = validators.String(if_missing=None)
79

    
80
    @validate(
81
        validators=DefaultSchema(),
82
        error_handler = process_form_errors)
83
    @expose('events_table.html')
84
    @require(access_restriction)
85
    def default(self, page, supitemgroup, host, service,
86
                output, trouble_ticket, from_date, to_date):
87
        """
88
        Page d'accueil de Vigiboard. Elle affiche, suivant la page demandée
89
        (page 1 par defaut), la liste des événements, rangés par ordre de prise
90
        en compte, puis de sévérité.
91
        Pour accéder à cette page, l'utilisateur doit être authentifié.
92

93
        @param page: Numéro de la page souhaitée, commence à 1
94
        @param host: Si l'utilisateur souhaite sélectionner seulement certains
95
                     événements suivant leur hôte, il peut placer une expression
96
                     ici en suivant la structure du LIKE en SQL
97
        @param service: Idem que host mais sur les services
98
        @param output: Idem que host mais sur le text explicatif
99
        @param trouble_ticket: Idem que host mais sur les tickets attribués
100

101
        Cette méthode permet de satisfaire les exigences suivantes : 
102
            - VIGILO_EXIG_VIGILO_BAC_0040, 
103
            - VIGILO_EXIG_VIGILO_BAC_0070,
104
            - VIGILO_EXIG_VIGILO_BAC_0100,
105
        """
106
        user = get_current_user()
107
        aggregates = VigiboardRequest(user)
108
        aggregates.add_table(
109
            CorrEvent,
110
            aggregates.items.c.hostname,
111
            aggregates.items.c.servicename
112
        )
113
        aggregates.add_join((Event, CorrEvent.idcause == Event.idevent))
114
        aggregates.add_join((aggregates.items, 
115
            Event.idsupitem == aggregates.items.c.idsupitem))
116
        aggregates.add_order_by(asc(aggregates.items.c.hostname))
117
        
118
        search = {
119
            'host': '',
120
            'service': '',
121
            'output': '',
122
            'tt': '',
123
            'from_date': '',
124
            'to_date': '',
125
            'supitemgroup': '',
126
        }
127

    
128
        # Application des filtres si nécessaire
129
        if supitemgroup:
130
            search['supitemgroup'] = supitemgroup
131
            supitemgroup = sql_escape_like(supitemgroup)
132
            aggregates.add_join((SupItemGroup, SupItemGroup.idgroup == \
133
                aggregates.items.c.idsupitemgroup))
134
            aggregates.add_filter(
135
                SupItemGroup.name.ilike('%s' % supitemgroup))
136

    
137
        if host:
138
            search['host'] = host
139
            host = sql_escape_like(host)
140
            aggregates.add_filter(aggregates.items.c.hostname.ilike(
141
                '%s' % host))
142

    
143
        if service:
144
            search['service'] = service
145
            service = sql_escape_like(service)
146
            aggregates.add_filter(aggregates.items.c.servicename.ilike(
147
                '%s' % service))
148

    
149
        if output:
150
            search['output'] = output
151
            output = sql_escape_like(output)
152
            aggregates.add_filter(Event.message.ilike('%s' % output))
153

    
154
        if trouble_ticket:
155
            search['tt'] = trouble_ticket
156
            trouble_ticket = sql_escape_like(trouble_ticket)
157
            aggregates.add_filter(CorrEvent.trouble_ticket.ilike(
158
                '%s' % trouble_ticket))
159

    
160
        if from_date:
161
            search['from_date'] = from_date.lower()
162
            try:
163
                # TRANSLATORS: Format de date et heure Python/JavaScript.
164
                # TRANSLATORS: http://www.dynarch.com/static/jscalendar-1.0/doc/html/reference.html#node_sec_5.3.5
165
                # TRANSLATORS: http://docs.python.org/release/2.5/lib/module-time.html
166
                from_date = datetime.strptime(
167
                    from_date, _('%Y-%m-%d %I:%M:%S %p'))
168
            except ValueError:
169
                # On ignore silencieusement la date invalide reçue.
170
                pass
171
            else:
172
                aggregates.add_filter(CorrEvent.timestamp_active >= from_date)
173

    
174
        if to_date:
175
            search['to_date'] = to_date.lower()
176
            try:
177
                # TRANSLATORS: Format de date et heure.
178
                # TRANSLATORS: http://www.dynarch.com/static/jscalendar-1.0/doc/html/reference.html#node_sec_5.3.5
179
                # TRANSLATORS: http://docs.python.org/release/2.5/lib/module-time.html
180
                to_date = datetime.strptime(
181
                    to_date, _('%Y-%m-%d %I:%M:%S %p'))
182
            except ValueError:
183
                # On ignore silencieusement la date invalide reçue.
184
                pass
185
            else:
186
                aggregates.add_filter(CorrEvent.timestamp_active <= to_date)
187

    
188
        # Calcul des éléments à afficher et du nombre de pages possibles
189
        total_rows = aggregates.num_rows()
190
        items_per_page = int(config['vigiboard_items_per_page'])
191

    
192
        # Si le numéro de page dépasse le nombre de pages existantes,
193
        # on redirige automatiquement vers la 1ère page.
194
        if total_rows and items_per_page * (page-1) > total_rows:
195
            redirect('/', page=1, **search)
196

    
197
        id_first_row = items_per_page * (page-1)
198
        id_last_row = min(id_first_row + items_per_page, total_rows)
199

    
200
        aggregates.format_events(id_first_row, id_last_row)
201
        aggregates.generate_tmpl_context()
202

    
203
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
204
        if not total_rows:
205
            id_first_row = 0
206
        else:
207
            id_first_row += 1
208

    
209
        return dict(
210
            hostname = None,
211
            servicename = None,
212
            events = aggregates.events,
213
            plugins = get_plugins_instances(),
214
            rows_info = {
215
                'id_first_row': id_first_row,
216
                'id_last_row': id_last_row,
217
                'total_rows': total_rows,
218
            },
219
            nb_pages = nb_pages,
220
            page = page,
221
            event_edit_status_options = edit_event_status_options,
222
            search = search,
223
            refresh_times = config['vigiboard_refresh_times'],
224
        )
225

    
226

    
227
    class MaskedEventsSchema(schema.Schema):
228
        """Schéma de validation de la méthode masked_events."""
229
        idcorrevent = validators.Int(not_empty=True)
230
        page = validators.Int(min=1, if_missing=1, if_invalid=1)
231

    
232
    @validate(
233
        validators=MaskedEventsSchema(),
234
        error_handler = process_form_errors)
235
    @expose('raw_events_table.html')
236
    @require(access_restriction)
237
    def masked_events(self, idcorrevent, page):
238
        """
239
        Affichage de la liste des événements bruts masqués d'un événement
240
        corrélé (événements agrégés dans l'événement corrélé).
241

242
        @param idcorrevent: identifiant de l'événement corrélé souhaité.
243
        @type idcorrevent: C{int}
244
        """
245
        user = get_current_user()
246

    
247
        # Récupère la liste des événements masqués de l'événement
248
        # corrélé donné par idcorrevent.
249
        events = VigiboardRequest(user, False)
250
        events.add_table(
251
            Event,
252
            events.items.c.hostname,
253
            events.items.c.servicename,
254
        )
255
        events.add_join((EVENTSAGGREGATE_TABLE, \
256
            EVENTSAGGREGATE_TABLE.c.idevent == Event.idevent))
257
        events.add_join((CorrEvent, CorrEvent.idcorrevent == \
258
            EVENTSAGGREGATE_TABLE.c.idcorrevent))
259
        events.add_join((events.items, 
260
            Event.idsupitem == events.items.c.idsupitem))
261
        events.add_filter(Event.idevent != CorrEvent.idcause)
262
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
263

    
264
        # Récupère l'instance de SupItem associé à la cause de
265
        # l'événement corrélé. Cette instance est utilisé pour
266
        # obtenir le nom d'hôte/service auquel la cause est
267
        # rattachée (afin de fournir un contexte à l'utilisateur).
268
        hostname = None
269
        servicename = None
270
        cause_supitem = DBSession.query(
271
                SupItem,
272
            ).join(
273
                (Event, Event.idsupitem == SupItem.idsupitem),
274
                (EVENTSAGGREGATE_TABLE, EVENTSAGGREGATE_TABLE.c.idevent ==
275
                    Event.idevent),
276
                (CorrEvent, CorrEvent.idcorrevent ==
277
                    EVENTSAGGREGATE_TABLE.c.idcorrevent),
278
            ).filter(CorrEvent.idcorrevent == idcorrevent
279
            ).filter(Event.idevent == CorrEvent.idcause
280
            ).one()
281

    
282
        if isinstance(cause_supitem, LowLevelService):
283
            hostname = cause_supitem.host.name
284
            servicename = cause_supitem.servicename
285
        elif isinstance(cause_supitem, Host):
286
            hostname = cause_supitem.name
287

    
288
        # Vérification que l'événement existe
289
        total_rows = events.num_rows()
290
        if total_rows < 1:
291
            flash(_('No masked event or access denied'), 'error')
292
            redirect('/')
293

    
294
        # Calcul des éléments à afficher et du nombre de pages possibles
295
        items_per_page = int(config['vigiboard_items_per_page'])
296

    
297
        id_first_row = items_per_page * (page-1)
298
        id_last_row = min(id_first_row + items_per_page, total_rows)
299

    
300
        events.format_events(id_first_row, id_last_row)
301
        events.generate_tmpl_context()
302

    
303
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
304
        if not total_rows:
305
            id_first_row = 0
306
        else:
307
            id_first_row += 1
308

    
309
        return dict(
310
            idcorrevent = idcorrevent,
311
            hostname = hostname,
312
            servicename = servicename,
313
            events = events.events,
314
            plugins = get_plugins_instances(),
315
            rows_info = {
316
                'id_first_row': id_first_row,
317
                'id_last_row': id_last_row,
318
                'total_rows': total_rows,
319
            },
320
            nb_pages = nb_pages,
321
            page = page,
322
            search = {
323
                'host': '',
324
                'service': '',
325
                'output': '',
326
                'tt': '',
327
                'from_date': '',
328
                'to_date': '',
329
                'supitemgroup': '',
330
            },
331
           refresh_times=config['vigiboard_refresh_times'],
332
        )
333

    
334

    
335
    class EventSchema(schema.Schema):
336
        """Schéma de validation de la méthode event."""
337
        idevent = validators.Int(not_empty=True)
338
        page = validators.Int(min=1, if_missing=1, if_invalid=1)
339

    
340
    @validate(
341
        validators=EventSchema(),
342
        error_handler = process_form_errors)
343
    @expose('history_table.html')
344
    @require(access_restriction)
345
    def event(self, idevent, page):
346
        """
347
        Affichage de l'historique d'un événement brut.
348
        Pour accéder à cette page, l'utilisateur doit être authentifié.
349

350
        @param idevent: identifiant de l'événement brut souhaité.
351
        @type idevent: C{int}
352
        @param page: numéro de la page à afficher.
353
        @type page: C{int}
354

355
        Cette méthode permet de satisfaire l'exigence
356
        VIGILO_EXIG_VIGILO_BAC_0080.
357
        """
358
        user = get_current_user()
359
        events = VigiboardRequest(user, False)
360
        events.add_table(
361
            Event,
362
            events.items.c.hostname.label('hostname'),
363
            events.items.c.servicename.label('servicename'),
364
        )
365
        events.add_join((EVENTSAGGREGATE_TABLE, \
366
            EVENTSAGGREGATE_TABLE.c.idevent == Event.idevent))
367
        events.add_join((CorrEvent, CorrEvent.idcorrevent == \
368
            EVENTSAGGREGATE_TABLE.c.idcorrevent))
369
        events.add_join((events.items, 
370
            Event.idsupitem == events.items.c.idsupitem))
371
        events.add_filter(Event.idevent == idevent)
372

    
373
        if events.num_rows() != 1:
374
            flash(_('No such event or access denied'), 'error')
375
            redirect('/')
376

    
377
        events.format_events(0, 1)
378
        events.generate_tmpl_context()
379
        history = events.format_history()
380

    
381
        total_rows = history.count()
382
        items_per_page = int(config['vigiboard_items_per_page'])
383

    
384
        id_first_row = items_per_page * (page-1)
385
        id_last_row = min(id_first_row + items_per_page, total_rows)
386

    
387
        history_entries = history[id_first_row : id_last_row]
388

    
389
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
390
        if not total_rows:
391
            id_first_row = 0
392
        else:
393
            id_first_row += 1
394

    
395
        event = events.req[0]
396

    
397
        return dict(
398
            idevent = idevent,
399
            hostname = event.hostname,
400
            servicename = event.servicename,
401
            plugins = get_plugins_instances(),
402
            rows_info = {
403
                'id_first_row': id_first_row,
404
                'id_last_row': id_last_row,
405
                'total_rows': total_rows,
406
            },
407
            nb_pages = nb_pages,
408
            page = page,
409
            history = history_entries,
410
            search = {
411
                'host': '',
412
                'service': '',
413
                'output': '',
414
                'tt': '',
415
                'from_date': '',
416
                'to_date': '',
417
                'supitemgroup': '',
418
            },
419
           refresh_times=config['vigiboard_refresh_times'],
420
        )
421

    
422

    
423
    class ItemSchema(schema.Schema):
424
        """Schéma de validation de la méthode item."""
425
        page = validators.Int(min=1, if_missing=1, if_invalid=1)
426
        host = validators.String(not_empty=True)
427
        service = validators.String(if_missing=None)
428

    
429
    @validate(
430
        validators=ItemSchema(),
431
        error_handler = process_form_errors)
432
    @expose('events_table.html')
433
    @require(access_restriction)
434
    def item(self, page, host, service):
435
        """
436
        Affichage de l'historique de l'ensemble des événements corrélés
437
        jamais ouverts sur l'hôte / service demandé.
438
        Pour accéder à cette page, l'utilisateur doit être authentifié.
439

440
        @param page: Numéro de la page à afficher.
441
        @param host: Nom de l'hôte souhaité.
442
        @param service: Nom du service souhaité
443

444
        Cette méthode permet de satisfaire l'exigence
445
        VIGILO_EXIG_VIGILO_BAC_0080.
446
        """
447
        idsupitem = SupItem.get_supitem(host, service)
448

    
449
        user = get_current_user()
450
        aggregates = VigiboardRequest(user, False)
451
        aggregates.add_table(
452
            CorrEvent,
453
            aggregates.items.c.hostname,
454
            aggregates.items.c.servicename,
455
        )
456
        aggregates.add_join((Event, CorrEvent.idcause == Event.idevent))
457
        aggregates.add_join((aggregates.items, 
458
            Event.idsupitem == aggregates.items.c.idsupitem))
459
        aggregates.add_filter(aggregates.items.c.idsupitem == idsupitem)
460

    
461
        # Vérification qu'il y a au moins 1 événement qui correspond
462
        total_rows = aggregates.num_rows()
463
        if not total_rows:
464
            flash(_('No access to this host/service or no event yet'), 'error')
465
            redirect('/')
466

    
467
        items_per_page = int(config['vigiboard_items_per_page'])
468

    
469
        id_first_row = items_per_page * (page-1)
470
        id_last_row = min(id_first_row + items_per_page, total_rows)
471

    
472
        aggregates.format_events(id_first_row, id_last_row)
473
        aggregates.generate_tmpl_context()
474

    
475
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
476
        if not total_rows:
477
            id_first_row = 0
478
        else:
479
            id_first_row += 1
480
        
481
        return dict(
482
            hostname = host,
483
            servicename = service,
484
            events = aggregates.events,
485
            plugins = get_plugins_instances(),
486
            rows_info = {
487
                'id_first_row': id_first_row,
488
                'id_last_row': id_last_row,
489
                'total_rows': total_rows,
490
            },
491
            nb_pages = nb_pages,
492
            page = page,
493
            event_edit_status_options = edit_event_status_options,
494
            search = {
495
                'host': '',
496
                'service': '',
497
                'output': '',
498
                'tt': '',
499
                'from_date': '',
500
                'to_date': '',
501
                'supitemgroup': '',
502
            },
503
            refresh_times=config['vigiboard_refresh_times'],
504
        )
505

    
506

    
507
    class UpdateSchema(schema.Schema):
508
        """Schéma de validation de la méthode update."""
509
        id = validators.Regex(r'^[0-9]+(,[0-9]+)*,?$')
510
        last_modification = validators.Number(not_empty=True)
511
        trouble_ticket = validators.String(if_missing='')
512
        ack = validators.OneOf(
513
            [unicode(s[0]) for s in edit_event_status_options],
514
            not_empty=True)
515

    
516
    @validate(
517
        validators=UpdateSchema(),
518
        error_handler = process_form_errors)
519
    @require(
520
        All(
521
            not_anonymous(msg=l_("You need to be authenticated")),
522
            Any(in_group('managers'),
523
                has_permission('vigiboard-update'),
524
                msg=l_("You don't have write access to VigiBoard"))
525
        ))
526
    @expose()
527
    def update(self, id, last_modification, trouble_ticket, ack):
528
        """
529
        Mise à jour d'un événement suivant les arguments passés.
530
        Cela peut être un changement de ticket ou un changement de statut.
531
        
532
        @param id: Le ou les identifiants des événements à traiter
533
        @param last_modification: La date de la dernière modification
534
            dont l'utilisateur est au courant.
535
        @param trouble_ticket: Nouveau numéro du ticket associé.
536
        @param ack: Nouvel état d'acquittement des événements sélectionnés.
537

538
        Cette méthode permet de satisfaire les exigences suivantes : 
539
            - VIGILO_EXIG_VIGILO_BAC_0020,
540
            - VIGILO_EXIG_VIGILO_BAC_0060,
541
            - VIGILO_EXIG_VIGILO_BAC_0110.
542
        """
543

    
544
        # On vérifie que des identifiants ont bien été transmis via
545
        # le formulaire, et on informe l'utilisateur le cas échéant.
546
        if id is None:
547
            flash(_('No event has been selected'), 'warning')
548
            raise redirect(request.environ.get('HTTP_REFERER', '/'))
549

    
550
        # On récupère la liste de tous les identifiants des événements
551
        # à mettre à jour.
552
        ids = map(int, id.strip(',').split(','))
553

    
554
        user = get_current_user()
555
        events = VigiboardRequest(user)
556
        events.add_table(CorrEvent)
557
        events.add_join((Event, CorrEvent.idcause == Event.idevent))
558
        events.add_join((events.items, 
559
            Event.idsupitem == events.items.c.idsupitem))
560
        events.add_filter(CorrEvent.idcorrevent.in_(ids))
561
        
562
        events.generate_request()
563
        idevents = [cause.idcause for cause in events.req]
564

    
565
        # Si des changements sont survenus depuis que la 
566
        # page est affichée, on en informe l'utilisateur.
567
        last_modification = datetime.fromtimestamp(last_modification)
568
        cur_last_modification = get_last_modification_timestamp(idevents, None)
569
        if cur_last_modification and last_modification < cur_last_modification:
570
            flash(_('Changes have occurred since the page was last displayed, '
571
                    'your changes HAVE NOT been saved.'), 'warning')
572
            raise redirect(request.environ.get('HTTP_REFERER', '/'))
573

    
574
        # Vérification que au moins un des identifiants existe et est éditable
575
        if not events.num_rows():
576
            flash(_('No access to this event'), 'error')
577
            redirect('/')
578

    
579
        if ack == u'Forced':
580
            condition = Any(
581
                in_group('managers'),
582
                has_permission('vigiboard-admin'),
583
                msg=l_("You don't have administrative access "
584
                        "to VigiBoard"))
585
            try:
586
                condition.check_authorization(request.environ)
587
            except NotAuthorizedError, e:
588
                reason = unicode(e)
589
                flash(reason, 'error')
590
                raise redirect(request.environ.get('HTTP_REFERER', '/'))
591

    
592
        # Modification des événements et création d'un historique
593
        # chaque fois que cela est nécessaire.
594
        for event in events.req:
595
            if trouble_ticket and trouble_ticket != event.trouble_ticket:
596
                history = EventHistory(
597
                        type_action="Ticket change",
598
                        idevent=event.idcause,
599
                        value=unicode(trouble_ticket),
600
                        text="Changed trouble ticket from '%(from)s' "
601
                             "to '%(to)s'" % {
602
                            'from': event.trouble_ticket,
603
                            'to': trouble_ticket,
604
                        },
605
                        username=user.user_name,
606
                        timestamp=datetime.now(),
607
                    )
608
                DBSession.add(history)   
609
                event.trouble_ticket = trouble_ticket
610

    
611
            # Changement du statut d'acquittement.
612
            if ack != u'NoChange':
613
                changed_ack = ack
614
                # Pour forcer l'acquittement d'un événement,
615
                # il faut en plus avoir la permission
616
                # "vigiboard-admin".
617
                if ack == u'Forced':
618
                    changed_ack = u'AAClosed'
619
                    # On met systématiquement l'état à "OK", même s'il
620
                    # s'agit d'un hôte. Techniquement, c'est incorrect,
621
                    # mais comme on fait ça pour masquer l'événement...
622
                    event.cause.current_state = \
623
                        StateName.statename_to_value(u'OK')
624

    
625
                    history = EventHistory(
626
                            type_action="Forced change state",
627
                            idevent=event.idcause,
628
                            value=u'OK',
629
                            text="Forced state to 'OK'",
630
                            username=user.user_name,
631
                            timestamp=datetime.now(),
632
                        )
633
                    DBSession.add(history)
634

    
635
                history = EventHistory(
636
                        type_action="Acknowledgement change state",
637
                        idevent=event.idcause,
638
                        value=ack,
639
                        text="Changed acknowledgement status "
640
                            "from '%s' to '%s'" % (
641
                            event.status, changed_ack
642
                        ),
643
                        username=user.user_name,
644
                        timestamp=datetime.now(),
645
                    )
646
                DBSession.add(history)
647
                event.status = changed_ack
648

    
649
        DBSession.flush()
650
        flash(_('Updated successfully'))
651
        redirect(request.environ.get('HTTP_REFERER', '/'))
652

    
653

    
654
    class GetPluginValueSchema(schema.Schema):
655
        """Schéma de validation de la méthode get_plugin_value."""
656
        idcorrevent = validators.Int(not_empty=True)
657
        plugin_name = validators.OneOf(
658
            [unicode(i[0]) for i in config.get('vigiboard_plugins', [])],
659
            not_empty=True, hideList=True)
660
        # Permet de passer des paramètres supplémentaires au plugin.
661
        allow_extra_fields = True
662

    
663
    @validate(
664
        validators=GetPluginValueSchema(),
665
        error_handler = handle_validation_errors_json)
666
    @expose('json')
667
    @require(access_restriction)
668
    def get_plugin_value(self, idcorrevent, plugin_name, *arg, **krgv):
669
        """
670
        Permet de récupérer la valeur d'un plugin associée à un CorrEvent
671
        donné via JSON.
672
        """
673
        plugins = config.get('vigiboard_plugins', {})
674
        # Permet de vérifier si l'utilisateur a bien les permissions
675
        # pour accéder à cet événement et si l'événement existe.
676
        user = get_current_user()
677
        events = VigiboardRequest(user, False)
678
        events.add_table(CorrEvent.idcorrevent)
679
        events.add_join((Event, CorrEvent.idcause == Event.idevent))
680
        events.add_join((events.items, 
681
            Event.idsupitem == events.items.c.idsupitem))
682
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
683

    
684
        # Pas d'événement ou permission refusée. On ne distingue pas
685
        # les 2 cas afin d'éviter la divulgation d'informations.
686
        if not events.num_rows():
687
            raise HTTPNotFound(_('No such incident or insufficient '
688
                                'permissions'))
689

    
690
        plugin_class = [p[1] for p in plugins if p[0] == plugin_name]
691
        if not plugin_class:
692
            raise HTTPNotFound(_('No such plugin'))
693

    
694
        plugin_class = plugin_class[0]
695
        try:
696
            mypac = __import__(
697
                'vigiboard.controllers.plugins.' + plugin_name,
698
                globals(), locals(), [plugin_class], -1)
699
            plugin = getattr(mypac, plugin_class)
700
            if callable(plugin):
701
                return plugin().get_value(idcorrevent, *arg, **krgv)
702
            raise HTTPInternalServerError(_('Not a valid plugin'))
703
        except ImportError:
704
            raise HTTPInternalServerError(_('Plugin could not be loaded'))
705

    
706
    @validate(validators={
707
        "fontsize": validators.Regex(
708
            r'[0-9]+(pt|px|em|%)',
709
            regexOps = ('I',)
710
        )}, error_handler = handle_validation_errors_json)
711
    @expose('json')
712
    def set_fontsize(self, fontsize):
713
        """Enregistre la taille de la police dans les préférences."""
714
        session['fontsize'] = fontsize
715
        session.save()
716
        return dict()
717

    
718
    @validate(validators={"refresh": validators.Int()},
719
            error_handler = handle_validation_errors_json)
720
    @expose('json')
721
    def set_refresh(self, refresh):
722
        """Enregistre le temps de rafraichissement dans les préférences."""
723
        session['refresh'] = refresh
724
        session.save()
725
        return dict()
726

    
727
    @expose('json')
728
    def set_theme(self, theme):
729
        """Enregistre le thème à utiliser dans les préférences."""
730
        # On sauvegarde l'ID du thème sans vérifications
731
        # car les thèmes (styles CSS) sont définies dans
732
        # les packages de thèmes (ex: vigilo-themes-default).
733
        # La vérification de la valeur est faite dans les templates.
734
        session['theme'] = theme
735
        session.save()
736
        return dict()
737

    
738
def get_last_modification_timestamp(event_id_list, 
739
                                    value_if_none=datetime.now()):
740
    """
741
    Récupère le timestamp de la dernière modification 
742
    opérée sur l'un des événements dont l'identifiant
743
    fait partie de la liste passée en paramètre.
744
    """
745
    last_modification_timestamp = DBSession.query(
746
                                func.max(EventHistory.timestamp),
747
                         ).filter(EventHistory.idevent.in_(event_id_list)
748
                         ).scalar()
749
    if not last_modification_timestamp:
750
        if not value_if_none:
751
            return None
752
        else:
753
            last_modification_timestamp = value_if_none
754
    return datetime.fromtimestamp(mktime(
755
        last_modification_timestamp.timetuple()))
756

    
757
def get_plugins_instances():
758
    """
759
    Renvoie une liste d'instances de plugins pour VigiBoard.
760

761
    @return: Liste de tuples contenant le nom du plugin
762
        et l'instance associée.
763
    @rtype: C{list} of C{tuple}
764
    """
765
    plugins = config.get('vigiboard_plugins', [])
766
    plugins_instances = []
767
    for (plugin_name, plugin_class) in plugins:
768
        try:
769
            mypac = __import__(
770
                'vigiboard.controllers.plugins.' + plugin_name,
771
                globals(), locals(), [plugin_class], -1)
772
            plugin = getattr(mypac, plugin_class)
773
            if callable(plugin):
774
                plugins_instances.append((plugin_name, plugin()))
775
        except ImportError:
776
            pass
777
    return plugins_instances
778