1 | # -*- coding: utf-8 -*- |
||
2 | # |
||
3 | # This file is part of Glances. |
||
4 | # |
||
5 | # Copyright (C) 2019 Nicolargo <[email protected]> |
||
6 | # |
||
7 | # Glances is free software; you can redistribute it and/or modify |
||
8 | # it under the terms of the GNU Lesser General Public License as published by |
||
9 | # the Free Software Foundation, either version 3 of the License, or |
||
10 | # (at your option) any later version. |
||
11 | # |
||
12 | # Glances is distributed in the hope that it will be useful, |
||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | # GNU Lesser General Public License for more details. |
||
16 | # |
||
17 | # You should have received a copy of the GNU Lesser General Public License |
||
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
19 | |||
20 | """Alert plugin.""" |
||
21 | |||
22 | from datetime import datetime |
||
23 | |||
24 | from glances.logger import logger |
||
25 | from glances.events import glances_events |
||
26 | from glances.thresholds import glances_thresholds |
||
27 | # from glances.logger import logger |
||
28 | from glances.plugins.glances_plugin import GlancesPlugin |
||
29 | |||
30 | # Static decision tree for the global alert message |
||
31 | # - msg: Message to be displayed (result of the decision tree) |
||
32 | # - threasholds: a list of stats to take into account |
||
33 | # - thresholds_min: minimal value of the threasholds sum |
||
34 | # - 0: OK |
||
35 | # - 1: CAREFUL |
||
36 | # - 2: WARNING |
||
37 | # - 3: CRITICAL |
||
38 | tree = [{'msg': 'No warning or critical alert detected', |
||
39 | 'thresholds': [], |
||
40 | 'thresholds_min': 0}, |
||
41 | {'msg': 'High CPU user mode', |
||
42 | 'thresholds': ['cpu_user'], |
||
43 | 'thresholds_min': 2}, |
||
44 | {'msg': 'High CPU kernel usage', |
||
45 | 'thresholds': ['cpu_system'], |
||
46 | 'thresholds_min': 2}, |
||
47 | {'msg': 'High CPU I/O waiting', |
||
48 | 'thresholds': ['cpu_iowait'], |
||
49 | 'thresholds_min': 2}, |
||
50 | {'msg': 'Large CPU stolen time. System running the hypervisor is too busy.', |
||
51 | 'thresholds': ['cpu_steal'], |
||
52 | 'thresholds_min': 2}, |
||
53 | {'msg': 'High CPU niced value', |
||
54 | 'thresholds': ['cpu_niced'], |
||
55 | 'thresholds_min': 2}, |
||
56 | {'msg': 'System overloaded in the last 5 minutes', |
||
57 | 'thresholds': ['load'], |
||
58 | 'thresholds_min': 2}, |
||
59 | {'msg': 'High swap (paging) usage', |
||
60 | 'thresholds': ['memswap'], |
||
61 | 'thresholds_min': 2}, |
||
62 | {'msg': 'High memory consumption', |
||
63 | 'thresholds': ['mem'], |
||
64 | 'thresholds_min': 2}, |
||
65 | ] |
||
66 | |||
67 | |||
68 | def global_message(): |
||
69 | """Parse the decision tree and return the message. |
||
70 | |||
71 | Note: message corresponding to the current threasholds values |
||
72 | """ |
||
73 | # Compute the weight for each item in the tree |
||
74 | current_thresholds = glances_thresholds.get() |
||
75 | for i in tree: |
||
76 | i['weight'] = sum([current_thresholds[t].value() for t in i['thresholds'] if t in current_thresholds]) |
||
77 | themax = max(tree, key=lambda d: d['weight']) |
||
78 | if themax['weight'] >= themax['thresholds_min']: |
||
0 ignored issues
–
show
unused-code
introduced
by
Loading history...
|
|||
79 | # Check if the weight is > to the minimal threashold value |
||
80 | return themax['msg'] |
||
81 | else: |
||
82 | return tree[0]['msg'] |
||
83 | |||
84 | |||
85 | class Plugin(GlancesPlugin): |
||
86 | """Glances alert plugin. |
||
87 | |||
88 | Only for display. |
||
89 | """ |
||
90 | |||
91 | def __init__(self, args=None, config=None): |
||
92 | """Init the plugin.""" |
||
93 | super(Plugin, self).__init__(args=args, |
||
94 | config=config, |
||
95 | stats_init_value=[]) |
||
96 | |||
97 | # We want to display the stat in the curse interface |
||
98 | self.display_curse = True |
||
99 | |||
100 | # Set the message position |
||
101 | self.align = 'bottom' |
||
102 | |||
103 | def update(self): |
||
104 | """Nothing to do here. Just return the global glances_log.""" |
||
105 | # Set the stats to the glances_events |
||
106 | self.stats = glances_events.get() |
||
107 | # Define the global message thanks to the current thresholds |
||
108 | # and the decision tree |
||
109 | # !!! Call directly in the msg_curse function |
||
110 | # global_message() |
||
111 | |||
112 | def msg_curse(self, args=None, max_width=None): |
||
113 | """Return the dict to display in the curse interface.""" |
||
114 | # Init the return message |
||
115 | ret = [] |
||
116 | |||
117 | # Only process if display plugin enable... |
||
118 | if not self.stats or self.is_disable(): |
||
119 | return ret |
||
120 | |||
121 | # Build the string message |
||
122 | # Header |
||
123 | ret.append(self.curse_add_line(global_message(), "TITLE")) |
||
124 | # Loop over alerts |
||
125 | for alert in self.stats: |
||
126 | # New line |
||
127 | ret.append(self.curse_new_line()) |
||
128 | # Start |
||
129 | msg = str(datetime.fromtimestamp(alert[0])) |
||
130 | ret.append(self.curse_add_line(msg)) |
||
131 | # Duration |
||
132 | if alert[1] > 0: |
||
133 | # If finished display duration |
||
134 | msg = ' ({})'.format(datetime.fromtimestamp(alert[1]) - |
||
135 | datetime.fromtimestamp(alert[0])) |
||
136 | else: |
||
137 | msg = ' (ongoing)' |
||
138 | ret.append(self.curse_add_line(msg)) |
||
139 | ret.append(self.curse_add_line(" - ")) |
||
140 | # Infos |
||
141 | if alert[1] > 0: |
||
142 | # If finished do not display status |
||
143 | msg = '{} on {}'.format(alert[2], alert[3]) |
||
144 | ret.append(self.curse_add_line(msg)) |
||
145 | else: |
||
146 | msg = str(alert[3]) |
||
147 | ret.append(self.curse_add_line(msg, decoration=alert[2])) |
||
148 | # Min / Mean / Max |
||
149 | if self.approx_equal(alert[6], alert[4], tolerance=0.1): |
||
150 | msg = ' ({:.1f})'.format(alert[5]) |
||
151 | else: |
||
152 | msg = ' (Min:{:.1f} Mean:{:.1f} Max:{:.1f})'.format( |
||
153 | alert[6], alert[5], alert[4]) |
||
154 | ret.append(self.curse_add_line(msg)) |
||
155 | # Top processes |
||
156 | top_process = ', '.join([p['name'] for p in alert[9]]) |
||
157 | if top_process != '': |
||
158 | msg = ': {}'.format(top_process) |
||
159 | ret.append(self.curse_add_line(msg)) |
||
160 | |||
161 | return ret |
||
162 | |||
163 | def approx_equal(self, a, b, tolerance=0.0): |
||
164 | """Compare a with b using the tolerance (if numerical).""" |
||
165 | if str(int(a)).isdigit() and str(int(b)).isdigit(): |
||
0 ignored issues
–
show
|
|||
166 | return abs(a - b) <= max(abs(a), abs(b)) * tolerance |
||
167 | else: |
||
168 | return a == b |
||
169 |