Merge branch 'issue3033' into develop

This commit is contained in:
nicolargo 2025-10-19 17:19:53 +02:00
commit 1114a9ab87
5 changed files with 103 additions and 17 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -21,6 +21,7 @@ from glances.events_list import glances_events
from glances.globals import json_dumps
from glances.logger import logger
from glances.password import GlancesPassword
from glances.plugins.plugin.dag import get_plugin_dependencies
from glances.processes import glances_processes
from glances.servers_list import GlancesServersList
from glances.servers_list_dynamic import GlancesAutoDiscoverClient
@ -183,10 +184,10 @@ class GlancesRestfulApi:
self.url_prefix = self.url_prefix.rstrip('/')
logger.debug(f'URL prefix: {self.url_prefix}')
def __update_stats(self):
def __update_stats(self, plugins_list_to_update=None):
# Never update more than 1 time per cached_time
if self.timer.finished():
self.stats.update()
self.stats.update(plugins_list_to_update=plugins_list_to_update)
self.timer = Timer(self.args.cached_time)
def __update_servers_list(self):
@ -436,7 +437,8 @@ class GlancesRestfulApi:
HTTP/1.1 404 Not Found
"""
# Update the stat
self.__update_stats()
# TODO: Why ??? Try to comment it
# self.__update_stats()
try:
plist = self.plugins_list
@ -528,7 +530,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat ID
@ -559,7 +561,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat ID
@ -585,7 +587,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat ID
@ -645,7 +647,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat views
@ -670,7 +672,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat views
@ -695,7 +697,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat views
@ -719,7 +721,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat views
@ -744,7 +746,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value of the stat history
@ -803,7 +805,7 @@ class GlancesRestfulApi:
self._check_if_plugin_available(plugin)
# Update the stat
self.__update_stats()
self.__update_stats(get_plugin_dependencies(plugin))
try:
# Get the RAW value
@ -975,3 +977,4 @@ class GlancesRestfulApi:
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Unknown PID process {pid}")
return GlancesJSONResponse(process_stats)
return GlancesJSONResponse(process_stats)

View File

@ -0,0 +1,68 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
# Glances DAG (direct acyclic graph) for plugins dependencies.
# It allows to define DAG dependencies between plugins
# For the moment, it will be used only for Restful API interface
_plugins_graph = {
'*': ['alert'], # All plugins depend on alert plugin
'cpu': ['core'],
'load': ['core'],
'processlist': ['core', 'processcount'],
'programlist': ['processcount'],
'quicklook': ['fs', 'load'],
'vms': ['processcount'],
}
def get_plugin_dependencies(plugin_name, _graph=_plugins_graph):
"""Return all transitive dependencies for a given plugin (including global ones)."""
seen = set()
def _resolve(plugin):
if plugin in seen:
return
seen.add(plugin)
# Get direct dependencies of this plugin
deps = _graph.get(plugin, [])
for dep in deps:
_resolve(dep)
# Resolve dependencies for this plugin
_resolve(plugin_name)
# Add global ("*") dependencies
for dep in _graph.get('*', []):
_resolve(dep)
# Remove the plugin itself if present
seen.discard(plugin_name)
# Preserve order of discovery (optional, for deterministic results)
result = []
added = set()
for dep in _graph.get(plugin_name, []) + _graph.get('*', []):
for d in _dfs_order(dep, _graph, set()):
if d not in added and d != plugin_name:
result.append(d)
added.add(d)
return [plugin_name] + result
def _dfs_order(plugin, graph, seen):
"""Helper to preserve depth-first order."""
if plugin in seen:
return []
seen.add(plugin)
order = []
for dep in graph.get(plugin, []):
order.extend(_dfs_order(dep, graph, seen))
order.append(plugin)
return order

View File

@ -275,13 +275,15 @@ please rename it to "{plugin_path.capitalize()}Plugin"'
self._plugins[p].update_views()
self._plugins[p].update_stats_history()
def update(self):
"""Wrapper method to update all stats.
Only called by standalone and server modes
def update(self, plugins_list_to_update=None):
"""Wrapper method to update stats.
If plugins_list_to_update is provided (list), only update the given plugins.
"""
if plugins_list_to_update is None:
plugins_list_to_update = self.getPluginsList(enable=True)
# Start update of all enable plugins
for p in self.getPluginsList(enable=True):
for p in plugins_list_to_update:
self.update_plugin(p)
def export(self, input_stats=None):
@ -419,3 +421,4 @@ please rename it to "{plugin_path.capitalize()}Plugin"'
# Close plugins
for p in self._plugins:
self._plugins[p].exit()
self._plugins[p].exit()

View File

@ -30,6 +30,7 @@ from glances.globals import LINUX, WINDOWS, auto_unit, pretty_date, string_value
from glances.main import GlancesMain
from glances.outputs.glances_bars import Bar
from glances.plugins.fs.zfs import zfs_enable, zfs_stats
from glances.plugins.plugin.dag import get_plugin_dependencies
from glances.plugins.plugin.model import GlancesPluginModel
from glances.stats import GlancesStats
from glances.thresholds import (
@ -519,6 +520,17 @@ class TestGlances(unittest.TestCase):
self.assertEqual(pretty_date(datetime(2023, 1, 1, 0, 0), datetime(2024, 1, 1, 12, 0)), 'an year')
self.assertEqual(pretty_date(datetime(2020, 1, 1, 0, 0), datetime(2024, 1, 1, 12, 0)), '4 years')
def test_022_plugin_dag(self):
"""Test Plugin DAG"""
print('INFO: [TEST_022] Plugins DAG')
self.assertEqual(get_plugin_dependencies('amps'), ['amps', 'alert'])
self.assertEqual(get_plugin_dependencies('cpu'), ['cpu', 'core', 'alert'])
self.assertEqual(get_plugin_dependencies('load'), ['load', 'core', 'alert'])
self.assertEqual(get_plugin_dependencies('processlist'), ['processlist', 'core', 'processcount', 'alert'])
self.assertEqual(get_plugin_dependencies('programlist'), ['programlist', 'processcount', 'alert'])
self.assertEqual(get_plugin_dependencies('quicklook'), ['quicklook', 'fs', 'core', 'load', 'alert'])
self.assertEqual(get_plugin_dependencies('vms'), ['vms', 'processcount', 'alert'])
def test_093_auto_unit(self):
"""Test auto_unit classe"""
print('INFO: [TEST_093] Auto unit')