feat: add smart_serial() function to extract manufacturer serial numbers from SMART data with fallback chain and parallel execution

Add smart_serial() function with smartctl -i command execution, fallback to -d sat mode, and Serial Number: field regex extraction with WWN/0x prefix filtering to prefer physical serial over logical identifiers. Add serial_futures dict with service_check_executor.submit() calls for parallel serial reads alongside smart_futures/temperature_futures. Update raid_status() disk
This commit is contained in:
2026-08-17 19:27:21 +02:00
parent 02449a80de
commit d4c24f02d4
+15 -1
View File
@@ -365,6 +365,18 @@ def smart_status(device):
health = re.search(r"(?:SMART overall-health self-assessment test result|SMART Health Status):\s*(.+)", smart_output, re.IGNORECASE)
return health.group(1).strip() if health else ("Fehler: " + smart_error.strip()[:80] if smart_error.strip() else "Nicht unterstützt")
def smart_serial(device):
"""Read the manufacturer serial, not a WWN or SCSI by-id alias."""
if not shutil.which("smartctl"):
return ""
smart_device = device.replace("/dev/", f"{HOST_DEVICE_PATH.rstrip('/')}/", 1)
for command in (["smartctl", "-i", "-d", "sat", smart_device], ["smartctl", "-i", smart_device]):
output, _ = _read_command(command, timeout=6, warn_on_nonzero=False)
match = re.search(r"^\s*Serial Number:\s*(\S+)\s*$", output, re.IGNORECASE | re.MULTILINE)
if match and not match.group(1).lower().startswith(("wwn-", "0x")):
return match.group(1).strip()
return ""
def smart_temperature(device):
"""Read the drive temperature in a controller-friendly way."""
if not SMART_ENABLED or not shutil.which("smartctl"):
@@ -457,9 +469,11 @@ def raid_status():
except (ValueError, AttributeError):
block_devices = {}
smart_futures = {}
serial_futures = {}
temperature_futures = {}
if shutil.which("smartctl"):
smart_futures = {disk["device"]: service_check_executor.submit(smart_status, disk["device"]) for disk in devices}
serial_futures = {disk["device"]: service_check_executor.submit(smart_serial, disk["device"]) for disk in devices}
temperature_futures = {disk["device"]: service_check_executor.submit(smart_temperature, disk["device"]) for disk in devices}
for disk in devices:
info = block_devices.get(disk["device"], {})
@@ -473,7 +487,7 @@ def raid_status():
info = host_items[0] if host_items else {}
except (ValueError, AttributeError):
info = {}
serial = info.get("serial") or _device_serial(disk["device"])
serial = (serial_futures[disk["device"]].result() if disk["device"] in serial_futures else "") or info.get("serial") or _device_serial(disk["device"])
disk.update({"size": format_bytes(info.get("size") or 0) if info.get("size") else "", "model": info.get("model") or "", "serial": serial or ""})
serial_log_key = "serial:" + disk["device"]
if not serial and serial_log_key not in hardware_log_once: