Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigigraph / vigigraph / controllers / rpc.py @ 696cffc9

History | View | Annotate | Download (33.2 KB)

1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2006-2020 CS GROUP - France
3
# License: GNU GPL v2 <http://www.gnu.org/licenses/gpl-2.0.html>
4

    
5
"""RPC controller for the combobox of vigigraph"""
6

    
7
# pylint: disable-msg=W0613
8
# W0613: Unused argument : les arguments des contrôleurs sont les composants
9
#        de la query-string de l'URL
10

    
11

    
12
import time
13
import urllib2
14
import urlparse
15
import logging
16

    
17
from tg.i18n import ugettext as _, lazy_ugettext as l_, lazy_ungettext as ln_
18
from tg import expose, request, redirect, tmpl_context, \
19
    config, validate, flash, exceptions as http_exc
20
from tg.predicates import not_anonymous, has_permission, in_group, Any, All
21

    
22
from formencode import schema
23
from tw.forms import validators
24
from sqlalchemy.orm import aliased, lazyload
25
from sqlalchemy.sql import functions
26
from sqlalchemy import or_
27

    
28
from vigilo.turbogears.controllers import BaseController
29
from vigilo.turbogears.helpers import get_current_user
30
from vigilo.turbogears.controllers.proxy import get_through_proxy
31
from tg.decorators import paginate
32

    
33
from vigilo.models.session import DBSession
34
from vigilo.models.tables import Host, SupItemGroup, PerfDataSource
35
from vigilo.models.tables import Graph, GraphGroup, Change, UserGroup
36
from vigilo.models.tables import DataPermission
37
from vigilo.models.tables.group import Group
38
from vigilo.models.tables.grouphierarchy import GroupHierarchy
39

    
40
from vigilo.models.tables.secondary_tables import SUPITEM_GROUP_TABLE
41
from vigilo.models.tables.secondary_tables import GRAPH_GROUP_TABLE
42
from vigilo.models.tables.secondary_tables import GRAPH_PERFDATASOURCE_TABLE
43
from vigilo.models.tables.secondary_tables import USER_GROUP_TABLE
44
from vigilo.models.functions import sql_escape_like
45

    
46
LOGGER = logging.getLogger(__name__)
47

    
48
__all__ = ['RpcController']
49

    
50
def ungettext(singular, plural, n):
51
    return ln_(singular, plural, n) % {
52
        'qtty': n,
53
    }
54

    
55
# pylint: disable-msg=R0201
56
class RpcController(BaseController):
57
    """
58
    Class Controleur TurboGears
59
    """
60

    
61
    # L'accès à ce contrôleur nécessite d'être identifié.
62
    allow_only = All(
63
        not_anonymous(msg=l_("You need to be authenticated")),
64
        Any(
65
            config.is_manager,
66
            has_permission('vigigraph-access',
67
                msg=l_("You don't have access to VigiGraph")),
68
        ),
69
    )
70

    
71
    # Plages de temps affichées sur la page de métrologie complète
72
    # d'un hôte avec la durée associée (en secondes).
73
    # Voir aussi graph.js pour l'équivalent côté JavaScript sur un graphe.
74
    presets = [
75
        {
76
            "caption" :
77
                ungettext("Last %(qtty)d hour", "Last %(qtty)d hours", 12),
78
            "duration" : 43200,
79
        },
80
        {
81
            "caption" :
82
                ungettext("Last %(qtty)d hour", "Last %(qtty)d hours", 24),
83
            "duration" : 86400,
84
        },
85
        {
86
            "caption" :
87
                ungettext("Last %(qtty)d day", "Last %(qtty)d days", 2),
88
            "duration" : 192800,
89
        },
90
        {
91
            "caption" :
92
                ungettext("Last %(qtty)d day", "Last %(qtty)d days", 7),
93
            "duration" : 604800,
94
        },
95
        {
96
            "caption" :
97
                ungettext("Last %(qtty)d day", "Last %(qtty)d days", 14),
98
            "duration" : 1209600,
99
        },
100
        {
101
            "caption" :
102
                ungettext("Last %(qtty)d month", "Last %(qtty)d months", 1),
103
            "duration" : 86400 * 30,
104
        },
105
        {
106
            "caption" :
107
                ungettext("Last %(qtty)d month", "Last %(qtty)d months", 3),
108
            "duration" : 86400 * 30 * 3,
109
        },
110
        {
111
            "caption" :
112
                ungettext("Last %(qtty)d month", "Last %(qtty)d months", 6),
113
            "duration" : 86400 * 30 * 6,
114
        },
115
        {
116
            "caption" :
117
                ungettext("Last %(qtty)d year", "Last %(qtty)d years", 1),
118
            "duration" : 86400 * 365,
119
        },
120
    ]
121

    
122
    def process_form_errors(self, *args, **kwargs):
123
        """
124
        Gestion des erreurs de validation : On affiche les erreurs
125
        puis on redirige vers la dernière page accédée.
126
        """
127
        for k in tmpl_context.form_errors:
128
            flash("'%s': %s" % (k, tmpl_context.form_errors[k]), 'error')
129
        redirect(request.environ.get('HTTP_REFERER', '/'))
130

    
131
    class SearchHostAndGraphSchema(schema.Schema):
132
        """Schéma de validation pour la méthode L{searchHostAndGraph}."""
133
        search_form_host = validators.UnicodeString(if_missing=None)
134
        search_form_graph = validators.UnicodeString(if_missing=None)
135

    
136
    # @TODO définir un error_handler différent pour remonter l'erreur via JS.
137
    @validate(
138
        validators = SearchHostAndGraphSchema(),
139
        error_handler = process_form_errors)
140
    @expose('json')
141
    def searchHostAndGraph(self, search_form_host=None, search_form_graph=None):
142
        """
143
        Determination des couples (hote-graphe) repondant aux criteres de
144
        recherche sur hote et/ou graphe.
145

146
        Un critere peut correspondre a un intitule complet hote ou graphe
147
        ou a un extrait.
148

149
        @return: couples hote-graphe
150
        @rtype: document json (sous forme de dict)
151
        """
152
        limit = 100
153
        user = get_current_user()
154
        ids = []
155
        labels = []
156

    
157
        if user is None:
158
            return dict(items=[])
159

    
160
        # On a un nom d'indicateur, mais pas de nom d'hôte,
161
        # on considère que l'utilisateur veut tous les indicateurs
162
        # correspondant au motif, quel que soit l'hôte.
163
        if search_form_graph:
164
            if not search_form_host:
165
                search_form_host = u'*'
166

    
167
            search_form_host = sql_escape_like(search_form_host)
168
            search_form_graph = sql_escape_like(search_form_graph)
169

    
170
            items = DBSession.query(
171
                    Host.idhost.label('idhost'),
172
                    Host.name.label('hostname'),
173
                    Host.address.label('address'),
174
                    Graph.idgraph.label('idgraph'),
175
                    Graph.name.label('graphname'),
176
                ).distinct().join(
177
                    (PerfDataSource, PerfDataSource.idhost == Host.idhost),
178
                    (GRAPH_PERFDATASOURCE_TABLE, \
179
                        GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource == \
180
                        PerfDataSource.idperfdatasource),
181
                    (Graph, Graph.idgraph == \
182
                        GRAPH_PERFDATASOURCE_TABLE.c.idgraph),
183
                    (SUPITEM_GROUP_TABLE, SUPITEM_GROUP_TABLE.c.idsupitem == \
184
                        Host.idhost),
185
                ).filter(or_(
186
                    Host.name.ilike(search_form_host),
187
                    Host.address.ilike(search_form_host),
188
                )).filter(Graph.name.ilike(search_form_graph)
189
                ).order_by(
190
                    Host.name.asc(),
191
                    Graph.name.asc(),
192
                )
193

    
194
        # On a ni hôte, ni indicateur. On renvoie une liste vide.
195
        # Si l'utilisateur voulait vraiment quelque chose,
196
        # il n'avait qu'à le demander.
197
        elif not search_form_host:
198
            return []
199

    
200
        # Sinon, on a juste un motif pour un hôte.
201
        # On renvoie la liste des hôtes correspondant.
202
        else:
203
            search_form_host = sql_escape_like(search_form_host)
204
            items = DBSession.query(
205
                    Host.idhost.label('idhost'),
206
                    Host.name.label('hostname'),
207
                    Host.address.label('address'),
208
                ).distinct().join(
209
                    (SUPITEM_GROUP_TABLE, SUPITEM_GROUP_TABLE.c.idsupitem == \
210
                        Host.idhost),
211
                ).filter(or_(
212
                    Host.name.ilike(search_form_host),
213
                    Host.address.ilike(search_form_host),
214
                )).order_by(Host.name.asc())
215

    
216
        # Les managers ont accès à tout.
217
        # Les autres ont un accès restreint.
218
        if not config.is_manager.is_met(request.environ):
219
            supitemgroups = [sig[0] for sig in user.supitemgroups() if sig[1]]
