Předpovídání vyčerpání místa na disku ve widgetu qtile
Vývojáři qtile mohou integrovat widget DiskSpaceAlert pro předpovídání vyčerpání místa na disku pomocí lineární regrese. Analyzuje trend volného místa na reálných souborových systémech, vynechává virtuální FS, a aktivuje se při překročení nuly v rozmezí 24 hodin. Widget dědí z InLoopPollText, dotazuje se každých 5 sekund přes /proc/mounts a os.statvfs, používá deque pro historii dat.
Kód funguje bez externích závislostí, je vhodný pro desktopy bez Promethea. První spuštění nastalo na /tmp v ext2 na zram (16 GB): pozadí logů zaplnilo oddíl, Bash tool spadl bez varování.
Filtrace mountpointů
Widget ignoruje virtuální FS a systémové cesty, zaměřuje se na reálné disky. Konstanty definují výjimky:
DISK_SPACE_SKIP_FS = {
'sysfs', 'proc', 'devtmpfs', 'devpts', 'tmpfs', 'securityfs',
'cgroup', 'cgroup2', 'pstore', 'debugfs', 'hugetlbfs', 'mqueue',
'configfs', 'fusectl', 'efivarfs', 'binfmt_misc', 'autofs',
'fuse.portal', 'nsfs', 'tracefs', 'bpf', 'ramfs', 'rpc_pipefs',
'nfsd', 'overlay', 'fuse.gvfsd-fuse',
}
DISK_SPACE_SKIP_PREFIXES = ('/sys/', '/proc/', '/dev/', '/run/')
Funkce get_mountpoints parsuje /proc/mounts, přeskočí záznamy podle typu FS a prefixu cesty. Pro tmpfs na zram filtr nefunguje, což umožňuje zachytit kritické scénáře.
def get_mountpoints(self):
mounts = []
try:
with open('/proc/mounts') as f:
for line in f:
parts = line.split()
if len(parts) < 3:
continue
mountpoint, fstype = parts[1], parts[2]
if fstype in DISK_SPACE_SKIP_FS:
continue
if any(mountpoint.startswith(p)
for p in DISK_SPACE_SKIP_PREFIXES):
continue
mounts.append(mountpoint)
except OSError:
pass
return mounts
Lineární regrese pro predikci
Předpověď je implementována metodou nejmenších čtverců (OLS) bez numpy. Aproximuje volné místo y(t) = a·t + b, vypočítá čas do y=0. Vzorce:
- a = (n∑(t_i y_i) - ∑t_i ∑y_i) / (n∑t_i² - (∑t_i)²)
- b = (∑y_i - a ∑t_i) / n
Čas do vyčerpání: -b / a minus uplynulý čas okna. Pokud slope ≥ 0 nebo seconds_left ≤ 0 — None.
@staticmethod
def _predict_exhaustion(history, now):
n = len(history)
t = [h[0] - history[0][0] for h in history]
y = [h[1] for h in history]
sum_t = sum(t)
sum_y = sum(y)
sum_tt = sum(ti * ti for ti in t)
sum_ty = sum(ti * yi for ti, yi in zip(t, y))
denom = n * sum_tt - sum_t * sum_t
if denom == 0:
return None
slope = (n * sum_ty - sum_t * sum_y) / denom
intercept = (sum_y - slope * sum_t) / n
if slope >= 0:
return None
time_to_zero = -intercept / slope
elapsed = now - history[0][0]
seconds_left = time_to_zero - elapsed
if seconds_left > 0:
return seconds_left / 3600
return None
Posuvné okno maxlen=120 (10 min při 5s intervalu) vyhlazuje šum od dočasných souborů.
Barevná indikace a logika alertů
Dvě úrovně poplachu:
- Červená (#FF0000): Aktivní spotřeba (posledních 30s místo ubývá) nebo <10% volno.
- Oranžová (#FF8800): Předpověď vyčerpání <24h, ale trend se stabilizoval.
Metoda _set_color aktualizuje self.foreground a self.layout.colour bez zbytečných draw(). Text: 'DISK /tmp: 8%free, 2h left'.
def _set_color(self, color):
self.foreground = color
if self.layout:
self.layout.colour = color
Vyčištění historie pro odstraněné mountpointy.
Celý kód třídy DiskSpaceAlert
DISK_SPACE_CHECK_INTERVAL = 5
DISK_SPACE_WARN_PERCENT = 10
DISK_SPACE_PREDICT_HOURS = 24
# ... (SKIP_FS a PREFIXES jako výše)
class DiskSpaceAlert(base.InLoopPollText):
COLOR_RED = '#FF0000'
COLOR_ORANGE = '#FF8800'
def __init__(self, **config):
config.setdefault('foreground', self.COLOR_RED)
config.setdefault('update_interval', DISK_SPACE_CHECK_INTERVAL)
super().__init__(**config)
self.space_history: dict[str, deque] = {}
# get_mountpoints() jako výše
# _predict_exhaustion() jako výše
# _set_color() jako výše
def poll(self):
now = time()
alerts = []
any_active = False
for mp in self.get_mountpoints():
try:
st = os.statvfs(mp)
except OSError:
continue
if st.f_blocks == 0:
continue
free_pct = st.f_bfree / st.f_blocks * 100
free_bytes = st.f_bfree * st.f_frsize
history = self.space_history.setdefault(
mp, deque(maxlen=120))
history.append((now, free_bytes))
reasons = []
if free_pct < DISK_SPACE_WARN_PERCENT:
reasons.append(f'{free_pct:.0f}%free')
any_active = True
if len(history) >= 2 and now - history[0][0] > 60:
hours_left = self._predict_exhaustion(
history, now)
if (hours_left is not None
and hours_left < DISK_SPACE_PREDICT_HOURS):
recent = [h for h in history
if now - h[0] < 30]
if (len(recent) >= 2
and recent[-1][1] <= recent[0][1]):
any_active = True
reasons.append(f'{hours_left:.0f}h left')
if reasons:
label = mp if mp != '/' else '/'
alerts.append(
f'DISK {label}: {", ".join(reasons)}')
self._set_color(
self.COLOR_RED if any_active
else self.COLOR_ORANGE)
current = set(self.get_mountpoints())
for mp in list(self.space_history):
if mp not in current:
del self.space_history[mp]
return ' | '.join(alerts)
Integrace do screens: DiskSpaceAlert(font=font, fontsize=fontsize).
Nastavení a ladění
- CHECK_INTERVAL: 5s (desktop), 30s (server).
- WARN_PERCENT: 10% pro okamžité červené.
- PREDICT_HOURS: 24h horizont.
- maxlen=120: 10 min historie.
Krátké okno — hlučné falešné poplachy, dlouhé — zpožďuje.
Co je důležité
- Lineární regrese OLS podle historie volného místa předpovídá vyčerpání přesněji než prahové kontroly.
- Filtrace /proc/mounts vylučuje virtuální FS, zaměřuje se na ext2/zram, reálné disky.
- Dvě barvy: červená pro aktivní hrozbu, oranžová pro stabilizující se trendy.
- Bez závislostí, 100 řádků v config.py qtile.
- Okno 10 min vyhlazuje výkyvy od temp-souborů.
— Editorial Team
Zatím žádné komentáře.