Project

General

Profile

Revision 911069bc

ID911069bc7b23036efa86aa1107b3e0b5e722c403
Parent ed4a7159
Child fed79c2f

Added by Francois POIROTTE over 14 years ago

Changement dans l'API de VigiboardRequest : toutes les jointures et les éléments à sélectionner doivent être expliciment indiqués.
Ajout de tests unitaires sur la boite de dialogue pointant vers les historiques.
Correction des autres tests et du code pour tenir compte du changement dans l'API.

git-svn-id: https://vigilo-dev.si.c-s.fr/svn@1725 b22e2e97-25c9-44ff-b637-2e5ceca36478

View differences:

vigiboard/controllers/root.py
11 11
from sqlalchemy import not_, and_,  or_, asc
12 12
from sqlalchemy.orm import aliased
13 13
from sqlalchemy.sql import func
14
from sqlalchemy.sql.expression import union
14 15
from datetime import datetime
15 16
from time import mktime
16 17
import math
......
88 89
        user = User.by_user_name(username)
89 90
        
90 91
        aggregates = VigiboardRequest(user)
92
        aggregates.add_table(
93
            CorrEvent,
94
            aggregates.items.c.hostname,
95
            aggregates.items.c.servicename
96
        )
97
        aggregates.add_join((Event, CorrEvent.idcause == Event.idevent))
91 98
        
92 99
        search = {
93 100
            'host': '',
......
208 215
        # Obtention de données sur l'événement et sur son historique
209 216
        username = request.environ.get('repoze.who.identity'
210 217
                    ).get('repoze.who.userid')
211
        user = User.by_user_name(username)
212
        user_groups = user.groups
213 218

  
214
#        try:
215
        
216
        lls_query = DBSession.query(
217
            ServiceLowLevel.idservice.label("idsupitem"),
218
            ServiceLowLevel.servicename,
219
            Host.name.label("hostname")
220
        ).join(
221
           (Host, Host.idhost == ServiceLowLevel.idhost),
222
        ).outerjoin(
223
            (HOST_GROUP_TABLE, HOST_GROUP_TABLE.c.idhost == ServiceLowLevel.idservice),
224
            (SERVICE_GROUP_TABLE, SERVICE_GROUP_TABLE.c.idservice == ServiceLowLevel.idservice),
225
        ).filter(
226
            or_(
227
                HOST_GROUP_TABLE.c.idgroup.in_(user_groups),
228
                SERVICE_GROUP_TABLE.c.idgroup.in_(user_groups),
229
            ),
230
        )
231
                            
232
        host_query = DBSession.query(
233
            Host.idhost.label("idsupitem"),
234
            "NULL",
235
            Host.name.label("hostname")
236
        ).join((HOST_GROUP_TABLE, HOST_GROUP_TABLE.c.idhost == Host.idhost)
237
        ).filter(HOST_GROUP_TABLE.c.idgroup.in_(user_groups),
219
        username = request.environ['repoze.who.identity']['repoze.who.userid']
220
        events = VigiboardRequest(User.by_user_name(username))
221
        events.add_table(
222
            Event,
223
            events.items.c.hostname,
224
            events.items.c.servicename,
238 225
        )
226
        events.add_join((CorrEvent, CorrEvent.idcause == Event.idevent))
227
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
239 228

  
240
        items = lls_query.union(host_query).subquery()
241
                
242
        event = DBSession.query(
243
                        CorrEvent.priority,
244
                        Event,
245
                        items.c.hostname,
246
                        items.c.servicename,
247
                 ).join(
248
                    (Event, CorrEvent.idcause == Event.idevent),
249
                    (items, Event.idsupitem == items.c.idsupitem),
250
                 ).filter(
251
                    # On masque les événements avec l'état OK
252
                    # et traités (status == u'AAClosed').
253
                    not_(and_(
254
                        StateName.statename == u'OK',
255
                        CorrEvent.status == u'AAClosed'
256
                    ))
257
                ).filter(CorrEvent.idcorrevent == idcorrevent
258
                ).one()
259
                
260
                
261
#        except:
262
#            # XXX Raise some HTTP error.
263
#            return None
229
        # Vérification que au moins un des identifiants existe et est éditable
230
        if events.num_rows() != 1:
231
            flash(_('No access to this event'), 'error')
232
            redirect('/')
264 233

  
234
        event = events.req[0]
265 235
        history = DBSession.query(
266 236
                    EventHistory,
267
                 ).filter(EventHistory.idevent == event[1].idevent
237
                 ).filter(EventHistory.idevent == event[0].idevent
268 238
                 ).order_by(asc(EventHistory.timestamp)
269 239
                 ).order_by(asc(EventHistory.type_action)).all()
270 240

  
......
275 245
            # Rappel:
276 246
            # event[0] = priorité de l'alerte corrélée.
277 247
            # event[1] = alerte brute.
278
            if event[3]:
279
                service = urllib.quote(event[3])
248
            if event.servicename:
249
                service = urllib.quote(event.servicename)
280 250
            else:
281 251
                service = None
282 252
            eventdetails[edname] = edlink[1] % {
283 253
                'idcorrevent': idcorrevent,
284
                'host': urllib.quote(event[2]),
254
                'host': urllib.quote(event.hostname),
285 255
                'service': service,
286
                'message': urllib.quote(event[1].message),
256
                'message': urllib.quote(event[0].message),
287 257
            }
288 258

  
289 259
        return dict(
290 260
                current_state = StateName.value_to_statename(
291
                                    event[1].current_state),
261
                                    event[0].current_state),
292 262
                initial_state = StateName.value_to_statename(
293
                                    event[1].initial_state),
263
                                    event[0].initial_state),
294 264
                peak_state = StateName.value_to_statename(
295
                                    event[1].peak_state),
265
                                    event[0].peak_state),
