Forráskód Böngészése

Añado botón de información para cada VM

Celestino Rey 6 hónapja
szülő
commit
e0859b9406
3 módosított fájl, 174 hozzáadás és 1 törlés
  1. 58 0
      app/main.py
  2. 96 1
      static/app.js
  3. 20 0
      static/index.html

+ 58 - 0
app/main.py

@@ -64,6 +64,64 @@ async def list_vms():
         })
     return {"vms": vms}
 
+@app.get("/vms/{vm_name}/info")
+async def get_vm_info(vm_name: str):
+    try:
+        domain = conn.lookupByName(vm_name)
+        xml_desc = domain.XMLDesc()
+        root = ET.fromstring(xml_desc)
+        
+        # Get basic info
+        memory = root.find("memory").text if root.find("memory") is not None else "Unknown"
+        cpus = root.find("vcpu").text if root.find("vcpu") is not None else "Unknown"
+        
+        # Get VNC port
+        graphics = root.find(".//graphics[@type='vnc']")
+        vnc_port = None
+        if graphics is not None:
+            port_str = graphics.get('port')
+            if port_str and port_str != '-1':
+                vnc_port = int(port_str)
+        
+        # Get disks
+        disks = []
+        for disk in root.findall(".//disk"):
+            device = disk.get('device')
+            driver = disk.find("driver")
+            source = disk.find("source")
+            target = disk.find("target")
+            disks.append({
+                "type": device,
+                "driver": driver.get('type') if driver is not None else "unknown",
+                "source": source.get('file') if source is not None else "unknown",
+                "target": target.get('dev') if target is not None else "unknown"
+            })
+        
+        # Get network interfaces
+        interfaces = []
+        for iface in root.findall(".//interface"):
+            mac = iface.find("mac")
+            source = iface.find("source")
+            model = iface.find("model")
+            interfaces.append({
+                "mac": mac.get('address') if mac is not None else "unknown",
+                "network": source.get('network') if source is not None else "unknown",
+                "model": model.get('type') if model is not None else "unknown"
+            })
+        
+        return {
+            "id": domain.ID(),
+            "name": domain.name(),
+            "state": get_state_string(domain.state()[0]),
+            "memory": int(memory),
+            "cpus": int(cpus),
+            "vnc_port": vnc_port,
+            "disks": disks,
+            "interfaces": interfaces
+        }
+    except Exception as e:
+        return {"error": str(e)}, 500
+
 @app.get("/vms/{vm_name}/start")
 async def start_vm(vm_name: str):
     try:

+ 96 - 1
static/app.js

@@ -83,10 +83,11 @@ async function loadVMs() {
                 <td>${vm.name}</td>
                 <td><span class="badge ${getStateClass(vm.state)}">${vm.state}</span></td>
                 <td>
+                    <button class="btn btn-sm btn-info" onclick="showVMInfo('${vm.name}')">Info</button>
                     <button class="btn btn-sm btn-success" onclick="startVM('${vm.name}')">Lanzar</button>
                     <button class="btn btn-sm btn-warning" onclick="stopVM('${vm.name}')">Stop</button>
                     <button class="btn btn-sm btn-danger" onclick="deleteVM('${vm.name}')">Delete</button>
-                    ${vm.vnc_port ? `<button class="btn btn-sm btn-info" onclick="openVNC('${vm.name}')">Console</button>` : ''}
+                    ${vm.vnc_port ? `<button class="btn btn-sm btn-primary" onclick="openVNC('${vm.name}')">Console</button>` : ''}
                 </td>
             `;
             tbody.appendChild(row);
@@ -101,6 +102,100 @@ async function loadVMs() {
 
 
 
+async function showVMInfo(name) {
+    try {
+        const response = await fetch(`${API_BASE}/vms/${name}/info`);
+        const data = await response.json();
+        
+        if (!response.ok) {
+            alert(data.error || 'Error loading VM information');
+            return;
+        }
+        
+        const modalBody = document.getElementById('infoModalBody');
+        modalBody.innerHTML = `
+            <div class="row mb-3">
+                <div class="col-md-6">
+                    <h6><strong>Nombre:</strong></h6>
+                    <p>${data.name}</p>
+                </div>
+                <div class="col-md-6">
+                    <h6><strong>ID:</strong></h6>
+                    <p>${data.id}</p>
+                </div>
+            </div>
+            <div class="row mb-3">
+                <div class="col-md-6">
+                    <h6><strong>Estado:</strong></h6>
+                    <p><span class="badge ${getStateClass(data.state)}">${data.state}</span></p>
+                </div>
+                <div class="col-md-6">
+                    <h6><strong>Memoria:</strong></h6>
+                    <p>${data.memory} MiB</p>
+                </div>
+            </div>
+            <div class="row mb-3">
+                <div class="col-md-6">
+                    <h6><strong>CPUs:</strong></h6>
+                    <p>${data.cpus}</p>
+                </div>
+                <div class="col-md-6">
+                    <h6><strong>Puerto VNC:</strong></h6>
+                    <p>${data.vnc_port || 'No asignado'}</p>
+                </div>
+            </div>
+            <div class="mb-3">
+                <h6><strong>Discos:</strong></h6>
+                <table class="table table-sm">
+                    <thead>
+                        <tr>
+                            <th>Dispositivo</th>
+                            <th>Tipo</th>
+                            <th>Ruta</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        ${data.disks.map(disk => `
+                            <tr>
+                                <td>${disk.target}</td>
+                                <td>${disk.type}</td>
+                                <td>${disk.source}</td>
+                            </tr>
+                        `).join('')}
+                    </tbody>
+                </table>
+            </div>
+            <div class="mb-3">
+                <h6><strong>Interfaces de Red:</strong></h6>
+                <table class="table table-sm">
+                    <thead>
+                        <tr>
+                            <th>MAC</th>
+                            <th>Red</th>
+                            <th>Modelo</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        ${data.interfaces.map(iface => `
+                            <tr>
+                                <td>${iface.mac}</td>
+                                <td>${iface.network}</td>
+                                <td>${iface.model}</td>
+                            </tr>
+                        `).join('')}
+                    </tbody>
+                </table>
+            </div>
+        `;
+        
+        const modal = new bootstrap.Modal(document.getElementById('infoModal'));
+        modal.show();
+    } catch (error) {
+        console.error('Error loading VM information:', error);
+        alert('Error loading VM information');
+    }
+}
+
 async function startVM(name) {
     try {
         await fetch(`${API_BASE}/vms/${name}/start`);

+ 20 - 0
static/index.html

@@ -102,6 +102,26 @@
         </div>
     </div>
 
+    <!-- Modal para información de VM -->
+    <div class="modal fade" id="infoModal" tabindex="-1">
+        <div class="modal-dialog modal-lg">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">Información de VM</h5>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+                </div>
+                <div class="modal-body" id="infoModalBody">
+                    <div class="spinner-border" role="status">
+                        <span class="visually-hidden">Loading...</span>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cerrar</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
     <script src="/static/app.js"></script>
 </body>