#!/usr/bin/env python3 """ Base Service Manager for Personal Internet Cell Provides standardized interface for all service managers """ import logging import json from abc import ABC, abstractmethod from typing import Dict, List, Optional, Any from datetime import datetime import traceback logger = logging.getLogger(__name__) class BaseServiceManager(ABC): """Base class for all service managers with standardized interface""" def __init__(self, service_name: str, data_dir: str = '/app/data', config_dir: str = '/app/config'): self.service_name = service_name self.data_dir = data_dir self.config_dir = config_dir self.logger = logging.getLogger(f'picell.{service_name}') # Ensure directories exist self._ensure_directories() def _ensure_directories(self): """Ensure required directories exist""" import os os.makedirs(self.data_dir, exist_ok=True) os.makedirs(self.config_dir, exist_ok=True) @abstractmethod def get_status(self) -> Dict[str, Any]: """Get service status - must be implemented by subclasses""" pass @abstractmethod def test_connectivity(self) -> Dict[str, Any]: """Test service connectivity - must be implemented by subclasses""" pass def get_logs(self, lines: int = 50) -> List[str]: """Get service logs - default implementation""" try: log_file = f"{self.data_dir}/{self.service_name}.log" import os if not os.path.exists(log_file): return [f"No log file found for {self.service_name}"] with open(log_file, 'r', encoding='utf-8', errors='ignore') as f: all_lines = f.readlines() return all_lines[-lines:] if lines > 0 else all_lines except Exception as e: self.logger.error(f"Error reading logs: {e}") return [f"Error reading logs: {str(e)}"] def restart_service(self) -> bool: """Restart service - default implementation""" try: self.logger.info(f"Restarting {self.service_name} service") # Default implementation - subclasses can override return True except Exception as e: self.logger.error(f"Error restarting {self.service_name}: {e}") return False def get_config(self) -> Dict[str, Any]: """Get service configuration - default implementation""" try: config_file = f"{self.config_dir}/{self.service_name}.json" import os if not os.path.exists(config_file): return {"error": f"No configuration file found for {self.service_name}"} with open(config_file, 'r') as f: return json.load(f) except Exception as e: self.logger.error(f"Error reading config: {e}") return {"error": str(e)} def update_config(self, config: Dict[str, Any]) -> bool: """Update service configuration - default implementation""" try: config_file = f"{self.config_dir}/{self.service_name}.json" import os os.makedirs(os.path.dirname(config_file), exist_ok=True) with open(config_file, 'w') as f: json.dump(config, f, indent=2) self.logger.info(f"Updated configuration for {self.service_name}") return True except Exception as e: self.logger.error(f"Error updating config: {e}") return False def validate_config(self, config: Dict[str, Any]) -> Dict[str, Any]: """Validate configuration - default implementation""" return { "valid": True, "errors": [], "warnings": [] } def get_metrics(self) -> Dict[str, Any]: """Get service metrics - default implementation""" return { "service": self.service_name, "timestamp": datetime.utcnow().isoformat(), "status": "unknown" } def handle_error(self, error: Exception, context: str = "") -> Dict[str, Any]: """Standardized error handling""" error_info = { "error": str(error), "type": type(error).__name__, "context": context, "timestamp": datetime.utcnow().isoformat(), "service": self.service_name, "traceback": traceback.format_exc() } self.logger.error(f"Error in {context}: {error}") return error_info def log_operation(self, operation: str, details: Dict[str, Any] = None): """Log service operations""" log_data = { "operation": operation, "service": self.service_name, "timestamp": datetime.utcnow().isoformat(), "details": details or {} } self.logger.info(f"Operation: {operation} - {json.dumps(details) if details else 'No details'}") def health_check(self) -> Dict[str, Any]: """Comprehensive health check""" try: status = self.get_status() connectivity = self.test_connectivity() metrics = self.get_metrics() return { "service": self.service_name, "timestamp": datetime.utcnow().isoformat(), "status": status, "connectivity": connectivity, "metrics": metrics, "healthy": self._is_healthy(status, connectivity) } except Exception as e: return self.handle_error(e, "health_check") def _is_healthy(self, status: Dict[str, Any], connectivity: Dict[str, Any]) -> bool: """Determine if service is healthy based on status and connectivity""" # Default implementation - subclasses can override return status.get("running", False) and connectivity.get("success", False)