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>
90 lines
2.7 KiB
Python
Executable File
90 lines
2.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Helper script to extract WWN to device mappings from InfluxDB.
|
|
Run this to help populate the devices table.
|
|
"""
|
|
|
|
import os
|
|
from influxdb_client import InfluxDBClient
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
def get_device_info():
|
|
"""Extract unique device WWNs and their latest data from InfluxDB."""
|
|
url = os.getenv('INFLUXDB_URL')
|
|
token = os.getenv('INFLUXDB_TOKEN')
|
|
org = os.getenv('INFLUXDB_ORG', 'scrutiny')
|
|
bucket = os.getenv('INFLUXDB_BUCKET', 'metrics')
|
|
|
|
print(f"Connecting to InfluxDB at {url}...")
|
|
client = InfluxDBClient(url=url, token=token, org=org)
|
|
query_api = client.query_api()
|
|
|
|
# Get unique device WWNs with their latest data
|
|
query = f'''
|
|
from(bucket: "{bucket}")
|
|
|> range(start: -24h)
|
|
|> filter(fn: (r) => r._measurement == "smart")
|
|
|> filter(fn: (r) =>
|
|
r._field == "temp" or
|
|
r._field == "power_on_hours"
|
|
)
|
|
|> last()
|
|
|> group(columns: ["device_wwn", "device_protocol"])
|
|
'''
|
|
|
|
print("\nDiscovered devices:\n")
|
|
print("=" * 80)
|
|
|
|
result = query_api.query(query)
|
|
devices = {}
|
|
|
|
for table in result:
|
|
for record in table.records:
|
|
wwn = record.values.get('device_wwn')
|
|
protocol = record.values.get('device_protocol')
|
|
|
|
if wwn not in devices:
|
|
devices[wwn] = {
|
|
'wwn': wwn,
|
|
'protocol': protocol,
|
|
'temp': None,
|
|
'power_on_hours': None
|
|
}
|
|
|
|
field = record.get_field()
|
|
if field == 'temp':
|
|
devices[wwn]['temp'] = record.get_value()
|
|
elif field == 'power_on_hours':
|
|
devices[wwn]['power_on_hours'] = record.get_value()
|
|
|
|
# Print devices
|
|
for i, (wwn, info) in enumerate(sorted(devices.items()), 1):
|
|
power_on_days = int(info['power_on_hours']) // 24 if info['power_on_hours'] else 0
|
|
|
|
print(f"Device {i}:")
|
|
print(f" WWN: {wwn}")
|
|
print(f" Protocol: {info['protocol']}")
|
|
print(f" Temperature: {info['temp']}°C")
|
|
print(f" Power-On Time: {power_on_days} days ({info['power_on_hours']} hours)")
|
|
print()
|
|
|
|
print("=" * 80)
|
|
print("\nNow run 'snapraid smart' or 'smartctl -a /dev/sdX' on each device to get:")
|
|
print(" - Serial number")
|
|
print(" - Model name")
|
|
print(" - Capacity")
|
|
print(" - Device path (/dev/sdX)")
|
|
print("\nThen update populate_devices.sql with this information.")
|
|
|
|
client.close()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if not os.getenv('INFLUXDB_URL'):
|
|
print("ERROR: Please create a .env file with your InfluxDB configuration")
|
|
exit(1)
|
|
|
|
get_device_info()
|