Test Failed
Push — develop ( 9b853d...46abd4 )
by
unknown
04:14 queued 02:01
created

glances.cpu_percent.CpuPercent.get_percpu()   A

Complexity

Conditions 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 1
dl 0
loc 9
rs 10
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
"""CPU percent stats shared between CPU and Quicklook plugins."""
10
11
from typing import List, Optional, TypedDict
12
13
import psutil
14
15
from glances.logger import logger
16
from glances.timer import Timer
17
18
__all__ = ["cpu_percent"]
19
20
21
class CpuInfo(TypedDict):
22
    cpu_name: str
23
    cpu_hz: Optional[float]
24
    cpu_hz_current: Optional[float]
25
26
27
class PerCpuPercentInfo(TypedDict):
28
    key: str
29
    cpu_number: int
30
    total: float
31
    user: float
32
    system: float
33
    idle: float
34
    nice: Optional[float]
35
    iowait: Optional[float]
36
    irq: Optional[float]
37
    softirq: Optional[float]
38
    steal: Optional[float]
39
    guest: Optional[float]
40
    guest_nice: Optional[float]
41
42
43
class CpuPercent:
44
    """Get and store the CPU percent."""
45
46
    def __init__(self, cached_timer_cpu: int = 2):
47
        # cached_timer_cpu is the minimum time interval between stats updates
48
        # since last update is passed (will retrieve old cached info instead)
49
        self.cached_timer_cpu = cached_timer_cpu
50
        # psutil.cpu_freq() consumes lots of CPU
51
        # So refresh CPU frequency stats every refresh * 2
52
        self.cached_timer_cpu_info = cached_timer_cpu * 2
53
54
        # Get CPU name
55
        self.timer_cpu_info = Timer(0)
56
        self.cpu_info: CpuInfo = {'cpu_name': self.__get_cpu_name(), 'cpu_hz_current': None, 'cpu_hz': None}
57
58
        # Warning from PsUtil documentation
59
        # The first time this function is called with interval = 0.0 or None
60
        # it will return a meaningless 0.0 value which you are supposed to ignore.
61
        self.timer_cpu = Timer(0)
62
        self.cpu_percent = self._compute_cpu()
63
        self.timer_percpu = Timer(0)
64
        self.percpu_percent = self._compute_percpu()
65
66
    def get_key(self):
67
        """Return the key of the per CPU list."""
68
        return 'cpu_number'
69
70
    def get_info(self) -> CpuInfo:
71
        """Get additional information about the CPU"""
72
        # Never update more than 1 time per cached_timer_cpu_info
73
        if self.timer_cpu_info.finished() and hasattr(psutil, 'cpu_freq'):
74
            # Get the CPU freq current/max
75
            try:
76
                cpu_freq = psutil.cpu_freq()
77
            except Exception as e:
78
                logger.debug(f'Can not grab CPU information ({e})')
79
            else:
80
                if hasattr(cpu_freq, 'current'):
81
                    self.cpu_info['cpu_hz_current'] = cpu_freq.current
82
                else:
83
                    self.cpu_info['cpu_hz_current'] = None
84
                if hasattr(cpu_freq, 'max'):
85
                    self.cpu_info['cpu_hz'] = cpu_freq.max
86
                else:
87
                    self.cpu_info['cpu_hz'] = None
88
                # Reset timer for cache
89
                self.timer_cpu_info.reset(duration=self.cached_timer_cpu_info)
90
        return self.cpu_info
91
92
    @staticmethod
93
    def __get_cpu_name() -> str:
94
        # Get the CPU name once from the /proc/cpuinfo file
95
        # Read the first line with the "model name" ("Model" for Raspberry Pi)
96
        try:
97
            cpuinfo_lines = open('/proc/cpuinfo').readlines()
98
        except (FileNotFoundError, PermissionError):
99
            logger.debug("No permission to read '/proc/cpuinfo'")
100
            return 'CPU'
101
102
        for line in cpuinfo_lines:
103
            if line.startswith('model name') or line.startswith('Model') or line.startswith('cpu model'):
104
                return line.split(':')[1].strip()
105
106
        return 'CPU'
107
108
    def get_cpu(self) -> float:
109
        """Update and/or return the CPU using the psutil library."""
110
        # Never update more than 1 time per cached_timer_cpu
111
        if self.timer_cpu.finished():
112
            # Reset timer for cache
113
            self.timer_cpu.reset(duration=self.cached_timer_cpu)
114
            # Update the stats
115
            self.cpu_percent = self._compute_cpu()
116
        return self.cpu_percent
117
118
    @staticmethod
119
    def _compute_cpu() -> float:
120
        return psutil.cpu_percent(interval=0.0)
121
122
    def get_percpu(self) -> List[PerCpuPercentInfo]:
123
        """Update and/or return the per CPU list using the psutil library."""
124
        # Never update more than 1 time per cached_timer_cpu
125
        if self.timer_percpu.finished():
126
            # Reset timer for cache
127
            self.timer_percpu.reset(duration=self.cached_timer_cpu)
128
            # Update stats
129
            self.percpu_percent = self._compute_percpu()
130
        return self.percpu_percent
131
132
    def _compute_percpu(self) -> List[PerCpuPercentInfo]:
133
        psutil_percpu = enumerate(psutil.cpu_times_percent(interval=0.0, percpu=True))
134
        return [
135
            {
136
                'key': self.get_key(),
137
                'cpu_number': cpu_number,
138
                'total': round(100 - cpu_times.idle, 1),
139
                'user': cpu_times.user,
140
                'system': cpu_times.system,
141
                'idle': cpu_times.idle,
142
                'nice': cpu_times.nice if hasattr(cpu_times, 'nice') else None,
143
                'iowait': cpu_times.iowait if hasattr(cpu_times, 'iowait') else None,
144
                'irq': cpu_times.irq if hasattr(cpu_times, 'irq') else None,
145
                'softirq': cpu_times.softirq if hasattr(cpu_times, 'softirq') else None,
146
                'steal': cpu_times.steal if hasattr(cpu_times, 'steal') else None,
147
                'guest': cpu_times.guest if hasattr(cpu_times, 'guest') else None,
148
                'guest_nice': cpu_times.steal if hasattr(cpu_times, 'guest_nice') else None,
149
            }
150
            for cpu_number, cpu_times in psutil_percpu
151
        ]
152
153
154
# CpuPercent instance shared between plugins
155
cpu_percent = CpuPercent()
156