Browse Source

Añadir disco adicional a VM

Celestino Rey 6 tháng trước cách đây
mục cha
commit
a81f223eeb
8 tập tin đã thay đổi với 658 bổ sung28 xóa
  1. 20 0
      README.md
  2. 137 0
      RUNNING.md
  3. 154 0
      SOLUTION.md
  4. 188 0
      TROUBLESHOOTING.md
  5. 93 20
      app/main.py
  6. 23 0
      create_disk.sh
  7. 35 8
      static/app.js
  8. 8 0
      static/index.html

+ 20 - 0
README.md

@@ -49,6 +49,21 @@ sudo mkdir -p /var/lib/libvirt/images/isos
    # Reinicia la sesión para aplicar cambios
    ```
 
+5. **Configurar permisos para crear discos virtuales:**
+   
+   VirtManager necesita crear discos en `/var/lib/libvirt/images/` con permisos correctos. Para esto, configura el script helper con permisos sudo:
+   
+   ```bash
+   # El script ya está en el proyecto
+   chmod +x create_disk.sh
+   
+   # Configura sudoers para permitir crear discos sin contraseña
+   echo "creylopez ALL=(ALL) NOPASSWD: /home/creylopez/virtmanager/create_disk.sh" | sudo tee /etc/sudoers.d/virtmanager
+   
+   # Verifica que la configuración sea correcta
+   sudo visudo -c -f /etc/sudoers.d/virtmanager
+   ```
+
 ### Configuración del Servicio Systemd (Auto-inicio)
 
 Para que VirtManager se inicie automáticamente con el sistema:
@@ -159,9 +174,14 @@ Accede a `http://localhost:8000/docs` para la documentación interactiva de Swag
 ## Notas
 
 - Las operaciones de VMs requieren que libvirt esté corriendo (`sudo systemctl start libvirtd`).
+- Para crear discos virtuales, el script `create_disk.sh` debe estar configurado con permisos sudo sin contraseña (ver sección de instalación).
 - Para crear VMs, asegúrate de que el usuario tenga permisos en `/var/lib/libvirt/images` o ajusta `LIBVIRT_IMAGES_PATH`.
 - La consola web usa websockify para proxy VNC a WebSocket.
 - El servicio systemd ejecuta como root para acceso completo a libvirt.
+- Si recibes error "undefined" al crear una VM, verifica que:
+  1. El script `create_disk.sh` tenga permisos de ejecución (`chmod +x create_disk.sh`)
+  2. Esté configurado en sudoers correctamente
+  3. Los permisos de `/var/lib/libvirt/images/` sean correctos
 
 ## Contribución
 

+ 137 - 0
RUNNING.md