296 266
                idcorrevent = idcorrevent,
297
                host = event[2],
298
                service = event[3],
267
                host = event.hostname,
268
                service = event.servicename,
299 269
                eventdetails = eventdetails,
300 270
            )
301 271

  
......
313 283

  
314 284
        username = request.environ['repoze.who.identity']['repoze.who.userid']
315 285
        events = VigiboardRequest(User.by_user_name(username))
286
        events.add_table(
287
            CorrEvent,
288
            events.items.c.hostname,
289
            events.items.c.servicename,
290
        )
291
        events.add_join((Event, CorrEvent.idcause == Event.idevent))
316 292
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
317 293
        
318 294
        # Vérification que l'événement existe
319 295
        if events.num_rows() != 1 :
320
            flash(_('Error in DB'), 'error')
296
            flash(_('No access to this event'), 'error')
321 297
            redirect('/')
322 298
       
323 299
        events.format_events(0, 1)
......
325 301
        events.generate_tmpl_context() 
326 302

  
327 303
        return dict(
328
                    events = events.events,
329
                    rows_info = {
330
                        'id_first_row': 1,
331
                        'id_last_row': 1,
332
                        'total_rows': 1,
333
                    },
334
                    nb_pages = 1,
335
                    page = 1,
336
                    event_edit_status_options = edit_event_status_options,
337
                    history = events.hist,
338
                    hist_error = True,
339
                    plugin_context = events.context_fct,
340
                    search = {
341
                        'host': '',
342
                        'service': '',
343
                        'output': '',
344
                        'tt': '',
345
                        'from_date': '',
346
                        'to_date': '',
347
                        'hostgroup': '',
348
                        'servicegroup': '',
349
                    },
350
                   refresh_times=config['vigiboard_refresh_times'],
351
                )
304
            events = events.events,
305
            rows_info = {
306
                'id_first_row': 1,
307
                'id_last_row': 1,
308
                'total_rows': 1,
309
            },
310
            nb_pages = 1,
311
            page = 1,
312
            event_edit_status_options = edit_event_status_options,
313
            history = events.hist,
314
            hist_error = True,
