Files
python-mcp/tools/qemu.py
AuroraCrimsonRose e471f9bc54 Added many tools
2026-06-03 06:01:06 -05:00

155 lines
3.7 KiB
Python

from __future__ import annotations
from typing import Any
import subprocess
import json
from pathlib import Path
from core.tools.base import BaseTool, ToolContext
from core.tools.registry import registry
from core.safety import safety
from core.events import bus
VM_STATE_FILE = Path("./vm_state.json")
class QemuTool(BaseTool):
name = "qemu"
description = "QEMU VM lifecycle management"
# -------------------------
# EXECUTE ROUTER
# -------------------------
def execute(self, payload: dict[str, Any], ctx: ToolContext):
action = str(payload.get("action", "")).strip()
bus.log(
"QEMU",
"qemu_execute",
"INFO",
{"action": action}
)
match action:
case "start":
return self.start_vm(payload, ctx)
case "stop":
return self.stop_vm(payload, ctx)
case "status":
return self.status(payload)
case "list":
return self.list_vms()
case _:
raise ValueError(f"Unknown qemu action: {action}")
# -------------------------
# STATE HELPERS
# -------------------------
def _load_state(self) -> dict[str, Any]:
if VM_STATE_FILE.exists():
return json.loads(VM_STATE_FILE.read_text())
return {}
def _save_state(self, state: dict[str, Any]):
VM_STATE_FILE.write_text(json.dumps(state, indent=2))
# -------------------------
# START VM
# -------------------------
def start_vm(self, payload: dict[str, Any], ctx: ToolContext):
name = payload.get("name")
image = payload.get("image")
if not isinstance(name, str):
raise ValueError("name must be string")
if not isinstance(image, str):
raise ValueError("image must be string")
image_path = safety.validate_path(image)
state = self._load_state()
if name in state and state[name].get("running"):
return {"status": "already_running", "name": name}
cmd = [
"qemu-system-x86_64",
"-m", "2048",
"-drive", f"file={image_path},format=qcow2",
"-nographic"
]
process = subprocess.Popen(cmd)
state[name] = {
"pid": process.pid,
"image": str(image_path),
"running": True
}
self._save_state(state)
return {
"status": "started",
"name": name,
"pid": process.pid
}
# -------------------------
# STOP VM
# -------------------------
def stop_vm(self, payload: dict[str, Any], ctx: ToolContext):
name = payload.get("name")
if not isinstance(name, str):
raise ValueError("name must be string")
state = self._load_state()
vm = state.get(name)
if not vm or not vm.get("running"):
return {"status": "not_running", "name": name}
pid = vm["pid"]
try:
subprocess.run(["kill", str(pid)], check=False)
except Exception:
pass
vm["running"] = False
self._save_state(state)
return {"status": "stopped", "name": name, "pid": pid}
# -------------------------
# STATUS
# -------------------------
def status(self, payload: dict[str, Any]):
name = payload.get("name")
if not isinstance(name, str):
raise ValueError("name must be string")
state = self._load_state()
return state.get(name, {"status": "unknown"})
# -------------------------
# LIST
# -------------------------
def list_vms(self):
return self._load_state()
registry.register(QemuTool())