1
|
|
|
# |
2
|
|
|
# Glances - An eye on your system |
3
|
|
|
# |
4
|
|
|
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <[email protected]> |
5
|
|
|
# |
6
|
|
|
# SPDX-License-Identifier: LGPL-3.0-only |
7
|
|
|
# |
8
|
|
|
|
9
|
|
|
from glances import __version__ as glances_version |
10
|
|
|
from glances.globals import auto_unit, weak_lru_cache |
11
|
|
|
from glances.main import GlancesMain |
12
|
|
|
from glances.outputs.glances_bars import Bar |
13
|
|
|
from glances.processes import sort_stats |
14
|
|
|
from glances.stats import GlancesStats |
15
|
|
|
|
16
|
|
|
plugin_dependencies_tree = { |
17
|
|
|
'processlist': ['processcount'], |
18
|
|
|
} |
19
|
|
|
|
20
|
|
|
|
21
|
|
|
class GlancesAPI: |
22
|
|
|
ttl = 2.0 # Default cache TTL in seconds |
23
|
|
|
|
24
|
|
|
def __init__(self, config=None, args=None, args_begin_at=1): |
25
|
|
|
self.__version__ = glances_version.split('.')[0] # Get the major version |
26
|
|
|
|
27
|
|
|
core = GlancesMain(args_begin_at) |
28
|
|
|
self.args = args if args is not None else core.get_args() |
29
|
|
|
self.config = config if config is not None else core.get_config() |
30
|
|
|
self._stats = GlancesStats(config=self.config, args=self.args) |
31
|
|
|
|
32
|
|
|
# Set the cache TTL for the API |
33
|
|
|
self.ttl = self.args.time if self.args.time is not None else self.ttl |
34
|
|
|
|
35
|
|
|
# Init the stats of all plugins in order to ensure that rate are computed |
36
|
|
|
self._stats.update() |
37
|
|
|
|
38
|
|
|
@weak_lru_cache(maxsize=1, ttl=ttl) |
39
|
|
|
def __getattr__(self, item): |
40
|
|
|
"""Fallback to the stats object for any missing attributes.""" |
41
|
|
|
if item in self._stats.getPluginsList(): |
42
|
|
|
if item in plugin_dependencies_tree: |
43
|
|
|
# Ensure dependencies are updated before accessing the plugin |
44
|
|
|
for dependency in plugin_dependencies_tree[item]: |
45
|
|
|
self._stats.get_plugin(dependency).update() |
46
|
|
|
# Update the plugin stats |
47
|
|
|
self._stats.get_plugin(item).update() |
48
|
|
|
return self._stats.get_plugin(item) |
49
|
|
|
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") |
50
|
|
|
|
51
|
|
|
def plugins(self): |
52
|
|
|
"""Return the list of available plugins.""" |
53
|
|
|
return self._stats.getPluginsList() |
54
|
|
|
|
55
|
|
|
def auto_unit(self, number, low_precision=False, min_symbol='K', none_symbol='-'): |
56
|
|
|
""" |
57
|
|
|
Converts a numeric value into a human-readable string with appropriate units. |
58
|
|
|
|
59
|
|
|
Args: |
60
|
|
|
number (float or int): The numeric value to be converted. |
61
|
|
|
low_precision (bool, optional): If True, use lower precision for the output. Defaults to False. |
62
|
|
|
min_symbol (str, optional): The minimum unit symbol to use (e.g., 'K' for kilo). Defaults to 'K'. |
63
|
|
|
none_symbol (str, optional): The symbol to display if the number is None. Defaults to '-'. |
64
|
|
|
|
65
|
|
|
Returns: |
66
|
|
|
str: A human-readable string representation of the number with units. |
67
|
|
|
""" |
68
|
|
|
return auto_unit(number, low_precision, min_symbol, none_symbol) |
69
|
|
|
|
70
|
|
|
def bar(self, value, size=18, bar_char='■', empty_char='□', pre_char='', post_char=''): |
71
|
|
|
""" |
72
|
|
|
Generate a progress bar representation for a given value. |
73
|
|
|
|
74
|
|
|
Args: |
75
|
|
|
value (float): The percentage value to represent in the bar (typically between 0 and 100). |
76
|
|
|
size (int, optional): The total length of the bar in characters. Defaults to 18. |
77
|
|
|
bar_char (str, optional): The character used to represent the filled portion of the bar. Defaults to '■'. |
78
|
|
|
empty_char (str, optional): The character used to represent the empty portion of the bar. Defaults to '□'. |
79
|
|
|
pre_char (str, optional): A string to prepend to the bar. Defaults to ''. |
80
|
|
|
post_char (str, optional): A string to append to the bar. Defaults to ''. |
81
|
|
|
|
82
|
|
|
Returns: |
83
|
|
|
str: A string representing the progress bar. |
84
|
|
|
""" |
85
|
|
|
b = Bar( |
86
|
|
|
size, bar_char=bar_char, empty_char=empty_char, pre_char=pre_char, post_char=post_char, display_value=False |
87
|
|
|
) |
88
|
|
|
b.percent = value |
89
|
|
|
return b.get() |
90
|
|
|
|
91
|
|
|
def top_process(self, limit=3, sorted_by='cpu_percent', sorted_by_secondary='memory_percent'): |
92
|
|
|
""" |
93
|
|
|
Returns a list of the top processes sorted by specified criteria. |
94
|
|
|
|
95
|
|
|
Args: |
96
|
|
|
limit (int, optional): The maximum number of top processes to return. Defaults to 3. |
97
|
|
|
sorted_by (str, optional): The primary key to sort processes by (e.g., 'cpu_percent'). |
98
|
|
|
Defaults to 'cpu_percent'. |
99
|
|
|
sorted_by_secondary (str, optional): The secondary key to sort processes by if primary keys are equal |
100
|
|
|
(e.g., 'memory_percent'). Defaults to 'memory_percent'. |
101
|
|
|
|
102
|
|
|
Returns: |
103
|
|
|
list: A list of dictionaries representing the top processes, excluding those with 'glances' in their |
104
|
|
|
command line. |
105
|
|
|
|
106
|
|
|
Note: |
107
|
|
|
The 'glances' process is excluded from the returned list to avoid self-generated CPU load affecting |
108
|
|
|
the results. |
109
|
|
|
""" |
110
|
|
|
# Exclude glances process from the top list |
111
|
|
|
# because in fetch mode, Glances generate a CPU load |
112
|
|
|
all_but_glances = [p for p in self._stats.get_plugin('processlist').get_raw() if 'glances' not in p['cmdline']] |
113
|
|
|
return sort_stats(all_but_glances, sorted_by=sorted_by, sorted_by_secondary=sorted_by_secondary)[:limit] |
114
|
|
|
|