Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / silence.py @ 011743be

History | View | Annotate | Download (23.7 KB)

1 d5a41c9b Vincent QUEMENER
# -*- coding: utf-8 -*-
2
# vim:set expandtab tabstop=4 shiftwidth=4:
3 011743be Francois POIROTTE
# Copyright (C) 2007-2020 CS GROUP - France
4 9b8d9497 Francois POIROTTE
# License: GNU GPL v2 <http://www.gnu.org/licenses/gpl-2.0.html>
5 d5a41c9b Vincent QUEMENER
6
"""Gère la planification des mises en silence."""
7
8
#import time
9
from datetime import datetime
10
11
from tg import expose, validate, require, flash, tmpl_context, \
12
    request, config, redirect
13 02c4a1e7 Francois POIROTTE
from tg.i18n import lazy_ugettext as l_, ugettext as _
14
from tg.support import paginate
15
from tg.predicates import Any, All, in_group, \
16 d5a41c9b Vincent QUEMENER
                                    has_permission, not_anonymous
17 02c4a1e7 Francois POIROTTE
from formencode import validators, schema
18 d5a41c9b Vincent QUEMENER
from formencode.compound import All as All_
19
from formencode.foreach import ForEach
20
from sqlalchemy.exc import InvalidRequestError, IntegrityError
21
from sqlalchemy.sql.expression import asc, desc
22
23 02c4a1e7 Francois POIROTTE
from vigilo.turbogears.helpers import get_current_user
24 d5a41c9b Vincent QUEMENER
from vigilo.turbogears.controllers import BaseController
25
from vigilo.models.session import DBSession
26
from vigilo.models.tables import SupItem, Host, LowLevelService, \
27
                            HighLevelService, StateName, Silence, UserSupItem
28
from vigilo.models.tables.secondary_tables import SILENCE_STATE_TABLE
29
from vigilo.models.utils import group_concat
30
31 9332c7c0 Yves OUATTARA
from vigiboard.lib import error_handler
32
33 d5a41c9b Vincent QUEMENER
import logging
34
35
LOGGER = logging.getLogger(__name__)
36
37
__all__ = ['SilenceController']
38
39
# pylint: disable-msg=R0201
40
class SilenceController(BaseController):
41
    """
42
    Contrôleur gérant la planification des mises en silence.
43
    """
44
45
    # Prédicat pour la restriction de l'accès aux interfaces.
46
    # L'utilisateur doit avoir la permission "vigiboard-silence"
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-silence'),
52
            msg=l_("Insufficient privileges for this action"))
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
    def query_silences(self):
65
        """
66
        Retourne une requête SQLAlchemy interrogeant
67
        la table des mises en silence
68
        """
69
70
        # Si l'utilisateur fait partie du groupe 'managers', on récupère la
71
        # liste de tous les supitems de la base de données
72
        if in_group('managers').is_met(request.environ):
73
            lls_query = DBSession.query(
74
                    LowLevelService.idservice.label('idsupitem'),
75
                    LowLevelService.servicename.label("servicename"),
76
                    Host.name.label('hostname')
77
                ).join((Host, Host.idhost == LowLevelService.idhost))
78
79
            host_query = DBSession.query(
80
                    Host.idhost.label('idsupitem'),
81
                    "NULL",
82
                    Host.name.label('hostname')
83
                )
84
85
            supitems = lls_query.union(host_query).subquery()
86
87
        # Sinon on ne récupère que les supitems auxquels l'utilisateurs a accès
88
        else:
89
            user_name = request.identity['repoze.who.userid']
90
            supitems = DBSession.query(
91
                UserSupItem.idsupitem.label('idsupitem'),
92
                UserSupItem.servicename.label("servicename"),
93
                UserSupItem.hostname.label('hostname')
94
            ).filter(
95
                UserSupItem.username == user_name
96
            ).distinct().subquery()
97
98
        # On interroge la base pour avoir la liste des règles de mise en silence
99
        # correspondant à ces supitems.
100
        states = DBSession.query(
101 b0a39adc Francois POIROTTE
                StateName.statename,
102 d5a41c9b Vincent QUEMENER
                SILENCE_STATE_TABLE.c.idsilence,
103 b0a39adc Francois POIROTTE
            ).join((SILENCE_STATE_TABLE,
104 d5a41c9b Vincent QUEMENER
                StateName.idstatename == SILENCE_STATE_TABLE.c.idstate)
105
            ).order_by(StateName.statename
106
            ).subquery()
