This commit is contained in:
root
2025-11-25 10:11:32 +03:00
parent 48b1934def
commit 60792735ad
38 changed files with 12695 additions and 0 deletions

View File

@ -0,0 +1,225 @@
"""
System Service Module
Provides system monitoring and health check functionality for the Remote Access API.
Includes checks for database connectivity, daemon status, and system resources.
"""
import socket
import psutil
from datetime import datetime
from typing import Dict, Any, Optional
import structlog
logger = structlog.get_logger(__name__)
class SystemService:
"""Service for system health checks and monitoring"""
def __init__(self, service_start_time: Optional[datetime] = None):
"""
Initialize SystemService
Args:
service_start_time: Service startup time for uptime calculation
"""
self.service_start_time = service_start_time or datetime.now()
@staticmethod
def check_database_connection(guacamole_client: Any, guacamole_url: str) -> Dict[str, Any]:
"""
Check Guacamole database connectivity
Args:
guacamole_client: Guacamole client instance
guacamole_url: Guacamole base URL
Returns:
Status dictionary with connection state
"""
try:
# Try to get system token (requires database access)
token = guacamole_client.get_system_token()
if token:
return {
"status": "ok",
"message": "Database connection healthy"
}
else:
return {
"status": "error",
"message": "Failed to obtain system token"
}
except Exception as e:
logger.error("Database connection check failed", error=str(e))
return {
"status": "error",
"error": str(e),
"message": "Database connection failed"
}
@staticmethod
def check_guacd_daemon() -> Dict[str, Any]:
"""
Check if guacd daemon is running
Returns:
Status dictionary with daemon state
"""
try:
# Check if guacd is listening on default port 4822
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(2)
result = sock.connect_ex(('localhost', 4822))
sock.close()
if result == 0:
return {
"status": "ok",
"message": "guacd daemon is running",
"port": 4822
}
else:
return {
"status": "error",
"message": "guacd daemon is not accessible",
"port": 4822
}
except Exception as e:
logger.error("guacd daemon check failed", error=str(e))
return {
"status": "error",
"error": str(e),
"message": "Failed to check guacd daemon"
}
@staticmethod
def check_system_resources() -> Dict[str, Any]:
"""
Check system resources (CPU, RAM, Disk)
Returns:
Status dictionary with resource usage
"""
try:
# CPU usage
cpu_percent = psutil.cpu_percent(interval=1)
# Memory usage
memory = psutil.virtual_memory()
memory_percent = memory.percent
# Disk usage
disk = psutil.disk_usage('/')
disk_percent = disk.percent
# Determine overall status based on thresholds
status = "ok"
warnings = []
if cpu_percent > 90:
status = "critical"
warnings.append(f"CPU usage critical: {cpu_percent}%")
elif cpu_percent > 80:
status = "warning"
warnings.append(f"CPU usage high: {cpu_percent}%")
if memory_percent > 90:
status = "critical"
warnings.append(f"Memory usage critical: {memory_percent}%")
elif memory_percent > 80:
if status == "ok":
status = "warning"
warnings.append(f"Memory usage high: {memory_percent}%")
if disk_percent > 90:
status = "critical"
warnings.append(f"Disk usage critical: {disk_percent}%")
elif disk_percent > 80:
if status == "ok":
status = "warning"
warnings.append(f"Disk usage high: {disk_percent}%")
result = {
"status": status,
"cpu_percent": round(cpu_percent, 2),
"memory_percent": round(memory_percent, 2),
"disk_percent": round(disk_percent, 2),
"memory_available_gb": round(memory.available / (1024**3), 2),
"disk_free_gb": round(disk.free / (1024**3), 2)
}
if warnings:
result["warnings"] = warnings
if status == "ok":
result["message"] = "System resources healthy"
return result
except Exception as e:
logger.error("System resources check failed", error=str(e))
return {
"status": "error",
"error": str(e),
"message": "Failed to check system resources"
}
def get_system_info(self) -> Dict[str, Any]:
"""
Get system information (uptime, version, etc.)
Returns:
Dictionary with system information
"""
try:
uptime_seconds = int((datetime.now() - self.service_start_time).total_seconds())
return {
"uptime_seconds": uptime_seconds,
"uptime_formatted": self._format_uptime(uptime_seconds),
"python_version": f"{psutil.PROCFS_PATH if hasattr(psutil, 'PROCFS_PATH') else 'N/A'}",
"cpu_count": psutil.cpu_count(),
"boot_time": datetime.fromtimestamp(psutil.boot_time()).isoformat()
}
except Exception as e:
logger.error("Failed to get system info", error=str(e))
return {
"error": str(e),
"message": "Failed to retrieve system information"
}
@staticmethod
def _format_uptime(seconds: int) -> str:
"""
Format uptime seconds to human-readable string
Args:
seconds: Uptime in seconds
Returns:
Formatted uptime string
"""
days = seconds // 86400
hours = (seconds % 86400) // 3600
minutes = (seconds % 3600) // 60
secs = seconds % 60
parts = []
if days > 0:
parts.append(f"{days}d")
if hours > 0:
parts.append(f"{hours}h")
if minutes > 0:
parts.append(f"{minutes}m")
if secs > 0 or not parts:
parts.append(f"{secs}s")
return " ".join(parts)