Merge pull request #126 from nicolargo/sensors

Sensors module
This commit is contained in:
Nicolas Hennion 2012-11-18 01:57:27 -08:00
commit c808bb083b
7 changed files with 258 additions and 96 deletions

5
NEWS
View File

@ -1,3 +1,8 @@
Version 1.5.2
=============
* Add sensors module (enable it with -e option)
Version 1.5.1
=============

14
README
View File

@ -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)

View File

@ -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)

BIN
doc/sensors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -19,14 +19,14 @@
#
__appname__ = 'glances'
__version__ = "1.5.1"
__version__ = "1.5.2b"
__author__ = "Nicolas Hennion <nicolas@nicolargo.com>"
__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,32 +69,95 @@ 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)
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:
# HTML output
# 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)
import jinja2
except ImportError:
html_lib_tag = False
@ -102,7 +165,7 @@ else:
html_lib_tag = True
try:
# CSV output
# CSV output (optionnal)
import csv
except ImportError:
cvs_lib_tag = False
@ -301,6 +364,7 @@ class glancesLogs:
self.logs_list = clean_logs_list
return self.len()
class glancesGrabFs:
"""
Get FS stats
@ -359,6 +423,48 @@ class glancesGrabFs:
return self.fs_list
class glancesGrabSensors:
"""
Get Sensors stats using the PySensors lib
"""
def __init__(self):
"""
Init sensors stats
"""
try:
sensors.init()
except:
self.initok = False
else:
self.initok = True
def __update__(self):
"""
Update the stats
"""
# Reset the list
self.sensors_list = []
# Open the current mounted FS
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):
if self.initok:
sensors.cleanup()
class glancesStats:
"""
This class store, update and give stats
@ -378,6 +484,13 @@ class glancesStats:
self.glancesgrabfs = glancesGrabFs()
except Exception:
self.glancesgrabfs = {}
# Init the sensors stats (optionnal)
if sensors_tag:
try:
self.glancesgrabsensors = glancesGrabSensors()
except:
self.sensors_tag = False
# Process list refresh
self.process_list_refresh = True
@ -710,6 +823,16 @@ class glancesStats:
if (self.server_tag):
self.all_stats["network"] = self.network
# SENSORS
if (self.client_tag):
if input_stats != {}:
self.sensors = input_stats["sensors"]
else:
if (sensors_tag):
self.sensors = self.glancesgrabsensors.get()
if (self.server_tag):
self.all_stats["sensors"] = self.sensors
# DISK I/O
if (self.client_tag):
if input_stats != {}:
@ -853,6 +976,13 @@ class glancesStats:
def getMemSwap(self):
return self.memswap
def getSensors(self):
if sensors_tag:
return sorted(self.sensors,
key=lambda sensors: sensors['label'])
else:
return 0
def getNetwork(self):
if psutil_network_io_tag:
return sorted(self.network,
@ -933,6 +1063,8 @@ class glancesScreen:
self.mem_y = 2
self.network_x = 0
self.network_y = 7
self.sensors_x = 0
self.sensors_y = -1
self.diskio_x = 0
self.diskio_y = -1
self.fs_x = 0
@ -1029,6 +1161,7 @@ class glancesScreen:
# What are we going to display
self.network_tag = psutil_network_io_tag
self.sensors_tag = sensors_tag
self.diskio_tag = psutil_disk_io_tag
self.fs_tag = psutil_fs_usage_tag
self.log_tag = True
@ -1151,6 +1284,9 @@ class glancesScreen:
def __getNetColor(self, current=0, max=100):
return self.__getColor2(current, max)
def __getSensorsColor(self, current=0, max=100):
return self.__getColor2(current, max)
def __getFsColor(self, current=0, max=100):
return self.__getColor2(current, max)
@ -1199,6 +1335,9 @@ class glancesScreen:
elif self.pressedkey == 112:
# 'p' > Sort processes by name
self.setProcessSortedBy('name')
elif self.pressedkey == 115:
# 's' > Show/Hide Sensors stats
self.sensors_tag = not self.sensors_tag
elif self.pressedkey == 119:
# 'w' > Delete finished warning logs
logs.clean()
@ -1234,12 +1373,14 @@ class glancesScreen:
self.displayLoad(stats.getLoad(), stats.getCore(), processlist, cpu_offset)
self.displayMem(stats.getMem(), stats.getMemSwap(), processlist, cpu_offset)
network_count = self.displayNetwork(stats.getNetwork())
sensors_count = self.displaySensors(stats.getSensors(),
self.network_y + network_count)
diskio_count = self.displayDiskIO(stats.getDiskIO(),
self.network_y + network_count)
self.network_y + sensors_count + network_count)
fs_count = self.displayFs(stats.getFs(),
self.network_y + network_count +
diskio_count)
log_count = self.displayLog(self.network_y + network_count +
self.network_y + sensors_count +
network_count + diskio_count)
log_count = self.displayLog(self.network_y + sensors_count + network_count +
diskio_count + fs_count)
self.displayProcess(processcount, processlist, log_count)
self.displayCaption(cs_status = cs_status)
@ -1555,6 +1696,39 @@ class glancesScreen:
return ret
return 0
def displaySensors(self, sensors, offset_y=0):
"""
Display the Sensors stats
Return the number of sensors stats
"""
if not self.sensors_tag or not sensors:
return 0
screen_x = self.screen.getmaxyx()[1]
screen_y = self.screen.getmaxyx()[0]
self.sensors_y = offset_y
if (screen_y > self.sensors_y + 3 and
screen_x > self.sensors_x + 28):
# Sensors header
self.term_window.addnstr(self.sensors_y, self.sensors_x,
_("Sensors"), 8, self.title_color
if self.hascolors else curses.A_UNDERLINE)
self.term_window.addnstr(self.sensors_y, self.sensors_x+22,
_("C"), 3)
# Adapt the maximum interface to the screen
ret = 2
sensors_num = min(screen_y - self.sensors_y - 3, len(sensors))
for i in range(0, sensors_num):
self.term_window.addnstr(
self.sensors_y + 1 + i, self.sensors_x,
sensors[i]['label'] + ':', 21)
self.term_window.addnstr(
self.sensors_y + 1 + i, self.sensors_x + 22,
format(int(sensors[i]['value'])), 3)
ret = ret + 1
return ret
return 0
def displayDiskIO(self, diskio, offset_y=0):
# Disk input/output rate
if not self.diskio_tag:
@ -2057,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)
@ -2401,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"))
@ -2442,16 +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
@ -2470,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:
@ -2512,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
@ -2582,69 +2770,10 @@ def main():
psutil_fs_usage_tag = True
psutil_disk_io_tag = True
psutil_network_io_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
sensors_tag = True
elif server_tag:
sensors_tag = True
# Init Glances depending of the mode (standalone, client, server)
if server_tag:
# Init the server

View File

@ -53,4 +53,3 @@ class TestGlancesStat(unittest.TestCase):
if __name__ == '__main__':
unittest.main()

View File

@ -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"]},