From a1ac933238af472d2bc20fdb9f4f30c31eec74fd Mon Sep 17 00:00:00 2001 From: Jeff Curless Date: Sun, 2 Nov 2025 16:10:22 -0500 Subject: [PATCH] Add support for a configuration file. Adding a coniguration file (/etc/sysmon.ini). This will allow users to no monitor the temperature of speciic drives by ignoring them, as well as ignoring the performance from specific drives. For instance on a system running mdraid, you could ignore performance on all of the drives that make up the raid array, and ignore the temperature of the raid device. --- monitor/configfile.py | 82 ++++++++++++++++++++++++++++++++++++++++ monitor/oneUpMon.py | 57 ++++++++++++++++++---------- monitor/systemsupport.py | 8 +++- monitor/test.ini | 5 +++ 4 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 monitor/configfile.py create mode 100644 monitor/test.ini diff --git a/monitor/configfile.py b/monitor/configfile.py new file mode 100644 index 0000000..318eaef --- /dev/null +++ b/monitor/configfile.py @@ -0,0 +1,82 @@ +import configparser + + +class ConfigClass: + ''' + Handle a .INI style configuration file. Every function is designed to not + crash, and always return a default of the items are not present. + + Currently support read-only. + ''' + def __init__( self, filename ): + self.filename = filename + self.config = configparser.ConfigParser() + self.readFile = False + self._openConfig() + + def _openConfig(self) -> None: + ''' + Open,, and read in the configuation file. If the file does not exist, keep + trying to reopen the file until the file does exist. While this approach does + not always help, it does allow for an application polls the configuration file + occasionally. + + ''' + if not self.readFile: + _result = self.config.read( self.filename ) + if len(_result) > 0: + self.readFile = True + + def getValue( self, section : str, key : str, default="" ) -> str: + ''' + This routine obtains the value of the key within the specified section, if there + is such a item. + + Parameter: + section - Name of the section to look for + key - Key of the value desired + default - Value to return if there is no key + + Returns: + The value of the key from the section read. + ''' + value = default + self._openConfig() + try: + value = self.config[section][key].replace('"','').strip() + except: + value = default + return value + + def getValueAsList( self, section : str, name : str, default = [] ) -> list[str]: + ''' + This routine looks for the key in the specified section and returns the data if + it exists, if not the default value is returned. + + Parameters: + section - Section to look for + key - Key to return the value of + default - If they key or section does not exist, return this value + + Returns: + a List of items + ''' + value = default + self._openConfig() + try: + temp = self.config[section][name] + value = [ n.replace('"','').strip() for n in temp.split(",")] + except: + value = default + return value + +if __name__ == "__main__": + cfg = ConfigClass( "test.ini" ) + print( f"Value = {cfg.getValue( 'temperature', 'ignore' )}" ) + print( f"Value = {cfg.getValueAsList( 'temperature', 'ignore' )}" ) + print( f"Value = {cfg.getValue( 'performance', 'ignore' )}" ) + print( f"Value = {cfg.getValueAsList( 'performance', 'ignore' )}" ) + + cfg = ConfigClass( "missingfile.ini" ) + + diff --git a/monitor/oneUpMon.py b/monitor/oneUpMon.py index 6301b3b..3f73428 100755 --- a/monitor/oneUpMon.py +++ b/monitor/oneUpMon.py @@ -9,13 +9,11 @@ Requires: PyQt5 (including QtCharts) import sys from systemsupport import CPUInfo, CPULoad, multiDriveStat +from configfile import ConfigClass # -------------------------- # Globals # -------------------------- -cpuinfo = CPUInfo() -cpuload = CPULoad() -multiDrive = multiDriveStat() # -------------------------- # UI @@ -25,6 +23,7 @@ from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QPainter from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout from PyQt5.QtChart import QChart, QChartView, QLineSeries, QValueAxis +from PyQt5 import QtGui class RollingChart(QWidget): ''' @@ -260,6 +259,16 @@ class MonitorWindow(QMainWindow): def __init__(self, refresh_ms: int = 1000, window = 120, parent=None): super().__init__(parent) + # Get all the filters loaded + self.config = ConfigClass("/etc/sysmon.ini") + self.driveTempFilter = self.config.getValueAsList( 'temperature', 'ignore' ) + self.drivePerfFilter = self.config.getValueAsList( 'performance', 'ignore' ) + + # Get supporting objects + self.cpuinfo = CPUInfo() + self.cpuload = CPULoad() + self.multiDrive = multiDriveStat() + self.setWindowTitle("System Monitor") self.setMinimumSize(900, 900) @@ -273,14 +282,16 @@ class MonitorWindow(QMainWindow): # Charts self.use_chart = RollingChart( title="CPU Utilization", - series_defs=[ (name, None) for name in cpuload.cpuNames ], + series_defs=[ (name, None) for name in self.cpuload.cpuNames ], y_min=0, y_max=100, window=120 ) series = [("CPU", None)] - for name in multiDrive.drives: - series.append( (name,None) ) + for name in self.multiDrive.drives: + if not name in self.driveTempFilter: + series.append( (name,None) ) + self.cpu_chart = RollingChart( title="Temperature (°C)", series_defs= series, @@ -296,9 +307,10 @@ class MonitorWindow(QMainWindow): ) series = [] - for name in multiDrive.drives: - series.append( (f"{name} Read", None) ) - series.append( (f"{name} Write", None ) ) + for name in self.multiDrive.drives: + if not name in self.drivePerfFilter: + series.append( (f"{name} Read", None) ) + series.append( (f"{name} Write", None ) ) self.io_chart = RollingChartDynamic( title="Disk I/O", @@ -331,39 +343,42 @@ class MonitorWindow(QMainWindow): # Obtain the current fan speed try: - fan_speed = cpuinfo.CPUFanSpeed + fan_speed = self.cpuinfo.CPUFanSpeed except Exception: fan_speed = None + # Setup the temperature for the CPU and Drives temperatures = [] try: - temperatures.append( float(cpuinfo.temperature) ) + temperatures.append( float(self.cpuinfo.temperature) ) except Exception: temperatures.append( 0.0 ) - # Obtain the NVMe device temperature + # Obtain the drive temperatures try: - for _drive in multiDrive.drives: - temperatures.append( multiDrive.driveTemp( _drive ) ) + for _drive in self.multiDrive.drives: + if not _drive in self.driveTempFilter: + temperatures.append( self.multiDrive.driveTemp( _drive ) ) except Exception: - temperatures = [ 0.0 for _ in multiDrive.drives ] + temperatures = [ 0.0 for _ in self.multiDrive.drives ] # Obtain the NVMe Device read and write rates try: rwData = [] - drives = multiDrive.readWriteBytes() + drives = self.multiDrive.readWriteBytes() for drive in drives: - rwData.append( float(drives[drive][0])) - rwData.append( float(drives[drive][1])) + if not drive in self.drivePerfFilter: + rwData.append( float(drives[drive][0])) + rwData.append( float(drives[drive][1])) except Exception : rwData = [ None, None ] # Get the CPU load precentages try: - p = cpuload.getPercentages() - values = [p[name] for name in cpuload.cpuNames] + p = self.cpuload.getPercentages() + values = [p[name] for name in self.cpuload.cpuNames] except Exception: - values = [ None for name in cpuload.cpuNames ] + values = [ None for name in self.cpuload.cpuNames ] # Append to charts self.cpu_chart.append( temperatures ) diff --git a/monitor/systemsupport.py b/monitor/systemsupport.py index b7fb60f..dbad19d 100755 --- a/monitor/systemsupport.py +++ b/monitor/systemsupport.py @@ -124,14 +124,18 @@ class multiDriveStat(): If there is a missing drive from the filter, that drive is eliminated. ''' - def __init__(self): + def __init__(self,driveIgnoreList : list[str]=[]): # # Get all drives # self._drives = [] with os.popen( 'ls -1 /sys/block | grep -v -e loop -e ram') as command: lsblk_raw = command.read() - self._drives = [ l for l in lsblk_raw.split('\n') if l] + for l in lsblk_raw.split('\n'): + if len(l) == 0: + continue + if not l in driveIgnoreList: + self._drives.append( l ) self._stats = [ DriveStats(_) for _ in self._drives ] @property diff --git a/monitor/test.ini b/monitor/test.ini new file mode 100644 index 0000000..2803e19 --- /dev/null +++ b/monitor/test.ini @@ -0,0 +1,5 @@ +[temperature] + ignore = "mmcblk0" + +[performance] + ignore = "mmcblk0"