Completed
Push — master ( 793552...8b5b19 )
by Nicolas
05:48 queued 01:54
created

glances.plugins.glances_load.Plugin.msg_curse()   C

Complexity

Conditions 11

Size

Total Lines 37
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 23
nop 3
dl 0
loc 37
rs 5.4
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like glances.plugins.glances_load.Plugin.msg_curse() 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) 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
"""Load plugin."""
21
22
import os
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
23
import psutil
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
introduced by
Unable to import 'psutil'
Loading history...
24
25
from glances.compat import iteritems
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
26
from glances.plugins.glances_core import Plugin as CorePlugin
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
27
from glances.plugins.glances_plugin import GlancesPlugin
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
28
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
29
30
# SNMP OID
31
# 1 minute Load: .1.3.6.1.4.1.2021.10.1.3.1
32
# 5 minute Load: .1.3.6.1.4.1.2021.10.1.3.2
33
# 15 minute Load: .1.3.6.1.4.1.2021.10.1.3.3
34
snmp_oid = {'min1': '1.3.6.1.4.1.2021.10.1.3.1',
0 ignored issues
show
Coding Style Naming introduced by
The name snmp_oid does not conform to the constant naming conventions ((([A-Z_][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...
35
            'min5': '1.3.6.1.4.1.2021.10.1.3.2',
36
            'min15': '1.3.6.1.4.1.2021.10.1.3.3'}
37
38
# Define the history items list
39
# All items in this list will be historised if the --enable-history tag is set
40
items_history_list = [{'name': 'min1',
0 ignored issues
show
Coding Style Naming introduced by
The name items_history_list does not conform to the constant naming conventions ((([A-Z_][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...
41
                       'description': '1 minute load'},
42
                      {'name': 'min5',
43
                       'description': '5 minutes load'},
44
                      {'name': 'min15',
45
                       'description': '15 minutes load'}]
46
47
48
class Plugin(GlancesPlugin):
49
    """Glances load plugin.
50
51
    stats is a dict
52
    """
53
54
    def __init__(self, args=None, config=None):
55
        """Init the plugin."""
56
        super(Plugin, self).__init__(args=args,
57
                                     config=config,
58
                                     items_history_list=items_history_list)
59
60
        # We want to display the stat in the curse interface
61
        self.display_curse = True
62
63
        # Call CorePlugin in order to display the core number
64
        try:
65
            self.nb_log_core = CorePlugin(args=self.args).update()["log"]
66
        except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
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...
67
            logger.debug('Error: Can not retrieve the CPU core number (set it to 1) ({})'.format(e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (100/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...
68
            self.nb_log_core = 1
69
70
    def _getloadavg(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...
71
        """Get load average. On both Linux and Windows thanks to PsUtil"""
72
        try:
73
            return psutil.getloadavg()
74
        except AttributeError:
75
            pass
76
        try:
77
            return os.getloadavg()
78
        except (AttributeError, OSError):
79
            return None
80
81
    @GlancesPlugin._check_decorator
82
    @GlancesPlugin._log_result_decorator
83
    def update(self):
84
        """Update load stats."""
85
        # Init new stats
86
        stats = self.get_init_value()
87
88
        if self.input_method == 'local':
89
            # Update stats using the standard system lib
90
91
            # Get the load using the os standard lib
92
            load = self._getloadavg()
93
            if load is None:
94
                stats = self.get_init_value()
95
            else:
96
                stats = {'min1': load[0],
97
                         'min5': load[1],
98
                         'min15': load[2],
99
                         'cpucore': self.nb_log_core}
100
101
        elif self.input_method == 'snmp':
102
            # Update stats using SNMP
103
            stats = self.get_stats_snmp(snmp_oid=snmp_oid)
104
105
            if stats['min1'] == '':
106
                stats = self.get_init_value()
107
                return stats
108
109
            # Python 3 return a dict like:
110
            # {'min1': "b'0.08'", 'min5': "b'0.12'", 'min15': "b'0.15'"}
111
            for k, v in iteritems(stats):
0 ignored issues
show
Coding Style Naming introduced by
The name v 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...
112
                stats[k] = float(v)
113
114
            stats['cpucore'] = self.nb_log_core
115
116
        # Update the stats
117
        self.stats = stats
118
119
        return self.stats
120
121
    def update_views(self):
122
        """Update stats views."""
123
        # Call the father's method
124
        super(Plugin, self).update_views()
125
126
        # Add specifics informations
127
        try:
128
            # Alert and log
129
            self.views['min15']['decoration'] = self.get_alert_log(self.stats['min15'], maximum=100 * self.stats['cpucore'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (124/80).

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

Loading history...
130
            # Alert only
131
            self.views['min5']['decoration'] = self.get_alert(self.stats['min5'], maximum=100 * self.stats['cpucore'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (118/80).

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

Loading history...
132
        except KeyError:
133
            # try/except mandatory for Windows compatibility (no load stats)
134
            pass
135
136
    def msg_curse(self, args=None, max_width=None):
137
        """Return the dict to display in the curse interface."""
138
        # Init the return message
139
        ret = []
140
141
        # Only process if stats exist, not empty (issue #871) and plugin not disabled
0 ignored issues
show
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...
142
        if not self.stats or (self.stats == {}) or self.is_disable():
143
            return ret
144
145
        # Build the string message
146
        # Header
147
        msg = '{:6}'.format('LOAD%' if (args.disable_irix and self.nb_log_core != 0) else 'LOAD')
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (97/80).

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

Loading history...
148
        ret.append(self.curse_add_line(msg, "TITLE"))
149
        # Core number
150
        if 'cpucore' in self.stats and self.stats['cpucore'] > 0:
151
            msg = '{:3}-core'.format(int(self.stats['cpucore']))
152
            ret.append(self.curse_add_line(msg))
153
        # Loop over 1min, 5min and 15min load
154
        for load_time in ['1', '5', '15']:
155
            ret.append(self.curse_new_line())
156
            msg = '{:8}'.format('{} min:'.format(load_time))
157
            ret.append(self.curse_add_line(msg))
158
            if args.disable_irix and self.nb_log_core != 0:
159
                # Enable Irix mode for load (see issue #1554)
160
                load_stat = self.stats['min{}'.format(load_time)] / self.nb_log_core * 100
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (90/80).

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

Loading history...
introduced by
division w/o __future__ statement
Loading history...
161
            else:
162
                load_stat = self.stats['min{}'.format(load_time)]
163
            msg = '{:>6.2f}'.format(load_stat)
164
            if load_time == '1':
165
                ret.append(self.curse_add_line(msg))
166
            else:
167
                # Alert is only for 5 and 15 min
168
                ret.append(self.curse_add_line(
169
                    msg, self.get_views(key='min{}'.format(load_time),
170
                                        option='decoration')))
171
172
        return ret
173