mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-10-11 12:26:18 +00:00
Update AppImage
This commit is contained in:
@@ -202,9 +202,11 @@ export function StorageOverview() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader>
|
||||||
<CardTitle className="text-sm font-medium">Avg Temperature</CardTitle>
|
<CardTitle className="flex items-center gap-2">
|
||||||
<Thermometer className="h-4 w-4 text-muted-foreground" />
|
<Thermometer className="h-5 w-5" />
|
||||||
|
Avg Temperature
|
||||||
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className={`text-2xl font-bold ${getTempColor(avgTemp)}`}>{avgTemp > 0 ? `${avgTemp}°C` : "N/A"}</div>
|
<div className={`text-2xl font-bold ${getTempColor(avgTemp)}`}>{avgTemp > 0 ? `${avgTemp}°C` : "N/A"}</div>
|
||||||
@@ -213,6 +215,68 @@ export function StorageOverview() {
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{storageData.disks.some((disk) => disk.mountpoint) && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Database className="h-5 w-5" />
|
||||||
|
Mounted Partitions
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{storageData.disks
|
||||||
|
.filter((disk) => disk.mountpoint)
|
||||||
|
.map((disk) => (
|
||||||
|
<div key={disk.name} className="border rounded-lg p-4">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold">{disk.mountpoint}</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
/dev/{disk.name} ({disk.fstype})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{disk.usage_percent !== undefined && (
|
||||||
|
<span className="text-sm font-medium">{disk.usage_percent}%</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{disk.usage_percent !== undefined && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Progress
|
||||||
|
value={disk.usage_percent}
|
||||||
|
className={`h-2 ${
|
||||||
|
disk.usage_percent > 90
|
||||||
|
? "[&>div]:bg-red-500"
|
||||||
|
: disk.usage_percent > 75
|
||||||
|
? "[&>div]:bg-yellow-500"
|
||||||
|
: "[&>div]:bg-blue-500"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
<div className="flex justify-between text-xs text-muted-foreground">
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
disk.usage_percent > 90
|
||||||
|
? "text-red-400"
|
||||||
|
: disk.usage_percent > 75
|
||||||
|
? "text-yellow-400"
|
||||||
|
: "text-blue-400"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{disk.used} GB used
|
||||||
|
</span>
|
||||||
|
<span className="text-green-400">
|
||||||
|
{disk.available} GB free of {disk.total} GB
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ZFS Pools */}
|
{/* ZFS Pools */}
|
||||||
{storageData.zfs_pools && storageData.zfs_pools.length > 0 && (
|
{storageData.zfs_pools && storageData.zfs_pools.length > 0 && (
|
||||||
<Card>
|
<Card>
|
||||||
|
@@ -496,7 +496,7 @@ def get_storage_info():
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_smart_data(disk_name):
|
def get_smart_data(disk_name):
|
||||||
"""Get SMART data for a specific disk - Enhanced with better parsing logic from Home Assistant"""
|
"""Get SMART data for a specific disk - Enhanced with multiple device type attempts"""
|
||||||
smart_data = {
|
smart_data = {
|
||||||
'temperature': 0,
|
'temperature': 0,
|
||||||
'health': 'unknown',
|
'health': 'unknown',
|
||||||
@@ -512,159 +512,212 @@ def get_smart_data(disk_name):
|
|||||||
print(f"[v0] ===== Starting SMART data collection for /dev/{disk_name} =====")
|
print(f"[v0] ===== Starting SMART data collection for /dev/{disk_name} =====")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print(f"[v0] Step 1: Attempting JSON output for {disk_name}...")
|
commands_to_try = [
|
||||||
result = subprocess.run(['smartctl', '-a', '-j', f'/dev/{disk_name}'],
|
['smartctl', '-a', '-j', f'/dev/{disk_name}'], # JSON output (preferred)
|
||||||
capture_output=True, text=True, timeout=10)
|
['smartctl', '-a', '-j', '-d', 'ata', f'/dev/{disk_name}'], # JSON with ATA device type
|
||||||
|
['smartctl', '-a', '-j', '-d', 'sat', f'/dev/{disk_name}'], # JSON with SAT device type
|
||||||
|
['smartctl', '-a', '-j', '-d', 'scsi', f'/dev/{disk_name}'], # JSON with SCSI device type
|
||||||
|
['smartctl', '-a', f'/dev/{disk_name}'], # Text output
|
||||||
|
['smartctl', '-a', '-d', 'ata', f'/dev/{disk_name}'], # Text with ATA device type
|
||||||
|
['smartctl', '-a', '-d', 'sat', f'/dev/{disk_name}'], # Text with SAT device type
|
||||||
|
['smartctl', '-i', '-H', f'/dev/{disk_name}'], # Basic info + health only
|
||||||
|
]
|
||||||
|
|
||||||
print(f"[v0] smartctl return code: {result.returncode}")
|
for cmd_index, cmd in enumerate(commands_to_try):
|
||||||
|
print(f"[v0] Attempt {cmd_index + 1}/{len(commands_to_try)}: Running command: {' '.join(cmd)}")
|
||||||
# smartctl returns: 0=OK, 2=SMART disabled, 4=threshold exceeded (still valid), 8=error log has errors
|
|
||||||
if result.returncode in [0, 4, 8] and result.stdout:
|
|
||||||
try:
|
try:
|
||||||
print(f"[v0] Attempting JSON parse...")
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
||||||
data = json.loads(result.stdout)
|
print(f"[v0] Command return code: {result.returncode}")
|
||||||
print(f"[v0] JSON parse successful!")
|
|
||||||
|
|
||||||
if 'model_name' in data:
|
if result.stderr:
|
||||||
smart_data['model'] = data['model_name']
|
stderr_preview = result.stderr[:300].replace('\n', ' ')
|
||||||
print(f"[v0] Model: {smart_data['model']}")
|
print(f"[v0] stderr: {stderr_preview}")
|
||||||
elif 'model_family' in data:
|
|
||||||
smart_data['model'] = data['model_family']
|
|
||||||
print(f"[v0] Model family: {smart_data['model']}")
|
|
||||||
|
|
||||||
if 'serial_number' in data:
|
# smartctl returns: 0=OK, 2=SMART disabled, 4=threshold exceeded (still valid), 8=error log has errors
|
||||||
smart_data['serial'] = data['serial_number']
|
if result.returncode in [0, 2, 4, 8] and result.stdout:
|
||||||
print(f"[v0] Serial: {smart_data['serial']}")
|
print(f"[v0] Got output ({len(result.stdout)} bytes)")
|
||||||
|
|
||||||
if 'smart_status' in data and 'passed' in data['smart_status']:
|
# Try JSON parsing first
|
||||||
smart_data['smart_status'] = 'passed' if data['smart_status']['passed'] else 'failed'
|
if '-j' in cmd:
|
||||||
smart_data['health'] = 'healthy' if data['smart_status']['passed'] else 'critical'
|
try:
|
||||||
print(f"[v0] SMART status: {smart_data['smart_status']}")
|
print(f"[v0] Attempting JSON parse...")
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
print(f"[v0] JSON parse successful!")
|
||||||
|
|
||||||
if 'temperature' in data and 'current' in data['temperature']:
|
# Extract model
|
||||||
smart_data['temperature'] = data['temperature']['current']
|
if 'model_name' in data:
|
||||||
print(f"[v0] Temperature: {smart_data['temperature']}°C")
|
smart_data['model'] = data['model_name']
|
||||||
|
print(f"[v0] Model: {smart_data['model']}")
|
||||||
|
elif 'model_family' in data:
|
||||||
|
smart_data['model'] = data['model_family']
|
||||||
|
print(f"[v0] Model family: {smart_data['model']}")
|
||||||
|
|
||||||
if 'ata_smart_attributes' in data and 'table' in data['ata_smart_attributes']:
|
# Extract serial
|
||||||
print(f"[v0] Parsing ATA SMART attributes...")
|
if 'serial_number' in data:
|
||||||
for attr in data['ata_smart_attributes']['table']:
|
smart_data['serial'] = data['serial_number']
|
||||||
attr_id = attr.get('id')
|
print(f"[v0] Serial: {smart_data['serial']}")
|
||||||
raw_value = attr.get('raw', {}).get('value', 0)
|
|
||||||
|
|
||||||
# ID mapping from Home Assistant coordinator
|
# Extract SMART status
|
||||||
if attr_id == 9: # Power_On_Hours
|
if 'smart_status' in data and 'passed' in data['smart_status']:
|
||||||
smart_data['power_on_hours'] = raw_value
|
smart_data['smart_status'] = 'passed' if data['smart_status']['passed'] else 'failed'
|
||||||
print(f"[v0] Power On Hours (ID 9): {raw_value}")
|
smart_data['health'] = 'healthy' if data['smart_status']['passed'] else 'critical'
|
||||||
elif attr_id == 194: # Temperature_Celsius
|
print(f"[v0] SMART status: {smart_data['smart_status']}, health: {smart_data['health']}")
|
||||||
if smart_data['temperature'] == 0:
|
|
||||||
smart_data['temperature'] = raw_value
|
|
||||||
print(f"[v0] Temperature (ID 194): {raw_value}°C")
|
|
||||||
elif attr_id == 190: # Airflow_Temperature_Cel
|
|
||||||
if smart_data['temperature'] == 0:
|
|
||||||
smart_data['temperature'] = raw_value
|
|
||||||
print(f"[v0] Airflow Temperature (ID 190): {raw_value}°C")
|
|
||||||
elif attr_id == 5: # Reallocated_Sector_Ct
|
|
||||||
smart_data['reallocated_sectors'] = raw_value
|
|
||||||
print(f"[v0] Reallocated Sectors (ID 5): {raw_value}")
|
|
||||||
elif attr_id == 197: # Current_Pending_Sector
|
|
||||||
smart_data['pending_sectors'] = raw_value
|
|
||||||
print(f"[v0] Pending Sectors (ID 197): {raw_value}")
|
|
||||||
elif attr_id == 199: # UDMA_CRC_Error_Count
|
|
||||||
smart_data['crc_errors'] = raw_value
|
|
||||||
print(f"[v0] CRC Errors (ID 199): {raw_value}")
|
|
||||||
|
|
||||||
if 'nvme_smart_health_information_log' in data:
|
# Extract temperature
|
||||||
print(f"[v0] Parsing NVMe SMART data...")
|
if 'temperature' in data and 'current' in data['temperature']:
|
||||||
nvme_data = data['nvme_smart_health_information_log']
|
smart_data['temperature'] = data['temperature']['current']
|
||||||
if 'temperature' in nvme_data:
|
print(f"[v0] Temperature: {smart_data['temperature']}°C")
|
||||||
smart_data['temperature'] = nvme_data['temperature']
|
|
||||||
print(f"[v0] NVMe Temperature: {smart_data['temperature']}°C")
|
|
||||||
if 'power_on_hours' in nvme_data:
|
|
||||||
smart_data['power_on_hours'] = nvme_data['power_on_hours']
|
|
||||||
print(f"[v0] NVMe Power On Hours: {smart_data['power_on_hours']}")
|
|
||||||
|
|
||||||
except json.JSONDecodeError as e:
|
# Parse ATA SMART attributes
|
||||||
print(f"[v0] JSON parse failed: {e}, falling back to text parsing...")
|
if 'ata_smart_attributes' in data and 'table' in data['ata_smart_attributes']:
|
||||||
output = result.stdout
|
print(f"[v0] Parsing ATA SMART attributes...")
|
||||||
|
for attr in data['ata_smart_attributes']['table']:
|
||||||
|
attr_id = attr.get('id')
|
||||||
|
raw_value = attr.get('raw', {}).get('value', 0)
|
||||||
|
|
||||||
# Get basic info
|
# ID mapping from Home Assistant coordinator
|
||||||
for line in output.split('\n'):
|
if attr_id == 9: # Power_On_Hours
|
||||||
line = line.strip()
|
smart_data['power_on_hours'] = raw_value
|
||||||
if line.startswith('Device Model:') or line.startswith('Model Number:'):
|
print(f"[v0] Power On Hours (ID 9): {raw_value}")
|
||||||
smart_data['model'] = line.split(':', 1)[1].strip()
|
elif attr_id == 194: # Temperature_Celsius
|
||||||
elif line.startswith('Serial Number:'):
|
if smart_data['temperature'] == 0:
|
||||||
smart_data['serial'] = line.split(':', 1)[1].strip()
|
smart_data['temperature'] = raw_value
|
||||||
elif line.startswith('Model Family:') and smart_data['model'] == 'Unknown':
|
print(f"[v0] Temperature (ID 194): {raw_value}°C")
|
||||||
smart_data['model'] = line.split(':', 1)[1].strip()
|
elif attr_id == 190: # Airflow_Temperature_Cel
|
||||||
|
if smart_data['temperature'] == 0:
|
||||||
|
smart_data['temperature'] = raw_value
|
||||||
|
print(f"[v0] Airflow Temperature (ID 190): {raw_value}°C")
|
||||||
|
elif attr_id == 5: # Reallocated_Sector_Ct
|
||||||
|
smart_data['reallocated_sectors'] = raw_value
|
||||||
|
print(f"[v0] Reallocated Sectors (ID 5): {raw_value}")
|
||||||
|
elif attr_id == 197: # Current_Pending_Sector
|
||||||
|
smart_data['pending_sectors'] = raw_value
|
||||||
|
print(f"[v0] Pending Sectors (ID 197): {raw_value}")
|
||||||
|
elif attr_id == 199: # UDMA_CRC_Error_Count
|
||||||
|
smart_data['crc_errors'] = raw_value
|
||||||
|
print(f"[v0] CRC Errors (ID 199): {raw_value}")
|
||||||
|
|
||||||
# Parse SMART status
|
# Parse NVMe SMART data
|
||||||
if 'SMART overall-health self-assessment test result: PASSED' in output:
|
if 'nvme_smart_health_information_log' in data:
|
||||||
smart_data['smart_status'] = 'passed'
|
print(f"[v0] Parsing NVMe SMART data...")
|
||||||
smart_data['health'] = 'healthy'
|
nvme_data = data['nvme_smart_health_information_log']
|
||||||
elif 'SMART Health Status: OK' in output: # NVMe
|
if 'temperature' in nvme_data:
|
||||||
smart_data['smart_status'] = 'passed'
|
smart_data['temperature'] = nvme_data['temperature']
|
||||||
smart_data['health'] = 'healthy'
|
print(f"[v0] NVMe Temperature: {smart_data['temperature']}°C")
|
||||||
elif 'SMART overall-health self-assessment test result: FAILED' in output:
|
if 'power_on_hours' in nvme_data:
|
||||||
smart_data['smart_status'] = 'failed'
|
smart_data['power_on_hours'] = nvme_data['power_on_hours']
|
||||||
smart_data['health'] = 'critical'
|
print(f"[v0] NVMe Power On Hours: {smart_data['power_on_hours']}")
|
||||||
|
|
||||||
in_attributes = False
|
# If we got good data, break out of the loop
|
||||||
for line in output.split('\n'):
|
if smart_data['model'] != 'Unknown' or smart_data['serial'] != 'Unknown':
|
||||||
line = line.strip()
|
print(f"[v0] Successfully extracted data from JSON (attempt {cmd_index + 1})")
|
||||||
|
break
|
||||||
|
|
||||||
if 'ID# ATTRIBUTE_NAME' in line:
|
except json.JSONDecodeError as e:
|
||||||
in_attributes = True
|
print(f"[v0] JSON parse failed: {e}, will try next command...")
|
||||||
continue
|
|
||||||
|
|
||||||
if in_attributes and line and not line.startswith('SMART'):
|
# Text parsing fallback
|
||||||
parts = line.split()
|
if smart_data['model'] == 'Unknown' or smart_data['serial'] == 'Unknown':
|
||||||
if len(parts) >= 10:
|
print(f"[v0] Parsing text output...")
|
||||||
try:
|
output = result.stdout
|
||||||
attr_id = parts[0]
|
|
||||||
raw_value = parts[9]
|
|
||||||
|
|
||||||
# Same ID mapping as JSON parsing
|
# Get basic info
|
||||||
if attr_id == '9': # Power On Hours
|
for line in output.split('\n'):
|
||||||
# Handle different formats: "12345", "12345h", "12345 hours"
|
line = line.strip()
|
||||||
raw_clean = raw_value.split()[0].replace('h', '')
|
if line.startswith('Device Model:') or line.startswith('Model Number:'):
|
||||||
smart_data['power_on_hours'] = int(raw_clean)
|
smart_data['model'] = line.split(':', 1)[1].strip()
|
||||||
elif attr_id == '194': # Temperature
|
print(f"[v0] Found model: {smart_data['model']}")
|
||||||
temp_str = raw_value.split()[0]
|
elif line.startswith('Serial Number:'):
|
||||||
smart_data['temperature'] = int(temp_str)
|
smart_data['serial'] = line.split(':', 1)[1].strip()
|
||||||
elif attr_id == '190': # Airflow Temperature
|
print(f"[v0] Found serial: {smart_data['serial']}")
|
||||||
if smart_data['temperature'] == 0:
|
elif line.startswith('Model Family:') and smart_data['model'] == 'Unknown':
|
||||||
temp_str = raw_value.split()[0]
|
smart_data['model'] = line.split(':', 1)[1].strip()
|
||||||
smart_data['temperature'] = int(temp_str)
|
print(f"[v0] Found model family: {smart_data['model']}")
|
||||||
elif attr_id == '5': # Reallocated Sectors
|
|
||||||
smart_data['reallocated_sectors'] = int(raw_value)
|
|
||||||
elif attr_id == '197': # Pending Sectors
|
|
||||||
smart_data['pending_sectors'] = int(raw_value)
|
|
||||||
elif attr_id == '199': # CRC Errors
|
|
||||||
smart_data['crc_errors'] = int(raw_value)
|
|
||||||
|
|
||||||
except (ValueError, IndexError) as e:
|
# Parse SMART status
|
||||||
|
if 'SMART overall-health self-assessment test result: PASSED' in output:
|
||||||
|
smart_data['smart_status'] = 'passed'
|
||||||
|
smart_data['health'] = 'healthy'
|
||||||
|
print(f"[v0] SMART status: PASSED")
|
||||||
|
elif 'SMART Health Status: OK' in output: # NVMe
|
||||||
|
smart_data['smart_status'] = 'passed'
|
||||||
|
smart_data['health'] = 'healthy'
|
||||||
|
print(f"[v0] NVMe Health: OK")
|
||||||
|
elif 'SMART overall-health self-assessment test result: FAILED' in output:
|
||||||
|
smart_data['smart_status'] = 'failed'
|
||||||
|
smart_data['health'] = 'critical'
|
||||||
|
print(f"[v0] SMART status: FAILED")
|
||||||
|
|
||||||
|
# Parse SMART attributes table
|
||||||
|
in_attributes = False
|
||||||
|
for line in output.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
if 'ID# ATTRIBUTE_NAME' in line:
|
||||||
|
in_attributes = True
|
||||||
|
print(f"[v0] Found SMART attributes table")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if smart_data['temperature'] == 0:
|
if in_attributes and line and not line.startswith('SMART'):
|
||||||
for line in output.split('\n'):
|
parts = line.split()
|
||||||
if 'Temperature:' in line:
|
if len(parts) >= 10:
|
||||||
try:
|
try:
|
||||||
temp_str = line.split(':')[1].strip().split()[0]
|
attr_id = parts[0]
|
||||||
smart_data['temperature'] = int(temp_str)
|
raw_value = parts[9]
|
||||||
except (ValueError, IndexError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
# Same ID mapping as JSON parsing
|
||||||
print(f"[v0] JSON command failed, trying basic info...")
|
if attr_id == '9': # Power On Hours
|
||||||
result_info = subprocess.run(['smartctl', '-i', f'/dev/{disk_name}'],
|
# Handle different formats: "12345", "12345h", "12345 hours"
|
||||||
capture_output=True, text=True, timeout=10)
|
raw_clean = raw_value.split()[0].replace('h', '')
|
||||||
if result_info.stdout:
|
smart_data['power_on_hours'] = int(raw_clean)
|
||||||
for line in result_info.stdout.split('\n'):
|
print(f"[v0] Power On Hours: {smart_data['power_on_hours']}")
|
||||||
line = line.strip()
|
elif attr_id == '194': # Temperature
|
||||||
if line.startswith('Device Model:') or line.startswith('Model Number:'):
|
temp_str = raw_value.split()[0]
|
||||||
smart_data['model'] = line.split(':', 1)[1].strip()
|
smart_data['temperature'] = int(temp_str)
|
||||||
elif line.startswith('Serial Number:'):
|
print(f"[v0] Temperature: {smart_data['temperature']}°C")
|
||||||
smart_data['serial'] = line.split(':', 1)[1].strip()
|
elif attr_id == '190': # Airflow Temperature
|
||||||
|
if smart_data['temperature'] == 0:
|
||||||
|
temp_str = raw_value.split()[0]
|
||||||
|
smart_data['temperature'] = int(temp_str)
|
||||||
|
print(f"[v0] Airflow Temperature: {smart_data['temperature']}°C")
|
||||||
|
elif attr_id == '5': # Reallocated Sectors
|
||||||
|
smart_data['reallocated_sectors'] = int(raw_value)
|
||||||
|
print(f"[v0] Reallocated Sectors: {smart_data['reallocated_sectors']}")
|
||||||
|
elif attr_id == '197': # Pending Sectors
|
||||||
|
smart_data['pending_sectors'] = int(raw_value)
|
||||||
|
print(f"[v0] Pending Sectors: {smart_data['pending_sectors']}")
|
||||||
|
elif attr_id == '199': # CRC Errors
|
||||||
|
smart_data['crc_errors'] = int(raw_value)
|
||||||
|
print(f"[v0] CRC Errors: {smart_data['crc_errors']}")
|
||||||
|
|
||||||
|
except (ValueError, IndexError) as e:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Try to find temperature in other formats
|
||||||
|
if smart_data['temperature'] == 0:
|
||||||
|
for line in output.split('\n'):
|
||||||
|
if 'Temperature:' in line or 'Temperature_Celsius' in line:
|
||||||
|
try:
|
||||||
|
temp_str = line.split(':')[1].strip().split()[0]
|
||||||
|
smart_data['temperature'] = int(temp_str)
|
||||||
|
print(f"[v0] Found temperature: {smart_data['temperature']}°C")
|
||||||
|
break
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# If we got some data, break
|
||||||
|
if smart_data['model'] != 'Unknown' or smart_data['serial'] != 'Unknown':
|
||||||
|
print(f"[v0] Successfully extracted data from text output (attempt {cmd_index + 1})")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"[v0] Command failed with return code {result.returncode}, trying next...")
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print(f"[v0] Command timeout for attempt {cmd_index + 1}, trying next...")
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[v0] Error in attempt {cmd_index + 1}: {type(e).__name__}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
if smart_data['reallocated_sectors'] > 0 or smart_data['pending_sectors'] > 0:
|
if smart_data['reallocated_sectors'] > 0 or smart_data['pending_sectors'] > 0:
|
||||||
smart_data['health'] = 'warning'
|
smart_data['health'] = 'warning'
|
||||||
@@ -685,10 +738,10 @@ def get_smart_data(disk_name):
|
|||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"[v0] ERROR: smartctl not found - install smartmontools")
|
print(f"[v0] ERROR: smartctl not found - install smartmontools")
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print(f"[v0] ERROR: Timeout getting SMART data for {disk_name}")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[v0] ERROR: Exception for {disk_name}: {type(e).__name__}: {e}")
|
print(f"[v0] ERROR: Unexpected exception for {disk_name}: {type(e).__name__}: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
print(f"[v0] ===== Final SMART data for /dev/{disk_name}: {smart_data} =====")
|
print(f"[v0] ===== Final SMART data for /dev/{disk_name}: {smart_data} =====")
|
||||||
return smart_data
|
return smart_data
|
||||||
|
Reference in New Issue
Block a user