summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBharath Vignesh J K <52282402+RazCrimson@users.noreply.github.com>2024-05-16 17:15:42 +0530
committerBharath Vignesh J K <52282402+RazCrimson@users.noreply.github.com>2024-05-16 17:15:42 +0530
commitfecd23b26feaf54187b5171881dec4d10a373859 (patch)
tree6055fe3c5a52c37e9e1fe622918b086a2baf983c
parenteb83f6e6b117176f327b0144a2462121ef4035ed (diff)
parent16621cf75ffe8031fdc1ee558d7369e81b46289e (diff)
Merge branch 'refs/heads/support/v4.0.x'
# Conflicts: # NEWS.rst # docs/api.rst # docs/man/glances.1 # glances/__init__.py # glances/plugins/sensors/__init__.py # snap/snapcraft.yaml
-rw-r--r--NEWS.rst13
-rw-r--r--docs/api.rst26
-rwxr-xr-x[-rw-r--r--]docs/man/glances.12
-rw-r--r--glances/__init__.py2
-rw-r--r--glances/plugins/sensors/__init__.py221
-rw-r--r--snap/snapcraft.yaml2
6 files changed, 122 insertions, 144 deletions
diff --git a/NEWS.rst b/NEWS.rst
index 04e63fce..7477fc1e 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -11,6 +11,19 @@ Under development, see roadmap here: https://github.com/nicolargo/glances/milest
Contributors are welcome !
===============
+Version 4.0.4
+===============
+
+Hostfix release for support sensors plugin on python3.8
+
+===============
+Version 4.0.3
+===============
+
+Additional fixes for Sensor plugin.
+
+
+===============
Version 4.0.2
===============
diff --git a/docs/api.rst b/docs/api.rst
index 3f793558..8aeaa79d 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -480,7 +480,13 @@ GET gpu
Get plugin stats::
# curl http://localhost:61208/api/4/gpu
- []
+ [{"fan_speed": 29,
+ "gpu_id": "nvidia0",
+ "key": "gpu_id",
+ "mem": 46.144612630208336,
+ "name": "NVIDIA GeForce GTX 1060 3GB",
+ "proc": 2,
+ "temperature": 57}]
Fields descriptions:
@@ -491,6 +497,22 @@ Fields descriptions:
* **temperature**: GPU temperature (unit is *celsius*)
* **fan_speed**: GPU fan speed (unit is *roundperminute*)
+Get a specific field::
+
+ # curl http://localhost:61208/api/4/gpu/gpu_id
+ {"gpu_id": ["nvidia0"]}
+
+Get a specific item when field matches the given value::
+
+ # curl http://localhost:61208/api/4/gpu/gpu_id/nvidia0
+ {"nvidia0": [{"fan_speed": 29,
+ "gpu_id": "nvidia0",
+ "key": "gpu_id",
+ "mem": 46.144612630208336,
+ "name": "NVIDIA GeForce GTX 1060 3GB",
+ "proc": 2,
+ "temperature": 57}]}
+
GET help
--------
@@ -1226,7 +1248,7 @@ GET version
Get plugin stats::
# curl http://localhost:61208/api/4/version
- "4.0.2"
+ "4.0.4"
GET wifi
--------
diff --git a/docs/man/glances.1 b/docs/man/glances.1
index 9eb2fd95..eb65ad92 100644..100755
--- a/docs/man/glances.1
+++ b/docs/man/glances.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "GLANCES" "1" "May 13, 2024" "4.0.2" "Glances"
+.TH "GLANCES" "1" "May 15, 2024" "4.0.4" "Glances"
.SH NAME
glances \- An eye on your system
.SH SYNOPSIS
diff --git a/glances/__init__.py b/glances/__init__.py
index 094532a7..1a156423 100644
--- a/glances/__init__.py
+++ b/glances/__init__.py
@@ -20,7 +20,7 @@ import sys
# Global name
# Version should start and end with a numerical char
# See https://packaging.python.org/specifications/core-metadata/#version
-__version__ = '4.0.2'
+__version__ = '4.0.4'
__apiversion__ = '4'
__author__ = 'Nicolas Hennion <nicolas@nicolargo.com>'
__license__ = 'LGPLv3'
diff --git a/glances/plugins/sensors/__init__.py b/glances/plugins/sensors/__init__.py
index 0173235a..8634d579 100644
--- a/glances/plugins/sensors/__init__.py
+++ b/glances/plugins/sensors/__init__.py
@@ -8,30 +8,33 @@
#
"""Sensors plugin."""
+from enum import Enum
+from concurrent.futures import ThreadPoolExecutor
+from typing import List, Dict, Literal, Any
import psutil
import warnings
-import threading
from glances.logger import logger
-from glances.globals import iteritems, to_fahrenheit
+from glances.globals import to_fahrenheit
from glances.timer import Counter
from glances.plugins.sensors.sensor.glances_batpercent import PluginModel as BatPercentPluginModel
from glances.plugins.sensors.sensor.glances_hddtemp import PluginModel as HddTempPluginModel
from glances.outputs.glances_unicode import unicode_message
from glances.plugins.plugin.model import GlancesPluginModel
-SENSOR_TEMP_TYPE = 'temperature_core'
-SENSOR_TEMP_UNIT = 'C'
-SENSOR_FAN_TYPE = 'fan_speed'
-SENSOR_FAN_UNIT = 'R'
+class SensorType(str, Enum):
+ CPU_TEMP = 'temperature_core'
+ FAN_SPEED = 'fan_speed'
+ HDD_TEMP = 'temperature_hdd'
+ BATTERY = 'battery'
-SENSOR_HDDTEMP_TYPE = 'temperature_hdd'
-SENSOR_HDDTEMP_UNIT = 'C'
-SENSORS_BATTERY_TYPE = 'battery'
-SENSORS_BATTERY_UNIT = '%'
+CPU_TEMP_UNIT = 'C'
+FAN_SPEED_UNIT = 'R'
+HDD_TEMP_UNIT = 'C'
+BATTERY_UNIT = '%'
# Define the default refresh multiplicator
# Default value is 3 * Glances refresh time
@@ -82,28 +85,38 @@ class PluginModel(GlancesPluginModel):
super(PluginModel, self).__init__(
args=args, config=config, stats_init_value=[], fields_description=fields_description
)
-
start_duration = Counter()
# Init the sensor class
start_duration.reset()
- # Hotfix! Refactor to use only one `GlancesGrabSensors` later
- self.glances_grab_sensors_fan_speed = GlancesGrabSensors()
- self.glances_grab_sensors_temperature = GlancesGrabSensors()
- logger.debug("Generic sensor plugin init duration: {} seconds".format(start_duration.get()))
+ glances_grab_sensors_cpu_temp = GlancesGrabSensors(SensorType.CPU_TEMP)
+ logger.debug("CPU Temp sensor plugin init duration: {} seconds".format(start_duration.get()))
+
+ start_duration.reset()
+ glances_grab_sensors_fan_speed = GlancesGrabSensors(SensorType.FAN_SPEED)
+ logger.debug("Fan speed sensor plugin init duration: {} seconds".format(start_duration.get()))
- # Instance for the HDDTemp Plugin in order to display the hard disks
- # temperatures
+ # Instance for the HDDTemp Plugin in order to display the hard disks temperatures
start_duration.reset()
- self.hddtemp_plugin = HddTempPluginModel(args=args, config=config)
+ hddtemp_plugin = HddTempPluginModel(args=args, config=config)
logger.debug("HDDTemp sensor plugin init duration: {} seconds".format(start_duration.get()))
- # Instance for the BatPercent in order to display the batteries
- # capacities
+ # Instance for the BatPercent in order to display the batteries capacities
start_duration.reset()
- self.batpercent_plugin = BatPercentPluginModel(args=args, config=config)
+ batpercent_plugin = BatPercentPluginModel(args=args, config=config)
logger.debug("Battery sensor plugin init duration: {} seconds".format(start_duration.get()))
+ self.sensors_grab_map: Dict[SensorType, Any] = {}
+
+ if glances_grab_sensors_cpu_temp.init:
+ self.sensors_grab_map[SensorType.CPU_TEMP] = glances_grab_sensors_cpu_temp
+
+ if glances_grab_sensors_fan_speed.init:
+ self.sensors_grab_map[SensorType.FAN_SPEED] = glances_grab_sensors_fan_speed
+
+ self.sensors_grab_map[SensorType.HDD_TEMP] = hddtemp_plugin
+ self.sensors_grab_map[SensorType.BATTERY] = batpercent_plugin
+
# We want to display the stat in the curse interface
self.display_curse = True
@@ -115,37 +128,15 @@ 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_temperature.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):
+ def __get_sensor_data(self, sensor_type: SensorType) -> List[Dict]:
try:
- fan_speed = self.__set_type(self.glances_grab_sensors_fan_speed.get(SENSOR_FAN_TYPE), SENSOR_FAN_TYPE)
+ data = self.sensors_grab_map[sensor_type].update()
+ data = self.__set_type(data, sensor_type)
except Exception as e:
- logger.error("Cannot grab FAN speed (%s)" % e)
+ logger.error(f"Cannot grab sensors `{sensor_type}` ({e})")
+ return []
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)
+ return self.__transform_sensors(data)
def __transform_sensors(self, threads_stats):
"""Hide, alias and sort the result"""
@@ -172,22 +163,17 @@ class PluginModel(GlancesPluginModel):
stats = self.get_init_value()
if self.input_method == 'local':
- 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()
+ with ThreadPoolExecutor(max_workers=len(self.sensors_grab_map)) as executor:
+ logger.debug(f"Sensors enabled sub plugins: {list(self.sensors_grab_map.keys())}")
+ futures = {t: executor.submit(self.__get_sensor_data, t) for t in self.sensors_grab_map.keys()}
+
# Merge the results
- for s in threads_stats:
- stats.extend(s)
+ for sensor_type, future in futures.items():
+ try:
+ stats.extend(future.result())
+ except Exception as e:
+ logger.error(f"Cannot parse sensors data for `{sensor_type}` ({e})")
+
elif self.input_method == 'snmp':
# Update stats using SNMP
# No standard:
@@ -220,7 +206,7 @@ class PluginModel(GlancesPluginModel):
"""
for i in stats:
# Set the sensors type
- i.update({'type': sensor_type})
+ i.update({'type': str(sensor_type)})
# also add the key name
i.update({'key': self.get_key()})
@@ -237,10 +223,10 @@ class PluginModel(GlancesPluginModel):
if not i['value']:
continue
# Alert processing
- if i['type'] == SENSOR_TEMP_TYPE:
- if self.is_limit('critical', stat_name=SENSOR_TEMP_TYPE + '_' + i['label']):
+ if i['type'] == SensorType.CPU_TEMP:
+ if self.is_limit('critical', stat_name=SensorType.CPU_TEMP + '_' + i['label']):
# By default use the thresholds configured in the glances.conf file (see #2058)
- alert = self.get_alert(current=i['value'], header=SENSOR_TEMP_TYPE + '_' + i['label'])
+ alert = self.get_alert(current=i['value'], header=SensorType.CPU_TEMP + '_' + i['label'])
else:
# Else use the system thresholds
if i['critical'] is None:
@@ -253,7 +239,7 @@ class PluginModel(GlancesPluginModel):
alert = 'WARNING'
else:
alert = 'OK'
- elif i['type'] == SENSORS_BATTERY_TYPE:
+ elif i['type'] == SensorType.BATTERY:
# Battery is in %
alert = self.get_alert(current=100 - i['value'], header=i['type'])
else:
@@ -297,7 +283,7 @@ class PluginModel(GlancesPluginModel):
# Stats
for i in self.stats:
# Do not display anything if no battery are detected
- if i['type'] == SENSORS_BATTERY_TYPE and i['value'] == []:
+ if i['type'] == SensorType.BATTERY and i['value'] == []:
continue
# New line
ret.append(self.curse_new_line())
@@ -309,7 +295,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'] != SENSORS_BATTERY_TYPE and i['type'] != SENSOR_FAN_TYPE:
+ if args.fahrenheit and i['type'] != SensorType.BATTERY and i['type'] != SensorType.FAN_SPEED:
trend = ''
value = to_fahrenheit(i['value'])
unit = 'F'
@@ -334,73 +320,42 @@ class PluginModel(GlancesPluginModel):
class GlancesGrabSensors(object):
"""Get sensors stats."""
- def __init__(self):
+ def __init__(self, sensor_type: Literal[SensorType.FAN_SPEED, SensorType.CPU_TEMP]):
"""Init sensors stats."""
- # Temperatures
- self.init_temp = False
- self.sensor_temps = {}
+ self.sensor_type = sensor_type
+ self.sensor_unit = CPU_TEMP_UNIT if self.sensor_type == SensorType.CPU_TEMP else FAN_SPEED_UNIT
+
+ self.init = False
try:
- # psutil>=5.1.0, Linux-only
- self.sensor_temps = psutil.sensors_temperatures()
+ self.__fetch_psutil()
+ self.init = True
except AttributeError:
- logger.debug("Cannot grab temperatures. Platform not supported.")
- else:
- self.init_temp = True
+ logger.debug(f"Cannot grab {sensor_type}. Platform not supported.")
+
+ def __fetch_psutil(self) -> Dict[str, list]:
+ if self.sensor_type == SensorType.CPU_TEMP:
# Solve an issue #1203 concerning a RunTimeError warning message displayed
# in the curses interface.
warnings.filterwarnings("ignore")
- # Fans
- self.init_fan = False
- self.sensor_fans = {}
- try:
- # psutil>=5.2.0, Linux-only
- self.sensor_fans = psutil.sensors_fans()
- except AttributeError:
- logger.debug("Cannot grab fans speed. Platform not supported.")
- else:
- self.init_fan = True
+ # psutil>=5.1.0, Linux-only
+ return psutil.sensors_temperatures()
- # Init the stats
- self.reset()
+ if self.sensor_type == SensorType.FAN_SPEED:
+ # psutil>=5.2.0, Linux-only
+ return psutil.sensors_fans()
- def reset(self):
- """Reset/init the stats."""
- self.sensors_list = []
+ raise ValueError(f"Unsupported sensor_type: {self.sensor_type}")
- def __update__(self):
+ def update(self) -> List[dict]:
"""Update the stats."""
- # Reset the list
- self.reset()
-
- if not self.init_temp:
- return self.sensors_list
+ if not self.init:
+ return []
# Temperatures sensors
- self.sensors_list.extend(self.build_sensors_list(SENSOR_TEMP_UNIT))
-
- # Fans sensors
- self.sensors_list.extend(self.build_sensors_list(SENSOR_FAN_UNIT))
-
- return self.sensors_list
-
- def build_sensors_list(self, type):
- """Build the sensors list depending of the type.
-
- type: SENSOR_TEMP_UNIT or SENSOR_FAN_UNIT
-
- output: a list
- """
ret = []
- if type == SENSOR_TEMP_UNIT and self.init_temp:
- input_list = self.sensor_temps
- self.sensor_temps = psutil.sensors_temperatures()
- elif type == SENSOR_FAN_UNIT and self.init_fan:
- input_list = self.sensor_fans
- self.sensor_fans = psutil.sensors_fans()
- else:
- return ret
- for chip_name, chip in iteritems(input_list):
+ data = self.__fetch_psutil()
+ for chip_name, chip in data.items():
label_index = 1
for chip_name_index, feature in enumerate(chip):
sensors_current = {}
@@ -413,8 +368,9 @@ class GlancesGrabSensors(object):
else:
sensors_current['label'] = feature.label
# Sensors value, limit and unit
- sensors_current['unit'] = type
- sensors_current['value'] = int(getattr(feature, 'current', 0) if getattr(feature, 'current', 0) else 0)
+ sensors_current['unit'] = self.sensor_unit
+ sensors_current['value'] = int(
+ getattr(feature, 'current', 0) if getattr(feature, 'current', 0) else 0)
system_warning = getattr(feature, 'high', None)
system_critical = getattr(feature, 'critical', None)
sensors_current['warning'] = int(system_warning) if system_warning is not None else None
@@ -422,16 +378,3 @@ class GlancesGrabSensors(object):
# Add sensor to the list
ret.append(sensors_current)
return ret
-
- def get(self, sensor_type=SENSOR_TEMP_TYPE):
- """Get sensors list."""
- self.__update__()
- if sensor_type == SENSOR_TEMP_TYPE:
- ret = [s for s in self.sensors_list if s['unit'] == SENSOR_TEMP_UNIT]
- elif sensor_type == SENSOR_FAN_TYPE:
- ret = [s for s in self.sensors_list if s['unit'] == SENSOR_FAN_UNIT]
- else:
- # Unknown type
- logger.debug("Unknown sensor type %s" % sensor_type)
- ret = []
- return ret
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 23436fe8..392bf9ea 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,5 +1,5 @@
name: glances
-version: '4.0.2'
+version: '4.0.4'
summary: Glances an Eye on your system. A top/htop alternative.
description: |