Passed
Push — master ( cdb6bf...0d35d5 )
by Nicolas
04:08 queued 14s
created

GlancesPlugin.update_views_hidden()   F

Complexity

Conditions 15

Size

Total Lines 42
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 22
nop 1
dl 0
loc 42
rs 2.9998
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like glances.plugins.glances_plugin.GlancesPlugin.update_views_hidden() 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 -*-
0 ignored issues
show
coding-style introduced by
Too many lines in module (1015/1000)
Loading history...
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 plugins.
24
"""
25
26
import re
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
27
import json
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
28
import copy
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
29
from operator import itemgetter
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
30
31
from glances.compat import iterkeys, itervalues, listkeys, map, mean, nativestr
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in map.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
introduced by
import missing from __future__ import absolute_import
Loading history...
32
from glances.actions import GlancesActions
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
33
from glances.history import GlancesHistory
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
34
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
35
from glances.events import glances_events
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
36
from glances.thresholds import glances_thresholds
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
37
from glances.timer import Counter
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
38
39
40
class GlancesPlugin(object):
0 ignored issues
show
best-practice introduced by
Too many instance attributes (14/7)
Loading history...
best-practice introduced by
Too many public methods (53/20)
Loading history...
41
    """Main class for Glances plugin."""
42
43
    def __init__(self,
0 ignored issues
show
Bug Best Practice introduced by
The default value {} might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
44
                 args=None,
45
                 config=None,
46
                 items_history_list=None,
47
                 stats_init_value={}):
48
        """Init the plugin of plugins class.
49
50
        All Glances' plugins should inherit from this class. Most of the
51
        methods are already implemented in the father classes.
52
53
        Your plugin should return a dict or a list of dicts (stored in the
54
        self.stats). As an example, you can have a look on the mem plugin
55
        (for dict) or network (for list of dicts).
56
57
        A plugin should implement:
58
        - the __init__ constructor: define the self.display_curse
59
        - the reset method: to set your self.stats variable to {} or []
60
        - the update method: where your self.stats variable is set
61
        and optionnaly:
62
        - the get_key method: set the key of the dict (only for list of dict)
63
        - the update_view method: only if you need to trick your output
64
        - the msg_curse: define the curse (UI) message (if display_curse is True)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
65
66
        :args: args parameters
67
        :items_history_list: list of items to store in the history
68
        :stats_init_value: Default value for a stats item
69
        """
70
        # Plugin name (= module name without glances_)
71
        pos = self.__class__.__module__.find('glances_') + len('glances') + 1
72
        self.plugin_name = self.__class__.__module__[pos:]
73
        # logger.debug("Init plugin %s" % self.plugin_name)
74
75
        # Init the args
76
        self.args = args
77
78
        # Init the default alignement (for curses)
79
        self._align = 'left'
80
81
        # Init the input method
82
        self._input_method = 'local'
83
        self._short_system_name = None
84
85
        # Init the history list
86
        self.items_history_list = items_history_list
87
        self.stats_history = self.init_stats_history()
88
89
        # Init the limits (configuration keys) dictionnary
90
        self._limits = dict()
91
        if config is not None:
92
            logger.debug('Load section {} in {}'.format(self.plugin_name,
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
93
                                                        config.config_file_paths()))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
94
            self.load_limits(config=config)
95
96
        # Init the actions
97
        self.actions = GlancesActions(args=args)
98
99
        # Init the views
100
        self.views = dict()
101
102
        # Hide stats if all the hide_zero_fields has never been != 0
103
        # Default is False, always display stats
104
        self.hide_zero = False
105
        self.hide_zero_fields = []
106
107
        # Init the stats
108
        self.stats_init_value = stats_init_value
109
        self.stats = None
110
        self.reset()
111
112
    def __repr__(self):
113
        """Return the raw stats."""
114
        return self.stats
115
116
    def __str__(self):
117
        """Return the human-readable stats."""
118
        return str(self.stats)
119
120
    def get_init_value(self):
121
        """Return a copy of the init value."""
122
        return copy.copy(self.stats_init_value)
123
124
    def reset(self):
125
        """Reset the stats.
126
127
        This method should be overwrited by childs' classes.
128
        """
129
        self.stats = self.get_init_value()
130
131
    def exit(self):
132
        """Just log an event when Glances exit."""
133
        logger.debug("Stop the {} plugin".format(self.plugin_name))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
134
135
    def get_key(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
136
        """Return the key of the list."""
137
        return None
138
139
    def is_enable(self, plugin_name=None):
140
        """Return true if plugin is enabled."""
141
        if not plugin_name:
142
            plugin_name = self.plugin_name
143
        try:
144
            d = getattr(self.args, 'disable_' + plugin_name)
0 ignored issues
show
Coding Style Naming introduced by
The name d 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...
145
        except AttributeError:
146
            return True
147
        else:
148
            return d is False
149
150
    def is_disable(self, plugin_name=None):
151
        """Return true if plugin is disabled."""
152
        return not self.is_enable(plugin_name=plugin_name)
153
154
    def _json_dumps(self, d):
0 ignored issues
show
Coding Style Naming introduced by
The name d does not conform to the argument 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...
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
155
        """Return the object 'd' in a JSON format.
