feat(vm): Medien pro VM (disk/floppy/iso) + ARIA-Urteilswissen
vm_register + Registry + boot fassen jetzt das ECHTE Boot-Medium (disk/floppy/ iso) — Eintrag, Startbefehl und App-Start spiegeln die reale Config. Leer = aria-vm erkennt disk.qcow2/floppy.img/cdrom.iso selbst. - project_vms.add_vm: floppy/disk-Felder. - main.py: VmAddBody + _vm_boot_args (baut --disk/--floppy/--iso), boot-Endpoint + boot_cmd nutzen sie. - agent.py: vm_register-Tool bekommt disk/floppy/iso. - Seed-Regel: URTEIL — VM nur wenn sinnvoll; Medium aus der Situation (Festplatte fuer DOS-Spiele, Diskette fuer OS-Dev, ISO fuer Installer); Architektur zum Task (ARM=aarch64, emuliert/langsam ok); Aenderungs-Zyklus stop→aendern→boot. py_compile clean. aria-vm schon live auf dem Host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+11
-4
@@ -1170,15 +1170,20 @@ META_TOOLS = [
|
|||||||
"Traegt eine QEMU-VM in die VM-Liste des AKTUELLEN Projekts ein, "
|
"Traegt eine QEMU-VM in die VM-Liste des AKTUELLEN Projekts ein, "
|
||||||
"damit sie in Stefans Desktop-Panel (Cockpit) erscheint und er sie "
|
"damit sie in Stefans Desktop-Panel (Cockpit) erscheint und er sie "
|
||||||
"starten/stoppen/verbinden kann. Rufe das auf, NACHDEM Du mit aria-vm "
|
"starten/stoppen/verbinden kann. Rufe das auf, NACHDEM Du mit aria-vm "
|
||||||
"eine VM gebaut/gebootet hast (z.B. bei einem OS-Bau-Projekt). "
|
"eine VM gebaut/gebootet hast — mit den Medien, die zum Task passen: "
|
||||||
"vnc_display bestimmt den VNC-Port (Port = 5900+display)."
|
"disk (Festplatte, z.B. DOS-Spiele), floppy (Diskette, OS-Dev), iso "
|
||||||
|
"(Installer). Leer lassen = aria-vm erkennt disk.qcow2/floppy.img/"
|
||||||
|
"cdrom.iso im VM-Ordner selbst. vnc_display → Port 5900+display "
|
||||||
|
"(mehrere VMs = verschiedene Displays)."
|
||||||
),
|
),
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"type": "string", "description": "VM-Name wie bei aria-vm (a-z0-9_-)."},
|
"name": {"type": "string", "description": "VM-Name wie bei aria-vm (a-z0-9_-)."},
|
||||||
"arch": {"type": "string", "description": "z.B. i386, x86_64, aarch64, mips."},
|
"arch": {"type": "string", "description": "z.B. i386, x86_64, aarch64, mips (Architektur zum Task waehlen)."},
|
||||||
"iso": {"type": "string", "description": "optional: Boot-ISO-Pfad auf dem Host."},
|
"disk": {"type": "string", "description": "optional: qcow2-Pfad (Festplatte)."},
|
||||||
|
"floppy": {"type": "string", "description": "optional: Disketten-Image-Pfad (-fda)."},
|
||||||
|
"iso": {"type": "string", "description": "optional: Boot-ISO-Pfad."},
|
||||||
"vnc_display": {"type": "integer", "description": "VNC-Display (Default 1 → Port 5901)."},
|
"vnc_display": {"type": "integer", "description": "VNC-Display (Default 1 → Port 5901)."},
|
||||||
"mem": {"type": "integer", "description": "RAM in MB (Default 1024)."},
|
"mem": {"type": "integer", "description": "RAM in MB (Default 1024)."},
|
||||||
},
|
},
|
||||||
@@ -2839,6 +2844,8 @@ class Agent:
|
|||||||
pid, (arguments.get("name") or "").strip(),
|
pid, (arguments.get("name") or "").strip(),
|
||||||
(arguments.get("arch") or "i386").strip(),
|
(arguments.get("arch") or "i386").strip(),
|
||||||
(arguments.get("iso") or "").strip(),
|
(arguments.get("iso") or "").strip(),
|
||||||
|
(arguments.get("floppy") or "").strip(),
|
||||||
|
(arguments.get("disk") or "").strip(),
|
||||||
int(arguments.get("vnc_display") or 1),
|
int(arguments.get("vnc_display") or 1),
|
||||||
int(arguments.get("mem") or 1024),
|
int(arguments.get("mem") or 1024),
|
||||||
)
|
)
|
||||||
|
|||||||
+18
-12
@@ -961,20 +961,30 @@ class VmAddBody(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
arch: str = "i386"
|
arch: str = "i386"
|
||||||
iso: str = ""
|
iso: str = ""
|
||||||
|
floppy: str = ""
|
||||||
|
disk: str = ""
|
||||||
vnc_display: int = 1
|
vnc_display: int = 1
|
||||||
mem: int = 1024
|
mem: int = 1024
|
||||||
create_disk: bool = False
|
create_disk: bool = False
|
||||||
size: str = "10G"
|
size: str = "10G"
|
||||||
|
|
||||||
|
|
||||||
|
def _vm_boot_args(v: dict) -> list:
|
||||||
|
args = ["boot", v.get("name", "?"),
|
||||||
|
"--vnc-display", str(v.get("vnc_display", 1)),
|
||||||
|
"--mem", str(v.get("mem", 1024))]
|
||||||
|
if v.get("disk"):
|
||||||
|
args += ["--disk", v["disk"]]
|
||||||
|
if v.get("floppy"):
|
||||||
|
args += ["--floppy", v["floppy"]]
|
||||||
|
if v.get("iso"):
|
||||||
|
args += ["--iso", v["iso"]]
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
def _vm_boot_cmd(v: dict) -> str:
|
def _vm_boot_cmd(v: dict) -> str:
|
||||||
"""Lesbarer Start-Befehl (aria-vm) als 'Wert' hinter dem VM-Eintrag."""
|
"""Lesbarer Start-Befehl (aria-vm) als 'Wert' hinter dem VM-Eintrag."""
|
||||||
parts = ["aria-vm", "boot", v.get("name", "?"),
|
return "aria-vm " + " ".join(_vm_boot_args(v))
|
||||||
"--vnc-display", str(v.get("vnc_display", 1)),
|
|
||||||
"--mem", str(v.get("mem", 1024))]
|
|
||||||
if v.get("iso"):
|
|
||||||
parts += ["--iso", v["iso"]]
|
|
||||||
return " ".join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/projects/{project_id}/vms")
|
@app.get("/projects/{project_id}/vms")
|
||||||
@@ -992,7 +1002,7 @@ def project_vms_list(project_id: str):
|
|||||||
def project_vm_add(project_id: str, body: VmAddBody):
|
def project_vm_add(project_id: str, body: VmAddBody):
|
||||||
try:
|
try:
|
||||||
vm = project_vms_mod.add_vm(project_id, body.name, body.arch, body.iso,
|
vm = project_vms_mod.add_vm(project_id, body.name, body.arch, body.iso,
|
||||||
body.vnc_display, body.mem)
|
body.floppy, body.disk, body.vnc_display, body.mem)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise HTTPException(status_code=400, detail=str(exc))
|
raise HTTPException(status_code=400, detail=str(exc))
|
||||||
if body.create_disk:
|
if body.create_disk:
|
||||||
@@ -1017,11 +1027,7 @@ def project_vm_boot(project_id: str, name: str):
|
|||||||
vm = project_vms_mod.get_vm(project_id, name)
|
vm = project_vms_mod.get_vm(project_id, name)
|
||||||
if not vm:
|
if not vm:
|
||||||
raise HTTPException(status_code=404, detail=f"VM '{name}' nicht gefunden")
|
raise HTTPException(status_code=404, detail=f"VM '{name}' nicht gefunden")
|
||||||
args = ["boot", name, "--vnc-display", str(vm.get("vnc_display", 1)),
|
rc, out, err = _ssh_aria_vm(*_vm_boot_args(vm), timeout=40)
|
||||||
"--mem", str(vm.get("mem", 1024))]
|
|
||||||
if vm.get("iso"):
|
|
||||||
args += ["--iso", vm["iso"]]
|
|
||||||
rc, out, err = _ssh_aria_vm(*args, timeout=40)
|
|
||||||
return {"ok": rc == 0, "name": name, "vnc_port": 5900 + int(vm.get("vnc_display", 1)),
|
return {"ok": rc == 0, "name": name, "vnc_port": 5900 + int(vm.get("vnc_display", 1)),
|
||||||
"output": (out.strip() or err.strip())[:500]}
|
"output": (out.strip() or err.strip())[:500]}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,12 @@ def get_vm(project_id: str, name: str) -> Optional[dict]:
|
|||||||
|
|
||||||
|
|
||||||
def add_vm(project_id: str, name: str, arch: str, iso: str = "",
|
def add_vm(project_id: str, name: str, arch: str, iso: str = "",
|
||||||
|
floppy: str = "", disk: str = "",
|
||||||
vnc_display: int = 1, mem: int = 1024) -> dict:
|
vnc_display: int = 1, mem: int = 1024) -> dict:
|
||||||
|
"""Registriert/aktualisiert eine VM. Medien (disk/floppy/iso) sind optional —
|
||||||
|
leer = aria-vm erkennt disk.qcow2/floppy.img/cdrom.iso im VM-Ordner selbst.
|
||||||
|
ARIA setzt hier, was sie zum Task passend gebaut hat (Festplatte fuer DOS-
|
||||||
|
Spiele, Diskette fuer OS-Dev, ISO fuer Installer)."""
|
||||||
if not NAME_RE.match(name or ""):
|
if not NAME_RE.match(name or ""):
|
||||||
raise ValueError(f"Ungueltiger VM-Name: {name!r} (nur a-z0-9_-, max 40)")
|
raise ValueError(f"Ungueltiger VM-Name: {name!r} (nur a-z0-9_-, max 40)")
|
||||||
if arch not in VALID_ARCH:
|
if arch not in VALID_ARCH:
|
||||||
@@ -60,14 +65,14 @@ def add_vm(project_id: str, name: str, arch: str, iso: str = "",
|
|||||||
data = _load()
|
data = _load()
|
||||||
lst = data.setdefault(project_id or "", [])
|
lst = data.setdefault(project_id or "", [])
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
|
fields = {"arch": arch, "iso": iso, "floppy": floppy, "disk": disk,
|
||||||
|
"vnc_display": int(vnc_display), "mem": int(mem), "updated_at": now}
|
||||||
for v in lst:
|
for v in lst:
|
||||||
if v.get("name") == name:
|
if v.get("name") == name:
|
||||||
v.update({"arch": arch, "iso": iso, "vnc_display": int(vnc_display),
|
v.update(fields)
|
||||||
"mem": int(mem), "updated_at": now})
|
|
||||||
_save(data)
|
_save(data)
|
||||||
return v
|
return v
|
||||||
vm = {"name": name, "arch": arch, "iso": iso, "vnc_display": int(vnc_display),
|
vm = {"name": name, "created_at": now, **fields}
|
||||||
"mem": int(mem), "created_at": now, "updated_at": now}
|
|
||||||
lst.append(vm)
|
lst.append(vm)
|
||||||
_save(data)
|
_save(data)
|
||||||
return vm
|
return vm
|
||||||
|
|||||||
@@ -434,11 +434,25 @@ SEED_RULES: List[dict] = [
|
|||||||
" - `aria-vm screenshot <name>` → PNG (an Stefan per [FILE:] schickbar).\n"
|
" - `aria-vm screenshot <name>` → PNG (an Stefan per [FILE:] schickbar).\n"
|
||||||
" - `aria-vm list` / `aria-vm stop <name>` / `aria-vm rm <name>`.\n"
|
" - `aria-vm list` / `aria-vm stop <name>` / `aria-vm rm <name>`.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"PFLICHT nach dem Bau/Boot: `vm_register(name, arch, vnc_display, mem, iso?)` "
|
"PFLICHT nach dem Bau/Boot: `vm_register(name, arch, disk?/floppy?/iso?, "
|
||||||
"im aktuellen Projekt aufrufen. ERST DANN erscheint die VM in Stefans "
|
"vnc_display, mem)` im aktuellen Projekt aufrufen — mit den Medien, die Du "
|
||||||
"Desktop-Panel (Cockpit), wo er sie Starten/Stoppen/Verbinden kann. Ohne "
|
"gebaut hast. ERST DANN erscheint die VM in Stefans Desktop-Panel (Cockpit), "
|
||||||
"vm_register bleibt seine Liste leer, obwohl die VM laeuft. Der Startbefehl "
|
"wo er sie Starten/Stoppen/Verbinden kann. Ohne vm_register bleibt seine "
|
||||||
"steht dann als Wert hinter dem Eintrag.\n"
|
"Liste leer, obwohl die VM laeuft. Der Startbefehl steht als Wert dahinter.\n"
|
||||||
|
"\n"
|
||||||
|
"URTEIL — bau eine VM NUR wenn's Sinn macht, und erkenne aus der SITUATION "
|
||||||
|
"was gebraucht wird:\n"
|
||||||
|
" - DOS-/Retro-Spiele, installierte Software → FESTPLATTE (create mit "
|
||||||
|
"Groesse, dann installieren; disk).\n"
|
||||||
|
" - Eigenes OS / Bootloader / Bare-Metal-Experiment → DISKETTE (create "
|
||||||
|
"'none', floppy.img; ggf. eine zweite Diskette einbinden).\n"
|
||||||
|
" - Fertiges System installieren/testen → ISO (--iso Installer).\n"
|
||||||
|
" - Architektur zum Task waehlen: 'teste mal was auf ARM' → arch=aarch64 "
|
||||||
|
"(-M virt, laeuft emuliert = langsam, das ist ok). x86-Zeug → i386/x86_64.\n"
|
||||||
|
"AENDERUNGS-ZYKLUS: willst Du an einer laufenden VM etwas aendern (Datei "
|
||||||
|
"tauschen, Diskette neu bauen), dann `aria-vm stop <name>` → Aenderung → "
|
||||||
|
"`aria-vm boot <name>` wieder starten. Stefan kann das auch selbst ueber "
|
||||||
|
"Start/Stop im Panel.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Der VNC-Stream laeuft AUTOMATISCH komplett durch den RVS-Server (die Bridge "
|
"Der VNC-Stream laeuft AUTOMATISCH komplett durch den RVS-Server (die Bridge "
|
||||||
"tunnelt die RFB-Bytes; der VNC-Port ist nur intern auf dem Host). Firewall-"
|
"tunnelt die RFB-Bytes; der VNC-Port ist nur intern auf dem Host). Firewall-"
|
||||||
|
|||||||
Reference in New Issue
Block a user