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