main
py 151 lines 5.29 KB
Raw
1 from helpers.extension import Extension
2 import os
3 import psutil
4
5
6 class SystemResourcesCheck(Extension):
7 async def execute(self, banners: list = [], frontend_context: dict = {}, **kwargs):
8 try:
9 cpu_percent = psutil.cpu_percent(interval=0.1)
10 except Exception:
11 cpu_percent = None
12
13 try:
14 cpu_cores = psutil.cpu_count(logical=True)
15 except Exception:
16 cpu_cores = None
17
18 load_avg = self._get_load_average()
19
20 try:
21 vm = psutil.virtual_memory()
22 ram_percent = vm.percent
23 ram_used_gb = (vm.total - vm.available) / (1024 ** 3)
24 ram_total_gb = vm.total / (1024 ** 3)
25 except Exception:
26 ram_percent = None
27
28 ram_used_gb = None
29 ram_total_gb = None
30
31 disk_percent, disk_used_gb, disk_total_gb, disk_path = self._get_disk_usage()
32
33 try:
34 net = psutil.net_io_counters()
35 net_sent = self._format_bytes(net.bytes_sent)
36 net_recv = self._format_bytes(net.bytes_recv)
37 except Exception:
38 net_sent = "N/A"
39 net_recv = "N/A"
40
41 load_value = "N/A"
42 if load_avg:
43 la1, la5, la15 = load_avg
44 load_value = f"{la1:.2f} / {la5:.2f} / {la15:.2f}"
45
46 if disk_percent is None or disk_used_gb is None or disk_total_gb is None:
47 disk_value = "N/A"
48 else:
49 disk_value = f"{disk_used_gb:.2f}/{disk_total_gb:.2f} GB"
50
51 if cpu_percent is None:
52 cpu_value = "N/A"
53 else:
54 cores_value = "" if cpu_cores is None else f" ({cpu_cores} cores)"
55 cpu_value = f"{cpu_percent:.0f}%{cores_value}"
56
57 if ram_percent is None or ram_used_gb is None or ram_total_gb is None:
58 ram_value = "N/A"
59 else:
60 ram_value = f"{ram_used_gb:.2f}/{ram_total_gb:.2f} GB"
61
62 cpu_bar = self._bar_html(cpu_percent)
63 ram_bar = self._bar_html(ram_percent)
64 disk_bar = self._bar_html(disk_percent)
65
66 banners.append({
67 "id": "system-resources",
68 "type": "info",
69 "priority": 10,
70 "title": "System Resources",
71 "html": (
72 "<div class=\"system-resource-body\">"
73 "<div class=\"system-resource-meter\">"
74 "<div class=\"system-resource-label\">"
75 "<div class=\"system-resource-name\">CPU</div>"
76 f"<div class=\"system-resource-value\">{cpu_value}</div>"
77 "</div>"
78 f"{cpu_bar}"
79 "</div>"
80 "<div class=\"system-resource-meter\">"
81 "<div class=\"system-resource-label\">"
82 "<div class=\"system-resource-name\">RAM</div>"
83 f"<div class=\"system-resource-value\">{ram_value}</div>"
84 "</div>"
85 f"{ram_bar}"
86 "</div>"
87 "<div class=\"system-resource-meter\">"
88 "<div class=\"system-resource-label\">"
89 "<div class=\"system-resource-name\">Disk</div>"
90 f"<div class=\"system-resource-value\">{disk_value}</div>"
91 "</div>"
92 f"{disk_bar}"
93 "</div>"
94 "<div class=\"system-resource-footer\">"
95 "<div class=\"system-resource-detail\">"
96 "<div class=\"system-resource-detail-title\">Load (1/5/15)</div>"
97 f"<div class=\"system-resource-detail-value\">{load_value}</div>"
98 "</div>"
99 "<div class=\"system-resource-detail\">"
100 "<div class=\"system-resource-detail-title\">Net (since boot)</div>"
101 f"<div class=\"system-resource-detail-value\">{net_sent} sent / {net_recv} recv</div>"
102 "</div>"
103 "</div>"
104 "</div>"
105 ),
106 "dismissible": True,
107 "source": "backend",
108 })
109
110 def _bar_html(self, percent: float | None) -> str:
111 if percent is None:
112 return ""
113
114 p = max(0.0, min(100.0, float(percent)))
115 if p >= 85:
116 color = "#ef4444"
117 elif p >= 70:
118 color = "#f59e0b"
119 else:
120 color = "#22c55e"
121
122 return (
123 "<div class=\"system-resource-track\">"
124 f"<span class=\"system-resource-fill\" style=\"--resource-value:{p:.0f}%;--resource-color:{color};\"></span>"
125 "</div>"
126 )
127
128 def _get_load_average(self) -> tuple[float, float, float] | None:
129 try:
130 return os.getloadavg()
131 except Exception:
132 return None
133
134 def _get_disk_usage(self) -> tuple[float | None, float | None, float | None, str]:
135 for path in ["/", os.path.expanduser("~")]:
136 try:
137 usage = psutil.disk_usage(path)
138 used_gb = usage.used / (1024 ** 3)
139 total_gb = usage.total / (1024 ** 3)
140 return usage.percent, used_gb, total_gb, path
141 except Exception:
142 continue
143 return None, None, None, "/"
144
145 def _format_bytes(self, value: int) -> str:
146 size = float(value)
147 for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
148 if size < 1024:
149 return f"{size:.1f} {unit}"
150 size /= 1024
151 return f"{size:.1f} EB"