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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user