vigigraph / vigigraph / controllers / rpc.py @ 56b77667
History | View | Annotate | Download (33.2 KB)
1 |
# -*- coding: utf-8 -*-
|
---|---|
2 |
# Copyright (C) 2006-2020 CS-SI
|
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']) |