Explorar el Código

Botón para descargar configuración

Celestino Rey hace 6 meses
padre
commit
dd88810f74
Se han modificado 4 ficheros con 113 adiciones y 23 borrados
  1. 44 6
      README.md
  2. 17 1
      app/main.py
  3. 50 9
      static/dashboard.js
  4. 2 7
      static/index.html

+ 44 - 6
README.md

@@ -8,6 +8,8 @@ Una API REST y interfaz web para gestionar máquinas virtuales en Ubuntu usando
 - **Configuración de arranque**: Cambiar el orden de arranque de las VMs (disco duro, CD-ROM, etc.).
 - **Subida de ISOs**: Sube imágenes ISO directamente desde la interfaz web para usar en la creación de VMs.
 - **Gestión de ISOs**: Lista y elimina ISOs subidas.
+- **Interfaz Responsive**: Diseño moderno con sidebar que se adapta a dispositivos móviles, tablets y escritorio.
+- **Descarga de configuración**: Exporta la configuración XML de cualquier VM para backup o transferencia.
 
 ## Instalación
 
@@ -133,6 +135,8 @@ export LIBVIRT_IMAGES_PATH=/ruta/a/imagenes
   - `memory`: Memoria en MiB.
   - `disk_size`: Tamaño del disco en GB.
   - `iso`: Ruta al archivo ISO para instalación.
+- `GET /vms/{vm_name}/info`: Obtiene información detallada de una VM (CPU, memoria, discos, interfaces, puerto VNC).
+- `GET /vms/{vm_name}/config`: Obtiene la configuración XML completa de una VM para descargar.
 - `GET /vms/{vm_name}/start`: Inicia una VM por nombre.
 - `GET /vms/{vm_name}/stop`: Detiene una VM por nombre.
 - `DELETE /vms/{vm_name}`: Elimina una VM por nombre.
@@ -160,12 +164,32 @@ export LIBVIRT_IMAGES_PATH=/ruta/a/imagenes
 
 Accede a `http://localhost:8000` en tu navegador.
 
-- **Lista de VMs**: Tabla con estado (badges coloreados), acciones (iniciar/detener/eliminar/consola).
-- **Crear VM**: Formulario para especificar nombre, memoria, disco e ISO (seleccionar de ISOs subidas).
-- **Set Boot Order**: Selecciona una VM y define el orden de arranque (ej. "hd,cdrom").
-- **Upload ISO**: Sube archivos ISO para usar en la creación de VMs.
-- **Manage ISOs**: Tabla con ISOs subidas, opción para eliminar.
-- **Consola**: Botón para abrir la consola web en una nueva pestaña (solo para VMs corriendo).
+### Dashboard Responsivo
+
+La interfaz web utiliza un diseño moderno con las siguientes características:
+
+- **Sidebar lateral**: Panel con lista de VMs, rápidamente visible en pantallas grandes
+- **Diseño adaptativo**: Se ajusta automáticamente para tablets y dispositivos móviles
+- **Menú hamburguesa**: En móviles, el sidebar se oculta y aparece un botón de menú (≡) en la barra superior
+- **Información organizada en pestañas**:
+  - **Resumen**: Configuración general (CPU, memoria, estado VNC)
+  - **Discos**: Lista de discos virtuales conectados
+  - **Red**: Configuración de interfaces de red
+
+### Funcionalidades
+
+- **Crear VM**: Formulario para especificar nombre, memoria, disco e ISO
+- **Gestionar ISOs**: 
+  - Subir nuevas imágenes ISO
+  - Ver lista de ISOs disponibles
+  - Eliminar ISOs ya no necesarias
+- **Acciones rápidas por VM**:
+  - Iniciar / Detener
+  - Acceder a consola VNC
+  - Configurar orden de arranque
+  - Agregar discos
+  - **Descargar configuración XML** (para backup o transferencia)
+- **Consola**: Botón para abrir la consola web en una nueva pestaña (solo para VMs corriendo)
 
 ## Documentación de la API
 
@@ -183,6 +207,20 @@ Accede a `http://localhost:8000/docs` para la documentación interactiva de Swag
   2. Esté configurado en sudoers correctamente
   3. Los permisos de `/var/lib/libvirt/images/` sean correctos
 
+### Responsive Design
+
+- **Escritorio (>1024px)**: Vista completa con sidebar de 300px
+- **Tablet (768px-1024px)**: Sidebar más compacto, fuentes ajustadas
+- **Móvil (<768px)**: Sidebar colapsable con botón hamburguesa, optimizado para toques
+- **Teléfonos pequeños (<480px)**: Ajuste máximo de espacios y tamaños de fuente
+
+### Descargar Configuración XML
+
+Cada VM tiene un botón "Descargar XML" en la sección de acciones que permite:
+- Exportar la configuración completa en formato XML
+- Crear backups de la configuración
+- Transferir VMs a otros servidores con la misma configuración
+
 ## Contribución
 
 Siéntete libre de contribuir con mejoras o reportar issues.