107
        states = DBSession.query(
108
                states.c.idsilence,
109
                group_concat(states.c.statename, ', ').label('states'),
110
            ).group_by(states.c.idsilence
111
            ).subquery()
112
        silences = DBSession.query(
113
                Silence,
114
                supitems.c.hostname,
115
                supitems.c.servicename,
116
                states.c.states
117
            ).join((supitems, supitems.c.idsupitem == Silence.idsupitem)
118
            ).join((states, states.c.idsilence == Silence.idsilence))
119
120
        return silences
121
122
    def check_silence_rule_existence(self, idsupitem):
123
        """
124
        S'assure qu'aucune règle de mise en silence n'existe dans la base de
125
        données pour le supitem considéré, et affiche un message d'erreur dans
126
        le cas contraire.
127

128
        @param idsupitem: Identifiant du supitem.
129
        @type  idsupitem: C{int}
130
        """
131
        silence = DBSession.query(Silence
132
            ).filter(Silence.idsupitem == idsupitem
133
            ).first()
134
        if not silence:
135
            return
136
        if isinstance(silence.supitem, LowLevelService):
137
            msg = _("Another rule already exists for service '%s' " \
138
                    "on host '%s'.") % (silence.supitem.servicename,
139
                        silence.supitem.host.name)
140
        else:
141
            msg = _("Another rule already exists for host '%s'.") % (
142
                silence.supitem.name)
143 9332c7c0 Yves OUATTARA
        error_handler.handle_error_message(msg)
144 d5a41c9b Vincent QUEMENER
145
    class IndexSchema(schema.Schema):
146
        """Schéma de validation de la méthode index."""
147
        # Si on ne passe pas le paramètre "page" ou qu'on passe une valeur
148
        # invalide ou pas de valeur du tout, alors on affiche la 1ère page.
149
        page = validators.Int(min=1, if_missing=1,
150
            if_invalid=1, not_empty=True)
151
        sort = validators.OneOf(
152
            ['hostname', 'servicename', 'lastmodification',
153
                'author', 'comment', 'states'],
154
            if_missing='lastmodification', if_invalid='lastmodification')
155
        order = validators.OneOf(['desc', 'asc'],
156
            if_missing='desc', if_invalid='desc')
157
158
    @validate(
159
        validators=IndexSchema(),
160
        error_handler = process_form_errors)
161
    @expose('silence.html')
162
    @require(access_restriction)
163
    def index(self, page=1, sort=None, order=None):
164
        """
165
        Affiche la liste des règles de mise en silence enregistrées dans
166
        la BDD, que l'utilisateur pourra ensuite éditer ou supprimer.
167

168
        @param sort: (optionnel) Critère de tri de la liste des
169
                     règles de mise en silence enregistrées.
170
        @type  sort: C{str}
171
        @param order: (optionnel) Ordre de tri.
172
        @type  order: C{str}
173
        """
174
175
#        # On récupère la langue de l'utilisateur
176
#        lang = get_lang()
177
#        if not lang:
178
#            lang = ['fr']
179
#        lang = lang[0]
180
181
        # On récupère tous les enregistrements de la table
182
        # silence, qu'ils concernent des hôtes, des services
183
        # de bas niveau, ou bien des services de haut niveau.
184
        silences = self.query_silences()
185
186
        # On trie ces enregistrements selon le critère choisi
187
        # par l'utilisateur (par défaut, la date d'ajout).
188
        sort_keys = {
189
            'hostname': 'hostname',
190
            'servicename': 'servicename',
191
            'lastmodification': Silence.lastmodification,
192
            'author': Silence.author,
193
            'comment': Silence.comment,
194
#            'start': Silence.start,
195
#            'end': Silence.end,
196
            'states': 'states',
197
        }
198
        if sort in sort_keys.keys():
199
            # Tri dans l'ordre croissant
200
            if order != 'desc':
201
                silences = silences.order_by(asc(sort_keys[sort]))
202
            # Tri dans l'ordre décroissant
203
            else:
204
                silences = silences.order_by(desc(sort_keys[sort]))
205
206
        # On calcule la pagination
207
        page = paginate.Page(silences, page=page,
208
            items_per_page=int(config['vigiboard_items_per_page']))
209
210
#        # On initialise les widgets des calendriers
211
#        # utilisés dans le formulaire de mise en silence.
212
#        start_calendar = CalendarDateTimePicker('start',
213
#                                            button_text = l_("Choose a date"),
214
#                                            date_format = '%Y-%m-%d %H:%M',
215
#                                            calendar_lang = lang)
216
#        end_calendar = CalendarDateTimePicker('end',
217
#                                            button_text = l_("Choose a date"),
218
#                                            date_format = '%Y-%m-%d %H:%M',
219
#                                            calendar_lang = lang)
220
221
        # Traduction du nom des colonnes
222
        columns = [
223
            ('hostname', l_('Host')),
224
            ('servicename', l_('Service')),
225
            ('states', l_('States')),
226
            ('lastmodification', l_('Last modification')),
227
            ('author', l_('Author')),
228
            ('comment', l_('Comment'))
229
        ]
230
231
        return dict(
232
            page=page,
233
            sort=sort,
234
            order=order,
235
#            start_calendar=start_calendar,
236
#            end_calendar=end_calendar,
237
            columns=columns
238
        )
239
240
    @expose('silence_form.html')
241
    @require(access_restriction)
242
    def add(self):
243
        """
244
        Affiche un formulaire d'ajout d'une règle de mise en silence.
245
        """
246
        return dict(
247
            id=None,
248
            hostname=None,
249
            servicename=None,
250
            states=None,
251
            comment=None,
252
#            start_calendar=start_calendar,
253
#            end_calendar=end_calendar,
254
        )
255
256
    class UpdateSchema(schema.Schema):
257
        """Schéma de validation de la méthode update."""
258
        id = validators.Int(min=1, not_empty=True)
259
260
    @validate(
261
        validators=UpdateSchema(),
262
        error_handler = process_form_errors)
263
    @expose('silence_form.html')
264
    @require(access_restriction)
265
    def update(self, id):
266
        """
267
        Affiche un formulaire de mise à jour d'une règle de mise en silence.
268

269
        @param id: Identifiant de la règle.
270
        @type  id: C{int}
271
        """
272
273
        # On s'assure que la règle existe bien dans la base
274
        try:
275
            silence = DBSession.query(Silence
276
                ).filter(Silence.idsilence == id).one()
277 0dff1e21 Francois POIROTTE
        except InvalidRequestError as e:
278 d5a41c9b Vincent QUEMENER
            msg = _('An exception has been raised while ' \
279
                    'querying the database: %s') % str(e)
280 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
281 d5a41c9b Vincent QUEMENER
        if not silence:
282
            msg = _("Silence rule #%s does not exist.") % id
283 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
284 d5a41c9b Vincent QUEMENER
285
        # On s'assure que l'utilisateur dispose bien des permissions sur le
286
        # supitem considéré
287
        user = get_current_user()
288
        if not silence.supitem.is_allowed_for(user):
289
            msg = _("Silence rule #%s does not exist.") % id
290 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
291 d5a41c9b Vincent QUEMENER
292
        if hasattr(silence.supitem, 'servicename'):
293
            hostname = silence.supitem.host.name
294
            servicename = silence.supitem.servicename
295
        else:
296
            hostname = silence.supitem.name
297
            servicename = None
298
299
        return dict(
300
            id=id,
301
            hostname=hostname,
302
            servicename=servicename,
303
            states=[s.statename for s in silence.states],
304
            comment=silence.comment,
305
#            start_calendar=start_calendar,
306
#            end_calendar=end_calendar,
307
        )
308
309
    class CreateOrModifySchema(schema.Schema):
310
        """Schéma de validation de la méthode create_or_modify."""
311
        states = All_(
312
            validators.Set(use_set=True),
313
            validators.OneOf(["WARNING", "CRITICAL", "DOWN", "UNKNOWN"],
314
                testValueList=True, hideList=True, not_empty=True)
315
        )
316
        host = validators.String(not_empty=True)
317
        service = validators.String()
318
        comment = validators.String()