220
            # pylint: disable-msg=E1103
221
            items = items.join(
222
                (GroupHierarchy, GroupHierarchy.idchild == \
223
                    SUPITEM_GROUP_TABLE.c.idgroup)
224
            ).filter(GroupHierarchy.idparent.in_(supitemgroups))
225

    
226
        items = items.limit(limit + 1).all() # pylint: disable-msg=E1103
227
        more_results = len(items) > limit
228

    
229
        if not search_form_graph:
230
            for i in xrange(min(limit, len(items))):
231
                ids.append((items[i].idhost, None))
232
                labels.append((items[i].hostname, items[i].address, None))
233
        else:
234
            for i in xrange(min(limit, len(items))):
235
                ids.append((items[i].idhost, items[i].idgraph))
236
                labels.append((
237
                    items[i].hostname,
238
                    items[i].address,
239
                    items[i].graphname
240
                ))
241

    
242
        return dict(labels=labels, ids=ids, more=more_results)
243

    
244
    @expose('graphslist.html')
245
    def graphsList(self, graphs=None, **kwargs):
246
        """
247
        Génération d'une page d'impression avec les graphes sélectionnés.
248

249
        @param graphs : Liste des graphes à imprimer
250
        @type graphs  : C{list}
251
        @param kwargs : arguments supplémentaires (inutilisés)
252
        @type kwargs  : c{dict}
253

254
        @return: Paramètres pour la génération de la page d'impression
255
        @rtype: dict
256
        """
257
        # @TODO: le must serait de hot-patcher mootools pour que son serializer
258
        # d'URL utilise directement le format attendu par TurboGears
259
        # (notation pointée plutôt qu'avec des crochets)
260

    
261
        if not graphs:
262
            return dict(graphslist=[])
263

    
264
        if not isinstance(graphs, list):
265
            graphs = [graphs]
266

    
267
        # On est obligé de convertir le format en UTF-8 car strftime
268
        # n'accepte pas les chaînes Unicode en entrée.
269
        # TRANSLATORS: Format Python de date/heure, lisible par un humain.
270
        format = _("%a, %d %b %Y %H:%M:%S").encode('utf8')
271
        graphslist = []
272
        for graph in graphs:
273
            params = urlparse.parse_qs(graph, True, True)
274
            try:
275
                host = params['host'][0]
276
                graph = params['graph'][0]
277
                start = int(params['start'][0] or time.time() - 86400)
278
                duration = int(params['duration'][0])
279
                nocache = params['nocache'][0]
280
            except (KeyError, TypeError, ValueError):
281
                break
282

    
283
            if not (host and graph and duration and nocache):
284
                break
285

    
286
            graphslist.append({
287
                'host': host,
288
                'graph': graph,
289
                'start': start,
290
                'duration': duration,
291
                'nocache': nocache,
292
                'start_date': time.strftime(format,
293
                    time.localtime(start)).decode('utf8'),
294
                'end_date': time.strftime(format,
295
                    time.localtime(start + duration)).decode('utf8')
296
            })
297
        return dict(graphslist=graphslist)
298

    
299
    @expose('json')
300
    def tempoDelayRefresh(self, nocache=None):
301
        """
302
        Determination valeur temporisation pour le rafraichissement automatique
303
        d un graphe
304

305
        @return: valeur de temporisation
306
        @rtype: C{str}
307
        """
308

    
309
        try:
310
            delay = int(config['refresh_delay'])
311
        except (ValueError, KeyError):
312
            delay = 30
313
        return {'delay': delay}
314

    
315
    class GetIndicatorsSchema(schema.Schema):
316
        """Schéma de validation pour la méthode L{getIndicators}."""
317
        host = validators.UnicodeString(not_empty=True)
318
        graph = validators.UnicodeString(not_empty=True)
319
        nocache = validators.UnicodeString(if_missing=None)
320

    
321
    # @TODO définir un error_handler différent pour remonter l'erreur via JS.
322
    @validate(
323
        validators = GetIndicatorsSchema(),
324
        error_handler = process_form_errors)
325
    @expose('json')
326
    def getIndicators(self, host, graph, nocache=None):
327
        """
328
        Liste d indicateurs associes a un graphe
329

330
        @param graph : graphe
331
        @type graph  : C{str}
332

333
        @return: dictionnaire des indicateurs d un graphe
334
        @rtype: document json (sous forme de dict)
335
        """
336

    
337
        indicators = self.getListIndicators(host, graph)
338
        indicators = [(ind.name, ind.label) for ind in indicators]
