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 | """The stats manager.""" |
||
21 | |||
22 | import collections |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
23 | import os |
||
0 ignored issues
–
show
|
|||
24 | import sys |
||
0 ignored issues
–
show
|
|||
25 | import threading |
||
0 ignored issues
–
show
|
|||
26 | import traceback |
||
0 ignored issues
–
show
|
|||
27 | |||
28 | from glances.logger import logger |
||
0 ignored issues
–
show
|
|||
29 | from glances.globals import exports_path, plugins_path, sys_path |
||
0 ignored issues
–
show
|
|||
30 | from glances.timer import Counter |
||
0 ignored issues
–
show
|
|||
31 | |||
32 | |||
33 | class GlancesStats(object): |
||
34 | |||
35 | """This class stores, updates and gives stats.""" |
||
36 | |||
37 | # Script header constant |
||
38 | header = "glances_" |
||
39 | |||
40 | def __init__(self, config=None, args=None): |
||
41 | # Set the config instance |
||
42 | self.config = config |
||
43 | |||
44 | # Set the argument instance |
||
45 | self.args = args |
||
46 | |||
47 | # Load plugins and exports modules |
||
48 | self.load_modules(self.args) |
||
49 | |||
50 | # Load the limits (for plugins) |
||
51 | # Not necessary anymore, configuration file is loaded on init |
||
52 | # self.load_limits(self.config) |
||
53 | |||
54 | def __getattr__(self, item): |
||
55 | """Overwrite the getattr method in case of attribute is not found. |
||
56 | |||
57 | The goal is to dynamically generate the following methods: |
||
58 | - getPlugname(): return Plugname stat in JSON format |
||
59 | - getViewsPlugname(): return views of the Plugname stat in JSON format |
||
60 | """ |
||
61 | # Check if the attribute starts with 'get' |
||
62 | if item.startswith('getViews'): |
||
63 | # Get the plugin name |
||
64 | plugname = item[len('getViews'):].lower() |
||
65 | # Get the plugin instance |
||
66 | plugin = self._plugins[plugname] |
||
67 | if hasattr(plugin, 'get_json_views'): |
||
68 | # The method get_views exist, return it |
||
69 | return getattr(plugin, 'get_json_views') |
||
70 | else: |
||
71 | # The method get_views is not found for the plugin |
||
72 | raise AttributeError(item) |
||
73 | elif item.startswith('get'): |
||
74 | # Get the plugin name |
||
75 | plugname = item[len('get'):].lower() |
||
76 | # Get the plugin instance |
||
77 | plugin = self._plugins[plugname] |
||
78 | if hasattr(plugin, 'get_stats'): |
||
79 | # The method get_stats exist, return it |
||
80 | return getattr(plugin, 'get_stats') |
||
81 | else: |
||
82 | # The method get_stats is not found for the plugin |
||
83 | raise AttributeError(item) |
||
84 | else: |
||
85 | # Default behavior |
||
86 | raise AttributeError(item) |
||
87 | |||
88 | def load_modules(self, args): |
||
89 | """Wrapper to load: plugins and export modules.""" |
||
90 | |||
91 | # Init the plugins dict |
||
92 | # Active plugins dictionnary |
||
93 | self._plugins = collections.defaultdict(dict) |
||
94 | # Load the plugins |
||
95 | self.load_plugins(args=args) |
||
96 | |||
97 | # Init the export modules dict |
||
98 | # Active exporters dictionnary |
||
99 | self._exports = collections.defaultdict(dict) |
||
100 | # All available exporters dictionnary |
||
101 | self._exports_all = collections.defaultdict(dict) |
||
102 | # Load the export modules |
||
103 | self.load_exports(args=args) |
||
104 | |||
105 | # Restoring system path |
||
106 | sys.path = sys_path |
||
107 | |||
108 | def _load_plugin(self, plugin_script, args=None, config=None): |
||
109 | """Load the plugin (script), init it and add to the _plugin dict.""" |
||
110 | # The key is the plugin name |
||
111 | # for example, the file glances_xxx.py |
||
112 | # generate self._plugins_list["xxx"] = ... |
||
113 | name = plugin_script[len(self.header):-3].lower() |
||
114 | |||
115 | # Loaf the plugin class |
||
116 | try: |
||
117 | # Import the plugin |
||
118 | plugin = __import__(plugin_script[:-3]) |
||
119 | # Init and add the plugin to the dictionary |
||
120 | self._plugins[name] = plugin.Plugin(args=args, config=config) |
||
121 | except Exception as e: |
||
0 ignored issues
–
show
Catching very general exceptions such as
Exception is usually not recommended.
Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed. So, unless you specifically plan to handle any error, consider adding a more specific exception.
Loading history...
The name
e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
122 | # If a plugin can not be loaded, display a critical message |
||
123 | # on the console but do not crash |
||
124 | logger.critical("Error while initializing the {} plugin ({})".format(name, e)) |
||
0 ignored issues
–
show
|
|||
125 | logger.error(traceback.format_exc()) |
||
126 | # Disable the plugin |
||
127 | if args is not None: |
||
128 | setattr(args, |
||
129 | 'disable_' + name, |
||
130 | False) |
||
131 | else: |
||
132 | # Set the disable_<name> to False by default |
||
133 | if args is not None: |
||
134 | setattr(args, |
||
135 | 'disable_' + name, |
||
136 | getattr(args, 'disable_' + name, False)) |
||
137 | |||
138 | def load_plugins(self, args=None): |
||
139 | """Load all plugins in the 'plugins' folder.""" |
||
140 | start_duration = Counter() |
||
141 | for item in os.listdir(plugins_path): |
||
142 | if (item.startswith(self.header) and |
||
143 | item.endswith(".py") and |
||
0 ignored issues
–
show
|
|||
144 | item != (self.header + "plugin.py")): |
||
0 ignored issues
–
show
|
|||
145 | # Load the plugin |
||
146 | start_duration.reset() |
||
147 | self._load_plugin(os.path.basename(item), |
||
148 | args=args, config=self.config) |
||
149 | logger.debug("Plugin {} started in {} seconds".format(item, |
||
0 ignored issues
–
show
|
|||
150 | start_duration.get())) |
||
0 ignored issues
–
show
|
|||
151 | |||
152 | # Log plugins list |
||
153 | logger.debug("Active plugins list: {}".format(self.getPluginsList())) |
||
0 ignored issues
–
show
|
|||
154 | |||
155 | def load_exports(self, args=None): |
||
156 | """Load all export modules in the 'exports' folder.""" |
||
157 | if args is None: |
||
158 | return False |
||
159 | header = "glances_" |
||
160 | # Build the export module available list |
||
161 | args_var = vars(locals()['args']) |
||
0 ignored issues
–
show
|
|||
162 | for item in os.listdir(exports_path): |
||
163 | export_name = os.path.basename(item)[len(header):-3].lower() |
||
164 | if (item.startswith(header) and |
||
165 | item.endswith(".py") and |
||
0 ignored issues
–
show
|
|||
166 | item != (header + "export.py") and |
||
0 ignored issues
–
show
|
|||
167 | item != (header + "history.py")): |
||
0 ignored issues
–
show
|
|||
168 | self._exports_all[export_name] = os.path.basename(item)[:-3] |
||
169 | # Set the disable_<name> to False by default |
||
170 | setattr(self.args, |
||
171 | 'export_' + export_name, |
||
172 | getattr(self.args, 'export_' + export_name, False)) |
||
173 | |||
174 | # Aim is to check if the export module should be loaded |
||
175 | for export_name in self._exports_all: |
||
176 | if getattr(self.args, 'export_' + export_name, False): |
||
177 | # Import the export module |
||
178 | export_module = __import__(self._exports_all[export_name]) |
||
179 | # Add the export to the dictionary |
||
180 | # The key is the module name |
||
181 | # for example, the file glances_xxx.py |
||
182 | # generate self._exports_list["xxx"] = ... |
||
183 | self._exports[export_name] = export_module.Export(args=args, |
||
184 | config=self.config) |
||
0 ignored issues
–
show
|
|||
185 | self._exports_all[export_name] = self._exports[export_name] |
||
186 | |||
187 | # Log plugins list |
||
188 | logger.debug("Active exports modules list: {}".format(self.getExportsList())) |
||
0 ignored issues
–
show
|
|||
189 | return True |
||
190 | |||
191 | def getPluginsList(self, enable=True): |
||
0 ignored issues
–
show
The name
getPluginsList does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
192 | """Return the plugins list. |
||
193 | |||
194 | if enable is True, only return the active plugins (default) |
||
195 | if enable is False, return all the plugins |
||
196 | |||
197 | Return: list of plugin name |
||
198 | """ |
||
199 | if enable: |
||
0 ignored issues
–
show
|
|||
200 | return [p for p in self._plugins if self._plugins[p].is_enable()] |
||
201 | else: |
||
202 | return [p for p in self._plugins] |
||
203 | |||
204 | def getExportsList(self, enable=True): |
||
0 ignored issues
–
show
The name
getExportsList does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
205 | """Return the exports list. |
||
206 | |||
207 | if enable is True, only return the active exporters (default) |
||
208 | if enable is False, return all the exporters |
||
209 | |||
210 | Return: list of export module name |
||
211 | """ |
||
212 | if enable: |
||
0 ignored issues
–
show
|
|||
213 | return [e for e in self._exports] |
||
214 | else: |
||
215 | return [e for e in self._exports_all] |
||
216 | |||
217 | def load_limits(self, config=None): |
||
218 | """Load the stats limits (except the one in the exclude list).""" |
||
219 | # For each plugins, call the load_limits method |
||
220 | for p in self._plugins: |
||
0 ignored issues
–
show
The name
p does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
221 | self._plugins[p].load_limits(config) |
||
222 | |||
223 | def update(self): |
||
224 | """Wrapper method to update the stats.""" |
||
225 | # For standalone and server modes |
||
226 | # For each plugins, call the update method |
||
227 | for p in self._plugins: |
||
0 ignored issues
–
show
The name
p does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
228 | if self._plugins[p].is_disable(): |
||
229 | # If current plugin is disable |
||
230 | # then continue to next plugin |
||
231 | continue |
||
232 | start_duration = Counter() |
||
0 ignored issues
–
show
|
|||
233 | # Update the stats... |
||
234 | self._plugins[p].update() |
||
235 | # ... the history |
||
236 | self._plugins[p].update_stats_history() |
||
237 | # ... and the views |
||
238 | self._plugins[p].update_views() |
||
239 | # logger.debug("Plugin {} update duration: {} seconds".format(p, |
||
240 | # start_duration.get())) |
||
0 ignored issues
–
show
|
|||
241 | |||
242 | def export(self, input_stats=None): |
||
243 | """Export all the stats. |
||
244 | |||
245 | Each export module is ran in a dedicated thread. |
||
246 | """ |
||
247 | # threads = [] |
||
248 | input_stats = input_stats or {} |
||
249 | |||
250 | for e in self._exports: |
||
0 ignored issues
–
show
The name
e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
251 | logger.debug("Export stats using the %s module" % e) |
||
0 ignored issues
–
show
|
|||
252 | thread = threading.Thread(target=self._exports[e].update, |
||
253 | args=(input_stats,)) |
||
254 | # threads.append(thread) |
||
255 | thread.start() |
||
256 | |||
257 | def getAll(self): |
||
0 ignored issues
–
show
The name
getAll does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
258 | """Return all the stats (list).""" |
||
259 | return [self._plugins[p].get_raw() for p in self._plugins] |
||
260 | |||
261 | def getAllAsDict(self): |
||
0 ignored issues
–
show
The name
getAllAsDict does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
262 | """Return all the stats (dict).""" |
||
263 | return {p: self._plugins[p].get_raw() for p in self._plugins} |
||
264 | |||
265 | def getAllExports(self, plugin_list=None): |
||
0 ignored issues
–
show
The name
getAllExports does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
266 | """ |
||
267 | Return all the stats to be exported (list). |
||
268 | Default behavor is to export all the stat |
||
269 | if plugin_list is provided, only export stats of given plugin (list) |
||
270 | """ |
||
271 | if plugin_list is None: |
||
272 | # All plugins should be exported |
||
273 | plugin_list = self._plugins |
||
274 | return [self._plugins[p].get_export() for p in self._plugins] |
||
275 | |||
276 | def getAllExportsAsDict(self, plugin_list=None): |
||
0 ignored issues
–
show
The name
getAllExportsAsDict does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
277 | """ |
||
278 | Return all the stats to be exported (list). |
||
279 | Default behavor is to export all the stat |
||
280 | if plugin_list is provided, only export stats of given plugin (list) |
||
281 | """ |
||
282 | if plugin_list is None: |
||
283 | # All plugins should be exported |
||
284 | plugin_list = self._plugins |
||
285 | return {p: self._plugins[p].get_export() for p in plugin_list} |
||
286 | |||
287 | def getAllLimits(self): |
||
0 ignored issues
–
show
The name
getAllLimits does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
288 | """Return the plugins limits list.""" |
||
289 | return [self._plugins[p].limits for p in self._plugins] |
||
290 | |||
291 | def getAllLimitsAsDict(self, plugin_list=None): |
||
0 ignored issues
–
show
The name
getAllLimitsAsDict does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
292 | """ |
||
293 | Return all the stats limits (dict). |
||
294 | Default behavor is to export all the limits |
||
295 | if plugin_list is provided, only export limits of given plugin (list) |
||
296 | """ |
||
297 | if plugin_list is None: |
||
298 | # All plugins should be exported |
||
299 | plugin_list = self._plugins |
||
300 | return {p: self._plugins[p].limits for p in plugin_list} |
||
301 | |||
302 | def getAllViews(self): |
||
0 ignored issues
–
show
The name
getAllViews does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
303 | """Return the plugins views.""" |
||
304 | return [self._plugins[p].get_views() for p in self._plugins] |
||
305 | |||
306 | def getAllViewsAsDict(self): |
||
0 ignored issues
–
show
The name
getAllViewsAsDict does not conform to the method naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
307 | """Return all the stats views (dict).""" |
||
308 | return {p: self._plugins[p].get_views() for p in self._plugins} |
||
309 | |||
310 | def get_plugin_list(self): |
||
311 | """Return the plugin list.""" |
||
312 | return self._plugins |
||
313 | |||
314 | def get_plugin(self, plugin_name): |
||
315 | """Return the plugin name.""" |
||
316 | if plugin_name in self._plugins: |
||
0 ignored issues
–
show
|
|||
317 | return self._plugins[plugin_name] |
||
318 | else: |
||
319 | return None |
||
320 | |||
321 | def end(self): |
||
322 | """End of the Glances stats.""" |
||
323 | # Close export modules |
||
324 | for e in self._exports: |
||
0 ignored issues
–
show
The name
e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
325 | self._exports[e].exit() |
||
326 | # Close plugins |
||
327 | for p in self._plugins: |
||
0 ignored issues
–
show
The name
p does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ ).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site.
Loading history...
|
|||
328 | self._plugins[p].exit() |
||
329 |