Reviewed-on: #23deb-jenkins-pkg 0.6.0
commit
48dfc3d190
58
README.md
58
README.md
|
@ -20,6 +20,9 @@ Paquetes APT requeridos:
|
|||
- **bittorrent** (se puede instalar con "sudo apt install bittorrent", pero previamente hay que añadir un repositorio de Debian)
|
||||
- **bittornado** (se puede instalar con "sudo apt install bittornado", pero previamente hay que añadir un repositorio de Debian)
|
||||
- **wakeonlan** (se puede instalar con "sudo apt install wakeonlan")
|
||||
- **qemu** (se puede instalar con "sudo apt install qemu-utils")
|
||||
- **partclone** (se puede instalar con "sudo apt install partclone")
|
||||
- **lzop** (se puede instalar con "sudo apt install lzop")
|
||||
|
||||
Librerías Python requeridas:
|
||||
- **flask** (se puede instalar con "sudo apt install python3-flask")
|
||||
|
@ -31,6 +34,7 @@ Librerías Python requeridas:
|
|||
Para que todos los endpoints y scripts funcionen con la configuración actual deben existir los siguientes directorios:
|
||||
- **/opt/opengnsys/ogrepository/images/**
|
||||
- **/opt/opengnsys/ogrepository/images_trash/** (debe estar en la misma partición que el anterior, o tardarán mucho las eliminaciones y restauraciones)
|
||||
- **/opt/opengnsys/ogrepository/images_virtual/** (aquí deben copiarse las imágenes virtuales que se quiera convertir a "img")
|
||||
- **/opt/opengnsys/ogrepository/bin/** (aquí deben estar todos los scripts de Python, y el binario "udp-sender")
|
||||
- **/opt/opengnsys/ogrepository/api/** (aquí debe estar la API y el Swagger)
|
||||
- **/opt/opengnsys/ogrepository/etc/** (aquí se guardan los archivos "repoinfo.json" y "trashinfo.json")
|
||||
|
@ -61,20 +65,21 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
|
|||
9. [Transferir una Imagen entre Repositorios](#transferir-una-imagen-entre-repositorios) - `POST /ogrepository/v1/repo/images`
|
||||
10. [Hacer Backup de una Imagen](#hacer-backup-de-una-imagen) - `PUT /ogrepository/v1/repo/images`
|
||||
11. [Crear archivos auxiliares](#crear-archivos-auxiliares) - `POST /ogrepository/v1/images/torrentsum`
|
||||
12. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
|
||||
13. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
||||
14. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
||||
15. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
||||
16. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
||||
17. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
||||
18. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
||||
19. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
|
||||
20. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
|
||||
12. [Convertir Imagen Virtual](#convertir-imagen-virtual) - `POST /ogrepository/v1/images/virtual`
|
||||
13. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
|
||||
14. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
||||
15. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
||||
16. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
||||
17. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
||||
18. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
||||
19. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
||||
20. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
|
||||
21. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
|
||||
|
||||
---
|
||||
### Obtener Información de Estado de ogRepository
|
||||
|
||||
Se devolverá informacion de CPU, memoria RAM, disco duro y el estado de ciertos servicios y procesos de ogRepository, en formato JSON.
|
||||
Se devolverá informacion de CPU, memoria RAM, disco duro y el estado de ciertos servicios y procesos de ogRepository, en formato JSON.
|
||||
Se puede utilizar el script "**getRepoStatus.py**, que debe ser llamado por el endpoint.
|
||||
**NOTA**: En los apartados "services" y "processes" he especificado los servicios y procesos que me han parecido interesantes, pero se puede añadir o eliminar los que se desee.
|
||||
|
||||
|
@ -382,9 +387,9 @@ Se puede hacer con el script "**importImage.py**", que debe ser llamado por el e
|
|||
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "repo_ip":"192.168.56.100", "user":"opengnsys"}' http://example.com/ogrepository/v1/repo/images
|
||||
```
|
||||
**Respuestas:**
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al importar la imagen.
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al transferir la imagen.
|
||||
- **Código 400 Bad Request:** No se ha encontrado la imagen y/o el equipo remoto especificados.
|
||||
- **Código 200 OK:** La imagen se está importando.
|
||||
- **Código 200 OK:** La imagen se está transfiriendo.
|
||||
|
||||
---
|
||||
### Hacer Backup de una Imagen
|
||||
|
@ -410,9 +415,9 @@ Se puede hacer con el script "**backupImage.py**", que debe ser llamado por el e
|
|||
curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "repo_ip":"192.168.56.100", "user":"opengnsys", "remote_path":"/home/opengnsys"}' http://example.com/ogrepository/v1/repo/images
|
||||
```
|
||||
**Respuestas:**
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al exportar la imagen.
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al hacer backup de la imagen.
|
||||
- **Código 400 Bad Request:** No se ha encontrado la imagen y/o el equipo remoto especificados.
|
||||
- **Código 200 OK:** La imagen se ha exportando exitosamente.
|
||||
- **Código 200 OK:** Se está haciendo backup de la imagen.
|
||||
|
||||
---
|
||||
### Crear archivos auxiliares
|
||||
|
@ -438,6 +443,31 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
|
|||
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
|
||||
- **Código 200 OK:** Los archivos auxiliares se están creando.
|
||||
|
||||
---
|
||||
### Convertir Imagen Virtual
|
||||
|
||||
Se convertirá la imagen virtual especificada (que debe haberse copiado previamente en la ruta "opt/opengnsys/ogrepository/images_virtual") en una imagen "img" como las que se generan desde OpenGnsys (con "partclone" y "lzop").
|
||||
Se puede hacer con el script "**convertVMtoIMG.py**", que debe ser llamado por el endpoint.
|
||||
**NOTA**: El script requiere que se le pase el nombre de la imagen virtual (con extensión) como primer parámetro, y el sistema de archivos de la partición a clonar como segundo parámetro (en formato "blkid"). Estos parámetros deben enviarse desde ogCore (en el JSON).
|
||||
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen virtual se está convirtiendo, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
|
||||
|
||||
**URL:** `/ogrepository/v1/images/virtual`
|
||||
**Método HTTP:** POST
|
||||
|
||||
**Cuerpo de la Solicitud (JSON):**
|
||||
- **virtual_image**: Nombre de la imagen virtual (con extensión).
|
||||
- **filesystem**: Sistema de archivos de la partición a clonar, en formato "blkid".
|
||||
|
||||
**Ejemplo de Solicitud:**
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"virtual_image":"UbuntuVM.vdi", "filesystem":"ext4"}' http://example.com/ogrepository/v1/images/virtual
|
||||
```
|
||||
**Respuestas:**
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al convertir la imagen virtual.
|
||||
- **Código 400 Bad Request:** No se ha encontrado la imagen virtual especificada.
|
||||
- **Código 200 OK:** La imagen virtual se está convirtiendo.
|
||||
|
||||
---
|
||||
### Enviar paquete Wake On Lan
|
||||
|
||||
|
|
|
@ -19,20 +19,21 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
|
|||
9. [Transferir una Imagen entre Repositorios](#transferir-una-imagen-entre-repositorios) - `POST /ogrepository/v1/repo/images`
|
||||
10. [Hacer Backup de una Imagen](#hacer-backup-de-una-imagen) - `PUT /ogrepository/v1/repo/images`
|
||||
11. [Crear archivos auxiliares](#crear-archivos-auxiliares) - `POST /ogrepository/v1/images/torrentsum`
|
||||
12. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
|
||||
13. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
||||
14. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
||||
15. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
||||
16. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
||||
17. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
||||
18. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
||||
19. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
|
||||
20. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
|
||||
12. [Convertir Imagen Virtual](#convertir-imagen-virtual) - `POST /ogrepository/v1/images/virtual`
|
||||
13. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
|
||||
14. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
||||
15. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
||||
16. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
||||
17. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
||||
18. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
||||
19. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
||||
20. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
|
||||
21. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
|
||||
|
||||
---
|
||||
### Obtener Información de Estado de ogRepository
|
||||
|
||||
Se devolverá informacion de CPU, memoria RAM, disco duro y el estado de ciertos servicios y procesos de ogRepository, en formato JSON.
|
||||
Se devolverá informacion de CPU, memoria RAM, disco duro y el estado de ciertos servicios y procesos de ogRepository, en formato JSON.
|
||||
Se puede utilizar el script "**getRepoStatus.py**, que debe ser llamado por el endpoint.
|
||||
**NOTA**: En los apartados "services" y "processes" he especificado los servicios y procesos que me han parecido interesantes, pero se puede añadir o eliminar los que se desee.
|
||||
|
||||
|
@ -340,9 +341,9 @@ Se puede hacer con el script "**importImage.py**", que debe ser llamado por el e
|
|||
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "repo_ip":"192.168.56.100", "user":"opengnsys"}' http://example.com/ogrepository/v1/repo/images
|
||||
```
|
||||
**Respuestas:**
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al importar la imagen.
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al transferir la imagen.
|
||||
- **Código 400 Bad Request:** No se ha encontrado la imagen y/o el equipo remoto especificados.
|
||||
- **Código 200 OK:** La imagen se está importando.
|
||||
- **Código 200 OK:** La imagen se está transfiriendo.
|
||||
|
||||
---
|
||||
### Hacer Backup de una Imagen
|
||||
|
@ -368,9 +369,9 @@ Se puede hacer con el script "**backupImage.py**", que debe ser llamado por el e
|
|||
curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "repo_ip":"192.168.56.100", "user":"opengnsys", "remote_path":"/home/opengnsys"}' http://example.com/ogrepository/v1/repo/images
|
||||
```
|
||||
**Respuestas:**
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al exportar la imagen.
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al hacer backup de la imagen.
|
||||
- **Código 400 Bad Request:** No se ha encontrado la imagen y/o el equipo remoto especificados.
|
||||
- **Código 200 OK:** La imagen se ha exportando exitosamente.
|
||||
- **Código 200 OK:** Se está haciendo backup de la imagen.
|
||||
|
||||
---
|
||||
### Crear archivos auxiliares
|
||||
|
@ -396,6 +397,31 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
|
|||
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
|
||||
- **Código 200 OK:** Los archivos auxiliares se están creando.
|
||||
|
||||
---
|
||||
### Convertir Imagen Virtual
|
||||
|
||||
Se convertirá la imagen virtual especificada (que debe haberse copiado previamente en la ruta "opt/opengnsys/ogrepository/images_virtual") en una imagen "img" como las que se generan desde OpenGnsys (con "partclone" y "lzop").
|
||||
Se puede hacer con el script "**convertVMtoIMG.py**", que debe ser llamado por el endpoint.
|
||||
**NOTA**: El script requiere que se le pase el nombre de la imagen virtual (con extensión) como primer parámetro, y el sistema de archivos de la partición a clonar como segundo parámetro (en formato "blkid"). Estos parámetros deben enviarse desde ogCore (en el JSON).
|
||||
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen virtual se está convirtiendo, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
|
||||
|
||||
**URL:** `/ogrepository/v1/images/virtual`
|
||||
**Método HTTP:** POST
|
||||
|
||||
**Cuerpo de la Solicitud (JSON):**
|
||||
- **virtual_image**: Nombre de la imagen virtual (con extensión).
|
||||
- **filesystem**: Sistema de archivos de la partición a clonar, en formato "blkid".
|
||||
|
||||
**Ejemplo de Solicitud:**
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"virtual_image":"UbuntuVM.vdi", "filesystem":"ext4"}' http://example.com/ogrepository/v1/images/virtual
|
||||
```
|
||||
**Respuestas:**
|
||||
- **Código 500 Internal Server Error:** Ocurrió un error al convertir la imagen virtual.
|
||||
- **Código 400 Bad Request:** No se ha encontrado la imagen virtual especificada.
|
||||
- **Código 200 OK:** La imagen virtual se está convirtiendo.
|
||||
|
||||
---
|
||||
### Enviar paquete Wake On Lan
|
||||
|
||||
|
|
233
api/repo_api.py
233
api/repo_api.py
|
@ -29,6 +29,7 @@ import threading
|
|||
import requests
|
||||
import random
|
||||
import hashlib
|
||||
import psutil
|
||||
from systemd import journal
|
||||
# Imports para Swagger:
|
||||
from flasgger import Swagger
|
||||
|
@ -40,6 +41,7 @@ import yaml
|
|||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
repo_path = '/opt/opengnsys/ogrepository/images/' # No borrar la barra final
|
||||
vm_path = '/opt/opengnsys/ogrepository/images_virtual/' # No borrar la barra final
|
||||
script_path = '/opt/opengnsys/ogrepository/bin'
|
||||
repo_file = '/opt/opengnsys/ogrepository/etc/repoinfo.json'
|
||||
trash_file = '/opt/opengnsys/ogrepository/etc/trashinfo.json'
|
||||
|
@ -453,6 +455,62 @@ def check_aux_files(image_file_path, job_id):
|
|||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
def check_virtual_image_conversion(image_name, job_id):
|
||||
""" Cada minuto comprueba si existe la imagen convertida (y sus archivos asociados), o si no existe ninguno de los archivos que se crean en el proceso.
|
||||
Cuando ya no exista el archivo ".lock" (pero si los demás archivos), le comunicará a ogCore que la conversión ha sido exitosa, y saldrá de bucle.
|
||||
Cuando no exista ninguno de los archivos que se crean en el proceso (incluyendo la imagen convertida), le comunicará a ogCore que la conversión ha fallado, y saldrá de bucle.
|
||||
Mientras no se cumpla ninguna de las condiciones anteriores, seguirá haciendo la comprobación (repitiendo el bucle cada minuto).
|
||||
"""
|
||||
journal.send("Running function 'check_virtual_image_conversion'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
# Esperamos 30 segundos, para dar tiempo a que se cree algún archivo:
|
||||
sleep(30)
|
||||
|
||||
# Construimos la ruta de la imagen (una vez convertida):
|
||||
image_file_path = f"{repo_path}{image_name}.img"
|
||||
|
||||
# Creamos un bucle infinito:
|
||||
while True:
|
||||
# Si ya no existe el archivo ".lock" (pero sí existen los demás), respondemos a ogCore (con "success: True") y salimos del bucle:
|
||||
if not os.path.exists(f"{image_file_path}.lock") and os.path.exists(image_file_path) and os.path.exists(f"{image_file_path}.full.sum") and os.path.exists(f"{image_file_path}.info.checked") and os.path.exists(f"{image_file_path}.size") and os.path.exists(f"{image_file_path}.sum") and os.path.exists(f"{image_file_path}.torrent"):
|
||||
journal.send("Task finalized (Virtual image converted)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'INFO', 'operation':'Run function check_virtual_image_conversion', 'desc':'Virtual image converted'}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
|
||||
# Almacenamos en un diccionario los datos a enviar a ogCore:
|
||||
data = {
|
||||
'job_id': job_id,
|
||||
'success': True
|
||||
}
|
||||
# Llamamos al endpoint de ogCore, enviando los datos del diccionario:
|
||||
journal.send(f"Calling function 'recall_ogcore' (JOB_ID: {job_id}, SUCCESS: True)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
recall_ogcore(data)
|
||||
break
|
||||
|
||||
# Si no existe ninguno de los archivos que se crean en el proceso (incluyendo la imagen convertida), respondemos a ogCore (con "success: False") y salimos del bucle:
|
||||
elif not os.path.exists(f"{vm_path}{image_name}.raw") and not os.path.exists(f"{vm_path}{image_name}.img") and not os.path.exists(f"{vm_path}{image_name}.img.lzo") and not os.path.exists(f"{repo_path}{image_name}.img"):
|
||||
journal.send("Virtual image conversion failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'ERROR', 'operation':'Run function check_virtual_image_conversion', 'desc':'Virtual image conversion failed'}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
|
||||
# Almacenamos en un diccionario los datos a enviar a ogCore:
|
||||
data = {
|
||||
'job_id': job_id,
|
||||
'success': False
|
||||
}
|
||||
# Llamamos al endpoint de ogCore, enviando los datos del diccionario:
|
||||
journal.send(f"Calling function 'recall_ogcore' (JOB_ID: {job_id}, SUCCESS: False)", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
recall_ogcore(data)
|
||||
break
|
||||
# Si no se han cumplido las condiciones anteriores), imprimimos un mensaje en la API:
|
||||
else:
|
||||
journal.send("Task in process (Conversion not finalized)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
# Esperamos 1 minuto para volver a realizar la comprobación:
|
||||
sleep(60)
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
def recall_ogcore(data):
|
||||
""" Hace una petición HTTP de tipo POST a un endpoint de ogCore, enviando datos que dependen del caso.
|
||||
Se utiliza para informar a ogCore del resultado de una tarea asíncrona,
|
||||
|
@ -516,6 +574,27 @@ def remove_remote_files(sftp_client, remote_path, image_name, extensions):
|
|||
journal.send(f"File with extension {ext} doesn't exist", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
def check_free_space(vm_image_name_full):
|
||||
""" Comprueba si hay suficiente espacio en disco para convertir la imagen virtual que recibe como parámetro
|
||||
(3 veces el tamaño del archivo), devolviendo "True" si lo hay, y "False" si no lo hay.
|
||||
Lo utiliza el endpoint "Convertir imagen virtual"
|
||||
"""
|
||||
# Obtenemos el tamaño de la imagen virtual:
|
||||
vm_size = int(os.path.getsize(f"{vm_path}{vm_image_name_full}"))
|
||||
# Obtenemos la cantidad de espacio libre en disco:
|
||||
disk = psutil.disk_usage('/')
|
||||
free_space = int(disk.free)
|
||||
|
||||
# Si no hay suficiente espacio libre en disco (3 veces el tamaño de la imagen virtual), devolvemos "False", y en caso contrario "True":
|
||||
if free_space < (vm_size * 3):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# ENDPOINTS
|
||||
|
@ -1136,20 +1215,12 @@ def backup_image():
|
|||
"process exception": str(error)
|
||||
}), 500
|
||||
except Exception as error_description:
|
||||
if "exit status 5" in str(error_description):
|
||||
journal.send("Image already exists on remote host", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'WARNING', 'http_code':'400', 'operation':'Run script backupImage.py', 'desc':'Warning: Image already exists on remote host'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": "Image already exists on remote host"
|
||||
}), 400
|
||||
else:
|
||||
journal.send(f"Script 'backupImage.py' result KO (Exception: {str(error_description)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send(f"{{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script backupImage.py', 'desc':'Result KO (Exception: {str(error_description)})'}}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": str(error_description)
|
||||
}), 500
|
||||
journal.send(f"Script 'backupImage.py' result KO (Exception: {str(error_description)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send(f"{{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script backupImage.py', 'desc':'Result KO (Exception: {str(error_description)})'}}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": str(error_description)
|
||||
}), 500
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
@ -1217,27 +1288,12 @@ def create_torrent_sum():
|
|||
"error": result.stderr
|
||||
}), 500
|
||||
except Exception as error_description:
|
||||
if "exit status 2" in str(error_description):
|
||||
journal.send("Image not found", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'WARNING', 'http_code':'400', 'operation':'Run script createTorrentSum.py', 'desc':'Warning: Image not found'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": "Image not found"
|
||||
}), 400
|
||||
elif "exit status 3" in str(error_description):
|
||||
journal.send("Image is locked", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'WARNING', 'http_code':'400', 'operation':'Run script createTorrentSum.py', 'desc':'Warning: Image is locked'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": "Image is locked"
|
||||
}), 400
|
||||
else:
|
||||
journal.send(f"Script 'createTorrentSum.py' result KO (Exception: {str(error_description)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send(f"{{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script createTorrentSum.py', 'desc':'Result KO (Exception: {str(error_description)})'}}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": str(error_description)
|
||||
}), 500
|
||||
journal.send(f"Script 'createTorrentSum.py' result KO (Exception: {str(error_description)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send(f"{{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script createTorrentSum.py', 'desc':'Result KO (Exception: {str(error_description)})'}}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": str(error_description)
|
||||
}), 500
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
@ -1786,6 +1842,113 @@ def stop_p2p():
|
|||
}), 500
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
# 21 - Endpoint "Convertir imagen virtual" (ASINCRONO):
|
||||
@app.route("/ogrepository/v1/images/virtual", methods=['POST'])
|
||||
def convert_virtual_image():
|
||||
""" Este endpoint convierte la imagen virtual especificada como primer parámetro en una imagen "img" como las que se generan desde OpenGnsys
|
||||
(con "partclone" y "lzop"), por lo que luego puede ser restaurada como cualquier otra imagen del repositorio.
|
||||
Para ello, ejecuta el script "convertVMtoIMG.py", con el nombre de la imagen virtual como primer parámetro,
|
||||
y el sistema de archivos de la partición a clonar (en formato "blkid") como segundo parámetro.
|
||||
"""
|
||||
journal.send("Running endpoint 'Convertir imagen virtual'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
# Almacenamos los parámetros enviados en el JSON, y extraemos el nombre y la extensión:
|
||||
json_data = json.loads(request.data)
|
||||
vm_image_name_full = json_data.get("virtual_image")
|
||||
vm_image_name = vm_image_name_full.split('.')[0]
|
||||
vm_extension = vm_image_name_full.split('.')[1]
|
||||
filesystem = json_data.get("filesystem").lower()
|
||||
|
||||
# Comprobamos si existe la imagen virtual, llamando a la función "check_file_exists":
|
||||
vm_image_exists = check_file_exists(f"{vm_path}{vm_image_name_full}")
|
||||
|
||||
# Si la imagen virtual no existe, devolvemos un error:
|
||||
if vm_image_exists == False:
|
||||
journal.send("Virtual image not found", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'WARNING', 'http_code':'400', 'operation':'Run endpoint convert_virtual_image', 'desc':'Warning: Virtual image not found'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": "Virtual image not found"
|
||||
}), 400
|
||||
|
||||
# Comprobamos si ya existe una imagen "img" con el mismo nombre que la imagen virtual, llamando a la función "check_file_exists":
|
||||
img_image_exists = check_file_exists(f"{repo_path}{vm_image_name}.img")
|
||||
|
||||
# Si existe una imagen con el mismo nombre que la imagen virtual (salvo por la extensión), devolvemos un error:
|
||||
if img_image_exists == True:
|
||||
journal.send("There is an image with the same name as the virtual image", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'WARNING', 'http_code':'400', 'operation':'Run endpoint convert_virtual_image', 'desc':'Warning: There is an image with the same name as the virtual image'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": "There is an image with the same name as the virtual image"
|
||||
}), 400
|
||||
|
||||
# Comprobamos si hay espacio suficiente en disco para convertir la imagen virtual (3 veces su tamaño):
|
||||
enough_free_space = check_free_space(vm_image_name_full)
|
||||
|
||||
# Si no hay suficiente espacio libre en disco, devolvemos un error:
|
||||
if enough_free_space == False:
|
||||
journal.send("There is not enough free disk space", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'WARNING', 'http_code':'400', 'operation':'Run endpoint convert_virtual_image', 'desc':'Warning: There is not enough free disk space'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": "There is not enough free disk space"
|
||||
}), 400
|
||||
|
||||
# Construimos la llamada al script:
|
||||
cmd = ['python3', f"{script_path}/convertVMtoIMG.py", vm_image_name_full, filesystem]
|
||||
|
||||
try:
|
||||
journal.send("Running script 'convertVMtoIMG.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
# Ejecutamos el script "convertVMtoIMG.py" (con los parámetros almacenados), y almacenamos el resultado (pero sin esperar a que termine el proceso):
|
||||
result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
|
||||
|
||||
# Generamos el ID para identificar el trabajo asíncrono:
|
||||
job_id = f"ConvertImage_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
|
||||
journal.send(f"JOB ID generated ({job_id})", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
|
||||
if result.returncode is None:
|
||||
journal.send("Script 'convertVMtoIMG.py' result OK (ReturnCode: None)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'INFO', 'http_code':'200', 'operation':'Run script convertVMtoIMG.py', 'desc':'Result OK (ReturnCode: None)'}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
|
||||
# Si el resultado es correcto, llamamos a la función "check_virtual_image_conversion" en un hilo paralelo
|
||||
# (para que compruebe si la imagen se ha acabado de convertir exitosamente):
|
||||
journal.send("Calling function 'check_virtual_image_conversion'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
threading.Thread(target=check_virtual_image_conversion, args=(vm_image_name, job_id,)).start()
|
||||
|
||||
# Informamos que la imagen se está convirtiendo, y salimos del endpoint:
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"output": "Converting virtual image...",
|
||||
"job_id": job_id
|
||||
}), 200
|
||||
else:
|
||||
journal.send("Script 'convertVMtoIMG.py' result KO (Virtual image conversion failed)", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send("{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script convertVMtoIMG.py', 'desc':'Result KO (Error: Virtual image conversion failed)'}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Virtual image conversion failed"
|
||||
}), 500
|
||||
except subprocess.CalledProcessError as error:
|
||||
journal.send(f"Script 'convertVMtoIMG.py' result KO (Process Exception: {str(error)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send(f"{{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script convertVMtoIMG.py', 'desc':'Result KO (Process Exception: {str(error)})'}}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"process exception": str(error)
|
||||
}), 500
|
||||
except Exception as error_description:
|
||||
journal.send(f"Script 'convertVMtoIMG.py' result KO (Exception: {str(error_description)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
journal.send(f"{{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script convertVMtoIMG.py', 'desc':'Result KO (Exception: {str(error_description)})'}}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"exception": str(error_description)
|
||||
}), 500
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
134
api/swagger.yaml
134
api/swagger.yaml
|
@ -1332,7 +1332,7 @@ paths:
|
|||
example: "user_name"
|
||||
responses:
|
||||
"200":
|
||||
description: "La imagen se está importando."
|
||||
description: "La imagen se está transfiriendo."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1450,7 +1450,7 @@ paths:
|
|||
example: "/home/opengnsys"
|
||||
responses:
|
||||
"200":
|
||||
description: "La imagen se está copiando."
|
||||
description: "Se está haciendo backup de la imagen."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1493,17 +1493,6 @@ paths:
|
|||
exception:
|
||||
type: string
|
||||
example: "Can't connect to remote host"
|
||||
"400 (Image present)":
|
||||
description: "La imagen ya existe en el equipo remoto."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
exception:
|
||||
type: string
|
||||
example: "Image already exists on remote host"
|
||||
"500 (Error)":
|
||||
description: "Error al copiar la imagen."
|
||||
schema:
|
||||
|
@ -1578,17 +1567,6 @@ paths:
|
|||
exception:
|
||||
type: string
|
||||
example: "Image not found"
|
||||
"400 (Image locked)":
|
||||
description: "La imagen está bloqueada."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
exception:
|
||||
type: string
|
||||
example: "Image is locked"
|
||||
"500 (Error)":
|
||||
description: "Error al crear los archivos auxiliares."
|
||||
schema:
|
||||
|
@ -1673,4 +1651,112 @@ paths:
|
|||
type: string
|
||||
example: "(Exception description)"
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
/ogrepository/v1/images/virtual:
|
||||
post:
|
||||
summary: "Convertir imagen virtual"
|
||||
description: |
|
||||
Este endpoint convierte la imagen virtual especificada como primer parámetro en una imagen "img" como las que se generan desde OpenGnsys, debiendo haberse copiado previamente en la ruta "opt/opengnsys/ogrepository/images_virtual".
|
||||
Utiliza el script "**convertVMtoIMG.py**", que recibe como parámetros el nombre de la imagen virtual, y el sistema de archivos de la partición a clonar (en formato "blkid").
|
||||
Se puede comprobar todos los sistemas de archivos aceptados por "blkid" ejecutando el comando "blkid -k".
|
||||
|
||||
**NOTA**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen virtual se está convirtiendo, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
|
||||
tags:
|
||||
- "Varios"
|
||||
parameters:
|
||||
- name: JSON
|
||||
in: body
|
||||
required: true
|
||||
description: |
|
||||
* **virtual_image** - Nombre de la imagen virtual, con extensión
|
||||
* **filesystem** - Sistema de archivos de la partición a clonar, en formato "blkid"
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
virtual_image:
|
||||
type: string
|
||||
example: "UbuntuVM.vdi"
|
||||
filesystem:
|
||||
type: string
|
||||
example: "ext4"
|
||||
responses:
|
||||
"200":
|
||||
description: "La imagen virtual se está convirtiendo."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
output:
|
||||
type: string
|
||||
example: "Converting virtual image..."
|
||||
"400 (Virtual image not found)":
|
||||
description: "No se ha encontrado la imagen virtual."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
exception:
|
||||
type: string
|
||||
example: "Virtual image not found"
|
||||
"400 (Name incorrect)":
|
||||
description: "Ya existe una imagen con el mismo nombre que la imagen virtual."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
exception:
|
||||
type: string
|
||||
example: "There is an image with the same name as the virtual image"
|
||||
"400 (No disk space)":
|
||||
description: "No hay espacio suficiente en disco."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
exception:
|
||||
type: string
|
||||
example: "There is not enough free disk space"
|
||||
"500":
|
||||
description: "Error al convertir la imagen virtual."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
error:
|
||||
type: string
|
||||
example: "Virtual image conversion failed"
|
||||
"500 (Error)":
|
||||
description: "Error al convertir la imagen virtual."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
error:
|
||||
type: string
|
||||
example: "(Error description)"
|
||||
"500 (Exception)":
|
||||
description: "Excepción inesperada al convertir la imagen virtual."
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
exception:
|
||||
type: string
|
||||
example: "(Exception description)"
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -27,7 +27,7 @@ import unittest
|
|||
from unittest.mock import patch, mock_open, MagicMock, AsyncMock
|
||||
from flask import json
|
||||
import os
|
||||
from repo_api import app, get_image_params, search_process, check_remote_connection, check_remote_image, check_lock_local, check_aux_files, check_file_exists, check_remote_backup
|
||||
from repo_api import app, get_image_params, search_process, check_remote_connection, check_remote_image, check_lock_local, check_aux_files, check_file_exists, check_remote_backup, check_virtual_image_conversion, check_free_space
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
@ -35,7 +35,8 @@ from repo_api import app, get_image_params, search_process, check_remote_connect
|
|||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
repo_path = '/opt/opengnsys/ogrepository/images'
|
||||
trash_path = '/opt/opengnsys/ogrepository/images_trash/'
|
||||
trash_path = '/opt/opengnsys/ogrepository/images_trash'
|
||||
vm_path = '/opt/opengnsys/ogrepository/images_virtual'
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
@ -56,23 +57,28 @@ class RepoApiTestCase(unittest.TestCase):
|
|||
def setUp(self):
|
||||
""" Configura el cliente de prueba de Flask y habilita el modo de prueba (antes de cada test).
|
||||
Esto permite simular peticiones HTTP a la API sin necesidad de un servidor en ejecución.
|
||||
Además, crea el archivo "test4unittest.img", para realizar las pruebas.
|
||||
Además, crea los archivos "test4unittest.img" y "test4unittest.vdi", para realizar las pruebas.
|
||||
"""
|
||||
self.app = app.test_client()
|
||||
self.app.testing = True
|
||||
# Hay que crear la imagen de prueba, y eliminarla al finalizar las pruebas.
|
||||
# Creamos las imágenes de prueba ("img" y "vdi"):
|
||||
with open(f"{repo_path}/test4unittest.img", 'w') as test_image:
|
||||
test_image.write(' ')
|
||||
with open(f"{vm_path}/test4unittestvm.vdi", 'w') as vtest_image:
|
||||
vtest_image.write(' ')
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
""" Limpia el entorno de pruebas (después de cada test).
|
||||
En este caso, elimina el archivo "test4unittest.img" (de "repo_path" y de "trash_path").
|
||||
En este caso, elimina el archivo "test4unittest.img" (de "repo_path" y de "trash_path"),
|
||||
y el archivo "test4unittestvm.vdi" (de "vm_path").
|
||||
"""
|
||||
if os.path.exists(f"{repo_path}/test4unittest.img"):
|
||||
os.remove(f"{repo_path}/test4unittest.img")
|
||||
if os.path.exists(f"{trash_path}/test4unittest.img"):
|
||||
os.remove(f"{trash_path}/test4unittest.img")
|
||||
if os.path.exists(f"{vm_path}/test4unittestvm.vdi"):
|
||||
os.remove(f"{vm_path}/test4unittestvm.vdi")
|
||||
|
||||
|
||||
def mock_search_process(process, string_to_search):
|
||||
|
@ -545,22 +551,6 @@ class RepoApiTestCase(unittest.TestCase):
|
|||
self.assertIn("Image is locked", json.loads(response.data)['exception'])
|
||||
|
||||
|
||||
@patch('repo_api.check_remote_connection')
|
||||
@patch('repo_api.get_image_params')
|
||||
@patch('repo_api.subprocess.Popen')
|
||||
def test_backup_image_error400_remote_image_exists(self, mock_popen, mock_get_image_params, mock_check_remote_connection):
|
||||
""" Método de prueba del endpoint "Hacer backup de una Imagen"
|
||||
(en caso de error "400", por imagen remota ya existente).
|
||||
"""
|
||||
print("Testing endpoint 'Hacer backup de una Imagen' (error 400, remote image exists)...")
|
||||
mock_check_remote_connection.return_value = True
|
||||
mock_get_image_params.return_value = {'name': 'test4unittest', 'extension': 'img'}
|
||||
mock_popen.side_effect = Exception("exit status 5")
|
||||
response = self.app.put('/ogrepository/v1/repo/images', data=json.dumps({"ID_img": "test_image_id", "repo_ip": "127.0.0.1", "user": "test_user", "remote_path": "/tmp"}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn("Image already exists on remote host", json.loads(response.data)['exception'])
|
||||
|
||||
|
||||
# ------------------------------------------------------------------- Tests "Crear archivos auxiliares"
|
||||
|
||||
|
||||
|
@ -601,16 +591,72 @@ class RepoApiTestCase(unittest.TestCase):
|
|||
self.assertIn('Image not found', json.loads(response.data)['exception'])
|
||||
|
||||
|
||||
# ------------------------------------------------------------------- Tests "Convertir imagen virtual"
|
||||
|
||||
|
||||
@patch('repo_api.subprocess.Popen')
|
||||
def test_create_torrent_sum_error400_image_locked(self, mock_popen):
|
||||
""" Método de prueba del endpoint "Crear archivos auxiliares"
|
||||
(en caso de error "400", por imagen bloqueada).
|
||||
@patch('repo_api.check_virtual_image_conversion')
|
||||
def test_convert_virtual_image(self, mock_popen, mock_check_virtual_image_conversion):
|
||||
""" Método de prueba del endpoint "Convertir imagen virtual".
|
||||
"""
|
||||
print("Testing endpoint 'Crear archivos auxiliares' (error 400, image locked)...")
|
||||
mock_popen.side_effect = Exception("exit status 3")
|
||||
response = self.app.post('/ogrepository/v1/images/torrentsum', data=json.dumps({"image": "test4unittest.img"}), content_type='application/json')
|
||||
print("Testing endpoint 'Convertir imagen virtual'...")
|
||||
mock_popen.return_value = MagicMock(returncode=None)
|
||||
mock_check_virtual_image_conversion.return_value = AsyncMock(returncode=None)
|
||||
response = self.app.post('/ogrepository/v1/images/virtual', data=json.dumps({"virtual_image":"test4unittestvm.vdi", "filesystem":"ext4"}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('Converting virtual image...', json.loads(response.data)['output'])
|
||||
|
||||
|
||||
@patch('repo_api.check_virtual_image_conversion')
|
||||
@patch('repo_api.check_file_exists')
|
||||
@patch('repo_api.subprocess.Popen')
|
||||
def test_convert_virtual_image_error500(self, mock_popen, mock_check_virtual_image_conversion, mock_check_file_exists):
|
||||
""" Método de prueba del endpoint "Convertir imagen virtual"
|
||||
(en caso de error "500").
|
||||
"""
|
||||
print("Testing endpoint 'Convertir imagen virtual' (error 500)...")
|
||||
mock_check_file_exists.return_value = True
|
||||
mock_popen.side_effect = Exception("Error al convertir la imagen virtual")
|
||||
mock_check_virtual_image_conversion.return_value = AsyncMock(returncode=None)
|
||||
response = self.app.post('/ogrepository/v1/images/virtual', data=json.dumps({"virtual_image":"test4unittestvm.vdi", "filesystem":"ext4"}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 500)
|
||||
self.assertIn('Error al convertir la imagen virtual', response.data.decode())
|
||||
|
||||
|
||||
@patch('repo_api.check_file_exists')
|
||||
def test_convert_virtual_image_error400_no_virtual_image(self, mock_check_file_exists):
|
||||
""" Método de prueba del endpoint "Convertir imagen virtual"
|
||||
(en caso de error "400", por imagen virtual inexistente).
|
||||
"""
|
||||
print("Testing endpoint 'Convertir imagen virtual' (error 400, no virtual image)...")
|
||||
mock_check_file_exists.return_value = False
|
||||
response = self.app.post('/ogrepository/v1/images/virtual', data=json.dumps({"virtual_image":"test4unittestvm.vdi", "filesystem":"ext4"}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn('Image is locked', json.loads(response.data)['exception'])
|
||||
self.assertIn('Virtual image not found', json.loads(response.data)['exception'])
|
||||
|
||||
|
||||
@patch('repo_api.check_file_exists')
|
||||
def test_convert_virtual_image_error400_name_incorrect(self, mock_check_file_exists):
|
||||
""" Método de prueba del endpoint "Convertir imagen virtual"
|
||||
(en caso de error "400", por nombre incorrecto).
|
||||
"""
|
||||
print("Testing endpoint 'Convertir imagen virtual' (error 400, name incorrect)...")
|
||||
mock_check_file_exists.return_value = True
|
||||
response = self.app.post('/ogrepository/v1/images/virtual', data=json.dumps({"virtual_image":"test4unittestvm.vdi", "filesystem":"ext4"}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn('There is an image with the same name as the virtual image', json.loads(response.data)['exception'])
|
||||
|
||||
|
||||
@patch('repo_api.check_free_space')
|
||||
def test_convert_virtual_image_error400_not_enough_space(self, mock_check_free_space):
|
||||
""" Método de prueba del endpoint "Convertir imagen virtual"
|
||||
(en caso de error "400", por falta de espacio en disco).
|
||||
"""
|
||||
print("Testing endpoint 'Convertir imagen virtual' (error 400, not enough space)...")
|
||||
mock_check_free_space.return_value = False
|
||||
response = self.app.post('/ogrepository/v1/images/virtual', data=json.dumps({"virtual_image":"test4unittestvm.vdi", "filesystem":"ext4"}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn('There is not enough free disk space', json.loads(response.data)['exception'])
|
||||
|
||||
|
||||
# ------------------------------------------------------------------- Tests "Enviar paquete Wake On Lan"
|
||||
|
|
|
@ -0,0 +1,412 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Este script convierte la imagen virtual especificada como primer parámetro (que debe haberse copiado previamente en la ruta "opt/opengnsys/ogrepository/images_virtual")
|
||||
en una imagen "img" como las que se generan desde OpenGnsys (con "partclone" y "lzop"), por lo que luego puede ser restaurada como cualquier otra imagen del repositorio.
|
||||
|
||||
Como segundo parámetro debe especificarse el sistema de archivos de la partición a clonar, en formato "blkid" ("ext4", "ntfs", etc).
|
||||
NOTA: Se puede comprobar todos los sistemas de archivos aceptados por "blkid" ejecutando el comando "blkid -k".
|
||||
|
||||
Una vez realizada la conversión llama al script "createTorrentSum.py", para crear los archivos auxiliares y actualizar la info del repositorio.
|
||||
|
||||
Paquetes APT requeridos: "qemu" (se puede instalar con "sudo apt install qemu-utils").
|
||||
"partclone" (se puede instalar con "sudo apt install partclone").
|
||||
"lzop" (se puede instalar con "sudo apt install lzop").
|
||||
|
||||
Parámetros
|
||||
------------
|
||||
sys.argv[1] - Nombre completo de la imagen virtual a convertir (sin ruta).
|
||||
- Ejemplo1: UbuntuVM.vdi
|
||||
- Ejemplo2: WindowsVM.vmdk
|
||||
|
||||
sys.argv[2] - Sistema de archivos de la partición a convertir (en formato "blkid").
|
||||
- Ejemplo1: ext4
|
||||
- Ejemplo2: ntfs
|
||||
|
||||
Sintaxis
|
||||
----------
|
||||
./convertVMtoIMG.py vm_image_name partition_filesystem
|
||||
|
||||
Ejemplos
|
||||
---------
|
||||
./convertVMtoIMG.py UbuntuVM.vdi ext4
|
||||
./convertVMtoIMG.py WindowsVM.vmdk ntfs
|
||||
"""
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# IMPORTS
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from systemd import journal
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# VARIABLES
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
script_name = os.path.basename(__file__)
|
||||
repo_path = '/opt/opengnsys/ogrepository/images/' # No borrar la barra final
|
||||
vm_path = '/opt/opengnsys/ogrepository/images_virtual/' # No borrar la barra final
|
||||
partclone_logfile = '/opt/opengnsys/ogrepository/log/partclone.log'
|
||||
create_torrent_script = '/opt/opengnsys/ogrepository/bin/createTorrentSum.py'
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# FUNCTIONS
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def show_help():
|
||||
""" Imprime la ayuda, cuando se ejecuta el script con el parámetro "help".
|
||||
"""
|
||||
help_text = f"""
|
||||
Sintaxis: {script_name} vm_image_name partition_filesystem
|
||||
Ejemplo1: {script_name} UbuntuVM.vdi ext4
|
||||
Ejemplo2: {script_name} WindowsVM.vmdk ntfs
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
|
||||
|
||||
def check_params():
|
||||
""" Comprueba que se haya enviado la cantidad correcta de parámetros, y en el formato correcto.
|
||||
Si no es así, muestra un mensaje de error, y sale del script.
|
||||
LLama a la función "show_help" cuando se ejecuta el script con el parámetro "help".
|
||||
"""
|
||||
# Si se ejecuta el script con el parámetro "help", se muestra la ayuda, y se sale del script:
|
||||
if len(sys.argv) == 2 and sys.argv[1] == "help":
|
||||
show_help()
|
||||
sys.exit(0)
|
||||
# Si se ejecuta el script con más o menos de 2 parámetroa, se muestra un error y la ayuda, y se sale del script:
|
||||
elif len(sys.argv) != 3:
|
||||
print(f"{script_name} Error: Formato incorrecto: Se debe especificar 2 parámetros")
|
||||
show_help()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
def convert_to_raw(vm_image_name, vm_extension):
|
||||
""" Convierte la imagen virtual a formato "RAW", mediante "qemu-img".
|
||||
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Running command 'qemu-img convert'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
result = subprocess.run(['qemu-img', 'convert', '-O', 'raw', f"{vm_path}{vm_image_name}.{vm_extension}", f"{vm_path}{vm_image_name}.raw"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Evaluamos el resultado de la ejecución, retornando "True" si es correcta y "False" si no lo es:
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# Si se produce un error o una excepción lo imprimimos en el log, y retornamos "False":
|
||||
except subprocess.CalledProcessError as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'qemu-img' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'qemu-img' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
print(f"Unexpected error: {error}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def map_vm_partitions(vm_image_name):
|
||||
""" Mapea las particiones de la imagen RAW en "dev/mapper", para que "partclone" pueda convertir la imagen.
|
||||
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
|
||||
NOTA: Debe ejecutarse con "sudo", o dará error.
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Running command 'kpartx -a'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
result = subprocess.run(['sudo', 'kpartx', '-a', f"{vm_path}{vm_image_name}.raw"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Evaluamos el resultado de la ejecución, retornando "True" si es correcta y "False" si no lo es:
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# Si se produce un error o una excepción lo imprimimos en el log, y retornamos "False":
|
||||
except subprocess.CalledProcessError as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'kpartx -a' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'kpartx -a' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def get_target_device(filesystem):
|
||||
""" Busca entre los mapeos generados por "kpartx" el dispositivo correspondiente a la partición a restaurar (en base al filesystem especificado como parámetro),
|
||||
ejecutando el comando "blkid" sobre cada dispositivo mapeado (por lo que el filesystem debe respetar la nomenclatura de "blkid").
|
||||
Si se ejecuta correctamente retorna el dispositivo de destino, y si da error retorna un mensaje que incluye "Filesystem".
|
||||
No estoy seguro de que sea necesario, pero por las dudas lo ejecuto con "sudo" (como no crea ningún archivo, no dará problemas de propietario).
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Getting target device...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
# Almacenamos en una lista los mapeos generados por "kpartx", y eliminamos el elemento "control" (que no lo ha generado "kpartx"):
|
||||
map_list = os.listdir('/dev/mapper')
|
||||
map_list.remove('control')
|
||||
|
||||
# Sobre cada mapeo ejecutamos el comando "blkid", buscamos el filesystem en la respuesta, y si lo encontramos extraemos el nombre del dispositivo (para pasárselo a "partclone"):
|
||||
for device in map_list:
|
||||
# Ejecutamos el comando "blkid" sobre el mapeo actual:
|
||||
result = subprocess.run(['sudo', 'blkid', f"/dev/mapper/{device}"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
|
||||
# Si encontramos el filesystem, extraemos el dispositivo, y lo retornamos:
|
||||
if f'TYPE="{filesystem}"' in result.stdout:
|
||||
target_device = result.stdout.split('/')[3].split(':')[0]
|
||||
journal.send(f"convertVMtoIMG.py: Target device obtained: {target_device}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return target_device
|
||||
|
||||
# Si no encontramos el filesystem en ninguno de los mapeos, guardamos un log y retornamos un mensaje informativo:
|
||||
journal.send("convertVMtoIMG.py: Filesystem not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return "Filesystem not found"
|
||||
|
||||
# Si se produce una excepción lo imprimimos en el log, y retornamos un mensaje informativo:
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'get_target_device' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return "Error getting Filesystem"
|
||||
|
||||
|
||||
|
||||
def convert_to_partclone(vm_image_name, target_device):
|
||||
""" Convierte la imagen "vm_image_name" con "partclone", para que pueda ser restaurada desde ogLive.
|
||||
Como origen no utiliza la imagen "RAW", sino una partición mapeada en "/dev/mapper" (almacenada en "target_device").
|
||||
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Running command 'partclone.extfs'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
result = subprocess.run(['partclone.extfs', '-c', '-s', f"/dev/mapper/{target_device}", '-o', f"{vm_path}{vm_image_name}.img", '-L', partclone_logfile], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Evaluamos el resultado de la ejecución, retornando "True" si es correcta y "False" si no lo es:
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# Si se produce un error o una excepción lo imprimimos en el log, y retornamos "False":
|
||||
except subprocess.CalledProcessError as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'partclone.extfs' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'partclone.extfs' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def umap_vm_partitions(vm_image_name):
|
||||
""" Desmapea las particiones de la imagen RAW, desde "dev/mapper".
|
||||
No retorna "True" o "False", porque este paso no afecta a la conversión de la imagen.
|
||||
NOTA: Debe ejecutarse con "sudo", o dará error.
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Running command 'kpartx -d'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
result = subprocess.run(['sudo', 'kpartx', '-d', f"{vm_path}{vm_image_name}.raw"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Evaluamos el resultado de la ejecución, imprimiendo en el log el mensaje correspondiente:
|
||||
if result.returncode == 0:
|
||||
journal.send("convertVMtoIMG.py: Partitions ummap OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Partitions umap failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
# Si se produce un error o una excepción lo imprimimos en el log:
|
||||
except subprocess.CalledProcessError as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'kpartx -d' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'kpartx -d' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
|
||||
def compress_image(vm_image_name):
|
||||
""" Comprime la imagen generada con "partclone", con "lzop".
|
||||
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Running command 'lzop'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
result = subprocess.run(['lzop', f"{vm_path}{vm_image_name}.img"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Evaluamos el resultado de la ejecución, retornando "True" si es correcta y "False" si no lo es:
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# Si se produce un error o una excepción lo imprimimos en el log, y retornamos "False":
|
||||
except subprocess.CalledProcessError as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'lzop' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'lzop' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def prepare_image(vm_image_name):
|
||||
""" Mueve la imagen comprimida al repositorio de imágenes, sustituyendo la extensión ".img.lzo" por ".img".
|
||||
Calcula el "datasize" aproximado, y crea el archivo "info", para dejar la imagen preparada para añadir al repositorio.
|
||||
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Preparing image...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
# Movemos la imagen comprimida al repositorio de imágenes, y sustituimos la extensión ".img.lzo" por ".img":
|
||||
shutil.move(f"{vm_path}{vm_image_name}.img.lzo", f"{repo_path}{vm_image_name}.img")
|
||||
|
||||
# Calculamos aproximadamente lo que puede ocupar la imagen una vez restaurada (multiplicando el tamaño de la imagen por "2.5"):
|
||||
datasize = int(os.path.getsize(f"{repo_path}{vm_image_name}.img") * 2.5)
|
||||
|
||||
# Creamos el archivo "info":
|
||||
line_to_write = f"PARTCLONE:LZOP:EXTFS:{datasize}:unknown"
|
||||
with open(f"{repo_path}{vm_image_name}.img.info", 'w') as file:
|
||||
file.write(line_to_write)
|
||||
|
||||
# Como todo ha ido bien hasta aquí, retornamos "True":
|
||||
return True
|
||||
|
||||
# Si se produce una excepción lo imprimimos en el log, y retornamos "False":
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: Prepare image exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def create_torrentsum(vm_image_name):
|
||||
""" Crea los archivos auxiliares asociados a la imagen convertida, y actualiza la información del repositorio
|
||||
(llamando al script "createTorrentSum.py", que a su vez llama al script "updateRepoInfo.py").
|
||||
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
|
||||
"""
|
||||
try:
|
||||
journal.send("convertVMtoIMG.py: Running script 'createTorrentSum.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
result = subprocess.run(['python3', create_torrent_script, f"{repo_path}{vm_image_name}.img"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Evaluamos el resultado de la ejecución, retornando "True" si es correcta y "False" si no lo es:
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# Si se produce un error o una excepción lo imprimimos en el log, y retornamos "False":
|
||||
except subprocess.CalledProcessError as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'createTorrentSum.py' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
except Exception as error:
|
||||
journal.send(f"convertVMtoIMG.py: 'createTorrentSum.py' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def erase_image_file(vm_image_name, ext):
|
||||
""" Borra el archivo "vm_image_name" con extensión "ext",
|
||||
desde el directorio de imágenes virtuales.
|
||||
No retorna "True" o "False", porque este paso no afecta a la conversión de la imagen.
|
||||
"""
|
||||
journal.send(f"convertVMtoIMG.py: Erasing file with extension {ext}...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
# Si existe el archivo "vm_image_name.ext", lo borramos:
|
||||
if os.path.exists(f"{vm_path}{vm_image_name}{ext}"):
|
||||
os.remove(f"{vm_path}{vm_image_name}{ext}")
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# MAIN
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
"""
|
||||
"""
|
||||
# Evaluamos si se ha enviado la cantidad correcta de parámetros, y en el formato correcto:
|
||||
check_params()
|
||||
|
||||
# Almacenamos el nombre completo de la imagen y el sistema de archivos (desde los parámetros), y extraemos el nombre y la extensión:
|
||||
vm_image_name_full = sys.argv[1]
|
||||
vm_image_name = vm_image_name_full.split('.')[0]
|
||||
vm_extension = vm_image_name_full.split('.')[1]
|
||||
filesystem = sys.argv[2].lower()
|
||||
|
||||
|
||||
# Convertimos la imagen virtual a RAW (con "qemu-img"):
|
||||
raw_conversion = convert_to_raw(vm_image_name, vm_extension)
|
||||
if raw_conversion == False:
|
||||
journal.send("convertVMtoIMG.py: Conversion to RAW failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
erase_image_file(vm_image_name, '.raw') # Como ha fallado, borramos la imagen "RAW"
|
||||
sys.exit(2)
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Conversion to RAW OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
# Mapeamos las particiones de la imagen RAW (con "kpartx -a"):
|
||||
partitions_map = map_vm_partitions(vm_image_name)
|
||||
if partitions_map == False:
|
||||
journal.send("convertVMtoIMG.py: Partitions map failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
erase_image_file(vm_image_name, '.raw') # Como ha fallado, borramos la imagen "RAW"
|
||||
sys.exit(3)
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Partitions map OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
# Obtenemos la partición mapeada de destino (con "blkid"):
|
||||
target_device = get_target_device(filesystem)
|
||||
if "Filesystem" in target_device:
|
||||
journal.send("convertVMtoIMG.py: Get target device failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
umap_vm_partitions(vm_image_name) # Como ha fallado, desmapeamos las particiones de la imagen "RAW"
|
||||
erase_image_file(vm_image_name, '.raw') # Como ha fallado, borramos la imagen "RAW"
|
||||
sys.exit(4)
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Get target device OK", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
# Convertimos la imagen con "partclone", desde la partición mapeada:
|
||||
partclone_conversion = convert_to_partclone(vm_image_name, target_device)
|
||||
if partclone_conversion == False:
|
||||
journal.send("convertVMtoIMG.py: Conversion to Partclone failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
erase_image_file(vm_image_name, '.raw') # Como ha fallado, borramos la imagen "RAW"
|
||||
erase_image_file(vm_image_name, '.img') # Como ha fallado, borramos la imagen generada con "partclone" (sin comprimir)
|
||||
sys.exit(5)
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Conversion to Partclone OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
# Desmapeamos las particiones de la imagen RAW (con "kpartx -d"):
|
||||
umap_vm_partitions(vm_image_name)
|
||||
|
||||
|
||||
# Borramos la imagen "RAW", generada con "qemu-img":
|
||||
erase_image_file(vm_image_name, '.raw')
|
||||
|
||||
|
||||
# Comprimimos la imagen con "lzop":
|
||||
image_compression = compress_image(vm_image_name)
|
||||
if image_compression == False:
|
||||
journal.send("convertVMtoIMG.py: Image compression failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
erase_image_file(vm_image_name, '.img') # Como ha fallado, borramos la imagen generada con "partclone" (sin comprimir)
|
||||
sys.exit(6)
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Image compression OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
# Borramos la imagen generada con "partclone" (sin comprimir):
|
||||
erase_image_file(vm_image_name, '.img')
|
||||
|
||||
|
||||
# Movemos la imagen comprimida al repositorio de imágenes, y creamos el archivo "info":
|
||||
image_prepared = prepare_image(vm_image_name)
|
||||
if image_prepared == False:
|
||||
journal.send("convertVMtoIMG.py: Image preparation failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
sys.exit(7)
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Image preparation OK", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
# Creamos los archivos auxiliares, y actualizamos la información del repositorio:
|
||||
image_ready = create_torrentsum(vm_image_name)
|
||||
if image_ready == False:
|
||||
journal.send("convertVMtoIMG.py: Auxiliar files creation failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
sys.exit(8)
|
||||
else:
|
||||
journal.send("convertVMtoIMG.py: Auxiliar files creation OK", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
Loading…
Reference in New Issue