339
        return dict(items=indicators)
340

    
341
    class StartTimeSchema(schema.Schema):
342
        """Schéma de validation pour la méthode L{getIndicators}."""
343
        host = validators.UnicodeString(not_empty=True)
344
        nocache = validators.UnicodeString(if_missing=None)
345

    
346
    # @TODO définir un error_handler différent pour remonter l'erreur via JS.
347
    @validate(
348
        validators = StartTimeSchema(),
349
        error_handler = process_form_errors)
350
    @expose('json')
351
    def startTime(self, host, nocache=None):
352
        # urllib2.quote() ne fonctionne pas sur le type unicode.
353
        # On transcode d'abord le nom d'hôte en UTF-8.
354
        quote_host = isinstance(host, unicode) and \
355
                        host.encode('utf-8') or host
356
        return get_through_proxy(
357
            'vigirrd', host,
358
            '/starttime?host=%s' % urllib2.quote(quote_host, '')
359
        )
360

    
361
    class FullHostPageSchema(schema.Schema):
362
        """Schéma de validation pour la méthode L{fullHostPage}."""
363
        host = validators.UnicodeString(not_empty=True)
364
        start = validators.Int(if_missing=None)
365
        duration = validators.Int(if_missing=86400)
366

    
367
    # VIGILO_EXIG_VIGILO_PERF_0010:Visualisation globale des graphes
368
    # VIGILO_EXIG_VIGILO_PERF_0020:Visualisation unitaire des graphes
369
    # On utilise la même page pour les 2 fonctionalités.
370
    @validate(
371
        validators = FullHostPageSchema(),
372
        error_handler = process_form_errors)
373
    @expose('fullhostpage.html')
374
    def fullHostPage(self, host, start=None, duration=86400):
375
        """
376
        Affichage de l'ensemble des graphes associes a un hote
377
        * d apres les donnees RRD
378
        * avec une date-heure de debut
379
        * pour une plage de temps
380

381
        @param host : hôte
382
        @type host : C{str}
383
        @param start : date-heure de debut des donnees
384
        @type start : C{str}
385
        @param duration : plage de temps des données. Parametre optionnel,
386
            initialisé a 86400 = plage de 1 jour.
387
        @type duration : C{str}
388

389
        @return: page avec les images des graphes et boutons de deplacement
390
            dans le temps
391
        @rtype: page html
392
        """
393
        if start is None:
394
            start = int(time.time()) - int(duration)
395
        else:
396
            start = int(start)
397
        duration = int(duration)
398

    
399
        user = get_current_user()
400
        if user is None:
401
            return dict(host=host, start=start, duration=duration,
402
                        presets=self.presets, graphs=[])
403

    
404
        # Vérification des permissions de l'utilisateur sur l'hôte.
405
        if not config.is_manager.is_met(request.environ):
406
            # Récupération des groupes auxquels l'utilisateur a accès.
407
            supitemgroups = [sig[0] for sig in user.supitemgroups() if sig[1]]
408

    
409
            # On vérifie que l'hôte en question existe bel et bien.
410
            host_obj = Host.by_host_name(host)
411
            if not host_obj:
412
                message = _('No such host "%s"') % host
413
                LOGGER.warning(message)
414
                raise http_exc.HTTPNotFound(message)
415

    
416
            # Récupération des groupes dont l'hôte fait partie
417
            hostgroups = [g.idgroup for g in host_obj.groups]
418
            # Si aucun des groupes de l'hôte ne fait partie des groupes
419
            # auxquels l'utilisateur a accès, on affiche une erreur 403.
420
            if len(set(hostgroups).intersection(set(supitemgroups))) < 1:
421
                message = _('Access denied to host "%s"') % host
422
                LOGGER.warning(message)
423
                raise http_exc.HTTPForbidden(message)
424

    
425
        # Récupération de la liste des noms des graphes associés à l'hôte.
426
        graphs = DBSession.query(
427
                Graph.name
428
            ).distinct(
429
            ).join(
430
                (GRAPH_PERFDATASOURCE_TABLE,
431
                    GRAPH_PERFDATASOURCE_TABLE.c.idgraph == Graph.idgraph),
432
                (PerfDataSource, PerfDataSource.idperfdatasource ==
433
                    GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource),
434
                (Host, Host.idhost == PerfDataSource.idhost),
435
            ).filter(Host.name == host)
436

    
437
        graphs = graphs.all()
438
        return dict(host=host, start=start, duration=duration,
439
                    presets=self.presets, graphs=graphs)