+ 17 - 1
app/main.py

@@ -475,4 +475,20 @@ async def create_disk(name: str, size: int):
         else:
             return {"error": result.stderr}
     except Exception as e:
-        return {"error": str(e)}
+        return {"error": str(e)}
+
+@app.get("/vms/{vm_name}/config")
+async def get_vm_config(vm_name: str):
+    """Get VM configuration in XML format"""
+    try:
+        conn = libvirt.open(None)
+        domain = conn.lookupByName(vm_name)
+        xml_desc = domain.XMLDesc()
+        conn.close()
+        
+        return {
+            "name": vm_name,
+            "xml": xml_desc
+        }
+    except libvirt.libvirtError as e:
+        return {"error": f"Error de libvirt: {str(e)}"}

+ 50 - 9
static/dashboard.js

@@ -6,6 +6,12 @@ document.addEventListener('DOMContentLoaded', () => {
     loadVMs();
     loadISOsForSelect();
     
+    // Initialize sidebar state for mobile
+    const sidebar = document.querySelector('.sidebar');
+    if (window.innerWidth <= 768) {
+        sidebar.classList.add('hidden-mobile');
+    }
+    
     // Create VM Form
     document.getElementById('createVMForm').addEventListener('submit', createVM);
 });
@@ -192,6 +198,9 @@ function displayVMDetails(vm, vmName) {
                     <button class="btn btn-sm btn-info btn-action" onclick="openAddDiskModal('${vmName}')" ${vm.state !== 'shut off' ? 'disabled' : ''}>
                         <i class="bi bi-plus-circle"></i> Agregar Disco
                     </button>
+                    <button class="btn btn-sm btn-secondary btn-action" onclick="downloadVMConfig('${vmName}')">
+                        <i class="bi bi-download"></i> Descargar XML
+                    </button>
                 </div>
             </div>
             
@@ -656,13 +665,7 @@ function toggleSidebar() {
     
     // Para móviles (max-width 768px), mostrar/ocultar el sidebar
     if (window.innerWidth <= 768) {
-        if (sidebar.classList.contains('show')) {
-            sidebar.classList.remove('show');
-            sidebar.classList.add('hidden');
-        } else {
-            sidebar.classList.remove('hidden');
-            sidebar.classList.add('show');
-        }
+        sidebar.classList.toggle('show');
     }
 }
 
@@ -675,7 +678,6 @@ window.selectVM = function(vmName) {
     if (window.innerWidth <= 768) {
         const sidebar = document.querySelector('.sidebar');
         sidebar.classList.remove('show');
-        sidebar.classList.add('hidden');
     }
 };
 
@@ -684,7 +686,46 @@ document.addEventListener('shown.bs.modal', function() {
     if (window.innerWidth <= 768) {
         const sidebar = document.querySelector('.sidebar');
         sidebar.classList.remove('show');
-        sidebar.classList.add('hidden');
     }
 });
 
+// Download VM configuration as XML
+async function downloadVMConfig(vmName) {
+    try {
+        const response = await fetch(`${API_BASE}/vms/${vmName}/config`);
+        
+        if (!response.ok) {
+            alert(`Error: El servidor respondió con estado ${response.status}`);
+            return;
+        }
+        
+        const data = await response.json();
+        
+        if (data.error) {
+            alert(`Error: ${data.error}`);
+            return;
+        }
+        
+        // Validar que tenemos el contenido XML
+        if (!data.xml || data.xml === undefined || data.xml === 'undefined') {
+            console.error('XML content is undefined or missing:', data);
+            alert('Error: No se pudo obtener la configuración XML');
+            return;
+        }
+        
+        // Create a blob with the XML content
+        const blob = new Blob([data.xml], { type: 'application/xml' });
+        const url = window.URL.createObjectURL(blob);
+        const link = document.createElement('a');
+        link.href = url;
+        link.download = `${vmName}-config.xml`;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        window.URL.revokeObjectURL(url);
+    } catch (error) {
+        console.error('Error downloading config:', error);
+        alert(`Error al descargar configuración: ${error.message}`);
+    }
+}
+

+ 2 - 7
static/index.html

@@ -356,11 +356,6 @@
                 max-height: calc(100vh - 50px);
             }
             
-            .sidebar.hidden {
-                transform: none;
-                max-height: 0;
-            }
-            
             .main-content {
                 width: 100%;
                 height: 100%;
@@ -550,7 +545,7 @@
 <body>
     <div class="dashboard-container">
         <!-- Sidebar -->
-        <div class="sidebar hidden" id="sidebar">
+        <div class="sidebar" id="sidebar">
             <div class="sidebar-header">
                 <h4><i class="bi bi-display"></i> VirtManager</h4>
                 <p>Virtual Machine Dashboard</p>
@@ -706,6 +701,6 @@
     </div>
 
     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
-    <script src="/static/dashboard.js"></script>
+    <script src="/static/dashboard.js?v=2"></script>
 </body>
 </html>