Test Failed
Push — master ( ee826a...d9056e )
by Nicolas
03:09
created

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

Complexity

Conditions 21

Size

Total Lines 73
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 51
nop 3
dl 0
loc 73
rs 0
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.percpu.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
"""Per-CPU plugin."""
11
12
from glances.cpu_percent import cpu_percent
13
from glances.plugins.plugin.model import GlancesPluginModel
14
15
# Fields description
16
# description: human readable description
17
# short_name: shortname to use un UI
18
# unit: unit type
19
# rate: is it a rate ? If yes, // by time_since_update when displayed,
20
# min_symbol: Auto unit should be used if value > than 1 'X' (K, M, G)...
21
fields_description = {
22
    'cpu_number': {
23
        'description': 'CPU number',
24
    },
25
    'total': {
26
        'description': 'Sum of CPU percentages (except idle) for current CPU number.',
27
        'unit': 'percent',
28
    },
29
    'system': {
30
        'description': 'Percent time spent in kernel space. System CPU time is the \
31
time spent running code in the Operating System kernel.',
32
        'unit': 'percent',
33
    },
34
    'user': {
35
        'description': 'CPU percent time spent in user space. \
36
User CPU time is the time spent on the processor running your program\'s code (or code in libraries).',
37
        'unit': 'percent',
38
    },
39
    'iowait': {
40
        'description': '*(Linux)*: percent time spent by the CPU waiting for I/O \
41
operations to complete.',
42
        'unit': 'percent',
43
    },
44
    'idle': {
45
        'description': 'percent of CPU used by any program. Every program or task \
46
that runs on a computer system occupies a certain amount of processing \
47
time on the CPU. If the CPU has completed all tasks it is idle.',
48
        'unit': 'percent',
49
    },
50
    'irq': {
51
        'description': '*(Linux and BSD)*: percent time spent servicing/handling \
52
hardware/software interrupts. Time servicing interrupts (hardware + \
53
software).',
54
        'unit': 'percent',
55
    },
56
    'nice': {
57
        'description': '*(Unix)*: percent time occupied by user level processes with \
58
a positive nice value. The time the CPU has spent running users\' \
59
processes that have been *niced*.',
60
        'unit': 'percent',
61
    },
62
    'steal': {
63
        'description': '*(Linux)*: percentage of time a virtual CPU waits for a real \
64
CPU while the hypervisor is servicing another virtual processor.',
65
        'unit': 'percent',
66
    },
67
    'guest': {
68
        'description': '*(Linux)*: percent of time spent running a virtual CPU for \
69
guest operating systems under the control of the Linux kernel.',
70
        'unit': 'percent',
71
    },
72
    'guest_nice': {
73
        'description': '*(Linux)*: percent of time spent running a niced guest (virtual CPU).',
74
        'unit': 'percent',
75
    },
76
    'softirq': {
77
        'description': '*(Linux)*: percent of time spent handling software interrupts.',
78
        'unit': 'percent',
79
    },
80
}
81
82
83
# Define the history items list
84
items_history_list = [
85
    {'name': 'user', 'description': 'User CPU usage', 'y_unit': '%'},
86
    {'name': 'system', 'description': 'System CPU usage', 'y_unit': '%'},
87
]
88
89
90
class PluginModel(GlancesPluginModel):
91
    """Glances per-CPU plugin.
92
93
    'stats' is a list of dictionaries that contain the utilization percentages
94
    for each CPU.
95
    """
96
97
    def __init__(self, args=None, config=None):
98
        """Init the plugin."""
99
        super(PluginModel, self).__init__(
100
            args=args,
101
            config=config,
102
            items_history_list=items_history_list,
103
            stats_init_value=[],
104
            fields_description=fields_description,
105
        )
106
107
        # We want to display the stat in the curse interface
108
        self.display_curse = True
109
110
        # Manage the maximum number of CPU to display (related to enhancement request #2734)
111
        if config:
