From 97bf2337762518a045508e871789b6906e906b33 Mon Sep 17 00:00:00 2001 From: nicolargo Date: Sat, 21 Nov 2015 10:53:49 +0100 Subject: [PATCH] Implement Folder plugin (issue #721) --- NEWS | 1 + README.rst | 3 +- conf/glances.conf | 37 ++++-- docs/glances-doc.rst | 36 +++++- docs/images/folders.png | Bin 0 -> 13715 bytes glances/folder_list.py | 193 +++++++++++++++++++++++++++++ glances/main.py | 2 + glances/outputs/glances_curses.py | 14 ++- glances/plugins/glances_folder.py | 112 +++++++++++++++++ glances/plugins/glances_fs.py | 2 +- glances/plugins/glances_sensors.py | 13 +- man/glances.1 | 5 + setup.py | 3 +- 13 files changed, 401 insertions(+), 20 deletions(-) create mode 100644 docs/images/folders.png create mode 100644 glances/folder_list.py create mode 100644 glances/plugins/glances_folder.py diff --git a/NEWS b/NEWS index cd777a7b..d7758a48 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Version 2.x Enhancements and new features: + * New folders' monitoring plugins (issue #721) * Add process summary min/max stats (issue #703) * Add timestamp to the CSV export module (issue #708) * [WebUI] add "pointer" cursor for sortable columns (issue #704 from @notFloran) diff --git a/README.rst b/README.rst index 1967c020..76fb419d 100644 --- a/README.rst +++ b/README.rst @@ -49,6 +49,7 @@ Optional dependencies: - ``matplotlib`` (for graphical/chart support) - ``pika`` (for the RabbitMQ/ActiveMQ export module) - ``py-cpuinfo`` (for the Quicklook CPU info module) +- ``scandir`` (for the Folders plugin) [Only for Python < 3.5] Installation ============ @@ -93,7 +94,7 @@ features (like the Web interface): .. code-block:: console - pip install bottle batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb potsdb statsd pystache docker-py pysnmp pika py-cpuinfo + pip install bottle batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb potsdb statsd pystache docker-py pysnmp pika py-cpuinfo scandir Install or upgrade Glances from the Git ``develop`` repository: diff --git a/conf/glances.conf b/conf/glances.conf index 2be758d9..dbee5a7d 100644 --- a/conf/glances.conf +++ b/conf/glances.conf @@ -1,4 +1,5 @@ [quicklook] +# Define CPU, MEM and SWAP thresholds in % cpu_careful=50 cpu_warning=70 cpu_critical=90 @@ -10,6 +11,7 @@ swap_warning=70 swap_critical=90 [cpu] +# Define CPU thresholds in % # Default values if not defined: 50/70/90 user_careful=50 user_warning=70 @@ -28,6 +30,7 @@ steal_critical=90 #steal_log=True [percpu] +# Define CPU thresholds in % # Default values if not defined: 50/70/90 user_careful=50 user_warning=70 @@ -40,6 +43,7 @@ system_warning=70 system_critical=90 [load] +# Define LOAD thresholds # Value * number of cores # Default values if not defined: 0.7/1.0/5.0 per number of cores # Source: http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages @@ -50,14 +54,14 @@ critical=5.0 #log=False [mem] -# Default limits for free RAM memory in % +# Define RAM thresholds in % # Default values if not defined: 50/70/90 careful=50 warning=70 critical=90 [memswap] -# Default limits for free swap memory in % +# Define SWAP thresholds in % # Default values if not defined: 50/70/90 careful=50 warning=70 @@ -85,7 +89,7 @@ critical=90 #sda1_alias=IntDisk [fs] -# Default limits for free filesytem space in % +# Define filesystem space thresholds in % # Default values if not defined: 50/70/90 # It is also possible to define per mount point value # Example: /_careful=40 @@ -95,18 +99,35 @@ critical=90 # Allow additionnals files types (comma-separated FS type) #allow=zfs +#[folders] +# Define a folder list to monitor +# The list is composed of items (list_#nb <= 10) +# An item is defined by: +# * path: absolute path +# * careful: optional careful threshold (in MB) +# * warning: optional warning threshold (in MB) +# * critical: optional critical threshold (in MB) +#folder_1_path=/tmp +#folder_1_careful=2500 +#folder_1_warning=3000 +#folder_1_critical=3500 +#folder_2_path=/home/nicolargo/Videos +#folder_2_warning=17000 +#folder_2_critical=20000 +#folder_3_path=/nonexisting + [sensors] -# Sensors core limits (in Celsius...) +# Sensors core thresholds (in Celsius...) # Default values if not defined: 60/70/80 temperature_core_careful=60 temperature_core_warning=70 temperature_core_critical=80 -# Temperatures in °C for hddtemp +# Temperatures threshold in °C for hddtemp # Default values if not defined: 45/52/60 temperature_hdd_careful=45 temperature_hdd_warning=52 temperature_hdd_critical=60 -# Battery % limits +# Battery threshold in % battery_careful=80 battery_warning=90 battery_critical=95 @@ -117,7 +138,7 @@ battery_critical=95 #core 1_alias=CPU Core 1 [processlist] -# Limit values for CPU/MEM per process in % +# Define CPU/MEM (per process) thresholds in % # Default values if not defined: 50/70/90 cpu_careful=50 cpu_warning=70 @@ -176,7 +197,7 @@ mem_critical=90 [influxdb] # Configuration file for the --export-influxdb option -# https://influxdb.com/ +# https://influxdb.com/ host=localhost port=8086 user=root diff --git a/docs/glances-doc.rst b/docs/glances-doc.rst index 0cf277f4..c6dcedfd 100644 --- a/docs/glances-doc.rst +++ b/docs/glances-doc.rst @@ -159,6 +159,7 @@ Command-Line Options --disable-ip disable IP module --disable-diskio disable disk I/O module --disable-fs disable file system module + --disable-folder disable folders monitoring module --disable-sensors disable sensors module --disable-hddtemp disable hddtemp module --disable-raid disable RAID module @@ -247,7 +248,7 @@ The following commands (key pressed) are supported while in Glances: ``E`` Erase current process filter ``f`` - Show/hide file system stats + Show/hide file system and folder monitoring stats ``F`` Switch between file system used and free space ``g`` @@ -573,6 +574,39 @@ following section in the configuration file: [fs] allow=zfs,misc +Folders +------- + +The folders plugin allows user, through the configuration file, to monitor size +of a predefined folders' list. + +.. image:: images/folders.png + +Each item is defined by: + +* ``path``: absolute path to monitor. +* ``warning``: optional careful threshold (in MB). +* ``warning``: optional warning threshold (in MB) +* ``critical``: optional critical threshold (in MB) + +Up to 10 items can be defined. + +For example, if you want to monitor the /tmp folder, the following definition +should do the job (threshold careful if size is > 2.5 GB, warning if > 3 Gb and +critical if > 3.5 GB): + +:: + + [folders] + folder_1_path=/tmp + folder_1_careful=2500 + folder_1_warning=3000 + folder_1_critical=3500 + +In client/server mode, the list is defined on the server side. + +Note: do *NOT* define folder containing lot of files and subfolders. + Sensors ------- diff --git a/docs/images/folders.png b/docs/images/folders.png new file mode 100644 index 0000000000000000000000000000000000000000..178211545cd8ed1468375054fa86fbce971437ab GIT binary patch literal 13715 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 x^(ZFie@=Mn5&lJ}v_ +# +# 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 . + +"""Manage the folder list.""" + +import os + +from glances.compat import range, u +from glances.logger import logger + +# Use the built-in version of scandir/walk if possible, otherwise +# use the scandir module version + +scandir_tag = True +try: + # For Python 3.5 or higher + from os import scandir +except ImportError: + # For others... + try: + from scandir import scandir + except Exception, e: + scandir_tag = False + + +class FolderList(object): + + """This class describes the optional monitored folder list. + + The folder list is a list of 'important' folder to monitor. + + The list (Python list) is composed of items (Python dict). + An item is defined (dict keys): + * path: Path to the folder + * careful: optional careful threshold (in MB) + * warning: optional warning threshold (in MB) + * critical: optional critical threshold (in MB) + """ + + # Maximum number of items in the list + __folder_list_max_size = 10 + # The folder list + __folder_list = [] + + def __init__(self, config): + """Init the folder list from the configuration file, if it exists.""" + self.config = config + + if self.config is not None and self.config.has_section('folders'): + if scandir_tag: + # Process monitoring list + logger.debug("Folder list configuration detected") + self.__set_folder_list('folders') + else: + logger.error('Scandir not found. Please use Python 3.5+ or install the scandir lib') + else: + self.__folder_list = [] + + def __set_folder_list(self, section): + """Init the monitored folder list. + + The list is defined in the Glances configuration file. + """ + for l in range(1, self.__folder_list_max_size + 1): + value = {} + key = 'folder_' + str(l) + '_' + + # Path is mandatory + try: + value['path'] = self.config.get_value(section, key + 'path') + except Exception as e: + logger.error("Cannot read folder list: {0}".format(e)) + continue + if value['path'] is None: + continue + + # Optional conf keys + for i in ['careful', 'warning', 'critical']: + try: + value[i] = self.config.get_value(section, key + i) + except: + value[i] = None + logger.debug("No {0} threshold for folder {1}".format(i, value["path"])) + + # Add the item to the list + self.__folder_list.append(value) + + def __str__(self): + return str(self.__folder_list) + + def __repr__(self): + return self.__folder_list + + def __getitem__(self, item): + return self.__folder_list[item] + + def __len__(self): + return len(self.__folder_list) + + def __get__(self, item, key): + """Meta function to return key value of item. + + Return None if not defined or item > len(list) + """ + if item < len(self.__folder_list): + try: + return self.__folder_list[item][key] + except Exception: + return None + else: + return None + + def __folder_size(self, path): + """Return the size of the directory given by path + + path: """ + + ret = 0 + for f in scandir(path): + if f.is_dir() and (f.name != '.' or f.name != '..'): + ret += self.__folder_size(os.path.join(path, f.name)) + else: + try: + ret += f.stat().st_size + except OSError: + pass + + return ret + + def update(self): + """Update the command result attributed.""" + # Only continue if monitor list is not empty + if len(self.__folder_list) == 0: + return self.__folder_list + + # Iter upon the folder list + for i in range(len(self.get())): + # Update folder size + try: + self.__folder_list[i]['size'] = self.__folder_size(self.path(i)) + except Exception as e: + self.__folder_list[i]['size'] = None + logger.debug('Can get folder size ({0}). Error: {1}'.format(self.path(i), e)) + + return self.__folder_list + + def get(self): + """Return the monitored list (list of dict).""" + return self.__folder_list + + def set(self, newlist): + """Set the monitored list (list of dict).""" + self.__folder_list = newlist + + def getAll(self): + # Deprecated: use get() + return self.get() + + def setAll(self, newlist): + # Deprecated: use set() + self.set(newlist) + + def path(self, item): + """Return the path of the item number (item).""" + return self.__get__(item, "path") + + def careful(self, item): + """Return the careful threshold of the item number (item).""" + return self.__get__(item, "careful") + + def warning(self, item): + """Return the warning threshold of the item number (item).""" + return self.__get__(item, "warning") + + def critical(self, item): + """Return the critical threshold of the item number (item).""" + return self.__get__(item, "critical") diff --git a/glances/main.py b/glances/main.py index 9477dc6f..0221f038 100644 --- a/glances/main.py +++ b/glances/main.py @@ -118,6 +118,8 @@ Start the client browser (browser mode):\n\ dest='disable_diskio', help='disable disk I/O module') parser.add_argument('--disable-fs', action='store_true', default=False, dest='disable_fs', help='disable filesystem module') + parser.add_argument('--disable-folder', action='store_true', default=False, + dest='disable_folder', help='disable folder module') parser.add_argument('--disable-sensors', action='store_true', default=False, dest='disable_sensors', help='disable sensors module') parser.add_argument('--disable-hddtemp', action='store_true', default=False, diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index e878cb59..3d340d8a 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -334,8 +334,9 @@ class _GlancesCurses(object): # 'F' > Switch between FS available and free space self.args.fs_free_space = not self.args.fs_free_space elif self.pressedkey == ord('f'): - # 'f' > Show/hide fs stats + # 'f' > Show/hide fs / folder stats self.args.disable_fs = not self.args.disable_fs + self.args.disable_folder = not self.args.disable_folder elif self.pressedkey == ord('g'): # 'g' > History self.history_tag = not self.history_tag @@ -496,6 +497,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_folder = stats.get_plugin('folder').get_stats_display( + args=self.args, max_width=plugin_max_width) stats_raid = stats.get_plugin('raid').get_stats_display( args=self.args) stats_sensors = stats.get_plugin( @@ -659,8 +662,11 @@ 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_raid and + if not (self.args.disable_network and + self.args.disable_diskio and + self.args.disable_fs and + self.args.disable_folder 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) @@ -669,6 +675,8 @@ class _GlancesCurses(object): self.new_line() self.display_plugin(stats_fs) self.new_line() + self.display_plugin(stats_folder) + self.new_line() self.display_plugin(stats_raid) self.new_line() self.display_plugin(stats_sensors) diff --git a/glances/plugins/glances_folder.py b/glances/plugins/glances_folder.py new file mode 100644 index 00000000..f95a5fe3 --- /dev/null +++ b/glances/plugins/glances_folder.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Glances. +# +# Copyright (C) 2015 Nicolargo +# +# 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 . + +"""Folder plugin.""" + +from glances.compat import u +from glances.folder_list import FolderList as glancesFolderList +from glances.plugins.glances_plugin import GlancesPlugin +from glances.logger import logger + + +class Plugin(GlancesPlugin): + + """Glances folder plugin.""" + + def __init__(self, args=None): + """Init the plugin.""" + super(Plugin, self).__init__(args=args) + + # We want to display the stat in the curse interface + self.display_curse = True + + # Init stats + self.glances_folders = None + self.stats = [] + + def load_limits(self, config): + """Load the foldered list from the config file, if it exists.""" + self.glances_folders = glancesFolderList(config) + + def update(self): + """Update the foldered list.""" + if self.input_method == 'local': + # Folder list only available in a full Glances environment + # Check if the glances_folder instance is init + if self.glances_folders is None: + return self.stats + + # Update the foldered list (result of command) + self.glances_folders.update() + + # Put it on the stats var + self.stats = self.glances_folders.get() + else: + pass + + return self.stats + + def get_alert(self, stat): + """Manage limits of the folder list""" + + if stat['size'] is None: + return 'DEFAULT' + else: + ret = 'OK' + + if stat['critical'] is not None and stat['size'] > int(stat['critical']) * 1000000: + ret = 'CRITICAL' + elif stat['warning'] is not None and stat['size'] > int(stat['warning']) * 1000000: + ret = 'WARNING' + elif stat['careful'] is not None and stat['size'] > int(stat['careful']) * 1000000: + ret = 'CAREFUL' + + return ret + + 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_folder: + return ret + + # Build the string message + # Header + msg = '{0}'.format('FOLDERS') + ret.append(self.curse_add_line(msg, "TITLE")) + + # Data + for i in self.stats: + ret.append(self.curse_new_line()) + if len(i['path']) > 15: + # Cut path if it is too long + path = '_' + i['path'][-15 + 1:] + else: + path = i['path'] + msg = '{0:<16} '.format(path) + ret.append(self.curse_add_line(msg)) + try: + msg = '{0:>6}'.format(self.auto_unit(i['size'])) + except: + msg = '{0:>6}'.format('?') + ret.append(self.curse_add_line(msg, self.get_alert(i))) + + return ret diff --git a/glances/plugins/glances_fs.py b/glances/plugins/glances_fs.py index 97b683cb..fbd53778 100644 --- a/glances/plugins/glances_fs.py +++ b/glances/plugins/glances_fs.py @@ -221,7 +221,7 @@ class Plugin(GlancesPlugin): msg = '{0:>7}'.format('Total') ret.append(self.curse_add_line(msg)) - # Disk list (sorted by name) + # Filesystem list (sorted by name) for i in sorted(self.stats, key=operator.itemgetter(self.get_key())): # New line ret.append(self.curse_new_line()) diff --git a/glances/plugins/glances_sensors.py b/glances/plugins/glances_sensors.py index 78e0249f..c3b980e6 100644 --- a/glances/plugins/glances_sensors.py +++ b/glances/plugins/glances_sensors.py @@ -202,11 +202,14 @@ class Plugin(GlancesPlugin): else: value = i['value'] unit = i['unit'] - msg = '{0:>7.0f}{1}'.format(value, unit) - ret.append(self.curse_add_line( - msg, self.get_views(item=i[self.get_key()], - key='value', - option='decoration'))) + try: + msg = '{0:>7.0f}{1}'.format(value, unit) + ret.append(self.curse_add_line( + msg, self.get_views(item=i[self.get_key()], + key='value', + option='decoration'))) + except ValueError: + pass return ret diff --git a/man/glances.1 b/man/glances.1 index ccc9002b..610dd7f0 100644 --- a/man/glances.1 +++ b/man/glances.1 @@ -16,6 +16,8 @@ terminal or web interface. This tool is written in Python and uses the psutil library to fetch the statistical values from key elements, like CPU, load average, memory, network, disks, file systems, processes and so on. +.PP +The full documentation is available on the ReadTheDocs Web site (http://glances.readthedocs.org/en/latest/) .SH COMMAND-LINE OPTIONS The command-line options are the following: .TP @@ -61,6 +63,9 @@ disable disk I/O module .B \-\-disable-fs disable file system module .TP +.B \-\-disable-folder +disable folders monitoring module +.TP .B \-\-disable-sensors disable sensors module .TP diff --git a/setup.py b/setup.py index 650eeb45..f32e75c9 100755 --- a/setup.py +++ b/setup.py @@ -55,7 +55,8 @@ setup( 'DOCKER': ['docker-py'], 'EXPORT': ['influxdb>=1.0.0', 'potsdb' 'statsd', 'pika'], 'ACTION': ['pystache'], - 'CPUINFO': ['py-cpuinfo'] + 'CPUINFO': ['py-cpuinfo'], + 'FOLDERS': ['scandir'] }, packages=['glances'], include_package_data=True,