156
157
        Manage the issue #815 for Windows OS
158
        """
159
        try:
160
            return json.dumps(d)
161
        except UnicodeDecodeError:
162
            return json.dumps(d, ensure_ascii=False)
163
164
    def history_enable(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
165
        return self.args is not None and not self.args.disable_history and self.get_items_history_list() is not None
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (116/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
166
167
    def init_stats_history(self):
168
        """Init the stats history (dict of GlancesAttribute)."""
169
        if self.history_enable():
170
            init_list = [a['name'] for a in self.get_items_history_list()]
0 ignored issues
show
introduced by
Non-iterable value self.get_items_history_list() is used in an iterating context
Loading history...
171
            logger.debug("Stats history activated for plugin {} (items: {})".format(self.plugin_name, init_list))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
172
        return GlancesHistory()
173
174
    def reset_stats_history(self):
175
        """Reset the stats history (dict of GlancesAttribute)."""
176
        if self.history_enable():
177
            reset_list = [a['name'] for a in self.get_items_history_list()]
0 ignored issues
show
introduced by
Non-iterable value self.get_items_history_list() is used in an iterating context
Loading history...
178
            logger.debug("Reset history for plugin {} (items: {})".format(self.plugin_name, reset_list))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
179
            self.stats_history.reset()
180
181
    def update_stats_history(self):
182
        """Update stats history."""
183
        # If the plugin data is a dict, the dict's key should be used
184
        if self.get_key() is None:
185
            item_name = ''
186
        else:
187
            item_name = self.get_key()
188
        # Build the history
189
        if self.get_export() and self.history_enable():
190
            for i in self.get_items_history_list():
0 ignored issues
show
introduced by
Non-iterable value self.get_items_history_list() is used in an iterating context
Loading history...
191
                if isinstance(self.get_export(), list):
192
                    # Stats is a list of data
193
                    # Iter throught it (for exemple, iter throught network
194
                    # interface)
195
                    for l in self.get_export():
0 ignored issues
show
Coding Style Naming introduced by
The name l 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...
196
                        self.stats_history.add(
197
                            nativestr(l[item_name]) + '_' + nativestr(i['name']),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
198
                            l[i['name']],
199
                            description=i['description'],
200
                            history_max_size=self._limits['history_size'])
201
                else:
202
                    # Stats is not a list
203
                    # Add the item to the history directly
204
                    self.stats_history.add(nativestr(i['name']),
205
                                           self.get_export()[i['name']],
206
                                           description=i['description'],
207
                                           history_max_size=self._limits['history_size'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (89/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
208
209
    def get_items_history_list(self):
210
        """Return the items history list."""
211
        return self.items_history_list
212
213
    def get_raw_history(self, item=None, nb=0):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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...
214
        """Return the history (RAW format).
215
216
        - the stats history (dict of list) if item is None
217
        - the stats history for the given item (list) instead
218
        - None if item did not exist in the history
219
        """
220
        s = self.stats_history.get(nb=nb)
0 ignored issues
show
Coding Style Naming introduced by
The name s 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
        if item is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
222
            return s
223
        else:
224
            if item in s:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
225
                return s[item]
226
            else:
227
                return None
228
229
    def get_json_history(self, item=None, nb=0):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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...
230
        """Return the history (JSON format).
231
232
        - the stats history (dict of list) if item is None
233
        - the stats history for the given item (list) instead
234
        - None if item did not exist in the history
235
        Limit to lasts nb items (all if nb=0)
236
        """
237
        s = self.stats_history.get_json(nb=nb)
0 ignored issues
show
Coding Style Naming introduced by
The name s 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...
238
        if item is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
239
            return s
240
        else:
241
            if item in s:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
242
                return s[item]
243
            else:
244
                return None
245
246
    def get_export_history(self, item=None):
247
        """Return the stats history object to export."""
248
        return self.get_raw_history(item=item)
249
250
    def get_stats_history(self, item=None, nb=0):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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
        """Return the stats history (JSON format)."""
252
        s = self.get_json_history(nb=nb)
0 ignored issues
show
Coding Style Naming introduced by
The name s 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...
253
254
        if item is None:
255
            return self._json_dumps(s)
256
257 View Code Duplication
        if isinstance(s, dict):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
258
            try:
259
                return self._json_dumps({item: s[item]})
260
            except KeyError as e:
0 ignored issues
show
Coding Style Naming introduced by
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...
261
                logger.error("Cannot get item history {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
262
                return None
263
        elif isinstance(s, list):
264
            try:
265
                # Source:
266
                # http://stackoverflow.com/questions/4573875/python-get-index-of-dictionary-item-in-list
267
                return self._json_dumps({item: map(itemgetter(item), s)})
268
            except (KeyError, ValueError) as e:
0 ignored issues
show
Coding Style Naming introduced by
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...
269
                logger.error("Cannot get item history {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
270
                return None
271
        else:
272
            return None
273
274
    def get_trend(self, item, nb=6):
0 ignored issues
show
Coding Style Naming introduced by
The name nb does not conform to the argument 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...
275
        """Get the trend regarding to the last nb values.