440

    
441

    
442
    class SearchHostSchema(schema.Schema):
443
        """Schéma de validation pour la méthode L{searchHost}."""
444
        allow_extra_fields = True
445
        filter_extra_fields = True
446
        query = validators.UnicodeString(not_empty=True)
447

    
448
    @expose('searchhost.html')
449
    @validate(
450
        validators = SearchHostSchema(),
451
        error_handler = process_form_errors)
452
    @paginate('hosts', items_per_page=10)
453
    def searchHost(self, query):
454
        """
455
        Affiche les résultats de la recherche par nom d'hôte.
456
        La requête de recherche (C{query}) correspond à un préfixe
457
        qui sera recherché dans le nom d'hôte. Ce préfixe peut
458
        contenir les caractères '*' et '?' qui agissent comme des
459
        "jokers".
460

461
        @keyword query: Prefixe de recherche sur les noms d'hôtes
462
        @type    query: C{unicode}
463
        """
464
        if not query:
465
            redirect("searchHostForm")
466
            return
467

    
468
        query = sql_escape_like(query.strip())
469
        user = get_current_user()
470
        if user is None:
471
            return dict(items=[])
472

    
473
        # Récupère les hôtes auxquels l'utilisateur a réellement accès.
474
        hosts = DBSession.query(
475
                Host.name,
476
                Host.address,
477
            ).distinct(
478
            ).join(
479
                (SUPITEM_GROUP_TABLE, SUPITEM_GROUP_TABLE.c.idsupitem == \
480
                    Host.idhost),
481
            ).filter(or_(
482
                Host.name.like(query + u'%'),
483
                Host.address.like(query + u'%')
484
            )).order_by(Host.name.asc(),)
485

    
486
        # Les managers ont accès à tout.
487
        # Les autres ont un accès restreint.
488
        if not config.is_manager.is_met(request.environ):
489
            supitemgroups = [sig[0] for sig in user.supitemgroups() if sig[1]]
490
            hosts = hosts.join(
491
                    (GroupHierarchy, GroupHierarchy.idchild == \
492
                        SUPITEM_GROUP_TABLE.c.idgroup)
493
                ).filter(GroupHierarchy.idparent.in_(supitemgroups))
494

    
495
        return dict(hosts=[(h.name, h.address) for h in hosts])
496

    
497
    # VIGILO_EXIG_VIGILO_PERF_0030:Moteur de recherche des graphes
498
    @expose('getopensearch.xml', content_type='text/xml')
499
    def getOpenSearch(self):
500
        """
501
        Moteur de recherche des graphes
502

503
        @return: document
504
        @rtype: document xml
505
        """
506
        return dict()
507

    
508
    @expose('json')
509
    def hosttree(self, parent_id=None, onlytype="", offset=0, noCache=None):
510
        """
511
        Affiche un étage de l'arbre de
512
        sélection des hôtes et groupes d'hôtes.
513

514
        @param parent_id: identifiant du groupe d'hôte parent
515
        @type  parent_id: C{int} or None
516
        """
517

    
518
        # Si l'identifiant du groupe parent n'est pas
519
        # spécifié, on retourne la liste des groupes racines,
520
        # fournie par la méthode get_root_hosts_groups.
521
        if parent_id is None:
522
            return self.get_root_host_groups()
523

    
524
        # TODO: Utiliser un schéma de validation
525
        parent_id = int(parent_id)
526
        offset = int(offset)
527

    
528
        # On vérifie si le groupe parent fait partie des
529
        # groupes auxquel l'utilisateur a accès, et on
530
        # retourne une liste vide dans le cas contraire
531
        is_manager = config.is_manager.is_met(request.environ)
532
        if not is_manager:
533
            direct_access = False
534
            user = get_current_user()
535

    
536
            # On calcule la distance de ce groupe par rapport aux groupes
537
            # sur lesquels l'utilisateur a explicitement les permissions.
538
            #
539
            # La distance est définie ainsi :
540
            # 0 : l'utilisateur a des droits explicites sur ce groupe.
541
            # > 0 : l'utilisateur a accès implicitement au groupe.
542
            # < 0 : l'utilisateur n'a pas d'accès (il peut juste parcourir
543
            #       ce groupe)
544
            #
545
            # Il faut 2 étapes pour trouver la distance. La 1ère essaye
546
            # de trouver une distance >= 0, la 2ème une distance <= 0.
547

    
548
            # Distance positive.
