1
|
|
|
# |
2
|
|
|
# This file is part of Glances. |
3
|
|
|
# |
4
|
|
|
# SPDX-FileCopyrightText: 2024 Nicolas Hennion <[email protected]> |
5
|
|
|
# |
6
|
|
|
# SPDX-License-Identifier: LGPL-3.0-only |
7
|
|
|
# |
8
|
|
|
|
9
|
|
|
""" |
10
|
|
|
I am your father... |
11
|
|
|
|
12
|
|
|
...for all Glances Application Monitoring Processes (AMP). |
13
|
|
|
|
14
|
|
|
AMP (Application Monitoring Process) |
15
|
|
|
A Glances AMP is a Python script called (every *refresh* seconds) if: |
16
|
|
|
- the AMP is *enabled* in the Glances configuration file |
17
|
|
|
- a process is running (match the *regex* define in the configuration file) |
18
|
|
|
The script should define a Amp (GlancesAmp) class with, at least, an update method. |
19
|
|
|
The update method should call the set_result method to set the AMP return string. |
20
|
|
|
The return string is a string with one or more line (\n between lines). |
21
|
|
|
If the *one_line* var is true then the AMP will be displayed in one line. |
22
|
|
|
""" |
23
|
|
|
|
24
|
|
|
from glances.globals import u |
25
|
|
|
from glances.logger import logger |
26
|
|
|
from glances.timer import Timer |
27
|
|
|
|
28
|
|
|
|
29
|
|
|
class GlancesAmp: |
30
|
|
|
"""Main class for Glances AMP.""" |
31
|
|
|
|
32
|
|
|
NAME = '?' |
33
|
|
|
VERSION = '?' |
34
|
|
|
DESCRIPTION = '?' |
35
|
|
|
AUTHOR = '?' |
36
|
|
|
EMAIL = '?' |
37
|
|
|
|
38
|
|
|
def __init__(self, name=None, args=None): |
39
|
|
|
"""Init AMP class.""" |
40
|
|
|
logger.debug(f"AMP - Init {self.NAME} version {self.VERSION}") |
41
|
|
|
|
42
|
|
|
# AMP name (= module name without glances_) |
43
|
|
|
if name is None: |
44
|
|
|
self.amp_name = self.__class__.__module__ |
45
|
|
|
else: |
46
|
|
|
self.amp_name = name |
47
|
|
|
|
48
|
|
|
# Init the args |
49
|
|
|
self.args = args |
50
|
|
|
|
51
|
|
|
# Init the configs |
52
|
|
|
self.configs = {} |
53
|
|
|
|
54
|
|
|
# A timer is needed to only update every refresh seconds |
55
|
|
|
# Init to 0 in order to update the AMP on startup |
56
|
|
|
self.timer = Timer(0) |
57
|
|
|
|
58
|
|
|
def load_config(self, config): |
59
|
|
|
"""Load AMP parameters from the configuration file.""" |
60
|
|
|
|
61
|
|
|
# Read AMP configuration. |
62
|
|
|
# For ex, the AMP foo should have the following section: |
63
|
|
|
# |
64
|
|
|
# [foo] |
65
|
|
|
# enable=true |
66
|
|
|
# regex=\/usr\/bin\/nginx |
67
|
|
|
# refresh=60 |
68
|
|
|
# |
69
|
|
|
# and optionally: |
70
|
|
|
# |
71
|
|
|
# one_line=false |
72
|
|
|
# option1=opt1 |
73
|
|
|
|
74
|
|
|
amp_section = 'amp_' + self.amp_name |
75
|
|
|
if hasattr(config, 'has_section') and config.has_section(amp_section): |
76
|
|
|
logger.debug(f"AMP - {self.NAME}: Load configuration") |
77
|
|
|
for param, _ in config.items(amp_section): |
78
|
|
|
try: |
79
|
|
|
self.configs[param] = config.get_float_value(amp_section, param) |
80
|
|
|
except ValueError: |
81
|
|
|
self.configs[param] = config.get_value(amp_section, param).split(',') |
82
|
|
|
if len(self.configs[param]) == 1: |
83
|
|
|
self.configs[param] = self.configs[param][0] |
84
|
|
|
logger.debug(f"AMP - {self.NAME}: Load parameter: {param} = {self.configs[param]}") |
85
|
|
|
else: |
86
|
|
|
logger.debug(f"AMP - {self.NAME}: Can not find section {self.amp_name} in the configuration file") |
87
|
|
|
return False |
88
|
|
|
|
89
|
|
|
if self.enable(): |
90
|
|
|
# Refresh option is mandatory |
91
|
|
|
for k in ['refresh']: |
92
|
|
|
if k not in self.configs: |
93
|
|
|
logger.warning( |
94
|
|
|
f"AMP - {self.NAME}: Can not find configuration key {k} in section {self.amp_name} " |
95
|
|
|
f"(the AMP will be disabled)" |
96
|
|
|
) |
97
|
|
|
self.configs['enable'] = 'false' |
98
|
|
|
else: |
99
|
|
|
logger.debug(f"AMP - {self.NAME} is disabled") |
100
|
|
|
|
101
|
|
|
# Init the count to 0 |
102
|
|
|
self.configs['count'] = 0 |
103
|
|
|
|
104
|
|
|
return self.enable() |
105
|
|
|
|
106
|
|
|
def get(self, key): |
107
|
|
|
"""Generic method to get the item in the AMP configuration""" |
108
|
|
|
if key in self.configs: |
109
|
|
|
return self.configs[key] |
110
|
|
|
return None |
111
|
|
|
|
112
|
|
|
def enable(self): |
113
|
|
|
"""Return True|False if the AMP is enabled in the configuration file (enable=true|false).""" |
114
|
|
|
ret = self.get('enable') |
115
|
|
|
if ret is None: |
116
|
|
|
return False |
117
|
|
|
return ret.lower().startswith('true') |
118
|
|
|
|
119
|
|
|
def regex(self): |
120
|
|
|
"""Return regular expression used to identified the current application.""" |
121
|
|
|
return self.get('regex') |
122
|
|
|
|
123
|
|
|
def refresh(self): |
124
|
|
|
"""Return refresh time in seconds for the current application monitoring process.""" |
125
|
|
|
return self.get('refresh') |
126
|
|
|
|
127
|
|
|
def one_line(self): |
128
|
|
|
"""Return True|False if the AMP should be displayed in one line (one_line=true|false).""" |
129
|
|
|
ret = self.get('one_line') |
130
|
|
|
if ret is None: |
131
|
|
|
return False |
132
|
|
|
return ret.lower().startswith('true') |
133
|
|
|
|
134
|
|
|
def time_until_refresh(self): |
135
|
|
|
"""Return time in seconds until refresh.""" |
136
|
|
|
return self.timer.get() |
137
|
|
|
|
138
|
|
|
def should_update(self): |
139
|
|
|
"""Return True is the AMP should be updated |
140
|
|
|
|
141
|
|
|
Conditions for update: |
142
|
|
|
- AMP is enable |
143
|
|
|
- only update every 'refresh' seconds |
144
|
|
|
""" |
145
|
|
|
if self.timer.finished(): |
146
|
|
|
self.timer.set(self.refresh()) |
147
|
|
|
self.timer.reset() |
148
|
|
|
return self.enable() |
149
|
|
|
return False |
150
|
|
|
|
151
|
|
|
def set_count(self, count): |
152
|
|
|
"""Set the number of processes matching the regex""" |
153
|
|
|
self.configs['count'] = count |
154
|
|
|
|
155
|
|
|
def count(self): |
156
|
|
|
"""Get the number of processes matching the regex""" |
157
|
|
|
return self.get('count') |
158
|
|
|
|
159
|
|
|
def count_min(self): |
160
|
|
|
"""Get the minimum number of processes""" |
161
|
|
|
return self.get('countmin') |
162
|
|
|
|
163
|
|
|
def count_max(self): |
164
|
|
|
"""Get the maximum number of processes""" |
165
|
|
|
return self.get('countmax') |
166
|
|
|
|
167
|
|
|
def set_result(self, result, separator=''): |
168
|
|
|
"""Store the result (string) into the result key of the AMP |
169
|
|
|
|
170
|
|
|
If one_line is true then it replaces `\n` by the separator |
171
|
|
|
""" |
172
|
|
|
if self.one_line(): |
173
|
|
|
self.configs['result'] = u(result).replace('\n', separator) |
174
|
|
|
else: |
175
|
|
|
self.configs['result'] = u(result) |
176
|
|
|
|
177
|
|
|
def result(self): |
178
|
|
|
"""Return the result of the AMP (as a string)""" |
179
|
|
|
ret = self.get('result') |
180
|
|
|
if ret is not None: |
181
|
|
|
ret = u(ret) |
182
|
|
|
return ret |
183
|
|
|
|
184
|
|
|
def update_wrapper(self, process_list): |
185
|
|
|
"""Wrapper for the children update""" |
186
|
|
|
# Set the number of running process |
187
|
|
|
self.set_count(len(process_list)) |
188
|
|
|
# Call the children update method |
189
|
|
|
if self.should_update(): |
190
|
|
|
return self.update(process_list) |
191
|
|
|
return self.result() |
192
|
|
|
|