276
277
        The trend is the diff between the mean of the last nb values
278
        and the current one.
279
        """
280
        raw_history = self.get_raw_history(item=item, nb=nb)
281
        if raw_history is None or len(raw_history) < nb:
282
            return None
283
        last_nb = [v[1] for v in raw_history]
284
        return last_nb[-1] - mean(last_nb[:-1])
285
286
    @property
287
    def input_method(self):
288
        """Get the input method."""
289
        return self._input_method
290
291
    @input_method.setter
292
    def input_method(self, input_method):
293
        """Set the input method.
294
295
        * local: system local grab (psutil or direct access)
296
        * snmp: Client server mode via SNMP
297
        * glances: Client server mode via Glances API
298
        """
299
        self._input_method = input_method
300
301
    @property
302
    def short_system_name(self):
303
        """Get the short detected OS name (SNMP)."""
304
        return self._short_system_name
305
306
    def sorted_stats(self):
307
        """Get the stats sorted by an alias (if present) or key."""
308
        key = self.get_key()
309
        return sorted(self.stats, key=lambda stat: tuple(map(
310
            lambda part: int(part) if part.isdigit() else part.lower(),
311
            re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key])
312
        )))
313
314
    @short_system_name.setter
315
    def short_system_name(self, short_name):
316
        """Set the short detected OS name (SNMP)."""
317
        self._short_system_name = short_name
318
319
    def set_stats(self, input_stats):
320
        """Set the stats to input_stats."""
321
        self.stats = input_stats
322
323
    def get_stats_snmp(self, bulk=False, snmp_oid=None):
324
        """Update stats using SNMP.
325
326
        If bulk=True, use a bulk request instead of a get request.
327
        """
328
        snmp_oid = snmp_oid or {}
329
330
        from glances.snmp import GlancesSNMPClient
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
331
332
        # Init the SNMP request
333
        clientsnmp = GlancesSNMPClient(host=self.args.client,
334
                                       port=self.args.snmp_port,
335
                                       version=self.args.snmp_version,
336
                                       community=self.args.snmp_community)
337
338
        # Process the SNMP request
339
        ret = {}
340
        if bulk:
0 ignored issues
show
unused-code introduced by
Too many nested blocks (6/5)
Loading history...
341
            # Bulk request
342
            snmpresult = clientsnmp.getbulk_by_oid(0, 10, itervalues(*snmp_oid))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable itervalues does not seem to be defined.
Loading history...
343
344
            if len(snmp_oid) == 1:
345
                # Bulk command for only one OID
346
                # Note: key is the item indexed but the OID result
347
                for item in snmpresult:
348
                    if iterkeys(item)[0].startswith(itervalues(snmp_oid)[0]):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable iterkeys does not seem to be defined.
Loading history...
349
                        ret[iterkeys(snmp_oid)[0] + iterkeys(item)
350
                            [0].split(itervalues(snmp_oid)[0])[1]] = itervalues(item)[0]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (88/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
351
            else:
352
                # Build the internal dict with the SNMP result
353
                # Note: key is the first item in the snmp_oid
354
                index = 1
355
                for item in snmpresult:
356
                    item_stats = {}
357
                    item_key = None
358
                    for key in iterkeys(snmp_oid):
359
                        oid = snmp_oid[key] + '.' + str(index)
360
                        if oid in item:
361
                            if item_key is None:
362
                                item_key = item[oid]
363
                            else:
364
                                item_stats[key] = item[oid]
365
                    if item_stats:
366
                        ret[item_key] = item_stats
367
                    index += 1
368
        else:
369
            # Simple get request
370
            snmpresult = clientsnmp.get_by_oid(itervalues(*snmp_oid))
371
372
            # Build the internal dict with the SNMP result
373
            for key in iterkeys(snmp_oid):
374
                ret[key] = snmpresult[snmp_oid[key]]
375
376
        return ret
377
378
    def get_raw(self):
379
        """Return the stats object."""
380
        return self.stats
381
382
    def get_export(self):
383
        """Return the stats object to export."""
384
        return self.get_raw()
385
386
    def get_stats(self):
387
        """Return the stats object in JSON format."""
388
        return self._json_dumps(self.stats)
389
390
    def get_stats_item(self, item):
391
        """Return the stats object for a specific item in JSON format.
392
393
        Stats should be a list of dict (processlist, network...)