549
            distance = DBSession.query(
550
                    functions.max(GroupHierarchy.hops)
551
                ).join(
552
                    (Group, Group.idgroup == GroupHierarchy.idparent),
553
                    (DataPermission,
554
                        DataPermission.idgroup == Group.idgroup),
555
                    (UserGroup,
556
                        UserGroup.idgroup == DataPermission.idusergroup),
557
                    (USER_GROUP_TABLE, USER_GROUP_TABLE.c.idgroup == \
558
                        UserGroup.idgroup),
559
                ).filter(USER_GROUP_TABLE.c.username == user.user_name
560
                ).filter(Group.grouptype == u'supitemgroup'
561
                ).filter(GroupHierarchy.idchild == parent_id
562
                ).scalar()
563

    
564
            if distance is None:
565
                # Distance négative.
566
                distance = DBSession.query(
567
                        functions.max(GroupHierarchy.hops)
568
                    ).join(
569
                        (Group, Group.idgroup == GroupHierarchy.idchild),
570
                        (DataPermission,
571
                            DataPermission.idgroup == Group.idgroup),
572
                        (UserGroup,
573
                            UserGroup.idgroup == DataPermission.idusergroup),
574
                        (USER_GROUP_TABLE, USER_GROUP_TABLE.c.idgroup == \
575
                            UserGroup.idgroup),
576
                    ).filter(USER_GROUP_TABLE.c.username == user.user_name
577
                    ).filter(Group.grouptype == u'supitemgroup'
578
                    ).filter(GroupHierarchy.idparent == parent_id
579
                    ).scalar()
580
                if distance is not None:
581
                    distance = -distance
582

    
583
            if distance is None:
584
                # Pas d'accès à ce groupe.
585
                return dict(groups = [], items = [])
586

    
587
            direct_access = distance >= 0
588

    
589
        limit = int(config.get("max_menu_entries", 20))
590
        result = {"groups": [], "items": []}
591

    
592
        if not onlytype or onlytype == "group":
593
            # On récupère la liste des groupes dont
594
            # l'identifiant du parent est passé en paramètre
595
            gh1 = aliased(GroupHierarchy, name='gh1')
596
            gh2 = aliased(GroupHierarchy, name='gh2')
597

    
598
            db_groups = DBSession.query(
599
                SupItemGroup
600
            ).options(lazyload('_path_obj')
601
            ).distinct(
602
            ).join(
603
                (gh1, gh1.idchild == SupItemGroup.idgroup),
604
            ).filter(gh1.hops == 1
605
            ).filter(gh1.idparent == parent_id
606
            ).order_by(SupItemGroup.name.asc())
607

    
608
            if not is_manager and not direct_access:
609
                # On ne doit afficher que les fils du groupe <parent_id>
610
                # tels que l'utilisateur a accès explicitement à l'un
611
                # des fils de l'un de ces groupes.
612
                db_groups = db_groups.join(
613
                        (gh2, gh2.idparent == gh1.idchild),
614
                        (DataPermission,
615
                            DataPermission.idgroup == gh2.idchild),
616
                        (UserGroup,
617
                            UserGroup.idgroup == DataPermission.idusergroup),
618
                        (USER_GROUP_TABLE,
619
                            USER_GROUP_TABLE.c.idgroup == UserGroup.idgroup),
620
                    ).filter(USER_GROUP_TABLE.c.username == user.user_name)
621

    
622
            num_children_left = db_groups.count() - offset
623
            if offset:
624
                result["continued_from"] = offset
625
                result["continued_type"] = "group"
626
            all_groups = db_groups.limit(limit).offset(offset).all()
627
            for group in all_groups:
628
                result["groups"].append({
629
                    'id'   : group.idgroup,
630
                    'name' : group.name,
631
                    'type' : "group",
632
                })
633
            if num_children_left > limit:
634
                result["groups"].append({
635
                    'name': _("Next %(limit)s") % {"limit": limit},
636
                    'offset': offset + limit,
637
                    'parent_id': parent_id,
638
                    'type': 'continued',
639
                    'for_type': 'group',
640
                })
641

    
642
        # On récupère la liste des hôtes appartenant au
643
        # groupe dont l'identifiant est passé en paramètre
644
        if ((not onlytype or onlytype == "item")
645
                and (is_manager or direct_access)):
646
            db_hosts = DBSession.query(
647
                Host.idhost,
648
                Host.name,
649
            ).join(
650
                (SUPITEM_GROUP_TABLE,
651
                    SUPITEM_GROUP_TABLE.c.idsupitem == Host.idhost
652
                    ),
653
            ).filter(SUPITEM_GROUP_TABLE.c.idgroup == parent_id
654
            ).order_by(Host.name.asc())
