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]
# Define the list of hidden network interfaces (comma separeted)
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_warning=5000000
wlan0_rx_critical=6000000
@ -62,6 +64,8 @@ wlan0_tx_critical=1000000
[diskio]
# Define the list of hidden disks (comma separeted)
hide=sda2,sda5
# Alias for sda1
#sda1_alias=IntDisk
[fs]
# Default limits for free filesytem space in %
@ -85,6 +89,12 @@ temperature_hdd_critical=60
battery_careful=80
battery_warning=90
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]
# Limit values for CPU/MEM per process in %

View File

@ -51,7 +51,9 @@ critical=90
#[network]
# Define the list of hidden network interfaces (comma separeted)
#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_warning=5000000
#wlan0_rx_critical=6000000
@ -62,6 +64,8 @@ critical=90
#[diskio]
# Define the list of hidden disks (comma separeted)
#hide=sda2,sda5
# Alias for sda1
#sda1_alias=IntDisk
[fs]
# Default limits for free filesytem space in %
@ -85,6 +89,11 @@ temperature_hdd_critical=60
battery_careful=80
battery_warning=90
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]
# 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
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(),
__psutil_version))

View File

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

View File

@ -60,6 +60,8 @@ class GlancesMain(object):
parser.add_argument('-C', '--config', dest='conf_file',
help=_('path to the configuration file'))
# 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,
dest='disable_bold', help=_('disable bold mode in the terminal'))
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):
# Init stats
self.stats = GlancesStats(config)
self.stats = GlancesStats(config=config, args=args)
# Initial system informations update
self.stats.update()

View File

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

View File

@ -163,6 +163,17 @@ class GlancesCurses(object):
self.term_window.nodelay(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):
# Catch ESC key AND numlock key (issue #163)
keycode = [0, 0]
@ -205,6 +216,9 @@ class GlancesCurses(object):
elif self.pressedkey == ord('f'):
# 'f' > Show/hide fs stats
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'):
# 'h' > Show/hide help
self.args.help_tag = not self.args.help_tag
@ -223,6 +237,9 @@ class GlancesCurses(object):
elif self.pressedkey == ord('p'):
# 'p' > Sort processes 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'):
# 's' > Show/hide sensors stats (Linux-only)
self.args.disable_sensors = not self.args.disable_sensors
@ -421,6 +438,58 @@ class GlancesCurses(object):
self.new_line()
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
def display_plugin(self, plugin_stats,
@ -532,8 +601,8 @@ class GlancesCurses(object):
while not countdown.finished():
# Getkey
if self.__catch_key() > -1:
# flush display
self.flush(stats, cs_status=cs_status)
# Redraw display
self.flush(stats, cs_status=cs_status)
# Wait 100ms...
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'},
'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):
"""
@ -43,7 +49,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None):
"""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
self.display_curse = True
@ -123,6 +129,9 @@ class Plugin(GlancesPlugin):
for key in list(self.stats.keys()):
self.stats[key] = float(self.stats[key])
# Update the history list
self.update_stats_history()
return self.stats
def msg_curse(self, args=None):

View File

@ -128,13 +128,15 @@ class Plugin(GlancesPlugin):
# Do not display hidden interfaces
if self.is_hide(i['disk_name']):
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
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
disk_name = '_' + i['disk_name'][-8:]
else:
disk_name = i['disk_name']
disk_name = '_' + disk_name[-8:]
msg = '{0:9}'.format(disk_name)
ret.append(self.curse_add_line(msg))
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())
msg = msg_col.format(_("s"), _("Show/hide sensors stats"))
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_new_line())
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',
'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):
@ -45,7 +52,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None):
"""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
self.display_curse = True
@ -53,6 +60,12 @@ class Plugin(GlancesPlugin):
# Init stats
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):
"""Reset/init the stats."""
self.stats = {}
@ -62,12 +75,6 @@ class Plugin(GlancesPlugin):
# Reset stats
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':
# Update stats using the standard system lib
@ -80,7 +87,7 @@ class Plugin(GlancesPlugin):
self.stats = {'min1': load[0],
'min5': load[1],
'min15': load[2],
'cpucore': nb_log_core}
'cpucore': self.nb_log_core}
elif self.get_input() == 'snmp':
# Update stats using SNMP
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid)
@ -98,7 +105,10 @@ class Plugin(GlancesPlugin):
for k, v in iteritems:
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

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',
'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):
@ -55,7 +60,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None):
"""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
self.display_curse = True
@ -146,6 +151,9 @@ class Plugin(GlancesPlugin):
# percent: the percentage usage calculated as (total - available) / 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
def msg_curse(self, args=None):

View File

@ -218,7 +218,10 @@ class Plugin(GlancesPlugin):
if self.is_hide(i['interface_name']):
continue
# 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:
# Cut interface name if it is too long
ifname = '_' + ifname[-ifname_max_width+1:]

View File

@ -24,6 +24,7 @@ I am your father...
"""
# Import system libs
from datetime import datetime
import json
from operator import itemgetter
@ -35,10 +36,11 @@ class GlancesPlugin(object):
"""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."""
# Plugin name (= module name without glances_)
self.plugin_name = self.__class__.__module__[len('glances_'):]
logger.debug(_("Init plugin %s") % self.plugin_name)
# Init the args
self.args = args
@ -53,6 +55,10 @@ class GlancesPlugin(object):
# Init the stats list
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
self.limits = dict()
@ -64,6 +70,49 @@ class GlancesPlugin(object):
"""Return the human-readable 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):
"""Set the input method.
@ -283,7 +332,7 @@ class GlancesPlugin(object):
else:
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."""
if header == "":
try:
@ -298,7 +347,14 @@ class GlancesPlugin(object):
def is_hide(self, value, header=""):
"""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):
"""Return default string to display in the curse interface."""

View File

@ -50,10 +50,10 @@ class Plugin(GlancesPlugin):
self.glancesgrabsensors = GlancesGrabSensors()
# 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
self.batpercent_plugin = BatPercentPlugin()
self.batpercent_plugin = BatPercentPlugin(args=args)
# We want to display the stat in the curse interface
self.display_curse = True
@ -136,7 +136,12 @@ class Plugin(GlancesPlugin):
for item in self.stats:
# 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))
msg = '{0:>5}'.format(item['value'])
if item['type'] == 'battery':