Add alias feature and optimize history feature footprint

This commit is contained in:
Nicolargo 2014-07-31 13:21:46 +02:00
parent 22ea5b1172
commit 257a90f718
16 changed files with 223 additions and 31 deletions

View File

@ -51,7 +51,9 @@ critical=90
[network] [network]
# Define the list of hidden network interfaces (comma separeted) # Define the list of hidden network interfaces (comma separeted)
hide=lo hide=lo
# Default limits (in bits per second aka bps) for interface bitrate # WLAN0 alias name
wlan0_alias=Wireless
# WLAN0 Default limits (in bits per second aka bps) for interface bitrate
wlan0_rx_careful=4000000 wlan0_rx_careful=4000000
wlan0_rx_warning=5000000 wlan0_rx_warning=5000000
wlan0_rx_critical=6000000 wlan0_rx_critical=6000000
@ -62,6 +64,8 @@ wlan0_tx_critical=1000000
[diskio] [diskio]
# Define the list of hidden disks (comma separeted) # Define the list of hidden disks (comma separeted)
hide=sda2,sda5 hide=sda2,sda5
# Alias for sda1
#sda1_alias=IntDisk
[fs] [fs]
# Default limits for free filesytem space in % # Default limits for free filesytem space in %
@ -85,6 +89,12 @@ temperature_hdd_critical=60
battery_careful=80 battery_careful=80
battery_warning=90 battery_warning=90
battery_critical=95 battery_critical=95
# Sensors alias
temp1_alias=Motherboard 0
temp2_alias=Motherboard 1
core 0_alias=CPU Core 0
core 1_alias=CPU Core 1
[processlist] [processlist]
# Limit values for CPU/MEM per process in % # Limit values for CPU/MEM per process in %

View File

@ -51,7 +51,9 @@ critical=90
#[network] #[network]
# Define the list of hidden network interfaces (comma separeted) # Define the list of hidden network interfaces (comma separeted)
#hide=lo #hide=lo
# Default limits (in bits per second aka bps) for interface bitrate # WLAN 0 alias
#wlan0_alias=Wireless IF
# WLAN 0 Default limits (in bits per second aka bps) for interface bitrate
#wlan0_rx_careful=4000000 #wlan0_rx_careful=4000000
#wlan0_rx_warning=5000000 #wlan0_rx_warning=5000000
#wlan0_rx_critical=6000000 #wlan0_rx_critical=6000000
@ -62,6 +64,8 @@ critical=90
#[diskio] #[diskio]
# Define the list of hidden disks (comma separeted) # Define the list of hidden disks (comma separeted)
#hide=sda2,sda5 #hide=sda2,sda5
# Alias for sda1
#sda1_alias=IntDisk
[fs] [fs]
# Default limits for free filesytem space in % # Default limits for free filesytem space in %
@ -85,6 +89,11 @@ temperature_hdd_critical=60
battery_careful=80 battery_careful=80
battery_warning=90 battery_warning=90
battery_critical=95 battery_critical=95
# Sensors alias
#temp1_alias=Motherboard 0
#temp2_alias=Motherboard 1
#core 0_alias=CPU Core 0
#core 1_alias=CPU Core 1
[processlist] [processlist]
# Limit values for CPU/MEM per process in % # Limit values for CPU/MEM per process in %

View File

@ -49,7 +49,7 @@ psutil_version = tuple([int(num) for num in __psutil_version.split('.')])
# First log with Glances and PSUtil version # First log with Glances and PSUtil version
logger.info('Start Glances {0}'.format(__version__)) logger.info('Start Glances {0}'.format(__version__))
logger.info('with {0} {1} and PSutil {2}'.format(platform.python_implementation(), logger.info('{0} {1} and PSutil {2} detected'.format(platform.python_implementation(),
platform.python_version(), platform.python_version(),
__psutil_version)) __psutil_version))

View File

