summaryrefslogtreecommitdiffstats
path: root/glances/plugins/cpu/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'glances/plugins/cpu/__init__.py')
-rw-r--r--glances/plugins/cpu/__init__.py392
1 files changed, 392 insertions, 0 deletions
diff --git a/glances/plugins/cpu/__init__.py b/glances/plugins/cpu/__init__.py
index e69de29b..222db72f 100644
--- a/glances/plugins/cpu/__init__.py
+++ b/glances/plugins/cpu/__init__.py
@@ -0,0 +1,392 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of Glances.
+#
+# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
+#
+# SPDX-License-Identifier: LGPL-3.0-only
+#
+
+"""CPU plugin."""
+
+from glances.timer import getTimeSinceLastUpdate
+from glances.globals import LINUX, WINDOWS, SUNOS, iterkeys
+from glances.cpu_percent import cpu_percent
+from glances.plugins.core import PluginModel as CorePluginModel
+from glances.plugins.plugin.model import GlancesPluginModel
+
+import psutil
+
+# 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 = {
+ 'total': {'description': 'Sum of all CPU percentages (except idle).', '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',
+ },
+ 'dpc': {
+ 'description': '*(Windows)*: time spent servicing deferred procedure calls (DPCs)',
+ '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',
+ },
+ 'ctx_switches': {
+ 'description': 'number of context switches (voluntary + involuntary) per \
+second. A context switch is a procedure that a computer\'s CPU (central \
+processing unit) follows to change from one task (or process) to \
+another while ensuring that the tasks do not conflict.',
+ 'unit': 'number',
+ 'rate': True,
+ 'min_symbol': 'K',
+ 'short_name': 'ctx_sw',
+ },
+ 'interrupts': {
+ 'description': 'number of interrupts per second.',
+ 'unit': 'number',
+ 'rate': True,
+ 'min_symbol': 'K',
+ 'short_name': 'inter',
+ },
+ 'soft_interrupts': {
+ 'description': 'number of software interrupts per second. Always set to \
+0 on Windows and SunOS.',
+ 'unit': 'number',
+ 'rate': True,
+ 'min_symbol': 'K',
+ 'short_name': 'sw_int',
+ },
+ 'syscalls': {
+ 'description': 'number of system calls per second. Always 0 on Linux OS.',
+ 'unit': 'number',
+ 'rate': True,
+ 'min_symbol': 'K',
+ 'short_name': 'sys_call',
+ },
+ 'cpucore': {'description': 'Total number of CPU core.', 'unit': 'number'},
+ 'time_since_update': {'description': 'Number of seconds since last update.', 'unit': 'seconds'},
+}
+
+# SNMP OID
+# percentage of user CPU time: .1.3.6.1.4.1.2021.11.9.0
+# percentages of system CPU time: .1.3.6.1.4.1.2021.11.10.0
+# percentages of idle CPU time: .1.3.6.1.4.1.2021.11.11.0
+snmp_oid = {
+ 'default': {
+ 'user': '1.3.6.1.4.1.2021.11.9.0',
+ 'system': '1.3.6.1.4.1.2021.11.10.0',
+ 'idle': '1.3.6.1.4.1.2021.11.11.0',
+ },
+ 'windows': {'percent': '1.3.6.1.2.1.25.3.3.1.2'},
+ 'esxi': {'percent': '1.3.6.1.2.1.25.3.3.1.2'},
+ 'netapp': {
+ 'system': '1.3.6.1.4.1.789.1.2.1.3.0',
+ 'idle': '1.3.6.1.4.1.789.1.2.1.5.0',
+ 'cpucore': '1.3.6.1.4.1.789.1.2.1.6.0',
+ },
+}
+
+# Define the history items list
+# - 'name' define the stat identifier
+# - 'y_unit' define the Y label
+items_history_list = [
+ {'name': 'user', 'description': 'User CPU usage', 'y_unit': '%'},
+ {'name': 'system', 'description': 'System CPU usage', 'y_unit': '%'},
+]
+
+
+class PluginModel(GlancesPluginModel):
+ """Glances CPU plugin.
+
+ 'stats' is a dictionary that contains the system-wide CPU utilization as a
+ percentage.
+ """
+
+ def __init__(self, args=None, config=None):
+ """Init the CPU plugin."""
+ super(PluginModel, self).__init__(
+ args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
+ )
+
+ # We want to display the stat in the curse interface
+ self.display_curse = True
+
+ # Call CorePluginModel in order to display the core number
+ try:
+ self.nb_log_core = CorePluginModel(args=self.args).update()["log"]
+ except Exception:
+ self.nb_log_core = 1
+
+ @GlancesPluginModel._check_decorator
+ @GlancesPluginModel._log_result_decorator
+ def update(self):
+ """Update CPU stats using the input method."""
+ # Grab stats into self.stats
+ if self.input_method == 'local':
+ stats = self.update_local()
+ elif self.input_method == 'snmp':
+ stats = self.update_snmp()
+ else:
+ stats = self.get_init_value()
+
+ # Update the stats
+ self.stats = stats
+
+ return self.stats
+
+ def update_local(self):
+ """Update CPU stats using psutil."""
+ # Grab CPU stats using psutil's cpu_percent and cpu_times_percent
+ # Get all possible values for CPU stats: user, system, idle,
+ # nice (UNIX), iowait (Linux), irq (Linux, FreeBSD), steal (Linux 2.6.11+)
+ # The following stats are returned by the API but not displayed in the UI:
+ # softirq (Linux), guest (Linux 2.6.24+), guest_nice (Linux 3.2.0+)
+
+ # Init new stats
+ stats = self.get_init_value()
+
+ stats['total'] = cpu_percent.get()
+ # Standards stats
+ # - user: time spent by normal processes executing in user mode; on Linux this also includes guest time
+ # - system: time spent by processes executing in kernel mode
+ # - idle: time spent doing nothing
+ # - nice (UNIX): time spent by niced (prioritized) processes executing in user mode
+ # on Linux this also includes guest_nice time
+ # - iowait (Linux): time spent waiting for I/O to complete.
+ # This is not accounted in idle time counter.
+ # - irq (Linux, BSD): time spent for servicing hardware interrupts
+ # - softirq (Linux): time spent for servicing software interrupts
+ # - steal (Linux 2.6.11+): time spent by other operating systems running in a virtualized environment
+ # - guest (Linux 2.6.24+): time spent running a virtual CPU for guest operating systems under
+ # the control of the Linux kernel
+ # - guest_nice (Linux 3.2.0+): time spent running a niced guest (virtual CPU for guest operating systems
+ # under the control of the Linux kernel)
+ # - interrupt (Windows): time spent for servicing hardware interrupts ( similar to “irq” on UNIX)
+ # - dpc (Windows): time spent servicing deferred procedure calls (DPCs)
+ cpu_times_percent = psutil.cpu_times_percent(interval=0.0)
+ for stat in cpu_times_percent._fields:
+ stats[stat] = getattr(cpu_times_percent, stat)
+
+ # Additional CPU stats (number of events not as a %; psutil>=4.1.0)
+ # - ctx_switches: number of context switches (voluntary + involuntary) since boot.
+ # - interrupts: number of interrupts since boot.
+ # - soft_interrupts: number of software interrupts since boot. Always set to 0 on Windows and SunOS.
+ # - syscalls: number of system calls since boot. Always set to 0 on Linux.
+ cpu_stats = psutil.cpu_stats()
+
+ # By storing time data we enable Rx/s and Tx/s calculations in the
+ # XML/RPC API, which would otherwise be overly difficult work
+ # for users of the API
+ stats['time_since_update'] = getTimeSinceLastUpdate('cpu')
+
+ # Core number is needed to compute the CTX switch limit
+ stats['cpucore'] = self.nb_log_core
+
+ # Previous CPU stats are stored in the cpu_stats_old variable
+ if not hasattr(self, 'cpu_stats_old'):
+ # Init the stats (needed to have the key name for export)
+ for stat in cpu_stats._fields:
+ # @TODO: better to set it to None but should refactor views and UI...
+ stats[stat] = 0
+ else:
+ # Others calls...
+ for stat in cpu_stats._fields:
+ if getattr(cpu_stats, stat) is not None:
+ stats[stat] = getattr(cpu_stats, stat) - getattr(self.cpu_stats_old, stat)
+
+ # Save stats to compute next step
+ self.cpu_stats_old = cpu_stats
+
+ return stats
+
+ def update_snmp(self):
+ """Update CPU stats using SNMP."""
+
+ # Init new stats
+ stats = self.get_init_value()
+
+ # Update stats using SNMP
+ if self.short_system_name in ('windows', 'esxi'):
+ # Windows or VMWare ESXi
+ # You can find the CPU utilization of windows system by querying the oid
+ # Give also the number of core (number of element in the table)
+ try:
+ cpu_stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name], bulk=True)
+ except KeyError:
+ self.reset()
+
+ # Iter through CPU and compute the idle CPU stats
+ stats['nb_log_core'] = 0
+ stats['idle'] = 0
+ for c in cpu_stats:
+ if c.startswith('percent'):
+ stats['idle'] += float(cpu_stats['percent.3'])
+ stats['nb_log_core'] += 1
+ if stats['nb_log_core'] > 0:
+ stats['idle'] = stats['idle'] / stats['nb_log_core']
+ stats['idle'] = 100 - stats['idle']
+ stats['total'] = 100 - stats['idle']
+
+ else:
+ # Default behavior
+ try:
+ stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name])
+ except KeyError:
+ stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
+
+ if stats['idle'] == '':
+ self.reset()
+ return self.stats
+
+ # Convert SNMP stats to float
+ for key in iterkeys(stats):
+ stats[key] = float(stats[key])
+ stats['total'] = 100 - stats['idle']
+
+ return stats
+
+ def update_views(self):
+ """Update stats views."""
+ # Call the father's method
+ super(PluginModel, self).update_views()
+
+ # Add specifics information
+ # Alert and log
+ for key in ['user', 'system', 'iowait', 'dpc', 'total']:
+ if key in self.stats:
+ self.views[key]['decoration'] = self.get_alert_log(self.stats[key], header=key)
+ # Alert only
+ for key in ['steal']:
+ if key in self.stats:
+ self.views[key]['decoration'] = self.get_alert(self.stats[key], header=key)
+ # Alert only but depend on Core number
+ for key in ['ctx_switches']:
+ if key in self.stats:
+ self.views[key]['decoration'] = self.get_alert(
+ self.stats[key], maximum=100 * self.stats['cpucore'], header=key
+ )
+ # Optional
+ for key in ['nice', 'irq', 'idle', 'steal', 'ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls']:
+ if key in self.stats:
+ self.views[key]['optional'] = True
+
+ def msg_curse(self, args=None, max_width=None):
+ """Return the list to display in the UI."""
+ # Init the return message
+ ret = []
+
+ # Only process if stats exist and plugin not disable
+ if not self.stats or self.args.percpu or self.is_disabled():
+ return ret
+
+ # Some tag to enable/disable stats (example: idle_tag triggered on Windows OS)
+ idle_tag = 'user' not in self.stats
+
+ # First line
+ # Total + (idle) + ctx_sw
+ msg = '{}'.format('CPU')
+ ret.append(self.curse_add_line(msg, "TITLE"))
+ trend_user = self.get_trend('user')
+ trend_system = self.get_trend('system')
+ if trend_user is None or trend_user is None:
+ trend_cpu = None
+ else:
+ trend_cpu = trend_user + trend_system
+ msg = ' {:4}'.format(self.trend_msg(trend_cpu))
+ ret.append(self.curse_add_line(msg))
+ # Total CPU usage
+ msg = '{:5.1f}%'.format(self.stats['total'])
+ ret.append(self.curse_add_line(msg, self.get_views(key='total', option='decoration')))
+ # Idle CPU
+ if 'idle' in self.stats and not idle_tag:
+ msg = ' {:8}'.format('idle')
+ ret.append(self.curse_add_line(msg, optional=self.get_views(key='idle', option='optional')))
+ msg = '{:4.1f}%'.format(self.stats['idle'])
+ ret.append(self.curse_add_line(msg, optional=self.get_views(key='idle', option='optional')))
+ # ctx_switches
+ # On WINDOWS/SUNOS the ctx_switches is displayed in the third line
+ if not WINDOWS and not SUNOS:
+ ret.extend(self.curse_add_stat('ctx_switches', width=15, header=' '))
+
+ # Second line
+ # user|idle + irq + interrupts
+ ret.append(self.curse_new_line())
+ # User CPU
+ if not idle_tag:
+ ret.extend(self.curse_add_stat('user', width=15))
+ elif 'idle' in self.stats:
+ ret.extend(self.curse_add_stat('idle', width=15))
+ # IRQ CPU
+ ret.extend(self.curse_add_stat('irq', width=14, header=' '))
+ # interrupts
+ ret.extend(self.curse_add_stat('interrupts', width=15, header=' '))
+
+ # Third line
+ # system|core + nice + sw_int
+ ret.append(self.curse_new_line())
+ # System CPU
+ if not idle_tag:
+ ret.extend(self.curse_add_stat('system', width=15))
+ else:
+ ret.extend(self.curse_add_stat('core', width=15))
+ # Nice CPU
+ ret.extend(self.curse_add_stat('nice', width=14, header=' '))
+ # soft_interrupts
+ if not WINDOWS and not SUNOS:
+ ret.extend(self.curse_add_stat('soft_interrupts', width=15, header=' '))
+ else:
+ ret.extend(self.curse_add_stat('ctx_switches', width=15, header=' '))
+
+ # Fourth line
+ # iowait + steal + syscalls
+ ret.append(self.curse_new_line())
+ if 'iowait' in self.stats:
+ # IOWait CPU
+ ret.extend(self.curse_add_stat('iowait', width=15))
+ elif 'dpc' in self.stats:
+ # DPC CPU
+ ret.extend(self.curse_add_stat('dpc', width=15))
+ # Steal CPU usage
+ ret.extend(self.curse_add_stat('steal', width=14, header=' '))
+ # syscalls: number of system calls since boot. Always set to 0 on Linux. (do not display)
+ if not LINUX:
+ ret.extend(self.curse_add_stat('syscalls', width=15, header=' '))
+
+ # Return the message with decoration
+ return ret