@@ -0,0 +1,137 @@
+# Instrucciones de Ejecución - VirtManager
+
+## Iniciar el Servidor
+
+### Con sudo (necesario para operaciones de libvirt)
+
+```bash
+cd /home/creylopez/virtmanager
+source venv/bin/activate
+sudo -E /home/creylopez/virtmanager/venv/bin/uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
+```
+
+La opción `-E` preserva las variables de entorno del usuario.
+
+### Verificar que el servidor está corriendo
+
+En otra terminal:
+```bash
+curl http://localhost:8000/vms
+```
+
+Deberías obtener una respuesta JSON con la lista de VMs.
+
+## Prueba Rápida de Creación de VM
+
+1. **Acceder a la interfaz web:**
+   - Abre `http://localhost:8000` en tu navegador
+
+2. **Subir una ISO (si no tienes):**
+   - Ve a la sección "Upload ISO"
+   - Selecciona un archivo ISO
+   - Haz clic en "Upload"
+
+3. **Crear una VM:**
+   - Ve a la sección "Create VM"
+   - Completa los campos:
+     - **Name:** nombre de la VM (ej: mivm)
+     - **Memory:** memoria en MiB (ej: 2048)
+     - **Disk Size:** tamaño del disco en GB (ej: 20)
+     - **CPUs:** número de CPUs (ej: 2)
+     - **ISO:** selecciona la ISO subida
+   - Haz clic en "Create VM"
+
+4. **Verificar creación:**
+   - Si todo va bien, verás un mensaje de éxito
+   - La VM aparecerá en la lista de VMs
+   - El disco se creará en `/var/lib/libvirt/images/`
+
+## Agregar Discos a una VM Existente
+
+⚠️ **REQUISITOS IMPORTANTES:**
+- La máquina virtual **DEBE estar parada** (shut off)
+- El nombre del disco **NO debe existir** previamente
+
+### Pasos:
+
+1. **Parar la VM:**
+   - En la lista de VMs, haz clic en "Stop" en la VM destino
+   - Espera a que cambie el estado a "shut off"
+
+2. **Agregar el disco:**
+   - Ve a la sección "Add Disk to VM"
+   - Selecciona la VM (debe estar parada)
+   - Ingresa un nombre para el disco (ej: datos, backup, etc.)
+   - Ingresa el tamaño en GB (ej: 50)
+   - Haz clic en "Add Disk"
+
+3. **Verificar:**
+   - Si todo va bien, verás un mensaje confirmando que se agregó el disco
+   - El disco estará asignado a un dispositivo virtio (vda, vdb, etc.)
+
+## Troubleshooting
+
+### Si aparece error "undefined"
+
+1. **Verifica los logs del servidor:**
+   ```bash
+   # En la terminal donde corre el servidor, busca lineas que empiezan con [CREATE_VM]
+   ```
+
+2. **Verifica los permisos:**
+   ```bash
+   sudo -l | grep create_disk
+   ls -l /home/creylopez/virtmanager/create_disk.sh
+   ```
+
+3. **Verifica que libvirt está corriendo:**
+   ```bash
+   sudo systemctl status libvirtd
+   ```
+
+4. **Verifica el espacio en disco:**
+   ```bash
+   df -h /var/lib/libvirt/images/
+   ```
+
+### Si el disco se crea pero falla la VM
+
+Esto generalmente significa que hay un problema con la definición de XML de la VM. Los logs del servidor mostrarán el error exacto de libvirt.
+
+Posibles causas:
+- El archivo ISO no existe o no es accesible
+- La ruta del ISO está incorrecta
+- Problema con la arquitectura o tipo de máquina en el XML
+
+## Detener el Servidor
+
+En la terminal donde corre el servidor, presiona `Ctrl+C`
+
+## Logs
+
+El servidor mostrará logs con prefijo `[CREATE_VM]` cuando se crea una VM. Estos logs incluyen:
+- Rutas de archivos
+- Estado de creación de disco
+- Errores de libvirt
+- Mensajes de éxito o fallo
+
+Ejemplo de log exitoso:
+```
+[CREATE_VM] Starting VM creation: name=mivm, memory=2048, disk_size=20, cpus=2
+[CREATE_VM] Using ISO path: /var/lib/libvirt/images/isos/ubuntu-22.04.iso
+[CREATE_VM] Script path: /home/creylopez/virtmanager/create_disk.sh
+[CREATE_VM] Disk creation return code: 0
+[CREATE_VM] Disk created successfully: /var/lib/libvirt/images/mivm.qcow2
+[CREATE_VM] Generated MAC: 52:54:00:ab:cd:ef
+[CREATE_VM] XML template prepared for VM definition
+[CREATE_VM] Defining VM with libvirt...
+[CREATE_VM] VM defined successfully. Starting VM...
+[CREATE_VM] VM started successfully!
+```
+
+## Notas Importantes
+
+1. El servidor debe ejecutarse con `sudo` para acceso total a libvirt
+2. El script `create_disk.sh` debe estar configurado en sudoers sin contraseña
+3. La ruta del script se calcula automáticamente desde la ruta del módulo app
+4. Los errores de libvirt se mostrarán claramente en los logs y en la respuesta del frontend

+ 154 - 0
SOLUTION.md

