main.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. from fastapi import FastAPI
  2. from fastapi.staticfiles import StaticFiles
  3. from fastapi.responses import FileResponse
  4. import os
  5. import libvirt
  6. import subprocess
  7. import random
  8. app = FastAPI(title="VirtManager API", description="API for managing virtual machines")
  9. app.mount("/static", StaticFiles(directory="static"), name="static")
  10. # Connect to libvirt
  11. conn = libvirt.open('qemu:///system')
  12. @app.get("/")
  13. async def root():
  14. return FileResponse("static/index.html")
  15. @app.get("/vms")
  16. async def list_vms():
  17. domains = conn.listAllDomains()
  18. vms = []
  19. for domain in domains:
  20. vms.append({
  21. "id": domain.ID(),
  22. "name": domain.name(),
  23. "state": domain.state()[0]
  24. })
  25. return {"vms": vms}
  26. @app.get("/vms/{vm_id}/start")
  27. async def start_vm(vm_id: int):
  28. # try:
  29. # domain = conn.lookupByID(vm_id)
  30. # domain.create()
  31. # return {"message": f"VM {vm_id} started"}
  32. # except Exception as e:
  33. # return {"error": str(e)}
  34. return {"message": f"VM {vm_id} started (mock)"}
  35. @app.get("/vms/{vm_id}/stop")
  36. async def stop_vm(vm_id: int):
  37. # try:
  38. # domain = conn.lookupByID(vm_id)
  39. # domain.destroy()
  40. # return {"message": f"VM {vm_id} stopped"}
  41. # except Exception as e:
  42. # return {"error": str(e)}
  43. return {"message": f"VM {vm_id} stopped (mock)"}
  44. @app.post("/vms")
  45. async def create_vm(name: str, memory: int, disk_size: int, iso: str):
  46. images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')
  47. disk_path = f"{images_path}/{name}.qcow2"
  48. iso_path = f"{images_path}/{iso}"
  49. print(f"Using ISO path: {iso_path}")
  50. # Create disk
  51. try:
  52. result = subprocess.run(['qemu-img', 'create', '-f', 'qcow2', disk_path, f'{disk_size}G'], capture_output=True, text=True)
  53. if result.returncode != 0:
  54. return {"error": f"Failed to create disk: {result.stderr}"}
  55. except Exception as e:
  56. return {"error": f"Error creating disk: {str(e)}"}
  57. # Generate unique MAC
  58. mac = f"52:54:00:{random.randint(0, 255):02x}:{random.randint(0, 255):02x}:{random.randint(0, 255):02x}"
  59. # Basic XML template
  60. xml_template = f"""<domain type='kvm'>
  61. <name>{name}</name>
  62. <memory unit='MiB'>{memory}</memory>
  63. <currentMemory unit='MiB'>{memory}</currentMemory>
  64. <vcpu placement='static'>1</vcpu>
  65. <os>
  66. <type arch='x86_64' machine='pc-i440fx-6.2'>hvm</type>
  67. <boot dev='cdrom'/>
  68. </os>
  69. <features>
  70. <acpi/>
  71. <apic/>
  72. </features>
  73. <cpu mode='host-model' check='partial'/>
  74. <clock offset='utc'>
  75. <timer name='rtc' tickpolicy='catchup'/>
  76. <timer name='pit' tickpolicy='delay'/>
  77. <timer name='hpet' present='no'/>
  78. </clock>
  79. <on_poweroff>destroy</on_poweroff>
  80. <on_reboot>restart</on_reboot>
  81. <on_crash>destroy</on_crash>
  82. <pm>
  83. <suspend-to-mem enabled='no'/>
  84. <suspend-to-disk enabled='no'/>
  85. </pm>
  86. <devices>
  87. <emulator>/usr/bin/kvm</emulator>
  88. <disk type='file' device='disk'>
  89. <driver name='qemu' type='qcow2'/>
  90. <source file='{disk_path}'/>
  91. <target dev='vda' bus='virtio'/>
  92. </disk>
  93. <disk type='file' device='cdrom'>
  94. <driver name='qemu' type='raw'/>
  95. <source file='{iso_path}'/>
  96. <target dev='hda' bus='ide'/>
  97. <readonly/>
  98. </disk>
  99. <interface type='network'>
  100. <mac address='{mac}'/>
  101. <source network='default'/>
  102. <model type='virtio'/>
  103. </interface>
  104. <console type='pty'>
  105. <target type='serial' port='0'/>
  106. </console>
  107. <input type='tablet' bus='usb'>
  108. <address type='usb' bus='0' port='1'/>
  109. </input>
  110. <input type='mouse' bus='ps2'/>
  111. <input type='keyboard' bus='ps2'/>
  112. <graphics type='vnc' port='-1' autoport='yes'/>
  113. <video>
  114. <model type='cirrus' vram='16384' heads='1' primary='yes'/>
  115. </video>
  116. <memballoon model='virtio'>
  117. <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
  118. </memballoon>
  119. </devices>
  120. </domain>"""
  121. try:
  122. domain = conn.defineXML(xml_template)
  123. domain.create()
  124. return {"message": f"VM {name} created with {memory} MiB memory, {disk_size}G disk, booting from {iso}"}
  125. except Exception as e:
  126. return {"error": str(e)}
  127. @app.delete("/vms/{vm_name}")
  128. async def delete_vm(vm_name: str):
  129. try:
  130. domain = conn.lookupByName(vm_name)
  131. if domain.isActive():
  132. domain.destroy()
  133. domain.undefine()
  134. return {"message": f"VM {vm_name} deleted"}
  135. except Exception as e:
  136. return {"error": str(e)}
  137. @app.post("/disks")
  138. async def create_disk(name: str, size: int):
  139. images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')
  140. disk_path = f"{images_path}/{name}.qcow2"
  141. try:
  142. result = subprocess.run(['qemu-img', 'create', '-f', 'qcow2', disk_path, f'{size}G'], capture_output=True, text=True)
  143. if result.returncode == 0:
  144. return {"message": f"Disk {name}.qcow2 created with size {size}G"}
  145. else:
  146. return {"error": result.stderr}
  147. except Exception as e:
  148. return {"error": str(e)}