85 lines
2.6 KiB
Python
85 lines
2.6 KiB
Python
from typing import Any
|
|
|
|
import psutil
|
|
from psutil._ntuples import sdiskio
|
|
|
|
|
|
def counters() -> dict[str, sdiskio]:
|
|
"""Current per-disk IO counters.
|
|
|
|
Returns:
|
|
A device-name to sdiskio mapping, or an empty dict on systems
|
|
without disk statistics.
|
|
"""
|
|
return psutil.disk_io_counters(perdisk=True) or {}
|
|
|
|
|
|
def rates(prev: dict[str, sdiskio], dt: float) -> tuple[float, float]:
|
|
"""Aggregate read/write rates in bytes/s since a previous counters call.
|
|
|
|
Disks that were not present in `prev` (hot-plugged) contribute
|
|
nothing, and negative byte deltas (counter wrap, reboots) are clamped
|
|
to zero.
|
|
|
|
Args:
|
|
prev: counters() result from the previous sample.
|
|
dt: seconds between the two snapshots.
|
|
|
|
Returns:
|
|
(read_bytes_per_s, write_bytes_per_s).
|
|
"""
|
|
cur = counters()
|
|
r = 0
|
|
w = 0
|
|
for name, c in cur.items():
|
|
p = prev.get(name)
|
|
if p is not None and dt > 0:
|
|
r += max(0, int(c.read_bytes) - int(p.read_bytes))
|
|
w += max(0, int(c.write_bytes) - int(p.write_bytes))
|
|
return (r / dt if dt > 0 else 0.0, w / dt if dt > 0 else 0.0)
|
|
|
|
|
|
def partitions() -> list[dict[str, Any]]:
|
|
"""Mounted real filesystems, grouped by device.
|
|
|
|
All partitions on the same device are merged into one entry; usage
|
|
stats come from the first readable mountpoint, unreadable ones are
|
|
skipped. When a device has more than 3 mountpoints, mounts_disp shows
|
|
the first three plus "+N more".
|
|
|
|
Returns:
|
|
One entry per device (device, fstype, usage, mounts, mounts_disp),
|
|
sorted by device name.
|
|
"""
|
|
groups: dict[str, dict[str, Any]] = {}
|
|
order: list[str] = []
|
|
for p in psutil.disk_partitions(all=False):
|
|
if p.device in groups:
|
|
g = groups[p.device]
|
|
if p.mountpoint not in g["mounts"]:
|
|
g["mounts"].append(p.mountpoint)
|
|
continue
|
|
try:
|
|
u = psutil.disk_usage(p.mountpoint)
|
|
except (OSError, PermissionError):
|
|
continue
|
|
g = {
|
|
"device": p.device,
|
|
"fstype": p.fstype,
|
|
"total": u.total,
|
|
"used": u.used,
|
|
"free": u.free,
|
|
"pct": u.percent,
|
|
"mounts": [p.mountpoint],
|
|
}
|
|
groups[p.device] = g
|
|
order.append(p.device)
|
|
out = [groups[d] for d in sorted(order)]
|
|
for g in out:
|
|
g["mounts"] = sorted(g["mounts"])
|
|
mounts = g["mounts"]
|
|
if len(mounts) > 3:
|
|
g["mounts_disp"] = " · ".join(mounts[:3]) + f" +{len(mounts) - 3} more"
|
|
else:
|
|
g["mounts_disp"] = " · ".join(mounts)
|
|
return out
|