Files
snapraid_smart_logging/debug_scrutiny_failure_rate.py
WesandClaude Sonnet 4.5 27afedb32e 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>
2025-12-07 06:55:39 -05:00

69 lines
1.9 KiB
Python

#!/usr/bin/env python3
"""
Check if Scrutiny has failure_rate fields we should be using
"""
import os
from influxdb_client import InfluxDBClient
from dotenv import load_dotenv
load_dotenv()
def check_scrutiny_failure_rates():
"""Check Scrutiny's own failure rate calculations."""
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 failure_rate fields
query = f'''
from(bucket: "{bucket}")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "smart")
|> filter(fn: (r) => r._field =~ /failure_rate/)
|> last()
'''
print("=" * 80)
print("Scrutiny's Failure Rate Calculations")
print("=" * 80)
result = query_api.query(query)
devices = {}
for table in result:
for record in table.records:
wwn = record.values.get('device_wwn')
field = record.get_field()
value = record.get_value()
if wwn not in devices:
devices[wwn] = {}
# Extract attribute ID
parts = field.split('.')
if len(parts) >= 2:
attr_id = parts[1]
devices[wwn][attr_id] = value
# Show per device
for wwn, attrs in sorted(devices.items()):
print(f"\nDevice {wwn}:")
max_rate = 0
for attr_id, rate in sorted(attrs.items(), key=lambda x: int(x[0])):
if rate and rate > 0:
print(f" Attr {attr_id:3s}: {rate:.4f}")
max_rate = max(max_rate, rate)
if max_rate > 0:
print(f" MAX: {max_rate:.4f} ({max_rate*100:.2f}%)")
client.close()
if __name__ == '__main__':
check_scrutiny_failure_rates()