Parcourir la source

Función de añadir disco a VM

Celestino Rey il y a 6 mois
Parent
commit
18c3a05a08
3 fichiers modifiés avec 150 ajouts et 31 suppressions
  1. 65 0
      app/main.py
  2. 66 31
      static/app.js
  3. 19 0
      static/index.html

+ 65 - 0
app/main.py

@@ -326,6 +326,71 @@ async def delete_iso(iso_name: str):
     else:
         return {"error": "ISO not found or invalid"}
 
+@app.post("/vms/{vm_name}/add-disk")
+async def add_disk_to_vm(vm_name: str, disk_name: str, disk_size: int):
+    images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')
+    disk_path = f"{images_path}/{disk_name}.qcow2"
+    
+    try:
+        # Check if disk already exists
+        if os.path.exists(disk_path):
+            return {"error": f"Disk {disk_name}.qcow2 already exists"}
+        
+        # Create the disk
+        result = subprocess.run(['qemu-img', 'create', '-f', 'qcow2', disk_path, f'{disk_size}G'], 
+                              capture_output=True, text=True)
+        if result.returncode != 0:
+            return {"error": f"Failed to create disk: {result.stderr}"}
+        
+        # Get the VM
+        domain = conn.lookupByName(vm_name)
+        if domain.isActive():
+            return {"error": "VM must be shut off to add a disk"}
+        
+        # Get the XML and add the disk
+        xml_desc = domain.XMLDesc()
+        root = ET.fromstring(xml_desc)
+        
+        # Find the devices section
+        devices = root.find("devices")
+        if devices is None:
+            return {"error": "No devices section found in VM XML"}
+        
+        # Find the next available disk target (vda, vdb, vdc, etc.)
+        existing_targets = set()
+        for disk in root.findall(".//disk/target"):
+            existing_targets.add(disk.get('dev'))
+        
+        # Generate next available target
+        target_letters = "abcdefghijklmnopqrstuvwxyz"
+        next_target = None
+        for i, letter in enumerate(target_letters):
+            target = f"vd{letter}"
+            if target not in existing_targets:
+                next_target = target
+                break
+        
+        if next_target is None:
+            return {"error": "No available disk targets"}
+        
+        # Create the new disk element
+        new_disk = ET.Element("disk", {"type": "file", "device": "disk"})
+        ET.SubElement(new_disk, "driver", {"name": "qemu", "type": "qcow2"})
+        ET.SubElement(new_disk, "source", {"file": disk_path})
+        ET.SubElement(new_disk, "target", {"dev": next_target, "bus": "virtio"})
+        
+        # Add the disk to devices
+        devices.append(new_disk)
+        
+        # Convert back to string and redefine the VM
+        new_xml = ET.tostring(root, encoding='unicode')
+        domain.undefine()
+        conn.defineXML(new_xml)
+        
+        return {"message": f"Disk {disk_name}.qcow2 ({disk_size}G) added to {vm_name} as {next_target}"}
+    except Exception as e:
+        return {"error": str(e)}
+
 @app.post("/disks")
 async def create_disk(name: str, size: int):
     images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')

+ 66 - 31
static/app.js

