From 10eb3a4191549d18081a85362cb4c8dc9af7c2e3 Mon Sep 17 00:00:00 2001 From: nessi Date: Sun, 16 Aug 2026 12:14:10 +0200 Subject: [PATCH] feat: add /host-sys volume mount and HOST_SYS_PATH environment variable for sysfs serial fallback and SMART health detection Add HOST_SYS_PATH=/host-sys to .env.example and app.py config with /host-sys default. Update _device_serial() to check HOST_SYS_PATH/class/block and HOST_SYS_PATH/block before fallback /sys paths for serial reads. Update raid_status() SMART regex to match both "SMART overall-health self-assessment test result" and "SMART Health Status" with case-insensitive flag for NVMe/SSD compatibility. Add / --- .env.example | 1 + app.py | 5 +++-- docker-compose.yml | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index e255670..c4f8eaa 100644 --- a/.env.example +++ b/.env.example @@ -37,4 +37,5 @@ RAID_DEVICE=/dev/md127 STORAGE_CACHE_SECONDS=900 # Optional: SMART-Abfragen aktivieren (benötigt Zugriff auf Host-Devices) SMART_ENABLED=false +HOST_SYS_PATH=/host-sys LOG_LEVEL=INFO diff --git a/app.py b/app.py index 449f7a4..f26f907 100644 --- a/app.py +++ b/app.py @@ -41,6 +41,7 @@ REQUEST_TIMEOUT = int(os.environ.get("REQUEST_TIMEOUT", "20")) DATASTORE_PATH = os.environ.get("DATASTORE_PATH", "/nesflix").rstrip("/") or "/" RAID_DEVICE = os.environ.get("RAID_DEVICE", "/dev/md127") HOST_DEVICE_PATH = os.environ.get("HOST_DEVICE_PATH", "/host-dev") +HOST_SYS_PATH = os.environ.get("HOST_SYS_PATH", "/host-sys") STORAGE_CACHE_SECONDS = int(os.environ.get("STORAGE_CACHE_SECONDS", "900")) SMART_ENABLED = os.environ.get("SMART_ENABLED", "false").lower() in {"1", "true", "yes", "on"} @@ -243,7 +244,7 @@ def _flatten_lsblk(nodes): def _device_serial(device): """Read a disk serial from sysfs when lsblk cannot expose it in Docker.""" name = Path(device).name - for path in (Path("/sys/class/block") / name / "device/serial", Path("/sys/block") / name / "device/serial"): + for path in (Path(HOST_SYS_PATH) / "class/block" / name / "device/serial", Path(HOST_SYS_PATH) / "block" / name / "device/serial", Path("/sys/class/block") / name / "device/serial", Path("/sys/block") / name / "device/serial"): try: value = path.read_text().strip() if value: @@ -357,7 +358,7 @@ def raid_status(): if SMART_ENABLED and shutil.which("smartctl"): smart_device = disk["device"].replace("/dev/", f"{HOST_DEVICE_PATH.rstrip('/')}/", 1) smart_output, _ = _read_command(["smartctl", "-H", smart_device], timeout=6) - health = re.search(r"SMART overall-health self-assessment test result:\s*(.+)", smart_output) + health = re.search(r"(?:SMART overall-health self-assessment test result|SMART Health Status):\s*(.+)", smart_output, re.IGNORECASE) disk["smart"] = health.group(1).strip() if health else "Nicht verfügbar" missing = members_match and members_match.group(1) != members_match.group(2) failed = detail.get("failed_devices", 0) > 0 or any(disk["status"] == "failed" for disk in devices) diff --git a/docker-compose.yml b/docker-compose.yml index aaef9ca..635482d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,11 @@ services: - "8099:8099" devices: - "${RAID_DEVICE:-/dev/md127}:${RAID_DEVICE:-/dev/md127}" + cap_add: + - SYS_RAWIO + device_cgroup_rules: + - "b 8:* rwm" + - "b 9:* rwm" extra_hosts: - "host.docker.internal:host-gateway" env_file: @@ -20,6 +25,8 @@ services: - ${DATASTORE_HOST_PATH:-/nesflix}:/nesflix:ro # Read-only Host-Devices für lsblk/smartctl; Media Max führt keine RAID-Schreiboperationen aus. - /dev:/host-dev:ro + # Read-only Hardware-Metadaten für Seriennummern und SMART-Zuordnung. + - /sys:/host-sys:ro - ./data:/data networks: - media