655
            num_children_left = db_hosts.count() - offset
656
            if offset:
657
                result["continued_from"] = offset
658
                result["continued_type"] = "item"
659
            all_hosts = db_hosts.limit(limit).offset(offset).all()
660
            for host in all_hosts:
661
                result["items"].append({
662
                    'id'   : host.idhost,
663
                    'name' : host.name,
664
                    'type' : "item",
665
                })
666
            if num_children_left > limit:
667
                result["items"].append({
668
                    'name': _("Next %(limit)s") % {"limit": limit},
669
                    'offset': offset + limit,
670
                    'parent_id': parent_id,
671
                    'type': 'continued',
672
                    'for_type': 'item',
673
                })
674

    
675
        return result
676

    
677
    @expose('json')
678
    def graphtree(self, host_id=None, parent_id=None, offset=0, noCache=None):
679
        """
680
        Affiche un étage de l'arbre de sélection
681
        des graphes et groupes de graphes.
682

683
        @param parent_id: identifiant du groupe de graphes parent
684
        @type  parent_id: C{int} or None
685
        """
686

    
687
        # Si l'identifiant de l'hôte n'est pas spécifié, on
688
        # retourne un dictionnaire contenant deux listes vides
689
        if host_id is None:
690
            return dict(groups = [], graphs=[])
691

    
692
        # On vérifie les permissions sur l'hôte
693
        # TODO: Utiliser un schéma de validation
694
        host_id = int(host_id)
695
        host = DBSession.query(Host
696
            ).filter(Host.idhost == host_id
697
            ).first()
698
        if host is None:
699
            return dict(groups = [], graphs=[])
700
        user = get_current_user()
701
        if not host.is_allowed_for(user):
702
            return dict(groups = [], graphs=[])
703

    
704
        # On récupère la liste des groupes de graphes associés à l'hôte
705
        host_graph_groups = DBSession.query(
706
            GraphGroup
707
        ).distinct(
708
        ).join(
709
            (GRAPH_GROUP_TABLE, \
710
                GRAPH_GROUP_TABLE.c.idgroup == GraphGroup.idgroup),
711
            (Graph, Graph.idgraph == GRAPH_GROUP_TABLE.c.idgraph),
712
            (GRAPH_PERFDATASOURCE_TABLE, \
713
                    GRAPH_PERFDATASOURCE_TABLE.c.idgraph == Graph.idgraph),
714
            (PerfDataSource, PerfDataSource.idperfdatasource == \
715
                    GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource),
716
            (SUPITEM_GROUP_TABLE, \
717
                SUPITEM_GROUP_TABLE.c.idsupitem == PerfDataSource.idhost),
718
        ).filter(PerfDataSource.idhost == host_id
719
        ).order_by(GraphGroup.name.asc()
720
        ).all()
721

    
722
        # Si l'identifiant du groupe parent n'est pas spécifié,
723
        # on récupère la liste des groupes de graphes racines.
724
        if parent_id is None:
725
            graph_groups = GraphGroup.get_top_groups()
726

    
727
        # Sinon on récupère la liste des graphes dont le
728
        # groupe passé en paramètre est le parent direct
729
        else:
730
            # TODO: Utiliser un schéma de validation
731
            parent_id = int(parent_id)
732
            graph_groups = DBSession.query(
733
                GraphGroup
734
            ).join(
735
                (GroupHierarchy, GroupHierarchy.idchild == \
736
                    GraphGroup.idgroup),
737
            ).filter(GroupHierarchy.hops == 1
738
            ).filter(GroupHierarchy.idparent == parent_id
739
            ).order_by(GraphGroup.name.asc()
740
            ).all()
741

    
742
        # On réalise l'intersection des deux listes
743
        groups = []
744
        for gg in graph_groups:
745
            if gg in host_graph_groups:
746
                groups.append({
747
                    'id'   : gg.idgroup,
748
                    'name' : gg.name,
749
                    'type' : "group",
750
                })
751

    
752
        # On récupère la liste des graphes appartenant au
753
        # groupe dont l'identifiant est passé en paramètre
754
        graphs = []
755
        if parent_id:
756
            db_graphs = DBSession.query(
757
                Graph.idgraph,
758
                Graph.name,
759
            ).distinct(
760
            ).join(
761
                (GRAPH_GROUP_TABLE,
762
                    GRAPH_GROUP_TABLE.c.idgraph == Graph.idgraph),
763
                (GRAPH_PERFDATASOURCE_TABLE,
764
                    GRAPH_PERFDATASOURCE_TABLE.c.idgraph == Graph.idgraph),
765
                (PerfDataSource,
766
                    PerfDataSource.idperfdatasource == \
767
                        GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource),
768
            ).filter(GRAPH_GROUP_TABLE.c.idgroup == parent_id
769
            ).filter(PerfDataSource.idhost == host_id
770
            ).order_by(Graph.name.asc())