394
        """
395 View Code Duplication
        if isinstance(self.stats, dict):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
396
            try:
397
                return self._json_dumps({item: self.stats[item]})
398
            except KeyError as e:
0 ignored issues
show
Coding Style Naming introduced by
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...
399
                logger.error("Cannot get item {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
400
                return None
401
        elif isinstance(self.stats, list):
402
            try:
403
                # Source:
404
                # http://stackoverflow.com/questions/4573875/python-get-index-of-dictionary-item-in-list
405
                # But https://github.com/nicolargo/glances/issues/1401
406
                return self._json_dumps({item: list(map(itemgetter(item), self.stats))})
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (88/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
407
            except (KeyError, ValueError) as e:
0 ignored issues
show
Coding Style Naming introduced by
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...
408
                logger.error("Cannot get item {} ({})".format(item, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
409
                return None
410
        else:
411
            return None
412
413
    def get_stats_value(self, item, value):
414
        """Return the stats object for a specific item=value in JSON format.
415
416
        Stats should be a list of dict (processlist, network...)
417
        """
418
        if not isinstance(self.stats, list):
419
            return None
420
        else:
421
            if value.isdigit():
422
                value = int(value)
423
            try:
424
                return self._json_dumps({value: [i for i in self.stats if i[item] == value]})
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (93/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
425
            except (KeyError, ValueError) as e:
0 ignored issues
show
Coding Style Naming introduced by
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...
426
                logger.error(
427
                    "Cannot get item({})=value({}) ({})".format(item, value, e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
428
                return None
429
430
    def update_views_hidden(self):
431
        """If the self.hide_zero is set then update the hidden field of the view
432
        It will check if all fields values are already be different from 0
433
        In this case, the hidden field is set to True
