Test Failed
Push — develop ( 66c9ff...e21229 )
by Nicolas
05:06
created

glances/stats.py (1 issue)

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
23
import os
24
import sys
25
import threading
26
import traceback
27
28
from glances.logger import logger
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
29
from glances.globals import exports_path, plugins_path, sys_path
30
from glances.timer import Counter
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:
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))
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
144
                    item != (self.header + "plugin.py")):
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,
150
                                                                      start_duration.get()))
151
152
        # Log plugins list
153
        logger.debug("Active plugins list: {}".format(self.getPluginsList()))
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'])
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
166
                    item != (header + "export.py") and
167
                    item != (header + "history.py")):
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)
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()))
189
        return True
190
191
    def getPluginsList(self, enable=True):
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:
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):
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:
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:
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:
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()
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()))
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:
251
            logger.debug("Export stats using the %s module" % e)
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):
258
        """Return all the stats (list)."""
259
        return [self._plugins[p].get_raw() for p in self._plugins]
260
261
    def getAllAsDict(self):
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):
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):
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):
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):
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):
303
        """Return the plugins views."""
304
        return [self._plugins[p].get_views() for p in self._plugins]
305
306
    def getAllViewsAsDict(self):
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:
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:
325
            self._exports[e].exit()
326
        # Close plugins
327
        for p in self._plugins:
328
            self._plugins[p].exit()
329