771
            for graph in db_graphs.all():
772
                graphs.append({
773
                    'id'   : graph.idgraph,
774
                    'name' : graph.name,
775
                    'type' : "item",
776
                })
777

    
778
        return dict(groups=groups, items=graphs)
779

    
780
    def get_root_host_groups(self):
781
        """
782
        Retourne tous les groupes racines (c'est à dire n'ayant
783
        aucun parent) d'hôtes auquel l'utilisateur a accès.
784

785
        @return: Un dictionnaire contenant la liste de ces groupes.
786
        @rtype : C{dict} of C{list} of C{dict} of C{mixed}
787
        """
788

    
789
        # On récupère tous les groupes qui ont un parent.
790
        children = DBSession.query(
791
            SupItemGroup,
792
        ).distinct(
793
        ).join(
794
            (GroupHierarchy, GroupHierarchy.idchild == SupItemGroup.idgroup)
795
        ).filter(GroupHierarchy.hops > 0)
796

    
797
        # Ensuite on les exclut de la liste des groupes,
798
        # pour ne garder que ceux qui sont au sommet de
799
        # l'arbre et qui constituent nos "root groups".
800
        root_groups = DBSession.query(
801
            SupItemGroup,
802
        ).except_(children
803
        ).order_by(SupItemGroup.name)
804

    
805
        # On filtre ces groupes racines afin de ne
806
        # retourner que ceux auquels l'utilisateur a accès
807
        user = get_current_user()
808
        if not config.is_manager.is_met(request.environ):
809
            user_groups = [ug[0] for ug in user.supitemgroups()]
810
            root_groups = root_groups.filter(
811
                SupItemGroup.idgroup.in_(user_groups))
812

    
813
        groups = []
814
        for group in root_groups.all():
815
            groups.append({
816
                'id'   : group.idgroup,
817
                'name' : group.name,
818
                'type' : "group",
819
            })
820

    
821
        return dict(groups=groups, items=[])
822

    
823
    def getListIndicators(self, host, graph):
824
        """
825
        Liste d indicateurs associes a un graphe
826

827
        @param graph : graphe
828
        @type graph  : C{str}
829

830
        @return: liste d indicateurs
831
        @rtype  : list
832
        """
833

    
834
        indicators = []
835
        if graph is not None:
836
            indicators = DBSession.query(
837
                    PerfDataSource.name, PerfDataSource.label
838
                ).distinct(
839
                ).join(
840
                    (GRAPH_PERFDATASOURCE_TABLE, \
841
                        GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource == \
842
                        PerfDataSource.idperfdatasource),
843
                    (Graph, Graph.idgraph == \
844
                        GRAPH_PERFDATASOURCE_TABLE.c.idgraph),
845
                    (Host, Host.idhost == PerfDataSource.idhost),
846
                ).filter(Graph.name == graph
847
                ).filter(Host.name == host
848
                ).all()
849
        return indicators
850

    
851
    @expose('json')
852
    def dbmtime(self):
853
        change = Change.by_table_name(u"Graph")
854
        if change is None:
855
            return {"mtime": None}
856
        mtime = change.last_modified.replace(microsecond=0)
857
        return {"mtime": mtime}
858

    
859
    @expose('json')
860
    def selectHostAndGraph(self, host, graph):
861
        # @TODO: vérifier les permissions
862
        ids = DBSession.query(
863
                Host.idhost, Graph.idgraph
864
            ).join(
865
                (PerfDataSource, PerfDataSource.idhost == Host.idhost),
866
                (GRAPH_PERFDATASOURCE_TABLE, \
867
                    GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource == \
868
                    PerfDataSource.idperfdatasource),
869
                (Graph, Graph.idgraph == \
870
                    GRAPH_PERFDATASOURCE_TABLE.c.idgraph),
871
            ).filter(Graph.name == unicode(graph)
872
            ).filter(Host.name == unicode(host)
873
            ).first()
874

    
875
        return {
876
            'idhost': ids and ids.idhost or None,
877
            'idgraph': ids and ids.idgraph or None,
878
        }
879

    
880
    @expose('json')
881
    def external_links(self):
882
        return dict(links=config['external_links'])