vigigraph / vigigraph / controllers / rpc.py @ 8089d65a
History | View | Annotate | Download (32.7 KB)
1 |
# -*- coding: utf-8 -*-
|
---|---|
2 |
# Copyright (C) 2006-2013 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 logging |
15 |
|
16 |
## La fonction parse_qsl a été déplacée dans Python 2.6.
|
17 |
#try:
|
18 |
# from urlparse import parse_qsl
|
19 |
#except ImportError:
|
20 |
# from cgi import parse_qsl
|
21 |
|
22 |
from pylons.i18n import ugettext as _, lazy_ugettext as l_, \ |
23 |
lazy_ungettext as ln_ |
24 |
from tg import expose, request, redirect, tmpl_context, \ |
25 |
config, validate, flash, exceptions as http_exc |
26 |
|
27 |
from repoze.what.predicates import not_anonymous, has_permission, \ |
28 |
in_group, Any, All |
29 |
from formencode import schema |
30 |
from tw.forms import validators |
31 |
from sqlalchemy.orm import aliased, lazyload |
32 |
from sqlalchemy.sql import functions |
33 |
|
34 |
from vigilo.turbogears.controllers import BaseController |
35 |
from vigilo.turbogears.helpers import get_current_user |
36 |
from vigilo.turbogears.controllers.proxy import get_through_proxy |
37 |
from vigilo.turbogears.decorators import paginate |
38 |
|
39 |
from vigilo.models.session import DBSession |
40 |
from vigilo.models.tables import Host, SupItemGroup, PerfDataSource |
41 |
from vigilo.models.tables import Graph, GraphGroup, Change, UserGroup |
42 |
from vigilo.models.tables import DataPermission |
43 |
from vigilo.models.tables.group import Group |
44 |
from vigilo.models.tables.grouphierarchy import GroupHierarchy |
45 |
|
46 |
from vigilo.models.tables.secondary_tables import SUPITEM_GROUP_TABLE |
47 |
from vigilo.models.tables.secondary_tables import GRAPH_GROUP_TABLE |
48 |
from vigilo.models.tables.secondary_tables import GRAPH_PERFDATASOURCE_TABLE |
49 |
from vigilo.models.tables.secondary_tables import USER_GROUP_TABLE |
50 |
from vigilo.models.functions import sql_escape_like |
51 |
|
52 |
LOGGER = logging.getLogger(__name__) |
53 |
|
54 |
__all__ = ['RpcController']
|
55 |
|
56 |
def ungettext(singular, plural, n): |
57 |
return ln_(singular, plural, n) % {
|
58 |
'qtty': n,
|
59 |
} |
60 |
|
61 |
# pylint: disable-msg=R0201
|
62 |
class RpcController(BaseController): |
63 |
"""
|
64 |
Class Controleur TurboGears
|
65 |
"""
|
66 |
|
67 |
# L'accès à ce contrôleur nécessite d'être identifié.
|
68 |
allow_only = All( |
69 |
not_anonymous(msg=l_("You need to be authenticated")),
|
70 |
Any( |
71 |
config.is_manager, |
72 |
has_permission('vigigraph-access',
|
73 |
msg=l_("You don't have access to VigiGraph")),
|
74 |
), |
75 |
) |
76 |
|
77 |
# Plages de temps affichées sur la page de métrologie complète
|
78 |
# d'un hôte avec la durée associée (en secondes).
|
79 |
# Voir aussi graph.js pour l'équivalent côté JavaScript sur un graphe.
|
80 |
presets = [ |
81 |
{ |
82 |
"caption" :
|
83 |
ungettext("Last %(qtty)d hour", "Last %(qtty)d hours", 12), |
84 |
"duration" : 43200, |
85 |
}, |
86 |
{ |
87 |
"caption" :
|
88 |
ungettext("Last %(qtty)d hour", "Last %(qtty)d hours", 24), |
89 |
"duration" : 86400, |
90 |
}, |
91 |
{ |
92 |
"caption" :
|
93 |
ungettext("Last %(qtty)d day", "Last %(qtty)d days", 2), |
94 |
"duration" : 192800, |
95 |
}, |
96 |
{ |
97 |
"caption" :
|
98 |
ungettext("Last %(qtty)d day", "Last %(qtty)d days", 7), |
99 |
"duration" : 604800, |
100 |
}, |
101 |
{ |
102 |
"caption" :
|
103 |
ungettext("Last %(qtty)d day", "Last %(qtty)d days", 14), |
104 |
"duration" : 1209600, |
105 |
}, |
106 |
{ |
107 |
"caption" :
|
108 |
ungettext("Last %(qtty)d month", "Last %(qtty)d months", 1), |
109 |
"duration" : 86400 * 30, |
110 |
}, |
111 |
{ |
112 |
"caption" :
|
113 |
ungettext("Last %(qtty)d month", "Last %(qtty)d months", 3), |
114 |
"duration" : 86400 * 30 * 3, |
115 |
}, |
116 |
{ |
117 |
"caption" :
|
118 |
ungettext("Last %(qtty)d month", "Last %(qtty)d months", 6), |
119 |
"duration" : 86400 * 30 * 6, |
120 |
}, |
121 |
{ |
122 |
"caption" :
|
123 |
ungettext("Last %(qtty)d year", "Last %(qtty)d years", 1), |
124 |
"duration" : 86400 * 365, |
125 |
}, |
126 |
] |
127 |
|
128 |
def process_form_errors(self, *args, **kwargs): |
129 |
"""
|
130 |
Gestion des erreurs de validation : On affiche les erreurs
|
131 |
puis on redirige vers la dernière page accédée.
|
132 |
"""
|
133 |
for k in tmpl_context.form_errors: |
134 |
flash("'%s': %s" % (k, tmpl_context.form_errors[k]), 'error') |
135 |
redirect(request.environ.get('HTTP_REFERER', '/')) |
136 |
|
137 |
class SearchHostAndGraphSchema(schema.Schema): |
138 |
"""Schéma de validation pour la méthode L{searchHostAndGraph}."""
|
139 |
search_form_host = validators.UnicodeString(if_missing=None)
|
140 |
search_form_graph = validators.UnicodeString(if_missing=None)
|
141 |
|
142 |
# @TODO définir un error_handler différent pour remonter l'erreur via JS.
|
143 |
@validate(
|
144 |
validators = SearchHostAndGraphSchema(), |
145 |
error_handler = process_form_errors) |
146 |
@expose('json') |
147 |
def searchHostAndGraph(self, search_form_host, search_form_graph): |
148 |
"""
|
149 |
Determination des couples (hote-graphe) repondant aux criteres de
|
150 |
recherche sur hote et/ou graphe.
|
151 |
|
152 |
Un critere peut correspondre a un intitule complet hote ou graphe
|
153 |
ou a un extrait.
|
154 |
|
155 |
@return: couples hote-graphe
|
156 |
@rtype: document json (sous forme de dict)
|
157 |
"""
|
158 |
limit = 100
|
159 |
user = get_current_user() |
160 |
ids = [] |
161 |
labels = [] |
162 |
|
163 |
if user is None: |
164 |
return dict(items=[]) |
165 |
|
166 |
# On a un nom d'indicateur, mais pas de nom d'hôte,
|
167 |
# on considère que l'utilisateur veut tous les indicateurs
|
168 |
# correspondant au motif, quel que soit l'hôte.
|
169 |
if search_form_graph:
|
170 |
if not search_form_host: |
171 |
search_form_host = u'*'
|
172 |
|
173 |
search_form_host = sql_escape_like(search_form_host) |
174 |
search_form_graph = sql_escape_like(search_form_graph) |
175 |
|
176 |
items = DBSession.query( |
177 |
Host.idhost.label('idhost'),
|
178 |
Host.name.label('hostname'),
|
179 |
Graph.idgraph.label('idgraph'),
|
180 |
Graph.name.label('graphname'),
|
181 |
).distinct().join( |
182 |
(PerfDataSource, PerfDataSource.idhost == Host.idhost), |
183 |
(GRAPH_PERFDATASOURCE_TABLE, \ |
184 |
GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource == \ |
185 |
PerfDataSource.idperfdatasource), |
186 |
(Graph, Graph.idgraph == \ |
187 |
GRAPH_PERFDATASOURCE_TABLE.c.idgraph), |
188 |
(SUPITEM_GROUP_TABLE, SUPITEM_GROUP_TABLE.c.idsupitem == \ |
189 |
Host.idhost), |
190 |
).filter(Host.name.ilike(search_form_host) |
191 |
).filter(Graph.name.ilike(search_form_graph) |
192 |
).order_by( |
193 |
Host.name.asc(), |
194 |
Graph.name.asc(), |
195 |
) |
196 |
|
197 |
# On a ni hôte, ni indicateur. On renvoie une liste vide.
|
198 |
# Si l'utilisateur voulait vraiment quelque chose,
|
199 |
# il n'avait qu'à le demander.
|
200 |
elif not search_form_host: |
201 |
return []
|
202 |
|
203 |
# Sinon, on a juste un motif pour un hôte.
|
204 |
# On renvoie la liste des hôtes correspondant.
|
205 |
else:
|
206 |
search_form_host = sql_escape_like(search_form_host) |
207 |
items = DBSession.query( |
208 |
Host.idhost.label('idhost'),
|
209 |
Host.name.label('hostname'),
|
210 |
).distinct().join( |
211 |
(SUPITEM_GROUP_TABLE, SUPITEM_GROUP_TABLE.c.idsupitem == \ |
212 |
Host.idhost), |
213 |
).filter(Host.name.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, None))
|
233 |
else:
|
234 |
for i in xrange(min(limit, len(items))): |
235 |
ids.append((items[i].idhost, items[i].idgraph)) |
236 |
labels.append((items[i].hostname, items[i].graphname)) |
237 |
|
238 |
return dict(labels=labels, ids=ids, more=more_results) |
239 |
|
240 |
@expose('graphslist.html') |
241 |
def graphsList(self, nocache=None, **kwargs): |
242 |
"""
|
243 |
Generation document avec url des graphes affiches
|
244 |
(pour l impression )
|
245 |
|
246 |
@param kwargs : arguments nommes
|
247 |
@type kwargs : dict
|
248 |
|
249 |
@return: url de graphes
|
250 |
@rtype: document html
|
251 |
"""
|
252 |
# @TODO: le must serait de hot-patcher mootools pour que son serializer
|
253 |
# d'URL utilise directement le format attendu par TurboGears
|
254 |
# (notation pointée plutôt qu'avec des crochets)
|
255 |
|
256 |
if not kwargs: |
257 |
return dict(graphslist=[]) |
258 |
|
259 |
# On est obligé de convertir le format en UTF-8 car strftime
|
260 |
# n'accepte pas les chaînes Unicode en entrée.
|
261 |
# TRANSLATORS: Format Python de date/heure, lisible par un humain.
|
262 |
format = _("%a, %d %b %Y %H:%M:%S").encode('utf8') |
263 |
i = 0
|
264 |
graphslist = [] |
265 |
|
266 |
while True: |
267 |
try:
|
268 |
host = kwargs['graphs[%d][host]' % i]
|
269 |
graph = kwargs['graphs[%d][graph]' % i]
|
270 |
start = int(kwargs.get('graphs[%d][start]' % i, |
271 |
time.time() - 86400))
|
272 |
duration = int(kwargs.get('graphs[%d][duration]' % i)) |
273 |
nocache = kwargs['graphs[%d][nocache]' % i]
|
274 |
except KeyError: |
275 |
break
|
276 |
|
277 |
if not (host and graph and duration and nocache): |
278 |
break
|
279 |
|
280 |
graphslist.append({ |
281 |
'host': host,
|
282 |
'graph': graph,
|
283 |
'start': start,
|
284 |
'duration': duration,
|
285 |
'nocache': nocache,
|
286 |
'start_date': time.strftime(format,
|
287 |
time.localtime(start)).decode('utf8'),
|
288 |
'end_date': time.strftime(format,
|
289 |
time.localtime(start + duration)).decode('utf8')
|
290 |
}) |
291 |
i += 1
|
292 |
return dict(graphslist=graphslist) |
293 |
|
294 |
@expose('json') |
295 |
def tempoDelayRefresh(self, nocache=None): |
296 |
"""
|
297 |
Determination valeur temporisation pour le rafraichissement automatique
|
298 |
d un graphe
|
299 |
|
300 |
@return: valeur de temporisation
|
301 |
@rtype: C{str}
|
302 |
"""
|
303 |
|
304 |
try:
|
305 |
delay = int(config['refresh_delay']) |
306 |
except (ValueError, KeyError): |
307 |
delay = 30
|
308 |
return {'delay': delay} |
309 |
|
310 |
class GetIndicatorsSchema(schema.Schema): |
311 |
"""Schéma de validation pour la méthode L{getIndicators}."""
|
312 |
host = validators.UnicodeString(not_empty=True)
|
313 |
graph = validators.UnicodeString(not_empty=True)
|
314 |
nocache = validators.UnicodeString(if_missing=None)
|
315 |
|
316 |
# @TODO définir un error_handler différent pour remonter l'erreur via JS.
|
317 |
@validate(
|
318 |
validators = GetIndicatorsSchema(), |
319 |
error_handler = process_form_errors) |
320 |
@expose('json') |
321 |
def getIndicators(self, host, graph, nocache): |
322 |
"""
|
323 |
Liste d indicateurs associes a un graphe
|
324 |
|
325 |
@param graph : graphe
|
326 |
@type graph : C{str}
|
327 |
|
328 |
@return: dictionnaire des indicateurs d un graphe
|
329 |
@rtype: document json (sous forme de dict)
|
330 |
"""
|
331 |
|
332 |
indicators = self.getListIndicators(host, graph)
|
333 |
indicators = [(ind.name, ind.label) for ind in indicators] |
334 |
return dict(items=indicators) |
335 |
|
336 |
class StartTimeSchema(schema.Schema): |
337 |
"""Schéma de validation pour la méthode L{getIndicators}."""
|
338 |
host = validators.UnicodeString(not_empty=True)
|
339 |
nocache = validators.UnicodeString(if_missing=None)
|
340 |
|
341 |
# @TODO définir un error_handler différent pour remonter l'erreur via JS.
|
342 |
@validate(
|
343 |
validators = StartTimeSchema(), |
344 |
error_handler = process_form_errors) |
345 |
@expose('json') |
346 |
def startTime(self, host, nocache): |
347 |
# urllib2.quote() ne fonctionne pas sur le type unicode.
|
348 |
# On transcode d'abord le nom d'hôte en UTF-8.
|
349 |
quote_host = isinstance(host, unicode) and \ |
350 |
host.encode('utf-8') or host |
351 |
return get_through_proxy(
|
352 |
'vigirrd', host,
|
353 |
'/starttime?host=%s' % urllib2.quote(quote_host, '') |
354 |
) |
355 |
|
356 |
class FullHostPageSchema(schema.Schema): |
357 |
"""Schéma de validation pour la méthode L{fullHostPage}."""
|
358 |
host = validators.UnicodeString(not_empty=True)
|
359 |
start = validators.Int(if_missing=None)
|
360 |
duration = validators.Int(if_missing=86400)
|
361 |
|
362 |
# VIGILO_EXIG_VIGILO_PERF_0010:Visualisation globale des graphes
|
363 |
# VIGILO_EXIG_VIGILO_PERF_0020:Visualisation unitaire des graphes
|
364 |
# On utilise la même page pour les 2 fonctionalités.
|
365 |
@validate(
|
366 |
validators = FullHostPageSchema(), |
367 |
error_handler = process_form_errors) |
368 |
@expose('fullhostpage.html') |
369 |
def fullHostPage(self, host, start=None, duration=86400): |
370 |
"""
|
371 |
Affichage de l'ensemble des graphes associes a un hote
|
372 |
* d apres les donnees RRD
|
373 |
* avec une date-heure de debut
|
374 |
* pour une plage de temps
|
375 |
|
376 |
@param host : hôte
|
377 |
@type host : C{str}
|
378 |
@param start : date-heure de debut des donnees
|
379 |
@type start : C{str}
|
380 |
@param duration : plage de temps des données. Parametre optionnel,
|
381 |
initialisé a 86400 = plage de 1 jour.
|
382 |
@type duration : C{str}
|
383 |
|
384 |
@return: page avec les images des graphes et boutons de deplacement
|
385 |
dans le temps
|
386 |
@rtype: page html
|
387 |
"""
|
388 |
if start is None: |
389 |
start = int(time.time()) - int(duration) |
390 |
else:
|
391 |
start = int(start)
|
392 |
duration = int(duration)
|
393 |
|
394 |
user = get_current_user() |
395 |
if user is None: |
396 |
return dict(host=host, start=start, duration=duration, |
397 |
presets=self.presets, graphs=[])
|
398 |
|
399 |
# Vérification des permissions de l'utilisateur sur l'hôte.
|
400 |
if not config.is_manager.is_met(request.environ): |
401 |
# Récupération des groupes auxquels l'utilisateur a accès.
|
402 |
supitemgroups = [sig[0] for sig in user.supitemgroups() if sig[1]] |
403 |
|
404 |
# On vérifie que l'hôte en question existe bel et bien.
|
405 |
host_obj = Host.by_host_name(host) |
406 |
if not host_obj: |
407 |
message = _('No such host "%s"') % host
|
408 |
LOGGER.warning(message) |
409 |
raise http_exc.HTTPNotFound(message)
|
410 |
|
411 |
# Récupération des groupes dont l'hôte fait partie
|
412 |
hostgroups = [g.idgroup for g in host_obj.groups] |
413 |
# Si aucun des groupes de l'hôte ne fait partie des groupes
|
414 |
# auxquels l'utilisateur a accès, on affiche une erreur 403.
|
415 |
if len(set(hostgroups).intersection(set(supitemgroups))) < 1: |
416 |
message = _('Access denied to host "%s"') % host
|
417 |
LOGGER.warning(message) |
418 |
raise http_exc.HTTPForbidden(message)
|
419 |
|
420 |
# Récupération de la liste des noms des graphes associés à l'hôte.
|
421 |
graphs = DBSession.query( |
422 |
Graph.name |
423 |
).distinct( |
424 |
).join( |
425 |
(GRAPH_PERFDATASOURCE_TABLE, |
426 |
GRAPH_PERFDATASOURCE_TABLE.c.idgraph == Graph.idgraph), |
427 |
(PerfDataSource, PerfDataSource.idperfdatasource == |
428 |
GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource), |
429 |
(Host, Host.idhost == PerfDataSource.idhost), |
430 |
).filter(Host.name == host) |
431 |
|
432 |
graphs = graphs.all() |
433 |
return dict(host=host, start=start, duration=duration, |
434 |
presets=self.presets, graphs=graphs)
|
435 |
|
436 |
|
437 |
class SearchHostSchema(schema.Schema): |
438 |
"""Schéma de validation pour la méthode L{getIndicators}."""
|
439 |
allow_extra_fields = True
|
440 |
filter_extra_fields = True
|
441 |
query = validators.UnicodeString(not_empty=True)
|
442 |
|
443 |
@expose('searchhost.html') |
444 |
@validate(
|
445 |
validators = SearchHostSchema(), |
446 |
error_handler = process_form_errors) |
447 |
@paginate('hosts', items_per_page=10) |
448 |
def searchHost(self, query): |
449 |
"""
|
450 |
Affiche les résultats de la recherche par nom d'hôte.
|
451 |
La requête de recherche (C{query}) correspond à un préfixe
|
452 |
qui sera recherché dans le nom d'hôte. Ce préfixe peut
|
453 |
contenir les caractères '*' et '?' qui agissent comme des
|
454 |
"jokers".
|
455 |
|
456 |
@keyword query: Prefixe de recherche sur les noms d'hôtes
|
457 |
@type query: C{unicode}
|
458 |
"""
|
459 |
if not query: |
460 |
redirect("searchHostForm")
|
461 |
return
|
462 |
|
463 |
query = sql_escape_like(query.strip()) |
464 |
user = get_current_user() |
465 |
if user is None: |
466 |
return dict(items=[]) |
467 |
|
468 |
# Récupère les hôtes auxquels l'utilisateur a réellement accès.
|
469 |
hosts = DBSession.query( |
470 |
Host.name, |
471 |
).distinct( |
472 |
).join( |
473 |
(SUPITEM_GROUP_TABLE, SUPITEM_GROUP_TABLE.c.idsupitem == \ |
474 |
Host.idhost), |
475 |
).filter(Host.name.like(query + u'%')
|
476 |
).order_by(Host.name.asc(),) |
477 |
|
478 |
# Les managers ont accès à tout.
|
479 |
# Les autres ont un accès restreint.
|
480 |
if not config.is_manager.is_met(request.environ): |
481 |
supitemgroups = [sig[0] for sig in user.supitemgroups() if sig[1]] |
482 |
hosts = hosts.join( |
483 |
(GroupHierarchy, GroupHierarchy.idchild == \ |
484 |
SUPITEM_GROUP_TABLE.c.idgroup) |
485 |
).filter(GroupHierarchy.idparent.in_(supitemgroups)) |
486 |
|
487 |
return dict(hosts=[h.name for h in hosts]) |
488 |
|
489 |
# VIGILO_EXIG_VIGILO_PERF_0030:Moteur de recherche des graphes
|
490 |
@expose('getopensearch.xml', content_type='text/xml') |
491 |
def getOpenSearch(self): |
492 |
"""
|
493 |
Moteur de recherche des graphes
|
494 |
|
495 |
@return: document
|
496 |
@rtype: document xml
|
497 |
"""
|
498 |
return dict() |
499 |
|
500 |
@expose('json') |
501 |
def hosttree(self, parent_id=None, onlytype="", offset=0, noCache=None): |
502 |
"""
|
503 |
Affiche un étage de l'arbre de
|
504 |
sélection des hôtes et groupes d'hôtes.
|
505 |
|
506 |
@param parent_id: identifiant du groupe d'hôte parent
|
507 |
@type parent_id: C{int} or None
|
508 |
"""
|
509 |
|
510 |
# Si l'identifiant du groupe parent n'est pas
|
511 |
# spécifié, on retourne la liste des groupes racines,
|
512 |
# fournie par la méthode get_root_hosts_groups.
|
513 |
if parent_id is None: |
514 |
return self.get_root_host_groups() |
515 |
|
516 |
# TODO: Utiliser un schéma de validation
|
517 |
parent_id = int(parent_id)
|
518 |
offset = int(offset)
|
519 |
|
520 |
# On vérifie si le groupe parent fait partie des
|
521 |
# groupes auxquel l'utilisateur a accès, et on
|
522 |
# retourne une liste vide dans le cas contraire
|
523 |
is_manager = config.is_manager.is_met(request.environ) |
524 |
if not is_manager: |
525 |
direct_access = False
|
526 |
user = get_current_user() |
527 |
|
528 |
# On calcule la distance de ce groupe par rapport aux groupes
|
529 |
# sur lesquels l'utilisateur a explicitement les permissions.
|
530 |
#
|
531 |
# La distance est définie ainsi :
|
532 |
# 0 : l'utilisateur a des droits explicites sur ce groupe.
|
533 |
# > 0 : l'utilisateur a accès implicitement au groupe.
|
534 |
# < 0 : l'utilisateur n'a pas d'accès (il peut juste parcourir
|
535 |
# ce groupe)
|
536 |
#
|
537 |
# Il faut 2 étapes pour trouver la distance. La 1ère essaye
|
538 |
# de trouver une distance >= 0, la 2ème une distance <= 0.
|
539 |
|
540 |
# Distance positive.
|
541 |
distance = DBSession.query( |
542 |
functions.max(GroupHierarchy.hops) |
543 |
).join( |
544 |
(Group, Group.idgroup == GroupHierarchy.idparent), |
545 |
(DataPermission, |
546 |
DataPermission.idgroup == Group.idgroup), |
547 |
(UserGroup, |
548 |
UserGroup.idgroup == DataPermission.idusergroup), |
549 |
(USER_GROUP_TABLE, USER_GROUP_TABLE.c.idgroup == \ |
550 |
UserGroup.idgroup), |
551 |
).filter(USER_GROUP_TABLE.c.username == user.user_name |
552 |
).filter(Group.grouptype == u'supitemgroup'
|
553 |
).filter(GroupHierarchy.idchild == parent_id |
554 |
).scalar() |
555 |
|
556 |
if distance is None: |
557 |
# Distance négative.
|
558 |
distance = DBSession.query( |
559 |
functions.max(GroupHierarchy.hops) |
560 |
).join( |
561 |
(Group, Group.idgroup == GroupHierarchy.idchild), |
562 |
(DataPermission, |
563 |
DataPermission.idgroup == Group.idgroup), |
564 |
(UserGroup, |
565 |
UserGroup.idgroup == DataPermission.idusergroup), |
566 |
(USER_GROUP_TABLE, USER_GROUP_TABLE.c.idgroup == \ |
567 |
UserGroup.idgroup), |
568 |
).filter(USER_GROUP_TABLE.c.username == user.user_name |
569 |
).filter(Group.grouptype == u'supitemgroup'
|
570 |
).filter(GroupHierarchy.idparent == parent_id |
571 |
).scalar() |
572 |
if distance is not None: |
573 |
distance = -distance |
574 |
|
575 |
if distance is None: |
576 |
# Pas d'accès à ce groupe.
|
577 |
return dict(groups = [], items = []) |
578 |
|
579 |
direct_access = distance >= 0
|
580 |
|
581 |
limit = int(config.get("max_menu_entries", 20)) |
582 |
result = {"groups": [], "items": []} |
583 |
|
584 |
if not onlytype or onlytype == "group": |
585 |
# On récupère la liste des groupes dont
|
586 |
# l'identifiant du parent est passé en paramètre
|
587 |
gh1 = aliased(GroupHierarchy, name='gh1')
|
588 |
gh2 = aliased(GroupHierarchy, name='gh2')
|
589 |
|
590 |
db_groups = DBSession.query( |
591 |
SupItemGroup |
592 |
).options(lazyload('_path_obj')
|
593 |
).distinct( |
594 |
).join( |
595 |
(gh1, gh1.idchild == SupItemGroup.idgroup), |
596 |
).filter(gh1.hops == 1
|
597 |
).filter(gh1.idparent == parent_id |
598 |
).order_by(SupItemGroup.name.asc()) |
599 |
|
600 |
if not is_manager and not direct_access: |
601 |
# On ne doit afficher que les fils du groupe <parent_id>
|
602 |
# tels que l'utilisateur a accès explicitement à l'un
|
603 |
# des fils de l'un de ces groupes.
|
604 |
db_groups = db_groups.join( |
605 |
(gh2, gh2.idparent == gh1.idchild), |
606 |
(DataPermission, |
607 |
DataPermission.idgroup == gh2.idchild), |
608 |
(UserGroup, |
609 |
UserGroup.idgroup == DataPermission.idusergroup), |
610 |
(USER_GROUP_TABLE, |
611 |
USER_GROUP_TABLE.c.idgroup == UserGroup.idgroup), |
612 |
).filter(USER_GROUP_TABLE.c.username == user.user_name) |
613 |
|
614 |
num_children_left = db_groups.count() - offset |
615 |
if offset:
|
616 |
result["continued_from"] = offset
|
617 |
result["continued_type"] = "group" |
618 |
all_groups = db_groups.limit(limit).offset(offset).all() |
619 |
for group in all_groups: |
620 |
result["groups"].append({
|
621 |
'id' : group.idgroup,
|
622 |
'name' : group.name,
|
623 |
'type' : "group", |
624 |
}) |
625 |
if num_children_left > limit:
|
626 |
result["groups"].append({
|
627 |
'name': _("Next %(limit)s") % {"limit": limit}, |
628 |
'offset': offset + limit,
|
629 |
'parent_id': parent_id,
|
630 |
'type': 'continued', |
631 |
'for_type': 'group', |
632 |
}) |
633 |
|
634 |
# On récupère la liste des hôtes appartenant au
|
635 |
# groupe dont l'identifiant est passé en paramètre
|
636 |
if ((not onlytype or onlytype == "item") |
637 |
and (is_manager or direct_access)): |
638 |
db_hosts = DBSession.query( |
639 |
Host.idhost, |
640 |
Host.name, |
641 |
).join( |
642 |
(SUPITEM_GROUP_TABLE, |
643 |
SUPITEM_GROUP_TABLE.c.idsupitem == Host.idhost |
644 |
), |
645 |
).filter(SUPITEM_GROUP_TABLE.c.idgroup == parent_id |
646 |
).order_by(Host.name.asc()) |
647 |
num_children_left = db_hosts.count() - offset |
648 |
if offset:
|
649 |
result["continued_from"] = offset
|
650 |
result["continued_type"] = "item" |
651 |
all_hosts = db_hosts.limit(limit).offset(offset).all() |
652 |
for host in all_hosts: |
653 |
result["items"].append({
|
654 |
'id' : host.idhost,
|
655 |
'name' : host.name,
|
656 |
'type' : "item", |
657 |
}) |
658 |
if num_children_left > limit:
|
659 |
result["items"].append({
|
660 |
'name': _("Next %(limit)s") % {"limit": limit}, |
661 |
'offset': offset + limit,
|
662 |
'parent_id': parent_id,
|
663 |
'type': 'continued', |
664 |
'for_type': 'item', |
665 |
}) |
666 |
|
667 |
return result
|
668 |
|
669 |
@expose('json') |
670 |
def graphtree(self, host_id=None, parent_id=None, offset=0, noCache=None): |
671 |
"""
|
672 |
Affiche un étage de l'arbre de sélection
|
673 |
des graphes et groupes de graphes.
|
674 |
|
675 |
@param parent_id: identifiant du groupe de graphes parent
|
676 |
@type parent_id: C{int} or None
|
677 |
"""
|
678 |
|
679 |
# Si l'identifiant de l'hôte n'est pas spécifié, on
|
680 |
# retourne un dictionnaire contenant deux listes vides
|
681 |
if host_id is None: |
682 |
return dict(groups = [], graphs=[]) |
683 |
|
684 |
# On vérifie les permissions sur l'hôte
|
685 |
# TODO: Utiliser un schéma de validation
|
686 |
host_id = int(host_id)
|
687 |
host = DBSession.query(Host |
688 |
).filter(Host.idhost == host_id |
689 |
).first() |
690 |
if host is None: |
691 |
return dict(groups = [], graphs=[]) |
692 |
user = get_current_user() |
693 |
if not host.is_allowed_for(user): |
694 |
return dict(groups = [], graphs=[]) |
695 |
|
696 |
# On récupère la liste des groupes de graphes associés à l'hôte
|
697 |
host_graph_groups = DBSession.query( |
698 |
GraphGroup |
699 |
).distinct( |
700 |
).join( |
701 |
(GRAPH_GROUP_TABLE, \ |
702 |
GRAPH_GROUP_TABLE.c.idgroup == GraphGroup.idgroup), |
703 |
(Graph, Graph.idgraph == GRAPH_GROUP_TABLE.c.idgraph), |
704 |
(GRAPH_PERFDATASOURCE_TABLE, \ |
705 |
GRAPH_PERFDATASOURCE_TABLE.c.idgraph == Graph.idgraph), |
706 |
(PerfDataSource, PerfDataSource.idperfdatasource == \ |
707 |
GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource), |
708 |
(SUPITEM_GROUP_TABLE, \ |
709 |
SUPITEM_GROUP_TABLE.c.idsupitem == PerfDataSource.idhost), |
710 |
).filter(PerfDataSource.idhost == host_id |
711 |
).order_by(GraphGroup.name.asc() |
712 |
).all() |
713 |
|
714 |
# Si l'identifiant du groupe parent n'est pas spécifié,
|
715 |
# on récupère la liste des groupes de graphes racines.
|
716 |
if parent_id is None: |
717 |
graph_groups = GraphGroup.get_top_groups() |
718 |
|
719 |
# Sinon on récupère la liste des graphes dont le
|
720 |
# groupe passé en paramètre est le parent direct
|
721 |
else:
|
722 |
# TODO: Utiliser un schéma de validation
|
723 |
parent_id = int(parent_id)
|
724 |
graph_groups = DBSession.query( |
725 |
GraphGroup |
726 |
).join( |
727 |
(GroupHierarchy, GroupHierarchy.idchild == \ |
728 |
GraphGroup.idgroup), |
729 |
).filter(GroupHierarchy.hops == 1
|
730 |
).filter(GroupHierarchy.idparent == parent_id |
731 |
).order_by(GraphGroup.name.asc() |
732 |
).all() |
733 |
|
734 |
# On réalise l'intersection des deux listes
|
735 |
groups = [] |
736 |
for gg in graph_groups: |
737 |
if gg in host_graph_groups: |
738 |
groups.append({ |
739 |
'id' : gg.idgroup,
|
740 |
'name' : gg.name,
|
741 |
'type' : "group", |
742 |
}) |
743 |
|
744 |
# On récupère la liste des graphes appartenant au
|
745 |
# groupe dont l'identifiant est passé en paramètre
|
746 |
graphs = [] |
747 |
if parent_id:
|
748 |
db_graphs = DBSession.query( |
749 |
Graph.idgraph, |
750 |
Graph.name, |
751 |
).distinct( |
752 |
).join( |
753 |
(GRAPH_GROUP_TABLE, |
754 |
GRAPH_GROUP_TABLE.c.idgraph == Graph.idgraph), |
755 |
(GRAPH_PERFDATASOURCE_TABLE, |
756 |
GRAPH_PERFDATASOURCE_TABLE.c.idgraph == Graph.idgraph), |
757 |
(PerfDataSource, |
758 |
PerfDataSource.idperfdatasource == \ |
759 |
GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource), |
760 |
).filter(GRAPH_GROUP_TABLE.c.idgroup == parent_id |
761 |
).filter(PerfDataSource.idhost == host_id |
762 |
).order_by(Graph.name.asc()) |
763 |
for graph in db_graphs.all(): |
764 |
graphs.append({ |
765 |
'id' : graph.idgraph,
|
766 |
'name' : graph.name,
|
767 |
'type' : "item", |
768 |
}) |
769 |
|
770 |
return dict(groups=groups, items=graphs) |
771 |
|
772 |
def get_root_host_groups(self): |
773 |
"""
|
774 |
Retourne tous les groupes racines (c'est à dire n'ayant
|
775 |
aucun parent) d'hôtes auquel l'utilisateur a accès.
|
776 |
|
777 |
@return: Un dictionnaire contenant la liste de ces groupes.
|
778 |
@rtype : C{dict} of C{list} of C{dict} of C{mixed}
|
779 |
"""
|
780 |
|
781 |
# On récupère tous les groupes qui ont un parent.
|
782 |
children = DBSession.query( |
783 |
SupItemGroup, |
784 |
).distinct( |
785 |
).join( |
786 |
(GroupHierarchy, GroupHierarchy.idchild == SupItemGroup.idgroup) |
787 |
).filter(GroupHierarchy.hops > 0)
|
788 |
|
789 |
# Ensuite on les exclut de la liste des groupes,
|
790 |
# pour ne garder que ceux qui sont au sommet de
|
791 |
# l'arbre et qui constituent nos "root groups".
|
792 |
root_groups = DBSession.query( |
793 |
SupItemGroup, |
794 |
).except_(children |
795 |
).order_by(SupItemGroup.name) |
796 |
|
797 |
# On filtre ces groupes racines afin de ne
|
798 |
# retourner que ceux auquels l'utilisateur a accès
|
799 |
user = get_current_user() |
800 |
if not config.is_manager.is_met(request.environ): |
801 |
user_groups = [ug[0] for ug in user.supitemgroups()] |
802 |
root_groups = root_groups.filter( |
803 |
SupItemGroup.idgroup.in_(user_groups)) |
804 |
|
805 |
groups = [] |
806 |
for group in root_groups.all(): |
807 |
groups.append({ |
808 |
'id' : group.idgroup,
|
809 |
'name' : group.name,
|
810 |
'type' : "group", |
811 |
}) |
812 |
|
813 |
return dict(groups=groups, items=[]) |
814 |
|
815 |
def getListIndicators(self, host, graph): |
816 |
"""
|
817 |
Liste d indicateurs associes a un graphe
|
818 |
|
819 |
@param graph : graphe
|
820 |
@type graph : C{str}
|
821 |
|
822 |
@return: liste d indicateurs
|
823 |
@rtype : list
|
824 |
"""
|
825 |
|
826 |
indicators = [] |
827 |
if graph is not None: |
828 |
indicators = DBSession.query( |
829 |
PerfDataSource.name, PerfDataSource.label |
830 |
).distinct( |
831 |
).join( |
832 |
(GRAPH_PERFDATASOURCE_TABLE, \ |
833 |
GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource == \ |
834 |
PerfDataSource.idperfdatasource), |
835 |
(Graph, Graph.idgraph == \ |
836 |
GRAPH_PERFDATASOURCE_TABLE.c.idgraph), |
837 |
(Host, Host.idhost == PerfDataSource.idhost), |
838 |
).filter(Graph.name == graph |
839 |
).filter(Host.name == host |
840 |
).all() |
841 |
return indicators
|
842 |
|
843 |
@expose('json') |
844 |
def dbmtime(self): |
845 |
change = Change.by_table_name(u"Graph")
|
846 |
if change is None: |
847 |
return {"mtime": None} |
848 |
mtime = change.last_modified.replace(microsecond=0)
|
849 |
return {"mtime": mtime} |
850 |
|
851 |
@expose('json') |
852 |
def selectHostAndGraph(self, host, graph): |
853 |
# @TODO: vérifier les permissions
|
854 |
ids = DBSession.query( |
855 |
Host.idhost, Graph.idgraph |
856 |
).join( |
857 |
(PerfDataSource, PerfDataSource.idhost == Host.idhost), |
858 |
(GRAPH_PERFDATASOURCE_TABLE, \ |
859 |
GRAPH_PERFDATASOURCE_TABLE.c.idperfdatasource == \ |
860 |
PerfDataSource.idperfdatasource), |
861 |
(Graph, Graph.idgraph == \ |
862 |
GRAPH_PERFDATASOURCE_TABLE.c.idgraph), |
863 |
).filter(Graph.name == unicode(graph)
|
864 |
).filter(Host.name == unicode(host)
|
865 |
).first() |
866 |
|
867 |
return {
|
868 |
'idhost': ids and ids.idhost or None, |
869 |
'idgraph': ids and ids.idgraph or None, |
870 |
} |
871 |
|
872 |
@expose('json') |
873 |
def external_links(self): |
874 |
return dict(links=config['external_links']) |