From f4c35ed56d4b3bde4f7d0bc221dc1dde61f012fe Mon Sep 17 00:00:00 2001 From: Christoph Zimmermann Date: Thu, 26 Oct 2023 14:43:58 +0200 Subject: [PATCH] Added loading of additional plugins feature --- glances/main.py | 1 + glances/plugins/plugin/model.py | 3 ++- glances/stats.py | 43 +++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/glances/main.py b/glances/main.py index aa7c58d6..0220b3b7 100644 --- a/glances/main.py +++ b/glances/main.py @@ -111,6 +111,7 @@ Examples of use: parser.add_argument('-V', '--version', action='version', version=version) parser.add_argument('-d', '--debug', action='store_true', default=False, dest='debug', help='enable debug mode') parser.add_argument('-C', '--config', dest='conf_file', help='path to the configuration file') + parser.add_argument('-P', '--plugins', dest='plugin_path', help='path to additional plugin directory') # Disable plugin parser.add_argument( '--modules-list', diff --git a/glances/plugins/plugin/model.py b/glances/plugins/plugin/model.py index 3c2eb4b4..d36ddbfb 100644 --- a/glances/plugins/plugin/model.py +++ b/glances/plugins/plugin/model.py @@ -70,7 +70,8 @@ class GlancesPluginModel(object): :stats_init_value: Default value for a stats item """ # Build the plugin name - self.plugin_name = self.__class__.__module__.split('.')[2] + # Get second-last entry as the last one will always be 'model' + self.plugin_name = self.__class__.__module__.split('.')[-2] if self.plugin_name.startswith('glances_'): self.plugin_name = self.plugin_name.split('glances_')[1] logger.debug("Init {} plugin".format(self.plugin_name)) diff --git a/glances/stats.py b/glances/stats.py index 6b67974a..ec960e2c 100644 --- a/glances/stats.py +++ b/glances/stats.py @@ -15,6 +15,7 @@ import sys import threading import traceback from importlib import import_module +import pkgutil from glances.logger import logger from glances.globals import exports_path, plugins_path, sys_path @@ -82,6 +83,9 @@ class GlancesStats(object): # Load the plugins self.load_plugins(args=args) + # Load addititional plugins + self.load_additional_plugins(args=args, config=self.config) + # Init the export modules dict # Active exporters dictionary self._exports = collections.defaultdict(dict) @@ -141,7 +145,46 @@ class GlancesStats(object): # Log plugins list logger.debug("Active plugins list: {}".format(self.getPluginsList())) + + def load_additional_plugins(self, args=None, config=None): + """ Load additional plugins if defined """ + def get_addl_plugins(self, plugin_path): + """ Get list of additonal plugins """ + _plugin_list = [] + for plugin in pkgutil.walk_packages([plugin_path]): + # Make sure we only include top-level packages in that directory + if plugin.ispkg and not plugin.name.startswith('__') and plugin.module_finder.path == plugin_path: + _plugin_list.append(plugin.name) + return _plugin_list + + # Skip section check as implied by has_option + if config and config.parser.has_option('global', 'plugin_dir'): + path = config.parser['global']['plugin_dir'] + # Get list before starting the counter + _sys_path = sys.path + start_duration = Counter() + # Ensure that plugins can be found in plugin_dir + sys.path.insert(1, path) + for plugin in get_addl_plugins(self, path): + start_duration.reset() + try: + _mod_loaded = import_module(plugin+'.model') + self._plugins[plugin] = _mod_loaded.PluginModel(args=args, config=config) + logger.debug("Plugin {} started in {} seconds".format(plugin, start_duration.get())) + except Exception as e: + # If a plugin can not be loaded, display a critical message + # on the console but do not crash + logger.critical("Error while initializing the {} plugin ({})".format(plugin, e)) + logger.error(traceback.format_exc()) + # An error occurred, disable the plugin + if args: + setattr(args, 'disable_' + plugin, False) + + sys.path = _sys_path + # Log plugins list + logger.debug("Active additional plugins list: {}".format(self.getPluginsList())) + def load_exports(self, args=None): """Load all exporters in the 'exports' folder.""" start_duration = Counter()