mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-02 10:52:22 +00:00
fix(monitor): place the Secure Gateway template on an active storage
This commit is contained in:
@@ -410,60 +410,16 @@ def _extract_temperature(data: dict[str, Any]) -> Optional[float]:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ── Leaving idle and excluded disks alone ──────────────────────────
|
||||
# ── Leaving excluded disks alone ─────────────────────────────────
|
||||
#
|
||||
# `-n standby` keeps a periodic reader from waking a disk that is asleep.
|
||||
# It does not let an awake one fall asleep: a drive's spin-down timer
|
||||
# counts time without commands, and a read every minute resets it, so an
|
||||
# unused drive whose timer is longer than that never spins down — and a
|
||||
# drive that parks its heads when idle loads them again on every read.
|
||||
#
|
||||
# So a rotational disk with no I/O since it was last looked at is not read
|
||||
# at all, not even asked for its power mode. The counters come from
|
||||
# /proc/diskstats, which costs no disk access, and a SMART query does not
|
||||
# move them — passthrough commands are not accounted as reads or writes —
|
||||
# so any change means something else used the disk. A disk in use is
|
||||
# already awake, and reading it then costs nothing. Solid-state disks have
|
||||
# no spindle and no heads, and keep their reading.
|
||||
#
|
||||
# A disk seen for the first time is read once, so a Monitor that has just
|
||||
# started still has values to show; after that it is left alone for as
|
||||
# long as nothing uses it.
|
||||
# A disk the user excludes in Settings receives nothing from a periodic
|
||||
# reader, not even the power-mode question, so it can spin down on its own
|
||||
# timer. Every other disk is read on schedule: `-n standby` still keeps a
|
||||
# reader from waking one that is already asleep.
|
||||
|
||||
READ = "read"
|
||||
IDLE = "idle"
|
||||
EXCLUDED = "excluded"
|
||||
|
||||
_last_io: dict[str, tuple[int, int]] = {}
|
||||
_idle_state: dict[str, float] = {}
|
||||
_IDLE_TTL = 600 # same horizon as the standby badge
|
||||
|
||||
|
||||
def _read_diskstats() -> dict[str, tuple[int, int]]:
|
||||
"""Reads and writes completed per device, from /proc/diskstats."""
|
||||
out: dict[str, tuple[int, int]] = {}
|
||||
try:
|
||||
with open("/proc/diskstats") as f:
|
||||
for line in f:
|
||||
parts = line.split()
|
||||
if len(parts) < 8:
|
||||
continue
|
||||
try:
|
||||
out[parts[2]] = (int(parts[3]), int(parts[7]))
|
||||
except ValueError:
|
||||
continue
|
||||
except OSError:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def _is_rotational(disk_name: str) -> bool:
|
||||
try:
|
||||
with open(f"/sys/block/{disk_name}/queue/rotational") as f:
|
||||
return f.read().strip() == "1"
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
_EXCLUDED_TTL = 15
|
||||
_excluded_cache: Optional[tuple[float, set]] = None
|
||||
@@ -503,50 +459,20 @@ _excluded_disk_names = excluded_disk_names
|
||||
|
||||
|
||||
def disk_read_policy(disk_name: str, excluded: Optional[set] = None) -> str:
|
||||
"""Whether a periodic reader may touch this disk now: READ, IDLE or
|
||||
EXCLUDED. Shared by every reader that runs on its own, so the
|
||||
temperature poller and the storage view cannot disagree about a disk.
|
||||
``excluded`` lets a caller that checks many disks pass the list once."""
|
||||
"""Whether a periodic reader may touch this disk: READ or EXCLUDED.
|
||||
Shared by every reader that runs on its own, so the temperature poller
|
||||
and the storage view cannot disagree about a disk. ``excluded`` lets a
|
||||
caller that checks many disks pass the list once."""
|
||||
if excluded is None:
|
||||
excluded = _excluded_disk_names()
|
||||
if disk_name in excluded:
|
||||
_idle_state.pop(disk_name, None)
|
||||
return EXCLUDED
|
||||
if not _is_rotational(disk_name):
|
||||
return READ
|
||||
current = _read_diskstats().get(disk_name)
|
||||
with _cache_lock:
|
||||
previous = _last_io.get(disk_name)
|
||||
if current is not None:
|
||||
_last_io[disk_name] = current
|
||||
if current is None or previous is None or current != previous:
|
||||
_idle_state.pop(disk_name, None)
|
||||
return READ
|
||||
_idle_state[disk_name] = time.time()
|
||||
return IDLE
|
||||
|
||||
|
||||
def settle_own_reads(disk_names) -> None:
|
||||
"""A reader that has just read these disks itself takes their counters as
|
||||
the new baseline, so its own I/O never counts as activity."""
|
||||
stats = _read_diskstats()
|
||||
with _cache_lock:
|
||||
for name in disk_names:
|
||||
if name in _last_io and name in stats:
|
||||
_last_io[name] = stats[name]
|
||||
|
||||
|
||||
def is_disk_idle(disk_name: str) -> bool:
|
||||
"""True while the disk is being left alone for having no I/O."""
|
||||
ts = _idle_state.get(disk_name)
|
||||
return ts is not None and (time.time() - ts) < _IDLE_TTL
|
||||
return EXCLUDED if disk_name in excluded else READ
|
||||
|
||||
|
||||
def record_all_disk_temperatures() -> int:
|
||||
"""Sample the disks that may be read now and persist their temperature.
|
||||
|
||||
USB disks are included. A disk the user excluded, or a rotational one
|
||||
with no I/O since the last cycle, is skipped — see ``disk_read_policy``.
|
||||
USB disks are included. A disk the user excluded is skipped — see
|
||||
``disk_read_policy``.
|
||||
|
||||
Sampling fans out across a thread pool so a host with N disks pays
|
||||
roughly the time of the slowest single ``smartctl`` call instead of
|
||||
|
||||
Reference in New Issue
Block a user