Refactor exports modules loading

This commit is contained in:
nicolargo 2021-09-04 16:46:20 +02:00
parent 23292675a3
commit a0b5982f1d
26 changed files with 163 additions and 138 deletions

View File

@ -62,6 +62,9 @@ run-client: venv
run-browser: venv
./venv/bin/python -m glances -C ./conf/glances.conf --browser
run-export-influxdb: venv
./venv/bin/python -m glances -C ./conf/glances.conf --export influxdb
show-version: venv
./venv/bin/python -m glances -C ./conf/glances.conf -V

View File

@ -52,8 +52,8 @@ except locale.Error:
print("Warning: Unable to set locale. Expect encoding problems.")
# Check Python version
if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 4):
print('Glances requires at least Python 2.7 or 3.4 to run.')
if sys.version_info < (3, 4):
print('Glances requires at least Python 3.4 to run.')
sys.exit(1)
# Check psutil version

View File

@ -24,7 +24,7 @@ from datetime import datetime
from numbers import Number
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from glances.globals import iteritems
from cassandra.auth import PlainTextAuthProvider

View File

@ -23,7 +23,7 @@ import sys
from datetime import datetime
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
import couchdb
import couchdb.mapping

View File

@ -26,7 +26,7 @@ import time
from glances.globals import PY3, iterkeys, itervalues
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
class Export(GlancesExport):

View File

@ -23,7 +23,7 @@ import sys
from datetime import datetime
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from elasticsearch import Elasticsearch, helpers
from elasticsearch import __version__ as elk_version

View File

@ -23,7 +23,7 @@ import sys
from platform import node
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from influxdb import InfluxDBClient
from influxdb.client import InfluxDBClientError

View File

@ -29,7 +29,7 @@ import errno
from glances.logger import logger
from glances.timer import Timer
from glances.globals import iteritems, time_serie_subsample
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
class Export(GlancesExport):

View File

@ -23,7 +23,7 @@ import sys
from numbers import Number
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from graphitesend import GraphiteClient

View File

@ -23,7 +23,7 @@ import sys
from platform import node
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from influxdb_client import InfluxDBClient, WriteOptions

View File

@ -5,7 +5,7 @@ import json
from glances.globals import PY3, listkeys
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
class Export(GlancesExport):

View File

@ -23,7 +23,7 @@ import sys
from glances.logger import logger
from glances.globals import iteritems
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from kafka import KafkaProducer
import json

View File

@ -24,7 +24,7 @@ import string
import json
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
# Import paho for MQTT
from requests import certs
@ -118,14 +118,14 @@ class Export(GlancesExport):
output_value = dict()
for key in sensor_values:
split_key = key.split('.')
# Add the parent keys if they don't exist
current_level = output_value
for depth in range(len(split_key) - 1):
if split_key[depth] not in current_level:
current_level[split_key[depth]] = dict()
current_level = current_level[split_key[depth]]
# Add the value
current_level[split_key[len(split_key) - 1]] = sensor_values[key]
@ -133,4 +133,4 @@ class Export(GlancesExport):
self.client.publish(topic, json_value)
except Exception as e:
logger.error("Can not export stats to MQTT server (%s)" % e)

View File

@ -23,7 +23,7 @@ import sys
from numbers import Number
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
import potsdb

View File

@ -23,7 +23,7 @@ import sys
from numbers import Number
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from glances.globals import iteritems, listkeys
from prometheus_client import start_http_server, Gauge

View File

@ -25,7 +25,7 @@ import sys
from numbers import Number
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
# Import pika for RabbitMQ
import pika

View File

@ -23,7 +23,7 @@ import sys
from glances.globals import listkeys
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from requests import post

View File

@ -24,7 +24,7 @@ import sys
from numbers import Number
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
# Import bernhard for Riemann
import bernhard

View File

@ -23,7 +23,7 @@ import sys
from numbers import Number
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
from statsd import StatsClient

View File

@ -24,7 +24,7 @@ import json
from glances.globals import b
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
from glances.exports.export import GlancesExport
import zmq
from zmq.utils.strtypes import asbytes

View File