319
        idsilence = validators.Int(min=1, if_missing=None,
320
            if_invalid=None, not_empty=False)
321
322
    @validate(
323
        validators=CreateOrModifySchema(),
324
        error_handler = process_form_errors)
325
    @expose()
326
    @require(access_restriction)
327
    def create_or_modify(
328
            self,
329
            states,
330
            host,
331
            service=None,
332
#            start=time.time(),
333
#            end=time.time(),
334
            comment=None,
335
            idsilence=None):
336
        """
337
        Ajoute une règle de mise en silence d'un hôte/service,
338
        ou en modifie une déjà existante.
339

340
        @param states: (optionnel) Liste des états concernés par la règle.
341
        @type  states: C{list} of C{unicode}
342
        @param host: Nom de l'hôte sur lequel porte la règle.
343
        @type  host: C{unicode}
344
        @param service: (optionnel) Nom du service sur lequel
345
            porte la règle.
346
        @type  service: C{unicode}
347
#        @param start: Début de la mise en silence planifiée.
348
#        @type  start: C{str}
349
#        @param end: Fin de la mise en silence planifiée.
350
#        @type  end: C{str}
351
        @param comment: (optionnel) Commentaire accompagnant la règle.
352
        @type  comment: C{unicode}
353
        @param idsilence: (optionnel) Identifiant de la règle dans le cas d'une
354
            mise à jour.
355
        @type  idsilence: C{int}
356
        """
357
358
        # TODO: Faire ce traitement dans le schéma de validation
359
        if not states:
360
            msg = _('No state specified for the silence rule.')
361 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
362 d5a41c9b Vincent QUEMENER
        states = list(states)
363
364
        # On récupère le nom et l'IP de l'utilisateur.
365
        user = get_current_user()
366
        user_name = user.user_name
367
        user_ip = request.remote_addr
368
369
        # On récupère l'identifiant de l'item (hôte
370
        # ou service) concerné par la mise en silence.
371
        idsupitem = SupItem.get_supitem(host, service)
372
        if idsupitem:
373
            try:
374
                supitem = DBSession.query(SupItem
375
                    ).filter(SupItem.idsupitem == idsupitem).one()
376 0dff1e21 Francois POIROTTE
            except InvalidRequestError as e:
377 d5a41c9b Vincent QUEMENER
                msg = _('An exception has been raised while ' \
378
                        'querying the database: %s') % str(e)
379 9332c7c0 Yves OUATTARA
                error_handler.handle_error_message(msg)
380 d5a41c9b Vincent QUEMENER
        if not idsupitem or not supitem.is_allowed_for(user):
381
            if not service:
382
                msg = _("Host '%s' does not exist.") % host
383 9332c7c0 Yves OUATTARA
                error_handler.handle_error_message(msg)
384 d5a41c9b Vincent QUEMENER
            else:
385
                msg = _("Service '%s' does not exist for host '%s'."
386
                    ) % (service, host)
387 9332c7c0 Yves OUATTARA
                error_handler.handle_error_message(msg)
388 d5a41c9b Vincent QUEMENER
389
        # On distingue mise à jour et création :
390
391
        # 1. Dans le cas d'une mise à jour :
392
        if idsilence:
393
394
            # - On s'assure que la règle existe bien dans la base
395
            try:
396
                silence = DBSession.query(Silence
397
                    ).filter(Silence.idsilence == idsilence).one()
398 0dff1e21 Francois POIROTTE
            except InvalidRequestError as e:
399 d5a41c9b Vincent QUEMENER
                msg = _('An exception has been raised while ' \
400
                        'querying the database: %s') % str(e)
401 9332c7c0 Yves OUATTARA
                error_handler.handle_error_message(msg)
402 d5a41c9b Vincent QUEMENER
            if not silence:
403
                msg = _("Silence rule #%s does not exist.") % idsilence
404 9332c7c0 Yves OUATTARA
                error_handler.handle_error_message(msg)
405 d5a41c9b Vincent QUEMENER
406
            # - Si le supitem a été modifié, on vérifie qu'aucune
407
            #   autre règle n'existe pour le nouveau supitem
408
            if silence.idsupitem != idsupitem:
409
                self.check_silence_rule_existence(idsupitem)
410
411
            # On supprime les états existants
