import asyncio import uuid import time from typing import Dict, Optional _tasks: Dict[str, dict] = {} def create_task() -> str: task_id = str(uuid.uuid4()) _tasks[task_id] = { "id": task_id, "status": "pending", "logs": [], "created": time.time(), } return task_id def get_task(task_id: str) -> Optional[dict]: return _tasks.get(task_id) async def run_test(task_id: str) -> None: task = _tasks[task_id] task["status"] = "running" def log(msg: str, type: str = "info") -> None: task["logs"].append({"msg": msg, "type": type, "ts": time.strftime("%H:%M:%S")}) log("▸ Checking UERANSIM Docker image…", "run") check = await asyncio.create_subprocess_exec( "docker", "images", "-q", "ueransim", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) out, _ = await check.communicate() if not out.strip(): log("✗ UERANSIM image not found.", "err") log(" SSH to host and run: bash /opt/p5g-marvis/build-ueransim.sh", "err") task["status"] = "error" return log(" UERANSIM image ready", "ok") log("▸ Starting test container — allow up to 60s…", "run") env_file = "/opt/p5g-marvis/config/ueransim.env" try: proc = await asyncio.create_subprocess_exec( "docker", "run", "--rm", "--network=host", "--privileged", "--env-file", env_file, "ueransim", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, ) try: stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=90) except asyncio.TimeoutError: proc.kill() log("✗ Test timed out after 90s — container killed", "err") task["status"] = "error" return for line in stdout.decode(errors="replace").splitlines(): line = line.strip() if not line: continue if "ERROR" in line: log(line, "err") elif "PASSED" in line or "established" in line or "successful" in line: log(line, "ok") elif "WARNING" in line: log(line, "warn") else: log(line, "info") if proc.returncode == 0: log("✓ Emulated data session completed successfully", "ok") task["status"] = "done" elif proc.returncode == 2: log("⚠ Credentials not configured — edit /opt/p5g-marvis/config/ueransim.env", "warn") task["status"] = "error" else: log(f"✗ Test exited with code {proc.returncode}", "err") task["status"] = "error" except Exception as exc: log(f"✗ Unexpected error: {exc}", "err") task["status"] = "error"