@@ -0,0 +1,154 @@
+# Solución de Problemas de Creación de VM - Resumen
+
+## Problema Original
+El error "undefined" aparecía al intentar crear una VM, y el disco virtual se creaba pero no se agregaba a la configuración de la VM en libvirt.
+
+## Causas Identificadas
+1. **Falta de permisos para crear discos**: El usuario no tenía permisos para escribir en `/var/lib/libvirt/images/`
+2. **Manejo de errores insuficiente**: Los errores no se propagaban correctamente al frontend
+3. **Falta de logging**: No había suficiente información de debug para identificar el problema
+4. **Ruta de script incorrecta**: La ruta relativa del script helper no funcionaba correctamente
+
+## Soluciones Implementadas
+
+### 1. Script Helper para Crear Discos
+**Archivo:** `create_disk.sh`
+
+- Script que se ejecuta con `sudo` para manejar correctamente los permisos
+- Crea el disco QCOW2 con los permisos correctos para `libvirt-qemu:kvm`
+- Proporciona feedback claro sobre errores
+
+### 2. Configuración de Sudoers
+**Archivo:** `/etc/sudoers.d/virtmanager`
+
+- Permite ejecutar el script sin contraseña
+- Configuración única (no requiere re-ejecutar para cada usuario)
+
+```
+creylopez ALL=(ALL) NOPASSWD: /home/creylopez/virtmanager/create_disk.sh
+```
+
+### 3. Backend Mejorado
+**Archivo:** `app/main.py` - función `create_vm()`
+
+**Cambios:**
+- Usa el script helper en lugar de `qemu-img` directo
+- Calcula la ruta del script de forma dinámica y robusta
+- Agrega logging extensivo con prefijo `[CREATE_VM]`
+- Verifica que el disco existe antes de crear la VM
+- Maneja errores específicos de libvirt
+- Limpia el disco si la definición de la VM falla
+- Timeout de 60 segundos para operaciones largas
+
+### 4. Frontend Mejorado
+**Archivo:** `static/app.js`
+
+**Cambios:**
+- Validación de que se seleccione una ISO
+- Mejor manejo de errores JSON
+- Mensajes más descriptivos en lugar de "undefined"
+- Logging en consola para debugging
+
+### 5. Documentación
+**Archivos nuevos:**
+- `RUNNING.md` - Instrucciones para ejecutar el servidor y probar
+- `TROUBLESHOOTING.md` - Guía de resolución de problemas
+- `README.md` - Actualizado con configuración de permisos
+
+## Validación
+
+Se realizó un test end-to-end que verifica:
+1. ✓ Creación de disco con permisos correctos
+2. ✓ Definición de VM en libvirt
+3. ✓ Verificación de existencia de la VM
+4. ✓ Limpieza correcta de recursos
+
+**Resultado:** ✅ TEST EXITOSO
+
+## Instrucciones de Ejecución
+
+### Configuración (una sola vez)
+
+```bash
+cd /home/creylopez/virtmanager
+chmod +x create_disk.sh
+echo "creylopez ALL=(ALL) NOPASSWD: /home/creylopez/virtmanager/create_disk.sh" | sudo tee /etc/sudoers.d/virtmanager
+sudo visudo -c -f /etc/sudoers.d/virtmanager
+```
+
+### Ejecutar el servidor
+
+```bash
+cd /home/creylopez/virtmanager
+source venv/bin/activate
+sudo -E venv/bin/uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
+```
+
+Accede a `http://localhost:8000`
+
+## Flujo de Creación de VM
+
+```
+Usuario → Frontend (/vms)
+         ↓
+       Backend (create_vm)
+         ↓
+    [1] Crear disco (script helper + sudo)
+         ↓
+    [2] Verificar que disco existe
+         ↓
+    [3] Generar MAC aleatoria
+         ↓
+    [4] Generar XML de configuración
+         ↓
+    [5] Definir VM en libvirt (defineXML)
+         ↓
+    [6] Iniciar VM (create)
+         ↓
+    Respuesta al usuario
+```
+
+## Logs Importantes
+
+Cuando creas una VM, verás logs como:
+
+```
+[CREATE_VM] Starting VM creation: name=mivm, memory=2048, disk_size=20, cpus=2
+[CREATE_VM] Using ISO path: /var/lib/libvirt/images/isos/ubuntu-22.04.iso
+[CREATE_VM] Script path: /home/creylopez/virtmanager/create_disk.sh
+[CREATE_VM] Disk creation return code: 0
+[CREATE_VM] Disk created successfully: /var/lib/libvirt/images/mivm.qcow2
+[CREATE_VM] Generated MAC: 52:54:00:ab:cd:ef
+[CREATE_VM] XML template prepared for VM definition
+[CREATE_VM] Defining VM with libvirt...
+[CREATE_VM] VM defined successfully. Starting VM...
+[CREATE_VM] VM started successfully!
+```
+
+Si hay un error, verás:
+
+```
+[CREATE_VM] ERROR: [descripción del error]
+```
+
+## Archivos Modificados
+
+1. `app/main.py` - Función `create_vm()` mejorada con logging y mejor manejo de errores
+2. `static/app.js` - Frontend con mejor validación y manejo de errores
+3. `README.md` - Documentación actualizada
+4. `create_disk.sh` - Script helper para crear discos
+5. `/etc/sudoers.d/virtmanager` - Configuración de permisos
+
+## Archivos Nuevos
+
+1. `RUNNING.md` - Instrucciones de ejecución
+2. `TROUBLESHOOTING.md` - Guía de troubleshooting
+3. `SOLUTION.md` - Este archivo (documentación de la solución)
+
+## Próximos Pasos (Opcional)
+
+1. Agregar validación de espacio en disco antes de crear
+2. Agregar limite de tamaño máximo para VMs
+3. Agregar compresión opcional de discos
+4. Implementar progreso en tiempo real para creación de VMs
+5. Agregar soporte para múltiples rutas de almacenamiento

