using QemuVmManager.Core; using QemuVmManager.Models; using QemuVmManager.Services; using Microsoft.Extensions.Logging; namespace QemuVmManager.Console; public class P2PConsole { private readonly P2PNode _p2pNode; private readonly VmManagementService _vmService; private readonly ILogger? _logger; private bool _isRunning = false; public P2PConsole(string nodeId, int port = 8080, ILogger? logger = null) { _p2pNode = new P2PNode(nodeId, port, logger: null); _vmService = new VmManagementService(); _logger = logger; // Subscribe to events _p2pNode.RoleChanged += OnRoleChanged; _p2pNode.NodeJoined += OnNodeJoined; _p2pNode.NodeLeft += OnNodeLeft; _p2pNode.VmStarted += OnVmStarted; _p2pNode.VmStopped += OnVmStopped; } public async Task StartAsync() { if (_isRunning) return; System.Console.WriteLine("=== QEMU P2P Cluster Manager ==="); System.Console.WriteLine($"Node ID: {_p2pNode.CurrentNode.NodeId}"); System.Console.WriteLine($"Hostname: {_p2pNode.CurrentNode.Hostname}"); System.Console.WriteLine($"IP Address: {_p2pNode.CurrentNode.IpAddress}"); System.Console.WriteLine($"Port: {_p2pNode.CurrentNode.Port}"); System.Console.WriteLine(); System.Console.WriteLine("Type 'help' for available commands"); System.Console.WriteLine(); await _p2pNode.StartAsync(); _isRunning = true; await RunInteractiveModeAsync(); } public async Task StopAsync() { if (!_isRunning) return; await _p2pNode.StopAsync(); _isRunning = false; } private async Task RunInteractiveModeAsync() { while (_isRunning) { try { System.Console.Write($"p2p-{_p2pNode.CurrentNode.NodeId}> "); var input = System.Console.ReadLine()?.Trim(); if (string.IsNullOrEmpty(input)) continue; var parts = input.Split(' ', StringSplitOptions.RemoveEmptyEntries); var command = parts[0].ToLower(); var arguments = parts.Skip(1).ToArray(); switch (command) { case "help": ShowHelp(); break; case "status": await ShowStatusAsync(); break; case "cluster": await ShowClusterAsync(); break; case "nodes": await ShowNodesAsync(); break; case "vms": await ShowVmsAsync(); break; case "start": await StartVmAsync(arguments); break; case "stop": await StopVmAsync(arguments); break; case "migrate": await MigrateVmAsync(arguments); break; case "forward": await ForwardPortAsync(arguments); break; case "create": await CreateVmAsync(arguments); break; case "list": await ListVmsAsync(); break; case "upnp": await ShowUPnPStatusAsync(); break; case "discovery": await ShowDiscoveryInfoAsync(); break; case "exit": case "quit": System.Console.WriteLine("Stopping P2P node..."); await StopAsync(); System.Console.WriteLine("Goodbye!"); return; default: System.Console.WriteLine($"Unknown command: {command}"); System.Console.WriteLine("Type 'help' for available commands"); break; } } catch (Exception ex) { System.Console.WriteLine($"Error: {ex.Message}"); } System.Console.WriteLine(); } } private void ShowHelp() { System.Console.WriteLine("Available commands:"); System.Console.WriteLine(" status - Show current node status"); System.Console.WriteLine(" cluster - Show cluster information"); System.Console.WriteLine(" nodes - List all nodes in cluster"); System.Console.WriteLine(" vms - List all VMs in cluster"); System.Console.WriteLine(" list - List local VM configurations"); System.Console.WriteLine(" create - Create a new VM (interactive)"); System.Console.WriteLine(" start [node-id] - Start a VM (optionally on specific node)"); System.Console.WriteLine(" stop - Stop a VM"); System.Console.WriteLine(" migrate - Migrate VM to different node"); System.Console.WriteLine(" forward - Forward port for VM (master only)"); System.Console.WriteLine(" upnp - Show UPnP status"); System.Console.WriteLine(" discovery - Show network discovery information"); System.Console.WriteLine(" help - Show this help"); System.Console.WriteLine(" exit/quit - Exit the application"); } private async Task ShowStatusAsync() { var node = _p2pNode.CurrentNode; System.Console.WriteLine($"=== Node Status ==="); System.Console.WriteLine($"Node ID: {node.NodeId}"); System.Console.WriteLine($"Hostname: {node.Hostname}"); System.Console.WriteLine($"IP Address: {node.IpAddress}"); System.Console.WriteLine($"Port: {node.Port}"); System.Console.WriteLine($"Role: {node.Role}"); System.Console.WriteLine($"State: {node.State}"); System.Console.WriteLine($"Is Master: {_p2pNode.IsMaster}"); System.Console.WriteLine(); System.Console.WriteLine("=== System Info ==="); var sysInfo = node.SystemInfo; System.Console.WriteLine($"OS: {sysInfo.OsName} {sysInfo.OsVersion}"); System.Console.WriteLine($"Architecture: {sysInfo.Architecture}"); System.Console.WriteLine($"CPU Cores: {sysInfo.CpuCores}"); System.Console.WriteLine($"Total Memory: {sysInfo.TotalMemory:N0} MB"); System.Console.WriteLine($"Available Memory: {sysInfo.AvailableMemory:N0} MB"); System.Console.WriteLine($"Virtualization: {sysInfo.AvailableVirtualization}"); System.Console.WriteLine($"QEMU Installed: {sysInfo.QemuInstalled}"); System.Console.WriteLine($"QEMU Version: {sysInfo.QemuVersion}"); } private async Task ShowClusterAsync() { var cluster = _p2pNode.ClusterState; System.Console.WriteLine($"=== Cluster Information ==="); System.Console.WriteLine($"Cluster ID: {cluster.ClusterId}"); System.Console.WriteLine($"Last Updated: {cluster.LastUpdated:yyyy-MM-dd HH:mm:ss}"); System.Console.WriteLine($"Total Nodes: {cluster.Nodes.Count}"); System.Console.WriteLine($"Total VMs: {cluster.DistributedVms.Count}"); if (cluster.MasterNode != null) { System.Console.WriteLine(); System.Console.WriteLine("=== Master Node ==="); System.Console.WriteLine($"Node ID: {cluster.MasterNode.NodeId}"); System.Console.WriteLine($"Hostname: {cluster.MasterNode.Hostname}"); System.Console.WriteLine($"IP Address: {cluster.MasterNode.IpAddress}"); System.Console.WriteLine($"Last Seen: {cluster.MasterNode.LastSeen:yyyy-MM-dd HH:mm:ss}"); } } private async Task ShowNodesAsync() { var cluster = _p2pNode.ClusterState; if (cluster.Nodes.Count == 0) { System.Console.WriteLine("No nodes in cluster."); return; } System.Console.WriteLine($"{"Node ID",-20} {"Hostname",-15} {"IP Address",-15} {"Role",-10} {"State",-10} {"Last Seen"}"); System.Console.WriteLine(new string('-', 90)); foreach (var node in cluster.Nodes) { var lastSeen = node.LastSeen.ToString("yyyy-MM-dd HH:mm:ss"); System.Console.WriteLine($"{node.NodeId,-20} {node.Hostname,-15} {node.IpAddress,-15} {node.Role,-10} {node.State,-10} {lastSeen}"); } } private async Task ShowVmsAsync() { var cluster = _p2pNode.ClusterState; if (cluster.DistributedVms.Count == 0) { System.Console.WriteLine("No VMs in cluster."); return; } System.Console.WriteLine($"{"VM ID",-36} {"Name",-20} {"Node ID",-20} {"State",-10} {"Started"}"); System.Console.WriteLine(new string('-', 100)); foreach (var vm in cluster.DistributedVms.Values) { var started = vm.StartedAt.ToString("yyyy-MM-dd HH:mm:ss"); System.Console.WriteLine($"{vm.VmId,-36} {vm.VmName,-20} {vm.NodeId,-20} {vm.State,-10} {started}"); if (vm.PublicEndpoint != null) { System.Console.WriteLine($" Public: {vm.PublicEndpoint.PublicIp}:{vm.PublicEndpoint.PublicPort}"); } } } private async Task ListVmsAsync() { var configs = _vmService.GetAllVmConfigurations().ToList(); if (configs.Count == 0) { System.Console.WriteLine("No VM configurations found."); return; } System.Console.WriteLine($"{"Name",-20} {"CPU",-8} {"Memory",-10} {"Description"}"); System.Console.WriteLine(new string('-', 70)); foreach (var config in configs) { var cpuText = $"{config.Cpu.Cores} cores"; var memoryText = $"{config.Memory.Size}{config.Memory.Unit}"; System.Console.WriteLine($"{config.Name,-20} {cpuText,-8} {memoryText,-10} {config.Description}"); } } private async Task CreateVmAsync(string[] arguments) { string vmName; if (arguments.Length > 0) { vmName = arguments[0]; } else { System.Console.Write("Enter VM name: "); vmName = System.Console.ReadLine()?.Trim() ?? ""; } if (string.IsNullOrEmpty(vmName)) { System.Console.WriteLine("VM name cannot be empty."); return; } var config = new VmConfiguration { Name = vmName, Description = GetUserInput("Description (optional): "), Cpu = new CpuConfiguration { Cores = int.Parse(GetUserInput("CPU cores (2): ", "2")), Model = GetUserInput("CPU model (qemu64): ", "qemu64") }, Memory = new MemoryConfiguration { Size = long.Parse(GetUserInput("Memory size in MB (2048): ", "2048")), Unit = "M" }, Storage = new StorageConfiguration { Disks = new List { new DiskConfiguration { Path = GetUserInput($"Disk path (vm-disks/{vmName}.qcow2): ", $"vm-disks/{vmName}.qcow2"), Size = long.Parse(GetUserInput("Disk size in GB (10): ", "10")), Format = GetUserInput("Disk format (qcow2): ", "qcow2"), Interface = GetUserInput("Disk interface (virtio): ", "virtio"), IsBoot = true } } }, Network = new NetworkConfiguration { Interfaces = new List { new NetworkInterfaceConfiguration { Type = "user", Model = "e1000", Mac = GetUserInput("MAC address (auto): ", "") } } }, Display = new DisplayConfiguration { Type = GetUserInput("Display type (gtk): ", "gtk"), Vga = GetUserInput("VGA type (virtio): ", "virtio") } }; await _vmService.CreateVmAsync(config); System.Console.WriteLine($"VM '{vmName}' created successfully."); } private async Task StartVmAsync(string[] arguments) { if (arguments.Length == 0) { System.Console.WriteLine("Usage: start [node-id]"); return; } var vmName = arguments[0]; var targetNodeId = arguments.Length > 1 ? arguments[1] : null; try { var config = _vmService.GetVmConfiguration(vmName); if (config == null) { System.Console.WriteLine($"VM configuration '{vmName}' not found."); return; } var vm = await _p2pNode.StartVmAsync(config, targetNodeId); System.Console.WriteLine($"VM '{vmName}' started successfully with ID: {vm.VmId}"); if (targetNodeId != null) { System.Console.WriteLine($"VM is running on node: {targetNodeId}"); } } catch (Exception ex) { System.Console.WriteLine($"Failed to start VM: {ex.Message}"); } } private async Task StopVmAsync(string[] arguments) { if (arguments.Length == 0) { System.Console.WriteLine("Usage: stop "); return; } var vmId = arguments[0]; try { var success = await _p2pNode.StopVmAsync(vmId); if (success) { System.Console.WriteLine($"VM '{vmId}' stopped successfully."); } else { System.Console.WriteLine($"Failed to stop VM '{vmId}'."); } } catch (Exception ex) { System.Console.WriteLine($"Error stopping VM: {ex.Message}"); } } private async Task MigrateVmAsync(string[] arguments) { if (arguments.Length < 2) { System.Console.WriteLine("Usage: migrate "); return; } var vmId = arguments[0]; var targetNodeId = arguments[1]; try { var response = await _p2pNode.MigrateVmAsync(vmId, targetNodeId); if (response.Success) { System.Console.WriteLine($"VM '{vmId}' migrated to node '{targetNodeId}' successfully."); } else { System.Console.WriteLine($"Failed to migrate VM: {response.ErrorMessage}"); } } catch (Exception ex) { System.Console.WriteLine($"Error migrating VM: {ex.Message}"); } } private async Task ForwardPortAsync(string[] arguments) { if (arguments.Length < 2) { System.Console.WriteLine("Usage: forward [public-port]"); return; } var vmId = arguments[0]; var privatePort = int.Parse(arguments[1]); var publicPort = arguments.Length > 2 ? int.Parse(arguments[2]) : (int?)null; try { var response = await _p2pNode.RequestPortForwardingAsync(vmId, privatePort, publicPort); if (response.Success) { System.Console.WriteLine($"Port forwarding created successfully."); System.Console.WriteLine($"Public IP: {response.PublicIp}"); System.Console.WriteLine($"Public Port: {response.PublicPort}"); System.Console.WriteLine($"Private Port: {privatePort}"); } else { System.Console.WriteLine($"Failed to create port forwarding: {response.ErrorMessage}"); } } catch (Exception ex) { System.Console.WriteLine($"Error creating port forwarding: {ex.Message}"); } } private async Task ShowUPnPStatusAsync() { try { var upnpManager = new UPnPManager(); var isAvailable = await upnpManager.IsUPnPAvailableAsync(); var externalIp = await upnpManager.GetExternalIpAddressAsync(); var mappings = await upnpManager.GetPortMappingsAsync(); System.Console.WriteLine("=== UPnP Status ==="); System.Console.WriteLine($"Available: {isAvailable}"); System.Console.WriteLine($"External IP: {externalIp}"); System.Console.WriteLine($"Active Mappings: {mappings.Count}"); if (mappings.Count > 0) { System.Console.WriteLine(); System.Console.WriteLine("=== Port Mappings ==="); System.Console.WriteLine($"{"External Port",-15} {"Internal Port",-15} {"Protocol",-10} {"Description"}"); System.Console.WriteLine(new string('-', 70)); foreach (var mapping in mappings) { System.Console.WriteLine($"{mapping.ExternalPort,-15} {mapping.InternalPort,-15} {mapping.Protocol,-10} {mapping.Description}"); } } } catch (Exception ex) { System.Console.WriteLine($"Error checking UPnP status: {ex.Message}"); } } private void OnRoleChanged(object? sender, NodeRole role) { System.Console.WriteLine($"Role changed to: {role}"); if (role == NodeRole.Master) { System.Console.WriteLine("🎉 This node is now the MASTER!"); } } private void OnNodeJoined(object? sender, NodeInfo node) { System.Console.WriteLine($"Node joined: {node.NodeId} ({node.IpAddress})"); } private void OnNodeLeft(object? sender, NodeInfo node) { System.Console.WriteLine($"Node left: {node.NodeId}"); } private void OnVmStarted(object? sender, VmInstance vm) { System.Console.WriteLine($"VM started: {vm.VmName} (ID: {vm.VmId}) on node {vm.NodeId}"); } private void OnVmStopped(object? sender, VmInstance vm) { System.Console.WriteLine($"VM stopped: {vm.VmName} (ID: {vm.VmId})"); } private string GetUserInput(string prompt, string defaultValue = "") { System.Console.Write(prompt); var input = System.Console.ReadLine()?.Trim(); return string.IsNullOrEmpty(input) ? defaultValue : input; } private async Task ShowDiscoveryInfoAsync() { System.Console.WriteLine("=== Network Discovery Information ==="); System.Console.WriteLine($"Current Node ID: {_p2pNode.CurrentNode.NodeId}"); System.Console.WriteLine($"Current Node IP: {_p2pNode.CurrentNode.IpAddress}"); System.Console.WriteLine($"Current Node Port: {_p2pNode.CurrentNode.Port}"); System.Console.WriteLine($"Current Role: {_p2pNode.CurrentNode.Role}"); System.Console.WriteLine($"Is Master: {_p2pNode.IsMaster}"); System.Console.WriteLine(); // Get cluster state to show known nodes var cluster = _p2pNode.ClusterState; System.Console.WriteLine($"Known Nodes: {cluster.Nodes.Count}"); if (cluster.Nodes.Count > 0) { System.Console.WriteLine(); System.Console.WriteLine("=== Known Nodes ==="); System.Console.WriteLine($"{"Node ID",-20} {"IP Address",-15} {"Port",-8} {"Role",-10} {"Last Seen"}"); System.Console.WriteLine(new string('-', 80)); foreach (var node in cluster.Nodes.Values) { var lastSeen = node.LastSeen.ToString("HH:mm:ss"); System.Console.WriteLine($"{node.NodeId,-20} {node.IpAddress,-15} {node.Port,-8} {node.Role,-10} {lastSeen}"); } } else { System.Console.WriteLine("No other nodes discovered yet."); System.Console.WriteLine(); System.Console.WriteLine("Discovery troubleshooting:"); System.Console.WriteLine("1. Make sure other nodes are running on the same network"); System.Console.WriteLine("2. Check if firewall is blocking UDP port 8080"); System.Console.WriteLine("3. Try running multiple instances on different machines"); System.Console.WriteLine("4. Check network connectivity between nodes"); } System.Console.WriteLine(); System.Console.WriteLine("=== Network Configuration ==="); System.Console.WriteLine("UDP Discovery: Enabled (port 8080)"); System.Console.WriteLine("TCP Communication: Enabled (port 8081)"); System.Console.WriteLine("Heartbeat Interval: 5 seconds"); System.Console.WriteLine("Discovery Interval: 30 seconds"); System.Console.WriteLine("Node Timeout: 30 seconds"); } }