Kompletter Neustart als Python-Projekt. Portiert von zfs-monitor Bash v3, saubere Architektur: - config.py: TOML-Config mit dataclasses - state.py: zentrales State-Management (JSON statt Einzeldateien) - notify.py: Telegram + Persistent Alerting als wiederverwendbare Klasse - checks/zfs.py: ZFS Pool-Health, I/O-Fehler, Kapazität - checks/smart.py: SATA + NVMe SMART-Monitoring - checks/nut.py: NUT/USV-Monitoring (APC Back-UPS RS 1500G)
45 lines
1.2 KiB
Python
45 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
def make_key(prefix: str, identifier: str) -> str:
|
|
"""Stable, collision-safe state key from prefix + arbitrary identifier."""
|
|
h = hashlib.md5(identifier.encode()).hexdigest()[:8]
|
|
return f"{prefix}_{h}"
|
|
|
|
|
|
class State:
|
|
def __init__(self, path: str) -> None:
|
|
self._path = Path(path)
|
|
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
self._data: dict[str, Any] = {}
|
|
self._load()
|
|
|
|
def _load(self) -> None:
|
|
if self._path.exists():
|
|
try:
|
|
self._data = json.loads(self._path.read_text())
|
|
except (json.JSONDecodeError, OSError):
|
|
self._data = {}
|
|
|
|
def _save(self) -> None:
|
|
self._path.write_text(json.dumps(self._data, indent=2))
|
|
|
|
def get(self, key: str, default: Any = None) -> Any:
|
|
return self._data.get(key, default)
|
|
|
|
def set(self, key: str, value: Any) -> None:
|
|
self._data[key] = value
|
|
self._save()
|
|
|
|
def delete(self, *keys: str) -> None:
|
|
changed = any(k in self._data for k in keys)
|
|
for k in keys:
|
|
self._data.pop(k, None)
|
|
if changed:
|
|
self._save()
|