@ -73,3 +73,4 @@ glances_processes = GlancesProcesses()
# The global instance for the logs # The global instance for the logs
from glances.core.glances_logs import GlancesLogs from glances.core.glances_logs import GlancesLogs
glances_logs = GlancesLogs() glances_logs = GlancesLogs()

View File

@ -60,6 +60,8 @@ class GlancesMain(object):
parser.add_argument('-C', '--config', dest='conf_file', parser.add_argument('-C', '--config', dest='conf_file',
help=_('path to the configuration file')) help=_('path to the configuration file'))
# Enable or disable option on startup # Enable or disable option on startup
parser.add_argument('--enable-history', action='store_true', default=False,
dest='enable_history', help=_('enable the history mode'))
parser.add_argument('--disable-bold', action='store_false', default=True, parser.add_argument('--disable-bold', action='store_false', default=True,
dest='disable_bold', help=_('disable bold mode in the terminal')) dest='disable_bold', help=_('disable bold mode in the terminal'))
parser.add_argument('--disable-diskio', action='store_true', default=False, parser.add_argument('--disable-diskio', action='store_true', default=False,

View File

@ -30,7 +30,7 @@ class GlancesStandalone(object):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
# Init stats # Init stats
self.stats = GlancesStats(config) self.stats = GlancesStats(config=config, args=args)
# Initial system informations update # Initial system informations update
self.stats.update() self.stats.update()

View File

@ -39,15 +39,18 @@ class GlancesStats(object):
"""This class stores, updates and gives stats.""" """This class stores, updates and gives stats."""
def __init__(self, config=None): def __init__(self, config=None, args=None):
# Init the plugin list dict # Init the plugin list dict
self._plugins = collections.defaultdict(dict) self._plugins = collections.defaultdict(dict)
# Set the argument instance
self.args = args
# Set the config instance # Set the config instance
self.config = config self.config = config
# Load the plugins # Load the plugins
self.load_plugins() self.load_plugins(args=args)
# Load the limits # Load the limits
self.load_limits(config) self.load_limits(config)

View File

@ -163,6 +163,17 @@ class GlancesCurses(object):
self.term_window.nodelay(1) self.term_window.nodelay(1)
self.pressedkey = -1 self.pressedkey = -1
# History tag
self.reset_history_tag = False
self.history_tag = False
if args.enable_history:
logger.info('Stats history enabled')
from glances.outputs.glances_history import GlancesHistory
self.glances_history = GlancesHistory()
if not self.glances_history.graph_enabled():
args.enable_history = False
logger.error('Stats history disabled because graph lib is not available')
def __get_key(self, window): def __get_key(self, window):
# Catch ESC key AND numlock key (issue #163) # Catch ESC key AND numlock key (issue #163)
keycode = [0, 0] keycode = [0, 0]
@ -205,6 +216,9 @@ class GlancesCurses(object):
elif self.pressedkey == ord('f'): elif self.pressedkey == ord('f'):
# 'f' > Show/hide fs stats # 'f' > Show/hide fs stats
self.args.disable_fs = not self.args.disable_fs self.args.disable_fs = not self.args.disable_fs
elif self.pressedkey == ord('g'):
# 'g' > History
self.history_tag = not self.history_tag
elif self.pressedkey == ord('h'): elif self.pressedkey == ord('h'):
# 'h' > Show/hide help # 'h' > Show/hide help
self.args.help_tag = not self.args.help_tag self.args.help_tag = not self.args.help_tag
@ -223,6 +237,9 @@ class GlancesCurses(object):
elif self.pressedkey == ord('p'): elif self.pressedkey == ord('p'):
# 'p' > Sort processes by name # 'p' > Sort processes by name
self.args.process_sorted_by = 'name' self.args.process_sorted_by = 'name'
elif self.pressedkey == ord('r'):
# 'r' > Reset history
self.reset_history_tag = not self.reset_history_tag
elif self.pressedkey == ord('s'): elif self.pressedkey == ord('s'):
# 's' > Show/hide sensors stats (Linux-only) # 's' > Show/hide sensors stats (Linux-only)
self.args.disable_sensors = not self.args.disable_sensors self.args.disable_sensors = not self.args.disable_sensors
@ -421,6 +438,58 @@ class GlancesCurses(object):
self.new_line() self.new_line()
self.display_plugin(stats_alert) self.display_plugin(stats_alert)
# History option
# Generate history graph
if self.history_tag and self.args.enable_history:
self.display_popup(_("Graphs history generated in %s") % self.glances_history.get_output_folder())
self.glances_history.generate_graph(stats)
elif self.reset_history_tag and self.args.enable_history:
self.display_popup(_("Reset history"))
self.glances_history.reset(stats)
elif (self.history_tag or self.reset_history_tag) and not self.args.enable_history:
self.display_popup(_("History disabled\nEnable it using --enable-history"))
self.history_tag = False
self.reset_history_tag = False
return True
def display_popup(self, message, size_x=None, size_y=None, duration=3):
"""
Display a centered popup with the given message during duration seconds
If size_x and size_y: set the popup size
else set it automatically
Return True if the popup could be displayed
"""
# Center the popup
if size_x is None:
size_x = len(message) + 4
if size_y is None:
size_y = message.count('\n') + 1 + 4
screen_x = self.screen.getmaxyx()[1]
screen_y = self.screen.getmaxyx()[0]
if size_x > screen_x or size_y > screen_y:
# No size to display the popup => abord
return False
pos_x = (screen_x - size_x) / 2
pos_y = (screen_y - size_y) / 2
# Create the popup
popup = curses.newwin(size_y, size_x, pos_y, pos_x)
# Fill the popup
popup.border()
# Add the message
y = 0
for m in message.split('\n'):
popup.addnstr(2 + y, 2, m, len(m))
y += 1
# Display the popup
popup.refresh()
curses.napms(duration * 1000)
return True return True
def display_plugin(self, plugin_stats, def display_plugin(self, plugin_stats,
@ -532,8 +601,8 @@ class GlancesCurses(object):
while not countdown.finished(): while not countdown.finished():
# Getkey # Getkey
if self.__catch_key() > -1: if self.__catch_key() > -1:
# flush display # Redraw display
self.flush(stats, cs_status=cs_status) self.flush(stats, cs_status=cs_status)
# Wait 100ms... # Wait 100ms...
curses.napms(100) curses.napms(100)

View File

@ -33,6 +33,12 @@ snmp_oid = {'default': {'user': '1.3.6.1.4.1.2021.11.9.0',
'windows': {'percent': '1.3.6.1.2.1.25.3.3.1.2'}, 'windows': {'percent': '1.3.6.1.2.1.25.3.3.1.2'},
'esxi': {'percent': '1.3.6.1.2.1.25.3.3.1.2'}} 'esxi': {'percent': '1.3.6.1.2.1.25.3.3.1.2'}}
# Define the history items list
# 'color' define the graph color in #RGB format
# All items in this list will be historised if the --enable-history tag is set
items_history_list = [{'name': 'user', 'color': '#00FF00'},
{'name': 'system', 'color': '#FF0000'}]
class Plugin(GlancesPlugin): class Plugin(GlancesPlugin):
""" """
@ -43,7 +49,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None): def __init__(self, args=None):
"""Init the CPU plugin.""" """Init the CPU plugin."""
GlancesPlugin.__init__(self, args=args) GlancesPlugin.__init__(self, args=args, items_history_list=items_history_list)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -123,6 +129,9 @@ class Plugin(GlancesPlugin):
for key in list(self.stats.keys()): for key in list(self.stats.keys()):
self.stats[key] = float(self.stats[key]) self.stats[key] = float(self.stats[key])
# Update the history list
self.update_stats_history()
return self.stats return self.stats
def msg_curse(self, args=None): def msg_curse(self, args=None):

View File

@ -128,13 +128,15 @@ class Plugin(GlancesPlugin):
# Do not display hidden interfaces # Do not display hidden interfaces
if self.is_hide(i['disk_name']): if self.is_hide(i['disk_name']):
continue continue
# Is there an alias for the disk name ?
disk_name = self.has_alias(i['disk_name'])
if disk_name is None:
disk_name = i['disk_name']
# New line # New line
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
if len(i['disk_name']) > 9: if len(disk_name) > 9:
# Cut disk name if it is too long # Cut disk name if it is too long
disk_name = '_' + i['disk_name'][-8:] disk_name = '_' + disk_name[-8:]
else:
disk_name = i['disk_name']
msg = '{0:9}'.format(disk_name) msg = '{0:9}'.format(disk_name)
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
txps = self.auto_unit(int(i['read_bytes'] // i['time_since_update'])) txps = self.auto_unit(int(i['read_bytes'] // i['time_since_update']))

View File

@ -116,7 +116,12 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
msg = msg_col.format(_("s"), _("Show/hide sensors stats")) msg = msg_col.format(_("s"), _("Show/hide sensors stats"))
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
msg = msg_col2.format(_("z"), _("Enable/disable processes stats")) msg = msg_col2.format(_("g"), _("Generate graphs for current history"))
ret.append(self.curse_add_line(msg))
ret.append(self.curse_new_line())
msg = msg_col.format(_("z"), _("Enable/disable processes stats"))
ret.append(self.curse_add_line(msg))
msg = msg_col2.format(_("r"), _("Reset history"))
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
msg = msg_col.format(_("q"), _("Quit (Esc and Ctrl-C also work)")) msg = msg_col.format(_("q"), _("Quit (Esc and Ctrl-C also work)"))

View File

@ -35,6 +35,13 @@ snmp_oid = {'min1': '1.3.6.1.4.1.2021.10.1.3.1',
'min5': '1.3.6.1.4.1.2021.10.1.3.2', 'min5': '1.3.6.1.4.1.2021.10.1.3.2',
'min15': '1.3.6.1.4.1.2021.10.1.3.3'} 'min15': '1.3.6.1.4.1.2021.10.1.3.3'}
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
# 'color' define the graph color in #RGB format
items_history_list = [{'name': 'min1', 'color': '#0000FF'},
{'name': 'min5', 'color': '#0000AA'},
{'name': 'min15', 'color': '#000044'}]
class Plugin(GlancesPlugin): class Plugin(GlancesPlugin):
@ -45,7 +52,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None): def __init__(self, args=None):
"""Init the plugin.""" """Init the plugin."""
GlancesPlugin.__init__(self, args=args) GlancesPlugin.__init__(self, args=args, items_history_list=items_history_list)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -53,6 +60,12 @@ class Plugin(GlancesPlugin):
# Init stats # Init stats
self.reset() self.reset()
# Call CorePlugin in order to display the core number
try:
self.nb_log_core = CorePlugin(args=self.args).update()["log"]
except Exception:
self.nb_log_core = 0
def reset(self): def reset(self):
"""Reset/init the stats.""" """Reset/init the stats."""
self.stats = {} self.stats = {}
@ -62,12 +75,6 @@ class Plugin(GlancesPlugin):
# Reset stats # Reset stats
self.reset() self.reset()
# Call CorePlugin in order to display the core number
try:
nb_log_core = CorePlugin().update()["log"]
except Exception:
nb_log_core = 0
if self.get_input() == 'local': if self.get_input() == 'local':
# Update stats using the standard system lib # Update stats using the standard system lib
@ -80,7 +87,7 @@ class Plugin(GlancesPlugin):
self.stats = {'min1': load[0], self.stats = {'min1': load[0],
'min5': load[1], 'min5': load[1],
'min15': load[2], 'min15': load[2],
'cpucore': nb_log_core} 'cpucore': self.nb_log_core}
elif self.get_input() == 'snmp': elif self.get_input() == 'snmp':
# Update stats using SNMP # Update stats using SNMP
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid) self.stats = self.set_stats_snmp(snmp_oid=snmp_oid)
@ -98,7 +105,10 @@ class Plugin(GlancesPlugin):
for k, v in iteritems: for k, v in iteritems:
self.stats[k] = float(v) self.stats[k] = float(v)
self.stats['cpucore'] = nb_log_core self.stats['cpucore'] = self.nb_log_core
# Update the history list
self.update_stats_history()
return self.stats return self.stats

View File

@ -45,6 +45,11 @@ snmp_oid = {'default': {'total': '1.3.6.1.4.1.2021.4.5.0',
'size': '1.3.6.1.2.1.25.2.3.1.5', 'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6'}} 'used': '1.3.6.1.2.1.25.2.3.1.6'}}
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
# 'color' define the graph color in #RGB format
items_history_list = [{'name': 'percent', 'color': '#00FF00'}]
class Plugin(GlancesPlugin): class Plugin(GlancesPlugin):
@ -55,7 +60,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None): def __init__(self, args=None):
"""Init the plugin.""" """Init the plugin."""
GlancesPlugin.__init__(self, args=args) GlancesPlugin.__init__(self, args=args, items_history_list=items_history_list)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -146,6 +151,9 @@ class Plugin(GlancesPlugin):
# percent: the percentage usage calculated as (total - available) / total * 100. # percent: the percentage usage calculated as (total - available) / total * 100.
self.stats['percent'] = float((self.stats['total'] - self.stats['free']) / self.stats['total'] * 100) self.stats['percent'] = float((self.stats['total'] - self.stats['free']) / self.stats['total'] * 100)
# Update the history list
self.update_stats_history()
return self.stats return self.stats
def msg_curse(self, args=None): def msg_curse(self, args=None):

