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

glances/plugins/glances_load.py (9 issues)

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

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

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

Loading history...
66
            self.nb_log_core = 1
67
68
    def _getloadavg(self):
0 ignored issues
show
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...
69
        """Get load average. On both Linux and Windows thanks to PsUtil"""
70
        try:
71
            return psutil.getloadavg()
72
        except AttributeError:
73
            pass
74
        try:
75
            return os.getloadavg()
76
        except (AttributeError, OSError):
77
            return None
78
79
    @GlancesPlugin._check_decorator
80
    @GlancesPlugin._log_result_decorator
81
    def update(self):
82
        """Update load stats."""
83
        # Init new stats
84
        stats = self.get_init_value()
85
86
        if self.input_method == 'local':
87
            # Update stats using the standard system lib
88
89
            # Get the load using the os standard lib
90
            load = self._getloadavg()
91
            if load is None:
92
                stats = self.get_init_value()
93
            else:
94
                stats = {'min1': load[0],
95
                         'min5': load[1],
96
                         'min15': load[2],
97
                         'cpucore': self.nb_log_core}
98
99
        elif self.input_method == 'snmp':
100
            # Update stats using SNMP
101
            stats = self.get_stats_snmp(snmp_oid=snmp_oid)
102
103
            if stats['min1'] == '':
104
                stats = self.get_init_value()
105
                return stats
106
107
            # Python 3 return a dict like:
108
            # {'min1': "b'0.08'", 'min5': "b'0.12'", 'min15': "b'0.15'"}
109
            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...
110
                stats[k] = float(v)
111
112
            stats['cpucore'] = self.nb_log_core
113
114
        # Update the stats
115
        self.stats = stats
116
117
        return self.stats
118
119
    def update_views(self):
120
        """Update stats views."""
121
        # Call the father's method
122
        super(Plugin, self).update_views()
123
124
        # Add specifics informations
125
        try:
126
            # Alert and log
127
            self.views['min15']['decoration'] = self.get_alert_log(self.stats['min15'], maximum=100 * self.stats['cpucore'])
128
            # Alert only
129
            self.views['min5']['decoration'] = self.get_alert(self.stats['min5'], maximum=100 * self.stats['cpucore'])
130
        except KeyError:
131
            # try/except mandatory for Windows compatibility (no load stats)
132
            pass
133
134
    def msg_curse(self, args=None, max_width=None):
135
        """Return the dict to display in the curse interface."""
136
        # Init the return message
137
        ret = []
138
139
        # Only process if stats exist, not empty (issue #871) and plugin not disabled
0 ignored issues
show
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...
140
        if not self.stats or (self.stats == {}) or self.is_disable():
141
            return ret
142
143
        # Build the string message
144
        # Header
145
        msg = '{:8}'.format('LOAD')
146
        ret.append(self.curse_add_line(msg, "TITLE"))
147
        # Core number
148
        if 'cpucore' in self.stats and self.stats['cpucore'] > 0:
149
            msg = '{}-core'.format(int(self.stats['cpucore']))
150
            ret.append(self.curse_add_line(msg))
151
        # New line
152
        ret.append(self.curse_new_line())
153
        # 1min load
154
        msg = '{:8}'.format('1 min:')
155
        ret.append(self.curse_add_line(msg))
156
        msg = '{:>6.2f}'.format(self.stats['min1'])
157
        ret.append(self.curse_add_line(msg))
158
        # New line
159
        ret.append(self.curse_new_line())
160
        # 5min load
161
        msg = '{:8}'.format('5 min:')
162
        ret.append(self.curse_add_line(msg))
163
        msg = '{:>6.2f}'.format(self.stats['min5'])
164
        ret.append(self.curse_add_line(
165
            msg, self.get_views(key='min5', option='decoration')))
166
        # New line
167
        ret.append(self.curse_new_line())
168
        # 15min load
169
        msg = '{:8}'.format('15 min:')
170
        ret.append(self.curse_add_line(msg))
171
        msg = '{:>6.2f}'.format(self.stats['min15'])
172
        ret.append(self.curse_add_line(
173
            msg, self.get_views(key='min15', option='decoration')))
174
175
        return ret
176