+ 188 - 0
TROUBLESHOOTING.md

@@ -0,0 +1,188 @@
+# Guía de Resolución de Problemas - VirtManager
+
+## Error al Agregar Disco a una VM
+
+### Síntomas
+- Aparece mensaje "Error: La máquina virtual debe estar PARADA"
+- "El disco ya existe"
+- El disco no se agregó a la VM
+
+### Causas y Soluciones
+
+1. **La VM está corriendo**
+   
+   **Error:** "La máquina virtual debe estar PARADA (shut off) para agregar un disco"
+   
+   **Solución:**
+   - Detén la VM haciendo clic en "Stop" en la lista de VMs
+   - Espera a que el estado cambie a "shut off"
+   - Intenta agregar el disco nuevamente
+
+2. **El nombre del disco ya existe**
+   
+   **Error:** "El disco [nombre].qcow2 ya existe. Usa un nombre de disco diferente"
+   
+   **Solución:**
+   - Usa un nombre de disco diferente (ej: cambiar "datos" a "datos2")
+   - O verifica en `/var/lib/libvirt/images/` si el archivo existe:
+     ```bash
+     ls -la /var/lib/libvirt/images/ | grep qcow2
+     ```
+   - Si existe y quieres reutilizarlo, puedes eliminarlo:
+     ```bash
+     sudo rm /var/lib/libvirt/images/nombre.qcow2
+     ```
+
+3. **No hay VM seleccionada**
+   
+   **Error:** "Por favor selecciona una máquina virtual"
+   
+   **Solución:**
+   - Asegúrate de seleccionar una VM en el dropdown antes de hacer clic en "Add Disk"
+
+4. **Nombre de disco vacío o inválido**
+   
+   **Error:** "Por favor ingresa un nombre para el disco"
+   
+   **Solución:**
+   - Ingresa un nombre válido (ej: datos, backup, storage)
+   - Usa solo letras, números y guiones
+
+5. **Tamaño de disco inválido**
+   
+   **Error:** "Por favor ingresa un tamaño de disco válido (> 0 GB)"
+   
+   **Solución:**
+   - Ingresa un número mayor que 0
+   - Usa valores razonables (ej: 10, 50, 100, etc.)
+
+## Error "undefined" al crear una VM
+
+### Síntomas
+- Al intentar crear una VM, aparece un popup con el mensaje "undefined"
+- En la consola del navegador aparecen errores de red
+
+### Causas Comunes
+
+1. **Permisos insuficientes para crear discos**
+   - El usuario no tiene permisos para escribir en `/var/lib/libvirt/images/`
+   
+   **Solución:**
+   ```bash
+   # Verifica que el script create_disk.sh esté configurado en sudoers
+   sudo -l | grep create_disk
+   
+   # Si no está configurado, ejecuta:
+   echo "creylopez ALL=(ALL) NOPASSWD: /home/creylopez/virtmanager/create_disk.sh" | sudo tee /etc/sudoers.d/virtmanager
+   sudo visudo -c -f /etc/sudoers.d/virtmanager
+   ```
+
+2. **Script create_disk.sh no tiene permisos de ejecución**
+   
+   **Solución:**
+   ```bash
+   chmod +x /home/creylopez/virtmanager/create_disk.sh
+   ```
+
+3. **ISO no seleccionada**
+   - Debes seleccionar una ISO antes de crear la VM
+   
+   **Solución:**
+   - Primero sube una ISO usando la sección "Upload ISO"
+   - Luego selecciónala en la forma de "Create VM"
+
+4. **Servidor no está corriendo**
+   
+   **Solución:**
+   ```bash
+   # Inicia el servidor
+   cd /home/creylopez/virtmanager
+   source venv/bin/activate
+   sudo uvicorn app.main:app --host 0.0.0.0 --port 8000
+   ```
+
+5. **libvirt no está ejecutándose**
+   
+   **Solución:**
+   ```bash
+   sudo systemctl start libvirtd
+   sudo systemctl status libvirtd
+   ```
+
+## Error al crear disco: "Permission denied"
+
+### Solución
+El script helper necesita ejecutarse con permisos elevados. Verifica la configuración de sudoers:
+
+```bash
+# Ver si está bien configurado
+sudo -l
+
+# Debe mostrar algo como:
+# (ALL) NOPASSWD: /home/creylopez/virtmanager/create_disk.sh
+
+# Si no está, configúralo:
+echo "creylopez ALL=(ALL) NOPASSWD: /home/creylopez/virtmanager/create_disk.sh" | sudo tee /etc/sudoers.d/virtmanager
+```
+
+## Consola VNC no abre
+
+### Verificar que websockify está instalado
+```bash
+which websockify
+pip list | grep websockify
+```
+
+### Verificar que la VM está corriendo
+```bash
+virsh list
+```
+
+### Reiniciar websockify
+```bash
+# Mata los procesos existentes
+pkill websockify
+
+# Intenta abrir la consola nuevamente
+```
+
+## VM no inicia después de crear
+
+### Verificar estado
+```bash
+virsh list --all
+virsh dominfo nombre_vm
+```
+
+### Ver logs de libvirt
+```bash
+sudo journalctl -u libvirtd -n 50 -f
+```
+
+### Eliminar y recrear la VM
+```bash
+virsh undefine nombre_vm --storage
+# Luego crea la VM nuevamente desde la interfaz web
+```
+
+## Performance o problemas de conexión
+
+### Verificar disponibilidad de puertos
+```bash
+# Verifica que los puertos estén disponibles
+ss -tlnp | grep 8000  # Puerto FastAPI
+ss -tlnp | grep 59    # Rango de puertos VNC
+```
+
+### Reiniciar los servicios
+```bash
+sudo systemctl restart libvirtd
+# Luego reinicia VirtManager
+```
+
+## Contacto y Soporte
+
+Si encuentras problemas no documentados aquí:
+1. Revisa los logs del servidor: `journalctl -u virtmanager -n 50`
+2. Revisa la consola del navegador (F12 -> Console)
+3. Verifica que todos los prerrequisitos estén instalados

