mirror of
https://github.com/nicolargo/glances.git
synced 2025-01-03 07:03:40 +03:00
Stats update is now threaded / Sensors are sorted by label/Alias in the UI
This commit is contained in:
parent
8608e97a9f
commit
39be7f554b
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -11,7 +11,8 @@ env:
|
||||
# Alpine image platform: https://hub.docker.com/_/alpine
|
||||
DOCKER_PLATFORMS: linux/amd64,linux/arm64/v8,linux/arm/v6,linux/arm/v7
|
||||
# Ubuntu image platforms list: https://hub.docker.com/_/ubuntu
|
||||
DOCKER_PLATFORMS_UBUNTU: linux/amd64,linux/arm64/v8,linux/arm/v7
|
||||
# linux/arm/v7 do not work (Cargo/Rust not available)
|
||||
DOCKER_PLATFORMS_UBUNTU: linux/amd64,linux/arm64/v8
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
@ -14,8 +14,8 @@ I am your father...
|
||||
"""
|
||||
|
||||
from glances.globals import json_dumps
|
||||
|
||||
from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys
|
||||
from glances.timer import Counter
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
@ -58,6 +58,22 @@ class GlancesExport(object):
|
||||
# Save last export list
|
||||
self._last_exported_list = None
|
||||
|
||||
def _log_result_decorator(fct):
|
||||
"""Log (DEBUG) the result of the function fct."""
|
||||
|
||||
def wrapper(*args, **kw):
|
||||
counter = Counter()
|
||||
ret = fct(*args, **kw)
|
||||
duration = counter.get()
|
||||
logger.debug(
|
||||
"{} {} {} return {} in {} seconds".format(
|
||||
args[0].__class__.__name__, args[0].__class__.__module__, fct.__name__, ret, duration
|
||||
)
|
||||
)
|
||||
return ret
|
||||
|
||||
return wrapper
|
||||
|
||||
def exit(self):
|
||||
"""Close the export module."""
|
||||
logger.debug("Finalise export interface %s" % self.export_name)
|
||||
|
@ -402,7 +402,7 @@ class GlancesPluginModel(object):
|
||||
|
||||
def get_stats(self):
|
||||
"""Return the stats object in JSON format."""
|
||||
return json_dumps(self.stats)
|
||||
return json_dumps(self.get_raw())
|
||||
|
||||
def get_json(self):
|
||||
"""Return the stats object in JSON format."""
|
||||
@ -413,14 +413,14 @@ class GlancesPluginModel(object):
|
||||
|
||||
Stats should be a list of dict (processlist, network...)
|
||||
"""
|
||||
return dictlist(self.stats, item)
|
||||
return dictlist(self.get_raw(), item)
|
||||
|
||||
def get_stats_item(self, item):
|
||||
"""Return the stats object for a specific item in JSON format.
|
||||
|
||||
Stats should be a list of dict (processlist, network...)
|
||||
"""
|
||||
return json_dumps_dictlist(self.stats, item)
|
||||
return json_dumps_dictlist(self.get_raw(), item)
|
||||
|
||||
def get_raw_stats_value(self, item, value):
|
||||
"""Return the stats object for a specific item=value.
|
||||
@ -428,13 +428,13 @@ class GlancesPluginModel(object):
|
||||
Return None if the item=value does not exist
|
||||
Return None if the item is not a list of dict
|
||||
"""
|
||||
if not isinstance(self.stats, list):
|
||||
if not isinstance(self.get_raw(), list):
|
||||
return None
|
||||
else:
|
||||
if (not isinstance(value, int) and not isinstance(value, float)) and value.isdigit():
|
||||
value = int(value)
|
||||
try:
|
||||
return {value: [i for i in self.stats if i[item] == value]}
|
||||
return {value: [i for i in self.get_raw() if i[item] == value]}
|
||||
except (KeyError, ValueError) as e:
|
||||
logger.error("Cannot get item({})=value({}) ({})".format(item, value, e))
|
||||
return None
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
import psutil
|
||||
import warnings
|
||||
import threading
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import iteritems, to_fahrenheit
|
||||
@ -26,6 +27,12 @@ SENSOR_TEMP_UNIT = 'C'
|
||||
SENSOR_FAN_TYPE = 'fan_speed'
|
||||
SENSOR_FAN_UNIT = 'R'
|
||||
|
||||
SENSOR_HDDTEMP_TYPE = 'temperature_hdd'
|
||||
SENSOR_HDDTEMP_UNIT = 'C'
|
||||
|
||||
SENSORS_BATTERY_TYPE = 'battery'
|
||||
SENSORS_BATTERY_UNIT = '%'
|
||||
|
||||
# Define the default refresh multiplicator
|
||||
# Default value is 3 * Glances refresh time
|
||||
# Can be overwritten by the refresh option in the sensors section of the glances.conf file
|
||||
@ -66,8 +73,8 @@ class PluginModel(GlancesPluginModel):
|
||||
"""Glances sensors plugin.
|
||||
|
||||
The stats list includes both sensors and hard disks stats, if any.
|
||||
The sensors are already grouped by chip type and then sorted by name.
|
||||
The hard disks are already sorted by name.
|
||||
The sensors are already grouped by chip type and then sorted by label.
|
||||
The hard disks are already sorted by label.
|
||||
"""
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
@ -108,6 +115,55 @@ class PluginModel(GlancesPluginModel):
|
||||
"""Return the key of the list."""
|
||||
return 'label'
|
||||
|
||||
def __get_temperature(self, stats, index):
|
||||
try:
|
||||
temperature = self.__set_type(self.glances_grab_sensors.get(SENSOR_TEMP_TYPE), SENSOR_TEMP_TYPE)
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab sensors temperatures (%s)" % e)
|
||||
else:
|
||||
stats[index] = self.__transform_sensors(temperature)
|
||||
|
||||
def __get_fan_speed(self, stats, index):
|
||||
try:
|
||||
fan_speed = self.__set_type(self.glances_grab_sensors.get(SENSOR_FAN_TYPE), SENSOR_FAN_TYPE)
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab FAN speed (%s)" % e)
|
||||
else:
|
||||
stats[index] = self.__transform_sensors(fan_speed)
|
||||
|
||||
def __get_hddtemp(self, stats, index):
|
||||
try:
|
||||
hddtemp = self.__set_type(self.hddtemp_plugin.update(), SENSOR_HDDTEMP_TYPE)
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab HDD temperature (%s)" % e)
|
||||
else:
|
||||
stats[index] = self.__transform_sensors(hddtemp)
|
||||
|
||||
def __get_bat_percent(self, stats, index):
|
||||
try:
|
||||
bat_percent = self.__set_type(self.batpercent_plugin.update(), SENSORS_BATTERY_TYPE)
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab battery percent (%s)" % e)
|
||||
else:
|
||||
stats[index] = self.__transform_sensors(bat_percent)
|
||||
|
||||
def __transform_sensors(self, threads_stats):
|
||||
"""Hide, alias and sort the result"""
|
||||
stats_transformed = []
|
||||
for stat in threads_stats:
|
||||
# Hide sensors configured in the hide ou show configuration key
|
||||
if not self.is_display(stat["label"].lower()):
|
||||
continue
|
||||
# Set alias for sensors
|
||||
stat["label"] = self.__get_alias(stat)
|
||||
# Add the stat to the stats_transformed list
|
||||
stats_transformed.append(stat)
|
||||
# Remove duplicates thanks to https://stackoverflow.com/a/9427216/1919431
|
||||
stats_transformed = [dict(t) for t in {tuple(d.items()) for d in stats_transformed}]
|
||||
# Sort by label
|
||||
stats_transformed = sorted(stats_transformed, key=lambda d: d['label'])
|
||||
return stats_transformed
|
||||
|
||||
@GlancesPluginModel._check_decorator
|
||||
@GlancesPluginModel._log_result_decorator
|
||||
def update(self):
|
||||
@ -116,60 +172,38 @@ class PluginModel(GlancesPluginModel):
|
||||
stats = self.get_init_value()
|
||||
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the dedicated lib
|
||||
stats = []
|
||||
# Get the temperature
|
||||
try:
|
||||
temperature = self.__set_type(self.glances_grab_sensors.get(SENSOR_TEMP_TYPE), SENSOR_TEMP_TYPE)
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab sensors temperatures (%s)" % e)
|
||||
else:
|
||||
# Append temperature
|
||||
stats.extend(temperature)
|
||||
# Get the FAN speed
|
||||
try:
|
||||
fan_speed = self.__set_type(self.glances_grab_sensors.get(SENSOR_FAN_TYPE), SENSOR_FAN_TYPE)
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab FAN speed (%s)" % e)
|
||||
else:
|
||||
# Append FAN speed
|
||||
stats.extend(fan_speed)
|
||||
# Update HDDtemp stats
|
||||
try:
|
||||
hddtemp = self.__set_type(self.hddtemp_plugin.update(), 'temperature_hdd')
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab HDD temperature (%s)" % e)
|
||||
else:
|
||||
# Append HDD temperature
|
||||
stats.extend(hddtemp)
|
||||
# Update batteries stats
|
||||
try:
|
||||
bat_percent = self.__set_type(self.batpercent_plugin.update(), 'battery')
|
||||
except Exception as e:
|
||||
logger.error("Cannot grab battery percent (%s)" % e)
|
||||
else:
|
||||
# Append Batteries %
|
||||
stats.extend(bat_percent)
|
||||
|
||||
threads_stats = [None] * 4
|
||||
threads = [
|
||||
threading.Thread(name=SENSOR_TEMP_TYPE,
|
||||
target=self.__get_temperature,
|
||||
args=(threads_stats, 0)),
|
||||
threading.Thread(name=SENSOR_FAN_TYPE,
|
||||
target=self.__get_fan_speed,
|
||||
args=(threads_stats, 1)),
|
||||
threading.Thread(name=SENSOR_HDDTEMP_TYPE,
|
||||
target=self.__get_hddtemp,
|
||||
args=(threads_stats, 2)),
|
||||
threading.Thread(name=SENSORS_BATTERY_TYPE,
|
||||
target=self.__get_bat_percent,
|
||||
args=(threads_stats, 3))
|
||||
]
|
||||
# Start threads in //
|
||||
for t in threads:
|
||||
t.start()
|
||||
# Wait threads are finished
|
||||
for t in threads:
|
||||
t.join()
|
||||
# Merge the results
|
||||
for s in threads_stats:
|
||||
stats.extend(s)
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
# No standard:
|
||||
# http://www.net-snmp.org/wiki/index.php/Net-SNMP_and_lm-sensors_on_Ubuntu_10.04
|
||||
pass
|
||||
|
||||
# Global change on stats
|
||||
stats_transformed = []
|
||||
for stat in stats:
|
||||
# Hide sensors configured in the hide ou show configuration key
|
||||
if not self.is_display(stat["label"].lower()):
|
||||
continue
|
||||
# Set alias for sensors
|
||||
stat["label"] = self.__get_alias(stat)
|
||||
# Add the stat to the stats_transformed list
|
||||
stats_transformed.append(stat)
|
||||
|
||||
# Update the stats
|
||||
self.stats = stats_transformed
|
||||
self.stats = stats
|
||||
|
||||
return self.stats
|
||||
|
||||
@ -270,7 +304,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# Stats
|
||||
for i in self.stats:
|
||||
# Do not display anything if no battery are detected
|
||||
if i['type'] == 'battery' and i['value'] == []:
|
||||
if i['type'] == SENSORS_BATTERY_TYPE and i['value'] == []:
|
||||
continue
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
@ -282,7 +316,7 @@ class PluginModel(GlancesPluginModel):
|
||||
self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='value', option='decoration'))
|
||||
)
|
||||
else:
|
||||
if args.fahrenheit and i['type'] != 'battery' and i['type'] != SENSOR_FAN_TYPE:
|
||||
if args.fahrenheit and i['type'] != SENSORS_BATTERY_TYPE and i['type'] != SENSOR_FAN_TYPE:
|
||||
trend = ''
|
||||
value = to_fahrenheit(i['value'])
|
||||
unit = 'F'
|
||||
|
@ -50,7 +50,7 @@ class GlancesStats(object):
|
||||
# Check if the attribute starts with 'get'
|
||||
if item.startswith('getViews'):
|
||||
# Get the plugin name
|
||||
plugname = item[len('getViews') :].lower()
|
||||
plugname = item[len('getViews'):].lower()
|
||||
# Get the plugin instance
|
||||
plugin = self._plugins[plugname]
|
||||
if hasattr(plugin, 'get_json_views'):
|
||||
@ -61,7 +61,7 @@ class GlancesStats(object):
|
||||
raise AttributeError(item)
|
||||
elif item.startswith('get'):
|
||||
# Get the plugin name
|
||||
plugname = item[len('get') :].lower()
|
||||
plugname = item[len('get'):].lower()
|
||||
# Get the plugin instance
|
||||
plugin = self._plugins[plugname]
|
||||
if hasattr(plugin, 'get_stats'):
|
||||
@ -260,21 +260,26 @@ class GlancesStats(object):
|
||||
for p in self._plugins:
|
||||
self._plugins[p].load_limits(config)
|
||||
|
||||
def __update_plugin(self, p):
|
||||
"""Update stats, history and views for the given plugin name p"""
|
||||
self._plugins[p].update()
|
||||
self._plugins[p].update_stats_history()
|
||||
self._plugins[p].update_views()
|
||||
|
||||
def update(self):
|
||||
"""Wrapper method to update the stats."""
|
||||
# For standalone and server modes
|
||||
# For each plugins, call the update method
|
||||
for p in self._plugins:
|
||||
if self._plugins[p].is_disabled():
|
||||
# If current plugin is disable
|
||||
# then continue to next plugin
|
||||
continue
|
||||
# Update the stats...
|
||||
self._plugins[p].update()
|
||||
# ... the history
|
||||
self._plugins[p].update_stats_history()
|
||||
# ... and the views
|
||||
self._plugins[p].update_views()
|
||||
"""Wrapper method to update the stats.
|
||||
|
||||
Only called by standalone and server modes
|
||||
"""
|
||||
threads = []
|
||||
# Start update of all enable plugins
|
||||
for p in self.getPluginsList(enable=True):
|
||||
thread = threading.Thread(target=self.__update_plugin, args=(p,))
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
# Wait the end of the update
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
def export(self, input_stats=None):
|
||||
"""Export all the stats.
|
||||
@ -288,7 +293,7 @@ class GlancesStats(object):
|
||||
|
||||
input_stats = input_stats or {}
|
||||
|
||||
for e in self._exports:
|
||||
for e in self.getExportsList(enable=True):
|
||||
logger.debug("Export stats using the %s module" % e)
|
||||
thread = threading.Thread(target=self._exports[e].update, args=(input_stats,))
|
||||
thread.start()
|
||||
|
Loading…
Reference in New Issue
Block a user