Complete rewrite: SnapRAID SMART logger with BackBlaze algorithm
Fetches SMART data from Scrutiny's InfluxDB and calculates failure probabilities using the exact BackBlaze tables from SnapRAID source. Key features: - Calculates both v12 (with attr 193) and v13 (without attr 193) algorithms - v12 matches SnapRAID pre-v13.0 (includes Load Cycle Count) - v13 matches modern SnapRAID v13.0+ (excludes Load Cycle Count) - Stores both values for trending and comparison - Correctly applies bit masks (16-bit for 187/188, 32-bit for others) - Annualizes monthly rates and applies Poisson distribution - Logs to PostgreSQL with device metadata Solved the mystery: SnapRAID v12 uses attribute 193 which was removed in v13.0. Load cycle count has massive impact on failure predictions for some drives (80% vs 4% difference). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debug script to see actual SMART attribute values from InfluxDB
|
||||
"""
|
||||
|
||||
import os
|
||||
from influxdb_client import InfluxDBClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
def debug_smart_values():
|
||||
"""Fetch and display raw SMART values for debugging."""
|
||||
url = os.getenv('INFLUXDB_URL')
|
||||
token = os.getenv('INFLUXDB_TOKEN')
|
||||
org = os.getenv('INFLUXDB_ORG', 'scrutiny')
|
||||
bucket = os.getenv('INFLUXDB_BUCKET', 'metrics')
|
||||
|
||||
client = InfluxDBClient(url=url, token=token, org=org)
|
||||
query_api = client.query_api()
|
||||
|
||||
# Get all SMART attribute fields for one device
|
||||
query = f'''
|
||||
from(bucket: "{bucket}")
|
||||
|> range(start: -1h)
|
||||
|> filter(fn: (r) => r._measurement == "smart")
|
||||
|> filter(fn: (r) => r.device_wwn == "0x5000c500c570663e")
|
||||
|> filter(fn: (r) =>
|
||||
r._field =~ /attr\\.5\\.(raw_value|transformed_value|value)/ or
|
||||
r._field =~ /attr\\.187\\.(raw_value|transformed_value|value)/ or
|
||||
r._field =~ /attr\\.188\\.(raw_value|transformed_value|value)/ or
|
||||
r._field =~ /attr\\.197\\.(raw_value|transformed_value|value)/ or
|
||||
r._field =~ /attr\\.198\\.(raw_value|transformed_value|value)/ or
|
||||
r._field =~ /attr\\.9\\.(raw_value|transformed_value|value)/
|
||||
)
|
||||
|> last()
|
||||
'''
|
||||
|
||||
print("=" * 80)
|
||||
print("SMART Attribute Values for /dev/sdc (ZLW0A3QJ) - Should be 81% failure")
|
||||
print("=" * 80)
|
||||
|
||||
result = query_api.query(query)
|
||||
|
||||
values = {}
|
||||
for table in result:
|
||||
for record in table.records:
|
||||
field = record.get_field()
|
||||
value = record.get_value()
|
||||
values[field] = value
|
||||
|
||||
# Group by attribute
|
||||
attrs = {}
|
||||
for field, value in sorted(values.items()):
|
||||
parts = field.split('.')
|
||||
if len(parts) >= 3:
|
||||
attr_id = parts[1]
|
||||
field_type = parts[2]
|
||||
|
||||
if attr_id not in attrs:
|
||||
attrs[attr_id] = {}
|
||||
attrs[attr_id][field_type] = value
|
||||
|
||||
# Display
|
||||
for attr_id in sorted(attrs.keys(), key=int):
|
||||
print(f"\nAttribute {attr_id}:")
|
||||
attr_data = attrs[attr_id]
|
||||
|
||||
raw = attr_data.get('raw_value', 'N/A')
|
||||
transformed = attr_data.get('transformed_value', 'N/A')
|
||||
value = attr_data.get('value', 'N/A')
|
||||
|
||||
print(f" raw_value: {raw}")
|
||||
if isinstance(raw, int):
|
||||
print(f" hex: 0x{raw:012x}")
|
||||
print(f" low byte: {raw & 0xFF}")
|
||||
print(f" low word (16-bit): {raw & 0xFFFF}")
|
||||
print(f" low 32-bit: {raw & 0xFFFFFFFF}")
|
||||
print(f" transformed_value: {transformed}")
|
||||
print(f" value: {value}")
|
||||
|
||||
client.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
debug_smart_values()
|
||||
Reference in New Issue
Block a user