+ 93 - 20
app/main.py

@@ -144,22 +144,42 @@ async def stop_vm(vm_name: str):
 async def create_vm(name: str, memory: int, disk_size: int, cpus: int = 1, iso: str = ""):
     if not iso:
         return {"error": "ISO path is required"}
+    
     images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')
     disk_path = f"{images_path}/{name}.qcow2"
-    iso_path = iso  # iso ya es la ruta completa desde el select
+    iso_path = iso
     
-    print(f"Using ISO path: {iso_path}")
+    print(f"[CREATE_VM] Starting VM creation: name={name}, memory={memory}, disk_size={disk_size}, cpus={cpus}")
+    print(f"[CREATE_VM] Using ISO path: {iso_path}")
     
-    # Create disk
+    # Create disk using sudo script for proper permissions
     try:
-        result = subprocess.run(['qemu-img', 'create', '-f', 'qcow2', disk_path, f'{disk_size}G'], capture_output=True, text=True)
+        # Get absolute path to the script (up one level from app directory)
+        app_dir = os.path.dirname(os.path.abspath(__file__))
+        project_root = os.path.dirname(app_dir)
+        script_path = os.path.join(project_root, 'create_disk.sh')
+        print(f"[CREATE_VM] Script path: {script_path}")
+        result = subprocess.run(['sudo', script_path, disk_path, f'{disk_size}G'], capture_output=True, text=True, timeout=60)
+        print(f"[CREATE_VM] Disk creation return code: {result.returncode}")
         if result.returncode != 0:
