import asyncio import sys import os from pathlib import Path # Add app directory to sys.path so we can import from app sys.path.append(str(Path(__file__).parent)) from app.config import settings from app.proxmox_client import proxmox async def main(): node = "dl380-0" vmid = 900 if len(sys.argv) > 1: vmid = int(sys.argv[1]) if len(sys.argv) > 2: node = sys.argv[2] print(f"Requesting termproxy ticket for VM {vmid} on node {node}...") try: res = await proxmox._request("POST", f"/nodes/{node}/qemu/{vmid}/termproxy") except Exception as e: print(f"Error requesting termproxy: {e}") return port = res["port"] ticket = res["ticket"] user = res["user"] import websockets from urllib.parse import quote # Construct ws url host_clean = settings.proxmox_host.replace("https://", "").replace("http://", "").split("/")[0] ws_url = f"wss://{host_clean}/api2/json/nodes/{node}/qemu/{vmid}/vncwebsocket?port={port}&vncticket={quote(ticket)}" print(f"Connecting to websocket: {ws_url}") import ssl ssl_context = ssl._create_unverified_context() # Proxmox websocket protocol requires sending the ticket to authenticate try: async with websockets.connect( ws_url, ssl=ssl_context, subprotocols=["binary"] ) as ws: # Handshake format: user:ticket\n handshake = f"{user}:{ticket}\n" await ws.send(handshake) print("Handshake sent, reading console stream (Ctrl+C to exit)...") # Proxmox termproxy sends data as frame-wrapped or raw binary while True: msg = await ws.recv() if isinstance(msg, bytes): # Proxmox termproxy data format: 1 byte channel, then data # Channel 0 is stdout, channel 1 is stderr if len(msg) > 1: sys.stdout.buffer.write(msg[1:]) sys.stdout.buffer.flush() else: sys.stdout.write(msg) sys.stdout.flush() except KeyboardInterrupt: print("\nExiting console reader.") except websockets.exceptions.ConnectionClosed as e: print(f"\nConnection closed: {e}") except Exception as e: print(f"\nError: {e}") if __name__ == "__main__": asyncio.run(main())