412
            silence.states = []
413
414
        # 2. Dans le cas d'une création :
415
        else:
416
417
            # - On s'assure qu'aucune autre règle n'existe pour le supitem
418
            self.check_silence_rule_existence(idsupitem)
419
420
            # - Et on crée l'objet représentant la règle
421
            silence = Silence()
422
423
        # Dans les 2 cas, on met à jour l'objet avec
424
        # les informations passées en paramètre
425
        silence.idsupitem = idsupitem
426
        silence.comment = comment
427 1c5486c7 Francois POIROTTE
        silence.lastmodification = datetime.utcnow().replace(microsecond=0)
428 d5a41c9b Vincent QUEMENER
        silence.author = user_name
429
        try:
430
            DBSession.add(silence)
431
            for state in states:
432
                s = DBSession.query(StateName
433
                    ).filter(StateName.statename == state).one()
434
                silence.states.append(s)
435
            DBSession.flush()
436 0dff1e21 Francois POIROTTE
        except (IntegrityError, InvalidRequestError) as e:
437 d5a41c9b Vincent QUEMENER
            msg = _('An exception has been raised while ' \
438
                    'updating the database: %s') % str(e)
439 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
440 d5a41c9b Vincent QUEMENER
441
        # On notifie l'opération dans les logs, on affiche un message de
442
        # succès, et on redirige le navigateur vers la liste des règles de
443
        # mise en silence.
444
        if idsilence:
445
            # Mise à jour d'une règle portant sur un service
446
            if hasattr(silence.supitem, 'servicename'):
447
                LOGGER.info(_(
448
                    'User %(user)s (IP: %(ip)s) updated silence rule ' \
449
                    '#%(id)s for service %(service)s on host %(host)s.'
450
                ) % {
451
                    'user': user_name,
452
                    'ip': user_ip,
453
                    'id': idsilence,
454
                    'host': host,
455
                    'service': service
456
                })
457
                flash(_(
458
                    'Silence rule #%(id)s (host: %(host)s, service: ' \
459
                    '%(service)s) has been successfully updated.') % {
460
                        'id': idsilence,
461
                        'host': host,
462
                        'service': service
463
                })
464
            # Mise à jour d'une règle portant sur un hôte
465
            else:
466
                LOGGER.info(_(
467
                    'User %(user)s (IP: %(ip)s) updated silence rule ' \
468
                    '#%(id)s for host %(host)s.') % {
469
                        'user': user_name,
470
                        'ip': user_ip,
471
                        'id': idsilence,
472
                        'host': host
473
                })
474
                flash(_(
475
                    'Silence rule #%(id)s (host: %(host)s) ' \
476
                    'has been successfully updated.') % {
477
                        'id': idsilence,
478
                        'host': host
479
                })
480
        else:
481
            # Ajout d'une règle portant sur un service
482
            if service:
483
                LOGGER.info(_(
484
                    'User %(user)s (IP: %(ip)s) added a silence rule (#' \
485
                    '%(id)s) for service %(service)s on host %(host)s.'
486
                ) % {
487
                    'user': user_name,
488
                    'ip': user_ip,
489
                    'id': idsilence,
490
                    'host': host,
491
                    'service': service
492
                })
493
                flash(_('A new silence rule (#%(id)s) has been added for '
494
                    'service "%(service)s" on host "%(host)s".') % {
495
                        'id': silence.idsilence,
496
                        'service': service,
497
                        'host': host
498
                    })
499
            # Ajout d'une règle portant sur un hôte
500
            else:
501
                LOGGER.info(_(
502
                    'User %(user)s (IP: %(ip)s) added a silence rule ' \
503
                    '(#%(id)s) for host %(host)s.') % {
504
                        'user': user_name,
505
                        'ip': user_ip,
506
                        'id': idsilence,
507
                        'host': host
508
                })
509
                flash(_('A new silence rule (#%(id)s) has been added for the '
510
                    'host "%(host)s".') % {
511
                        'id': silence.idsilence,
512
                        'host': host
513
                    })
514
        redirect('./')
515
516
517
    class DeleteSchema(schema.Schema):
518
        """Schéma de validation de la méthode delete."""
519
        id = All_(
520
            validators.Set(use_set=True),
521
            ForEach(validators.Int(min=1)),
522
        )
