mirror of
https://github.com/nicolargo/glances.git
synced 2024-11-26 03:27:06 +03:00
Merge branch 'feature/issue447' into develop
This commit is contained in:
commit
8b03f1a8d6
1
NEWS
1
NEWS
@ -5,6 +5,7 @@ Glances Version 2.x
|
||||
Version 2.X
|
||||
===========
|
||||
|
||||
* Add the RAID plugins (issue #447)
|
||||
* Fix incorrect kernel thread detection with --hide-kernel-threads (issue #457)
|
||||
* Handle IOError exception if no /etc/os-release to use Glances on Synology DSM (issue #458)
|
||||
* Check issue error in client/server mode (issue #459)
|
||||
|
@ -75,6 +75,8 @@ class GlancesMain(object):
|
||||
dest='disable_fs', help=_('disable filesystem module'))
|
||||
parser.add_argument('--disable-sensors', action='store_true', default=False,
|
||||
dest='disable_sensors', help=_('disable sensors module'))
|
||||
parser.add_argument('--disable-raid', action='store_true', default=False,
|
||||
dest='disable_raid', help=_('disable RAID module'))
|
||||
parser.add_argument('--disable-left-sidebar', action='store_true', default=False,
|
||||
dest='disable_left_sidebar', help=_('disable network, disk io, FS and sensors modules'))
|
||||
parser.add_argument('--disable-process', action='store_true', default=False,
|
||||
|
@ -291,6 +291,7 @@ class GlancesBottle(object):
|
||||
html += self.display_plugin('network', self.stats.get_plugin('network').get_stats_display(args=self.args))
|
||||
html += self.display_plugin('diskio', self.stats.get_plugin('diskio').get_stats_display(args=self.args))
|
||||
html += self.display_plugin('fs', self.stats.get_plugin('fs').get_stats_display(args=self.args))
|
||||
html += self.display_plugin('raid', self.stats.get_plugin('raid').get_stats_display(args=self.args))
|
||||
html += self.display_plugin('sensors', self.stats.get_plugin('sensors').get_stats_display(args=self.args))
|
||||
html += '</aside>'
|
||||
html += '<section id="rightstats">'
|
||||
|
@ -306,6 +306,9 @@ class _GlancesCurses(object):
|
||||
elif self.pressedkey == ord('r'):
|
||||
# 'r' > Reset history
|
||||
self.reset_history_tag = not self.reset_history_tag
|
||||
elif self.pressedkey == ord('R'):
|
||||
# 'R' > Hide RAID plugins
|
||||
self.args.disable_raid = not self.args.disable_raid
|
||||
elif self.pressedkey == ord('s'):
|
||||
# 's' > Show/hide sensors stats (Linux-only)
|
||||
self.args.disable_sensors = not self.args.disable_sensors
|
||||
@ -424,6 +427,8 @@ class _GlancesCurses(object):
|
||||
'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_raid = stats.get_plugin('raid').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()
|
||||
@ -507,7 +512,8 @@ 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 self.args.disable_fs and self.args.disable_raid
|
||||
and self.args.disable_sensors)) \
|
||||
and not self.args.disable_left_sidebar:
|
||||
self.new_line()
|
||||
self.display_plugin(stats_network)
|
||||
@ -516,6 +522,8 @@ class _GlancesCurses(object):
|
||||
self.new_line()
|
||||
self.display_plugin(stats_fs)
|
||||
self.new_line()
|
||||
self.display_plugin(stats_raid)
|
||||
self.new_line()
|
||||
self.display_plugin(stats_sensors)
|
||||
self.new_line()
|
||||
self.display_plugin(stats_now)
|
||||
|
@ -116,6 +116,9 @@ div#newline{
|
||||
#fs {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
#raid {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
#sensors {}
|
||||
#rightstats {}
|
||||
#alert {
|
||||
|
148
glances/plugins/glances_raid.py
Normal file
148
glances/plugins/glances_raid.py
Normal file
@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2014 Nicolargo <nicolas@nicolargo.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
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""RAID plugin."""
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
# pymdstat only available on GNU/Linux OS
|
||||
try:
|
||||
from pymdstat import MdStat
|
||||
except ImportError:
|
||||
logger.debug("pymdstat library not found. Glances cannot grab RAID info.")
|
||||
pass
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' RAID plugin.
|
||||
|
||||
stats is a dict (see pymdstat documentation)
|
||||
"""
|
||||
|
||||
def __init__(self, args=None):
|
||||
"""Init the plugin."""
|
||||
GlancesPlugin.__init__(self, args=args)
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
|
||||
# Init the stats
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
"""Reset/init the stats."""
|
||||
self.stats = {}
|
||||
|
||||
@GlancesPlugin._log_result_decorator
|
||||
def update(self):
|
||||
"""Update RAID stats using the input method."""
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
# Update stats using the PyMDstat lib (https://github.com/nicolargo/pymdstat)
|
||||
try:
|
||||
mds = MdStat()
|
||||
self.stats = mds.get_stats()['arrays']
|
||||
except Exception as e:
|
||||
logger.debug("Can not grab RAID stats (%s)" % e)
|
||||
return self.stats
|
||||
|
||||
elif self.get_input() == 'snmp':
|
||||
# Update stats using SNMP
|
||||
# No standard way for the moment...
|
||||
pass
|
||||
|
||||
return self.stats
|
||||
|
||||
def msg_curse(self, args=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
# Init the return message
|
||||
ret = []
|
||||
|
||||
# Only process if stats exist and display plugin enable...
|
||||
if not self.stats or args.disable_raid:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0:11}'.format(_('RAID disks'))
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
msg = '{0:>6}'.format(_("Used"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6}'.format(_("Avail"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Data
|
||||
arrays = self.stats.keys()
|
||||
arrays.sort()
|
||||
for array in arrays:
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# Display the current status
|
||||
status = self.raid_alert(self.stats[array]['status'], self.stats[array]['used'], self.stats[array]['available'])
|
||||
# Data: RAID type name | disk used | disk available
|
||||
array_type = self.stats[array]['type'].upper() if self.stats[array]['type'] is not None else _('UNKNOWN')
|
||||
msg = '{0:<5}{1:>6}'.format(array_type, array)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
if self.stats[array]['status'] == 'active':
|
||||
msg = '{0:>6}'.format(self.stats[array]['used'])
|
||||
ret.append(self.curse_add_line(msg, status))
|
||||
msg = '{0:>6}'.format(self.stats[array]['available'])
|
||||
ret.append(self.curse_add_line(msg, status))
|
||||
elif self.stats[array]['status'] == 'inactive':
|
||||
ret.append(self.curse_new_line())
|
||||
msg = '└─ Status {}'.format(self.stats[array]['status'])
|
||||
ret.append(self.curse_add_line(msg, status))
|
||||
components = self.stats[array]['components'].keys()
|
||||
components.sort()
|
||||
for i, component in enumerate(components):
|
||||
if i == len(components) - 1:
|
||||
tree_char = '└─'
|
||||
else:
|
||||
tree_char = '├─'
|
||||
ret.append(self.curse_new_line())
|
||||
msg = ' {0} disk {1}: '.format(tree_char, self.stats[array]['components'][component])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0}'.format(component)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
if self.stats[array]['used'] < self.stats[array]['available']:
|
||||
# Display current array configuration
|
||||
ret.append(self.curse_new_line())
|
||||
msg = '└─ Degraded mode'
|
||||
ret.append(self.curse_add_line(msg, status))
|
||||
if len(self.stats[array]['config']) < 17:
|
||||
ret.append(self.curse_new_line())
|
||||
msg = ' └─ {0}'.format(self.stats[array]['config'].replace('_', 'A'))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
return ret
|
||||
|
||||
def raid_alert(self, status, used, available):
|
||||
"""
|
||||
[available/used] means that ideally the array would have _available_ devices however, _used_ devices are in use.
|
||||
Obviously when used >= available then things are good.
|
||||
"""
|
||||
if status == 'inactive':
|
||||
return 'CRITICAL'
|
||||
if used < available:
|
||||
return 'WARNING'
|
||||
return 'OK'
|
Loading…
Reference in New Issue
Block a user