diff --git a/NEWS b/NEWS index 77cefea5..60ad00fe 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +Version 1.5.2 +============= + + * Add sensors module (enable it with -e option) + Version 1.5.1 ============= diff --git a/README b/README index 2659f1aa..07e40070 100644 --- a/README +++ b/README @@ -32,6 +32,7 @@ Pre-requisites (information for packagers): * python-setuptools (for the installation via setup.py) * python-psutil 0.4.1+ (replace the old libstatgrab's lib) * python-jinja2 2.0+ (optional for HTML export) +* pysensors (Python lib for sensors stats) ### From package manager (very easy way) @@ -164,6 +165,7 @@ When Glances is running, you can press: * 'b' switch between bit/s or byte/s for network IO * 'c' sort the processes list by CPU consumption * 'd' disable or enable the disk IO stats +* 'e' enable the sensors module (PySensors lib is needed) * 'f' disable or enable the file system stats * 'l' disable or enable the logs * 'm' sort the processes list by process MEM @@ -274,6 +276,18 @@ If bit rate is > 90%, then status is set to "CRITICAL". For example, on a 100 Mbps Ethernet interface, the warning status is set if the bit rate is higher than 70 Mbps. +### Sensors (optional) + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/sensors.png) + +Optinaly, Glances displays the sensors informations (lm-sensors). + +You should enable this module using the following command line: + + glances -e + +There is no alert on this information. + ### Disk I/O ![screenshot](https://github.com/nicolargo/glances/raw/master/doc/diskio.png) diff --git a/README-fr b/README-fr index 21f3a5ea..f22044e0 100644 --- a/README-fr +++ b/README-fr @@ -28,6 +28,7 @@ Glances a besoin des dépendances suivantes: * python-setuptools (pour l'installation via setup.py) * python-psutil 0.4.1+ (remplace la librairie libstatgrab) * python-jinja2 2.0+ (optionnel seulement pour le module export HTML) +* pysensors (librairie optionnelle pour les capteurs) ### Depuis le gestionnaire de paquet de votre système @@ -168,6 +169,7 @@ Quand Glances est lancé, il est possible d'utiliser les touches suivantes: * 'b' passer le débit des interfaces réseaux de bit par sec à octet par sec * 'c' forcer le tri par consommation CPU * 'd' desactiver ou activer l'affichage des entrées/sorties disques +* 'e' activer ou desactiver l'affichage des capteurs * 'f' desactiver ou activer l'affichage de l'occupation des FS * 'l' desactiver ou activer l'affichage des logs * 'm' forcer le tri par consommation MEMOIRE @@ -277,6 +279,18 @@ Si le débit > 90%, alors le status est "CRITICAL". Par exemple, sur une interface Ethernet Fast Ethernet (100 Mbps), le status passera à WARNING si le débit dépasse les 70 Mbps. +### Capteur (en option) + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/sensors.png) + +Glances peut afficher les informations sur les capteurs (lm-sensors). + +Ce module est optionnel et doit être activé avec l'option: + + glances -e + +IL nécessite l'installation de la librairie Python pysensors. + ### Entrées/Sorties disque ![screenshot](https://github.com/nicolargo/glances/raw/master/doc/diskio.png) diff --git a/doc/sensors.png b/doc/sensors.png new file mode 100644 index 00000000..8b17c0af Binary files /dev/null and b/doc/sensors.png differ diff --git a/glances/glances.py b/glances/glances.py index 359ed4ad..c6de39e0 100755 --- a/glances/glances.py +++ b/glances/glances.py @@ -26,7 +26,7 @@ __licence__ = "LGPL" # Libraries #========== -# Standard lib +# Standards libs import os import sys import platform @@ -37,7 +37,7 @@ from datetime import datetime, timedelta import gettext gettext.install(__appname__) -# Selective lib +# Specifics libs import json import collections @@ -69,37 +69,92 @@ if platform.system() != 'Windows': sys.exit(1) try: + # PSUtil is the main lib used to grab stats import psutil except ImportError: print(_('PsUtil module not found. Glances cannot start.')) print() - print(_('On Ubuntu 12.04 or higher:')) - print(_('$ sudo apt-get install python-psutil')) - print() - print(_('To install PsUtil using pip (as root):')) - print(_('# pip install psutil')) - print() sys.exit(1) if (int(psutil.__version__.split('.')[0]) == 0) \ and (int(psutil.__version__.split('.')[1]) < 4): + print(_('PsUtil version %s detected') % psutil.__version__) print(_('PsUtil > 0.4.0 is needed. Glances cannot start.')) print() - print(_('On Ubuntu 12.04 or higher:')) - print(_('$ sudo apt-get install python-psutil')) - print() - print(_('To install PsUtil using pip (as root):')) - print(_('# pip install psutil')) - print() sys.exit(1) +try: + # get_cpu_percent method only available with PsUtil 0.2.0+ + psutil.Process(os.getpid()).get_cpu_percent(interval=0) +except Exception: + psutil_get_cpu_percent_tag = False +else: + psutil_get_cpu_percent_tag = True + +try: + # get_io_counter method only available with PsUtil 0.2.1+ + psutil.Process(os.getpid()).get_io_counters() +except Exception: + psutil_get_io_counter_tag = False +else: + # get_io_counter only available on Linux + if sys.platform.startswith("linux"): + psutil_get_io_counter_tag = True + else: + psutil_get_io_counter_tag = False + +try: + # virtual_memory() is only available with PsUtil 0.6+ + psutil.virtual_memory() +except: + try: + # (phy|virt)mem_usage methods only available with PsUtil 0.3.0+ + psutil.phymem_usage() + psutil.virtmem_usage() + except Exception: + psutil_mem_usage_tag = False + else: + psutil_mem_usage_tag = True + psutil_mem_vm = False +else: + psutil_mem_usage_tag = True + psutil_mem_vm = True + +try: + # disk_(partitions|usage) methods only available with PsUtil 0.3.0+ + psutil.disk_partitions() + psutil.disk_usage('/') +except Exception: + psutil_fs_usage_tag = False +else: + psutil_fs_usage_tag = True + +try: + # disk_io_counters method only available with PsUtil 0.4.0+ + psutil.disk_io_counters() +except Exception: + psutil_disk_io_tag = False +else: + psutil_disk_io_tag = True + +try: + # network_io_counters method only available with PsUtil 0.4.0+ + psutil.network_io_counters() +except Exception: + psutil_network_io_tag = False +else: + psutil_network_io_tag = True + + try: # Sensors (optionnal) import sensors except ImportError: sensors_lib_tag = False + sensors_tag = False else: sensors_lib_tag = True + sensors_tag = True try: # HTML output (optionnal) @@ -377,7 +432,12 @@ class glancesGrabSensors: """ Init sensors stats """ - sensors.init() + try: + sensors.init() + except: + self.initok = False + else: + self.initok = True def __update__(self): """ @@ -387,20 +447,22 @@ class glancesGrabSensors: self.sensors_list = [] # Open the current mounted FS - for chip in sensors.iter_detected_chips(): - for feature in chip: - sensors_current = {} - sensors_current['label'] = chip.prefix+" "+feature.label - sensors_current['label'] = sensors_current['label'][-20:] - sensors_current['value'] = feature.get_value() - self.sensors_list.append(sensors_current) + if self.initok: + for chip in sensors.iter_detected_chips(): + for feature in chip: + sensors_current = {} + sensors_current['label'] = chip.prefix+" "+feature.label + sensors_current['label'] = sensors_current['label'][-20:] + sensors_current['value'] = feature.get_value() + self.sensors_list.append(sensors_current) def get(self): self.__update__() return self.sensors_list def quit(self): - sensors.cleanup() + if self.initok: + sensors.cleanup() class glancesStats: @@ -416,7 +478,6 @@ class glancesStats: self.server_tag = server_tag self.client_tag = client_tag - self.sensors_tag = sensors_tag # Init the fs stats try: @@ -425,7 +486,7 @@ class glancesStats: self.glancesgrabfs = {} # Init the sensors stats (optionnal) - if self.sensors_tag: + if sensors_tag: try: self.glancesgrabsensors = glancesGrabSensors() except: @@ -764,13 +825,10 @@ class glancesStats: # SENSORS if (self.client_tag): - if hasattr(input_stats, "sensors"): - if input_stats != {}: - self.sensors = input_stats["sensors"] - else: - self.sensors_tag = False + if input_stats != {}: + self.sensors = input_stats["sensors"] else: - if (self.sensors_tag): + if (sensors_tag): self.sensors = self.glancesgrabsensors.get() if (self.server_tag): self.all_stats["sensors"] = self.sensors @@ -919,8 +977,9 @@ class glancesStats: return self.memswap def getSensors(self): - if self.sensors_tag: - return self.sensors + if sensors_tag: + return sorted(self.sensors, + key=lambda sensors: sensors['label']) else: return 0 @@ -1642,7 +1701,7 @@ class glancesScreen: Display the Sensors stats Return the number of sensors stats """ - if not self.sensors_tag: + if not self.sensors_tag or not sensors: return 0 screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] @@ -1653,12 +1712,8 @@ class glancesScreen: self.term_window.addnstr(self.sensors_y, self.sensors_x, _("Sensors"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) - - # If there is no data to display... - if not sensors: - self.term_window.addnstr(self.sensors_y + 1, self.sensors_x, - _("Compute data..."), 15) - return 3 + self.term_window.addnstr(self.sensors_y, self.sensors_x+22, + _("C"), 3) # Adapt the maximum interface to the screen ret = 2 @@ -2176,25 +2231,30 @@ class glancesScreen: self.term_window.addnstr( self.help_y + 13, self.help_x, "{0:^{width}} {1}".format( - _("l"), _("Show/hide log messages"), width=width), 79) + _("s"), _("Show/hide sensors stats"), width=width), + 79, self.ifCRITICAL_color2 if not psutil_network_io_tag else 0) self.term_window.addnstr( self.help_y + 14, self.help_x, "{0:^{width}} {1}".format( - _("w"), _("Delete finished warning logs messages"), width=width), 79) + _("l"), _("Show/hide log messages"), width=width), 79) self.term_window.addnstr( self.help_y + 15, self.help_x, "{0:^{width}} {1}".format( - _("x"), _("Delete finished warning and critical logs"), width=width), 79) + _("w"), _("Delete finished warning logs messages"), width=width), 79) self.term_window.addnstr( self.help_y + 16, self.help_x, "{0:^{width}} {1}".format( - _("1"), _("Switch between global CPU and per core stats"), width=width), 79) + _("x"), _("Delete finished warning and critical logs"), width=width), 79) self.term_window.addnstr( self.help_y + 17, self.help_x, "{0:^{width}} {1}".format( - _("h"), _("Show/hide this help message"), width=width), 79) + _("1"), _("Switch between global CPU and per core stats"), width=width), 79) self.term_window.addnstr( self.help_y + 18, self.help_x, + "{0:^{width}} {1}".format( + _("h"), _("Show/hide this help message"), width=width), 79) + self.term_window.addnstr( + self.help_y + 19, self.help_x, "{0:^{width}} {1}".format( _("q"), _("Quit (Esc and Ctrl-C also work)"), width=width), 79) @@ -2520,6 +2580,7 @@ def printSyntax(): print(_("\t-B IP|NAME\tBind server to the given IP or host NAME")) print(_("\t-c @IP|host\tConnect to a Glances server")) print(_("\t-d\t\tDisable disk I/O module")) + print(_("\t-e\t\tEnable the sensors module")) print(_("\t-f file\t\tSet the output folder (HTML) or file (CSV)")) print(_("\t-h\t\tDisplay the syntax and exit")) print(_("\t-m\t\tDisable mount module")) @@ -2561,17 +2622,18 @@ def main(): # Glances - Init stuff ###################### - global network_bytepersec_tag global limits, logs, stats, screen global htmloutput, csvoutput global html_tag, csv_tag, server_tag, client_tag global psutil_get_cpu_percent_tag, psutil_get_io_counter_tag, psutil_mem_usage_tag global psutil_mem_vm, psutil_fs_usage_tag, psutil_disk_io_tag, psutil_network_io_tag + global network_bytepersec_tag global sensors_tag global refresh_time, client, server, server_port, server_ip # Set default tags network_bytepersec_tag = False + sensors_tag = False html_tag = False csv_tag = False client_tag = False @@ -2590,10 +2652,10 @@ def main(): # Manage args try: - opts, args = getopt.getopt(sys.argv[1:], "B:bdmnho:f:t:vsc:p:", + opts, args = getopt.getopt(sys.argv[1:], "B:bdemnho:f:t:vsc:p:", ["bind", "bytepersec", "diskio", "mount", - "netrate", "help", "output", "file", - "time", "version", "server", + "sensors", "netrate", "help", "output", + "file", "time", "version", "server", "client", "port"]) except getopt.GetoptError as err: # Print help information and exit: @@ -2632,6 +2694,12 @@ def main(): print(_("Error: Unknown output %s" % arg)) printSyntax() sys.exit(2) + elif opt in ("-e", "--sensors"): + if not sensors_lib_tag: + print(_("Error: PySensors lib not found")) + sys.exit(2) + else: + sensors_tag = True elif opt in ("-f", "--file"): output_file = arg output_folder = arg @@ -2703,75 +2771,9 @@ def main(): psutil_disk_io_tag = True psutil_network_io_tag = True sensors_tag = True - else: - try: - # get_cpu_percent method only available with PsUtil 0.2.0+ - psutil.Process(os.getpid()).get_cpu_percent(interval=0) - except Exception: - psutil_get_cpu_percent_tag = False - else: - psutil_get_cpu_percent_tag = True - - try: - # get_io_counter method only available with PsUtil 0.2.1+ - psutil.Process(os.getpid()).get_io_counters() - except Exception: - psutil_get_io_counter_tag = False - else: - # get_io_counter only available on Linux - if sys.platform.startswith("linux"): - psutil_get_io_counter_tag = True - else: - psutil_get_io_counter_tag = False - - try: - # virtual_memory() is only available with PsUtil 0.6+ - psutil.virtual_memory() - except: - try: - # (phy|virt)mem_usage methods only available with PsUtil 0.3.0+ - psutil.phymem_usage() - psutil.virtmem_usage() - except Exception: - psutil_mem_usage_tag = False - else: - psutil_mem_usage_tag = True - psutil_mem_vm = False - else: - psutil_mem_usage_tag = True - psutil_mem_vm = True - - try: - # disk_(partitions|usage) methods only available with PsUtil 0.3.0+ - psutil.disk_partitions() - psutil.disk_usage('/') - except Exception: - psutil_fs_usage_tag = False - else: - psutil_fs_usage_tag = True - - try: - # disk_io_counters method only available with PsUtil 0.4.0+ - psutil.disk_io_counters() - except Exception: - psutil_disk_io_tag = False - else: - psutil_disk_io_tag = True - - try: - # network_io_counters method only available with PsUtil 0.4.0+ - psutil.network_io_counters() - except Exception: - psutil_network_io_tag = False - else: - psutil_network_io_tag = True + elif server_tag: + sensors_tag = True - if sensors_lib_tag == True: - # Sensors lib is available - sensors_tag = True - else: - sensors_tag = False - # Init Glances depending of the mode (standalone, client, server) if server_tag: # Init the server diff --git a/glances/unitest.py b/glances/unitest.py index 47659003..136dd35f 100755 --- a/glances/unitest.py +++ b/glances/unitest.py @@ -53,4 +53,3 @@ class TestGlancesStat(unittest.TestCase): if __name__ == '__main__': unittest.main() - diff --git a/setup.py b/setup.py index 3bfb738b..fbc63a41 100755 --- a/setup.py +++ b/setup.py @@ -35,7 +35,8 @@ setup(name='Glances', install_requires=['psutil>=0.4.1'], packages=['glances'], extras_require = { - 'HTML': ['jinja2>=2.0'],}, + 'HTML': ['jinja2>=2.0'], + 'SENSORS': ['pysensors>=0.0.2'],}, include_package_data=True, data_files=data_files, entry_points={"console_scripts": ["glances = glances.glances:main"]},