523
524
    @validate(
525
        validators=DeleteSchema(),
526
        error_handler = process_form_errors)
527
    @expose()
528
    @require(access_restriction)
529
    def delete(self, id):
530
        """
531
        Suppression d'une règle ou d'une liste de règles de mise en silence.
532

533
        @param id: Liste des identifiants des règles à supprimer.
534
        @type  id: C{list} of C{int}
535
        """
536
537
        # TODO: Faire ce traitement dans le schéma de validation
538
        if not id:
539
            msg = _('No silence rule id specified.')
540 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
541 d5a41c9b Vincent QUEMENER
        id = list(id)
542
543
        # On recherche les règles dans la BDD.
544
        try:
545
            silences = DBSession.query(Silence
546
                ).filter(Silence.idsilence.in_(id)).all()
547 0dff1e21 Francois POIROTTE
        except InvalidRequestError as e:
548 d5a41c9b Vincent QUEMENER
            msg = _('An exception has been raised while ' \
549
                    'querying the database: %s') % str(e)
550 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
551 d5a41c9b Vincent QUEMENER
552
        # On s'assure que toutes les règles ont bien été trouvées dans la
553
        # base, faute de quoi on lève une erreur et on arrête le traitement
554
        if len(silences) != len(id):
555
            missing_ids = [
556
                i for i in id if i not in [s.idsilence for s in silences]]
557
            if len(missing_ids) > 1:
558
                msg = _('Error: the following silence rules do not exist:' \
559
                    ' %s.') % ", ".join('#' + str(i) for i in missing_ids)
560
            else:
561
                msg = _('Error: silence rule #%s does not exist.'
562
                    ) % ", ".join(str(i) for i in missing_ids)
563 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
564 d5a41c9b Vincent QUEMENER
565
        # On s'assure que l'utilisateur dispose bien des permissions nécessaires
566
        # pour supprimer chacune des règles
567
        user = get_current_user()
568
        for s in silences:
569
            if not s.supitem.is_allowed_for(user):
570
                msg = _("Silence rule #%s does not exist.") % s.idsilence
571 9332c7c0 Yves OUATTARA
                error_handler.handle_error_message(msg)
572 d5a41c9b Vincent QUEMENER
573
        # On supprime les règles dans la BDD.
574
        try:
575
            for silence in silences:
576
                DBSession.delete(silence)
577
            DBSession.flush()
578 0dff1e21 Francois POIROTTE
        except InvalidRequestError as e:
579 d5a41c9b Vincent QUEMENER
            msg = _('An exception has been raised while ' \
580
                    'deleting the silence rules: %s') % str(e)
581 9332c7c0 Yves OUATTARA
            error_handler.handle_error_message(msg)
582 d5a41c9b Vincent QUEMENER
583
        # On notifie l'opération dans les logs
584
        user_name = user.user_name
585
        user_ip = request.remote_addr
586
        for s in silences:
587
            # Règle concernant un service de bas niveau
588
            if hasattr(s.supitem, 'servicename'):
589
                LOGGER.info(_(
590
                    'User %(user)s (IP: %(ip)s) deleted silence rule ' \
591
                    '#%(id)s for service %(service)s on host ' \
592
                    '%(host)s') % {
593
                        'user': user_name,
594
                        'ip': user_ip,
595
                        'id': s.idsilence,
596
                        'host': s.supitem.host.name,
597
                        'service': s.supitem.servicename
598
                })
599
            # Règle concernant un hôte
600
            else:
601
                LOGGER.info(_(
602
                    'User %(user)s (IP: %(ip)s) deleted silence rule ' \
603
                    '#%(id)s for host %(host)s') % {
604
                        'user': user_name,
605
                        'ip': user_ip,
606
                        'id': s.idsilence,
607
                        'host': s.supitem.name,
608
                })
609
610
        # On affiche un message de succès
611
        if len(id) > 1:
612
            flash(_('The following silence rules have been successfully ' \
613
                'deleted: %s.') % ", ".join(str(i) for i in id))
614
        else:
615
            flash(_('Silence rule #%s has been successfully ' \
616
                'deleted.') % id[0])
617
618
        # On redirige le navigateur vers la page d'index
619
        redirect('./', )