Completed
Push — master ( e61e99...a0b053 )
by Nicolas
01:23
created

GlancesStats   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 222
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 222
rs 4.8387
wmc 58

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __getattr__() 0 21 3
A __init__() 0 12 1
B load_plugins() 0 20 6
A load_modules() 0 17 1
F load_exports() 0 26 9
B getAllPlugins() 0 7 5
A load_limits() 0 5 2
A getAll() 0 3 2
A update() 0 15 3
A export() 0 14 2
A getExportList() 0 3 2
A getAllViewsAsDict() 0 3 2
A getAllViews() 0 3 2
A getAllExportsAsDict() 0 10 3
A getAllExports() 0 6 2
A getAllAsDict() 0 3 2
A get_plugin() 0 6 2
A getAllLimits() 0 3 2
A end() 0 8 3
A get_plugin_list() 0 3 1
A getAllLimitsAsDict() 0 10 3

How to fix   Complexity   

Complex Class

Complex classes like GlancesStats often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2017 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
27
from glances.globals import exports_path, plugins_path, sys_path
28
from glances.logger import logger
29
30
31
class GlancesStats(object):
32
33
    """This class stores, updates and gives stats."""
34
35
    def __init__(self, config=None, args=None):
36
        # Set the config instance
37
        self.config = config
38
39
        # Set the argument instance
40
        self.args = args
41
42
        # Load plugins and exports modules
43
        self.load_modules(self.args)
44
45
        # Load the limits (for plugins)
46
        self.load_limits(self.config)
47
48
    def __getattr__(self, item):
49
        """Overwrite the getattr method in case of attribute is not found.
50
51
        The goal is to dynamically generate the following methods:
52
        - getPlugname(): return Plugname stat in JSON format
53
        """
54
        # Check if the attribute starts with 'get'
55
        if item.startswith('get'):
56
            # Get the plugin name
57
            plugname = item[len('get'):].lower()
58
            # Get the plugin instance
59
            plugin = self._plugins[plugname]
60
            if hasattr(plugin, 'get_stats'):
61
                # The method get_stats exist, return it
62
                return getattr(plugin, 'get_stats')
63
            else:
64
                # The method get_stats is not found for the plugin
65
                raise AttributeError(item)
66
        else:
67
            # Default behavior
68
            raise AttributeError(item)
69
70
    def load_modules(self, args):
71
        """Wrapper to load: plugins and export modules."""
72
73
        # Init the plugins dict
74
        self._plugins = collections.defaultdict(dict)
75
76
        # Load the plugins
77
        self.load_plugins(args=args)
78
79
        # Init the export modules dict
80
        self._exports = collections.defaultdict(dict)
81
82
        # Load the export modules
83
        self.load_exports(args=args)
84
85
        # Restoring system path
86
        sys.path = sys_path
87
88
    def load_plugins(self, args=None):
89
        """Load all plugins in the 'plugins' folder."""
90
        header = "glances_"
91
        for item in os.listdir(plugins_path):
92
            if (item.startswith(header) and
93
                    item.endswith(".py") and
94
                    item != (header + "plugin.py")):
95
                # Import the plugin
96
                plugin = __import__(os.path.basename(item)[:-3])
97
                # Add the plugin to the dictionary
98
                # The key is the plugin name
99
                # for example, the file glances_xxx.py
100
                # generate self._plugins_list["xxx"] = ...
101
                plugin_name = os.path.basename(item)[len(header):-3].lower()
102
                if plugin_name in ('help', 'amps', 'ports'):
103
                    self._plugins[plugin_name] = plugin.Plugin(args=args, config=self.config)
104
                else:
105
                    self._plugins[plugin_name] = plugin.Plugin(args=args)
106
        # Log plugins list
107
        logger.debug("Available plugins list: {}".format(self.getAllPlugins()))
108
109
    def load_exports(self, args=None):
110
        """Load all export modules in the 'exports' folder."""
111
        if args is None:
112
            return False
113
        header = "glances_"
114
        # Transform the arguments list into a dict
115
        # The aim is to chec if the export module should be loaded
116
        args_var = vars(locals()['args'])
117
        for item in os.listdir(exports_path):
118
            export_name = os.path.basename(item)[len(header):-3].lower()
119
            if (item.startswith(header) and
120
                    item.endswith(".py") and
121
                    item != (header + "export.py") and
122
                    item != (header + "history.py") and
123
                    args_var['export_' + export_name] is not None and
124
                    args_var['export_' + export_name] is not False):
