From 471b04fe85fb4f8fa41f21449fe7d503399aba2f Mon Sep 17 00:00:00 2001 From: Nicolas Hennion Date: Sun, 23 Feb 2014 17:06:03 +0100 Subject: First commit for the Alert module --- glances/README.txt | 36 ++++---- glances/core/glances_globals.py | 6 ++ glances/core/glances_logs.py | 181 +++++++++++++++++++++++++++++++++++++ glances/core/glances_standalone.py | 2 +- glances/plugins/glances_alert.py | 71 +++++++++++++++ glances/plugins/glances_cpu.py | 8 +- glances/plugins/glances_diskio.py | 2 + glances/plugins/glances_plugin.py | 39 ++++---- 8 files changed, 310 insertions(+), 35 deletions(-) create mode 100644 glances/core/glances_logs.py create mode 100644 glances/plugins/glances_alert.py diff --git a/glances/README.txt b/glances/README.txt index a4fac1a0..2cbb4004 100644 --- a/glances/README.txt +++ b/glances/README.txt @@ -5,24 +5,28 @@ If you are lookink for user manual, please follow this link: https://github.com/ === -__init__.py Global module init -__main__.py Entry point for module +__init__.py Global module init +__main__.py Entry point for module core/ - glances_main.py Main script to rule them up... - glances_globals.py Share variables uppon modules - glances_config.py Manage configuration file - glances_timer.py Manage timer - glances_stats.py Inteface to grab stats - glances_client.py Glances client - glances_server.py Glances_server + glances_config.py Manage configuration file + glances_globals.py Share variables uppon modules + glances_limits.py Manage limits + glances_logs.py Manage logs + glances_main.py Main script to rule them up... + glances_stats.py Inteface to grab stats + glances_client.py Glances client + glances_server.py Glances server + glances_standalone.py Glances standalone (with curse interface) + glances_stats.py The stats manager + glances_timer.py Manage timer plugins/ - glances_plugins.py "Father class" for others plugins - glances_cpu.py Manage CPU stats - glances_load.py Manage LOAD stats - glances_mem.py Manage MEM (both RAM and SWAP) stats + glances_plugins.py "Father class" for others plugins + glances_cpu.py Manage CPU stats + glances_load.py Manage LOAD stats + glances_mem.py Manage MEM (both RAM and SWAP) stats ... outputs/ - glances_curse.py The Curse (console) interface - glances_csv.py The CSV interface - glances_html.py The HTML interface + glances_curse.py The Curse interface + glances_csv.py The CSV interface + glances_html.py The HTML interface ... diff --git a/glances/core/glances_globals.py b/glances/core/glances_globals.py index 13165c77..3eaf978f 100644 --- a/glances/core/glances_globals.py +++ b/glances/core/glances_globals.py @@ -30,6 +30,9 @@ import os import gettext import locale +# Import Glances libs +from ..core.glances_logs import glancesLogs + # Import PsUtil try: from psutil import __version__ as __psutil_version__ @@ -74,3 +77,6 @@ elif os.path.exists(sys_i18n_path): else: locale_dir = None gettext.install(gettext_domain, locale_dir) + +# Logs instance share between all scripts +glances_logs = glancesLogs() diff --git a/glances/core/glances_logs.py b/glances/core/glances_logs.py new file mode 100644 index 00000000..60a8cec3 --- /dev/null +++ b/glances/core/glances_logs.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Glances - An eye on your system +# +# Copyright (C) 2014 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 . + +# Import system libs +import time +from datetime import datetime + +class glancesLogs: + """ + Manage logs inside the Glances software + Logs is a list of list (stored in the self.logs_list var) + + item_state = "OK|CAREFUL|WARNING|CRITICAL" + item_type = "CPU*|LOAD|MEM|MON" + item_value = value + Item is defined by: + ["begin", "end", "WARNING|CRITICAL", "CPU|LOAD|MEM", + MAX, AVG, MIN, SUM, COUNT, + [top3 process list], + "Processes description"] + """ + + def __init__(self): + """ + Init the logs class + """ + + # Maximum size of the logs list + self.logs_max = 10 + + # Init the logs list + self.logs_list = [] + + # Automaticaly define the sort to apply on the processes list + self.sort_process_by = 'none' + + + def get(self): + """ + Return the logs list (RAW) + """ + return self.logs_list + + + def len(self): + """ + Return the number of item in the log list + """ + return self.logs_list.__len__() + + + def __itemexist__(self, item_type): + """ + An item exist in the list if: + * end is < 0 + * item_type is matching + """ + for i in range(self.len()): + if self.logs_list[i][1] < 0 and self.logs_list[i][3] == item_type: + return i + return -1 + + + def add(self, item_state, item_type, item_value, proc_list=[], proc_desc=""): + """ + If item is a 'new one': + Add the new item at the beginning of the logs list + Else: + Update the existing item + """ + # Add Top process sort depending on alert type + self.sort_process_by = 'none' + if (item_type.startswith("MEM")): + # Sort TOP process by memory_percent + self.sort_process_by = 'memory_percent' + elif (item_type.startswith("CPU IO")): + # Sort TOP process by io_counters (only for Linux OS) + self.sort_process_by = 'io_counters' + elif (item_type.startswith("MON")): + # Do no sort process for monitored prcesses list + self.sort_process_by = 'none' + else: + # Default TOP process sort is cpu_percent + self.sort_process_by = 'cpu_percent' + + # Sort processes + if (self.sort_process_by != 'none'): + topprocess = sorted(proc_list, key=lambda process: process[self.sort_process_by], + reverse=True) + else: + topprocess = proc_list + + # Add or update the log + item_index = self.__itemexist__(item_type) + if (item_index < 0): + # Item did not exist, add if WARNING or CRITICAL + if ((item_state == "WARNING") or (item_state == "CRITICAL")): + # Time is stored in Epoch format + # Epoch -> DMYHMS = datetime.fromtimestamp(epoch) + item = [] + # START DATE + item.append(time.mktime(datetime.now().timetuple())) + # END DATE + item.append(-1) + item.append(item_state) # STATE: WARNING|CRITICAL + item.append(item_type) # TYPE: CPU, LOAD, MEM... + item.append(item_value) # MAX + item.append(item_value) # AVG + item.append(item_value) # MIN + item.append(item_value) # SUM + item.append(1) # COUNT + item.append(topprocess[0:3]) # TOP 3 PROCESS LIST + item.append(proc_desc) # MONITORED PROCESSES DESC + self.logs_list.insert(0, item) + if self.len() > self.logs_max: + self.logs_list.pop() + else: + # Item exist, update + if ((item_state == "OK") or (item_state == "CAREFUL")): + # Close the item + self.logs_list[item_index][1] = time.mktime( + datetime.now().timetuple()) + # TOP PROCESS LIST + self.logs_list[item_index][9] = [] + else: + # Update the item + # State + if (item_state == "CRITICAL"): + self.logs_list[item_index][2] = item_state + # Value + if (item_value > self.logs_list[item_index][4]): + # MAX + self.logs_list[item_index][4] = item_value + elif (item_value < self.logs_list[item_index][6]): + # MIN + self.logs_list[item_index][6] = item_value + # AVG + self.logs_list[item_index][7] += item_value + self.logs_list[item_index][8] += 1 + self.logs_list[item_index][5] = (self.logs_list[item_index][7] / + self.logs_list[item_index][8]) + # TOP PROCESS LIST + self.logs_list[item_index][9] = topprocess[0:3] + # MONITORED PROCESSES DESC + self.logs_list[item_index][10] = proc_desc + + return self.len() + + + def clean(self, critical=False): + """ + Clean the log list by deleting finished item + By default, only delete WARNING message + If critical = True, also delete CRITICAL message + """ + # Create a new clean list + clean_logs_list = [] + while self.len() > 0: + item = self.logs_list.pop() + if item[1] < 0 or (not critical and item[2] == "CRITICAL"): + clean_logs_list.insert(0, item) + # The list is now the clean one + self.logs_list = clean_logs_list + return self.len() diff --git a/glances/core/glances_standalone.py b/glances/core/glances_standalone.py index 40fab08f..f1a5af06 100644 --- a/glances/core/glances_standalone.py +++ b/glances/core/glances_standalone.py @@ -50,7 +50,7 @@ class GlancesStandalone(): self.stats = GlancesStats(config) # Initial update - # !!! The first time Glances display wrong CPU information + # !!! The first time Glances display wrong CPU/MEM information self.stats.update() self.refresh_time = args.time diff --git a/glances/plugins/glances_alert.py b/glances/plugins/glances_alert.py new file mode 100644 index 00000000..e6c37986 --- /dev/null +++ b/glances/plugins/glances_alert.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Glances - An eye on your system +# +# Copyright (C) 2014 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 . + +# Import Glances lib +from glances_plugin import GlancesPlugin +from glances.core.glances_globals import glances_logs + + +class Plugin(GlancesPlugin): + """ + Glances's alert Plugin + + Only for display + """ + + def __init__(self): + GlancesPlugin.__init__(self) + + # We want to display the stat in the curse interface + self.display_curse = True + # Set the message position + # It is NOT the curse position but the Glances column/line + # Enter -1 to right align + self.column_curse = 1 + # Enter -1 to diplay bottom + self.line_curse = 4 + + + def update(self): + """ + Nothing to do here + Just return the global glances_log + """ + + self.stats = glances_logs.get() + + + def msg_curse(self, args=None): + """ + Return the dict to display in the curse interface + """ + # Init the return message + ret = [] + + # Build the string message + # Header + if (self.stats == []): + msg = "{0:8}".format(_("No alert detected")) + ret.append(self.curse_add_line(msg, "TITLE")) + else: + msg = "{0:8}".format(_("ALERT")) + ret.append(self.curse_add_line(msg, "TITLE")) + + return ret diff --git a/glances/plugins/glances_cpu.py b/glances/plugins/glances_cpu.py index 3966728b..72eedaff 100644 --- a/glances/plugins/glances_cpu.py +++ b/glances/plugins/glances_cpu.py @@ -25,6 +25,7 @@ from psutil import cpu_times, cpu_times_percent # from ..plugins.glances_plugin import GlancesPlugin from glances_plugin import GlancesPlugin + class Plugin(GlancesPlugin): """ Glances' Cpu Plugin @@ -51,7 +52,10 @@ class Plugin(GlancesPlugin): """ # Grab CPU using the PSUtil cpu_times_percent method (PSUtil 0.7 or higher) - cputimespercent = cpu_times_percent(interval=0, percpu=False) + try: + cputimespercent = cpu_times_percent(interval=0, percpu=False) + except: + return self.update_deprecated() self.stats = {} for cpu in ['user', 'system', 'idle', 'nice', @@ -64,8 +68,8 @@ class Plugin(GlancesPlugin): def update_deprecated(self): """ - !!! Not used anymore... Update CPU stats + Only used if cpu_times_percent failed """ # Grab CPU using the PSUtil cpu_times method diff --git a/glances/plugins/glances_diskio.py b/glances/plugins/glances_diskio.py index 09e85291..a8f4344a 100644 --- a/glances/plugins/glances_diskio.py +++ b/glances/plugins/glances_diskio.py @@ -18,8 +18,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . +# Import system lib from psutil import disk_io_counters +# Import Glances lib from glances.core.glances_globals import is_Mac from glances_plugin import GlancesPlugin, getTimeSinceLastUpdate diff --git a/glances/plugins/glances_plugin.py b/glances/plugins/glances_plugin.py index e15ccb9f..7cefd83c 100644 --- a/glances/plugins/glances_plugin.py +++ b/glances/plugins/glances_plugin.py @@ -18,13 +18,16 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . +# Import system libs import json from time import time +# Import Glances lib +from glances.core.glances_globals import glances_logs + # Global list to manage the elapsed time last_update_times = {} - def getTimeSinceLastUpdate(IOType): global last_update_times # assert(IOType in ['net', 'disk', 'process_disk']) @@ -112,27 +115,31 @@ class GlancesPlugin(object): except ZeroDivisionError: return 'DEFAULT' - # If log is enable than add _LOG to the return string - if (log): - log_str = "_LOG" - else: - log_str = "" - - # if (self.plugin_name == "processlist"): - # print "*"*300 - # print self.limits - # sys.exit(0) - # Manage limits + ret = 'OK' if (value > self.get_limit_critical(header=header)): - return 'CRITICAL'+log_str + ret = 'CRITICAL' elif (value > self.get_limit_warning(header=header)): - return 'WARNING'+log_str + ret = 'WARNING' elif (value > self.get_limit_careful(header=header)): - return 'CAREFUL'+log_str + ret = 'CAREFUL' + + # Manage log (if needed) + log_str = "" + if (log): + # Add _LOG to the return string + # So stats will be highlited with a specific color + log_str = "_LOG" + # Get the stat_name = plugin_name (+ header) + if (header == ""): + stat_name = self.plugin_name + else: + stat_name = self.plugin_name + '_' + header + # !!! TODO: Manage the process list (last param => []) + glances_logs.add(ret, stat_name.upper(), value, []) # Default is ok - return 'OK'+log_str + return ret + log_str def get_alert_log(self, current=0, min=0, max=100, header=""): -- cgit v1.2.3