init
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
#!/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)
|
||||
Reference in New Issue
Block a user