-            return {"error": f"Failed to create disk: {result.stderr}"}
+            error_msg = result.stderr if result.stderr else result.stdout
+            print(f"[CREATE_VM] ERROR creating disk: {error_msg}")
+            return {"error": f"Failed to create disk: {error_msg}"}
+        print(f"[CREATE_VM] Disk created successfully: {disk_path}")
+    except subprocess.TimeoutExpired:
+        return {"error": "Disk creation timed out"}
     except Exception as e:
+        print(f"[CREATE_VM] Exception creating disk: {str(e)}")
         return {"error": f"Error creating disk: {str(e)}"}
     
+    # Verify disk exists
+    if not os.path.exists(disk_path):
+        print(f"[CREATE_VM] ERROR: Disk file not found after creation: {disk_path}")
+        return {"error": f"Disk file not created at {disk_path}"}
+    
     # Generate unique MAC
     mac = f"52:54:00:{random.randint(0, 255):02x}:{random.randint(0, 255):02x}:{random.randint(0, 255):02x}"
+    print(f"[CREATE_VM] Generated MAC: {mac}")
     
     # Basic XML template
     xml_template = f"""<domain type='kvm'>
@@ -224,12 +244,33 @@ async def create_vm(name: str, memory: int, disk_size: int, cpus: int = 1, iso:
   </devices>
 </domain>"""
     
+    print(f"[CREATE_VM] XML template prepared for VM definition")
+    
     try:
+        print(f"[CREATE_VM] Defining VM with libvirt...")
         domain = conn.defineXML(xml_template)
+        print(f"[CREATE_VM] VM defined successfully. Starting VM...")
         domain.create()
+        print(f"[CREATE_VM] VM started successfully!")
         return {"message": f"VM {name} created with {cpus} CPUs, {memory} MiB memory, {disk_size}G disk, booting from {iso}"}
+    except libvirt.libvirtError as e:
+        print(f"[CREATE_VM] Libvirt error: {str(e)}")
+        # Try to clean up the disk if VM definition failed
+        try:
+            os.remove(disk_path)
+            print(f"[CREATE_VM] Cleaned up disk file after error")
+        except:
+            pass
+        return {"error": f"Libvirt error: {str(e)}"}
     except Exception as e:
-        return {"error": str(e)}
+        print(f"[CREATE_VM] Unexpected error: {str(e)}")
+        # Try to clean up the disk if VM definition failed
+        try:
+            os.remove(disk_path)
+            print(f"[CREATE_VM] Cleaned up disk file after error")
+        except:
+            pass
+        return {"error": f"Error: {str(e)}"}
 
 @app.get("/console/{vm_name}")
 async def get_console(vm_name: str):
@@ -331,35 +372,57 @@ 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"
     
+    print(f"[ADD_DISK] Starting disk addition: vm={vm_name}, disk_name={disk_name}, disk_size={disk_size}")
+    
     try:
+        # Get the VM first to check its state
+        print(f"[ADD_DISK] Looking up VM: {vm_name}")
+        domain = conn.lookupByName(vm_name)
+        
+        # Check if VM is running BEFORE creating disk
+        print(f"[ADD_DISK] Checking VM state...")
+        if domain.isActive():
+            print(f"[ADD_DISK] ERROR: VM is running, must be shut off to add disk")
+            return {"error": "Error: La máquina virtual debe estar PARADA (shut off) para agregar un disco"}
+        print(f"[ADD_DISK] VM state OK (shut off)")
+        
         # Check if disk already exists
