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

glances.plugins.glances_raid.Plugin.__init__()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
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