Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / root.py @ a45763b4

History | View | Annotate | Download (16.4 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
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

    
24
__all__ = ('RootController', )
25

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

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

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

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

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

    
75
        try:
76
            page = int(page)
77
        except ValueError:
78
            abort(404)
79

    
80
        if page < 1:
81
            page = 1
82

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

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

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

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

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

    
123
        if total_rows <= items_per_page * (page-1):
124
            page = 1
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
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
131

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

160
        @param id: identifiant de l'événement
161
        """
162

    
163
        # Obtention de données sur l'événement et sur son historique
164
        username = request.environ.get('repoze.who.identity'
165
                    ).get('repoze.who.userid')
166
        user = User.by_user_name(username)
167

    
168
        event = DBSession.query(
169
                        CorrEvent.priority,
170
                        Event,
171
                 ).join(
172
                    (Event, CorrEvent.idcause == Event.idevent),
173
                    (HostGroup, Event.hostname == HostGroup.hostname),
174
                 ).filter(HostGroup.idgroup.in_(user.groups)
175
                 ).filter(CorrEvent.idcorrevent == idcorrevent
176
                 ).one()
177

    
178
        history = DBSession.query(
179
                    EventHistory,
180
                 ).filter(EventHistory.idevent == event[1].idevent
181
                 ).order_by(asc(EventHistory.timestamp)
182
                 ).order_by(asc(EventHistory.type_action)).all()
183

    
184
        eventdetails = {}
185
        for edname, edlink in \
186
                config['vigiboard_links.eventdetails'].iteritems():
187

    
188
            eventdetails[edname] = edlink[1] % {
189
                    'idcorrevent': idcorrevent,
190
                    'host': event[1].hostname,
191
                    'service': event[1].servicename
192
                    }
193

    
194
        return dict(
195
                current_state = StateName.value_to_statename(
196
                                    event[1].current_state),
197
                initial_state = StateName.value_to_statename(
198
                                    event[1].initial_state),
199
                peak_state = StateName.value_to_statename(
200
                                    event[1].peak_state),
201
                idcorrevent = idcorrevent,
202
                host = event[1].hostname,
203
                service = event[1].servicename,
204
                eventdetails = eventdetails,
205
            )
206

    
207
    @validate(validators={'idcorrevent':validators.String(not_empty=True)},
208
            error_handler=process_form_errors)
209
    @expose('vigiboard.html')
210
    @require(Any(not_anonymous(), msg=_("You need to be authenticated")))
211
    def event(self, idcorrevent):
212
        """
213
        Affichage de l'historique d'un événement.
214
        Pour accéder à cette page, l'utilisateur doit être authentifié.
215

216
        @param idevent: identifiant de l'événement souhaité
217
        """
218

    
219
        username = request.environ['repoze.who.identity']['repoze.who.userid']
220
        events = VigiboardRequest(User.by_user_name(username))
221
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
222
        
223
        # Vérification que l'événement existe
224
        if events.num_rows() != 1 :
225
            flash(_('Error in DB'), 'error')
226
            redirect('/')
227
       
228
        events.format_events(0, 1)
229
        events.format_history()
230
        events.generate_tmpl_context() 
231

    
232
        return dict(
233
                    events = events.events,
234
                    rows_info = {
235
                        'id_first_row': 1,
236
                        'id_last_row': 1,
237
                        'total_rows': 1,
238
                    },
239
                    pages = [1],
240
                    page = 1,
241
                    event_edit_status_options = edit_event_status_options,
242
                    history = events.hist,
243
                    hist_error = True,
244
                    plugin_context = events.context_fct,
245
                    search = {
246
                        'host': None,
247
                        'service': None,
248
                        'output': None,
249
                        'tt': None
250
                    },
251
                   refresh_times=self.refresh_times,
252
                )
253

    
254
    @validate(validators={'host':validators.NotEmpty(),
255
        'service':validators.NotEmpty()}, error_handler=process_form_errors)
256
    @expose('vigiboard.html')
257
    @require(Any(not_anonymous(), msg=_("You need to be authenticated")))
258
    def host_service(self, host, service):
259
        
260
        """
261
        Affichage de l'historique de l'ensemble des événements correspondant
262
        au host et service demandé.
263
        Pour accéder à cette page, l'utilisateur doit être authentifié.
264

265
        @param host: Nom de l'hôte souhaité.
266
        @param service: Nom du service souhaité
267
        """
268

    
269
        username = request.environ['repoze.who.identity']['repoze.who.userid']
270
        events = VigiboardRequest(User.by_user_name(username))
271
        events.add_filter(Event.hostname == host,
272
                Event.servicename == service)
273
        # XXX On devrait avoir une autre API que ça !!!
274
        # Supprime le filtre qui empêche d'obtenir des événements fermés
275
        # (ie: ayant l'état Nagios 'OK' et le statut 'AAClosed').
276
        if len(events.filter) > 2:
277
            del events.filter[2]
278

    
279
        # Vérification qu'il y a au moins 1 événement qui correspond
280
        if events.num_rows() == 0 :
281
            redirect('/')
282

    
283
        events.format_events(0, events.num_rows())
284
        events.format_history()
285
        events.generate_tmpl_context()
286

    
287
        return dict(
288
                    events = events.events,
289
                    rows_info = {
290
                        'id_first_row': 1,
291
                        'id_last_row': 1,
292
                        'total_rows': 1,
293
                    },
294
                    pages = [1],
295
                    page = 1,
296
                    event_edit_status_options = edit_event_status_options,
297
                    history = events.hist,
298
                    hist_error = True,
299
                    plugin_context = events.context_fct,
300
                    search = {
301
                        'host': None,
302
                        'service': None,
303
                        'output': None,
304
                        'tt': None
305
                    },
306
                    refresh_times=self.refresh_times,
307
                )
308

    
309
    @validate(validators={
310
        "id":validators.Regex(r'^[^,]+(,[^,]*)*,?$'),
311
#        "trouble_ticket":validators.Regex(r'^[0-9]*$'),
312
        "status":validators.OneOf(['NoChange', 'None', 'Acknowledged',
313
                'AAClosed'])
314
        }, error_handler=process_form_errors)
315
    @require(Any(not_anonymous(), msg=_("You need to be authenticated")))
316
    def update(self,**krgv):
317
        
318
        """
319
        Mise à jour d'un événement suivant les arguments passés.
320
        Cela peut être un changement de ticket ou un changement de statut.
321
        
322
        @param krgv['id']: Le ou les identifiants des événements à traiter
323
        @param krgv['tt']: Nouveau numéro du ticket associé.
324
        @param krgv['status']: Nouveau status de/des événements.
325
        """
326
        
327
        # Si l'utilisateur édite plusieurs événements à la fois,
328
        # il nous faut chacun des identifiants
329

    
330
        if krgv['id'] is None:
331
            flash(_('No event has been selected'), 'warning')
332
            raise redirect(request.environ.get('HTTP_REFERER', url('/')))
333

    
334
        ids = krgv['id'].split(',')
335
       
336
        if len(ids) > 1 :
337
            ids = ids[:-1]
338
        
339
        username = request.environ['repoze.who.identity']['repoze.who.userid']
340
        events = VigiboardRequest(User.by_user_name(username))
341
        events.add_filter(CorrEvent.idcorrevent.in_(ids))
342
        
343
        # Vérification que au moins un des identifiants existe et est éditable
344
        if events.num_rows() <= 0 :
345
            flash(_('No access to this event'), 'error')
346
            redirect('/')
347
        
348
        # Modification des événements et création d'un historique
349
        # pour chacun d'eux.
350
        username = request.environ['repoze.who.identity']['repoze.who.userid']
351

    
352
        for req in events.req:
353
            if isinstance(req, CorrEvent):
354
                event = req
355
            else:
356
                event = req[0]
357

    
358
            if krgv['trouble_ticket'] != '' :
359
                history = EventHistory(
360
                        type_action="Ticket change",
361
                        idevent=event.idcause,
362
                        value=krgv['trouble_ticket'],
363
                        text=_("Changed trouble ticket from '%s' to '%s'") % (
364
                            event.trouble_ticket, krgv['trouble_ticket']
365
                        ),
366
                        username=username,
367
                        timestamp=datetime.now(),
368
                    )
369
                DBSession.add(history)   
370
                event.trouble_ticket = krgv['trouble_ticket']
371

    
372
            if krgv['status'] != 'NoChange' :
373
                history = EventHistory(
374
                        type_action="Acknowlegement change state",
375
                        idevent=event.idcause,
376
                        value=krgv['status'],
377
                        text=_("Changed acknowledgement status from '%s' to '%s'") % (
378
                            event.status, krgv['status']
379
                        ),
380
                        username=username,
381
                        timestamp=datetime.now(),
382
                    )
383
                DBSession.add(history)
384
                event.status = krgv['status']
385

    
386
        DBSession.flush()
387
        flash(_('Updated successfully'))
388
        redirect(request.environ.get('HTTP_REFERER', url('/')))
389

    
390

    
391
    @validate(validators={"plugin_name":validators.OneOf(
392
        [i for [i, j] in config.get('vigiboard_plugins', [])])},
393
                error_handler = process_form_errors)
394
    @expose('json')
395
    def get_plugin_value(self, plugin_name, *arg, **krgv):
396
        """
397
        Permet aux plugins de pouvoir récupérer des valeurs Json
398
        """
399
        plugins = config['vigiboard_plugins']
400
        if plugins is None:
401
            return
402

    
403
        plugin = [i for i in plugins if i[0] == plugin_name][0]
404
        try:
405
            mypac = __import__(
406
                'vigiboard.controllers.vigiboard_plugin.' + plugin[0],
407
                globals(), locals(), [plugin[1]], -1)
408
            plug = getattr(mypac, plugin[1])()
409
            return plug.controller(*arg, **krgv)
410
        except:
411
            raise
412
    
413
#    @validate(validators= {"fontsize": validators.Int()},
414
#                    error_handler = process_form_errors)
415
    @expose('json')
416
    def set_fontsize(self, fontsize):
417
        """
418
        Save font size
419
        """
420
        session['fontsize'] = fontsize
421
        session.save()
422
        return dict(ret= 'ok')
423

    
424
    @validate(validators= {"refresh": validators.Int()},
425
            error_handler = process_form_errors)
426
    @expose('json')
427
    def set_refresh(self, refresh):
428
        """
429
        Save refresh time
430
        """
431
        session['refresh'] = refresh
432
        session.save()
433
        return dict(ret= 'ok')
434