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

glances.plugins.glances_raid.Plugin.msg_curse()   F

Complexity

Conditions 14

Size

Total Lines 73
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 57
nop 3
dl 0
loc 73
rs 3.6
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like glances.plugins.glances_raid.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
"""RAID plugin."""
21
22
from glances.compat import iterkeys
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
23
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
24
from glances.plugins.glances_plugin import GlancesPlugin
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
25
26
# Import plugin specific dependency
27
try:
28
    from pymdstat import MdStat
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
29
except ImportError 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...
30
    import_error_tag = True
0 ignored issues
show
Coding Style Naming introduced by
The name import_error_tag 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...
31
    logger.warning("Missing Python Lib ({}), Raid plugin is disabled".format(e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
32
else:
33
    import_error_tag = False
0 ignored issues
show
Coding Style Naming introduced by
The name import_error_tag 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...
34
35
36
class Plugin(GlancesPlugin):
37
    """Glances RAID plugin.
38
39
    stats is a dict (see pymdstat documentation)
40
    """
41
42
    def __init__(self, args=None, config=None):
43
        """Init the plugin."""
44
        super(Plugin, self).__init__(args=args, config=config)
45
46
        # We want to display the stat in the curse interface
47
        self.display_curse = True
48
49
    @GlancesPlugin._check_decorator
50
    @GlancesPlugin._log_result_decorator
51
    def update(self):
52
        """Update RAID stats using the input method."""
53
        # Init new stats
54
        stats = self.get_init_value()
55
56
        if import_error_tag:
57
            return self.stats
58
59
        if self.input_method == 'local':
60
            # Update stats using the PyMDstat lib (https://github.com/nicolargo/pymdstat)
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...
61
            try:
62
                # Just for test
63
                # mds = MdStat(path='/home/nicolargo/dev/pymdstat/tests/mdstat.10')
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...
64
                mds = MdStat()
65
                stats = mds.get_stats()['arrays']
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("Can not grab RAID stats (%s)" % e)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
68
                return self.stats
69
70
        elif self.input_method == 'snmp':
71
            # Update stats using SNMP
72
            # No standard way for the moment...
73
            pass
74
75
        # Update the stats
76
        self.stats = stats
77
78
        return self.stats
79
80
    def msg_curse(self, args=None, max_width=None):
81
        """Return the dict to display in the curse interface."""
82
        # Init the return message
83
        ret = []
84
85
        # Only process if stats exist...
86
        if not self.stats or self.is_disable():
87
            return ret
88
89
        # Max size for the interface name
90
        name_max_width = max_width - 12
91
92
        # Header
93
        msg = '{:{width}}'.format('RAID disks',
94
                                  width=name_max_width)
95
        ret.append(self.curse_add_line(msg, "TITLE"))
96
        msg = '{:>7}'.format('Used')
97
        ret.append(self.curse_add_line(msg))
98
        msg = '{:>7}'.format('Avail')
99
        ret.append(self.curse_add_line(msg))
100
        # Data
101
        arrays = sorted(iterkeys(self.stats))
102
        for array in arrays:
103
            # New line
104
            ret.append(self.curse_new_line())
105
            # Display the current status
106
            status = self.raid_alert(self.stats[array]['status'],
107
                                     self.stats[array]['used'],
108
                                     self.stats[array]['available'],
109
                                     self.stats[array]['type'])
110
            # Data: RAID type name | disk used | disk available
111
            array_type = self.stats[array]['type'].upper() if self.stats[array]['type'] is not None else 'UNKNOWN'
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/80).

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

Loading history...
112
            # Build the full name = array type + array name
113
            full_name = '{} {}'.format(array_type, array)
114
            msg = '{:{width}}'.format(full_name,
115
                                      width=name_max_width)
116
            ret.append(self.curse_add_line(msg))
117
            if self.stats[array]['type'] == 'raid0' and self.stats[array]['status'] == 'active':
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (96/80).

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

Loading history...
118
                msg = '{:>7}'.format(len(self.stats[array]['components']))
119
                ret.append(self.curse_add_line(msg, status))
120
                msg = '{:>7}'.format('-')
121
                ret.append(self.curse_add_line(msg, status))
122
            elif self.stats[array]['status'] == 'active':
123
                msg = '{:>7}'.format(self.stats[array]['used'])
124
                ret.append(self.curse_add_line(msg, status))
125
                msg = '{:>7}'.format(self.stats[array]['available'])
126
                ret.append(self.curse_add_line(msg, status))
127
            elif self.stats[array]['status'] == 'inactive':
128
                ret.append(self.curse_new_line())
129
                msg = '└─ Status {}'.format(self.stats[array]['status'])
130
                ret.append(self.curse_add_line(msg, status))
131
                components = sorted(iterkeys(self.stats[array]['components']))
132
                for i, component in enumerate(components):
133
                    if i == len(components) - 1:
134
                        tree_char = '└─'
135
                    else:
136
                        tree_char = '├─'
137
                    ret.append(self.curse_new_line())
138
                    msg = '   {} disk {}: '.format(tree_char, self.stats[array]['components'][component])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (105/80).

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

Loading history...
139
                    ret.append(self.curse_add_line(msg))
140
                    msg = '{}'.format(component)
141
                    ret.append(self.curse_add_line(msg))
142
            if self.stats[array]['type'] != 'raid0' and (self.stats[array]['used'] < self.stats[array]['available']):
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (117/80).

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

Loading history...
143
                # Display current array configuration
144
                ret.append(self.curse_new_line())
145
                msg = '└─ Degraded mode'
146
                ret.append(self.curse_add_line(msg, status))
147
                if len(self.stats[array]['config']) < 17:
148
                    ret.append(self.curse_new_line())
149
                    msg = '   └─ {}'.format(self.stats[array]['config'].replace('_', 'A'))
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...
150
                    ret.append(self.curse_add_line(msg))
151
152
        return ret
153
154
    def raid_alert(self, status, used, available, type):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

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

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
        """RAID alert messages.
156
157
        [available/used] means that ideally the array may have _available_
158
        devices however, _used_ devices are in use.
159
        Obviously when used >= available then things are good.
160
        """
161
        if type == 'raid0':
162
            return 'OK'
163
        if status == 'inactive':
164
            return 'CRITICAL'
165
        if used is None or available is None:
166
            return 'DEFAULT'
167
        elif used < available:
168
            return 'WARNING'
169
        return 'OK'
170