feat: add drive temperature monitoring with async updates and multi-pattern SMART parsing
Add smart_temperature() function with smartctl -A command execution, fallback to -d sat mode, and regex patterns for Temperature_Celsius/Airflow_Temperature_Cel/Drive_Temperature/Current Drive Temperature attributes plus Temperature: field parsing with 1-3 digit capture and °C formatting. Add temperature_futures dict with service_check_executor.submit() calls for parallel temperature reads alongside smart_futures. Add disk["
This commit is contained in:
@@ -352,6 +352,24 @@ 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_temperature(device):
|
||||
"""Read the drive temperature in a controller-friendly way."""
|
||||
if not SMART_ENABLED or not shutil.which("smartctl"):
|
||||
return "Nicht verfügbar"
|
||||
smart_device = device.replace("/dev/", f"{HOST_DEVICE_PATH.rstrip('/')}/", 1)
|
||||
output, error = _read_command(["smartctl", "-A", smart_device], timeout=6, warn_on_nonzero=False)
|
||||
if not output:
|
||||
output, error = _read_command(["smartctl", "-A", "-d", "sat", smart_device], timeout=6, warn_on_nonzero=False)
|
||||
patterns = (
|
||||
r"(?:Temperature_Celsius|Airflow_Temperature_Cel|Drive_Temperature|Current Drive Temperature)\s+.*?\s(\d{1,3})(?:\s*°?C)?\s*$",
|
||||
r"^\s*Temperature:\s*(\d{1,3})\s*(?:C|°C|Celsius)?\s*$",
|
||||
)
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, output, re.IGNORECASE | re.MULTILINE)
|
||||
if match:
|
||||
return f"{int(match.group(1))} °C"
|
||||
return "Nicht verfügbar"
|
||||
|
||||
def raid_status():
|
||||
global raid_log_state
|
||||
mdstat, mdstat_error = "", ""
|
||||
@@ -406,8 +424,10 @@ def raid_status():
|
||||
except (ValueError, AttributeError):
|
||||
block_devices = {}
|
||||
smart_futures = {}
|
||||
if SMART_ENABLED and shutil.which("smartctl"):
|
||||
temperature_futures = {}
|
||||
if shutil.which("smartctl"):
|
||||
smart_futures = {disk["device"]: service_check_executor.submit(smart_status, 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"], {})
|
||||
parent = re.sub(r"(p)?\d+$", "", disk["device"])
|
||||
@@ -435,6 +455,7 @@ def raid_status():
|
||||
elif disk["smart"] != "PASSED" and smart_log_key not in hardware_log_once:
|
||||
log.warning("SMART-Status für %s nicht verfügbar: %s", disk["device"], disk["smart"])
|
||||
hardware_log_once.add(smart_log_key)
|
||||
disk["temperature"] = temperature_futures[disk["device"]].result() if disk["device"] in temperature_futures else smart_temperature(disk["device"])
|
||||
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)
|
||||
rebuilding = bool(progress_match)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<!doctype html>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded',()=>{const updateTemperatures=async()=>{const container=document.querySelector('#raidDisks');if(!container)return;try{const data=await (await fetch('/api/datastore')).json();const disks=data.raid?.devices||[];container.querySelectorAll('.raid-disk').forEach(row=>{const device=row.querySelector('strong')?.textContent.trim();const disk=disks.find(item=>item.device===device);if(!disk)return;let node=row.querySelector('.raid-temperature');if(!node){node=document.createElement('small');node.className='raid-temperature';const smart=row.querySelector('.raid-smart');if(smart)smart.after(node);else row.querySelector('div')?.append(node)}node.textContent=`Temperatur: ${disk.temperature||'Nicht verfügbar'}`})}catch(error){}};updateTemperatures();setInterval(updateTemperatures,5000)});
|
||||
</script>
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('/sw.js?v=4', {scope: '/'}).catch(function () {});
|
||||
|
||||
Reference in New Issue
Block a user