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)
39 lines
1.1 KiB
Python
39 lines
1.1 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import subprocess
|
|
from abc import ABC, abstractmethod
|
|
|
|
from config import Config
|
|
from notify import Alerter
|
|
from state import State
|
|
|
|
|
|
class Check(ABC):
|
|
def __init__(self, config: Config, state: State, alerter: Alerter) -> None:
|
|
self.config = config
|
|
self.state = state
|
|
self.alerter = alerter
|
|
self.log = logging.getLogger(self.__class__.__name__)
|
|
|
|
@abstractmethod
|
|
def run(self) -> None:
|
|
pass
|
|
|
|
def _run(self, cmd: list[str], timeout: int) -> str | None:
|
|
"""Run command, return stdout (even on non-zero exit) or None on error."""
|
|
try:
|
|
result = subprocess.run(
|
|
cmd,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=timeout,
|
|
)
|
|
return result.stdout
|
|
except subprocess.TimeoutExpired:
|
|
self.log.warning("Timeout: %s", " ".join(cmd))
|
|
return None
|
|
except (FileNotFoundError, OSError) as e:
|
|
self.log.warning("Fehler bei '%s': %s", cmd[0], e)
|
|
return None
|