@ -29,11 +29,6 @@ from glances.plugins.plugin import GlancesPlugin
import psutil
# Fields description
# description: human readable description
# short_name: shortname to use un UI
# unit: unit type
# rate: is it a rate ? If yes, // by time_since_update when displayed,
# min_symbol: Auto unit should be used if value > than 1 'X' (K, M, G)...
fields_description = {
'total': {'description': 'Sum of all CPU percentages (except idle).',
'unit': 'percent'},
@ -65,21 +60,12 @@ CPU while the hypervisor is servicing another virtual processor.',
second. A context switch is a procedure that a computer\'s CPU (central \
processing unit) follows to change from one task (or process) to \
another while ensuring that the tasks do not conflict.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'ctx_sw'},
'unit': 'percent'},
'interrupts': {'description': 'number of interrupts per second.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'inter'},
'unit': 'percent'},
'soft_interrupts': {'description': 'number of software interrupts per second. Always set to \
0 on Windows and SunOS.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'sw_int'},
'unit': 'percent'},
'cpucore': {'description': 'Total number of CPU core.',
'unit': 'number'},
'time_since_update': {'description': 'Number of seconds since last update.',
@ -281,12 +267,12 @@ class Plugin(GlancesPlugin):
if not self.stats or self.args.percpu or self.is_disable():
return ret
# Build the string message
# If user stat is not here, display only idle / total CPU usage (for
# exemple on Windows OS)
idle_tag = 'user' not in self.stats
# First line
# Total + (idle) + ctx_sw
# Header
msg = '{}'.format('CPU')
ret.append(self.curse_add_line(msg, "TITLE"))
trend_user = self.get_trend('user')
@ -302,47 +288,103 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(
msg, self.get_views(key='total', option='decoration')))
# Idle CPU
if not idle_tag:
ret.extend(self.curse_add_stat('idle', width=15, header=' '))
if 'idle' in self.stats and not idle_tag:
msg = ' {:8}'.format('idle:')
ret.append(self.curse_add_line(msg))
msg = '{:5.1f}%'.format(self.stats['idle'])
ret.append(self.curse_add_line(msg))
# ctx_switches
ret.extend(self.curse_add_stat('ctx_switches', width=15, header=' '))
if 'ctx_switches' in self.stats:
msg = ' {:8}'.format('ctx_sw:')
ret.append(self.curse_add_line(msg, optional=self.get_views(key='ctx_switches', option='optional')))
msg = '{:>5}'.format(self.auto_unit(int(self.stats['ctx_switches'] // self.stats['time_since_update']),
min_symbol='K'))
ret.append(self.curse_add_line(
msg, self.get_views(key='ctx_switches', option='decoration'),
optional=self.get_views(key='ctx_switches', option='optional')))
# Second line
# user|idle + irq + interrupts
# New line
ret.append(self.curse_new_line())
# User CPU
if not idle_tag:
ret.extend(self.curse_add_stat('user', width=15))
if 'user' in self.stats:
msg = '{:8}'.format('user:')
ret.append(self.curse_add_line(msg))
msg = '{:5.1f}%'.format(self.stats['user'])
ret.append(self.curse_add_line(
msg, self.get_views(key='user', option='decoration')))
elif 'idle' in self.stats:
ret.extend(self.curse_add_stat('idle', width=15))
msg = '{:8}'.format('idle:')
ret.append(self.curse_add_line(msg))
msg = '{:5.1f}%'.format(self.stats['idle'])
ret.append(self.curse_add_line(msg))
# IRQ CPU
ret.extend(self.curse_add_stat('irq', width=15, header=' '))
if 'irq' in self.stats:
msg = ' {:8}'.format('irq:')
ret.append(self.curse_add_line(msg, optional=self.get_views(key='irq', option='optional')))
msg = '{:5.1f}%'.format(self.stats['irq'])
ret.append(self.curse_add_line(msg, optional=self.get_views(key='irq', option='optional')))
# interrupts
ret.extend(self.curse_add_stat('interrupts', width=15, header=' '))
if 'interrupts' in self.stats:
msg = ' {:8}'.format('inter:')
ret.append(self.curse_add_line(msg, optional=self.get_views(key='interrupts', option='optional')))
msg = '{:>5}'.format(int(self.stats['interrupts'] // self.stats['time_since_update']))
ret.append(self.curse_add_line(msg, optional=self.get_views(key='interrupts', option='optional')))
# Third line
# system|core + nice + sw_int
# New line
ret.append(self.curse_new_line())
# System CPU
if not idle_tag:
ret.extend(self.curse_add_stat('system', width=15))
if 'system' in self.stats and not idle_tag:
msg = '{:8}'.format('system:')
ret.append(self.curse_add_line(msg))
msg = '{:5.1f}%'.format(self.stats['system'])
ret.append(self.curse_add_line(
msg, self.get_views(key='system', option='decoration')))
else:
ret.extend(self.curse_add_stat('core', width=15))
msg = '{:8}'.format('core:')
ret.append(self.curse_add_line(msg))
msg = '{:>6}'.format(self.stats['nb_log_core'])
ret.append(self.curse_add_line(msg))
# Nice CPU
ret.extend(self.curse_add_stat('nice', width=15, header=' '))
if 'nice' in self.stats:
msg = ' {:8}'.format('nice:')
ret.append(self.curse_add_line(
msg, optional=self.get_views(key='nice', option='optional')))
msg = '{:5.1f}%'.format(self.stats['nice'])
ret.append(self.curse_add_line(
msg, optional=self.get_views(key='nice', option='optional')))
# soft_interrupts
ret.extend(self.curse_add_stat('soft_interrupts', width=15, header=' '))
if 'soft_interrupts' in self.stats:
msg = ' {:8}'.format('sw_int:')
ret.append(self.curse_add_line(msg, optional=self.get_views(key='soft_interrupts', option='optional')))
msg = '{:>5}'.format(int(self.stats['soft_interrupts'] // self.stats['time_since_update']))
ret.append(self.curse_add_line(msg, optional=self.get_views(key='soft_interrupts', option='optional')))
# Fourth line
# iowat + steal + syscalls
# New line
ret.append(self.curse_new_line())
# IOWait CPU
ret.extend(self.curse_add_stat('iowait', width=15))
if 'iowait' in self.stats:
msg = '{:8}'.format('iowait:')
ret.append(self.curse_add_line(
msg, optional=self.get_views(key='iowait', option='optional')))
msg = '{:5.1f}%'.format(self.stats['iowait'])
ret.append(self.curse_add_line(
msg, self.get_views(key='iowait', option='decoration'),
optional=self.get_views(key='iowait', option='optional')))
# Steal CPU usage
ret.extend(self.curse_add_stat('steal', width=15, header=' '))
if 'steal' in self.stats:
msg = ' {:8}'.format('steal:')
ret.append(self.curse_add_line(msg, optional=self.get_views(key='steal', option='optional')))
msg = '{:5.1f}%'.format(self.stats['steal'])
ret.append(self.curse_add_line(
msg, self.get_views(key='steal', option='decoration'),
optional=self.get_views(key='steal', option='optional')))
# syscalls
# syscalls: number of system calls since boot. Always set to 0 on Linux. (do not display)
if not LINUX:
ret.extend(self.curse_add_stat('syscalls', width=15, header=' '))
if 'syscalls' in self.stats and not LINUX:
msg = ' {:8}'.format('syscal:')
ret.append(self.curse_add_line(msg, optional=self.get_views(key='syscalls', option='optional')))
msg = '{:>5}'.format(int(self.stats['syscalls'] // self.stats['time_since_update']))
ret.append(self.curse_add_line(msg, optional=self.get_views(key='syscalls', option='optional')))
# Return the message with decoration
return ret

View File

@ -103,39 +103,7 @@ class GlancesStats(object):
# Restoring system path
sys.path = sys_path
# TODO: To be removed and replace by the _load_plugin_v4
def _load_plugin(self, plugin_script, args=None, config=None):
"""Load the plugin (script), init it and add to the _plugin dict."""
# The key is the plugin name
# for example, the file glances_xxx.py
# generate self._plugins_list["xxx"] = ...
name = plugin_script[len(self.header):-3].lower()
# Loaf the plugin class
try:
# Import the plugin
plugin = import_module(plugin_script[:-3])
# Init and add the plugin to the dictionary
self._plugins[name] = plugin.Plugin(args=args, config=config)
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(name, e))
logger.error(traceback.format_exc())
# Disable the plugin
if args is not None:
setattr(args,
'disable_' + name,
False)
else:
# Set the disable_<name> to False by default
if args is not None:
setattr(args,
'disable_' + name,
getattr(args, 'disable_' + name, False))
# TODO: To be rename to _load_plugin
def _load_plugin_v4(self, plugin_path, args=None, config=None):
def _load_plugin(self, plugin_path, args=None, config=None):
"""Load the plugin, init it and add to the _plugin dict."""
# The key is the plugin name = plugin_path
# for example, the path ./glances/plugins/xxx
@ -173,24 +141,14 @@ class GlancesStats(object):
def load_plugins(self, args=None):
"""Load all plugins in the 'plugins' folder."""
start_duration = Counter()
for item in os.listdir(plugins_path):
if (item.startswith(self.header) and
item.endswith(".py") and
item != (self.header + "plugin.py")):
if os.path.isdir(os.path.join(plugins_path, item)) and \
not item.startswith('__'):
# Load the plugin
start_duration.reset()
self._load_plugin(os.path.basename(item),
args=args, config=self.config)
logger.debug("Plugin {} started in {} seconds".format(item,
start_duration.get()))
for item in os.listdir(plugins_path):
if os.path.isdir(os.path.join(plugins_path, item)) and \
not item.startswith('__') and \
item != "plugin":
# Load the plugin
start_duration.reset()
self._load_plugin_v4(os.path.basename(item),
args=args, config=self.config)
logger.debug("Plugin {} started in {} seconds".format(item,
start_duration.get()))
@ -198,36 +156,40 @@ class GlancesStats(object):
logger.debug("Active plugins list: {}".format(self.getPluginsList()))
def load_exports(self, args=None):
"""Load all export modules in the 'exports' folder."""
if args is None:
return False
header = "glances_"
# Build the export module available list
args_var = vars(locals()['args'])
"""Load all exporters in the 'exports' folder."""
start_duration = Counter()
if args is None: return False
for item in os.listdir(exports_path):
export_name = os.path.basename(item)[len(header):-3].lower()
if (item.startswith(header) and
item.endswith(".py") and
item != (header + "export.py") and
item != (header + "history.py")):
self._exports_all[export_name] = os.path.basename(item)[:-3]
if os.path.isdir(os.path.join(exports_path, item)) and \
not item.startswith('__'):
# Load the exporter
start_duration.reset()
if item.startswith('glances_'):
# Avoid circular loop when Glances exporter uses lib with same name
# Example: influxdb should be named to glances_influxdb
exporter_name = os.path.basename(item).split('glances_')[1]
else:
exporter_name = os.path.basename(item)
# Set the disable_<name> to False by default
setattr(self.args,
'export_' + export_name,
getattr(self.args, 'export_' + export_name, False))
# Aim is to check if the export module should be loaded
for export_name in self._exports_all:
if getattr(self.args, 'export_' + export_name, False):
# Import the export module
export_module = __import__(self._exports_all[export_name])
# Add the export to the dictionary
# The key is the module name
# for example, the file glances_xxx.py
# generate self._exports_list["xxx"] = ...
self._exports[export_name] = export_module.Export(args=args,
config=self.config)
self._exports_all[export_name] = self._exports[export_name]
'export_' + exporter_name,
getattr(self.args, 'export_' + exporter_name, False))
# We should import the module
if getattr(self.args, 'export_' + exporter_name, False):
# Import the export module
export_module = import_module(item)
# Add the exporter instance to the active exporters dictionary
self._exports[exporter_name] = export_module.Export(args=args,
config=self.config)
# Add the exporter instance to the available exporters dictionary
self._exports_all[exporter_name] = self._exports[exporter_name]
else:
# Add the exporter name to the available exporters dictionary
self._exports_all[exporter_name] = exporter_name
logger.debug("Exporter {} started in {} seconds".format(exporter_name,
start_duration.get()))
# Log plugins list
logger.debug("Active exports modules list: {}".format(self.getExportsList()))

View File

@ -26,6 +26,12 @@ import subprocess
import time
import numbers
import unittest
import sys
# Check Python version
if sys.version_info < (3, 4):
print('Glances requires at least Python 3.4 to run.')
sys.exit(1)
from glances import __version__
from glances.globals import text_type

View File

@ -26,6 +26,12 @@ import shlex
import subprocess
import time
import unittest
import sys
# Check Python version
if sys.version_info < (3, 4):
print('Glances requires at least Python 3.4 to run.')
sys.exit(1)
from glances import __version__
from glances.globals import ServerProxy

View File

@ -22,6 +22,12 @@
import time
import unittest
import sys
# Check Python version
if sys.version_info < (3, 4):
print('Glances requires at least Python 3.4 to run.')
sys.exit(1)
from glances.main import GlancesMain
from glances.stats import GlancesStats