Test Failed
Push — develop ( e38e2c...fbd0ee )
by Nicolas
01:03 queued 18s
created

PluginModel.summarize_all_cpus_not_displayed()   A

Complexity

Conditions 5

Size

Total Lines 16
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 14
nop 4
dl 0
loc 16
rs 9.2333
c 0
b 0
f 0
1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
9
"""Per-CPU plugin."""
10
11
from glances.cpu_percent import cpu_percent
12
from glances.globals import BSD, LINUX, MACOS, WINDOWS
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
    'dpc': {
81
        'description': '*(Windows)*: percent of time spent handling deferred procedure calls.',
82
        'unit': 'percent',
83
    },
84
    'interrupt': {
85
        'description': '*(Windows)*: percent of time spent handling software interrupts.',
86
        'unit': 'percent',
87
    },
88
}
89
90
# Define the history items list
91
items_history_list = [
92
    {'name': 'user', 'description': 'User CPU usage', 'y_unit': '%'},
93
    {'name': 'system', 'description': 'System CPU usage', 'y_unit': '%'},
94
]
95
96
97
class PluginModel(GlancesPluginModel):
98
    """Glances per-CPU plugin.
99
100
    'stats' is a list of dictionaries that contain the utilization percentages
101
    for each CPU.
102
    """
103
104
    def __init__(self, args=None, config=None):
105
        """Init the plugin."""
106
        super().__init__(
107
            args=args,
108
            config=config,
109
            items_history_list=items_history_list,
110
            stats_init_value=[],
111
            fields_description=fields_description,
112
        )
113
114
        # We want to display the stat in the curse interface
115
        self.display_curse = True
116
117
        # Manage the maximum number of CPU to display (related to enhancement request #2734)
118
        if config:
119
            self.max_cpu_display = config.get_int_value('percpu', 'max_cpu_display', 4)
120
        else:
121
            self.max_cpu_display = 4
122
123
    def get_key(self):
124
        """Return the key of the list."""
125
        return 'cpu_number'
126
127
    @GlancesPluginModel._check_decorator
128
    @GlancesPluginModel._log_result_decorator
129
    def update(self):
130
        """Update per-CPU stats using the input method."""
131
        # Grab per-CPU stats using psutil's
132
        if self.input_method == 'local':
133
            stats = cpu_percent.get_percpu()
134
        else:
135
            # Update stats using SNMP
136
            stats = self.get_init_value()
137
138
        # Update the stats
139
        self.stats = stats
140
141
        return self.stats
142
143
    def define_headers_from_os(self):
144
        base = ['user', 'system']
145
146
        if LINUX:
147
            extension = ['iowait', 'idle', 'irq', 'nice', 'steal', 'guest']
148
        elif MACOS:
149
            extension = ['idle', 'nice']
150
        elif BSD:
151
            extension = ['idle', 'irq', 'nice']
152
        elif WINDOWS:
153
            extension = ['dpc', 'interrupt']
154
155
        return base + extension
0 ignored issues
show
introduced by
The variable extension does not seem to be defined for all execution paths.
Loading history...
156
157
    def maybe_build_string_msg(self, header, return_):
158
        if self.is_disabled('quicklook'):
159
            msg = '{:5}'.format('CPU')
160
            return_.append(self.curse_add_line(msg, "TITLE"))
161
            header.insert(0, 'total')
162
163
        return (header, return_)
164
165
    def display_cpu_stats_per_line(self, header, return_):
166
        for stat in header:
167
            msg = f'{stat:>7}'
168
            return_.append(self.curse_add_line(msg))
169
170
        return return_
171
172
    def manage_max_cpu_to_display(self):
173
        if len(self.stats) > self.max_cpu_display:
174
            # sort and display top 'n'
175
            percpu_list = sorted(self.stats, key=lambda x: x['total'], reverse=True)
176
        else:
177
            percpu_list = self.stats
178
179
        return percpu_list
180
181
    def display_cpu_header_in_columns(self, cpu, return_):
182
        return_.append(self.curse_new_line())
183
        if self.is_disabled('quicklook'):
184
            try:
185
                cpu_id = cpu[cpu['key']]
186
                if cpu_id < 10:
187
                    msg = f'CPU{cpu_id:1} '
188
                else:
189
                    msg = f'{cpu_id:4} '
190
            except TypeError:
191
                # TypeError: string indices must be integers (issue #1027)
192
                msg = '{:4} '.format('?')
193
            return_.append(self.curse_add_line(msg))
194
195
        return return_
196
197
    def display_cpu_stats_in_columns(self, cpu, header, return_):
198
        for stat in header:
199
            try:
200
                msg = f'{cpu[stat]:6.1f}%'
201
            except TypeError:
202
                msg = '{:>6}%'.format('?')
203
            return_.append(self.curse_add_line(msg, self.get_alert(cpu[stat], header=stat)))
204
205
        return return_
206
207
    def summarize_all_cpus_not_displayed(self, percpu_list, header, return_):
208
        if len(self.stats) > self.max_cpu_display:
209
            return_.append(self.curse_new_line())
210
            if self.is_disabled('quicklook'):
211
                return_.append(self.curse_add_line('CPU* '))
212
213
            for stat in header:
214
                percpu_stats = [i[stat] for i in percpu_list[0 : self.max_cpu_display]]
215
                cpu_stat = sum(percpu_stats) / len(percpu_stats)
216
                try:
217
                    msg = f'{cpu_stat:6.1f}%'
218
                except TypeError:
219
                    msg = '{:>6}%'.format('?')
220
                return_.append(self.curse_add_line(msg, self.get_alert(cpu_stat, header=stat)))
221
222
        return return_
223
224
    def msg_curse(self, args=None, max_width=None):
225
        """Return the dict to display in the curse interface."""
226
227
        # Init the return message
228
        return_ = []
229
230
        # Only process if stats exist...
231
        missing = [not self.stats, not self.args.percpu, self.is_disabled()]
232
        if any(missing):
233
            return return_
234
235
        # Define the headers based on OS
236
        header = self.define_headers_from_os()
237
238
        # Build the string message
239
        header, return_ = self.maybe_build_string_msg(header, return_)
240
241
        # Per CPU stats displayed per line
242
        return_ = self.display_cpu_stats_per_line(header, return_)
243
244
        # Manage the maximum number of CPU to display (related to enhancement request #2734)
245
        percpu_list = self.manage_max_cpu_to_display()
246
247
        # Per CPU stats displayed per column
248
        for cpu in percpu_list[0 : self.max_cpu_display]:
249
            header_added = self.display_cpu_header_in_columns(cpu, return_)
250
            stats_added = self.display_cpu_stats_in_columns(cpu, header, header_added)
251
252
        # Add a new line with sum of all others CPU
253
        return_ = self.summarize_all_cpus_not_displayed(percpu_list, header, stats_added)
0 ignored issues
show
introduced by
The variable stats_added does not seem to be defined in case the for loop on line 248 is not entered. Are you sure this can never be the case?
Loading history...
254
255
        return return_
256