Test Failed
Push — develop ( 30cc9f...48251c )
by Nicolas
02:03
created

glances.plugins.raid.PluginModel.msg_curse()   F

Complexity

Conditions 15

Size

Total Lines 75
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 58
nop 3
dl 0
loc 75
rs 2.9998
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.raid.PluginModel.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
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
6
#
7
# SPDX-License-Identifier: LGPL-3.0-only
8
#
9
10
"""RAID plugin."""
11
12
from glances.globals import iterkeys
13
from glances.logger import logger
14
from glances.plugins.plugin.model import GlancesPluginModel
15
16
# Import plugin specific dependency
17
try:
18
    from pymdstat import MdStat
19
except ImportError as e:
20
    import_error_tag = True
21
    logger.warning("Missing Python Lib ({}), Raid plugin is disabled".format(e))
22
else:
23
    import_error_tag = False
24
25
26
class PluginModel(GlancesPluginModel):
27
    """Glances RAID plugin.
28
29
    stats is a dict (see pymdstat documentation)
30
    """
31
32
    def __init__(self, args=None, config=None):
33
        """Init the plugin."""
34
        super(PluginModel, self).__init__(args=args, config=config)
35
36
        # We want to display the stat in the curse interface
37
        self.display_curse = True
38
39
    @GlancesPluginModel._check_decorator
40
    @GlancesPluginModel._log_result_decorator
41
    def update(self):
42
        """Update RAID stats using the input method."""
43
        # Init new stats
44
        stats = self.get_init_value()
45
46
        if import_error_tag:
47
            return self.stats
48
49
        if self.input_method == 'local':
50
            # Update stats using the PyMDstat lib (https://github.com/nicolargo/pymdstat)
51
            try:
52
                # Just for test
53
                # mds = MdStat(path='/home/nicolargo/dev/pymdstat/tests/mdstat.10')
54
                mds = MdStat()
55
                stats = mds.get_stats()['arrays']
56
            except Exception as e:
57
                logger.debug("Can not grab RAID stats (%s)" % e)
58
                return self.stats
59
60
        elif self.input_method == 'snmp':
61
            # Update stats using SNMP
62
            # No standard way for the moment...
63
            pass
64
65
        # Update the stats
66
        self.stats = stats
67
68
        return self.stats
69
70
    def msg_curse(self, args=None, max_width=None):
71
        """Return the dict to display in the curse interface."""
72
        # Init the return message
73
        ret = []
74
75
        # Only process if stats exist...
76
        if not self.stats or self.is_disabled():
77
            return ret
78
79
        # Max size for the interface name
80
        name_max_width = max_width - 12
81
82
        # Header
83
        msg = '{:{width}}'.format('RAID disks', width=name_max_width)
84
        ret.append(self.curse_add_line(msg, "TITLE"))
85
        msg = '{:>7}'.format('Used')
86
        ret.append(self.curse_add_line(msg))
87
        msg = '{:>7}'.format('Avail')
88
        ret.append(self.curse_add_line(msg))
89
        # Data
90
        arrays = sorted(iterkeys(self.stats))
91
        for array in arrays:
92
            # New line
93
            ret.append(self.curse_new_line())
94
            # Display the current status
95
            if not isinstance(self.stats[array], dict):
96
                continue
97
            status = self.raid_alert(
98
                self.stats[array]['status'],
99
                self.stats[array]['used'],
100
                self.stats[array]['available'],
101
                self.stats[array]['type'],
102
            )
103
            # Data: RAID type name | disk used | disk available
104
            array_type = self.stats[array]['type'].upper() if self.stats[array]['type'] is not None else 'UNKNOWN'
105
            # Build the full name = array type + array name
106
            full_name = '{} {}'.format(array_type, array)
107
            msg = '{:{width}}'.format(full_name, width=name_max_width)
108
            ret.append(self.curse_add_line(msg))
109
            if self.stats[array]['type'] == 'raid0' and self.stats[array]['status'] == 'active':
110
                msg = '{:>7}'.format(len(self.stats[array]['components']))
111
                ret.append(self.curse_add_line(msg, status))
112
                msg = '{:>7}'.format('-')
113
                ret.append(self.curse_add_line(msg, status))
114
            elif self.stats[array]['status'] == 'active':
115
                msg = '{:>7}'.format(self.stats[array]['used'])
116
                ret.append(self.curse_add_line(msg, status))
117
                msg = '{:>7}'.format(self.stats[array]['available'])
118
                ret.append(self.curse_add_line(msg, status))
119
            elif self.stats[array]['status'] == 'inactive':
120
                ret.append(self.curse_new_line())
121
                msg = '└─ Status {}'.format(self.stats[array]['status'])
122
                ret.append(self.curse_add_line(msg, status))
123
                components = sorted(iterkeys(self.stats[array]['components']))
124
                for i, component in enumerate(components):
125
                    if i == len(components) - 1:
126
                        tree_char = '└─'
127
                    else:
128
                        tree_char = '├─'
129
                    ret.append(self.curse_new_line())
130
                    msg = '   {} disk {}: '.format(tree_char, self.stats[array]['components'][component])
131
                    ret.append(self.curse_add_line(msg))
132
                    msg = '{}'.format(component)
133
                    ret.append(self.curse_add_line(msg))
134
            if self.stats[array]['type'] != 'raid0' and (self.stats[array]['used'] < self.stats[array]['available']):
135
                # Display current array configuration
136
                ret.append(self.curse_new_line())
137
                msg = '└─ Degraded mode'
138
                ret.append(self.curse_add_line(msg, status))
139
                if len(self.stats[array]['config']) < 17:
140
                    ret.append(self.curse_new_line())
141
                    msg = '   └─ {}'.format(self.stats[array]['config'].replace('_', 'A'))
142
                    ret.append(self.curse_add_line(msg))
143
144
        return ret
145
146
    def raid_alert(self, status, used, available, type):
147
        """RAID alert messages.
148
149
        [available/used] means that ideally the array may have _available_
150
        devices however, _used_ devices are in use.
151
        Obviously when used >= available then things are good.
152
        """
153
        if type == 'raid0':
154
            return 'OK'
155
        if status == 'inactive':
156
            return 'CRITICAL'
157
        if used is None or available is None:
158
            return 'DEFAULT'
159
        elif used < available:
160
            return 'WARNING'
161
        return 'OK'
162