summaryrefslogtreecommitdiffstats
path: root/glances/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'glances/plugins')
-rw-r--r--glances/plugins/glances_diskio.py12
-rw-r--r--glances/plugins/glances_docker.py18
-rw-r--r--glances/plugins/glances_fs.py2
-rw-r--r--glances/plugins/glances_gpu.py6
-rw-r--r--glances/plugins/glances_ip.py3
-rw-r--r--glances/plugins/glances_memswap.py35
-rw-r--r--glances/plugins/glances_network.py14
-rw-r--r--glances/plugins/glances_now.py16
-rw-r--r--glances/plugins/glances_plugin.py86
-rw-r--r--glances/plugins/glances_processlist.py47
-rw-r--r--glances/plugins/glances_quicklook.py10
11 files changed, 196 insertions, 53 deletions
diff --git a/glances/plugins/glances_diskio.py b/glances/plugins/glances_diskio.py
index 52ddfebc..3fe66543 100644
--- a/glances/plugins/glances_diskio.py
+++ b/glances/plugins/glances_diskio.py
@@ -23,6 +23,7 @@ from __future__ import unicode_literals
from glances.compat import nativestr, n
from glances.timer import getTimeSinceLastUpdate
from glances.plugins.glances_plugin import GlancesPlugin
+from glances.logger import logger
import psutil
@@ -51,6 +52,9 @@ class Plugin(GlancesPlugin):
# We want to display the stat in the curse interface
self.display_curse = True
+ # Hide stats if it has never been != 0
+ self.hide_zero = True
+ self.hide_zero_fields = ['read_bytes', 'write_bytes']
def get_key(self):
"""Return the key of the list."""
@@ -143,9 +147,12 @@ class Plugin(GlancesPlugin):
# Call the father's method
super(Plugin, self).update_views()
+ # Check if the stats should be hidden
+ self.update_views_hidden()
+
# Add specifics informations
# Alert
- for i in self.stats:
+ for i in self.get_raw():
disk_real_name = i['disk_name']
self.views[i[self.get_key()]]['read_bytes']['decoration'] = self.get_alert(int(i['read_bytes'] // i['time_since_update']),
header=disk_real_name + '_rx')
@@ -179,6 +186,9 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
# Disk list (sorted by name)
for i in self.sorted_stats():
+ # Hide stats if never be different from 0 (issue #1787)
+ if all([self.get_views(item=i[self.get_key()], key=f, option='hidden') for f in self.hide_zero_fields]):
+ continue
# Is there an alias for the disk name ?
disk_real_name = i['disk_name']
disk_name = self.has_alias(i['disk_name'])
diff --git a/glances/plugins/glances_docker.py b/glances/plugins/glances_docker.py
index 80c550df..82dd1693 100644
--- a/glances/plugins/glances_docker.py
+++ b/glances/plugins/glances_docker.py
@@ -81,6 +81,9 @@ class Plugin(GlancesPlugin):
# The plgin can be disable using: args.disable_docker
self.args = args
+ # Default config keys
+ self.config = config
+
# We want to display the stat in the curse interface
self.display_curse = True
@@ -207,6 +210,14 @@ class Plugin(GlancesPlugin):
# Get stats for all containers
stats['containers'] = []
for container in containers:
+ # Only show specific containers
+ if not self.is_show(nativestr(container.name)):
+ continue
+
+ # Do not take hiden container into account
+ if self.is_hide(nativestr(container.name)):
+ continue
+
# Init the stats for the current container
container_stats = {}
# The key is the container name and not the Id
@@ -524,8 +535,11 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_new_line())
# Header
ret.append(self.curse_new_line())
- # Get the maximum containers name (cutted to 20 char max)
- name_max_width = min(20, len(max(self.stats['containers'], key=lambda x: len(x['name']))['name']))
+ # Get the maximum containers name
+ # Max size is configurable. See feature request #1723.
+ name_max_width = min(self.config.get_int_value('docker', 'max_name_size', default=20),
+ len(max(self.stats['containers'],
+ key=lambda x: len(x['name']))['name']))
msg = ' {:{width}}'.format('Name', width=name_max_width)
ret.append(self.curse_add_line(msg))
msg = '{:>10}'.format('Status')
diff --git a/glances/plugins/glances_fs.py b/glances/plugins/glances_fs.py
index 6c1e618a..7d43d988 100644
--- a/glances/plugins/glances_fs.py
+++ b/glances/plugins/glances_fs.py
@@ -103,8 +103,6 @@ class Plugin(GlancesPlugin):
return self.stats
# Optionnal hack to allow logicals mounts points (issue #448)
- # Ex: Had to put 'allow=zfs' in the [fs] section of the conf file
- # to allow zfs monitoring
for fstype in self.get_conf_value('allow'):
try:
fs_stat += [f for f in psutil.disk_partitions(all=True) if f.fstype.find(fstype) >= 0]
diff --git a/glances/plugins/glances_gpu.py b/glances/plugins/glances_gpu.py
index e0cdb96b..8c2905b1 100644
--- a/glances/plugins/glances_gpu.py
+++ b/glances/plugins/glances_gpu.py
@@ -2,7 +2,7 @@
#
# This file is part of Glances.
#
-# Copyright (C) 2018 Kirby Banman <kirby.banman@gmail.com>
+# Copyright (C) 2020 Kirby Banman <kirby.banman@gmail.com>
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
@@ -226,11 +226,11 @@ class Plugin(GlancesPlugin):
id_msg = '{}'.format(gpu_stats['gpu_id'])
try:
proc_msg = '{:>3.0f}%'.format(gpu_stats['proc'])
- except ValueError:
+ except (ValueError, TypeError):
proc_msg = '{:>4}'.format('N/A')
try:
mem_msg = '{:>3.0f}%'.format(gpu_stats['mem'])
- except ValueError:
+ except (ValueError, TypeError):
mem_msg = '{:>4}'.format('N/A')
msg = '{}: {} mem: {}'.format(id_msg,
proc_msg,
diff --git a/glances/plugins/glances_ip.py b/glances/plugins/glances_ip.py
index 8aa38f7c..8a659bc4 100644
--- a/glances/plugins/glances_ip.py
+++ b/glances/plugins/glances_ip.py
@@ -150,6 +150,9 @@ class Plugin(GlancesPlugin):
"""
# Thanks to @Atticfire
# See https://github.com/nicolargo/glances/issues/1417#issuecomment-469894399
+ if ip is None:
+ # Correct issue #1528
+ return 0
return sum(bin(int(x)).count('1') for x in ip.split('.'))
diff --git a/glances/plugins/glances_memswap.py b/glances/plugins/glances_memswap.py
index 42c367a8..87d1fb38 100644
--- a/glances/plugins/glances_memswap.py
+++ b/glances/plugins/glances_memswap.py
@@ -66,20 +66,24 @@ class Plugin(GlancesPlugin):
if self.input_method == 'local':
# Update stats using the standard system lib
# Grab SWAP using the psutil swap_memory method
- sm_stats = psutil.swap_memory()
-
- # Get all the swap stats (copy/paste of the psutil documentation)
- # total: total swap memory in bytes
- # used: used swap memory in bytes
- # free: free swap memory in bytes
- # percent: the percentage usage
- # sin: the number of bytes the system has swapped in from disk (cumulative)
- # sout: the number of bytes the system has swapped out from disk
- # (cumulative)
- for swap in ['total', 'used', 'free', 'percent',
- 'sin', 'sout']:
- if hasattr(sm_stats, swap):
- stats[swap] = getattr(sm_stats, swap)
+ try:
+ sm_stats = psutil.swap_memory()
+ except RuntimeError:
+ # Crash on startup on Illumos when no swap is configured #1767
+ pass
+ else:
+ # Get all the swap stats (copy/paste of the psutil documentation)
+ # total: total swap memory in bytes
+ # used: used swap memory in bytes
+ # free: free swap memory in bytes
+ # percent: the percentage usage
+ # sin: the number of bytes the system has swapped in from disk (cumulative)
+ # sout: the number of bytes the system has swapped out from disk
+ # (cumulative)
+ for swap in ['total', 'used', 'free', 'percent',
+ 'sin', 'sout']:
+ if hasattr(sm_stats, swap):
+ stats[swap] = getattr(sm_stats, swap)
elif self.input_method == 'snmp':
# Update stats using SNMP
if self.short_system_name == 'windows':
@@ -135,7 +139,8 @@ class Plugin(GlancesPlugin):
# Add specifics informations
# Alert and log
- self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
+ if 'used' in self.stats and 'total' in self.stats:
+ self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
def msg_curse(self, args=None, max_width=None):
"""Return the dict to display in the curse interface."""
diff --git a/glances/plugins/glances_network.py b/glances/plugins/glances_network.py
index 00fc88b0..2adf3653 100644
--- a/glances/plugins/glances_network.py
+++ b/glances/plugins/glances_network.py
@@ -61,6 +61,9 @@ class Plugin(GlancesPlugin):
# We want to display the stat in the curse interface
self.display_curse = True
+ # Hide stats if it has never been != 0
+ self.hide_zero = True
+ self.hide_zero_fields = ['rx', 'tx']
def get_key(self):
"""Return the key of the list."""
@@ -223,13 +226,17 @@ class Plugin(GlancesPlugin):
# Call the father's method
super(Plugin, self).update_views()
+ # Check if the stats should be hidden
+ self.update_views_hidden()
+
# Add specifics informations
# Alert
- for i in self.stats:
+ for i in self.get_raw():
ifrealname = i['interface_name'].split(':')[0]
- # Convert rate in bps ( to be able to compare to interface speed)
+ # Convert rate in bps (to be able to compare to interface speed)
bps_rx = int(i['rx'] // i['time_since_update'] * 8)
bps_tx = int(i['tx'] // i['time_since_update'] * 8)
+
# Decorate the bitrate with the configuration file thresolds
alert_rx = self.get_alert(bps_rx, header=ifrealname + '_rx')
alert_tx = self.get_alert(bps_tx, header=ifrealname + '_tx')
@@ -290,6 +297,9 @@ class Plugin(GlancesPlugin):
# Do not display interface in down state (issue #765)
if ('is_up' in i) and (i['is_up'] is False):
continue
+ # Hide stats if never be different from 0 (issue #1787)
+ if all([self.get_views(item=i[self.get_key()], key=f, option='hidden') for f in self.hide_zero_fields]):
+ continue
# Format stats
# Is there an alias for the interface name ?
ifrealname = i['interface_name'].split(':')[0]
diff --git a/glances/plugins/glances_now.py b/glances/plugins/glances_now.py
index 254d71b8..d3edb839 100644
--- a/glances/plugins/glances_now.py
+++ b/glances/plugins/glances_now.py
@@ -41,6 +41,12 @@ class Plugin(GlancesPlugin):
# Set the message position
self.align = 'bottom'
+ if args.strftime_format:
+ self.strftime = args.strftime_format
+ elif config is not None:
+ if 'global' in config.as_dict():
+ self.strftime = config.as_dict()['global']['strftime_format']
+
def reset(self):
"""Reset/init the stats."""
self.stats = ''
@@ -49,10 +55,14 @@ class Plugin(GlancesPlugin):
"""Update current date/time."""
# Had to convert it to string because datetime is not JSON serializable
# Add the time zone (issue #1249 / #1337 / #1598)
- if (len(tzname[1]) > 6):
- self.stats = strftime('%Y-%m-%d %H:%M:%S %z')
+
+ if self.strftime:
+ self.stats = strftime(self.strftime)
else:
- self.stats = strftime('%Y-%m-%d %H:%M:%S %Z')
+ if (len(tzname[1]) > 6):
+ self.stats = strftime('%Y-%m-%d %H:%M:%S %z')
+ else:
+ self.stats = strftime('%Y-%m-%d %H:%M:%S %Z')
return self.stats
diff --git a/glances/plugins/glances_plugin.py b/glances/plugins/glances_plugin.py
index 936db58c..5588263d 100644
--- a/glances/plugins/glances_plugin.py
+++ b/glances/plugins/glances_plugin.py
@@ -99,6 +99,11 @@ class GlancesPlugin(object):
# Init the views
self.views = dict()
+ # Hide stats if all the hide_zero_fields has never been != 0
+ # Default is False, always display stats
+ self.hide_zero = False
+ self.hide_zero_fields = []
+
# Init the stats
self.stats_init_value = stats_init_value
self.stats = None
@@ -422,16 +427,61 @@ class GlancesPlugin(object):
"Cannot get item({})=value({}) ({})".format(item, value, e))
return None
+ def update_views_hidden(self):
+ """If the self.hide_zero is set then update the hidden field of the view
+ It will check if all fields values are already be different from 0
+ In this case, the hidden field is set to True
+
+ Note: This function should be called by plugin (in the update_views method)
+
+ Example (for network plugin):
+ __Init__
+ self.hide_zero_fields = ['rx', 'tx']
+ Update views
+ ...
+ self.update_views_hidden()
+ """
+ if not self.hide_zero:
+ return False
+ if (isinstance(self.get_raw(), list) and
+ self.get_raw() is not None and
+ self.get_key() is not None):
+ # Stats are stored in a list of dict (ex: NETWORK, FS...)
+ for i in self.get_raw():
+ if any([i[f] for f in self.hide_zero_fields]):
+ for f in self.hide_zero_fields:
+ self.views[i[self.get_key(
+ )]][f]['_zero'] = self.views[i[self.get_key()]][f]['hidden']
+ for f in self.hide_zero_fields:
+ self.views[i[self.get_key(
+ )]][f]['hidden'] = self.views[i[self.get_key()]][f]['_zero'] and i[f] == 0
+ elif isinstance(self.get_raw(), dict) and self.get_raw() is not None:
+ #
+ # Warning: This code has never been tested because
+ # no plugin with dict instance use the hidden function...
+ # vvvv
+ #
+ # Stats are stored in a dict (ex: CPU, LOAD...)
+ for key in listkeys(self.get_raw()):
+ if any([self.get_raw()[f] for f in self.hide_zero_fields]):
+ for f in self.hide_zero_fields:
+ self.views[f]['_zero'] = self.views[f]['hidden']
+ for f in self.hide_zero_fields:
+ self.views[f]['hidden'] = self.views['_zero'] and self.views[f] == 0
+ return True
+
def update_views(self):
"""Update the stats views.
The V of MVC
A dict of dict with the needed information to display the stats.
Example for the stat xxx:
- 'xxx': {'decoration': 'DEFAULT',
- 'optional': False,
- 'additional': False,
- 'splittable': False}
+ 'xxx': {'decoration': 'DEFAULT', >>> The decoration of the stats
+ 'optional': False, >>> Is the stat optional
+ 'additional': False, >>> Is the stat provide additional information
+ 'splittable': False, >>> Is the stat can be cut (like process lon name)
+ 'hidden': False, >>> Is the stats should be hidden in the UI
+ '_zero': True} >>> For internal purpose only
"""
ret = {}
@@ -440,12 +490,15 @@ class GlancesPlugin(object):
self.get_key() is not None):
# Stats are stored in a list of dict (ex: NETWORK, FS...)
for i in self.get_raw():
+ # i[self.get_key()] is the interface name (example for NETWORK)
ret[i[self.get_key()]] = {}
for key in listkeys(i):
value = {'decoration': 'DEFAULT',
'optional': False,
'additional': False,
- 'splittable': False}
+ 'splittable': False,
+ 'hidden': False,
+ '_zero': self.views[i[self.get_key()]][key]['_zero'] if i[self.get_key()] in self.views and key in self.views[i[self.get_key()]] else True}
ret[i[self.get_key()]][key] = value
elif isinstance(self.get_raw(), dict) and self.get_raw() is not None:
# Stats are stored in a dict (ex: CPU, LOAD...)
@@ -453,7 +506,9 @@ class GlancesPlugin(object):
value = {'decoration': 'DEFAULT',
'optional': False,
'additional': False,
- 'splittable': False}
+ 'splittable': False,
+ 'hidden': False,
+ '_zero': self.views[key]['_zero'] if key in self.views and '_zero' in self.views[key] else True}
ret[key] = value
self.views = ret
@@ -752,6 +807,21 @@ class GlancesPlugin(object):
except KeyError:
return default
+ def is_show(self, value, header=""):
+ """Return True if the value is in the show configuration list.
+ If the show value is empty, return True (show by default)
+
+ The show configuration list is defined in the glances.conf file.
+ It is a comma separed list of regexp.
+ Example for diskio:
+ show=sda.*
+ """
+ # @TODO: possible optimisation: create a re.compile list
+ if self.get_conf_value('show', header=header) == []:
+ return True
+ else:
+ return any(j for j in [re.match(i, value) for i in self.get_conf_value('show', header=header)])
+
def is_hide(self, value, header=""):
"""Return True if the value is in the hide configuration list.
@@ -760,9 +830,7 @@ class GlancesPlugin(object):
Example for diskio:
hide=sda2,sda5,loop.*
"""
- # TODO: possible optimisation: create a re.compile list
- # Old version (see issue #1691)
- #return not all(j is None for j in [re.match(i, value.lower()) for i in self.get_conf_value('hide', header=header)])
+ # @TODO: possible optimisation: create a re.compile list
return any(j for j in [re.match(i, value) for i in self.get_conf_value('hide', header=header)])
def has_alias(self, header):
diff --git a/glances/plugins/glances_processlist.py b/glances/plugins/glances_processlist.py
index 47029f40..0abf02e0 100644
--- a/glances/plugins/glances_processlist.py
+++ b/glances/plugins/glances_processlist.py
@@ -70,12 +70,13 @@ class Plugin(GlancesPlugin):
'status': '{:>1} ',
'ior': '{:>4} ',
'iow': '{:<4} ',
- 'command': '{}',
+ 'command': '{} {}',
}
# Define the stat layout of the processes list columns
layout_stat = {
- 'cpu': '{:<6.1f} ',
+ 'cpu': '{:<6.1f}',
+ 'cpu_no_digit': '{:<6.0f}',
'mem': '{:<5.1f} ',
'virt': '{:<5} ',
'res': '{:<5} ',
@@ -172,19 +173,26 @@ class Plugin(GlancesPlugin):
pass
return 'DEFAULT'
- def get_process_curses_data(self, p, first, args):
+ def get_process_curses_data(self, p, selected, args):
"""Get curses data to display for a process.
- p is the process to display
- - first is a tag=True if the process is the first on the list
+ - selected is a tag=True if the selected process
"""
ret = [self.curse_new_line()]
+ # When a process is selected:
+ # * display a special caracter at the beginning of the line
+ # * underline the command name
+ if args.is_standalone:
+ ret.append(self.curse_add_line('>' if selected else ' ', 'SELECTED'))
# CPU
if 'cpu_percent' in p and p['cpu_percent'] is not None and p['cpu_percent'] != '':
+ cpu_layout = self.layout_stat['cpu'] if p['cpu_percent'] < 100 else self.layout_stat['cpu_no_digit']
if args.disable_irix and self.nb_log_core != 0:
- msg = self.layout_stat['cpu'].format(p['cpu_percent'] / float(self.nb_log_core))
+ msg = cpu_layout.format(
+ p['cpu_percent'] / float(self.nb_log_core))
else:
- msg = self.layout_stat['cpu'].format(p['cpu_percent'])
+ msg = cpu_layout.format(p['cpu_percent'])
alert = self.get_alert(p['cpu_percent'],
highlight_zero=False,
is_max=(p['cpu_percent'] == self.max_values['cpu_percent']),
@@ -321,28 +329,34 @@ class Plugin(GlancesPlugin):
else:
cmdline = '?'
try:
+ process_decoration = 'PROCESS_SELECTED' if (selected and args.is_standalone) else 'PROCESS'
if cmdline:
path, cmd, arguments = split_cmdline(cmdline)
+ # Manage end of line in arguments (see #1692)
+ arguments.replace('\r\n', ' ')
+ arguments.replace('\n', ' ')
if os.path.isdir(path) and not args.process_short_name:
msg = self.layout_stat['command'].format(path) + os.sep
ret.append(self.curse_add_line(msg, splittable=True))
- ret.append(self.curse_add_line(cmd, decoration='PROCESS', splittable=True))
+ ret.append(self.curse_add_line(
+ cmd, decoration=process_decoration, splittable=True))
else:
msg = self.layout_stat['command'].format(cmd)
- ret.append(self.curse_add_line(msg, decoration='PROCESS', splittable=True))
+ ret.append(self.curse_add_line(
+ msg, decoration=process_decoration, splittable=True))
if arguments:
msg = ' ' + self.layout_stat['command'].format(arguments)
ret.append(self.curse_add_line(msg, splittable=True))
else:
msg = self.layout_stat['name'].format(p['name'])
- ret.append(self.curse_add_line(msg, splittable=True))
+ ret.append(self.curse_add_line(msg, decoration=process_decoration, splittable=True))
except (TypeError, UnicodeEncodeError) as e:
# Avoid crach after running fine for several hours #1335
logger.debug("Can not decode command line '{}' ({})".format(cmdline, e))
ret.append(self.curse_add_line('', splittable=True))
# Add extended stats but only for the top processes
- if first and 'extended_stats' in p and args.enable_process_extended:
+ if args.cursor_position == 0 and 'extended_stats' in p and args.enable_process_extended:
# Left padding
xpad = ' ' * 13
# First line is CPU affinity
@@ -429,11 +443,13 @@ class Plugin(GlancesPlugin):
# Process list
# Loop over processes (sorted by the sort key previously compute)
- first = True
+ i = 0
for p in self.__sort_stats(process_sort_key):
- ret.extend(self.get_process_curses_data(p, first, args))
- # End of extended stats
- first = False
+ ret.extend(self.get_process_curses_data(
+ p, i == args.cursor_position, args))
+ i += 1
+
+ # A filter is set Display the stats summaries
if glances_processes.process_filter is not None:
if args.reset_minmax_tag:
args.reset_minmax_tag = not args.reset_minmax_tag
@@ -478,7 +494,8 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True))
msg = self.layout_header['iow'].format('W/s')
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True))
- msg = self.layout_header['command'].format('Command')
+ msg = self.layout_header['command'].format('Command',
+ "('k' to kill)" if args.is_standalone else "")
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
def __msg_curse_sum(self, ret, sep_char='_', mmm=None, args=None):
diff --git a/glances/plugins/glances_quicklook.py b/glances/plugins/glances_quicklook.py
index e49628e4..babca182 100644
--- a/glances/plugins/glances_quicklook.py
+++ b/glances/plugins/glances_quicklook.py
@@ -81,7 +81,11 @@ class Plugin(GlancesPlugin):
stats['percpu'] = cpu_percent.get(percpu=True)
# Use the psutil lib for the memory (virtual and swap)
stats['mem'] = psutil.virtual_memory().percent
- stats['swap'] = psutil.swap_memory().percent
+ try:
+ stats['swap'] = psutil.swap_memory().percent
+ except RuntimeError:
+ # Correct issue in Illumos OS (see #1767)
+ stats['swap'] = None
elif self.input_method == 'snmp':
# Not available
pass
@@ -99,8 +103,12 @@ class Plugin(GlancesPlugin):
stats['cpu_name'] = cpu_info.get('brand', 'CPU')
if 'hz_actual_raw' in cpu_info:
stats['cpu_hz_current'] = cpu_info['hz_actual_raw'][0]
+ elif 'hz_actual' in cpu_info:
+ stats['cpu_hz_current'] = cpu_info['hz_actual'][0]
if 'hz_advertised_raw' in cpu_info:
stats['cpu_hz'] = cpu_info['hz_advertised_raw'][0]
+ elif 'hz_advertised' in cpu_info:
+ stats['cpu_hz'] = cpu_info['hz_advertised'][0]
# Update the stats
self.stats = stats