315
            plugin_context = events.context_fct,
316
            search = {
317
                'host': '',
318
                'service': '',
319
                'output': '',
320
                'tt': '',
321
                'from_date': '',
322
                'to_date': '',
323
                'hostgroup': '',
324
                'servicegroup': '',
325
            },
326
           refresh_times=config['vigiboard_refresh_times'],
327
        )
352 328

  
353 329
    @validate(
354 330
        validators={
......
359 335
    @expose('vigiboard.html')
360 336
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
361 337
    def host_service(self, host, service=None):
362
        
363 338
        """
364 339
        Affichage de l'historique de l'ensemble des événements correspondant
365 340
        au host et service demandé.
......
373 348

  
374 349
        username = request.environ['repoze.who.identity']['repoze.who.userid']
375 350
        events = VigiboardRequest(User.by_user_name(username))
351
        events.add_table(
352
            CorrEvent,
353
            events.items.c.hostname,
354
            events.items.c.servicename,
355
        )
356
        events.add_join((Event, CorrEvent.idcause == Event.idevent))
376 357
        events.add_filter(events.items.c.idsupitem == idsupitem)
377 358

  
378 359
        # XXX On devrait avoir une autre API que ça !!!
......
383 364

  
384 365
        # Vérification qu'il y a au moins 1 événement qui correspond
385 366
        if events.num_rows() == 0 :
367
            flash(_('No access to this host/service or no event yet'), 'error')
386 368
            redirect('/')
387 369

  
388 370
        events.format_events(0, events.num_rows())
......
450 432
        last_modification = get_last_modification_timestamp(ids, None)
451 433
        if last_modification and datetime.fromtimestamp(\
452 434
            float(krgv['last_modification'])) < last_modification:
453
            flash(_('Changes have occurred since the page was displayed, '
435
            flash(_('Changes have occurred since the page was last displayed, '
454 436
                    'your changes HAVE NOT been saved.'), 'warning')
455
            print "\n\n\n\n\n"
456
            print datetime.fromtimestamp(float(krgv['last_modification']))
457
            print get_last_modification_timestamp(ids)
458
            print "\n\n\n\n\n"
459
                        
460 437
            raise redirect(request.environ.get('HTTP_REFERER', url('/')))
461 438

  
462 439
        # Si l'utilisateur édite plusieurs événements à la fois,
......
467 444
        
468 445
        username = request.environ['repoze.who.identity']['repoze.who.userid']
469 446
        events = VigiboardRequest(User.by_user_name(username))
447
        events.add_table(CorrEvent)
448
        events.add_join((Event, CorrEvent.idcause == Event.idevent))
470 449
        events.add_filter(CorrEvent.idcorrevent.in_(ids))
471 450
        
472 451
        # Vérification que au moins un des identifiants existe et est éditable
vigiboard/controllers/vigiboardrequest.py
82 82
        ).filter(HOST_GROUP_TABLE.c.idgroup.label('idhostgroup').in_(self.user_groups),
83 83
        )
84 84

  
85
        # Object Selectable renvoyant des informations sur un supitem
86
        # concerné par une alerte, avec prise en compte des droits d'accès.
85 87
        # On est obligés d'utiliser sqlalchemy.sql.expression.union
86 88
        # pour indiquer à SQLAlchemy de NE PAS regrouper les tables
87 89
        # dans la requête principale, sans quoi les résultats sont
88 90
        # incorrects.
89 91
        self.items = union(lls_query, host_query, correlate=False).alias()
90 92

  
91
        self.table = [
92
            CorrEvent,
93
            sql.func.count(CorrEvent.idcorrevent),
94
            self.items.c.hostname,
95
            self.items.c.servicename,
96
        ]
93
        # Éléments à retourner (SELECT ...)
94
        self.table = []
97 95

  
98
        self.join = [
99
            (Event, CorrEvent.idcause == Event.idevent),
100
            (self.items, Event.idsupitem == self.items.c.idsupitem),
101
            (StateName, StateName.idstatename == Event.current_state),
102
        ]
103
        
96
        # Tables sur lesquelles porte la récupération (JOIN)
97
        self.join = []
98

  
99
        # Tables sur lesquelles porte la récupération (OUTER JOIN)
104 100
        self.outerjoin = []
105 101

  
102
        # Critères de filtrage (WHERE)
106 103
        self.filter = [
107 104
            # On masque les événements avec l'état OK
108 105
            # et traités (status == u'AAClosed').
......
119 116
        else:
120 117
            priority_order = desc(CorrEvent.priority)
121 118

  
119
        # Tris (ORDER BY)
122 120
        self.orderby = [
123 121
            desc(CorrEvent.status),         # None, Acknowledged, AAClosed
124 122
            priority_order,                 # Priorité ITIL (entier).
......
127 125
            asc(self.items.c.hostname),
128 126
        ]
129 127

  
128
        # Regroupements (GROUP BY)
130 129
        self.groupby = [
131
                CorrEvent,
132
                self.items.c.hostname,
133
                self.items.c.servicename,
134
                StateName.order,
135
                Event.timestamp,
136
            ]
130
            StateName.order,
131
            Event.timestamp,
132
            CorrEvent.status,
133
            CorrEvent.priority,
134
        ]
137 135

  
138 136
        self.plugin = []
139 137
        self.events = []
......
167 165
        Génération de la requête avec l'ensemble des données stockées
168 166
        et la place dans la variable rq de la classe
169 167
        """
168
        if self.generaterq:
169
            return
170

  
170 171
        for plug in config.get('vigiboard_plugins', []):
171 172
            try:
172 173
                mypac = __import__(
......
176 177
            except:
177 178
                raise
178 179

  
180
        self.join.extend([
181
            (self.items, Event.idsupitem == self.items.c.idsupitem),
182
            (StateName, StateName.idstatename == Event.current_state),
183
        ])
184
        self.add_group_by(*self.table)
185

  
179 186
        # query et join ont besoin de referrence
180 187
        self.req = self.req.query(*self.table)
181 188
        self.req = self.req.join(*self.join)
......
190 197
        for i in self.orderby:
191 198
            self.req = self.req.order_by(i)
192 199

  
200
        self.generaterq = True
201

  
193 202
    def num_rows(self):
194 203
        """
195 204
        Retourne le nombre de lignes de la requête.
......
198 207
        @return: Nombre de ligne
199 208
        """
200 209

  
201
        if not self.generaterq:
202
            self.generate_request()
203
            self.generaterq = True
210
        self.generate_request()
204 211
        return self.req.count()
205 212

  
206 213
    def add_table(self, *argv):
......
344 351
        """
345 352
        
346 353
        # Si la requête n'est pas générée, on le fait
347
        if not self.generaterq :
348
            self.generate_request()
349
            self.generaterq = True
354
        self.generate_request()
350 355

  
351 356
        # Liste des éléments pour la tête du tableau
352 357

  
......
378 383
                event = req
379 384
            else:
380 385
                event = req[0]
381
                hostname = req[2]
382
                servicename = req[3]
386
                hostname = req.hostname
387
                servicename = req.servicename
383 388
            ids.append(event.idcause)
384 389

  
385 390
            # La liste pour l'événement actuel comporte dans l'ordre :
vigiboard/tests/functional/test_history_form.py
1
# -*- coding: utf-8 -*-
2
"""
3
Teste le formulaire donnant les liens vers les outils extérieurs
4
et les données de l'historique.
5
"""
6
from nose.tools import assert_true, assert_equal
7
from datetime import datetime
8
import transaction
9

  
10
from vigiboard.tests import TestController
11
from vigiboard.model import DBSession, ServiceGroup, HostGroup, \
12
                            Host, Permission, StateName, \
13
                            ServiceLowLevel, Event, CorrEvent
14

  
15
def insert_deps(return_service):
16
    """Insère les dépendances nécessaires aux tests."""
17
    timestamp = datetime.now()
18
    DBSession.add(StateName(statename=u'OK', order=1))
19
    DBSession.add(StateName(statename=u'UNKNOWN', order=1))
20
    DBSession.add(StateName(statename=u'WARNING', order=1))
21
    DBSession.add(StateName(statename=u'CRITICAL', order=1))
22
    DBSession.flush()
23

  
24
    hostgroup = HostGroup(
25
        name=u'foo',
26
    )
27
    DBSession.add(hostgroup)
28

  
29
    host = Host(
30
        name=u'bar',
31
        checkhostcmd=u'',
32
        description=u'',
33
        hosttpl=u'',
34
        mainip=u'127.0.0.1',
35
        snmpport=42,
36
        snmpcommunity=u'public',
37
        snmpversion=u'3',
38
        weight=42,
39
    )
40
    DBSession.add(host)
41
    DBSession.flush()
42

  
43
    hostgroup.hosts.append(host)
44
    DBSession.flush()
45

  
46
    servicegroup = ServiceGroup(
47
        name=u'foo',
48
    )
49
    DBSession.add(servicegroup)
50

  
51
    service = ServiceLowLevel(
52
        host=host,
53
        command=u'',
54
        weight=42,
55
        servicename=u'baz',
56
        op_dep=u'&',
57
    )
58
    DBSession.add(service)
59
    DBSession.flush()
60

  
61
    servicegroup.services.append(service)
62
    DBSession.flush()
63

  
64
    event = Event(
65
        timestamp=timestamp,
66
        current_state=StateName.statename_to_value(u'WARNING'),
67
        message=u'Hello world',
68
    )
69
    if return_service:
70
        event.supitem = service
71
    else:
72
        event.supitem = host
73
    DBSession.add(event)
74
    DBSession.flush()
75

  
76
    correvent = CorrEvent(
77
        impact=42,
78
        priority=42,
79
        trouble_ticket=None,
80
        status=u'None',
81
        occurrence=42,
82
        timestamp_active=timestamp,
83
        cause=event,
84
    )
85
    correvent.events.append(event)
86
    DBSession.add(correvent)
87
    DBSession.flush()
88

  
89
    return (hostgroup, correvent.idcorrevent)
90

  
91
class TestHistoryForm(TestController):
92
    """Teste le dialogue pour l'accès aux historiques."""
93

  
94
    def test_history_form_LLS_alert_when_allowed(self):
95
        """Teste le formulaire d'historique avec un LLS (alerte visible)."""
96
        hostgroup, idcorrevent = insert_deps(True)
97
        manage = Permission.by_permission_name(u'manage')
98
        manage.hostgroups.append(hostgroup)
99
        DBSession.flush()
100
        transaction.commit()
101

  
102
        response = self.app.post('/history_dialog',
103
            {'idcorrevent': idcorrevent},
104
            extra_environ={'REMOTE_USER': 'manager'})
105
        json = response.json
106

  
107
        # Le contenu de "eventdetails" varie facilement.
108
        # On le teste séparément.
109
        json.pop('eventdetails', None)
110
        assert_true('eventdetails' in response.json)
111

  
112
        assert_equal(json, {
113
            "idcorrevent": idcorrevent,
114
            "service": "baz",
115
            "peak_state": "WARNING",
116
            "current_state": "WARNING",
117
            "host": "bar",
118
            "initial_state": "WARNING"
119
        })
120

  
121
    def test_history_form_host_alert_when_allowed(self):
122
        """Teste le formulaire d'historique avec un hôte (alerte visible)."""
123
        hostgroup, idcorrevent = insert_deps(False)
124
        manage = Permission.by_permission_name(u'manage')
125
        manage.hostgroups.append(hostgroup)
126
        DBSession.flush()
127
        transaction.commit()
128

  
129
        response = self.app.post('/history_dialog',
130
            {'idcorrevent': idcorrevent},
131
            extra_environ={'REMOTE_USER': 'manager'})
132
        json = response.json
133

  
134
        # Le contenu de "eventdetails" varie facilement.
135
        # On le teste séparément.
136
        json.pop('eventdetails', None)
137
        assert_true('eventdetails' in response.json)
138

  
139
        assert_equal(json, {
140
            "idcorrevent": idcorrevent,
141
            "service": None,
142
            "peak_state": "WARNING",
143
            "current_state": "WARNING",
144
            "host": "bar",
145
            "initial_state": "WARNING"
146
        })
147

  
148

  
149
    def test_history_form_LLS_when_forbidden(self):
150
        """Teste le formulaire d'historique avec un LLS (alerte invisible)."""
151
        idcorrevent = insert_deps(True)[1]
152
        DBSession.flush()
153
        transaction.commit()
154

  
155
        response = self.app.post('/history_dialog',
156
            {'idcorrevent': idcorrevent},
157
            extra_environ={'REMOTE_USER': 'manager'},
158
            status=302)
159

  
160
    def test_history_form_host_when_forbidden(self):
161
        """Teste le formulaire d'historique avec un LLS (alerte invisible)."""
162
        idcorrevent = insert_deps(False)[1]
163
        DBSession.flush()
164
        transaction.commit()
165

  
166
        response = self.app.post('/history_dialog',
167
            {'idcorrevent': idcorrevent},
168
            extra_environ={'REMOTE_USER': 'manager'},
169
            status=302)
170

  
vigiboard/tests/functional/test_host_vigiboardrequest.py
128 128
        # Derrière, VigiboardRequest doit charger le plugin de tests tout seul
129 129
        tg.config['vigiboard_plugins'] = [['tests', 'MonPlugin']]
130 130
        vigi_req = VigiboardRequest(User.by_user_name(u'editor'))
131
        vigi_req.add_table(
132
            CorrEvent,
133
            vigi_req.items.c.hostname,
134
            vigi_req.items.c.servicename,
135
        )
136
        vigi_req.add_join((Event, CorrEvent.idcause == Event.idevent))
131 137

  
132 138
        # On vérifie que le nombre d'événements corrélés 
133 139
        # trouvés par la requête est bien égal à 1.
......
156 162

  
157 163
        vigi_req = VigiboardRequest(User.by_user_name(u'manager'))
158 164
        vigi_req.add_plugin(MonPlugin)
165
        vigi_req.add_table(
166
            CorrEvent,
167
            vigi_req.items.c.hostname,
168
            vigi_req.items.c.servicename,
169
        )
170
        vigi_req.add_join((Event, CorrEvent.idcause == Event.idevent))
159 171

  
160 172
        # On vérifie que le nombre d'événements corrélés 
161 173
        # trouvés par la requête est bien égal à 2.
vigiboard/tests/functional/test_vigiboardrequest.py
194 194
        # Derrière, VigiboardRequest doit charger le plugin de tests tout seul
195 195
        tg.config['vigiboard_plugins'] = [['tests', 'MonPlugin']]
196 196
        vigi_req = VigiboardRequest(User.by_user_name(u'editor'))
197
        vigi_req.add_table(
198
            CorrEvent,
199
            vigi_req.items.c.hostname,
200
            vigi_req.items.c.servicename,
201
        )
202
        vigi_req.add_join((Event, CorrEvent.idcause == Event.idevent))
197 203

  
198 204
        # On effectue les tests suivants :
199 205
        #   le nombre de lignes (historique et événements) doivent
......
221 227
        tg.request = response.request
222 228

  
223 229
        vigi_req = VigiboardRequest(User.by_user_name(u'manager'))
230
        vigi_req.add_table(
231
            CorrEvent,
232
            vigi_req.items.c.hostname,
233
            vigi_req.items.c.servicename,
234
        )
235
        vigi_req.add_join((Event, CorrEvent.idcause == Event.idevent))
224 236
        vigi_req.add_plugin(MonPlugin)
225 237

  
226 238
        num_rows = vigi_req.num_rows()

Also available in: Unified diff