125
                # Import the export module
126
                export_module = __import__(os.path.basename(item)[:-3])
127
                # Add the export to the dictionary
128
                # The key is the module name
129
                # for example, the file glances_xxx.py
130
                # generate self._exports_list["xxx"] = ...
131
                self._exports[export_name] = export_module.Export(args=args, config=self.config)
132
        # Log plugins list
133
        logger.debug("Available exports modules list: {}".format(self.getExportList()))
134
        return True
135
136
    def getAllPlugins(self, enable=True):
137
        """Return the enable plugins list.
138
        if enable is False, return the list of all the plugins"""
139
        if enable:
140
            return [p for p in self._plugins if self._plugins[p].is_enable()]
141
        else:
142
            return [p for p in self._plugins]
143
144
    def getExportList(self):
145
        """Return the exports modules list."""
146
        return [e for e in self._exports]
147
148
    def load_limits(self, config=None):
149
        """Load the stats limits (except the one in the exclude list)."""
150
        # For each plugins, call the load_limits method
151
        for p in self._plugins:
152
            self._plugins[p].load_limits(config)
153
154
    def update(self):
155
        """Wrapper method to update the stats."""
156
        # For standalone and server modes
157
        # For each plugins, call the update method
158
        for p in self._plugins:
159
            if self._plugins[p].is_disable():
160
                # If current plugin is disable
161
                # then continue to next plugin
162
                continue
163
            # Update the stats...
164
            self._plugins[p].update()
165
            # ... the history
166
            self._plugins[p].update_stats_history()
167
            # ... and the views
168
            self._plugins[p].update_views()
169
170
    def export(self, input_stats=None):
171
        """Export all the stats.
172
173
        Each export module is ran in a dedicated thread.
174
        """
175
        # threads = []
176
        input_stats = input_stats or {}
177
178
        for e in self._exports:
179
            logger.debug("Export stats using the %s module" % e)
180
            thread = threading.Thread(target=self._exports[e].update,
181
                                      args=(input_stats,))
182
            # threads.append(thread)
183
            thread.start()
184
185
    def getAll(self):
186
        """Return all the stats (list)."""
187
        return [self._plugins[p].get_raw() for p in self._plugins]
188
189
    def getAllAsDict(self):
190
        """Return all the stats (dict)."""
191
        return {p: self._plugins[p].get_raw() for p in self._plugins}
192
193
    def getAllExports(self):
194
        """
195
        Return all the stats to be exported (list).
196
        Default behavor is to export all the stat
197
        """
198
        return [self._plugins[p].get_export() for p in self._plugins]
199
200
    def getAllExportsAsDict(self, plugin_list=None):
201
        """
202
        Return all the stats to be exported (list).
203
        Default behavor is to export all the stat
204
        if plugin_list is provided, only export stats of given plugin (list)
205
        """
206
        if plugin_list is None:
207
            # All plugins should be exported
208
            plugin_list = self._plugins
209
        return {p: self._plugins[p].get_export() for p in plugin_list}
210
211
    def getAllLimits(self):
212
        """Return the plugins limits list."""
213
        return [self._plugins[p].limits for p in self._plugins]
214
215
    def getAllLimitsAsDict(self, plugin_list=None):
216
        """
217
        Return all the stats limits (dict).
218
        Default behavor is to export all the limits
219
        if plugin_list is provided, only export limits of given plugin (list)
220
        """
221
        if plugin_list is None:
222
            # All plugins should be exported
223
            plugin_list = self._plugins
224
        return {p: self._plugins[p].limits for p in plugin_list}
225
226
    def getAllViews(self):
227
        """Return the plugins views."""
228
        return [self._plugins[p].get_views() for p in self._plugins]
229
230
    def getAllViewsAsDict(self):
231
        """Return all the stats views (dict)."""
232
        return {p: self._plugins[p].get_views() for p in self._plugins}
233
234
    def get_plugin_list(self):
235
        """Return the plugin list."""
236
        return self._plugins
237
238
    def get_plugin(self, plugin_name):
239
        """Return the plugin name."""
240
        if plugin_name in self._plugins:
241
            return self._plugins[plugin_name]
242
        else:
243
            return None
244
245
    def end(self):
246
        """End of the Glances stats."""
247
        # Close export modules
248
        for e in self._exports:
249
            self._exports[e].exit()
250
        # Close plugins
251
        for p in self._plugins:
252
            self._plugins[p].exit()
253