#!/usr/bin/env python3 """ Cell Manager for Personal Internet Cell Handles overall cell configuration and service orchestration """ from network_manager import NetworkManager from wireguard_manager import WireGuardManager from peer_registry import PeerRegistry from email_manager import EmailManager from calendar_manager import CalendarManager from file_manager import FileManager from routing_manager import RoutingManager from vault_manager import VaultManager from container_manager import ContainerManager from datetime import datetime import json import logging from pathlib import Path from typing import Dict, List, Any from base_service_manager import BaseServiceManager logger = logging.getLogger(__name__) class CellManager(BaseServiceManager): """Manages overall cell configuration and service orchestration""" def __init__(self, config_path=None, data_dir: str = '/app/data', config_dir: str = '/app/config'): super().__init__('cell', data_dir, config_dir) import os self.config_path = Path(config_path or os.environ.get('CELL_CONFIG_PATH', 'cell_config.json')) # Initialize all service managers self.network_manager = NetworkManager(data_dir, config_dir) self.wireguard_manager = WireGuardManager(data_dir, config_dir) self.peer_registry = PeerRegistry() self.email_manager = EmailManager(data_dir, config_dir) self.calendar_manager = CalendarManager(data_dir, config_dir) self.file_manager = FileManager(data_dir, config_dir) self.routing_manager = RoutingManager(data_dir, config_dir) self.vault_manager = VaultManager(config_dir, data_dir) self.container_manager = ContainerManager(data_dir, config_dir) self._peers = [] self._uptime = 3600 # Load config from file if exists if self.config_path.exists(): with open(self.config_path, 'r') as f: self.config = json.load(f) else: self.cell_name = os.environ.get("CELL_NAME", "personal-internet-cell") self.domain = os.environ.get("DOMAIN", f"{self.cell_name}.cell") self.ip_range = os.environ.get("IP_RANGE", "10.0.0.0/24") self.wireguard_port = int(os.environ.get("WIREGUARD_PORT", 51820)) self.dns_port = int(os.environ.get("DNS_PORT", 53)) self.dhcp_range = os.environ.get("DHCP_RANGE", "10.0.0.100-10.0.200") self.config = { "cell_name": self.cell_name, "domain": self.domain, "ip_range": self.ip_range, "wireguard_port": self.wireguard_port, "dns_port": self.dns_port, "dhcp_range": self.dhcp_range, "created_at": datetime.utcnow().isoformat() } # Always update attributes from config for k, v in self.config.items(): setattr(self, k, v) def get_status(self) -> Dict[str, Any]: """Get cell service status""" try: services_status = self.get_services_status() # Count healthy services healthy_services = 0 total_services = len(services_status) for service_name, status in services_status.items(): if status.get('running', False): healthy_services += 1 status = { 'running': healthy_services > 0, 'status': 'online' if healthy_services > 0 else 'offline', 'cell_name': self.config["cell_name"], 'domain': self.config["domain"], 'ip_range': self.config["ip_range"], 'wireguard_port': self.config["wireguard_port"], 'dns_port': self.config["dns_port"], 'dhcp_range': self.config["dhcp_range"], 'uptime': self._uptime, 'peers_count': len(self._peers), 'healthy_services': healthy_services, 'total_services': total_services, 'services': services_status, 'timestamp': datetime.utcnow().isoformat() } return status except Exception as e: return self.handle_error(e, "get_status") def test_connectivity(self) -> Dict[str, Any]: """Test cell service connectivity""" try: # Test all service managers connectivity network_test = self.network_manager.test_connectivity() wireguard_test = self.wireguard_manager.test_connectivity() email_test = self.email_manager.test_connectivity() calendar_test = self.calendar_manager.test_connectivity() files_test = self.file_manager.test_connectivity() routing_test = self.routing_manager.test_connectivity() vault_test = self.vault_manager.test_connectivity() container_test = self.container_manager.test_connectivity() # Calculate overall success all_tests = [ network_test, wireguard_test, email_test, calendar_test, files_test, routing_test, vault_test, container_test ] successful_tests = sum(1 for test in all_tests if test.get('success', False)) total_tests = len(all_tests) results = { 'network': network_test, 'wireguard': wireguard_test, 'email': email_test, 'calendar': calendar_test, 'files': files_test, 'routing': routing_test, 'vault': vault_test, 'container': container_test, 'success': successful_tests > 0, 'successful_tests': successful_tests, 'total_tests': total_tests, 'timestamp': datetime.utcnow().isoformat() } return results except Exception as e: return self.handle_error(e, "test_connectivity") def save_config(self): """Save cell configuration""" try: with open(self.config_path, 'w') as f: json.dump(self.config, f, indent=2) self.logger.info("Cell configuration saved") return True except Exception as e: self.logger.error(f"Error saving cell configuration: {e}") return False def get_config(self) -> Dict[str, Any]: """Get cell configuration""" return { "cell_name": self.cell_name, "domain": self.domain, "ip_range": self.ip_range, "wireguard_port": self.wireguard_port, "dns_port": self.dns_port, "dhcp_range": self.dhcp_range } def update_config(self, config: Dict[str, Any]) -> Dict[str, Any]: """Update cell configuration""" try: # Update config attributes from dict for k, v in config.items(): if hasattr(self, k): setattr(self, k, v) self.config[k] = v # Save updated config self.save_config() return {"status": "updated", "message": "Configuration updated successfully"} except Exception as e: return {"status": "error", "message": f"Failed to update configuration: {str(e)}"} def get_peers(self) -> List[Dict[str, Any]]: """Get all peers""" return self._peers def add_peer(self, peer: Dict[str, Any]) -> tuple[bool, str]: """Add a new peer""" try: # Simulate validation: must have name, ip, public_key if not all(k in peer for k in ("name", "ip", "public_key")): return False, "Missing required fields" # Prevent duplicate peer names if any(p['name'] == peer['name'] for p in self._peers): return False, "Peer already exists" self._peers.append(peer) self.logger.info(f"Added peer: {peer['name']}") return True, "Peer added successfully" except Exception as e: self.logger.error(f"Error adding peer: {e}") return False, f"Error adding peer: {str(e)}" def remove_peer(self, name: str) -> tuple[bool, str]: """Remove a peer""" try: for i, p in enumerate(self._peers): if p['name'] == name: del self._peers[i] self.logger.info(f"Removed peer: {name}") return True, "Peer removed successfully" return False, "Peer not found" except Exception as e: self.logger.error(f"Error removing peer {name}: {e}") return False, f"Error removing peer: {str(e)}" def get_services_status(self) -> Dict[str, Any]: """Get status of all services""" try: return { "network": self.network_manager.get_status(), "wireguard": self.wireguard_manager.get_status(), "email": self.email_manager.get_status(), "calendar": self.calendar_manager.get_status(), "files": self.file_manager.get_status(), "routing": self.routing_manager.get_status(), "vault": self.vault_manager.get_status(), "container": self.container_manager.get_status() } except Exception as e: self.logger.error(f"Error getting services status: {e}") return {} def get_uptime(self) -> int: """Get cell uptime""" return self._uptime def restart_all_services(self) -> Dict[str, Any]: """Restart all services""" try: results = {} # Restart each service manager services = { 'network': self.network_manager, 'wireguard': self.wireguard_manager, 'email': self.email_manager, 'calendar': self.calendar_manager, 'files': self.file_manager, 'routing': self.routing_manager, 'vault': self.vault_manager, 'container': self.container_manager } for service_name, service_manager in services.items(): try: success = service_manager.restart_service() results[service_name] = { 'success': success, 'message': f"Service {'restarted' if success else 'failed to restart'}" } except Exception as e: results[service_name] = { 'success': False, 'message': f"Error restarting service: {str(e)}" } return { 'success': any(r.get('success', False) for r in results.values()), 'results': results, 'timestamp': datetime.utcnow().isoformat() } except Exception as e: return self.handle_error(e, "restart_all_services") def get_health_summary(self) -> Dict[str, Any]: """Get comprehensive health summary""" try: services_status = self.get_services_status() connectivity = self.test_connectivity() # Calculate health metrics healthy_services = sum(1 for status in services_status.values() if status.get('running', False)) total_services = len(services_status) health_percentage = (healthy_services / total_services * 100) if total_services > 0 else 0 return { 'overall_health': 'healthy' if health_percentage >= 80 else 'degraded' if health_percentage >= 50 else 'unhealthy', 'health_percentage': round(health_percentage, 2), 'healthy_services': healthy_services, 'total_services': total_services, 'services_status': services_status, 'connectivity': connectivity, 'uptime': self._uptime, 'peers_count': len(self._peers), 'timestamp': datetime.utcnow().isoformat() } except Exception as e: return self.handle_error(e, "get_health_summary")