+        print(f"[ADD_DISK] Checking if disk exists: {disk_path}")
         if os.path.exists(disk_path):
-            return {"error": f"Disk {disk_name}.qcow2 already exists"}
+            print(f"[ADD_DISK] ERROR: Disk already exists")
+            return {"error": f"Error: El disco {disk_name}.qcow2 ya existe. Usa un nombre de disco diferente"}
+        print(f"[ADD_DISK] Disk does not exist (OK)")
         
-        # Create the disk
-        result = subprocess.run(['qemu-img', 'create', '-f', 'qcow2', disk_path, f'{disk_size}G'], 
-                              capture_output=True, text=True)
+        # Create the disk using sudo script for proper permissions
+        print(f"[ADD_DISK] Creating disk...")
+        app_dir = os.path.dirname(os.path.abspath(__file__))
+        project_root = os.path.dirname(app_dir)
+        script_path = os.path.join(project_root, 'create_disk.sh')
+        result = subprocess.run(['sudo', script_path, disk_path, f'{disk_size}G'], 
+                              capture_output=True, text=True, timeout=60)
         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"}
+            error_msg = result.stderr if result.stderr else result.stdout
+            print(f"[ADD_DISK] ERROR creating disk: {error_msg}")
+            return {"error": f"Error al crear disco: {error_msg}"}
+        print(f"[ADD_DISK] Disk created successfully")
         
         # Get the XML and add the disk
+        print(f"[ADD_DISK] Getting VM XML...")
         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"}
+            print(f"[ADD_DISK] ERROR: No devices section in XML")
+            return {"error": "Error: No se encontró sección devices en la configuración de la VM"}
         
         # Find the next available disk target (vda, vdb, vdc, etc.)
+        print(f"[ADD_DISK] Finding next available disk target...")
         existing_targets = set()
         for disk in root.findall(".//disk/target"):
             existing_targets.add(disk.get('dev'))
+        print(f"[ADD_DISK] Existing targets: {existing_targets}")
         
         # Generate next available target
         target_letters = "abcdefghijklmnopqrstuvwxyz"
@@ -371,9 +434,12 @@ async def add_disk_to_vm(vm_name: str, disk_name: str, disk_size: int):
                 break
         
         if next_target is None:
-            return {"error": "No available disk targets"}
+            print(f"[ADD_DISK] ERROR: No available disk targets")
+            return {"error": "Error: No hay dispositivos de disco disponibles"}
+        print(f"[ADD_DISK] Next target: {next_target}")
         
         # Create the new disk element
