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>
113 lines
3.5 KiB
Python
Executable File
113 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Explore Scrutiny's InfluxDB schema to understand how SMART data is stored.
|
|
Run this first to understand the data structure before running the main logger.
|
|
"""
|
|
|
|
import os
|
|
from influxdb_client import InfluxDBClient
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
def explore_influxdb():
|
|
"""Connect to InfluxDB and explore the schema."""
|
|
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}")
|
|
print(f"Organization: {org}")
|
|
print(f"Bucket: {bucket}\n")
|
|
|
|
client = InfluxDBClient(url=url, token=token, org=org)
|
|
query_api = client.query_api()
|
|
|
|
# 1. List all measurements in the bucket
|
|
print("=" * 80)
|
|
print("STEP 1: Discovering measurements in bucket")
|
|
print("=" * 80)
|
|
|
|
query_measurements = f'''
|
|
import "influxdata/influxdb/schema"
|
|
schema.measurements(bucket: "{bucket}")
|
|
'''
|
|
|
|
try:
|
|
result = query_api.query(query_measurements)
|
|
measurements = []
|
|
for table in result:
|
|
for record in table.records:
|
|
measurement = record.get_value()
|
|
measurements.append(measurement)
|
|
print(f" - {measurement}")
|
|
|
|
if not measurements:
|
|
print(" No measurements found!")
|
|
return
|
|
|
|
print(f"\nFound {len(measurements)} measurement(s)\n")
|
|
|
|
# 2. For each measurement, show field keys
|
|
print("=" * 80)
|
|
print("STEP 2: Discovering fields for each measurement")
|
|
print("=" * 80)
|
|
|
|
for measurement in measurements[:5]: # Limit to first 5 measurements
|
|
print(f"\nMeasurement: {measurement}")
|
|
query_fields = f'''
|
|
import "influxdata/influxdb/schema"
|
|
schema.measurementFieldKeys(
|
|
bucket: "{bucket}",
|
|
measurement: "{measurement}"
|
|
)
|
|
'''
|
|
|
|
result = query_api.query(query_fields)
|
|
for table in result:
|
|
for record in table.records:
|
|
field = record.get_value()
|
|
print(f" Field: {field}")
|
|
|
|
# 3. Show sample data from the first measurement
|
|
print("\n" + "=" * 80)
|
|
print("STEP 3: Sample data from first measurement")
|
|
print("=" * 80)
|
|
|
|
first_measurement = measurements[0]
|
|
query_sample = f'''
|
|
from(bucket: "{bucket}")
|
|
|> range(start: -24h)
|
|
|> filter(fn: (r) => r._measurement == "{first_measurement}")
|
|
|> limit(n: 10)
|
|
'''
|
|
|
|
print(f"\nSample records from '{first_measurement}':\n")
|
|
result = query_api.query(query_sample)
|
|
for table in result:
|
|
for record in table.records:
|
|
print(f"Time: {record.get_time()}")
|
|
print(f" Measurement: {record.get_measurement()}")
|
|
print(f" Field: {record.get_field()}")
|
|
print(f" Value: {record.get_value()}")
|
|
print(f" Tags: {record.values}")
|
|
print()
|
|
|
|
except Exception as e:
|
|
print(f"Error querying InfluxDB: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
finally:
|
|
client.close()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if not os.getenv('INFLUXDB_URL'):
|
|
print("ERROR: Please create a .env file with your InfluxDB configuration")
|
|
print("Copy .env.example to .env and fill in your details")
|
|
exit(1)
|
|
|
|
explore_influxdb()
|