112
            self.max_cpu_display = config.get_int_value('percpu', 'max_cpu_display', 4)
113
        else:
114
            self.max_cpu_display = 4
115
116
    def get_key(self):
117
        """Return the key of the list."""
118
        return 'cpu_number'
119
120
    @GlancesPluginModel._check_decorator
121
    @GlancesPluginModel._log_result_decorator
122
    def update(self):
123
        """Update per-CPU stats using the input method."""
124
        # Init new stats
125
        stats = self.get_init_value()
126
127
        # Grab per-CPU stats using psutil's cpu_percent(percpu=True) and
128
        # cpu_times_percent(percpu=True) methods
129
        if self.input_method == 'local':
130
            stats = cpu_percent.get(percpu=True)
131
        else:
132
            # Update stats using SNMP
133
            pass
134
135
        # Update the stats
136
        self.stats = stats
137
138
        return self.stats
139
140
    def msg_curse(self, args=None, max_width=None):
141
        """Return the dict to display in the curse interface."""
142
        # Init the return message
143
        ret = []
144
145
        # Only process if stats exist...
146
        if not self.stats or not self.args.percpu or self.is_disabled():
147
            return ret
148
149
        # Define the default header
150
        header = ['user', 'system', 'idle', 'iowait', 'steal']
151
152
        # Build the string message
153
        if self.is_disabled('quicklook'):
154
            msg = '{:5}'.format('CPU')
155
            ret.append(self.curse_add_line(msg, "TITLE"))
156
            header.insert(0, 'total')
157
158
        # Per CPU stats displayed per line
159
        for stat in header:
160
            if stat not in self.stats[0]:
161
                continue
162
            msg = '{:>7}'.format(stat)
163
            ret.append(self.curse_add_line(msg))
164
165
        # Manage the maximum number of CPU to display (related to enhancement request #2734)
166
        if len(self.stats) > self.max_cpu_display:
167
            # If the number of CPU is > max_cpu_display then sort and display top 'n'
168
            percpu_list = sorted(self.stats, key=lambda x: x['total'], reverse=True)
169
        else:
170
            percpu_list = self.stats
171
172
        # Per CPU stats displayed per column
173
        for cpu in percpu_list[0 : self.max_cpu_display]:
174
            ret.append(self.curse_new_line())
175
            if self.is_disabled('quicklook'):
176
                try:
177
                    cpu_id = cpu[cpu['key']]
178
                    if cpu_id < 10:
179
                        msg = 'CPU{:1} '.format(cpu_id)
180
                    else:
181
                        msg = '{:4} '.format(cpu_id)
182
                except TypeError:
183
                    # TypeError: string indices must be integers (issue #1027)
184
                    msg = '{:4} '.format('?')
185
                ret.append(self.curse_add_line(msg))
186
            for stat in header:
187
                if stat not in self.stats[0]:
188
                    continue
189
                try:
190
                    msg = '{:6.1f}%'.format(cpu[stat])
191
                except TypeError:
192
                    msg = '{:>6}%'.format('?')
193
                ret.append(self.curse_add_line(msg, self.get_alert(cpu[stat], header=stat)))
194
195
        # Add a new line with sum of all others CPU
196
        if len(self.stats) > self.max_cpu_display:
197
            ret.append(self.curse_new_line())
198
            if self.is_disabled('quicklook'):
199
                ret.append(self.curse_add_line('CPU* '))
200
            for stat in header:
201
                if stat not in self.stats[0]:
202
                    continue
203
                cpu_stat = sum([i[stat] for i in percpu_list[0 : self.max_cpu_display]]) / len(
204
                    [i[stat] for i in percpu_list[0 : self.max_cpu_display]]
205
                )
206
                try:
207
                    msg = '{:6.1f}%'.format(cpu_stat)
208
                except TypeError:
209
                    msg = '{:>6}%'.format('?')
210
                ret.append(self.curse_add_line(msg, self.get_alert(cpu_stat, header=stat)))
211
212
        return ret
213