glances.plugins.glances_diskio.Plugin.update()   F
last analyzed

Complexity

Conditions 14

Size

Total Lines 81
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 48
nop 1
dl 0
loc 81
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_diskio.Plugin.update() 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
"""Disk I/O plugin."""
21
from __future__ import unicode_literals
22
23
from glances.compat import nativestr, n
0 ignored issues
show
introduced by Nicolas Hennion
import missing from __future__ import absolute_import
Loading history...
24
from glances.timer import getTimeSinceLastUpdate
0 ignored issues
show
introduced by Alessio Sergi
import missing from __future__ import absolute_import
Loading history...
25
from glances.plugins.glances_plugin import GlancesPlugin
0 ignored issues
show
introduced by Alessio Sergi
import missing from __future__ import absolute_import
Loading history...
26
27
import psutil
0 ignored issues
show
introduced by Alessio Sergi
import missing from __future__ import absolute_import
Loading history...
introduced by Alessio Sergi
Unable to import 'psutil'
Loading history...
28
29
30
# Define the history items list
31
items_history_list = [{'name': 'read_bytes',
0 ignored issues
show
Coding Style Naming introduced by Nicolas Hennion
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...
32
                       'description': 'Bytes read per second',
33
                       'y_unit': 'B/s'},
34
                      {'name': 'write_bytes',
35
                       'description': 'Bytes write per second',
36
                       'y_unit': 'B/s'}]
37
38
39
class Plugin(GlancesPlugin):
40
    """Glances disks I/O plugin.
41
42
    stats is a list
43
    """
44
45
    def __init__(self, args=None, config=None):
46
        """Init the plugin."""
47
        super(Plugin, self).__init__(args=args,
48
                                     config=config,
49
                                     items_history_list=items_history_list,
50
                                     stats_init_value=[])
51
52
        # We want to display the stat in the curse interface
53
        self.display_curse = True
54
55
    def get_key(self):
56
        """Return the key of the list."""
57
        return 'disk_name'
58
59
    @GlancesPlugin._check_decorator
60
    @GlancesPlugin._log_result_decorator
61
    def update(self):
62
        """Update disk I/O stats using the input method."""
63
        # Init new stats
64
        stats = self.get_init_value()
65
66
        if self.input_method == 'local':
67
            # Update stats using the standard system lib
68
            # Grab the stat using the psutil disk_io_counters method
69
            # read_count: number of reads
70
            # write_count: number of writes
71
            # read_bytes: number of bytes read
72
            # write_bytes: number of bytes written
73
            # read_time: time spent reading from disk (in milliseconds)
74
            # write_time: time spent writing to disk (in milliseconds)
75
            try:
76
                diskiocounters = psutil.disk_io_counters(perdisk=True)
77
            except Exception:
0 ignored issues
show
Best Practice introduced by Alessio Sergi
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...
78
                return stats
79
80
            # Previous disk IO stats are stored in the diskio_old variable
81
            if not hasattr(self, 'diskio_old'):
82
                # First call, we init the diskio_old var
83
                try:
84
                    self.diskio_old = diskiocounters
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
The attribute diskio_old was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
85
                except (IOError, UnboundLocalError):
86
                    pass
87
            else:
88
                # By storing time data we enable Rx/s and Tx/s calculations in the
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (82/80).

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

Loading history...
89
                # XML/RPC API, which would otherwise be overly difficult work
90
                # for users of the API
91
                time_since_update = getTimeSinceLastUpdate('disk')
92
93
                diskio_new = diskiocounters
94
                for disk in diskio_new:
95
                    # By default, RamFS is not displayed (issue #714)
96
                    if self.args is not None and not self.args.diskio_show_ramfs and disk.startswith('ram'):
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (108/80).

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

Loading history...
97
                        continue
98
99
                    # Do not take hide disk into account
100
                    if self.is_hide(disk):
101
                        continue
102
103
                    # Compute count and bit rate
104
                    try:
105
                        read_count = (diskio_new[disk].read_count -
106
                                      self.diskio_old[disk].read_count)
107
                        write_count = (diskio_new[disk].write_count -
108
                                       self.diskio_old[disk].write_count)
109
                        read_bytes = (diskio_new[disk].read_bytes -
110
                                      self.diskio_old[disk].read_bytes)
111
                        write_bytes = (diskio_new[disk].write_bytes -
112
                                       self.diskio_old[disk].write_bytes)
113
                        diskstat = {
114
                            'time_since_update': time_since_update,
115
                            'disk_name': n(disk),
116
                            'read_count': read_count,
117
                            'write_count': write_count,
118
                            'read_bytes': read_bytes,
119
                            'write_bytes': write_bytes}
120
                        # Add alias if exist (define in the configuration file)
121
                        if self.has_alias(disk) is not None:
122
                            diskstat['alias'] = self.has_alias(disk)
123
                    except KeyError:
124
                        continue
125
                    else:
126
                        diskstat['key'] = self.get_key()
127
                        stats.append(diskstat)
128
129
                # Save stats to compute next bitrate
130
                self.diskio_old = diskio_new
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
The attribute diskio_old was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
131
        elif self.input_method == 'snmp':
132
            # Update stats using SNMP
133
            # No standard way for the moment...
134
            pass
135
136
        # Update the stats
137
        self.stats = stats
138
139
        return self.stats
140
141
    def update_views(self):
142
        """Update stats views."""
143
        # Call the father's method
144
        super(Plugin, self).update_views()
145
146
        # Add specifics informations
147
        # Alert
148
        for i in self.stats:
149
            disk_real_name = i['disk_name']
150
            self.views[i[self.get_key()]]['read_bytes']['decoration'] = self.get_alert(int(i['read_bytes'] // i['time_since_update']),
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (134/80).

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

Loading history...
151
                                                                                       header=disk_real_name + '_rx')
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
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...
152
            self.views[i[self.get_key()]]['write_bytes']['decoration'] = self.get_alert(int(i['write_bytes'] // i['time_since_update']),
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (136/80).

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

Loading history...
153
                                                                                        header=disk_real_name + '_tx')
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
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...
154
155
    def msg_curse(self, args=None, max_width=None):
156
        """Return the dict to display in the curse interface."""
157
        # Init the return message
158
        ret = []
159
160
        # Only process if stats exist and display plugin enable...
161
        if not self.stats or self.is_disable():
162
            return ret
163
164
        # Max size for the interface name
165
        name_max_width = max_width - 12
166
167
        # Header
168
        msg = '{:{width}}'.format('DISK I/O', width=name_max_width)
169
        ret.append(self.curse_add_line(msg, "TITLE"))
170
        if args.diskio_iops:
171
            msg = '{:>7}'.format('IOR/s')
172
            ret.append(self.curse_add_line(msg))
173
            msg = '{:>7}'.format('IOW/s')
174
            ret.append(self.curse_add_line(msg))
175
        else:
176
            msg = '{:>7}'.format('R/s')
177
            ret.append(self.curse_add_line(msg))
178
            msg = '{:>7}'.format('W/s')
179
            ret.append(self.curse_add_line(msg))
180
        # Disk list (sorted by name)
181
        for i in self.sorted_stats():
182
            # Is there an alias for the disk name ?
183
            disk_real_name = i['disk_name']
184
            disk_name = self.has_alias(i['disk_name'])
185
            if disk_name is None:
186
                disk_name = disk_real_name
187
            # New line
188
            ret.append(self.curse_new_line())
189
            if len(disk_name) > name_max_width:
190
                # Cut disk name if it is too long
191
                disk_name = '_' + disk_name[-name_max_width:]
192
            msg = '{:{width}}'.format(nativestr(disk_name),
193
                                      width=name_max_width)
194
            ret.append(self.curse_add_line(msg))
195
            if args.diskio_iops:
196
                # count
197
                txps = self.auto_unit(
198
                    int(i['read_count'] // i['time_since_update']))
199
                rxps = self.auto_unit(
200
                    int(i['write_count'] // i['time_since_update']))
201
                msg = '{:>7}'.format(txps)
202
                ret.append(self.curse_add_line(msg,
203
                                               self.get_views(item=i[self.get_key()],
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
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...
204
                                                              key='read_count',
205
                                                              option='decoration')))
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (84/80).

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

Loading history...
206
                msg = '{:>7}'.format(rxps)
207
                ret.append(self.curse_add_line(msg,
208
                                               self.get_views(item=i[self.get_key()],
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
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...
209
                                                              key='write_count',
210
                                                              option='decoration')))
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (84/80).

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

Loading history...
211
            else:
212
                # Bitrate
213
                txps = self.auto_unit(
214
                    int(i['read_bytes'] // i['time_since_update']))
215
                rxps = self.auto_unit(
216
                    int(i['write_bytes'] // i['time_since_update']))
217
                msg = '{:>7}'.format(txps)
218
                ret.append(self.curse_add_line(msg,
219
                                               self.get_views(item=i[self.get_key()],
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
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...
220
                                                              key='read_bytes',
221
                                                              option='decoration')))
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (84/80).

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

Loading history...
222
                msg = '{:>7}'.format(rxps)
223
                ret.append(self.curse_add_line(msg,
224
                                               self.get_views(item=i[self.get_key()],
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
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...
225
                                                              key='write_bytes',
226
                                                              option='decoration')))
0 ignored issues
show
Coding Style introduced by Nicolas Hennion
This line is too long as per the coding-style (84/80).

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

Loading history...
227
228
        return ret
229