""" Disk Space Monitor Checks disk usage on: - /mnt/ssd (Hub Dati - 1TB SSD) - /mnt/backup-hdd (Cassaforte - 5TB HDD) - / (root filesystem) """ import subprocess import re from typing import Dict, List class DiskMonitor: """Monitor disk space usage""" PATHS = [ {'path': '/mnt/ssd', 'name': 'Hub SSD', 'warn': 85, 'crit': 95}, {'path': '/mnt/backup-hdd', 'name': 'Cassaforte HDD', 'warn': 90, 'crit': 95}, {'path': '/', 'name': 'Root', 'warn': 80, 'crit': 90} ] def check(self) -> Dict: """ Check disk usage. Returns: { 'status': 'healthy' | 'warning' | 'critical', 'disks': [ { 'name': 'Hub SSD', 'path': '/mnt/ssd', 'used_percent': 71, 'used_gb': 460, 'total_gb': 650, 'status': 'healthy' }, ... ], 'message': '...' } """ try: # Run: df -h result = subprocess.run( ['df', '-h'], capture_output=True, text=True, timeout=5 ) disks = [] overall_status = 'healthy' for disk_config in self.PATHS: disk_data = self._parse_df_for_path(result.stdout, disk_config['path']) if disk_data: # Determine disk status usage = disk_data['used_percent'] if usage >= disk_config['crit']: disk_status = 'critical' overall_status = 'critical' elif usage >= disk_config['warn']: disk_status = 'warning' if overall_status == 'healthy': overall_status = 'warning' else: disk_status = 'healthy' disks.append({ 'name': disk_config['name'], 'path': disk_config['path'], 'used_percent': usage, 'used_gb': disk_data['used_gb'], 'total_gb': disk_data['total_gb'], 'free_gb': disk_data['free_gb'], 'status': disk_status }) return { 'status': overall_status, 'disks': disks, 'message': self._create_message(disks, overall_status) } except Exception as e: return { 'status': 'warning', 'error': str(e), 'message': f'Disk check error: {e}' } def _parse_df_for_path(self, df_output: str, path: str) -> Dict: """Parse df output for specific path""" for line in df_output.split('\n'): if line.endswith(path): # Format: Filesystem Size Used Avail Use% Mounted parts = line.split() if len(parts) >= 6: size_str = parts[1] used_str = parts[2] avail_str = parts[3] use_percent_str = parts[4].rstrip('%') try: return { 'total_gb': self._convert_to_gb(size_str), 'used_gb': self._convert_to_gb(used_str), 'free_gb': self._convert_to_gb(avail_str), 'used_percent': int(use_percent_str) } except ValueError: pass return None def _convert_to_gb(self, size_str: str) -> int: """Convert size string (e.g., '123G', '4.5T') to GB""" if size_str.endswith('T'): return int(float(size_str[:-1]) * 1024) elif size_str.endswith('G'): return int(float(size_str[:-1])) elif size_str.endswith('M'): return int(float(size_str[:-1]) / 1024) else: return 0 def _create_message(self, disks: List[Dict], overall_status: str) -> str: """Create human-readable message""" if not disks: return "No disk data available" parts = [] for disk in disks: parts.append(f"{disk['name']}: {disk['used_percent']}%") return ", ".join(parts)