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