View File

@ -218,7 +218,10 @@ class Plugin(GlancesPlugin):
if self.is_hide(i['interface_name']): if self.is_hide(i['interface_name']):
continue continue
# Format stats # Format stats
ifname = i['interface_name'].split(':')[0] # Is there an alias for the interface name ?
ifname = self.has_alias(i['interface_name'])
if ifname is None:
ifname = i['interface_name'].split(':')[0]
if len(ifname) > ifname_max_width: if len(ifname) > ifname_max_width:
# Cut interface name if it is too long # Cut interface name if it is too long
ifname = '_' + ifname[-ifname_max_width+1:] ifname = '_' + ifname[-ifname_max_width+1:]

View File

@ -24,6 +24,7 @@ I am your father...
""" """
# Import system libs # Import system libs
from datetime import datetime
import json import json
from operator import itemgetter from operator import itemgetter
@ -35,10 +36,11 @@ class GlancesPlugin(object):
"""Main class for Glances' plugin.""" """Main class for Glances' plugin."""
def __init__(self, args=None): def __init__(self, args=None, items_history_list=None):
"""Init the plugin of plugins class.""" """Init the plugin of plugins class."""
# Plugin name (= module name without glances_) # Plugin name (= module name without glances_)
self.plugin_name = self.__class__.__module__[len('glances_'):] self.plugin_name = self.__class__.__module__[len('glances_'):]
logger.debug(_("Init plugin %s") % self.plugin_name)
# Init the args # Init the args
self.args = args self.args = args
@ -53,6 +55,10 @@ class GlancesPlugin(object):
# Init the stats list # Init the stats list
self.stats = None self.stats = None
# Init the history list
self.items_history_list = items_history_list
self.stats_history = self.init_stats_history()
# Init the limits dictionnary # Init the limits dictionnary
self.limits = dict() self.limits = dict()
@ -64,6 +70,49 @@ class GlancesPlugin(object):
"""Return the human-readable stats.""" """Return the human-readable stats."""
return str(self.stats) return str(self.stats)
def init_stats_history(self):
"""Init the stats history (dict of list)"""
ret = None
if self.args is not None and self.args.enable_history and self.get_items_history_list() is not None:
iList = [i['name'] for i in self.get_items_history_list()]
logger.debug(_("Stats history activated for plugin %s (items: %s)") % (self.plugin_name, iList))
ret = {}
# First column for the date
ret['date'] = []
for i in self.get_items_history_list():
# One column per item
ret[i['name']] = []
return ret
def reset_stats_history(self):
"""Reset the stats history (dict of list)"""
if self.args is not None and self.args.enable_history and self.get_items_history_list() is not None:
iList = [i['name'] for i in self.get_items_history_list()]
logger.debug(_("Reset history for plugin %s (items: %s)") % (self.plugin_name, iList))
self.stats_history = {}
# First column for the date
self.stats_history['date'] = []
for i in self.get_items_history_list():
# One column per item
self.stats_history[i['name']] = []
return self.stats_history
def update_stats_history(self):
"""Update stats history"""
if self.args is not None and self.args.enable_history and self.get_items_history_list() is not None:
self.stats_history['date'].append(datetime.now())
for i in self.get_items_history_list():
self.stats_history[i['name']].append(self.stats[i['name']])
return self.stats_history
def get_stats_history(self):
"""Return the stats history"""
return self.stats_history
def get_items_history_list(self):
"""Return the items history list"""
return self.items_history_list
def set_input(self, input_method, short_system_name=None): def set_input(self, input_method, short_system_name=None):
"""Set the input method. """Set the input method.
@ -283,7 +332,7 @@ class GlancesPlugin(object):
else: else:
return self.limits[self.plugin_name + '_' + header + '_' + 'careful'] return self.limits[self.plugin_name + '_' + header + '_' + 'careful']
def get_hide(self, header=""): def __get_hide(self, header=""):
"""Return the hide configuration list key for the current plugin.""" """Return the hide configuration list key for the current plugin."""
if header == "": if header == "":
try: try:
@ -298,7 +347,14 @@ class GlancesPlugin(object):
def is_hide(self, value, header=""): def is_hide(self, value, header=""):
"""Return True if the value is in the hide configuration list.""" """Return True if the value is in the hide configuration list."""
return value in self.get_hide(header=header) return value in self.__get_hide(header=header)
def has_alias(self, header):
"""Return the alias name for the relative header or None if nonexist"""
try:
return self.limits[self.plugin_name + '_' + header + '_' + 'alias'][0]
except (KeyError, IndexError):
return None
def msg_curse(self, args=None, max_width=None): def msg_curse(self, args=None, max_width=None):
"""Return default string to display in the curse interface.""" """Return default string to display in the curse interface."""

View File

@ -50,10 +50,10 @@ class Plugin(GlancesPlugin):
self.glancesgrabsensors = GlancesGrabSensors() self.glancesgrabsensors = GlancesGrabSensors()
# Instance for the HDDTemp Plugin in order to display the hard disks temperatures # Instance for the HDDTemp Plugin in order to display the hard disks temperatures
self.hddtemp_plugin = HddTempPlugin() self.hddtemp_plugin = HddTempPlugin(args=args)
# Instance for the BatPercent in order to display the batteries capacities # Instance for the BatPercent in order to display the batteries capacities
self.batpercent_plugin = BatPercentPlugin() self.batpercent_plugin = BatPercentPlugin(args=args)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -136,7 +136,12 @@ class Plugin(GlancesPlugin):
for item in self.stats: for item in self.stats:
# New line # New line
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
msg = '{0:18}'.format(item['label'][:18]) # Alias for the lable name ?
label = self.has_alias(item['label'].lower())
if label is None:
label = item['label']
label = label[:18]
msg = '{0:18}'.format(label)
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
msg = '{0:>5}'.format(item['value']) msg = '{0:>5}'.format(item['value'])
if item['type'] == 'battery': if item['type'] == 'battery':