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)
|
- **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)
|
- **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")
|
- **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:
|
Librerías Python requeridas:
|
||||||
- **flask** (se puede instalar con "sudo apt install python3-flask")
|
- **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:
|
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/**
|
||||||
- **/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_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/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/api/** (aquí debe estar la API y el Swagger)
|
||||||
- **/opt/opengnsys/ogrepository/etc/** (aquí se guardan los archivos "repoinfo.json" y "trashinfo.json")
|
- **/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`
|
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`
|
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`
|
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`
|
12. [Convertir Imagen Virtual](#convertir-imagen-virtual) - `POST /ogrepository/v1/images/virtual`
|
||||||
13. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
13. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
|
||||||
14. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
14. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
||||||
15. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
15. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
||||||
16. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
16. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
||||||
17. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
17. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
||||||
18. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
18. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
||||||
19. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
|
19. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
||||||
20. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
|
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
|
### 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.
|
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.
|
**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
|
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:**
|
**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 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
|
### 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
|
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:**
|
**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 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
|
### 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 400 Bad Request:** No se ha encontrado la imagen especificada.
|
||||||
- **Código 200 OK:** Los archivos auxiliares se están creando.
|
- **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
|
### 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`
|
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`
|
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`
|
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`
|
12. [Convertir Imagen Virtual](#convertir-imagen-virtual) - `POST /ogrepository/v1/images/virtual`
|
||||||
13. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
13. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
|
||||||
14. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
14. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
|
||||||
15. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
15. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
|
||||||
16. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
16. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
|
||||||
17. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
17. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
|
||||||
18. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
18. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
|
||||||
19. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
|
19. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
|
||||||
20. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
|
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
|
### 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.
|
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.
|
**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
|
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:**
|
**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 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
|
### 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
|
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:**
|
**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 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
|
### 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 400 Bad Request:** No se ha encontrado la imagen especificada.
|
||||||
- **Código 200 OK:** Los archivos auxiliares se están creando.
|
- **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
|
### Enviar paquete Wake On Lan
|
||||||
|
|
||||||
|
|
233
api/repo_api.py
233
api/repo_api.py
|
@ -29,6 +29,7 @@ import threading
|
||||||
import requests
|
import requests
|
||||||
import random
|
import random
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import psutil
|
||||||
from systemd import journal
|
from systemd import journal
|
||||||
# Imports para Swagger:
|
# Imports para Swagger:
|
||||||
from flasgger import Swagger
|
from flasgger import Swagger
|
||||||
|
@ -40,6 +41,7 @@ import yaml
|
||||||
# --------------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
repo_path = '/opt/opengnsys/ogrepository/images/' # No borrar la barra final
|
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'
|
script_path = '/opt/opengnsys/ogrepository/bin'
|
||||||
repo_file = '/opt/opengnsys/ogrepository/etc/repoinfo.json'
|
repo_file = '/opt/opengnsys/ogrepository/etc/repoinfo.json'
|
||||||
trash_file = '/opt/opengnsys/ogrepository/etc/trashinfo.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):
|
def recall_ogcore(data):
|
||||||
""" Hace una petición HTTP de tipo POST a un endpoint de ogCore, enviando datos que dependen del caso.
|
""" 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,
|
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")
|
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
|
# ENDPOINTS
|
||||||
|
@ -1136,20 +1215,12 @@ def backup_image():
|
||||||
"process exception": str(error)
|
"process exception": str(error)
|
||||||
}), 500
|
}), 500
|
||||||
except Exception as error_description:
|
except Exception as error_description:
|
||||||
if "exit status 5" in str(error_description):
|
journal.send(f"Script 'backupImage.py' result KO (Exception: {str(error_description)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||||
journal.send("Image already exists on remote host", PRIORITY=journal.LOG_WARNING, 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")
|
||||||
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({
|
||||||
return jsonify({
|
"success": False,
|
||||||
"success": False,
|
"exception": str(error_description)
|
||||||
"exception": "Image already exists on remote host"
|
}), 500
|
||||||
}), 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
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
@ -1217,27 +1288,12 @@ def create_torrent_sum():
|
||||||
"error": result.stderr
|
"error": result.stderr
|
||||||
}), 500
|
}), 500
|
||||||
except Exception as error_description:
|
except Exception as error_description:
|
||||||
if "exit status 2" in str(error_description):
|
journal.send(f"Script 'createTorrentSum.py' result KO (Exception: {str(error_description)})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
|
||||||
journal.send("Image not found", PRIORITY=journal.LOG_WARNING, 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")
|
||||||
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({
|
||||||
return jsonify({
|
"success": False,
|
||||||
"success": False,
|
"exception": str(error_description)
|
||||||
"exception": "Image not found"
|
}), 500
|
||||||
}), 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
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
@ -1786,6 +1842,113 @@ def stop_p2p():
|
||||||
}), 500
|
}), 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"
|
example: "user_name"
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: "La imagen se está importando."
|
description: "La imagen se está transfiriendo."
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -1450,7 +1450,7 @@ paths:
|
||||||
example: "/home/opengnsys"
|
example: "/home/opengnsys"
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: "La imagen se está copiando."
|
description: "Se está haciendo backup de la imagen."
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -1493,17 +1493,6 @@ paths:
|
||||||
exception:
|
exception:
|
||||||
type: string
|
type: string
|
||||||
example: "Can't connect to remote host"
|
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)":
|
"500 (Error)":
|
||||||
description: "Error al copiar la imagen."
|
description: "Error al copiar la imagen."
|
||||||
schema:
|
schema:
|
||||||
|
@ -1578,17 +1567,6 @@ paths:
|
||||||
exception:
|
exception:
|
||||||
type: string
|
type: string
|
||||||
example: "Image not found"
|
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)":
|
"500 (Error)":
|
||||||
description: "Error al crear los archivos auxiliares."
|
description: "Error al crear los archivos auxiliares."
|
||||||
schema:
|
schema:
|
||||||
|
@ -1673,4 +1651,112 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
example: "(Exception description)"
|
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 unittest.mock import patch, mock_open, MagicMock, AsyncMock
|
||||||
from flask import json
|
from flask import json
|
||||||
import os
|
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'
|
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):
|
def setUp(self):
|
||||||
""" Configura el cliente de prueba de Flask y habilita el modo de prueba (antes de cada test).
|
""" 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.
|
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 = app.test_client()
|
||||||
self.app.testing = True
|
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:
|
with open(f"{repo_path}/test4unittest.img", 'w') as test_image:
|
||||||
test_image.write(' ')
|
test_image.write(' ')
|
||||||
|
with open(f"{vm_path}/test4unittestvm.vdi", 'w') as vtest_image:
|
||||||
|
vtest_image.write(' ')
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
""" Limpia el entorno de pruebas (después de cada test).
|
""" 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"):
|
if os.path.exists(f"{repo_path}/test4unittest.img"):
|
||||||
os.remove(f"{repo_path}/test4unittest.img")
|
os.remove(f"{repo_path}/test4unittest.img")
|
||||||
if os.path.exists(f"{trash_path}/test4unittest.img"):
|
if os.path.exists(f"{trash_path}/test4unittest.img"):
|
||||||
os.remove(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):
|
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'])
|
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"
|
# ------------------------------------------------------------------- Tests "Crear archivos auxiliares"
|
||||||
|
|
||||||
|
|
||||||
|
@ -601,16 +591,72 @@ class RepoApiTestCase(unittest.TestCase):
|
||||||
self.assertIn('Image not found', json.loads(response.data)['exception'])
|
self.assertIn('Image not found', json.loads(response.data)['exception'])
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------- Tests "Convertir imagen virtual"
|
||||||
|
|
||||||
|
|
||||||
@patch('repo_api.subprocess.Popen')
|
@patch('repo_api.subprocess.Popen')
|
||||||
def test_create_torrent_sum_error400_image_locked(self, mock_popen):
|
@patch('repo_api.check_virtual_image_conversion')
|
||||||
""" Método de prueba del endpoint "Crear archivos auxiliares"
|
def test_convert_virtual_image(self, mock_popen, mock_check_virtual_image_conversion):
|
||||||
(en caso de error "400", por imagen bloqueada).
|
""" Método de prueba del endpoint "Convertir imagen virtual".
|
||||||
"""
|
"""
|
||||||
print("Testing endpoint 'Crear archivos auxiliares' (error 400, image locked)...")
|
print("Testing endpoint 'Convertir imagen virtual'...")
|
||||||
mock_popen.side_effect = Exception("exit status 3")
|
mock_popen.return_value = MagicMock(returncode=None)
|
||||||
response = self.app.post('/ogrepository/v1/images/torrentsum', data=json.dumps({"image": "test4unittest.img"}), content_type='application/json')
|
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.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"
|
# ------------------------------------------------------------------- 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