434
435
        Note: This function should be called by plugin (in the update_views method)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (83/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
436
437
        Example (for network plugin):
438
        __Init__
439
            self.hide_zero_fields = ['rx', 'tx']
440
        Update views
441
            ...
442
            self.update_views_hidden()
443
        """
444
        if not self.hide_zero:
445
            return False
446
        if (isinstance(self.get_raw(), list) and
447
                self.get_raw() is not None and
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 4 spaces).
Loading history...
448
                self.get_key() is not None):
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 4 spaces).
Loading history...
449
            # Stats are stored in a list of dict (ex: NETWORK, FS...)
450
            for i in self.get_raw():
451
                if any([i[f] for f in self.hide_zero_fields]):
452
                    for f in self.hide_zero_fields:
0 ignored issues
show
Coding Style Naming introduced by
The name f 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...
453
                        self.views[i[self.get_key(
454
                        )]][f]['_zero'] = self.views[i[self.get_key()]][f]['hidden']
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
455
                for f in self.hide_zero_fields:
0 ignored issues
show
Coding Style Naming introduced by
The name f 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...
456
                    self.views[i[self.get_key(
457
                    )]][f]['hidden'] = self.views[i[self.get_key()]][f]['_zero'] and i[f] == 0
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
458
        elif isinstance(self.get_raw(), dict) and self.get_raw() is not None:
459
            # 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
460
            # Warning: This code has never been tested because 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
461
            # no plugin with dict instance use the hidden function...
462
            #                       vvvv
463
            # 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
464
            # Stats are stored in a dict (ex: CPU, LOAD...)
465
            for key in listkeys(self.get_raw()):
0 ignored issues
show
Unused Code introduced by
The variable key seems to be unused.
Loading history...
466
                if any([self.get_raw()[f] for f in self.hide_zero_fields]):
467
                    for f in self.hide_zero_fields:
0 ignored issues
show
Coding Style Naming introduced by
The name f 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...
468
                        self.views[f]['_zero'] = self.views[f]['hidden']
469
                for f in self.hide_zero_fields:
0 ignored issues
show
Coding Style Naming introduced by
The name f 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...
470
                    self.views[f]['hidden'] = self.views['_zero'] and self.views[f] == 0
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (88/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
471
        return True
472
473
    def update_views(self):
474
        """Update the stats views.
475
476
        The V of MVC
477
        A dict of dict with the needed information to display the stats.
478
        Example for the stat xxx:
479
        'xxx': {'decoration': 'DEFAULT',  >>> The decoration of the stats
480
                'optional': False,        >>> Is the stat optional
481
                'additional': False,      >>> Is the stat provide additional information
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (88/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
482
                'splittable': False,      >>> Is the stat can be cut (like process lon name)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (92/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
483
                'hidden': False,          >>> Is the stats should be hidden in the UI 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
Coding Style introduced by
This line is too long as per the coding-style (85/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
484
                '_zero': True}            >>> For internal purpose only
485
        """
486
        ret = {}
487
488
        if (isinstance(self.get_raw(), list) and
489
                self.get_raw() is not None and
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 4 spaces).
Loading history...
490
                self.get_key() is not None):
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 4 spaces).
Loading history...
491
            # Stats are stored in a list of dict (ex: NETWORK, FS...)
492
            for i in self.get_raw():
493
                # i[self.get_key()] is the interface name (example for NETWORK)
494
                ret[i[self.get_key()]] = {}
495
                for key in listkeys(i):
496
                    value = {'decoration': 'DEFAULT',
497
                             'optional': False,
498
                             'additional': False,
499
                             'splittable': False,
500
                             'hidden': False,
501
                             '_zero': self.views[i[self.get_key()]][key]['_zero'] if i[self.get_key()] in self.views and key in self.views[i[self.get_key()]] else True}
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (168/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
502
                    ret[i[self.get_key()]][key] = value
503
        elif isinstance(self.get_raw(), dict) and self.get_raw() is not None:
504
            # Stats are stored in a dict (ex: CPU, LOAD...)
505
            for key in listkeys(self.get_raw()):
506
                value = {'decoration': 'DEFAULT',
507
                         'optional': False,
508
                         'additional': False,
509
                         'splittable': False,
510
                         'hidden': False,
511
                         '_zero': self.views[key]['_zero'] if key in self.views and '_zero' in self.views[key] else True}
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (121/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
512
                ret[key] = value
513
514
        self.views = ret
515
516
        return self.views
517
518
    def set_views(self, input_views):
519
        """Set the views to input_views."""
520
        self.views = input_views
521
522
    def get_views(self, item=None, key=None, option=None):
523
        """Return the views object.
524
525
        If key is None, return all the view for the current plugin
526
        else if option is None return the view for the specific key (all option)
527
        else return the view fo the specific key/option
528
529
        Specify item if the stats are stored in a dict of dict (ex: NETWORK, FS...)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (83/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
530
        """
531
        if item is None:
532
            item_views = self.views
533
        else:
534
            item_views = self.views[item]
535
536
        if key is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
537
            return item_views
538
        else:
539
            if option is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
540
                return item_views[key]
541
            else:
542
                if option in item_views[key]:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
543
                    return item_views[key][option]
544
                else:
545
                    return 'DEFAULT'
546
547
    def get_json_views(self, item=None, key=None, option=None):
548
        """Return the views (in JSON)."""
549
        return self._json_dumps(self.get_views(item, key, option))
550
551
    def load_limits(self, config):
552
        """Load limits from the configuration file, if it exists."""
553
        # By default set the history length to 3 points per second during one day
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
554
        self._limits['history_size'] = 28800
555
556
        if not hasattr(config, 'has_section'):
557
            return False
558
559
        # Read the global section
560
        # @TODO: not optimized because this section is loaded for each plugin...
561
        if config.has_section('global'):
562
            self._limits['history_size'] = config.get_float_value('global', 'history_size', default=28800)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
563
            logger.debug("Load configuration key: {} = {}".format('history_size', self._limits['history_size']))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (112/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
564
565
        # Read the plugin specific section
566
        if config.has_section(self.plugin_name):
567
            for level, _ in config.items(self.plugin_name):
568
                # Read limits
569
                limit = '_'.join([self.plugin_name, level])
570
                try:
571
                    self._limits[limit] = config.get_float_value(self.plugin_name, level)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (89/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
572
                except ValueError:
573
                    self._limits[limit] = config.get_value(self.plugin_name, level).split(",")
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
574
                logger.debug("Load limit: {} = {}".format(limit, self._limits[limit]))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (86/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
575
576
        return True
577
578
    @property
579
    def limits(self):
580
        """Return the limits object."""
581
        return self._limits
582
583
    @limits.setter
584
    def limits(self, input_limits):
585
        """Set the limits to input_limits."""
586
        self._limits = input_limits
587
588
    def get_stats_action(self):
589
        """Return stats for the action.
590
591
        By default return all the stats.
592
        Can be overwrite by plugins implementation.
593
        For example, Docker will return self.stats['containers']
594
        """
595
        return self.stats
596
597
    def get_stat_name(self, header=""):
598
        """"Return the stat name with an optional header"""
599
        ret = self.plugin_name
600
        if header != "":
601
            ret += '_' + header
602
        return ret
603
604
    def get_alert(self,
0 ignored issues
show
best-practice introduced by
Too many arguments (9/5)
Loading history...
605
                  current=0,
606
                  minimum=0,
607
                  maximum=100,
608
                  highlight_zero=True,
609
                  is_max=False,
610
                  header="",
611
                  action_key=None,
612
                  log=False):
613
        """Return the alert status relative to a current value.
614
615
        Use this function for minor stats.
616
617
        If current < CAREFUL of max then alert = OK
618
        If current > CAREFUL of max then alert = CAREFUL
619
        If current > WARNING of max then alert = WARNING
620
        If current > CRITICAL of max then alert = CRITICAL
621
622
        If highlight=True than 0.0 is highlighted
623
624
        If defined 'header' is added between the plugin name and the status.
625
        Only useful for stats with several alert status.
626
627
        If defined, 'action_key' define the key for the actions.
628
        By default, the action_key is equal to the header.
629
630
        If log=True than add log if necessary
631
        elif log=False than do not log
632
        elif log=None than apply the config given in the conf file
633
        """
634
        # Manage 0 (0.0) value if highlight_zero is not True
635
        if not highlight_zero and current == 0:
636
            return 'DEFAULT'
637
638
        # Compute the %
639
        try:
640
            value = (current * 100) / maximum
0 ignored issues
show
introduced by
division w/o __future__ statement
Loading history...
641
        except ZeroDivisionError:
642
            return 'DEFAULT'
643
        except TypeError:
644
            return 'DEFAULT'
645
646
        # Build the stat_name
647
        stat_name = self.get_stat_name(header=header)
648
649
        # Manage limits
650
        # If is_max is set then display the value in MAX
651
        ret = 'MAX' if is_max else 'OK'
652
        try:
653
            if value >= self.get_limit('critical', stat_name=stat_name):
654
                ret = 'CRITICAL'
655
            elif value >= self.get_limit('warning', stat_name=stat_name):
656
                ret = 'WARNING'
657
            elif value >= self.get_limit('careful', stat_name=stat_name):
658
                ret = 'CAREFUL'
659
            elif current < minimum:
660
                ret = 'CAREFUL'
661
        except KeyError:
662
            return 'DEFAULT'
663
664
        # Manage log
665
        log_str = ""
666
        if self.get_limit_log(stat_name=stat_name, default_action=log):
667
            # Add _LOG to the return string
668
            # So stats will be highlited with a specific color
669
            log_str = "_LOG"
670
            # Add the log to the list
671
            glances_events.add(ret, stat_name.upper(), value)
672
673
        # Manage threshold
674
        self.manage_threshold(stat_name, ret)
675
676
        # Manage action
677
        self.manage_action(stat_name, ret.lower(), header, action_key)
678
679
        # Default is 'OK'
680
        return ret + log_str
681
682
    def manage_threshold(self,
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
683
                         stat_name,
684
                         trigger):
685
        """Manage the threshold for the current stat."""
686
        glances_thresholds.add(stat_name, trigger)
687
688
    def manage_action(self,
689
                      stat_name,
690
                      trigger,
691
                      header,
692
                      action_key):
693
        """Manage the action for the current stat."""
694
        # Here is a command line for the current trigger ?
695
        try:
696
            command, repeat = self.get_limit_action(trigger, stat_name=stat_name)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
697
        except KeyError:
698
            # Reset the trigger
699
            self.actions.set(stat_name, trigger)
700
        else:
701
            # Define the action key for the stats dict
702
            # If not define, then it sets to header
703
            if action_key is None:
704
                action_key = header
705
706
            # A command line is available for the current alert
707
            # 1) Build the {{mustache}} dictionnary
708
            if isinstance(self.get_stats_action(), list):
709
                # If the stats are stored in a list of dict (fs plugin for exemple)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (83/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
710
                # Return the dict for the current header
711
                mustache_dict = {}
712
                for item in self.get_stats_action():
713
                    if item[self.get_key()] == action_key:
714
                        mustache_dict = item
715
                        break
716
            else:
717
                # Use the stats dict
718
                mustache_dict = self.get_stats_action()
719
            # 2) Run the action
720
            self.actions.run(
721
                stat_name, trigger,
722
                command, repeat, mustache_dict=mustache_dict)
723
724
    def get_alert_log(self,
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
725
                      current=0,
726
                      minimum=0,
727
                      maximum=100,
728
                      header="",
729
                      action_key=None):
730
        """Get the alert log."""
731
        return self.get_alert(current=current,
732
                              minimum=minimum,
733
                              maximum=maximum,
734
                              header=header,
735
                              action_key=action_key,
736
                              log=True)
737
738
    def get_limit(self, criticity, stat_name=""):
739
        """Return the limit value for the alert."""
740
        # Get the limit for stat + header
741
        # Exemple: network_wlan0_rx_careful
742
        try:
743
            limit = self._limits[stat_name + '_' + criticity]
744
        except KeyError:
745
            # Try fallback to plugin default limit
746
            # Exemple: network_careful
747
            limit = self._limits[self.plugin_name + '_' + criticity]
748
749
        # logger.debug("{} {} value is {}".format(stat_name, criticity, limit))
750
751
        # Return the limiter
752
        return limit
753
754
    def get_limit_action(self, criticity, stat_name=""):
755
        """Return the tuple (action, repeat) for the alert.
756
757
        - action is a command line
758
        - repeat is a bool
759
        """
760
        # Get the action for stat + header
761
        # Exemple: network_wlan0_rx_careful_action
762
        # Action key available ?
763
        ret = [(stat_name + '_' + criticity + '_action', False),
764
               (stat_name + '_' + criticity + '_action_repeat', True),
765
               (self.plugin_name + '_' + criticity + '_action', False),
766
               (self.plugin_name + '_' + criticity + '_action_repeat', True)]
767
        for r in ret:
0 ignored issues
show
Coding Style Naming introduced by
The name r 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...
768
            if r[0] in self._limits:
769
                return self._limits[r[0]], r[1]
770
771
        # No key found, the raise an error
772
        raise KeyError
773
774
    def get_limit_log(self, stat_name, default_action=False):
775
        """Return the log tag for the alert."""
776
        # Get the log tag for stat + header
777
        # Exemple: network_wlan0_rx_log
778
        try:
779
            log_tag = self._limits[stat_name + '_log']
780
        except KeyError:
781
            # Try fallback to plugin default log
782
            # Exemple: network_log
783
            try:
784
                log_tag = self._limits[self.plugin_name + '_log']
785
            except KeyError:
786
                # By defaukt, log are disabled
787
                return default_action
788
789
        # Return the action list
790
        return log_tag[0].lower() == 'true'
791
792
    def get_conf_value(self, value, header="", plugin_name=None, default=[]):
0 ignored issues
show
Bug Best Practice introduced by
The default value [] might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
793
        """Return the configuration (header_) value for the current plugin.
794
795
        ...or the one given by the plugin_name var.
796
        """
797
        if plugin_name is None:
798
            # If not default use the current plugin name
799
            plugin_name = self.plugin_name
800
801
        if header != "":
802
            # Add the header
803
            plugin_name = plugin_name + '_' + header
804
805
        try:
806
            return self._limits[plugin_name + '_' + value]
807
        except KeyError:
808
            return default
809
810
    def is_show(self, value, header=""):
811
        """Return True if the value is in the show configuration list.
812
        If the show value is empty, return True (show by default)
813
814
        The show configuration list is defined in the glances.conf file.
815
        It is a comma separed list of regexp.
816
        Example for diskio:
817
        show=sda.*
818
        """
819
        # @TODO: possible optimisation: create a re.compile list
820
        if self.get_conf_value('show', header=header) == []:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
821
            return True
822
        else:
823
            return any(j for j in [re.match(i, value) for i in self.get_conf_value('show', header=header)])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (107/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
824
825
    def is_hide(self, value, header=""):
826
        """Return True if the value is in the hide configuration list.
827
828
        The hide configuration list is defined in the glances.conf file.
829
        It is a comma separed list of regexp.
830
        Example for diskio:
831
        hide=sda2,sda5,loop.*
832
        """
833
        # @TODO: possible optimisation: create a re.compile list
834
        return any(j for j in [re.match(i, value) for i in self.get_conf_value('hide', header=header)])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
835
836
    def has_alias(self, header):
837
        """Return the alias name for the relative header or None if nonexist."""
838
        try:
839
            # Force to lower case (issue #1126)
840
            return self._limits[self.plugin_name + '_' + header.lower() + '_' + 'alias'][0]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (91/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
841
        except (KeyError, IndexError):
842
            # logger.debug("No alias found for {}".format(header))
843
            return None
844
845
    def msg_curse(self, args=None, max_width=None):
0 ignored issues
show
Unused Code introduced by
The argument args seems to be unused.
Loading history...
Unused Code introduced by
The argument max_width seems to be unused.
Loading history...
846
        """Return default string to display in the curse interface."""
847
        return [self.curse_add_line(str(self.stats))]
848
849
    def get_stats_display(self, args=None, max_width=None):
850
        """Return a dict with all the information needed to display the stat.
851
852
        key     | description
853
        ----------------------------
854
        display | Display the stat (True or False)
855
        msgdict | Message to display (list of dict [{ 'msg': msg, 'decoration': decoration } ... ])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (99/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
856
        align   | Message position
857
        """
858
        display_curse = False
859
860
        if hasattr(self, 'display_curse'):
861
            display_curse = self.display_curse
0 ignored issues
show
Bug introduced by
The Instance of GlancesPlugin does not seem to have a member named display_curse.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
862
        if hasattr(self, 'align'):
863
            align_curse = self._align
864
865
        if max_width is not None:
866
            ret = {'display': display_curse,
867
                   'msgdict': self.msg_curse(args, max_width=max_width),
868
                   'align': align_curse}
0 ignored issues
show
introduced by
The variable align_curse does not seem to be defined in case hasattr(self, 'align') on line 862 is False. Are you sure this can never be the case?
Loading history...
869
        else:
870
            ret = {'display': display_curse,
871
                   'msgdict': self.msg_curse(args),
872
                   'align': align_curse}
873
874
        return ret
875
876
    def curse_add_line(self, msg, decoration="DEFAULT",
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
877
                       optional=False, additional=False,
878
                       splittable=False):
879
        """Return a dict with.
880
881
        Where:
882
            msg: string
883
            decoration:
884
                DEFAULT: no decoration
885
                UNDERLINE: underline
886
                BOLD: bold
887
                TITLE: for stat title
888
                PROCESS: for process name
889
                STATUS: for process status
890
                NICE: for process niceness
891
                CPU_TIME: for process cpu time
892
                OK: Value is OK and non logged
893
                OK_LOG: Value is OK and logged
894
                CAREFUL: Value is CAREFUL and non logged
895
                CAREFUL_LOG: Value is CAREFUL and logged
896
                WARNING: Value is WARINING and non logged
897
                WARNING_LOG: Value is WARINING and logged
898
                CRITICAL: Value is CRITICAL and non logged
899
                CRITICAL_LOG: Value is CRITICAL and logged
900
            optional: True if the stat is optional (display only if space is available)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (87/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
901
            additional: True if the stat is additional (display only if space is available after optional)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
902
            spittable: Line can be splitted to fit on the screen (default is not)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
903
        """
904
        return {'msg': msg, 'decoration': decoration, 'optional': optional, 'additional': additional, 'splittable': splittable}
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (127/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
905
906
    def curse_new_line(self):
907
        """Go to a new line."""
908
        return self.curse_add_line('\n')
909
910
    @property
911
    def align(self):
912
        """Get the curse align."""
913
        return self._align
914
915
    @align.setter
916
    def align(self, value):
917
        """Set the curse align.
918
919
        value: left, right, bottom.
920
        """
921
        self._align = value
922
923
    def auto_unit(self, number,
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
924
                  low_precision=False,
925
                  min_symbol='K'
926
                  ):
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 1 space).
Loading history...
927
        """Make a nice human-readable string out of number.
928
929
        Number of decimal places increases as quantity approaches 1.
930
        CASE: 613421788        RESULT:       585M low_precision:       585M
931
        CASE: 5307033647       RESULT:      4.94G low_precision:       4.9G
932
        CASE: 44968414685      RESULT:      41.9G low_precision:      41.9G
933
        CASE: 838471403472     RESULT:       781G low_precision:       781G
934
        CASE: 9683209690677    RESULT:      8.81T low_precision:       8.8T
935
        CASE: 1073741824       RESULT:      1024M low_precision:      1024M
936
        CASE: 1181116006       RESULT:      1.10G low_precision:       1.1G
937
938
        :low_precision: returns less decimal places potentially (default is False)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (82/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
939
                        sacrificing precision for more readability.
940
        :min_symbol: Do not approache if number < min_symbol (default is K)
941
        """
942
        symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
943
        if min_symbol in symbols:
944
            symbols = symbols[symbols.index(min_symbol):]
945
        prefix = {
946
            'Y': 1208925819614629174706176,
947
            'Z': 1180591620717411303424,
948
            'E': 1152921504606846976,
949
            'P': 1125899906842624,
950
            'T': 1099511627776,
951
            'G': 1073741824,
952
            'M': 1048576,
953
            'K': 1024
954
        }
955
956
        for symbol in reversed(symbols):
957
            value = float(number) / prefix[symbol]
0 ignored issues
show
introduced by
division w/o __future__ statement
Loading history...
958
            if value > 1:
959
                decimal_precision = 0
960
                if value < 10:
961
                    decimal_precision = 2
962
                elif value < 100:
963
                    decimal_precision = 1
964
                if low_precision:
965
                    if symbol in 'MK':
966
                        decimal_precision = 0
967
                    else:
968
                        decimal_precision = min(1, decimal_precision)
969
                elif symbol in 'K':
970
                    decimal_precision = 0
971
                return '{:.{decimal}f}{symbol}'.format(
972
                    value, decimal=decimal_precision, symbol=symbol)
973
        return '{!s}'.format(number)
974
975
    def trend_msg(self, trend, significant=1):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
976
        """Return the trend message.
977
978
        Do not take into account if trend < significant
979
        """
980
        ret = '-'
981
        if trend is None:
982
            ret = ' '
983
        elif trend > significant:
984
            ret = '/'
985
        elif trend < -significant:
986
            ret = '\\'
987
        return ret
988
989
    def _check_decorator(fct):
990
        """Check if the plugin is enabled."""
991
        def wrapper(self, *args, **kw):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
992
            if self.is_enable():
993
                ret = fct(self, *args, **kw)
994
            else:
995
                ret = self.stats
996
            return ret
997
        return wrapper
998
999
    def _log_result_decorator(fct):
1000
        """Log (DEBUG) the result of the function fct."""
1001
        def wrapper(*args, **kw):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
1002
            counter = Counter()
1003
            ret = fct(*args, **kw)
1004
            duration = counter.get()
1005
            logger.debug("%s %s %s return %s in %s seconds" % (
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
1006
                args[0].__class__.__name__,
1007
                args[0].__class__.__module__[len('glances_'):],
1008
                fct.__name__, ret,
1009
                duration))
1010
            return ret
1011
        return wrapper
1012
1013
    # Mandatory to call the decorator in childs' classes
1014
    _check_decorator = staticmethod(_check_decorator)
0 ignored issues
show
introduced by
Consider using a decorator instead of calling staticmethod
Loading history...
1015
    _log_result_decorator = staticmethod(_log_result_decorator)
0 ignored issues
show
introduced by
Consider using a decorator instead of calling staticmethod
Loading history...
1016