From c6e07e1670f181e95335f1f497ead640e51eaaaa Mon Sep 17 00:00:00 2001 From: nicolargo Date: Mon, 6 Feb 2012 18:14:24 +0100 Subject: First version of the experimental BRANCH PSUtil replace StatGrab for CPU, LOAD and MEM --- README.md | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/glances.py | 72 ++++++++++++++--- 2 files changed, 311 insertions(+), 10 deletions(-) mode change 120000 => 100644 README.md diff --git a/README.md b/README.md deleted file mode 120000 index 100b9382..00000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -README \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..3ccb7acc --- /dev/null +++ b/README.md @@ -0,0 +1,248 @@ +[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=nicolargo&url=https://github.com/nicolargo/glances&title=Glances&language=&tags=github&category=software) + +============================= +Glances -- Eye on your system +============================= + +## Description + +Glances is a CLI curses based monitoring tool for GNU/Linux or BSD OS. + +Glances uses the libstatgrab library to get information from your system. +It is developed in Python and uses the python-statgrab lib. + +![screenshot](https://github.com/nicolargo/glances/raw/master/screenshot.png) + +## Installation + +### From package manager + +Packages exist for Arch, Fedora, Redhat ... + +### From source + +Get the latest version: + + $ wget https://github.com/downloads/nicolargo/glances/glances-1.3.7.tar.gz + +Glances use a standard GNU style installer: + + $ tar zxvf glances-1.3.7.tar.gz + $ cd glances-1.3.7 + $ ./configure + $ make + $ sudo make install + +Pre-requisites: + +* Python 2.6+ (not tested with Python 3+) +* python-statgrab 0.5+ (did NOT work with python-statgrab 0.4) + +Notes: For Debian. +The Debian Squeeze repos only include the python-statgrab 0.4. +You had to install the version 0.5 using the following commands: + + $ sudo apt-get install libstatgrab-dev pkg-config python-dev make + $ wget http://ftp.uk.i-scream.org/sites/ftp.i-scream.org/pub/i-scream/pystatgrab/pystatgrab-0.5.tar.gz + $ tar zxvf pystatgrab-0.5.tar.gz + $ cd pystatgrab-0.5/ + $ ./setup.py build + $ sudo ./setup.py install + +Notes: For Ubuntu 10.04 and 10.10. +The instruction to install the version 0.5 are here: +https://github.com/nicolargo/glances/issues/5#issuecomment-3033194 + +## Running + +Easy: + + $ glances.py + +## User guide + +By default, stats are refreshed every second, to change this setting, you can +use the -t option. For exemple to set the refrech rate to 5 seconds: + + $ glances.py -t 5 + +Importants stats are colored: + +* GREEN: stat counter is "OK" +* BLUE: stat counter is "CAREFUL" +* MAGENTA: stat counter is "WARNING" +* RED: stat counter is "CRITICAL" + +When Glances is running, you can press: + +* 'h' to display an help message whith the keys you can press +* 'a' to set the automatic mode. The processes are sorted automatically + + If CPU > 70%, sort by process "CPU consumption" + + If MEM > 70%, sort by process "memory size" + +* 'c' to sort the processes list by CPU consumption +* 'd' Disable or enable the disk IO stats +* 'f' Disable or enable the file system stats +* 'l' Disable or enable the logs +* 'm' to sort the processes list by process size +* 'n' Disable or enable the network interfaces stats +* 'q' Exit + +### Header + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/header.png) + +The header shows the Glances version, the host name and the operating +system name, version and architecture. + +### CPU + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/cpu.png) + +The CPU states are shown as a percentage and for the configured refresh +time. + +If user|kernel|nice CPU is < 50%, then status is set to "OK". + +If user|kernel|nice CPU is > 50%, then status is set to "CAREFUL". + +If user|kernel|nice CPU is > 70%, then status is set to "WARNING". + +If user|kernel|nice CPU is > 90%, then status is set to "CRITICAL". + +### Load + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/load.png) + +On the Nosheep blog, Zach defines the average load: "In short it is the +average sum of the number of processes waiting in the run-queue plus the +number currently executing over 1, 5, and 15 minute time periods." + +Glances gets the number of CPU cores to adapt the alerts. With Glances, +alerts on average load are only set on 5 and 15 mins. + +If average load is < O.7*Core, then status is set to "OK". + +If average load is > O.7*Core, then status is set to "CAREFUL". + +If average load is > 1*Core, then status is set to "WARNING". + +If average load is > 5*Core, then status is set to "CRITICAL". + +### Memory + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/mem.png) + +Glances uses tree columns: memory (RAM), swap and "real". + +Real used memory is: used - cache. + +Real free memory is: free + cache. + +With Glances, alerts are only set for on used swap and real memory. + +If memory is < 50%, then status is set to "OK". + +If memory is > 50%, then status is set to "CAREFUL". + +If memory is > 70%, then status is set to "WARNING". + +If memory is > 90%, then status is set to "CRITICAL". + +### Network bit rate + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/network.png) + +Glances display the network interface bit rate. The unit is adapted +dynamicaly (bits per second, Kbits per second, Mbits per second...). + +Alerts are set only if the network interface maximum speed is available. + +If bitrate is < 50%, then status is set to "OK". + +If bitrate is > 50%, then status is set to "CAREFUL". + +If bitrate is > 70%, then status is set to "WARNING". + +If bitrate is > 90%, then status is set to "CRITICAL". + +For exemple, on a 100 Mbps Ethernet interface, the warning status is set +if the bit rate is higher than 70 Mbps. + +### Disk I/O + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/diskio.png) + +Glances display the disk I/O throughput. The unit is adapted dynamicaly +(bytes per second, Kbytes per second, Mbytes per second...). + +There is no alert on this information. + +### Filesystem + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/fs.png) + +Glances display the total and used filesytem disk space. The unit is +adapted dynamicaly (bytes per second, Kbytes per second, Mbytes per +second...). + +Alerts are set for used disk space: + +If disk used is < 50%, then status is set to "OK". + +If disk used is > 50%, then status is set to "CAREFUL". + +If disk used is > 70%, then status is set to "WARNING". + +If disk used is > 90%, then status is set to "CRITICAL". + +### Processes + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/processlist.png) + +Glances displays a summary and a list of processes. + +By default (or if you hit the 'a' key) the process list is automaticaly +sorted by CPU of memory consumption. + +The number of processes in the list is adapted to the screen size. + +### Logs + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/logs.png) + +A logs list is displayed in the bottom of the screen if (an only if): + +* at least one WARNING or CRITICAL alert was occured. +* space is available in the bottom of the console/terminal + +There is one line per alert with the following information: + +* start date +* end date +* alert name +* (min/avg/max) values + +### Footer + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/footer.png) + +Glances displays a caption and the current time/date. + +## Localisation + +To generate french locale execute as root or sudo : +i18n_francais_generate.sh + +To generate spanish locale execute as root or sudo : +i18n_espanol_generate.sh + +## Todo + +You are welcome to contribute to this software. + +* Packaging for Debian, Ubuntu, BSD... +* Check the needed Python library in the configure.ac +* Add file system stats when the python-statgrab is corrected diff --git a/src/glances.py b/src/glances.py index 8ff0b818..73f709ea 100755 --- a/src/glances.py +++ b/src/glances.py @@ -38,9 +38,18 @@ except KeyboardInterrupt: #===== application = 'glances' -__version__ = "1.3.7" +__version__ = "1.4.0a" gettext.install(application) +try: + import psutil +except: + print _('PsUtil library initialization failed, Glances cannot start.') + print + sys.exit(1) + +# TODO: test PsUtil psutil.__version__ + try: import statgrab except: @@ -294,28 +303,68 @@ class glancesStats(): Update the stats """ - # Get informations from libstatgrab and others... + # Get system informations + + # Host and OS informations try: self.host = statgrab.sg_get_host_info() except: self.host = {} self.system = self.host + + # CPU try: - self.cpu = statgrab.sg_get_cpu_percents() + self.cputime_old except: + self.cputime_old = psutil.cpu_times() + self.cputime_total_old = self.cputime_old.user+self.cputime_old.nice+self.cputime_old.system+self.cputime_old.idle+self.cputime_old.iowait+self.cputime_old.irq+self.cputime_old.softirq self.cpu = {} + else: + try: + self.cputime_new = psutil.cpu_times() + self.cputime_total_new = self.cputime_new.user+self.cputime_new.nice+self.cputime_new.system+self.cputime_new.idle+self.cputime_new.iowait+self.cputime_new.irq+self.cputime_new.softirq + percent = 100/(self.cputime_total_new-self.cputime_total_old) + self.cpu = { 'kernel': (self.cputime_new.system-self.cputime_old.system)*percent, + 'user': (self.cputime_new.user-self.cputime_old.user)*percent, + 'idle': (self.cputime_new.idle-self.cputime_old.idle)*percent, + 'nice': (self.cputime_new.nice-self.cputime_old.nice)*percent } + self.cputime_old = self.cputime_new + self.cputime_total_old = self.cputime_total_new + except: + self.cpu = {} + + # LOAD try: - self.load = statgrab.sg_get_load_stats() + getload = os.getloadavg() + self.load = { 'min1': getload[0], + 'min5': getload[1], + 'min15': getload[2] } except: self.load = {} + + # MEM + try: + # Only for Linux + cachemem = psutil.cached_phymem()+psutil.phymem_buffers() + except: + cachemem = 0 try: - self.mem = statgrab.sg_get_mem_stats() + phymem = psutil.phymem_usage() + self.mem = { 'cache': cachemem, + 'total': phymem.total, + 'free': phymem.free, + 'used': phymem.used } except: self.mem = {} try: - self.memswap = statgrab.sg_get_swap_stats() + virtmem = psutil.virtmem_usage() + self.memswap = { 'total': virtmem.total, + 'free': virtmem.free, + 'used': virtmem.used } except: self.memswap = {} + + # NET try: self.networkinterface = statgrab.sg_get_network_iface_stats() except: @@ -324,15 +373,21 @@ class glancesStats(): self.network = statgrab.sg_get_network_io_stats_diff() except: self.network = {} + + # DISK IO try: self.diskio = statgrab.sg_get_disk_io_stats_diff() except: self.diskio = {} + + # FILE SYSTEM try: # Replace the bugged self.fs = statgrab.sg_get_fs_stats() self.fs = self.glancesgrabfs.get() except: self.fs = {} + + # PROCESS try: self.processcount = statgrab.sg_get_process_count() except: @@ -345,9 +400,8 @@ class glancesStats(): # Get the current date/time self.now = datetime.datetime.now() - # Get the number of core (CPU) - # Used to display load alerts - self.core_number = multiprocessing.cpu_count() + # Get the number of core (CPU) (Used to display load alerts) + self.core_number = psutil.NUM_CPUS def end(self): -- cgit v1.2.3 From 89b5ebc8b4bac47bd67ef2443d64de6809fc2e79 Mon Sep 17 00:00:00 2001 From: nicolargo Date: Mon, 6 Feb 2012 18:54:31 +0100 Subject: DISK IO --- src/glances.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/glances.py b/src/glances.py index 73f709ea..99efff0c 100755 --- a/src/glances.py +++ b/src/glances.py @@ -376,9 +376,23 @@ class glancesStats(): # DISK IO try: - self.diskio = statgrab.sg_get_disk_io_stats_diff() + self.diskio_old except: - self.diskio = {} + self.diskio_old = psutil.disk_io_counters(True) + self.diskio = [] + else: + try: + self.diskio_new = psutil.disk_io_counters(True) + self.diskio = [] + for disk in self.diskio_new: + diskstat = {} + diskstat['disk_name'] = disk + diskstat['read_bytes'] = self.diskio_new[disk].read_bytes - self.diskio_old[disk].read_bytes + diskstat['write_bytes'] = self.diskio_new[disk].write_bytes - self.diskio_old[disk].write_bytes + self.diskio.append(diskstat) + self.diskio_old = self.diskio_new + except: + self.diskio = [] # FILE SYSTEM try: @@ -992,7 +1006,7 @@ class glancesScreen(): # Adapt the maximum disk to the screen disk = 0 for disk in range(0, min(screen_y-self.diskio_y-3, len(diskio))): - elapsed_time = max(1, diskio[disk]['systime']) + elapsed_time = max(1, self.__refresh_time) self.term_window.addnstr(self.diskio_y+1+disk, self.diskio_x, diskio[disk]['disk_name']+':', 8) self.term_window.addnstr(self.diskio_y+1+disk, self.diskio_x+10, self.__autoUnit(diskio[disk]['write_bytes']/elapsed_time) + "B", 8) self.term_window.addnstr(self.diskio_y+1+disk, self.diskio_x+20, self.__autoUnit(diskio[disk]['read_bytes']/elapsed_time) + "B", 8) -- cgit v1.2.3 From e1e8a99276d5a0f17a274750fea662aa12c210ab Mon Sep 17 00:00:00 2001 From: nicolargo Date: Tue, 7 Feb 2012 11:50:05 +0100 Subject: Replace FsGrab by PsUtil --- src/glances.py | 131 +++++++++++++++++++++++++-------------------------------- 1 file changed, 58 insertions(+), 73 deletions(-) diff --git a/src/glances.py b/src/glances.py index 99efff0c..2b89171c 100755 --- a/src/glances.py +++ b/src/glances.py @@ -218,7 +218,7 @@ class glancesLogs(): class glancesGrabFs(): """ - Get FS stats: idem as structure http://www.i-scream.org/libstatgrab/docs/sg_get_fs_stats.3.html + Get FS stats """ def __init__(self): @@ -229,45 +229,28 @@ class glancesGrabFs(): """ Update the stats """ - - # Reset the list - self.fs_list = [] - + # Ignore the following fs - ignore_fsname = ('none', 'gvfs-fuse-daemon', 'fusectl', 'cgroup') + ignore_fsname = ('', 'none', 'gvfs-fuse-daemon', 'fusectl', 'cgroup') ignore_fstype = ('binfmt_misc', 'devpts', 'iso9660', 'none', 'proc', 'sysfs', 'usbfs') + + # Reset the list + self.fs_list = [] # Open the current mounted FS - mtab = open("/etc/mtab", "r") - for line in mtab.readlines(): - if line.split()[0] in ignore_fsname: continue - if line.split()[2] in ignore_fstype: continue - # Get FS stats + fs_stat = psutil.disk_partitions(True) + for fs in range(len(fs_stat)): fs_current = {} - fs_name = self.__getmount__(line.split()[1]) - fs_stats = os.statvfs(fs_name) - # Build the list - fs_current['device_name'] = str(line.split()[0]) - fs_current['fs_type'] = str(line.split()[2]) - fs_current['mnt_point'] = str(fs_name) - fs_current['size'] = float(fs_stats.f_blocks) * long(fs_stats.f_frsize) - fs_current['used'] = float(fs_stats.f_blocks - fs_stats.f_bfree) * long(fs_stats.f_frsize) - fs_current['avail'] = float(fs_stats.f_bfree) * long(fs_stats.f_frsize) - self.fs_list.append(fs_current) - mtab.close() - - - def __getmount__(self, path): - """ - Return the real root path of a file - Exemple: /home/nicolargo can return /home or / - """ - path = os.path.realpath(os.path.abspath(path)) - while path != os.path.sep: - if os.path.ismount(path): - return path - path = os.path.abspath(os.path.join(path, os.pardir)) - return path + fs_current['device_name'] = fs_stat[fs].device + if fs_current['device_name'] in ignore_fsname: continue + fs_current['fs_type'] = fs_stat[fs].fstype + if fs_current['fs_type'] in ignore_fstype: continue + fs_current['mnt_point'] = fs_stat[fs].mountpoint + fs_usage = psutil.disk_usage(fs_current['mnt_point']) + fs_current['size'] = fs_usage.total + fs_current['used'] = fs_usage.used + fs_current['avail'] = fs_usage.free + self.fs_list.append(fs_current) def get(self): @@ -366,13 +349,23 @@ class glancesStats(): # NET try: - self.networkinterface = statgrab.sg_get_network_iface_stats() + self.network_old except: - self.networkinterface = {} - try: - self.network = statgrab.sg_get_network_io_stats_diff() - except: - self.network = {} + self.network_old = psutil.network_io_counters(True) + self.network = [] + else: + try: + self.network_new = psutil.network_io_counters(True) + self.network = [] + for net in self.network_new: + netstat = {} + netstat['interface_name'] = net + netstat['rx'] = self.network_new[net].bytes_recv - self.network_old[net].bytes_recv + netstat['tx'] = self.network_new[net].bytes_sent - self.network_old[net].bytes_sent + self.network.append(netstat) + self.network_old = self.network_new + except: + self.network = [] # DISK IO try: @@ -454,11 +447,7 @@ class glancesStats(): def getMemSwap(self): return self.memswap - - - def getNetworkInterface(self): - return self.networkinterface - + def getNetwork(self): return self.network @@ -626,14 +615,23 @@ class glancesScreen(): 560745673 -> 561M ... """ - if val >= 1073741824L: - return "%.1fG" % (val / 1073741824L) - elif val >= 1048576L: - return "%.1fM" % (val / 1048576L) - elif val >= 1024: - return "%.1fK" % (val / 1024) - else: - return str(int(val)) + symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + prefix = { + 'Y': 1208925819614629174706176L, + 'Z': 1180591620717411303424L, + 'E': 1152921504606846976L, + 'P': 1125899906842624L, + 'T': 1099511627776L, + 'G': 1073741824, + 'M': 1048576, + 'K': 1024 + } + for key in reversed(symbols): + if val >= prefix[key]: + value = float(val) / prefix[key] + return '%.1f%s' % (value, key) + return "%s" % val + def __getAlert(self, current = 0, max = 100): # If current < CAREFUL of max then alert = OK @@ -760,7 +758,7 @@ class glancesScreen(): self.displayCpu(stats.getCpu()) self.displayLoad(stats.getLoad(), stats.getCore()) self.displayMem(stats.getMem(), stats.getMemSwap()) - network_count = self.displayNetwork(stats.getNetwork(), stats.getNetworkInterface()) + network_count = self.displayNetwork(stats.getNetwork()) diskio_count = self.displayDiskIO(stats.getDiskIO(), self.network_y + network_count) fs_count = self.displayFs(stats.getFs(), self.network_y + network_count + diskio_count) log_count = self.displayLog(self.network_y + network_count + diskio_count + fs_count) @@ -950,27 +948,18 @@ class glancesScreen(): self.term_window.addnstr(self.mem_y+3, self.mem_x+30, str((mem['free']+mem['cache'])/1048576), 8) - def displayNetwork(self, network, networkinterface): + def displayNetwork(self, network): """ Display the network interface bitrate Return the number of interfaces """ # Network interfaces bitrate - if (not network or not networkinterface or not self.network_tag): + if (not network or not self.network_tag): return 0 screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] if ((screen_y > self.network_y+3) and (screen_x > self.network_x+28)): - # Get the speed of the network interface - # TODO: optimize... - speed = {} - for i in range(0, len(networkinterface)): - # Strange think, on Ubuntu, libstatgrab return 65525 for my ethernet card... - if networkinterface[i]['speed'] == 65535: - speed[networkinterface[i]['interface_name']] = 0 - else: - speed[networkinterface[i]['interface_name']] = networkinterface[i]['speed']*1000000 # Network interfaces bitrate self.term_window.addnstr(self.network_y, self.network_x, _("Net rate"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) self.term_window.addnstr(self.network_y, self.network_x+10, _("Rx/ps"), 8) @@ -978,14 +967,10 @@ class glancesScreen(): # Adapt the maximum interface to the screen ret = 2 for i in range(0, min(screen_y-self.network_y-3, len(network))): - try: - speed[network[i]['interface_name']] - except: - break - elapsed_time = max (1, network[i]['systime']) + elapsed_time = max (1, self.__refresh_time) self.term_window.addnstr(self.network_y+1+i, self.network_x, network[i]['interface_name']+':', 8) - self.term_window.addnstr(self.network_y+1+i, self.network_x+10, self.__autoUnit(network[i]['rx']/elapsed_time*8) + "b", 8, self.__getNetColor(network[i]['rx']/elapsed_time*8, speed[network[i]['interface_name']])) - self.term_window.addnstr(self.network_y+1+i, self.network_x+20, self.__autoUnit(network[i]['tx']/elapsed_time*8) + "b", 8, self.__getNetColor(network[i]['tx']/elapsed_time*8, speed[network[i]['interface_name']])) + self.term_window.addnstr(self.network_y+1+i, self.network_x+10, self.__autoUnit(network[i]['rx']/elapsed_time*8) + "b", 8) + self.term_window.addnstr(self.network_y+1+i, self.network_x+20, self.__autoUnit(network[i]['tx']/elapsed_time*8) + "b", 8) ret = ret + 1 return ret return 0 -- cgit v1.2.3 From 73e92da8fc3e4af2103e093f68e45e957fb28212 Mon Sep 17 00:00:00 2001 From: nicolargo Date: Wed, 8 Feb 2012 14:32:53 +0100 Subject: PsUtil - Add process stat --- src/glances.py | 113 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 21 deletions(-) diff --git a/src/glances.py b/src/glances.py index 2b89171c..2f072d13 100755 --- a/src/glances.py +++ b/src/glances.py @@ -1,8 +1,8 @@ #!/usr/bin/env python # -# Glances is a simple CLI monitoring tool based on libstatgrab +# Glances is a simple textual monitoring tool # -# Pre-requisites: python-statgrab 0.5 or > +# Pre-requisites: Python 2.6+ and PsUtil 0.4.0+ # # Copyright (C) Nicolargo 2012 # @@ -23,6 +23,7 @@ from __future__ import generators try: import os + import platform import getopt import sys import signal @@ -45,10 +46,24 @@ try: import psutil except: print _('PsUtil library initialization failed, Glances cannot start.') + print _('On Debian/Ubuntu, you can try (as root):') + print _('# apt-get install python-dev python-pip') + print _('# pip install psutil') print sys.exit(1) - -# TODO: test PsUtil psutil.__version__ + +try: + # This function is available in the 0.4.0+ version of PsUtil + psutil.disk_io_counters() +except: + print _('PsUtil version 0.4.0 or higher is needed.') + print _('On Debian/Ubuntu, you can try (as root):') + print _('# apt-get install python-dev python-pip') + print _('# pip install --upgrade psutil') + print + sys.exit(1) + +# TODO: Remove after test PsUtil psutil.__version__ try: import statgrab @@ -222,7 +237,7 @@ class glancesGrabFs(): """ def __init__(self): - self.__update__() + pass def __update__(self): @@ -276,10 +291,8 @@ class glancesStats(): try: self.glancesgrabfs = glancesGrabFs() except: - self.glancesgrabfs = {} - - # Do the first update - self.__update__() + self.glancesgrabfs = {} + def __update__(self): """ @@ -289,13 +302,28 @@ class glancesStats(): # Get system informations # Host and OS informations + self.host = {} + self.host['hostname'] = platform.node() + self.host['platform'] = platform.architecture()[0] + self.host['processor'] = platform.processor() + self.host['os_name'] = platform.system() try: - self.host = statgrab.sg_get_host_info() + if (self.host['os_name'] == "Linux" or self.host['os_name'] == "FreeBSD"): + os_version = platform.linux_distribution() + self.host['os_version'] = os_version[0]+" "+os_version[1]+" ("+os_version[2]+")" + elif (self.host['os_name'] == "Windows"): + os_version = platform.win32_ver() + self.host['os_version'] = os_version[0]+" "+os_version[2] + elif (self.host['os_name'] == "Darwin"): + os_version = platform.mac_ver() + self.host['os_version'] = os_version[0] + else: + self.host['os_version'] = "" except: - self.host = {} - self.system = self.host + self.host['os_version'] = "" # CPU + percent = 0 try: self.cputime_old except: @@ -389,20 +417,61 @@ class glancesStats(): # FILE SYSTEM try: - # Replace the bugged self.fs = statgrab.sg_get_fs_stats() self.fs = self.glancesgrabfs.get() except: self.fs = {} # PROCESS + # Initialiation of the processes list + self.processcount = {'zombie': 0, 'running': 0, 'total': 0, 'stopped': 0, 'sleeping': 0, 'disk sleep': 0} try: - self.processcount = statgrab.sg_get_process_count() + self.process except: - self.processcount = {} + self.process = [] + try: - self.process = statgrab.sg_get_process_stats() + self.process_all except: - self.process = {} + self.process_all = [proc for proc in psutil.process_iter()] + for proc in self.process_all[:]: + # Global stats + try: + self.processcount[str(proc.status)] + except: + pass + else: + self.processcount[str(proc.status)] += 1 + self.processcount['total'] += 1 + # A first value is needed to compute the CPU percent + try: + proc.get_cpu_percent(interval=0) + except: + pass + else: + proc._before = proc + else: + self.process = [] + for proc in self.process_all[:]: + # Global stats + try: + self.processcount[str(proc.status)] + except: + pass + else: + self.processcount[str(proc.status)] += 1 + self.processcount['total'] += 1 + # Per process stats + try: + procstat = {} + procstat['process_name'] = proc.name + procstat['proctitle'] = " ".join(str(i) for i in proc.cmdline) + procstat['proc_size'] = proc.get_memory_info().vms + procstat['proc_resident'] = proc.get_memory_info().rss + procstat['cpu_percent'] = proc._before.get_cpu_percent(interval=0) + self.process.append(procstat) + except: + pass + del(self.process_all) # Get the current date/time self.now = datetime.datetime.now() @@ -426,7 +495,7 @@ class glancesStats(): def getSystem(self): - return self.system + return self.host def getCpu(self): @@ -1015,8 +1084,8 @@ class glancesScreen(): mounted = 0 for mounted in range(0, min(screen_y-self.fs_y-3, len(fs))): self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x, fs[mounted]['mnt_point'], 8) - self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+10, self.__autoUnit(fs[mounted]['size']), 8) - self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+20, self.__autoUnit(fs[mounted]['used']), 8, self.__getFsColor(fs[mounted]['used'], fs[mounted]['size'])) + self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+10, self.__autoUnit(fs[mounted]['size']) + "B", 8) + self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+20, self.__autoUnit(fs[mounted]['used']) + "B", 8, self.__getFsColor(fs[mounted]['used'], fs[mounted]['size'])) return mounted+3 return 0 @@ -1058,7 +1127,7 @@ class glancesScreen(): def displayProcess(self, processcount, processlist, log_count = 0): # Process - if (not processcount or not processlist): + if (not processcount): return 0 screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] @@ -1083,6 +1152,8 @@ class glancesScreen(): self.term_window.addnstr(self.process_y+1, process_x+30,str(processcount['sleeping']), 8) self.term_window.addnstr(self.process_y+1, process_x+40,str(processcount['stopped']+stats.getProcessCount()['zombie']), 8) # Display the process detail + if (not processlist): + return 3 if ((screen_y > self.process_y+6) and (screen_x > process_x+49)): # Processes detail -- cgit v1.2.3 From 2799eb8369f55391587e4d6a4567a68c58b35aad Mon Sep 17 00:00:00 2001 From: nicolargo Date: Sat, 11 Feb 2012 15:33:31 +0100 Subject: Change the help windows --- NEWS | 8 ++ README.md | 249 +-------------------------------------------------------- src/glances.py | 249 +++++++++++++++++++++++++++++++++------------------------ 3 files changed, 153 insertions(+), 353 deletions(-) mode change 100644 => 120000 README.md diff --git a/NEWS b/NEWS index 15d68d23..567e378c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +Version 1.4 +=========== + + * No more StatGrab, welcome to the PsUtil library ! + * Sort by Process name ('p' key) + * Only major stats (CPU, Load and memory) use background colors + * Improve operating system name + Version 1.3.7 ============= diff --git a/README.md b/README.md deleted file mode 100644 index 3ccb7acc..00000000 --- a/README.md +++ /dev/null @@ -1,248 +0,0 @@ -[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=nicolargo&url=https://github.com/nicolargo/glances&title=Glances&language=&tags=github&category=software) - -============================= -Glances -- Eye on your system -============================= - -## Description - -Glances is a CLI curses based monitoring tool for GNU/Linux or BSD OS. - -Glances uses the libstatgrab library to get information from your system. -It is developed in Python and uses the python-statgrab lib. - -![screenshot](https://github.com/nicolargo/glances/raw/master/screenshot.png) - -## Installation - -### From package manager - -Packages exist for Arch, Fedora, Redhat ... - -### From source - -Get the latest version: - - $ wget https://github.com/downloads/nicolargo/glances/glances-1.3.7.tar.gz - -Glances use a standard GNU style installer: - - $ tar zxvf glances-1.3.7.tar.gz - $ cd glances-1.3.7 - $ ./configure - $ make - $ sudo make install - -Pre-requisites: - -* Python 2.6+ (not tested with Python 3+) -* python-statgrab 0.5+ (did NOT work with python-statgrab 0.4) - -Notes: For Debian. -The Debian Squeeze repos only include the python-statgrab 0.4. -You had to install the version 0.5 using the following commands: - - $ sudo apt-get install libstatgrab-dev pkg-config python-dev make - $ wget http://ftp.uk.i-scream.org/sites/ftp.i-scream.org/pub/i-scream/pystatgrab/pystatgrab-0.5.tar.gz - $ tar zxvf pystatgrab-0.5.tar.gz - $ cd pystatgrab-0.5/ - $ ./setup.py build - $ sudo ./setup.py install - -Notes: For Ubuntu 10.04 and 10.10. -The instruction to install the version 0.5 are here: -https://github.com/nicolargo/glances/issues/5#issuecomment-3033194 - -## Running - -Easy: - - $ glances.py - -## User guide - -By default, stats are refreshed every second, to change this setting, you can -use the -t option. For exemple to set the refrech rate to 5 seconds: - - $ glances.py -t 5 - -Importants stats are colored: - -* GREEN: stat counter is "OK" -* BLUE: stat counter is "CAREFUL" -* MAGENTA: stat counter is "WARNING" -* RED: stat counter is "CRITICAL" - -When Glances is running, you can press: - -* 'h' to display an help message whith the keys you can press -* 'a' to set the automatic mode. The processes are sorted automatically - - If CPU > 70%, sort by process "CPU consumption" - - If MEM > 70%, sort by process "memory size" - -* 'c' to sort the processes list by CPU consumption -* 'd' Disable or enable the disk IO stats -* 'f' Disable or enable the file system stats -* 'l' Disable or enable the logs -* 'm' to sort the processes list by process size -* 'n' Disable or enable the network interfaces stats -* 'q' Exit - -### Header - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/header.png) - -The header shows the Glances version, the host name and the operating -system name, version and architecture. - -### CPU - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/cpu.png) - -The CPU states are shown as a percentage and for the configured refresh -time. - -If user|kernel|nice CPU is < 50%, then status is set to "OK". - -If user|kernel|nice CPU is > 50%, then status is set to "CAREFUL". - -If user|kernel|nice CPU is > 70%, then status is set to "WARNING". - -If user|kernel|nice CPU is > 90%, then status is set to "CRITICAL". - -### Load - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/load.png) - -On the Nosheep blog, Zach defines the average load: "In short it is the -average sum of the number of processes waiting in the run-queue plus the -number currently executing over 1, 5, and 15 minute time periods." - -Glances gets the number of CPU cores to adapt the alerts. With Glances, -alerts on average load are only set on 5 and 15 mins. - -If average load is < O.7*Core, then status is set to "OK". - -If average load is > O.7*Core, then status is set to "CAREFUL". - -If average load is > 1*Core, then status is set to "WARNING". - -If average load is > 5*Core, then status is set to "CRITICAL". - -### Memory - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/mem.png) - -Glances uses tree columns: memory (RAM), swap and "real". - -Real used memory is: used - cache. - -Real free memory is: free + cache. - -With Glances, alerts are only set for on used swap and real memory. - -If memory is < 50%, then status is set to "OK". - -If memory is > 50%, then status is set to "CAREFUL". - -If memory is > 70%, then status is set to "WARNING". - -If memory is > 90%, then status is set to "CRITICAL". - -### Network bit rate - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/network.png) - -Glances display the network interface bit rate. The unit is adapted -dynamicaly (bits per second, Kbits per second, Mbits per second...). - -Alerts are set only if the network interface maximum speed is available. - -If bitrate is < 50%, then status is set to "OK". - -If bitrate is > 50%, then status is set to "CAREFUL". - -If bitrate is > 70%, then status is set to "WARNING". - -If bitrate is > 90%, then status is set to "CRITICAL". - -For exemple, on a 100 Mbps Ethernet interface, the warning status is set -if the bit rate is higher than 70 Mbps. - -### Disk I/O - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/diskio.png) - -Glances display the disk I/O throughput. The unit is adapted dynamicaly -(bytes per second, Kbytes per second, Mbytes per second...). - -There is no alert on this information. - -### Filesystem - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/fs.png) - -Glances display the total and used filesytem disk space. The unit is -adapted dynamicaly (bytes per second, Kbytes per second, Mbytes per -second...). - -Alerts are set for used disk space: - -If disk used is < 50%, then status is set to "OK". - -If disk used is > 50%, then status is set to "CAREFUL". - -If disk used is > 70%, then status is set to "WARNING". - -If disk used is > 90%, then status is set to "CRITICAL". - -### Processes - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/processlist.png) - -Glances displays a summary and a list of processes. - -By default (or if you hit the 'a' key) the process list is automaticaly -sorted by CPU of memory consumption. - -The number of processes in the list is adapted to the screen size. - -### Logs - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/logs.png) - -A logs list is displayed in the bottom of the screen if (an only if): - -* at least one WARNING or CRITICAL alert was occured. -* space is available in the bottom of the console/terminal - -There is one line per alert with the following information: - -* start date -* end date -* alert name -* (min/avg/max) values - -### Footer - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/footer.png) - -Glances displays a caption and the current time/date. - -## Localisation - -To generate french locale execute as root or sudo : -i18n_francais_generate.sh - -To generate spanish locale execute as root or sudo : -i18n_espanol_generate.sh - -## Todo - -You are welcome to contribute to this software. - -* Packaging for Debian, Ubuntu, BSD... -* Check the needed Python library in the configure.ac -* Add file system stats when the python-statgrab is corrected diff --git a/README.md b/README.md new file mode 120000 index 00000000..100b9382 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +README \ No newline at end of file diff --git a/src/glances.py b/src/glances.py index 2f072d13..19fce56f 100755 --- a/src/glances.py +++ b/src/glances.py @@ -39,7 +39,7 @@ except KeyboardInterrupt: #===== application = 'glances' -__version__ = "1.4.0a" +__version__ = "1.4a" gettext.install(application) try: @@ -442,8 +442,9 @@ class glancesStats(): else: self.processcount[str(proc.status)] += 1 self.processcount['total'] += 1 - # A first value is needed to compute the CPU percent + # Per process stats try: + # A first value is needed to compute the CPU percent proc.get_cpu_percent(interval=0) except: pass @@ -468,6 +469,8 @@ class glancesStats(): procstat['proc_size'] = proc.get_memory_info().vms procstat['proc_resident'] = proc.get_memory_info().rss procstat['cpu_percent'] = proc._before.get_cpu_percent(interval=0) + procstat['diskio_read'] = proc.get_io_counters().read_bytes - proc._before.get_io_counters().read_bytes + procstat['diskio_write'] = proc.get_io_counters().write_bytes - proc._before.get_io_counters().write_bytes self.process.append(procstat) except: pass @@ -519,15 +522,15 @@ class glancesStats(): def getNetwork(self): - return self.network + return sorted(self.network, key=lambda network: network['interface_name']) def getDiskIO(self): - return self.diskio + return sorted(self.diskio, key=lambda diskio: diskio['disk_name']) def getFs(self): - return self.fs + return sorted(self.fs, key=lambda fs: fs['mnt_point']) def getProcessCount(self): @@ -539,6 +542,7 @@ class glancesStats(): Return the sorted process list """ + sortedReverse = True if sortedby == 'auto': # If global Mem > 70% sort by process size # Else sort by cpu comsoption @@ -546,7 +550,10 @@ class glancesStats(): if ( self.mem['total'] != 0): if ( ( (self.mem['used'] - self.mem['cache']) * 100 / self.mem['total']) > limits.getSTDWarning()): sortedby = 'proc_size' - return sorted(self.process, key=lambda process: process[sortedby], reverse=True) + elif sortedby == 'process_name': + sortedReverse = False + + return sorted(self.process, key=lambda process: process[sortedby], reverse = sortedReverse) def getNow(self): @@ -579,7 +586,7 @@ class glancesScreen(): self.fs_x = 0 ; self.fs_y = -1 self.process_x = 30; self.process_y = 9 self.log_x = 0 ; self.log_y = -1 - self.help_x = 30; self.help_y = 12 + self.help_x = 0; self.help_y = 0 self.now_x = 79; self.now_y = 3 self.caption_x = 0 ; self.caption_y = 3 @@ -600,7 +607,7 @@ class glancesScreen(): # Init colors self.hascolors = False - if curses.has_colors(): + if (curses.has_colors() and curses.COLOR_PAIRS > 8): self.hascolors = True # Init FG color BG color curses.init_pair(1, curses.COLOR_WHITE, -1) @@ -608,8 +615,10 @@ class glancesScreen(): curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_GREEN) curses.init_pair(4, curses.COLOR_WHITE, curses.COLOR_BLUE) curses.init_pair(5, curses.COLOR_WHITE, curses.COLOR_MAGENTA) - curses.init_pair(6, curses.COLOR_WHITE, curses.COLOR_CYAN) - curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_YELLOW) + curses.init_pair(6, curses.COLOR_RED, -1) + curses.init_pair(7, curses.COLOR_GREEN, -1) + curses.init_pair(8, curses.COLOR_BLUE, -1) + curses.init_pair(9, curses.COLOR_MAGENTA, -1) else: self.hascolors = False @@ -622,6 +631,10 @@ class glancesScreen(): self.ifCAREFUL_color = curses.color_pair(4)|curses.A_BOLD self.ifWARNING_color = curses.color_pair(5)|curses.A_BOLD self.ifCRITICAL_color = curses.color_pair(2)|curses.A_BOLD + self.default_color2 = curses.color_pair(7)|curses.A_BOLD + self.ifCAREFUL_color2 = curses.color_pair(8)|curses.A_BOLD + self.ifWARNING_color2 = curses.color_pair(9)|curses.A_BOLD + self.ifCRITICAL_color2 = curses.color_pair(6)|curses.A_BOLD else: # B&W text styles self.no_color = curses.A_NORMAL @@ -629,8 +642,12 @@ class glancesScreen(): self.ifCAREFUL_color = curses.A_UNDERLINE self.ifWARNING_color = curses.A_BOLD self.ifCRITICAL_color = curses.A_REVERSE + self.default_color2 = curses.A_NORMAL + self.ifCAREFUL_color2 = curses.A_UNDERLINE + self.ifWARNING_color2 = curses.A_BOLD + self.ifCRITICAL_color2 = curses.A_REVERSE - # Define the colors list (hash table) + # Define the colors list (hash table) for logged stats self.__colors_list = { # CAREFUL WARNING CRITICAL 'DEFAULT': self.no_color, @@ -640,25 +657,26 @@ class glancesScreen(): 'CRITICAL': self.ifCRITICAL_color } + # Define the colors list (hash table) for non logged stats + self.__colors_list2 = { + # CAREFUL WARNING CRITICAL + 'DEFAULT': self.no_color, + 'OK': self.default_color2, + 'CAREFUL': self.ifCAREFUL_color2, + 'WARNING': self.ifWARNING_color2, + 'CRITICAL': self.ifCRITICAL_color2 + } + # By default all the stats are displayed self.network_tag = True self.diskio_tag = True self.fs_tag = True self.log_tag = True + self.help_tag = False # Init main window self.term_window = self.screen.subwin(0, 0) - # Init help panel - # TODO: pb when size of the screen < 22 lines - screen_x = self.screen.getmaxyx()[1] - screen_y = self.screen.getmaxyx()[0] - if (screen_x > (self.term_w-self.help_x) and - (screen_y > (self.term_h-self.help_y-2))): - term_help = self.screen.subwin(self.term_h-self.help_y-2, self.term_w-self.help_x, self.help_y, self.help_x) - self.panel_help = curses.panel.new_panel(term_help) - self.hideHelp() - # Init refresh time self.__refresh_time = refresh_time @@ -725,8 +743,18 @@ class glancesScreen(): def __getColor(self, current = 0, max = 100): + """ + Return colors for logged stats + """ return self.__colors_list[self.__getAlert(current, max)] + + def __getColor2(self, current = 0, max = 100): + """ + Return colors for non logged stats + """ + return self.__colors_list2[self.__getAlert(current, max)] + def __getCpuAlert(self, current = 0, max = 100): return self.__getAlert(current, max) @@ -765,11 +793,15 @@ class glancesScreen(): def __getNetColor(self, current = 0, max = 100): - return self.__getColor(current, max) + return self.__getColor2(current, max) def __getFsColor(self, current = 0, max = 100): - return self.__getColor(current, max) + return self.__getColor2(current, max) + + + def __getProcessColor(self, current = 0, max = 100): + return self.__getColor2(current, max) def __catchKey(self): @@ -796,10 +828,7 @@ class glancesScreen(): self.fs_tag = not self.fs_tag elif (self.pressedkey == 104): # 'h' > Enable/Disable help - if (self.panel_help.hidden()): - self.showHelp() - else: - self.hideHelp() + self.help_tag = not self.help_tag elif (self.pressedkey == 108): # 'l' > Enable/Disable logs list self.log_tag = not self.log_tag @@ -809,6 +838,9 @@ class glancesScreen(): elif (self.pressedkey == 110): # 'n' > Enable/Disable network stats self.network_tag = not self.network_tag + elif (self.pressedkey == 112): + # 'p' > Sort process list by Process name + self.setProcessSortedBy('process_name') # Return the key code return self.pressedkey @@ -834,10 +866,8 @@ class glancesScreen(): self.displayProcess(stats.getProcessCount(), stats.getProcessList(screen.getProcessSortedBy()), log_count) self.displayCaption() self.displayNow(stats.getNow()) + self.displayHelp() - # Display help panel - if (not self.panel_help.hidden()): - self.displayHelp() def erase(self): # Erase the content of the screen @@ -868,43 +898,6 @@ class glancesScreen(): curses.napms(100) - def displayHelp(self): - """ - Display the help panel (active| desactive with the 'h' key) - """ - screen_x = self.screen.getmaxyx()[1] - screen_y = self.screen.getmaxyx()[0] - if ((screen_y > 23) - and (screen_x > 79)): - helpWindow = self.panel_help.window() - helpWindow.resize(self.term_h-self.help_y-2, self.term_w-self.help_x) - helpWindow.clear() - msg = _("Glances help (press 'h' to hide)") - helpWindow.addnstr(1, 2, _("'a'\tto sort processes automatically"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(2, 2, _("'c'\tto sort processes by CPU consumption"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(3, 2, _("'d'\tto disable|enable the disk IO stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(4, 2, _("'f'\tto disable|enable the file system stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(5, 2, _("'l'\tto display|hide the logs messages"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(6, 2, _("'m'\tto sort processes by process size"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(7, 2, _("'n'\tto disable|enable the network interfaces stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(8, 2, _("'q'\tto exit Glances"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.box() - - - def showHelp(self): - """ - Show the help panel - """ - self.panel_help.show() - - - def hideHelp(self): - """ - Hide the help panel - """ - self.panel_help.hide() - - def displayHost(self, host): # Host information if (not host): @@ -931,14 +924,17 @@ class glancesScreen(): def displayCpu(self, cpu): # CPU % - if (not cpu): - return 0 screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] if ((screen_y > self.cpu_y+6) and (screen_x > self.cpu_x+18)): self.term_window.addnstr(self.cpu_y, self.cpu_x, _("Cpu"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) self.term_window.addnstr(self.cpu_y, self.cpu_x+10,"%", 8) + + if (not cpu): + self.term_window.addnstr(self.cpu_y+1, self.cpu_x, _("Compute data..."), 15) + return 0 + self.term_window.addnstr(self.cpu_y+1, self.cpu_x, _("User:"), 8) self.term_window.addnstr(self.cpu_y+2, self.cpu_x, _("Kernel:"), 8) self.term_window.addnstr(self.cpu_y+3, self.cpu_x, _("Nice:"), 8) @@ -992,7 +988,7 @@ class glancesScreen(): screen_y = self.screen.getmaxyx()[0] if ((screen_y > self.mem_y+5) and (screen_x > self.mem_x+38)): - self.term_window.addnstr(self.mem_y, self.mem_x, _("Mem MB"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) + self.term_window.addnstr(self.mem_y, self.mem_x, _("Mem B"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) self.term_window.addnstr(self.mem_y, self.mem_x+10,_("Mem"), 8) self.term_window.addnstr(self.mem_y, self.mem_x+20,_("Swap"), 8) self.term_window.addnstr(self.mem_y, self.mem_x+30,_("Real"), 8) @@ -1000,21 +996,21 @@ class glancesScreen(): self.term_window.addnstr(self.mem_y+2, self.mem_x, _("Used:"), 8) self.term_window.addnstr(self.mem_y+3, self.mem_x, _("Free:"), 8) - self.term_window.addnstr(self.mem_y+1, self.mem_x+10, str(mem['total']/1048576), 8) - self.term_window.addnstr(self.mem_y+2, self.mem_x+10, str(mem['used']/1048576), 8) - self.term_window.addnstr(self.mem_y+3, self.mem_x+10, str(mem['free']/1048576), 8) + self.term_window.addnstr(self.mem_y+1, self.mem_x+10, self.__autoUnit(mem['total']), 8) + self.term_window.addnstr(self.mem_y+2, self.mem_x+10, self.__autoUnit(mem['used']), 8) + self.term_window.addnstr(self.mem_y+3, self.mem_x+10, self.__autoUnit(mem['free']), 8) alert = self.__getMemAlert(memswap['used'], memswap['total']) logs.add(alert, "MEM swap", memswap['used']/1048576) - self.term_window.addnstr(self.mem_y+1, self.mem_x+20, str(memswap['total']/1048576), 8) - self.term_window.addnstr(self.mem_y+2, self.mem_x+20, str(memswap['used']/1048576), 8, self.__colors_list[alert]) - self.term_window.addnstr(self.mem_y+3, self.mem_x+20, str(memswap['free']/1048576), 8) + self.term_window.addnstr(self.mem_y+1, self.mem_x+20, self.__autoUnit(memswap['total']), 8) + self.term_window.addnstr(self.mem_y+2, self.mem_x+20, self.__autoUnit(memswap['used']), 8, self.__colors_list[alert]) + self.term_window.addnstr(self.mem_y+3, self.mem_x+20, self.__autoUnit(memswap['free']), 8) alert = self.__getMemAlert(mem['used']-mem['cache'], mem['total']) logs.add(alert, "MEM real", (mem['used']-mem['cache'])/1048576) self.term_window.addnstr(self.mem_y+1, self.mem_x+30, "-", 8) - self.term_window.addnstr(self.mem_y+2, self.mem_x+30, str((mem['used']-mem['cache'])/1048576), 8, self.__colors_list[alert]) - self.term_window.addnstr(self.mem_y+3, self.mem_x+30, str((mem['free']+mem['cache'])/1048576), 8) + self.term_window.addnstr(self.mem_y+2, self.mem_x+30, self.__autoUnit((mem['used']-mem['cache'])), 8, self.__colors_list[alert]) + self.term_window.addnstr(self.mem_y+3, self.mem_x+30, self.__autoUnit((mem['free']+mem['cache'])), 8) def displayNetwork(self, network): @@ -1023,8 +1019,8 @@ class glancesScreen(): Return the number of interfaces """ # Network interfaces bitrate - if (not network or not self.network_tag): - return 0 + if (not self.network_tag): + return 0 screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] if ((screen_y > self.network_y+3) @@ -1033,6 +1029,12 @@ class glancesScreen(): self.term_window.addnstr(self.network_y, self.network_x, _("Net rate"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) self.term_window.addnstr(self.network_y, self.network_x+10, _("Rx/ps"), 8) self.term_window.addnstr(self.network_y, self.network_x+20, _("Tx/ps"), 8) + + # If there is no data to display... + if (not network): + self.term_window.addnstr(self.network_y+1, self.network_x, _("Compute data..."), 15) + return 3 + # Adapt the maximum interface to the screen ret = 2 for i in range(0, min(screen_y-self.network_y-3, len(network))): @@ -1047,7 +1049,7 @@ class glancesScreen(): def displayDiskIO(self, diskio, offset_y = 0): # Disk input/output rate - if (not diskio or not self.diskio_tag): + if (not self.diskio_tag): return 0 screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] @@ -1057,6 +1059,12 @@ class glancesScreen(): self.term_window.addnstr(self.diskio_y, self.diskio_x, _("Disk I/O"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) self.term_window.addnstr(self.diskio_y, self.diskio_x+10, _("In/ps"), 8) self.term_window.addnstr(self.diskio_y, self.diskio_x+20, _("Out/ps"), 8) + + # If there is no data to display... + if (not diskio): + self.term_window.addnstr(self.diskio_y+1, self.diskio_x, _("Compute data..."), 15) + return 3 + # Adapt the maximum disk to the screen disk = 0 for disk in range(0, min(screen_y-self.diskio_y-3, len(diskio))): @@ -1077,15 +1085,15 @@ class glancesScreen(): self.fs_y = offset_y if ((screen_y > self.fs_y+3) and (screen_x > self.fs_x+28)): - self.term_window.addnstr(self.fs_y, self.fs_x, _("Mount"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) + self.term_window.addnstr(self.fs_y, self.fs_x, _("Mount B"), 8, self.title_color if self.hascolors else curses.A_UNDERLINE) self.term_window.addnstr(self.fs_y, self.fs_x+10, _("Total"), 8) self.term_window.addnstr(self.fs_y, self.fs_x+20, _("Used"), 8) # Adapt the maximum disk to the screen mounted = 0 for mounted in range(0, min(screen_y-self.fs_y-3, len(fs))): self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x, fs[mounted]['mnt_point'], 8) - self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+10, self.__autoUnit(fs[mounted]['size']) + "B", 8) - self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+20, self.__autoUnit(fs[mounted]['used']) + "B", 8, self.__getFsColor(fs[mounted]['used'], fs[mounted]['size'])) + self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+10, self.__autoUnit(fs[mounted]['size']), 8) + self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+20, self.__autoUnit(fs[mounted]['used']), 8, self.__getFsColor(fs[mounted]['used'], fs[mounted]['size'])) return mounted+3 return 0 @@ -1152,27 +1160,23 @@ class glancesScreen(): self.term_window.addnstr(self.process_y+1, process_x+30,str(processcount['sleeping']), 8) self.term_window.addnstr(self.process_y+1, process_x+40,str(processcount['stopped']+stats.getProcessCount()['zombie']), 8) # Display the process detail - if (not processlist): - return 3 if ((screen_y > self.process_y+6) and (screen_x > process_x+49)): # Processes detail - if (self.getProcessSortedBy() == 'cpu_percent'): - sortchar = '^' - else: - sortchar = ' ' - self.term_window.addnstr(self.process_y+3, process_x,"Cpu %"+sortchar, 8) - if (self.getProcessSortedBy() == 'proc_size'): - sortchar = '^' - else: - sortchar = ' ' - self.term_window.addnstr(self.process_y+3, process_x+10,_("Size MB")+sortchar, 8) - self.term_window.addnstr(self.process_y+3, process_x+20,_("Res MB"), 8) - self.term_window.addnstr(self.process_y+3, process_x+30,_("Name"), 8) + self.term_window.addnstr(self.process_y+3, process_x, _("Cpu %"), 5, curses.A_UNDERLINE if (self.getProcessSortedBy() == 'cpu_percent') else 0) + self.term_window.addnstr(self.process_y+3, process_x+7, _("Mem virt."), 9, curses.A_UNDERLINE if (self.getProcessSortedBy() == 'proc_size') else 0) + self.term_window.addnstr(self.process_y+3, process_x+18, _("Mem resi."), 9) + self.term_window.addnstr(self.process_y+3, process_x+30, _("Process name"), 12, curses.A_UNDERLINE if (self.getProcessSortedBy() == 'process_name') else 0) + + # If there is no data to display... + if (not processlist): + self.term_window.addnstr(self.process_y+4, self.process_x, _("Compute data..."), 15) + return 6 + for processes in range(0, min(screen_y-self.term_h+self.process_y-log_count, len(processlist))): - self.term_window.addnstr(self.process_y+4+processes, process_x, "%.1f" % processlist[processes]['cpu_percent'], 8, self.__getColor(processlist[processes]['cpu_percent'])) - self.term_window.addnstr(self.process_y+4+processes, process_x+10, str((processlist[processes]['proc_size'])/1048576), 8) - self.term_window.addnstr(self.process_y+4+processes, process_x+20, str((processlist[processes]['proc_resident'])/1048576), 8) + self.term_window.addnstr(self.process_y+4+processes, process_x, "%.1f" % processlist[processes]['cpu_percent'], 8, self.__getProcessColor(processlist[processes]['cpu_percent'])) + self.term_window.addnstr(self.process_y+4+processes, process_x+7, self.__autoUnit(processlist[processes]['proc_size']), 9) + self.term_window.addnstr(self.process_y+4+processes, process_x+18, self.__autoUnit(processlist[processes]['proc_resident']), 9) maxprocessname = screen_x-process_x-30 # If screen space is available then display long name if ((len(processlist[processes]['proctitle']) > maxprocessname) @@ -1187,13 +1191,47 @@ class glancesScreen(): # Caption screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] + if ((screen_x < 80) or (screen_y < 24)): + # Help can only be displayed on a 80x24 console + return 0 if ((screen_y > self.caption_y) and (screen_x > self.caption_x+32)): - self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x, _(" OK "), 8, self.default_color) - self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+8, _("CAREFUL "), 8, self.ifCAREFUL_color) - self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+16, _("WARNING "), 8, self.ifWARNING_color) - self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+24, _("CRITICAL"), 8, self.ifCRITICAL_color) + self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x, _("Press 'h' for help"), self.default_color) + + def displayHelp(self): + """ + Show the help panel + """ + if (not self.help_tag): + return 0 + screen_x = self.screen.getmaxyx()[1] + screen_y = self.screen.getmaxyx()[0] + if ((screen_y > self.help_y+23) + and (screen_x > self.help_x+79)): + # Console 80x24 is mandatory to display teh help message + self.erase() + + self.term_window.addnstr(self.help_y, self.help_x, _("Glances v")+self.__version+_(" user guide"), 79, self.title_color if self.hascolors else 0) + + self.term_window.addnstr(self.help_y+2, self.help_x, _("Captions: "), 79) + self.term_window.addnstr(self.help_y+2, self.help_x+10, _(" OK "), 8, self.default_color) + self.term_window.addnstr(self.help_y+2, self.help_x+18, _("CAREFUL "), 8, self.ifCAREFUL_color) + self.term_window.addnstr(self.help_y+2, self.help_x+26, _("WARNING "), 8, self.ifWARNING_color) + self.term_window.addnstr(self.help_y+2, self.help_x+34, _("CRITICAL"), 8, self.ifCRITICAL_color) + + self.term_window.addnstr(self.help_y+4 , self.help_x, _("Key") + "\t" + _("Function"), 79, self.title_color if self.hascolors else 0) + self.term_window.addnstr(self.help_y+5 , self.help_x, _("a") + "\t" + _("Sort process list automaticaly"), 79) + self.term_window.addnstr(self.help_y+6 , self.help_x, _("c") + "\t" + _("Sort process list by CPU usage"), 79) + self.term_window.addnstr(self.help_y+7 , self.help_x, _("m") + "\t" + _("Sort process list by virtual memory usage"), 79) + self.term_window.addnstr(self.help_y+8 , self.help_x, _("p") + "\t" + _("Sort process list by name"), 79) + self.term_window.addnstr(self.help_y+9 , self.help_x, _("d") + "\t" + _("Enable/Disable disk IO stats"), 79) + self.term_window.addnstr(self.help_y+10, self.help_x, _("f") + "\t" + _("Enable/Disable file system stats"), 79) + self.term_window.addnstr(self.help_y+11, self.help_x, _("n") + "\t" + _("Enable/Disable network stats"), 79) + self.term_window.addnstr(self.help_y+12, self.help_x, _("l") + "\t" + _("Enable/Disable log list (only available if display > 24 lines)"), 79) + self.term_window.addnstr(self.help_y+13, self.help_x, _("h") + "\t" + _("Display/Hide help message"), 79) + self.term_window.addnstr(self.help_y+14, self.help_x, _("q") + "\t" + _("Exit from Glances (ESC key and CRTL-C also work...)"), 79) + def displayNow(self, now): # Display the current date and time (now...) - Center @@ -1303,6 +1341,7 @@ def end(): def signal_handler(signal, frame): end() + # Main #===== -- cgit v1.2.3 From 274516f262d8a85c00f70c71dc61449d408f4cab Mon Sep 17 00:00:00 2001 From: nicolargo Date: Sun, 12 Feb 2012 14:13:32 +0100 Subject: Ready for beta test --- NEWS | 2 +- src/glances.py | 35 +++++++---------------------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/NEWS b/NEWS index 567e378c..50d8b695 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,7 @@ Version 1.4 =========== - * No more StatGrab, welcome to the PsUtil library ! + * Goodby StatGrab... Welcome to the PsUtil library ! * Sort by Process name ('p' key) * Only major stats (CPU, Load and memory) use background colors * Improve operating system name diff --git a/src/glances.py b/src/glances.py index 19fce56f..e9a520d8 100755 --- a/src/glances.py +++ b/src/glances.py @@ -34,12 +34,11 @@ try: except KeyboardInterrupt: pass - # i18n #===== application = 'glances' -__version__ = "1.4a" +__version__ = "1.4b" gettext.install(application) try: @@ -63,20 +62,12 @@ except: print sys.exit(1) -# TODO: Remove after test PsUtil psutil.__version__ - -try: - import statgrab -except: - print _('Statgrab initialization failed, Glances cannot start.') - print - sys.exit(1) - try: import curses import curses.panel except: print _('Textmode GUI initialization failed, Glances cannot start.') + print _('Use Python 2.6 or higher') print sys.exit(1) @@ -275,18 +266,14 @@ class glancesGrabFs(): class glancesStats(): """ - This class store, update and give the libstatgrab stats + This class store, update and give stats """ def __init__(self): """ - Init the libstatgrab and process to the first update + Init the stats """ - - # Init libstatgrab - if not statgrab.sg_init(): - print _("Error: Can not init the libstatgrab library.\n") - + # Init the fs stats try: self.glancesgrabfs = glancesGrabFs() @@ -298,8 +285,6 @@ class glancesStats(): """ Update the stats """ - - # Get system informations # Host and OS informations self.host = {} @@ -310,7 +295,7 @@ class glancesStats(): try: if (self.host['os_name'] == "Linux" or self.host['os_name'] == "FreeBSD"): os_version = platform.linux_distribution() - self.host['os_version'] = os_version[0]+" "+os_version[1]+" ("+os_version[2]+")" + self.host['os_version'] = os_version[0]+" "+os_version[2]+" "+os_version[1] elif (self.host['os_name'] == "Windows"): os_version = platform.win32_ver() self.host['os_version'] = os_version[0]+" "+os_version[2] @@ -481,12 +466,7 @@ class glancesStats(): # Get the number of core (CPU) (Used to display load alerts) self.core_number = psutil.NUM_CPUS - - def end(self): - # Shutdown the libstatgrab - statgrab.sg_shutdown() - def update(self): # Update the stats @@ -917,7 +897,7 @@ class glancesScreen(): screen_x = self.screen.getmaxyx()[1] screen_y = self.screen.getmaxyx()[0] if ((screen_y > self.system_y) - and (screen_x > self.system_x+79)): + and (screen_x > self.system_x+79)): system_msg = system['os_name']+" "+system['platform']+" "+system['os_version'] self.term_window.addnstr(self.system_y, self.system_x+int(screen_x/2)-len(system_msg)/2, system_msg, 80) @@ -1332,7 +1312,6 @@ def main(): def end(): - stats.end() screen.end() sys.exit(0) -- cgit v1.2.3 From 1e0a303efb7c6c4aa64f6cfbabf8e80813dbf781 Mon Sep 17 00:00:00 2001 From: nicolargo Date: Sun, 12 Feb 2012 16:41:01 +0100 Subject: Remove the unused get_io_counters calls --- src/glances.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/glances.py b/src/glances.py index e9a520d8..8352b644 100755 --- a/src/glances.py +++ b/src/glances.py @@ -38,7 +38,7 @@ except KeyboardInterrupt: #===== application = 'glances' -__version__ = "1.4b" +__version__ = "1.4b2" gettext.install(application) try: @@ -454,8 +454,8 @@ class glancesStats(): procstat['proc_size'] = proc.get_memory_info().vms procstat['proc_resident'] = proc.get_memory_info().rss procstat['cpu_percent'] = proc._before.get_cpu_percent(interval=0) - procstat['diskio_read'] = proc.get_io_counters().read_bytes - proc._before.get_io_counters().read_bytes - procstat['diskio_write'] = proc.get_io_counters().write_bytes - proc._before.get_io_counters().write_bytes + #~ procstat['diskio_read'] = proc.get_io_counters().read_bytes - proc._before.get_io_counters().read_bytes + #~ procstat['diskio_write'] = proc.get_io_counters().write_bytes - proc._before.get_io_counters().write_bytes self.process.append(procstat) except: pass -- cgit v1.2.3 From c9f5c9b518d2399a8b19d4edd09e22ad1376ec61 Mon Sep 17 00:00:00 2001 From: nicolargo Date: Mon, 13 Feb 2012 11:17:49 +0100 Subject: Change the process list algorithm --- src/glances.py | 144 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 52 deletions(-) diff --git a/src/glances.py b/src/glances.py index 8352b644..c1ccac2a 100755 --- a/src/glances.py +++ b/src/glances.py @@ -38,7 +38,7 @@ except KeyboardInterrupt: #===== application = 'glances' -__version__ = "1.4b2" +__version__ = "1.4b3" gettext.install(application) try: @@ -279,7 +279,7 @@ class glancesStats(): self.glancesgrabfs = glancesGrabFs() except: self.glancesgrabfs = {} - + def __update__(self): """ @@ -407,45 +407,30 @@