summaryrefslogtreecommitdiffstats
path: root/glances/plugins/percpu/__init__.py
blob: d16ffb4558ed5ab64f77b858818fa06b5db12e49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#

"""Per-CPU plugin."""

from glances.logger import logger
from glances.cpu_percent import cpu_percent
from glances.plugins.plugin.model import GlancesPluginModel

# Fields description
# description: human readable description
# short_name: shortname to use un UI
# unit: unit type
# rate: is it a rate ? If yes, // by time_since_update when displayed,
# min_symbol: Auto unit should be used if value > than 1 'X' (K, M, G)...
fields_description = {
    'cpu_number': {
        'description': 'CPU number',
    },
    'total': {
        'description': 'Sum of CPU percentages (except idle) for current CPU number.',
        'unit': 'percent',
    },
    'system': {
        'description': 'Percent time spent in kernel space. System CPU time is the \
time spent running code in the Operating System kernel.',
        'unit': 'percent',
    },
    'user': {
        'description': 'CPU percent time spent in user space. \
User CPU time is the time spent on the processor running your program\'s code (or code in libraries).',
        'unit': 'percent',
    },
    'iowait': {
        'description': '*(Linux)*: percent time spent by the CPU waiting for I/O \
operations to complete.',
        'unit': 'percent',
    },
    'idle': {
        'description': 'percent of CPU used by any program. Every program or task \
that runs on a computer system occupies a certain amount of processing \
time on the CPU. If the CPU has completed all tasks it is idle.',
        'unit': 'percent',
    },
    'irq': {
        'description': '*(Linux and BSD)*: percent time spent servicing/handling \
hardware/software interrupts. Time servicing interrupts (hardware + \
software).',
        'unit': 'percent',
    },
    'nice': {
        'description': '*(Unix)*: percent time occupied by user level processes with \
a positive nice value. The time the CPU has spent running users\' \
processes that have been *niced*.',
        'unit': 'percent',
    },
    'steal': {
        'description': '*(Linux)*: percentage of time a virtual CPU waits for a real \
CPU while the hypervisor is servicing another virtual processor.',
        'unit': 'percent',
    },
    'guest': {
        'description': '*(Linux)*: percent of time spent running a virtual CPU for \
guest operating systems under the control of the Linux kernel.',
        'unit': 'percent',
    },
    'guest_nice': {
        'description': '*(Linux)*: percent of time spent running a niced guest (virtual CPU).',
        'unit': 'percent',
    },
    'softirq': {
        'description': '*(Linux)*: percent of time spent handling software interrupts.',
        'unit': 'percent',
    },
}


# Define the history items list
items_history_list = [
    {'name': 'user', 'description': 'User CPU usage', 'y_unit': '%'},
    {'name': 'system', 'description': 'System CPU usage', 'y_unit': '%'},
]


class PluginModel(GlancesPluginModel):
    """Glances per-CPU plugin.

    'stats' is a list of dictionaries that contain the utilization percentages
    for each CPU.
    """

    def __init__(self, args=None, config=None):
        """Init the plugin."""
        super(PluginModel, self).__init__(
            args=args, config=config,
            items_history_list=items_history_list,
            stats_init_value=[],
            fields_description=fields_description
        )

        # We want to display the stat in the curse interface
        self.display_curse = True

        # Manage the maximum number of CPU to display (related to enhancement request #2734)
        self.max_cpu_display = config.get_int_value('percpu', 'max_cpu_display', 4)

    def get_key(self):
        """Return the key of the list."""
        return 'cpu_number'

    @GlancesPluginModel._check_decorator
    @GlancesPluginModel._log_result_decorator
    def update(self):
        """Update per-CPU stats using the input method."""
        # Init new stats
        stats = self.get_init_value()

        # Grab per-CPU stats using psutil's cpu_percent(percpu=True) and
        # cpu_times_percent(percpu=True) methods
        if self.input_method == 'local':
            stats = cpu_percent.get(percpu=True)
        else:
            # Update stats using SNMP
            pass

        # Update the stats
        self.stats = stats

        return self.stats

    def msg_curse(self, args=None, max_width=None):
        """Return the dict to display in the curse interface."""
        # Init the return message
        ret = []

        # Only process if stats exist...
        if not self.stats or not self.args.percpu or self.is_disabled():
            return ret

        # Define the default header
        header = ['user', 'system', 'idle', 'iowait', 'steal']

        # Build the string message
        if self.is_disabled('quicklook'):
            msg = '{:5}'.format('CPU')
            ret.append(self.curse_add_line(msg, "TITLE"))
            header.insert(0, 'total')

        # Per CPU stats displayed per line
        for stat in header:
            if stat not in self.stats[0]:
                continue
            msg = '{:>7}'.format(stat)
            ret.append(self.curse_add_line(msg))

        # Manage the maximum number of CPU to display (related to enhancement request #2734)
        if len(self.stats) > self.max_cpu_display:
            # If the number of CPU is > max_cpu_display then sort and display top 'n'
            percpu_list = sorted(self.stats, key=lambda x: x['total'], reverse=True)
        else:
            percpu_list = self.stats

        # Per CPU stats displayed per column
        for cpu in percpu_list[0: self.max_cpu_display]:
            ret.append(self.curse_new_line())
            if self.is_disabled('quicklook'):
                try:
                    cpu_id = cpu[cpu['key']]
                    if cpu_id < 10:
                        msg = 'CPU{:1} '.format(cpu_id)
                    else:
                        msg = '{:4} '.format(cpu_id)
                except TypeError:
                    # TypeError: string indices must be integers (issue #1027)
                    msg = '{:4} '.format('?')
                ret.append(self.curse_add_line(msg))
            for stat in header:
                if stat not in self.stats[0]:
                    continue
                try:
                    msg = '{:6.1f}%'.format(cpu[stat])
                except TypeError:
                    msg = '{:>6}%'.format('?')
                ret.append(self.curse_add_line(msg, self.get_alert(cpu[stat], header=stat)))

        # Add a new line with sum of all others CPU
        if len(self.stats) > self.max_cpu_display:
            ret.append(self.curse_new_line())
            if self.is_disabled('quicklook'):
                ret.append(self.curse_add_line('CPU* '))
            for stat in header:
                if stat not in self.stats[0]:
                    continue
                cpu_stat = sum([i[stat] for i in percpu_list[0: self.max_cpu_display]]) / len(
                    [i[stat] for i in percpu_list[0: self.max_cpu_display]]
                )
                try:
                    msg = '{:6.1f}%'.format(cpu_stat)
                except TypeError:
                    msg = '{:>6}%'.format('?')
                ret.append(self.curse_add_line(msg, self.get_alert(cpu_stat, header=stat)))

        return ret