+        print(f"[ADD_DISK] Creating disk XML 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})
@@ -383,13 +449,20 @@ async def add_disk_to_vm(vm_name: str, disk_name: str, disk_size: int):
         devices.append(new_disk)
         
         # Convert back to string and redefine the VM
+        print(f"[ADD_DISK] Redefining VM with new disk...")
         new_xml = ET.tostring(root, encoding='unicode')
         domain.undefine()
         conn.defineXML(new_xml)
+        print(f"[ADD_DISK] VM redefined successfully")
         
-        return {"message": f"Disk {disk_name}.qcow2 ({disk_size}G) added to {vm_name} as {next_target}"}
+        print(f"[ADD_DISK] SUCCESS: Disk added")
+        return {"message": f"Disco {disk_name}.qcow2 ({disk_size}G) agregado a {vm_name} como {next_target}"}
+    except libvirt.libvirtError as e:
+        print(f"[ADD_DISK] Libvirt error: {str(e)}")
+        return {"error": f"Error de libvirt: {str(e)}"}
     except Exception as e:
-        return {"error": str(e)}
+        print(f"[ADD_DISK] Unexpected error: {str(e)}")
+        return {"error": f"Error: {str(e)}"}
 
 @app.post("/disks")
 async def create_disk(name: str, size: int):

+ 23 - 0
create_disk.sh

@@ -0,0 +1,23 @@
+#!/bin/bash
+# Script helper para crear discos virtuales con permisos elevados
+# Este script debe ser ejecutable con sudo sin contraseña
+
+DISK_PATH="$1"
+DISK_SIZE="$2"
+
+if [ -z "$DISK_PATH" ] || [ -z "$DISK_SIZE" ]; then
+    echo "Error: disk path and size required"
+    exit 1
+fi
+
+# Crear el disco
+qemu-img create -f qcow2 "$DISK_PATH" "$DISK_SIZE"
+exit_code=$?
+
+if [ $exit_code -eq 0 ]; then
+    # Asegurar permisos correctos
+    chmod 660 "$DISK_PATH"
+    chown libvirt-qemu:kvm "$DISK_PATH"
+fi
+
+exit $exit_code

+ 35 - 8
static/app.js

@@ -255,6 +255,11 @@ document.getElementById('createVMForm').addEventListener('submit', async (e) =>
     const cpus = document.getElementById('cpus').value;
     const iso = document.getElementById('iso').value;
     
+    if (!iso) {
+        alert('Por favor seleccione una imagen ISO');
+        return;
+    }
+    
     try {
         const response = await fetch(`${API_BASE}/vms?name=${name}&memory=${memory}&disk_size=${disk_size}&cpus=${cpus}&iso=${iso}`, {
             method: 'POST'
@@ -263,18 +268,19 @@ document.getElementById('createVMForm').addEventListener('submit', async (e) =>
         try {
             result = await response.json();
         } catch {
-            result = { error: 'Unknown error' };
+            result = { error: `Server error: ${response.status} ${response.statusText}` };
         }
         if (response.ok) {
-            alert(result.message);
+            alert(result.message || 'VM creada correctamente');
             loadVMs();
             document.getElementById('createVMForm').reset();
         } else {
-            alert(result.error || 'Error creating VM');
+            console.error('Error response:', result);
+            alert(`Error: ${result.error || 'Error desconocido al crear VM'}`);
         }
     } catch (error) {
         console.error('Error creating VM:', error);
-        alert('Error creating VM');
+        alert(`Error de conexión: ${error.message}`);
     }
 });
 
@@ -315,21 +321,42 @@ if (document.getElementById('addDiskForm')) {
         const disk_name = document.getElementById('diskName').value;
         const disk_size = document.getElementById('diskSize').value;
         
+        if (!vm_name) {
+            alert('Por favor selecciona una máquina virtual');
+            return;
+        }
+        
+        if (!disk_name) {
+            alert('Por favor ingresa un nombre para el disco');
+            return;
+        }
+        
+        if (!disk_size || disk_size <= 0) {
+            alert('Por favor ingresa un tamaño de disco válido (> 0 GB)');
+            return;
+        }
+        
         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();
+            let result;
+            try {
+                result = await response.json();
+            } catch {
+                result = { error: `Server error: ${response.status} ${response.statusText}` };
+            }
             if (response.ok) {
-                alert(result.message);
+                alert(result.message || 'Disco agregado correctamente');
                 loadVMs();
                 document.getElementById('addDiskForm').reset();
             } else {
-                alert(result.error || 'Error adding disk');
+                console.error('Add disk error:', result);
+                alert(result.error || 'Error al agregar disco');
             }
         } catch (error) {
             console.error('Error adding disk:', error);
-            alert('Error adding disk');
+            alert(`Error de conexión: ${error.message}`);
         }
     });
 }

+ 8 - 0
static/index.html

@@ -72,6 +72,14 @@
                 </form>
                 
                 <h2 class="mt-4">Add Disk to VM</h2>
+                <div class="alert alert-warning" role="alert">
+                    <strong>⚠️ Importante:</strong>
+                    <ul>
+                        <li>La máquina virtual <strong>debe estar PARADA</strong> (shut off)</li>
+                        <li>El nombre del disco <strong>no puede existir</strong> previamente</li>
+                        <li>El disco se agregará automáticamente a la VM</li>
+                    </ul>
+                </div>
                 <form id="addDiskForm">
                     <div class="mb-3">
                         <label for="diskVM" class="form-label">VM Name</label>