提交 41342c81 编写于 作者: N Nicolargo

Add Network, FS, diskio to the graph generation feature

上级 94614e1f
......@@ -197,7 +197,7 @@ class GlancesMain(object):
if not os.access(args.path_history, os.W_OK):
logger.critical(_("History output path (%s) do not exist or is not writable") % args.path_history)
sys.exit(2)
logger.info(_("History output path is %s") % args.path_history)
logger.debug(_("History output path is set to %s") % args.path_history)
return args
......
......@@ -23,7 +23,7 @@
import sys
# Import Glances lib
from glances.core.glances_globals import glances_logs, glances_processes, is_linux, is_bsd, is_mac, is_windows, logger
from glances.core.glances_globals import glances_logs, glances_processes, is_mac, is_windows, logger
from glances.core.glances_timer import Timer
# Import curses lib for "normal" operating system and consolelog for Windows
......@@ -31,9 +31,10 @@ if not is_windows:
try:
import curses
import curses.panel
from curses.textpad import Textbox, rectangle
from curses.textpad import Textbox
except ImportError:
logger.critical('Curses module not found. Glances cannot start in standalone mode.')
logger.critical(
'Curses module not found. Glances cannot start in standalone mode.')
sys.exit(1)
else:
from glances.outputs.glances_colorconsole import WCurseLight
......@@ -184,15 +185,17 @@ class GlancesCurses(object):
self.reset_history_tag = False
self.history_tag = False
if args.enable_history:
logger.info('Stats history enabled with output path %s' % args.path_history)
logger.info('Stats history enabled with output path %s' %
args.path_history)
from glances.outputs.glances_history import GlancesHistory
self.glances_history = GlancesHistory(args.path_history)
if not self.glances_history.graph_enabled():
args.enable_history = False
logger.error('Stats history disabled because MatPlotLib is not installed')
logger.error(
'Stats history disabled because MatPlotLib is not installed')
def set_cursor(self, value):
"""Configure the cursor
"""Configure the cursor
0: invisible
1: visible
2: very visible
......@@ -234,7 +237,7 @@ class GlancesCurses(object):
self.args.percpu = not self.args.percpu
elif self.pressedkey == ord('2'):
# '2' > Enable/disable left sidebar
self.args.disable_left_sidebar = not self.args.disable_left_sidebar
self.args.disable_left_sidebar = not self.args.disable_left_sidebar
elif self.pressedkey == ord('/'):
# '/' > Switch between short/long name for processes
self.args.process_short_name = not self.args.process_short_name
......@@ -289,7 +292,7 @@ class GlancesCurses(object):
glances_processes.setmanualsortkey(self.args.process_sorted_by)
elif self.pressedkey == ord('r'):
# 'r' > Reset history
self.reset_history_tag = not self.reset_history_tag
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
......@@ -318,7 +321,7 @@ class GlancesCurses(object):
return self.pressedkey
def end(self):
"""Shutdown the curses window."""
"""Shutdown the curses window."""
if hasattr(curses, 'echo'):
curses.echo()
if hasattr(curses, 'nocbreak'):
......@@ -329,7 +332,7 @@ class GlancesCurses(object):
except Exception:
pass
curses.endwin()
def init_line_column(self):
"""Init the line and column position for the curses inteface"""
self.line = 0
......@@ -386,10 +389,10 @@ class GlancesCurses(object):
# Update the stats messages
###########################
# Update the client server status
self.args.cs_status = cs_status
stats_system = stats.get_plugin('system').get_stats_display(args=self.args)
stats_system = stats.get_plugin(
'system').get_stats_display(args=self.args)
stats_uptime = stats.get_plugin('uptime').get_stats_display()
if self.args.percpu:
stats_percpu = stats.get_plugin('percpu').get_stats_display()
......@@ -398,27 +401,37 @@ class GlancesCurses(object):
stats_load = stats.get_plugin('load').get_stats_display()
stats_mem = stats.get_plugin('mem').get_stats_display()
stats_memswap = stats.get_plugin('memswap').get_stats_display()
stats_network = stats.get_plugin('network').get_stats_display(args=self.args, max_width=plugin_max_width)
stats_diskio = stats.get_plugin('diskio').get_stats_display(args=self.args)
stats_fs = stats.get_plugin('fs').get_stats_display(args=self.args, max_width=plugin_max_width)
stats_sensors = stats.get_plugin('sensors').get_stats_display(args=self.args)
stats_network = stats.get_plugin('network').get_stats_display(
args=self.args, max_width=plugin_max_width)
stats_diskio = stats.get_plugin(
'diskio').get_stats_display(args=self.args)
stats_fs = stats.get_plugin('fs').get_stats_display(
args=self.args, max_width=plugin_max_width)
stats_sensors = stats.get_plugin(
'sensors').get_stats_display(args=self.args)
stats_now = stats.get_plugin('now').get_stats_display()
stats_processcount = stats.get_plugin('processcount').get_stats_display(args=self.args)
stats_monitor = stats.get_plugin('monitor').get_stats_display(args=self.args)
stats_alert = stats.get_plugin('alert').get_stats_display(args=self.args)
stats_processcount = stats.get_plugin(
'processcount').get_stats_display(args=self.args)
stats_monitor = stats.get_plugin(
'monitor').get_stats_display(args=self.args)
stats_alert = stats.get_plugin(
'alert').get_stats_display(args=self.args)
# Adapt number of processes to the available space
max_processes_displayed = screen_y - 11 - self.get_stats_display_height(stats_alert)
max_processes_displayed = screen_y - 11 - \
self.get_stats_display_height(stats_alert)
if not self.args.disable_process_extended:
max_processes_displayed -= 4
if max_processes_displayed < 0:
max_processes_displayed = 0
max_processes_displayed = 0
if glances_processes.get_max_processes() is None or \
glances_processes.get_max_processes() != max_processes_displayed:
logger.debug(_("Set number of displayed processes to %s") % max_processes_displayed)
logger.debug(
_("Set number of displayed processes to %s") % max_processes_displayed)
glances_processes.set_max_processes(max_processes_displayed)
stats_processlist = stats.get_plugin('processlist').get_stats_display(args=self.args)
stats_processlist = stats.get_plugin(
'processlist').get_stats_display(args=self.args)
# Display the stats on the curses interface
###########################################
......@@ -426,13 +439,15 @@ class GlancesCurses(object):
# Help screen (on top of the other stats)
if self.args.help_tag:
# Display the stats...
self.display_plugin(stats.get_plugin('help').get_stats_display(args=self.args))
self.display_plugin(
stats.get_plugin('help').get_stats_display(args=self.args))
# ... and exit
return False
# Display first line (system+uptime)
self.new_line()
l = self.get_stats_display_width(stats_system) + self.get_stats_display_width(stats_uptime) + self.space_between_column
l = self.get_stats_display_width(
stats_system) + self.get_stats_display_width(stats_uptime) + self.space_between_column
self.display_plugin(stats_system, display_optional=(screen_x >= l))
self.new_column()
self.display_plugin(stats_uptime)
......@@ -440,14 +455,16 @@ class GlancesCurses(object):
# Display second line (CPU|PERCPU+LOAD+MEM+SWAP+<SUMMARY>)
# CPU|PERCPU
self.init_column()
self.new_line()
self.new_line()
if self.args.percpu:
l = self.get_stats_display_width(stats_percpu)
else:
l = self.get_stats_display_width(stats_cpu)
l += self.get_stats_display_width(stats_load) + self.get_stats_display_width(stats_mem) + self.get_stats_display_width(stats_memswap)
l += self.get_stats_display_width(stats_load) + self.get_stats_display_width(
stats_mem) + self.get_stats_display_width(stats_memswap)
# Space between column
space_number = int(stats_load['msgdict'] != []) + int(stats_mem['msgdict'] != []) + int(stats_memswap['msgdict'] != [])
space_number = int(stats_load['msgdict'] != [
]) + int(stats_mem['msgdict'] != []) + int(stats_memswap['msgdict'] != [])
if space_number == 0:
space_number = 1
if screen_x > (space_number * self.space_between_column + l):
......@@ -460,7 +477,8 @@ class GlancesCurses(object):
self.new_column()
self.display_plugin(stats_load)
self.new_column()
self.display_plugin(stats_mem, display_optional=(screen_x >= (space_number * self.space_between_column + l)))
self.display_plugin(stats_mem, display_optional=(
screen_x >= (space_number * self.space_between_column + l)))
self.new_column()
self.display_plugin(stats_memswap)
# Space between column
......@@ -471,9 +489,9 @@ class GlancesCurses(object):
# Display left sidebar (NETWORK+DISKIO+FS+SENSORS+Current time)
self.init_column()
if (not (self.args.disable_network and self.args.disable_diskio \
and self.args.disable_fs and self.args.disable_sensors)) \
and not self.args.disable_left_sidebar:
if (not (self.args.disable_network and self.args.disable_diskio
and self.args.disable_fs and self.args.disable_sensors)) \
and not self.args.disable_left_sidebar:
self.new_line()
self.display_plugin(stats_network)
self.new_line()
......@@ -490,18 +508,19 @@ class GlancesCurses(object):
# Restore line position
self.next_line = self.saved_line
# Display right sidebar (PROCESS_COUNT+MONITORED+PROCESS_LIST+ALERT)
# Display right sidebar
# (PROCESS_COUNT+MONITORED+PROCESS_LIST+ALERT)
self.new_column()
self.new_line()
self.display_plugin(stats_processcount)
if glances_processes.get_process_filter() == None and cs_status == 'None':
if glances_processes.get_process_filter() is None and cs_status == 'None':
# Do not display stats monitor list if a filter exist
self.new_line()
self.display_plugin(stats_monitor)
self.new_line()
self.display_plugin(stats_processlist,
display_optional=(screen_x > 102),
display_additional=(is_mac == False),
display_additional=(not is_mac),
max_y=(screen_y - self.get_stats_display_height(stats_alert) - 2))
self.new_line()
self.display_plugin(stats_alert)
......@@ -509,36 +528,40 @@ class GlancesCurses(object):
# 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.display_popup(
_("Generate graphs history in %s\nPlease wait...") % 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)
self.glances_history.reset(stats)
elif (self.history_tag or self.reset_history_tag) and not self.args.enable_history:
try:
self.glances_history.graph_enabled()
except:
self.display_popup(_("History disabled\nEnable it using --enable-history"))
self.display_popup(
_("History disabled\nEnable it using --enable-history"))
else:
self.display_popup(_("History disabled\nPlease install MatPlotLib"))
self.display_popup(
_("History disabled\nPlease install MatPlotLib"))
self.history_tag = False
self.reset_history_tag = False
# Display edit filter popup
# Only in standalone mode (cs_status == 'None')
if self.edit_filter and cs_status == 'None':
new_filter = self.display_popup(_("Process filter pattern: "),
new_filter = self.display_popup(_("Process filter pattern: "),
is_input=True,
input_value=glances_processes.get_process_filter())
glances_processes.set_process_filter(new_filter)
elif self.edit_filter and cs_status != 'None':
self.display_popup(_("Process filter only available in standalone mode"))
self.display_popup(
_("Process filter only available in standalone mode"))
self.edit_filter = False
return True
def display_popup(self, message,
size_x=None, size_y=None,
def display_popup(self, message,
size_x=None, size_y=None,
duration=3,
is_input=False,
input_size=30,
......@@ -553,7 +576,7 @@ class GlancesCurses(object):
Display a centered popup with the given message and a input field
If size_x and size_y: set the popup size
else set it automatically
Return the input string or None if the field is empty
Return the input string or None if the field is empty
"""
# Center the popup
......@@ -568,13 +591,13 @@ class GlancesCurses(object):
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
return False
pos_x = int((screen_x - size_x) / 2)
pos_y = int((screen_y - size_y) / 2)
# Create the popup
popup = curses.newwin(size_y, size_x, pos_y, pos_x)
# Fill the popup
popup.border()
......@@ -600,7 +623,8 @@ class GlancesCurses(object):
textbox.edit()
self.set_cursor(0)
if textbox.gather() != '':
logger.debug(_("User enters the following process filter patern: %s") % textbox.gather())
logger.debug(
_("User enters the following process filter patern: %s") % textbox.gather())
return textbox.gather()[:-1]
else:
logger.debug(_("User clears the process filter patern"))
......@@ -611,9 +635,9 @@ class GlancesCurses(object):
curses.napms(duration * 1000)
return True
def display_plugin(self, plugin_stats,
def display_plugin(self, plugin_stats,
display_optional=True,
display_additional=True,
display_additional=True,
max_y=65535):
"""Display the plugin_stats on the screen.
......@@ -643,7 +667,7 @@ class GlancesCurses(object):
display_y = screen_y - self.get_stats_display_height(plugin_stats)
else:
display_y = self.line
# Display
x = display_x
y = display_y
......@@ -673,8 +697,9 @@ class GlancesCurses(object):
try:
self.term_window.addnstr(y, x,
m['msg'],
screen_x - x, # Do not disply outside the screen
self.__colors_list[m['decoration']])
# Do not disply outside the screen
screen_x - x,
self.__colors_list[m['decoration']])
except:
pass
else:
......@@ -721,7 +746,7 @@ class GlancesCurses(object):
# Getkey
if self.__catch_key() > -1:
# Redraw display
self.flush(stats, cs_status=cs_status)
self.flush(stats, cs_status=cs_status)
# Wait 100ms...
curses.napms(100)
......@@ -734,11 +759,11 @@ class GlancesCurses(object):
if without_option:
# Size without options
c = len(max(''.join([(i['msg'] if not i['optional'] else "")
for i in curse_msg['msgdict']]).split('\n'), key=len))
for i in curse_msg['msgdict']]).split('\n'), key=len))
else:
# Size with all options
c = len(max(''.join([i['msg']
for i in curse_msg['msgdict']]).split('\n'), key=len))
for i in curse_msg['msgdict']]).split('\n'), key=len))
except:
return 0
else:
......@@ -758,14 +783,15 @@ class GlancesCurses(object):
if not is_windows:
class glances_textbox(Textbox):
"""
"""
def __init__(*args, **kwargs):
Textbox.__init__(*args, **kwargs)
def do_command(self, ch):
if ch == 10: # Enter
if ch == 10: # Enter
return 0
if ch == 127: # Enter
if ch == 127: # Enter
return 8
return Textbox.do_command(self, ch)
\ No newline at end of file
return Textbox.do_command(self, ch)
......@@ -29,10 +29,10 @@ from glances.core.glances_globals import logger
try:
from matplotlib import __version__ as matplotlib_version
import matplotlib.pyplot as plt
import matplotlib.dates as dates
except:
matplotlib_check = False
logger.warning('Can not load Matplotlib library. Please install it using "pip install matplotlib"')
logger.warning(
'Can not load Matplotlib library. Please install it using "pip install matplotlib"')
else:
matplotlib_check = True
logger.info('Load Matplotlib version %s' % matplotlib_version)
......@@ -65,6 +65,28 @@ class GlancesHistory(object):
stats.get_plugin(p).reset_stats_history()
return True
def get_graph_color(self, item):
"""
Get the item's color
"""
try:
ret = item['color']
except KeyError:
return '#FFFFFF'
else:
return ret
def get_graph_ylegend(self, item):
"""
Get the item's Y legend
"""
try:
ret = item['label_y']
except KeyError:
return ''
else:
return ' ' + ret
def generate_graph(self, stats):
"""
Generate graphs from plugins history
......@@ -74,32 +96,92 @@ class GlancesHistory(object):
for p in stats.getAllPlugins():
h = stats.get_plugin(p).get_stats_history()
if h is not None:
# Build the graph
# fig = plt.figure(dpi=72)
ax = plt.subplot(1, 1, 1)
# Label
plt.title("%s stats" % p)
handles = []
for i in stats.get_plugin(p).get_items_history_list():
handles.append(plt.Rectangle((0, 0), 1, 1, fc=i['color'], ec=i['color'], linewidth=1))
labels = [i['name'] for i in stats.get_plugin(p).get_items_history_list()]
plt.legend(handles, labels, loc=1, prop={'size': 9})
formatter = dates.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(formatter)
# ax.set_ylabel('%')
# Draw the stats
for i in stats.get_plugin(p).get_items_history_list():
ax.plot_date(h['date'], h[i['name']],
i['color'],
label='%s' % i['name'],
xdate=True, ydate=False)
# Save and display
plt.savefig(os.path.join(self.output_folder, 'glances_%s.png' % p), dpi=72)
# plt.show()
# Init graph
plt.clf()
fig = plt.gcf()
fig.set_size_inches(20, 10)
# Data
if h is None:
# History (h) not available for plugin (p)
continue
index_graph = 0
for i in stats.get_plugin(p).get_items_history_list():
if i['name'] in h.keys():
# The key exist
# Add the curve in the current chart
logger.debug("Generate graph: %s %s" % (p, i['name']))
index_graph += 1
plt.title(p.capitalize())
plt.subplot(len(stats.get_plugin(p).get_items_history_list()), 1, index_graph)
plt.ylabel(i['name'] + self.get_graph_ylegend(i))
plt.grid(True)
plt.plot_date(h['date'], h[i['name']],
fmt='', drawstyle='default', linestyle='-',
color=self.get_graph_color(i),
xdate=True, ydate=False)
else:
# The key did not exist
# Find if anothers key ends with the key
# Ex: key='tx' => 'ethernet_tx'
stats_history_filtered = sorted([key for key in h.keys() if key.endswith(i['name'])])
logger.debug("Generate graphs: %s %s" % (p, stats_history_filtered))
if len(stats_history_filtered) > 0:
# Create 'n' graph
# Each graph iter through the stats
plt.clf()
index_item = 0
for k in stats_history_filtered:
index_item += 1
plt.title(p.capitalize())
plt.subplot(len(stats_history_filtered), 1, index_item)
plt.ylabel(k + self.get_graph_ylegend(i))
plt.grid(True)
plt.plot_date(h['date'], h[k],
fmt='', drawstyle='default', linestyle='-',
color=self.get_graph_color(i),
xdate=True, ydate=False)
# Save the graph to output file
plt.xlabel('Date')
plt.savefig(os.path.join(self.output_folder, 'glances_%s_%s.png' % (p, i['name'])), dpi=72)
if index_graph > 0:
# Save the graph to output file
plt.xlabel('Date')
plt.savefig(os.path.join(self.output_folder, 'glances_%s.png' % (p)), dpi=72)
plt.close()
return True
def generate_graph_OLD(self, stats):
"""
Generate graphs from plugins history
"""
if not self.graph_enabled():
return False
for p in stats.getAllPlugins():
h = stats.get_plugin(p).get_stats_history()
# Generate graph (init)
plt.clf()
# Title and axis
plt.title(p.capitalize())
plt.grid(True)
if h is not None:
# History (h) available for plugin (p)
index = 1
for k, v in h.iteritems():
if k != 'date':
plt.subplot(len(h), 1, index)
index += 1
plt.xlabel('Date')
plt.ylabel(self.get_graph_ylegend(stats, p, k))
# Data
plt.plot_date(h['date'], h[k],
fmt='', drawstyle='default', linestyle='-',
color=self.get_graph_color(stats, p, k),
xdate=True, ydate=False)
# Save the graph to output file
plt.savefig(os.path.join(self.output_folder, 'glances_%s_%s.png' % (p, k)), dpi=72)
return True
......@@ -34,10 +34,12 @@ snmp_oid = {'default': {'user': '1.3.6.1.4.1.2021.11.9.0',
'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
# - 'name' define the stat identifier
# - 'color' define the graph color in #RGB format
# - 'label_y' define the Y label
# 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'}]
items_history_list = [{'name': 'user', 'color': '#00FF00', 'label_y': '(%)'},
{'name': 'system', 'color': '#FF0000', 'label_y': '(%)'}]
class Plugin(GlancesPlugin):
......
......@@ -25,6 +25,12 @@ from glances.plugins.glances_plugin import GlancesPlugin
import psutil
# 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': 'read_bytes', 'color': '#00FF00', 'label_y': '(B/s)'},
{'name': 'write_bytes', 'color': '#FF0000', 'label_y': '(B/s)'}]
class Plugin(GlancesPlugin):
......@@ -35,7 +41,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
......@@ -104,6 +110,9 @@ class Plugin(GlancesPlugin):
# No standard way for the moment...
pass
# Update the history list
self.update_stats_history('disk_name')
return self.stats
def msg_curse(self, args=None):
......
......@@ -19,11 +19,7 @@
"""File system plugin."""
# System libs
import base64
# Glances libs
from glances.core.glances_globals import version, logger
from glances.plugins.glances_plugin import GlancesPlugin
# PSutil lib for local grab
......@@ -54,6 +50,11 @@ snmp_oid = {'default': {'mnt_point': '1.3.6.1.4.1.2021.9.1.2',
'used': '1.3.6.1.2.1.25.2.3.1.6'}}
snmp_oid['esxi'] = snmp_oid['windows']
# 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):
......@@ -64,7 +65,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
......@@ -114,12 +115,12 @@ class Plugin(GlancesPlugin):
elif self.get_input() == 'snmp':
# Update stats using SNMP
# SNMP bulk command to get all file system in one shot
# SNMP bulk command to get all file system in one shot
try:
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
bulk=True)
except KeyError:
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
bulk=True)
# Loop over fs
......@@ -135,7 +136,7 @@ class Plugin(GlancesPlugin):
fs_current['size'] = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
fs_current['used'] = int(fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
fs_current['percent'] = float(fs_current['used'] * 100 / fs_current['size'])
self.stats.append(fs_current)
self.stats.append(fs_current)
else:
# Default behavor
for fs in fs_stat:
......@@ -147,6 +148,9 @@ class Plugin(GlancesPlugin):
fs_current['percent'] = float(fs_stat[fs]['percent'])
self.stats.append(fs_current)
# Update the history list
self.update_stats_history('mnt_point')
return self.stats
def msg_curse(self, args=None, max_width=None):
......
......@@ -31,7 +31,12 @@ snmp_oid = {'default': {'total': '1.3.6.1.4.1.2021.4.3.0',
'windows': {'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'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):
......@@ -43,7 +48,8 @@ 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
......@@ -71,7 +77,8 @@ class Plugin(GlancesPlugin):
# 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)
# 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):
......@@ -81,21 +88,26 @@ class Plugin(GlancesPlugin):
if self.get_short_system_name() == 'windows':
# Mem stats for Windows OS are stored in the FS table
try:
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
bulk=True)
except KeyError:
self.reset()
else:
for fs in fs_stat:
# The virtual memory concept is used by the operating system to extend (virtually) the physical
# memory and thus to run more programs by swapping unused memory zone (page) to a disk file.
for fs in fs_stat:
# The virtual memory concept is used by the operating system to extend (virtually) the physical
# memory and thus to run more programs by swapping
# unused memory zone (page) to a disk file.
if fs == 'Virtual Memory':
self.stats['total'] = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
self.stats['used'] = int(fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
self.stats['percent'] = float(self.stats['used'] * 100 / self.stats['total'])
self.stats['free'] = self.stats['total'] - self.stats['used']
self.stats['total'] = int(
fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
self.stats['used'] = int(
fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
self.stats['percent'] = float(
self.stats['used'] * 100 / self.stats['total'])
self.stats['free'] = self.stats[
'total'] - self.stats['used']
break
else:
else:
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid['default'])
if self.stats['total'] == '':
......@@ -109,8 +121,13 @@ class Plugin(GlancesPlugin):
# used=total-free
self.stats['used'] = self.stats['total'] - self.stats['free']
# percent: the percentage usage calculated as (total - available) / total * 100.
self.stats['percent'] = float((self.stats['total'] - self.stats['free']) / self.stats['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)
# Update the history list
self.update_stats_history()
return self.stats
......
......@@ -33,6 +33,12 @@ snmp_oid = {'default': {'interface_name': '1.3.6.1.2.1.2.2.1.2',
'cumulative_rx': '1.3.6.1.2.1.2.2.1.10',
'cumulative_tx': '1.3.6.1.2.1.2.2.1.16'}}
# 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': 'rx', 'color': '#00FF00', 'label_y': '(bit/s)'},
{'name': 'tx', 'color': '#FF0000', 'label_y': '(bit/s)'}]
class Plugin(GlancesPlugin):
......@@ -43,7 +49,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
......@@ -90,7 +96,7 @@ class Plugin(GlancesPlugin):
for net in network_new:
try:
# Try necessary to manage dynamic network interface
netstat = {}
netstat = {}
netstat['interface_name'] = net
netstat['time_since_update'] = time_since_update
netstat['cumulative_rx'] = network_new[net].bytes_recv
......@@ -115,10 +121,10 @@ class Plugin(GlancesPlugin):
# SNMP bulk command to get all network interface in one shot
try:
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
bulk=True)
except KeyError:
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
bulk=True)
# Previous network interface stats are stored in the network_old variable
......@@ -166,6 +172,9 @@ class Plugin(GlancesPlugin):
# Save stats to compute next bitrate
self.network_old = network_new
# Update the history list
self.update_stats_history('interface_name')
return self.stats
def msg_curse(self, args=None, max_width=None):
......
......@@ -70,39 +70,48 @@ class GlancesPlugin(object):
"""Return the human-readable stats."""
return str(self.stats)
def add_item_history(self, key, value):
"""Add an new item (key, value) to the current history"""
try:
self.stats_history[key].append(value)
except KeyError:
self.stats_history[key] = [value]
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))
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))
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):
def update_stats_history(self, item_name=''):
"""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())
if self.stats != [] and self.args is not None and self.args.enable_history and self.get_items_history_list() is not None:
self.add_item_history('date', datetime.now())
for i in self.get_items_history_list():
self.stats_history[i['name']].append(self.stats[i['name']])
if type(self.stats) is list:
# Stats is a list of data
# Iter throught it (for exemple, iter throught network
# interface)
for l in self.stats:
self.add_item_history(
l[item_name] + '_' + i['name'], l[i['name']])
else:
# Stats is not a list
# Add the item to the history directly
self.add_item_history(i['name'], self.stats[i['name']])
return self.stats_history
def get_stats_history(self):
......@@ -160,10 +169,11 @@ class GlancesPlugin(object):
if len(snmp_oid) == 1:
# Bulk command for only one OID
# Note: key is the item indexed but the OID result
# Note: key is the item indexed but the OID result
for item in snmpresult:
if item.keys()[0].startswith(snmp_oid.values()[0]):
ret[snmp_oid.keys()[0] + item.keys()[0].split(snmp_oid.values()[0])[1]] = item.values()[0]
ret[snmp_oid.keys()[0] + item.keys()
[0].split(snmp_oid.values()[0])[1]] = item.values()[0]
else:
# Build the internal dict with the SNMP result
# Note: key is the first item in the snmp_oid
......@@ -203,19 +213,20 @@ class GlancesPlugin(object):
"""
Return the stats object for a specific item (in JSON format)
Stats should be a list of dict (processlist, network...)
"""
"""
if type(self.stats) is not list:
if type(self.stats) is dict:
try:
return json.dumps({ item: self.stats[item] })
return json.dumps({item: self.stats[item]})
except KeyError as e:
logger.error(_("Can not get item %s (%s)") % (item, e))
else:
return None
else:
try:
# Source: http://stackoverflow.com/questions/4573875/python-get-index-of-dictionary-item-in-list
return json.dumps({ item: map(itemgetter(item), self.stats) })
# Source:
# http://stackoverflow.com/questions/4573875/python-get-index-of-dictionary-item-in-list
return json.dumps({item: map(itemgetter(item), self.stats)})
except (KeyError, ValueError) as e:
logger.error(_("Can not get item %s (%s)") % (item, e))
return None
......@@ -231,9 +242,10 @@ class GlancesPlugin(object):
if value.isdigit():
value = int(value)
try:
return json.dumps({ value: [i for i in self.stats if i[item] == value] })
return json.dumps({value: [i for i in self.stats if i[item] == value]})
except (KeyError, ValueError) as e:
logger.error(_("Can not get item(%s)=value(%s) (%s)") % (item, value,e))
logger.error(
_("Can not get item(%s)=value(%s) (%s)") % (item, value, e))
return None
def load_limits(self, config):
......@@ -243,9 +255,11 @@ class GlancesPlugin(object):
for s, v in config.items(self.plugin_name):
# Read limits
try:
self.limits[self.plugin_name + '_' + s] = config.get_option(self.plugin_name, s)
self.limits[
self.plugin_name + '_' + s] = config.get_option(self.plugin_name, s)
except ValueError:
self.limits[self.plugin_name + '_' + s] = config.get_raw_option(self.plugin_name, s).split(",")
self.limits[
self.plugin_name + '_' + s] = config.get_raw_option(self.plugin_name, s).split(",")
def set_limits(self, input_limits):
"""Set the limits to input_limits."""
......@@ -389,8 +403,8 @@ class GlancesPlugin(object):
return ret
def curse_add_line(self, msg, decoration="DEFAULT",
optional=False, additional=False,
def curse_add_line(self, msg, decoration="DEFAULT",
optional=False, additional=False,
splittable=False):
"""Return a dict with
......@@ -424,7 +438,7 @@ class GlancesPlugin(object):
def set_align(self, align='left'):
"""Set the Curse align"""
if align in ('left', 'right', 'bottom'):
if align in ('left', 'right', 'bottom'):
self.align = align
else:
self.align = 'left'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册