@@ -75,6 +75,8 @@ async function loadVMs() {
         tbody.innerHTML = '';
         const select = document.getElementById('bootVM');
         select.innerHTML = '<option value="">Select VM</option>';
+        const diskSelect = document.getElementById('diskVM');
+        diskSelect.innerHTML = '<option value="">Select VM</option>';
         
         data.vms.forEach(vm => {
             const row = document.createElement('tr');
@@ -92,6 +94,7 @@ async function loadVMs() {
             `;
             tbody.appendChild(row);
             select.innerHTML += `<option value="${vm.name}">${vm.name}</option>`;
+            diskSelect.innerHTML += `<option value="${vm.name}">${vm.name}</option>`;
         });
         loadISOsForSelect();  // Load ISOs after loading VMs
     } catch (error) {
@@ -275,35 +278,64 @@ document.getElementById('createVMForm').addEventListener('submit', async (e) =>
     }
 });
 
-document.getElementById('setBootOrderForm').addEventListener('submit', async (e) => {
-    e.preventDefault();
-    const vm_name = document.getElementById('bootVM').value;
-    const boot_order_str = document.getElementById('bootOrder').value;
-    const order = boot_order_str.split(',').map(s => s.trim());
-    
-    try {
-        const response = await fetch(`${API_BASE}/vms/${vm_name}/boot-order`, {
-            method: 'PUT',
-            headers: {
-                'Content-Type': 'application/json'
-            },
-            body: JSON.stringify({ order: order })
-        });
-        const result = await response.json();
-        if (response.ok) {
-            alert(result.message);
-            loadVMs();
-            document.getElementById('setBootOrderForm').reset();
-        } else {
-            alert(result.error);
+if (document.getElementById('setBootOrderForm')) {
+    document.getElementById('setBootOrderForm').addEventListener('submit', async (e) => {
+        e.preventDefault();
+        const vm_name = document.getElementById('bootVM').value;
+        const boot_order_str = document.getElementById('bootOrder').value;
+        const order = boot_order_str.split(',').map(s => s.trim());
+        
+        try {
+            const response = await fetch(`${API_BASE}/vms/${vm_name}/boot-order`, {
+                method: 'PUT',
+                headers: {
+                    'Content-Type': 'application/json'
+                },
+                body: JSON.stringify({ order: order })
+            });
+            const result = await response.json();
+            if (response.ok) {
+                alert(result.message);
+                loadVMs();
+                document.getElementById('setBootOrderForm').reset();
+            } else {
+                alert(result.error);
+            }
+        } catch (error) {
+            console.error('Error setting boot order:', error);
+            alert('Error setting boot order');
         }
-    } catch (error) {
-        console.error('Error setting boot order:', error);
-        alert('Error setting boot order');
-    }
-});
+    });
+}
+
+if (document.getElementById('addDiskForm')) {
+    document.getElementById('addDiskForm').addEventListener('submit', async (e) => {
+        e.preventDefault();
+        const vm_name = document.getElementById('diskVM').value;
+        const disk_name = document.getElementById('diskName').value;
+        const disk_size = document.getElementById('diskSize').value;
+        
+        try {
+            const response = await fetch(`${API_BASE}/vms/${vm_name}/add-disk?disk_name=${disk_name}&disk_size=${disk_size}`, {
+                method: 'POST'
+            });
+            const result = await response.json();
+            if (response.ok) {
+                alert(result.message);
+                loadVMs();
+                document.getElementById('addDiskForm').reset();
+            } else {
+                alert(result.error || 'Error adding disk');
+            }
+        } catch (error) {
+            console.error('Error adding disk:', error);
+            alert('Error adding disk');
+        }
+    });
+}
 
-document.getElementById('uploadIsoForm').addEventListener('submit', (e) => {
+if (document.getElementById('uploadIsoForm')) {
+    document.getElementById('uploadIsoForm').addEventListener('submit', (e) => {
     e.preventDefault();
     const fileInput = document.getElementById('isoFile');
     const file = fileInput.files[0];
@@ -362,8 +394,11 @@ document.getElementById('uploadIsoForm').addEventListener('submit', (e) => {
     
     xhr.open('POST', `${API_BASE}/upload-iso`);
     xhr.send(formData);
-});
+    });
+}
 
-// Load VMs on page load
-loadVMs();
-loadISOs();
+// Initialize event listeners and load data when DOM is ready
+document.addEventListener('DOMContentLoaded', function() {
+    loadVMs();
+    loadISOs();
+});

+ 19 - 0
static/index.html

@@ -71,6 +71,25 @@
                     <button type="submit" class="btn btn-warning">Set Boot Order</button>
                 </form>
                 
+                <h2 class="mt-4">Add Disk to VM</h2>
+                <form id="addDiskForm">
+                    <div class="mb-3">
+                        <label for="diskVM" class="form-label">VM Name</label>
+                        <select class="form-control" id="diskVM" required>
+                            <option value="">Select VM</option>
+                        </select>
+                    </div>
+                    <div class="mb-3">
+                        <label for="diskName" class="form-label">Disk Name</label>
+                        <input type="text" class="form-control" id="diskName" placeholder="e.g., data-disk" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="diskSize" class="form-label">Disk Size (GB)</label>
+                        <input type="number" class="form-control" id="diskSize" min="1" value="10" required>
+                    </div>
+                    <button type="submit" class="btn btn-info">Add Disk</button>
+                </form>
+                
                 <h2 class="mt-4">Upload ISO</h2>
                 <form id="uploadIsoForm" enctype="multipart/form-data">
                     <div class="mb-3">