94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
"""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}
|