""" Status command implementation for WiFi-DensePose API """ import asyncio import json import psutil import time from datetime import datetime, timedelta from pathlib import Path from typing import Dict, Any, Optional from src.config.settings import Settings from src.logger import get_logger logger = get_logger(__name__) async def status_command( settings: Settings, output_format: str = "text", detailed: bool = False ) -> None: """Show the status of the WiFi-DensePose API server.""" logger.debug("Gathering server status information...") try: # Collect status information status_data = await _collect_status_data(settings, detailed) # Output status if output_format == "json": print(json.dumps(status_data, indent=2, default=str)) else: _print_text_status(status_data, detailed) except Exception as e: logger.error(f"Failed to get status: {e}") raise async def _collect_status_data(settings: Settings, detailed: bool) -> Dict[str, Any]: """Collect comprehensive status data.""" status_data = { "timestamp": datetime.utcnow().isoformat(), "server": await _get_server_status(settings), "system": _get_system_status(), "configuration": _get_configuration_status(settings), } if detailed: status_data.update({ "database": await _get_database_status(settings), "background_tasks": await _get_background_tasks_status(settings), "resources": _get_resource_usage(), "health": await _get_health_status(settings), }) return status_data async def _get_server_status(settings: Settings) -> Dict[str, Any]: """Get server process status.""" from src.commands.stop import get_server_status status = get_server_status(settings) server_info = { "running": status["running"], "pid": status["pid"], "pid_file": status["pid_file"], "pid_file_exists": status["pid_file_exists"], } if status["running"] and status["pid"]: try: # Get process information process = psutil.Process(status["pid"]) server_info.update({ "start_time": datetime.fromtimestamp(process.create_time()).isoformat(), "uptime_seconds": time.time() - process.create_time(), "memory_usage_mb": process.memory_info().rss / (1024 * 1024), "cpu_percent": process.cpu_percent(), "status": process.status(), "num_threads": process.num_threads(), "connections": len(process.connections()) if hasattr(process, 'connections') else None, }) except (psutil.NoSuchProcess, psutil.AccessDenied) as e: server_info["error"] = f"Cannot access process info: {e}" return server_info def _get_system_status() -> Dict[str, Any]: """Get system status information.""" uname_info = psutil.os.uname() return { "hostname": uname_info.nodename, "platform": uname_info.sysname, "architecture": uname_info.machine, "python_version": f"{psutil.sys.version_info.major}.{psutil.sys.version_info.minor}.{psutil.sys.version_info.micro}", "boot_time": datetime.fromtimestamp(psutil.boot_time()).isoformat(), "uptime_seconds": time.time() - psutil.boot_time(), } def _get_configuration_status(settings: Settings) -> Dict[str, Any]: """Get configuration status.""" return { "environment": settings.environment, "debug": settings.debug, "version": settings.version, "host": settings.host, "port": settings.port, "database_configured": bool(settings.database_url or (settings.db_host and settings.db_name)), "redis_enabled": settings.redis_enabled, "monitoring_enabled": settings.monitoring_interval_seconds > 0, "cleanup_enabled": settings.cleanup_interval_seconds > 0, "backup_enabled": settings.backup_interval_seconds > 0, } async def _get_database_status(settings: Settings) -> Dict[str, Any]: """Get database status.""" db_status = { "connected": False, "connection_pool": None, "tables": {}, "error": None, } try: from src.database.connection import get_database_manager db_manager = get_database_manager(settings) # Test connection await db_manager.test_connection() db_status["connected"] = True # Get connection stats connection_stats = await db_manager.get_connection_stats() db_status["connection_pool"] = connection_stats # Get table counts async with db_manager.get_async_session() as session: from sqlalchemy import text, func from src.database.models import Device, Session, CSIData, PoseDetection, SystemMetric, AuditLog tables = { "devices": Device, "sessions": Session, "csi_data": CSIData, "pose_detections": PoseDetection, "system_metrics": SystemMetric, "audit_logs": AuditLog, } for table_name, model in tables.items(): try: result = await session.execute( text(f"SELECT COUNT(*) FROM {table_name}") ) count = result.scalar() db_status["tables"][table_name] = {"count": count} except Exception as e: db_status["tables"][table_name] = {"error": str(e)} except Exception as e: db_status["error"] = str(e) return db_status async def _get_background_tasks_status(settings: Settings) -> Dict[str, Any]: """Get background tasks status.""" tasks_status = {} try: # Cleanup tasks from src.tasks.cleanup import get_cleanup_manager cleanup_manager = get_cleanup_manager(settings) tasks_status["cleanup"] = cleanup_manager.get_stats() except Exception as e: tasks_status["cleanup"] = {"error": str(e)} try: # Monitoring tasks from src.tasks.monitoring import get_monitoring_manager monitoring_manager = get_monitoring_manager(settings) tasks_status["monitoring"] = monitoring_manager.get_stats() except Exception as e: tasks_status["monitoring"] = {"error": str(e)} try: # Backup tasks from src.tasks.backup import get_backup_manager backup_manager = get_backup_manager(settings) tasks_status["backup"] = backup_manager.get_stats() except Exception as e: tasks_status["backup"] = {"error": str(e)} return tasks_status def _get_resource_usage() -> Dict[str, Any]: """Get system resource usage.""" # CPU usage cpu_percent = psutil.cpu_percent(interval=1) cpu_count = psutil.cpu_count() # Memory usage memory = psutil.virtual_memory() swap = psutil.swap_memory() # Disk usage disk = psutil.disk_usage('/') # Network I/O network = psutil.net_io_counters() return { "cpu": { "usage_percent": cpu_percent, "count": cpu_count, }, "memory": { "total_mb": memory.total / (1024 * 1024), "used_mb": memory.used / (1024 * 1024), "available_mb": memory.available / (1024 * 1024), "usage_percent": memory.percent, }, "swap": { "total_mb": swap.total / (1024 * 1024), "used_mb": swap.used / (1024 * 1024), "usage_percent": swap.percent, }, "disk": { "total_gb": disk.total / (1024 * 1024 * 1024), "used_gb": disk.used / (1024 * 1024 * 1024), "free_gb": disk.free / (1024 * 1024 * 1024), "usage_percent": (disk.used / disk.total) * 100, }, "network": { "bytes_sent": network.bytes_sent, "bytes_recv": network.bytes_recv, "packets_sent": network.packets_sent, "packets_recv": network.packets_recv, } if network else None, } async def _get_health_status(settings: Settings) -> Dict[str, Any]: """Get overall health status.""" health = { "status": "healthy", "checks": {}, "issues": [], } # Check database health try: from src.database.connection import get_database_manager db_manager = get_database_manager(settings) await db_manager.test_connection() health["checks"]["database"] = "healthy" except Exception as e: health["checks"]["database"] = "unhealthy" health["issues"].append(f"Database connection failed: {e}") health["status"] = "unhealthy" # Check disk space disk = psutil.disk_usage('/') disk_usage_percent = (disk.used / disk.total) * 100 if disk_usage_percent > 90: health["checks"]["disk_space"] = "critical" health["issues"].append(f"Disk usage critical: {disk_usage_percent:.1f}%") health["status"] = "critical" elif disk_usage_percent > 80: health["checks"]["disk_space"] = "warning" health["issues"].append(f"Disk usage high: {disk_usage_percent:.1f}%") if health["status"] == "healthy": health["status"] = "warning" else: health["checks"]["disk_space"] = "healthy" # Check memory usage memory = psutil.virtual_memory() if memory.percent > 90: health["checks"]["memory"] = "critical" health["issues"].append(f"Memory usage critical: {memory.percent:.1f}%") health["status"] = "critical" elif memory.percent > 80: health["checks"]["memory"] = "warning" health["issues"].append(f"Memory usage high: {memory.percent:.1f}%") if health["status"] == "healthy": health["status"] = "warning" else: health["checks"]["memory"] = "healthy" # Check log directory log_dir = Path(settings.log_directory) if log_dir.exists() and log_dir.is_dir(): health["checks"]["log_directory"] = "healthy" else: health["checks"]["log_directory"] = "unhealthy" health["issues"].append(f"Log directory not accessible: {log_dir}") health["status"] = "unhealthy" # Check backup directory backup_dir = Path(settings.backup_directory) if backup_dir.exists() and backup_dir.is_dir(): health["checks"]["backup_directory"] = "healthy" else: health["checks"]["backup_directory"] = "unhealthy" health["issues"].append(f"Backup directory not accessible: {backup_dir}") health["status"] = "unhealthy" return health def _print_text_status(status_data: Dict[str, Any], detailed: bool) -> None: """Print status in human-readable text format.""" print("=" * 60) print("WiFi-DensePose API Server Status") print("=" * 60) print(f"Timestamp: {status_data['timestamp']}") print() # Server status server = status_data["server"] print("๐Ÿ–ฅ๏ธ Server Status:") if server["running"]: print(f" โœ… Running (PID: {server['pid']})") if "start_time" in server: uptime = timedelta(seconds=int(server["uptime_seconds"])) print(f" โฑ๏ธ Uptime: {uptime}") print(f" ๐Ÿ’พ Memory: {server['memory_usage_mb']:.1f} MB") print(f" ๐Ÿ”ง CPU: {server['cpu_percent']:.1f}%") print(f" ๐Ÿงต Threads: {server['num_threads']}") else: print(" โŒ Not running") if server["pid_file_exists"]: print(" โš ๏ธ Stale PID file exists") print() # System status system = status_data["system"] print("๐Ÿ–ฅ๏ธ System:") print(f" Hostname: {system['hostname']}") print(f" Platform: {system['platform']} ({system['architecture']})") print(f" Python: {system['python_version']}") uptime = timedelta(seconds=int(system["uptime_seconds"])) print(f" Uptime: {uptime}") print() # Configuration config = status_data["configuration"] print("โš™๏ธ Configuration:") print(f" Environment: {config['environment']}") print(f" Debug: {config['debug']}") print(f" API Version: {config['version']}") print(f" Listen: {config['host']}:{config['port']}") print(f" Database: {'โœ…' if config['database_configured'] else 'โŒ'}") print(f" Redis: {'โœ…' if config['redis_enabled'] else 'โŒ'}") print(f" Monitoring: {'โœ…' if config['monitoring_enabled'] else 'โŒ'}") print(f" Cleanup: {'โœ…' if config['cleanup_enabled'] else 'โŒ'}") print(f" Backup: {'โœ…' if config['backup_enabled'] else 'โŒ'}") print() if detailed: # Database status if "database" in status_data: db = status_data["database"] print("๐Ÿ—„๏ธ Database:") if db["connected"]: print(" โœ… Connected") if "tables" in db: print(" ๐Ÿ“Š Table counts:") for table, info in db["tables"].items(): if "count" in info: print(f" {table}: {info['count']:,}") else: print(f" {table}: Error - {info.get('error', 'Unknown')}") else: print(f" โŒ Not connected: {db.get('error', 'Unknown error')}") print() # Background tasks if "background_tasks" in status_data: tasks = status_data["background_tasks"] print("๐Ÿ”„ Background Tasks:") for task_name, task_info in tasks.items(): if "error" in task_info: print(f" โŒ {task_name}: {task_info['error']}") else: manager_info = task_info.get("manager", {}) print(f" ๐Ÿ“‹ {task_name}:") print(f" Running: {manager_info.get('running', 'Unknown')}") print(f" Last run: {manager_info.get('last_run', 'Never')}") print(f" Run count: {manager_info.get('run_count', 0)}") print() # Resource usage if "resources" in status_data: resources = status_data["resources"] print("๐Ÿ“Š Resource Usage:") cpu = resources["cpu"] print(f" ๐Ÿ”ง CPU: {cpu['usage_percent']:.1f}% ({cpu['count']} cores)") memory = resources["memory"] print(f" ๐Ÿ’พ Memory: {memory['usage_percent']:.1f}% " f"({memory['used_mb']:.0f}/{memory['total_mb']:.0f} MB)") disk = resources["disk"] print(f" ๐Ÿ’ฟ Disk: {disk['usage_percent']:.1f}% " f"({disk['used_gb']:.1f}/{disk['total_gb']:.1f} GB)") print() # Health status if "health" in status_data: health = status_data["health"] print("๐Ÿฅ Health Status:") status_emoji = { "healthy": "โœ…", "warning": "โš ๏ธ", "critical": "โŒ", "unhealthy": "โŒ" } print(f" Overall: {status_emoji.get(health['status'], 'โ“')} {health['status'].upper()}") if health["issues"]: print(" Issues:") for issue in health["issues"]: print(f" โ€ข {issue}") print(" Checks:") for check, status in health["checks"].items(): emoji = status_emoji.get(status, "โ“") print(f" {emoji} {check}: {status}") print() print("=" * 60) def get_quick_status(settings: Settings) -> str: """Get a quick one-line status.""" from src.commands.stop import get_server_status status = get_server_status(settings) if status["running"]: return f"โœ… Running (PID: {status['pid']})" elif status["pid_file_exists"]: return "โš ๏ธ Not running (stale PID file)" else: return "โŒ Not running" async def check_health(settings: Settings) -> bool: """Quick health check - returns True if healthy.""" try: status_data = await _collect_status_data(settings, detailed=True) # Check if server is running if not status_data["server"]["running"]: return False # Check health status if "health" in status_data: health_status = status_data["health"]["status"] return health_status in ["healthy", "warning"] return True except Exception: return False