Test Failed
Push — master ( f8aa98...cc9054 )
by Nicolas
02:25 queued 13s
created

glances.cpu_percent.CpuPercent._compute_cpu()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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