|
@@ -7,6 +7,7 @@ import subprocess
|
|
|
import random
|
|
import random
|
|
|
import xml.etree.ElementTree as ET
|
|
import xml.etree.ElementTree as ET
|
|
|
import socket
|
|
import socket
|
|
|
|
|
+from pydantic import BaseModel
|
|
|
|
|
|
|
|
app = FastAPI(title="VirtManager API", description="API for managing virtual machines")
|
|
app = FastAPI(title="VirtManager API", description="API for managing virtual machines")
|
|
|
|
|
|
|
@@ -32,6 +33,9 @@ def get_state_string(state_code):
|
|
|
}
|
|
}
|
|
|
return states.get(state_code, "unknown")
|
|
return states.get(state_code, "unknown")
|
|
|
|
|
|
|
|
|
|
+class BootOrder(BaseModel):
|
|
|
|
|
+ order: list[str]
|
|
|
|
|
+
|
|
|
# Connect to libvirt
|
|
# Connect to libvirt
|
|
|
conn = libvirt.open('qemu:///system')
|
|
conn = libvirt.open('qemu:///system')
|
|
|
|
|
|
|
@@ -60,25 +64,23 @@ async def list_vms():
|
|
|
})
|
|
})
|
|
|
return {"vms": vms}
|
|
return {"vms": vms}
|
|
|
|
|
|
|
|
-@app.get("/vms/{vm_id}/start")
|
|
|
|
|
-async def start_vm(vm_id: int):
|
|
|
|
|
- # try:
|
|
|
|
|
- # domain = conn.lookupByID(vm_id)
|
|
|
|
|
- # domain.create()
|
|
|
|
|
- # return {"message": f"VM {vm_id} started"}
|
|
|
|
|
- # except Exception as e:
|
|
|
|
|
- # return {"error": str(e)}
|
|
|
|
|
- return {"message": f"VM {vm_id} started (mock)"}
|
|
|
|
|
-
|
|
|
|
|
-@app.get("/vms/{vm_id}/stop")
|
|
|
|
|
-async def stop_vm(vm_id: int):
|
|
|
|
|
- # try:
|
|
|
|
|
- # domain = conn.lookupByID(vm_id)
|
|
|
|
|
- # domain.destroy()
|
|
|
|
|
- # return {"message": f"VM {vm_id} stopped"}
|
|
|
|
|
- # except Exception as e:
|
|
|
|
|
- # return {"error": str(e)}
|
|
|
|
|
- return {"message": f"VM {vm_id} stopped (mock)"}
|
|
|
|
|
|
|
+@app.get("/vms/{vm_name}/start")
|
|
|
|
|
+async def start_vm(vm_name: str):
|
|
|
|
|
+ try:
|
|
|
|
|
+ domain = conn.lookupByName(vm_name)
|
|
|
|
|
+ domain.create()
|
|
|
|
|
+ return {"message": f"VM {vm_name} started"}
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return {"error": str(e)}
|
|
|
|
|
+
|
|
|
|
|
+@app.get("/vms/{vm_name}/stop")
|
|
|
|
|
+async def stop_vm(vm_name: str):
|
|
|
|
|
+ try:
|
|
|
|
|
+ domain = conn.lookupByName(vm_name)
|
|
|
|
|
+ domain.destroy()
|
|
|
|
|
+ return {"message": f"VM {vm_name} stopped"}
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return {"error": str(e)}
|
|
|
|
|
|
|
|
@app.post("/vms")
|
|
@app.post("/vms")
|
|
|
async def create_vm(name: str, memory: int, disk_size: int, iso: str):
|
|
async def create_vm(name: str, memory: int, disk_size: int, iso: str):
|
|
@@ -210,6 +212,29 @@ async def delete_vm(vm_name: str):
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
return {"error": str(e)}
|
|
return {"error": str(e)}
|
|
|
|
|
|
|
|
|
|
+@app.put("/vms/{vm_name}/boot-order")
|
|
|
|
|
+async def set_boot_order(vm_name: str, boot_order: BootOrder):
|
|
|
|
|
+ try:
|
|
|
|
|
+ domain = conn.lookupByName(vm_name)
|
|
|
|
|
+ if domain.isActive():
|
|
|
|
|
+ return {"error": "VM must be shut off to change boot order"}
|
|
|
|
|
+ xml_desc = domain.XMLDesc()
|
|
|
|
|
+ root = ET.fromstring(xml_desc)
|
|
|
|
|
+ os_section = root.find("os")
|
|
|
|
|
+ # Remove existing boot elements
|
|
|
|
|
+ for boot in os_section.findall("boot"):
|
|
|
|
|
+ os_section.remove(boot)
|
|
|
|
|
+ # Add new boot elements
|
|
|
|
|
+ for dev in boot_order.order:
|
|
|
|
|
+ ET.SubElement(os_section, "boot", {"dev": dev})
|
|
|
|
|
+ new_xml = ET.tostring(root, encoding='unicode')
|
|
|
|
|
+ # Undefine and redefine
|
|
|
|
|
+ domain.undefine()
|
|
|
|
|
+ new_domain = conn.defineXML(new_xml)
|
|
|
|
|
+ return {"message": f"Boot order for {vm_name} updated to {boot_order.order}"}
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return {"error": str(e)}
|
|
|
|
|
+
|
|
|
@app.post("/disks")
|
|
@app.post("/disks")
|
|
|
async def create_disk(name: str, size: int):
|
|
async def create_disk(name: str, size: int):
|
|
|
images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')
|
|
images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')
|