"""Olares K3s pod listing and GPU status.""" import subprocess from fastapi import APIRouter, Query router = APIRouter(tags=["olares"]) def _ssh_olares(cmd: str, timeout: int = 10) -> str: """Run a command on olares via SSH.""" result = subprocess.run( ["ssh", "-o", "ConnectTimeout=3", "olares", cmd], capture_output=True, text=True, timeout=timeout, ) return result.stdout if result.returncode == 0 else "" @router.get("/olares/pods") def olares_pods(namespace: str | None = Query(None)): """List K3s pods on olares.""" if namespace: cmd = f"kubectl get pods -n {namespace} -o wide --no-headers" else: cmd = "kubectl get pods -A -o wide --no-headers" output = _ssh_olares(cmd, timeout=15) if not output: return [] pods = [] for line in output.strip().split("\n"): parts = line.split() if not parts: continue if namespace: # No namespace column when -n is used if len(parts) >= 7: pods.append({ "namespace": namespace, "name": parts[0], "ready": parts[1], "status": parts[2], "restarts": parts[3], "age": parts[4], "ip": parts[5] if len(parts) > 5 else "", "node": parts[6] if len(parts) > 6 else "", }) else: # Has namespace column if len(parts) >= 8: pods.append({ "namespace": parts[0], "name": parts[1], "ready": parts[2], "status": parts[3], "restarts": parts[4], "age": parts[5], "ip": parts[6] if len(parts) > 6 else "", "node": parts[7] if len(parts) > 7 else "", }) return pods @router.get("/olares/gpu") def olares_gpu(): """GPU status from olares.""" output = _ssh_olares( "nvidia-smi --query-gpu=name,temperature.gpu,power.draw,power.limit," "memory.used,memory.total,utilization.gpu --format=csv,noheader,nounits" ) if not output: return {"available": False} parts = [p.strip() for p in output.strip().split(",")] def _float(val: str) -> float | None: try: return float(val) except (ValueError, TypeError): return None if len(parts) >= 7: return { "available": True, "name": parts[0], "temp_c": _float(parts[1]), "power_draw_w": _float(parts[2]), "power_limit_w": _float(parts[3]), "memory_used_mb": _float(parts[4]), "memory_total_mb": _float(parts[5]), "utilization_pct": _float(parts[6]), } return {"available": False}