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