diff --git a/NEWS b/NEWS index 24298846..7ecc1530 100644 --- a/NEWS +++ b/NEWS @@ -2,12 +2,13 @@ Glances Version 2.x ============================================================================== -Version 2.X +Version 2.3 =========== + * Add actions on alerts (issue #132). It is now possible to run action (command line) by triggers. Action could containq {Mustache} {{tag}} (Mustache) with stat value. * Add InfluxDB export module (--export-influxdb) (issue #455) * Add Statsd export module (--export-statsd) (issue #465) - * Refactor export module (CSV export option is now --export-csv). It is now possible to export stats from the Glances client (issue #463) + * Refactor export module (CSV export option is now --export-csv). It is now possible to export stats from the Glances client mode (issue #463) * The Web inteface is now based on BootStarp / RWD grid (issue #417, #366 and #461) Thanks to Nicolas Hart @nclsHart * Add the RAID plugins (issue #447) diff --git a/conf/glances-test.conf b/conf/glances-test.conf index d4f9dbdc..4a804eea 100644 --- a/conf/glances-test.conf +++ b/conf/glances-test.conf @@ -3,7 +3,7 @@ user_careful=50 user_warning=70 user_critical=90 -user_critical_action=touch /tmp/glances.alert +user_critical_action=echo {{user}} {{value}} {{max}} > /tmp/cpu.alert iowait_careful=50 iowait_warning=70 iowait_critical=90 @@ -72,13 +72,14 @@ hide=sda2,sda5 # Default limits for free filesytem space in % # Default values if not defined: 50/70/90 careful=50 +careful_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert warning=70 critical=90 [sensors] # Sensors core limits # Default values if not defined: 60/70/80 -temperature_core_careful=60 +temperature_core_careful=50 temperature_core_warning=70 temperature_core_critical=80 # Temperatures in °C for hddtemp diff --git a/glances/core/glances_actions.py b/glances/core/glances_actions.py index 4c7011e0..6069e0e8 100644 --- a/glances/core/glances_actions.py +++ b/glances/core/glances_actions.py @@ -21,6 +21,7 @@ # Import system lib from subprocess import Popen +import pystache # Import Glances lib from glances.core.glances_logging import logger @@ -50,11 +51,12 @@ class GlancesActions(object): """Set the stat_name to criticity""" self.status[stat_name] = criticity - def run(self, stat_name, criticity, commands): + def run(self, stat_name, criticity, commands, mustache_dict=None): """Run the commands (in background) - stats_name: plugin_name (+ header) - criticity: criticity of the trigger - - commands: a list of command line + - commands: a list of command line with optional {{mustache}} + - mustache_dict: Plugin stats (can be use within {{mustache}}) Return True if the commands have been ran""" @@ -64,10 +66,13 @@ class GlancesActions(object): # Ran all actions in background for cmd in commands: - logger.info("Action triggered for {0} ({1}): {2}".format(stat_name, criticity, cmd)) - splitted_cmd = cmd.split() + # Replace {{arg}} by the dict one (Thk to {Mustache}) + cmd_full = pystache.render(cmd, mustache_dict) + # Execute the action + logger.info("Action triggered for {0} ({1}): {2}".format(stat_name, criticity, cmd_full)) + logger.debug("Stats value for the trigger: {0}".format(mustache_dict)) try: - Popen(splitted_cmd) + Popen(cmd_full, shell=True) except OSError as e: logger.error("Can't execute the action ({0})".format(e)) diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index 5cedb395..378dc64b 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -697,6 +697,7 @@ class _GlancesCurses(object): # Display x = display_x + x_max = x y = display_y for m in plugin_stats['msgdict']: # New line @@ -740,9 +741,11 @@ class _GlancesCurses(object): # good offset = len(m['msg']) x = x + offset + if x > x_max: + x_max = x # Compute the next Glances column/line position - self.next_column = max(self.next_column, x + self.space_between_column) + self.next_column = max(self.next_column, x_max + self.space_between_column) self.next_line = max(self.next_line, y + self.space_between_line) def erase(self): diff --git a/glances/plugins/glances_fs.py b/glances/plugins/glances_fs.py index 20dc85c1..621ea5bc 100644 --- a/glances/plugins/glances_fs.py +++ b/glances/plugins/glances_fs.py @@ -209,7 +209,9 @@ class Plugin(GlancesPlugin): msg = '{0:>7}'.format(self.auto_unit(i['free'])) else: msg = '{0:>7}'.format(self.auto_unit(i['used'])) - ret.append(self.curse_add_line(msg, self.get_alert(i['used'], max=i['size']))) + ret.append(self.curse_add_line(msg, self.get_alert(i['used'], + max=i['size'], + header=i['mnt_point']))) msg = '{0:>7}'.format(self.auto_unit(i['size'])) ret.append(self.curse_add_line(msg)) diff --git a/glances/plugins/glances_plugin.py b/glances/plugins/glances_plugin.py index ecc2d4ca..15ba53e1 100644 --- a/glances/plugins/glances_plugin.py +++ b/glances/plugins/glances_plugin.py @@ -333,12 +333,25 @@ class GlancesPlugin(object): # Manage action # Here is a command line for the current trigger ? - command = self.__get_limit_action(ret.lower(), header=header) - if command is not None: - # Acommand line is available for the current alert, run it - self.actions.run(stat_name, ret.lower(), command) - else: + try: + command = self.__get_limit_action(ret.lower(), header=header) + except KeyError: + # Reset the trigger self.actions.set(stat_name, ret.lower()) + else: + # A command line is available for the current alert, run it + # Build the {{mustache}} dictionnary + if type(self.stats) is list: + # If the stats are stored in a list of dict (fs plugin for exemple) + # Return the dict for the current header + try: + mustache_dict = (item for item in self.stats if item[self.get_key()] == header).next() + except StopIteration: + mustache_dict = {} + else: + # Use the stats dict + mustache_dict = self.stats + self.actions.run(stat_name, ret.lower(), command, mustache_dict=mustache_dict) # Default is ok return ret + log_str @@ -352,38 +365,37 @@ class GlancesPlugin(object): prefix = self.plugin_name + '_' if header != "": prefix += header + '_' - action = self.limits[prefix + criticity] - return action + + # Get the limit for stat + header + # Exemple: network_wlan0_rx_careful + try: + limit = self.limits[prefix + criticity] + except KeyError: + # Try fallback to plugin default limit + # Exemple: network_careful + limit = self.limits[self.plugin_name + '_' + criticity] + + # Return the limit + return limit def __get_limit_action(self, criticity, header=""): """Return the action for the alert""" prefix = self.plugin_name + '_' if header != "": prefix += header + '_' + + # Get the limit for stat + header + # Exemple: network_wlan0_rx_careful_action try: action = self.limits[prefix + criticity + '_action'] except KeyError: - action = None + # Try fallback to plugin default limit + # Exemple: network_careful_action + action = self.limits[self.plugin_name + '_' + criticity + '_action'] + + # Return the action list return action - # def __get_limit_critical(self, header=""): - # if header == "": - # return self.limits[self.plugin_name + '_' + 'critical'] - # else: - # return self.limits[self.plugin_name + '_' + header + '_' + 'critical'] - - # def __get_limit_warning(self, header=""): - # if header == "": - # return self.limits[self.plugin_name + '_' + 'warning'] - # else: - # return self.limits[self.plugin_name + '_' + header + '_' + 'warning'] - - # def __get_limit_careful(self, header=""): - # if header == "": - # return self.limits[self.plugin_name + '_' + 'careful'] - # else: - # return self.limits[self.plugin_name + '_' + header + '_' + 'careful'] - def get_conf_value(self, value, header="", plugin_name=None): """Return the configuration (header_)value for the current plugin (or the one given by the plugin_name var)""" if plugin_name is None: diff --git a/glances/plugins/glances_sensors.py b/glances/plugins/glances_sensors.py index 089933a9..00fc3552 100644 --- a/glances/plugins/glances_sensors.py +++ b/glances/plugins/glances_sensors.py @@ -64,6 +64,10 @@ class Plugin(GlancesPlugin): # Init the stats self.reset() + def get_key(self): + """Return the key of the list""" + return 'label' + def reset(self): """Reset/init the stats.""" self.stats = []