""" Restic Backup Monitor (Lightweight) Checks last backup snapshot age by examining file modification times. Uses: filesystem checks only (no restic command) Repository: /mnt/backup-hdd/flotta-backup """ from pathlib import Path from datetime import datetime, timezone from typing import Dict class BackupMonitor: """Monitor Restic backup status (lightweight file-based check)""" SNAPSHOT_DIR = Path('/mnt/backup-hdd/flotta-backup/snapshots') def check(self) -> Dict: """ Check backup status by examining snapshot files. Tests: 1. Snapshot directory accessible 2. Last snapshot file < 30h old (daily at 3 AM, allow margin) 3. Snapshot file count reasonable Returns: { 'status': 'healthy' | 'warning' | 'critical', 'last_snapshot_age_hours': X, 'snapshot_count': Y, 'last_snapshot_date': '...', 'message': '...' } """ try: # Check directory exists and is accessible if not self.SNAPSHOT_DIR.exists(): return { 'status': 'critical', 'message': 'Snapshot directory not found' } if not self.SNAPSHOT_DIR.is_dir(): return { 'status': 'critical', 'message': 'Snapshot path is not a directory' } # List all snapshot files (exclude . and ..) snapshot_files = [ f for f in self.SNAPSHOT_DIR.iterdir() if f.is_file() and not f.name.startswith('.') ] if not snapshot_files: return { 'status': 'critical', 'snapshot_count': 0, 'message': 'No backup snapshots found' } # Find most recent snapshot by modification time latest_snapshot = max(snapshot_files, key=lambda f: f.stat().st_mtime) # Get modification time mtime = latest_snapshot.stat().st_mtime last_time = datetime.fromtimestamp(mtime, tz=timezone.utc) # Calculate age now = datetime.now(timezone.utc) age_hours = (now - last_time).total_seconds() / 3600 # Determine status (daily backup at 3 AM, allow 30h margin) if age_hours < 30: status = 'healthy' elif age_hours < 48: status = 'warning' else: status = 'critical' return { 'status': status, 'last_snapshot_age_hours': round(age_hours, 1), 'snapshot_count': len(snapshot_files), 'last_snapshot_date': last_time.strftime('%Y-%m-%d %H:%M'), 'message': f"Last backup {round(age_hours)}h ago ({len(snapshot_files)} snapshots)" } except PermissionError: return { 'status': 'warning', 'message': 'Permission denied accessing snapshots directory' } except Exception as e: return { 'status': 'warning', 'error': str(e), 'message': f'Backup check error: {str(e)[:50]}' }