1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
# |
3
|
|
|
# This file is part of Glances. |
4
|
|
|
# |
5
|
|
|
# Copyright (C) 2016 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 |
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("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("{}: 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("{}: Load parameter: {} = {}".format(self.NAME, param, self.configs[param])) |
98
|
|
|
else: |
99
|
|
|
logger.debug("{}: 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("{}: Can not find configuration key {} in section {}".format(self.NAME, k, self.amp_name)) |
108
|
|
|
self.configs['enable'] = 'false' |
109
|
|
|
else: |
110
|
|
|
logger.debug("{} 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'] = str(result).replace('\n', separator) |
185
|
|
|
else: |
186
|
|
|
self.configs['result'] = str(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
|
|
|
|