refs #1689 - Add Convert IMG to VM

pull/31/head
Gerardo GIl Elizeire 2025-03-18 16:29:29 +01:00
parent 8fb7a25c5e
commit 997819aafe
7 changed files with 1647 additions and 635 deletions

162
CHANGELOG.md 100644
View File

@ -0,0 +1,162 @@
# Changelog
## [0.7.0] - 2025-03-18
### Added
- Merge pull request 'refs #1689 - Add Convert IMG to VM' (#28) from add_python_scripts into main
## [0.6.4] - 2025-03-13
### Added
- Merge pull request 'refs #1701 - 'convertVMtoIMG.py' improvement' (#27) from add_python_scripts into main
## [0.6.3] - 2025-03-12
### Added
- Merge pull request 'refs #1701 - Fix script convertVMtoIMG.py' (#26) from add_python_scripts into main
## [0.6.2] - 2025-03-10
### Added
- Merge pull request 'refs #1681 - Add ogGit install status' (#25) from add_python_scripts into main
## [0.6.1] - 2025-03-05
### Added
- Merge pull request 'refs #1642 - Modify Cancel Transfers scripts' (#24) from add_python_scripts into main
## [0.6.0] - 2025-03-03
### Added
- Merge pull request 'refs #1525 - Add Convert VM to IMG' (#23) from add_python_scripts into main
## [0.5.23] - 2025-02-20
### Added
- Merge pull request 'refs #1530 - Add 'backupImage.py' and related endpoint' (#22) from add_python_scripts into main
## [0.5.22] - 2025-02-20
### Added
- Merge pull request 'refs #1530 - Add 'backupImage.py' and related endpoint' (#22) from add_python_scripts into main
## [0.5.20] - 2025-02-11
### Added
- Merge pull request 'refs #1482 - Modify Paramiko SSH Client parameters' (#21) from add_python_scripts into main
## [0.5.19] - 2025-02-06
### Added
- Merge pull request 'add_python_scripts' (#20) from add_python_scripts into main
## [0.5.18] - 2025-02-03
### Added
- Merge pull request 'refs #1437 - Scripts corrections' (#19) from add_python_scripts into main
## [0.5.17] - 2025-02-03
### Added
- Merge pull request 'refs #1437 - Remove unnecessary sudo calls' (#18) from add_python_scripts into main
## [0.5.16] - 2025-01-31
### Added
- Merge pull request 'refs #1378 - Add API tests and modify API' (#17) from add_python_scripts into main
## [0.5.15] - 2025-01-28
### Added
- Merge pull request 'refs #1378 - Fix API tests errors' (#16) from add_python_scripts into main
## [0.5.14] - 2025-01-24
### Added
- Merge pull request 'refs #1346 - Add API tests' (#15) from add_python_scripts into main
## [0.5.13] - 2025-01-10
### Added
- Merge pull request 'refs #1335 - Supress recursive image search' (#14) from add_python_scripts into main
## [0.5.12] - 2024-12-17
### Added
- Merge pull request 'refs #1294 - Modify logs format' (#13) from add_python_scripts into main
## [0.5.11] - 2024-12-12
### Added
- Merge pull request 'refs #1242 - API logs improvement' (#12) from add_python_scripts into main
## [0.5.10] - 2024-12-02
### Added
- Merge pull request 'refs #631 - Modify logs in API and some scripts' (#11) from add_python_scripts into main
## [0.5.9] - 2024-11-29
### Added
- Merge pull request 'refs #631 - Add journalctl logs to scripts' (#10) from add_python_scripts into main
## [0.5.8] - 2024-11-28
### Added
- Merge pull request 'refs #631 - Add journalctl logs to API' (#9) from add_python_scripts into main
## [0.5.7] - 2024-11-26
## [0.5.6] - 2024-11-22
### Added
- Merge pull request 'refs #631 - More API improvements' (#8) from add_python_scripts into main
## [0.5.5] - 2024-11-21
### Added
- Merge pull request 'refs #631 - API improvement' (#7) from add_python_scripts into main
## [0.5.4] - 2024-11-20
### Added
- Merge pull request 'add_python_scripts' (#6) from add_python_scripts into main
## [0.5.3] - 2024-11-18
## [0.5.2] - 2024-11-18
## [0.5.1] - 2024-11-15
## [0.5.0] - 2024-11-15

511
README.md
View File

@ -62,19 +62,20 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
6. [Eliminar una Imagen](#eliminar-una-imagen) - `DELETE /ogrepository/v1/images/{ID_img}?method={method}`
7. [Recuperar una Imagen](#recuperar-una-imagen) - `POST /ogrepository/v1/trash/images`
8. [Eliminar una Imagen de la Papelera](#eliminar-una-imagen-de-la-papelera) - `DELETE /ogrepository/v1/trash/images/{ID_img}`
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. [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`
9. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
10. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
11. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
12. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
13. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
14. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
15. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
16. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
17. [Transferir una Imagen entre Repositorios](#transferir-una-imagen-entre-repositorios) - `POST /ogrepository/v1/repo/images`
18. [Hacer Backup de una Imagen](#hacer-backup-de-una-imagen) - `PUT /ogrepository/v1/repo/images`
19. [Convertir Imagen Virtual a Imagen OpenGnsys](#convertir-imagen-virtual-a-imagen-opengnsys) - `POST /ogrepository/v1/images/virtual`
20. [Convertir Imagen OpenGnsys a Imagen Virtual](#convertir-imagen-opengnsys-a-imagen-virtual) - `PUT /ogrepository/v1/images/virtual`
21. [Crear archivos auxiliares](#crear-archivos-auxiliares) - `POST /ogrepository/v1/images/torrentsum`
22. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
---
### Obtener Información de Estado de ogRepository
@ -367,6 +368,210 @@ curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/t
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se eliminó exitosamente.
---
### Enviar una Imagen mediante UDPcast
Se enviará la imagen especificada por Multicast, mediante la aplicación UDPcast.
Se puede hacer con el script "**sendFileMcast.py**", que a su vez llama al binario "**udp-sender**", que es quien realmente realiza el envío.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **method**: Modalidad half-duplex o full-duplex ("half" o "full").
- **ip**: IP Multicast.
- **bitrate**: Velocidad de transmisión (en Mbps).
- **nclients**: Número mínimo de clientes.
- **maxtime**: Tiempo máximo de espera.
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "method":"full", "ip":"239.194.17.2", "bitrate":"70M", "nclients":"20", "maxtime":"120"}' http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UDPcast.
---
### Ver Estado de Transmisiones UDPcast
Se devolverá el pid de los procesos de transferencias UDPcast activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUDPcastInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UDPcast.
- **Código 400 Bad Request:** No se han encontrado transmisiones UDPcast activas.
- **Código 200 OK:** La información de las transmisiones UDPcast activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UDPcast activas en formato JSON.
```json
{
"6720": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"6721": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Cancelar Transmisión UDPcast
Se cancelará la transmisión por UDPcast activa de la imagen especificada como parámetro, deteniendo el proceso "udp-sender" asociado a dicha imagen.
Se puede hacer con el script "**stopUDPcast.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/udpcast/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UDPcast.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UDPcast activas para la imagen especificada.
- **Código 200 OK:** La transmisión UDPcast se ha cancelado exitosamente.
---
### Enviar una Imagen mediante UFTP
Se enviará la imagen especificada por Unicast o Multicast, mediante el protocolo "UFTP".
Se puede hacer con el script "**sendFileUFTP.py**", que requiere que previamente los clientes ogLive destino se pongan en escucha con un daemon "UFTPD" (ejecutando el script "**listenUFTPD.py**"). Esto funciona al revés que "UDPcast", ya que primero se debe ejecutar un comando en los clientes, y luego en el servidor.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **ip**: IP Unicast/Multicast.
- **bitrate**: Velocidad de transmisión (con "K" para Kbps, "M" para Mbps o "G" para Gbps).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "ip":"239.194.17.2", "bitrate":"1G"}' http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UFTP.
---
### Ver Estado de Transmisiones UFTP
Se devolverá el pid de los procesos de transferencias UFTP activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUFTPInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UFTP.
- **Código 400 Bad Request:** No se han encontrado transmisiones UFTP activas.
- **Código 200 OK:** La información de las transmisiones UFTP activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UFTP activas en formato JSON.
```json
{
"3427": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"4966": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Cancelar Transmisión UFTP
Se cancelará la transmisión por UFTP activa de la imagen especificada como parámetro, deteniendo el proceso "uftp" asociado a dicha imagen.
Se puede hacer con el script "**stopUFTP.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/uftp/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UFTP.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UFTP activas para la imagen especificada.
- **Código 200 OK:** La transmisión UFTP se ha cancelado exitosamente.
---
### Enviar una Imagen mediante P2P
Se enviará la imagen especificada mediante "P2P", iniciando el tracker y el seeder (que harán tracking y seed de los torrents contenidos en el directorio de imágenes).
Se puede hacer con los scripts "**runTorrentTracker.py**" y "**runTorrentSeeder.py**", que deben ser llamados por el endpoint.
**NOTA**: Estos scripts no reciben parámetros, pero es necesario que el endpoint compruebe si la imagen a enviar existe antes de llamarlos, por lo que se le debe enviar el ID de la imagen (que corresponde al contenido del archivo "full.sum") 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 se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d"}' http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al intentar enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante P2P.
---
### Cancelar Transmisiones P2P
Se cancelarán las transmisiones P2P activas en el ogRepository al que se envíe la orden, deteniendo los procesos "bttrack" y "btlaunchmany.bittornado".
Se puede hacer con el script "**stopP2P.py**", que debe ser llamado por el endpoint.
**NOTA**: No he encontrado la forma de detener la transmisión de una imagen concreta, ya que "bttrack" y "btlaunchmany.bittornado" hacen tracking y seed (respectivamente) de todos los torrents existentes en la raíz del directorio especificado.
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar las transmisiones P2P.
- **Código 200 OK:** Las transmisiones P2P se han cancelado exitosamente.
---
### Transferir una Imagen entre Repositorios
@ -423,31 +628,7 @@ curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 200 OK:** Se está haciendo backup de la imagen.
---
### Crear archivos auxiliares
Se crearán los archivos ".sum", ".full.sum", ".size" y ".torrent", para la imagen especificada como parámetro.
Se puede hacer con el script "**createTorrentSum.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este parámetro no puede obtenerse en la API, a partir del ID de imagen (como en otros casos), porque el ID corresponde al contenido del archivo "full.sum" asociado (que no estará creado hasta que no se ejecute este script).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar cierto tiempo, por lo que solo informa de que los archivos auxiliares se están creando, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
**URL:** `/ogrepository/v1/images/torrentsum`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **image**: Nombre de la imagen (con extensión).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img"}' http://example.com/ogrepository/v1/images/torrentsum
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al crear los archivos auxiliares.
- **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
### Convertir Imagen Virtual a Imagen OpenGnsys
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.
@ -471,6 +652,55 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 400 Bad Request:** No se ha encontrado la imagen virtual especificada.
- **Código 200 OK:** La imagen virtual se está convirtiendo.
---
### Convertir Imagen OpenGnsys a Imagen Virtual
Se convertirá la imagen "img" especificada en una imagen virtual con la extensión especificada ("vdi", "vmdk", etc), guardándola en la ruta "opt/opengnsys/ogrepository/images_virtual/export".
Se puede hacer con el script "**convertIMGtoVM.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y la extensión del disco virtual destino ("vdi", "vmdk", etc) como segundo parámetro. El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero la extensión del disco virtual debe enviarse en el JSON.
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está convirtiendo a virtual, 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:** PUT
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **vm_extension**: Extensión del disco virtual destino ("vdi", "vmdk", etc).
**Ejemplo de Solicitud:**
```bash
curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "vm_extension":"vdi"}' http://example.com/ogrepository/v1/images/virtual
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al convertir la imagen a virtual.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está convirtiendo a virtual.
---
### Crear archivos auxiliares
Se crearán los archivos ".sum", ".full.sum", ".size" y ".torrent", para la imagen especificada como parámetro.
Se puede hacer con el script "**createTorrentSum.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este parámetro no puede obtenerse en la API, a partir del ID de imagen (como en otros casos), porque el ID corresponde al contenido del archivo "full.sum" asociado (que no estará creado hasta que no se ejecute este script).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar cierto tiempo, por lo que solo informa de que los archivos auxiliares se están creando, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
**URL:** `/ogrepository/v1/images/torrentsum`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **image**: Nombre de la imagen (con extensión).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img"}' http://example.com/ogrepository/v1/images/torrentsum
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al crear los archivos auxiliares.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** Los archivos auxiliares se están creando.
---
### Enviar paquete Wake On Lan
@ -494,209 +724,4 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 500 Internal Server Error:** Ocurrió un error al enviar el paquete Wake On Lan.
- **Código 200 OK:** El paquete Wake On Lan se ha enviado exitosamente.
---
### Enviar una Imagen mediante UDPcast
Se enviará la imagen especificada por Multicast, mediante la aplicación UDPcast.
Se puede hacer con el script "**sendFileMcast.py**", que a su vez llama al binario "**udp-sender**", que es quien realmente realiza el envío.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **method**: Modalidad half-duplex o full-duplex ("half" o "full").
- **ip**: IP Multicast.
- **bitrate**: Velocidad de transmisión (en Mbps).
- **nclients**: Número mínimo de clientes.
- **maxtime**: Tiempo máximo de espera.
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "method":"full", "ip":"239.194.17.2", "bitrate":"70M", "nclients":"20", "maxtime":"120"}' http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UDPcast.
---
### Enviar una Imagen mediante UFTP
Se enviará la imagen especificada por Unicast o Multicast, mediante el protocolo "UFTP".
Se puede hacer con el script "**sendFileUFTP.py**", que requiere que previamente los clientes ogLive destino se pongan en escucha con un daemon "UFTPD" (ejecutando el script "**listenUFTPD.py**"). Esto funciona al revés que "UDPcast", ya que primero se debe ejecutar un comando en los clientes, y luego en el servidor.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **ip**: IP Unicast/Multicast.
- **bitrate**: Velocidad de transmisión (con "K" para Kbps, "M" para Mbps o "G" para Gbps).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "ip":"239.194.17.2", "bitrate":"1G"}' http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UFTP.
---
### Enviar una Imagen mediante P2P
Se enviará la imagen especificada mediante "P2P", iniciando el tracker y el seeder (que harán tracking y seed de los torrents contenidos en el directorio de imágenes).
Se puede hacer con los scripts "**runTorrentTracker.py**" y "**runTorrentSeeder.py**", que deben ser llamados por el endpoint.
**NOTA**: Estos scripts no reciben parámetros, pero es necesario que el endpoint compruebe si la imagen a enviar existe antes de llamarlos, por lo que se le debe enviar el ID de la imagen (que corresponde al contenido del archivo "full.sum") 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 se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d"}' http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al intentar enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante P2P.
---
### Ver Estado de Transmisiones UDPcast
Se devolverá el pid de los procesos de transferencias UDPcast activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUDPcastInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UDPcast.
- **Código 400 Bad Request:** No se han encontrado transmisiones UDPcast activas.
- **Código 200 OK:** La información de las transmisiones UDPcast activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UDPcast activas en formato JSON.
```json
{
"6720": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"6721": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Ver Estado de Transmisiones UFTP
Se devolverá el pid de los procesos de transferencias UFTP activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUFTPInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UFTP.
- **Código 400 Bad Request:** No se han encontrado transmisiones UFTP activas.
- **Código 200 OK:** La información de las transmisiones UFTP activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UFTP activas en formato JSON.
```json
{
"3427": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"4966": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Cancelar Transmisión UDPcast
Se cancelará la transmisión por UDPcast activa de la imagen especificada como parámetro, deteniendo el proceso "udp-sender" asociado a dicha imagen.
Se puede hacer con el script "**stopUDPcast.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/udpcast/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UDPcast.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UDPcast activas para la imagen especificada.
- **Código 200 OK:** La transmisión UDPcast se ha cancelado exitosamente.
---
### Cancelar Transmisión UFTP
Se cancelará la transmisión por UFTP activa de la imagen especificada como parámetro, deteniendo el proceso "uftp" asociado a dicha imagen.
Se puede hacer con el script "**stopUFTP.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/uftp/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UFTP.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UFTP activas para la imagen especificada.
- **Código 200 OK:** La transmisión UFTP se ha cancelado exitosamente.
---
### Cancelar Transmisiones P2P
Se cancelarán las transmisiones P2P activas en el ogRepository al que se envíe la orden, deteniendo los procesos "bttrack" y "btlaunchmany.bittornado".
Se puede hacer con el script "**stopP2P.py**", que debe ser llamado por el endpoint.
**NOTA**: No he encontrado la forma de detener la transmisión de una imagen concreta, ya que "bttrack" y "btlaunchmany.bittornado" hacen tracking y seed (respectivamente) de todos los torrents existentes en la raíz del directorio especificado.
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar las transmisiones P2P.
- **Código 200 OK:** Las transmisiones P2P se han cancelado exitosamente.
---

View File

@ -16,19 +16,20 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
6. [Eliminar una Imagen](#eliminar-una-imagen) - `DELETE /ogrepository/v1/images/{ID_img}?method={method}`
7. [Recuperar una Imagen](#recuperar-una-imagen) - `POST /ogrepository/v1/trash/images`
8. [Eliminar una Imagen de la Papelera](#eliminar-una-imagen-de-la-papelera) - `DELETE /ogrepository/v1/trash/images/{ID_img}`
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. [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`
9. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/udpcast`
10. [Ver Estado de Transmisiones UDPcast](#ver-estado-de-transmisiones-udpcast) - `GET /ogrepository/v1/udpcast`
11. [Cancelar Transmisión UDPcast](#cancelar-transmisión-udpcast) - `DELETE /ogrepository/v1/udpcast/images/{ID_img}`
12. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/uftp`
13. [Ver Estado de Transmisiones UFTP](#ver-estado-de-transmisiones-uftp) - `GET /ogrepository/v1/uftp`
14. [Cancelar Transmisión UFTP](#cancelar-transmisión-uftp) - `DELETE /ogrepository/v1/uftp/images/{ID_img}`
15. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/p2p`
16. [Cancelar Transmisiones P2P](#cancelar-transmisiones-p2p) - `DELETE /ogrepository/v1/p2p`
17. [Transferir una Imagen entre Repositorios](#transferir-una-imagen-entre-repositorios) - `POST /ogrepository/v1/repo/images`
18. [Hacer Backup de una Imagen](#hacer-backup-de-una-imagen) - `PUT /ogrepository/v1/repo/images`
19. [Convertir Imagen Virtual a Imagen OpenGnsys](#convertir-imagen-virtual-a-imagen-opengnsys) - `POST /ogrepository/v1/images/virtual`
20. [Convertir Imagen OpenGnsys a Imagen Virtual](#convertir-imagen-opengnsys-a-imagen-virtual) - `PUT /ogrepository/v1/images/virtual`
21. [Crear archivos auxiliares](#crear-archivos-auxiliares) - `POST /ogrepository/v1/images/torrentsum`
22. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
---
### Obtener Información de Estado de ogRepository
@ -321,6 +322,210 @@ curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/t
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se eliminó exitosamente.
---
### Enviar una Imagen mediante UDPcast
Se enviará la imagen especificada por Multicast, mediante la aplicación UDPcast.
Se puede hacer con el script "**sendFileMcast.py**", que a su vez llama al binario "**udp-sender**", que es quien realmente realiza el envío.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **method**: Modalidad half-duplex o full-duplex ("half" o "full").
- **ip**: IP Multicast.
- **bitrate**: Velocidad de transmisión (en Mbps).
- **nclients**: Número mínimo de clientes.
- **maxtime**: Tiempo máximo de espera.
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "method":"full", "ip":"239.194.17.2", "bitrate":"70M", "nclients":"20", "maxtime":"120"}' http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UDPcast.
---
### Ver Estado de Transmisiones UDPcast
Se devolverá el pid de los procesos de transferencias UDPcast activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUDPcastInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UDPcast.
- **Código 400 Bad Request:** No se han encontrado transmisiones UDPcast activas.
- **Código 200 OK:** La información de las transmisiones UDPcast activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UDPcast activas en formato JSON.
```json
{
"6720": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"6721": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Cancelar Transmisión UDPcast
Se cancelará la transmisión por UDPcast activa de la imagen especificada como parámetro, deteniendo el proceso "udp-sender" asociado a dicha imagen.
Se puede hacer con el script "**stopUDPcast.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/udpcast/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UDPcast.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UDPcast activas para la imagen especificada.
- **Código 200 OK:** La transmisión UDPcast se ha cancelado exitosamente.
---
### Enviar una Imagen mediante UFTP
Se enviará la imagen especificada por Unicast o Multicast, mediante el protocolo "UFTP".
Se puede hacer con el script "**sendFileUFTP.py**", que requiere que previamente los clientes ogLive destino se pongan en escucha con un daemon "UFTPD" (ejecutando el script "**listenUFTPD.py**"). Esto funciona al revés que "UDPcast", ya que primero se debe ejecutar un comando en los clientes, y luego en el servidor.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **ip**: IP Unicast/Multicast.
- **bitrate**: Velocidad de transmisión (con "K" para Kbps, "M" para Mbps o "G" para Gbps).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "ip":"239.194.17.2", "bitrate":"1G"}' http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UFTP.
---
### Ver Estado de Transmisiones UFTP
Se devolverá el pid de los procesos de transferencias UFTP activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUFTPInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UFTP.
- **Código 400 Bad Request:** No se han encontrado transmisiones UFTP activas.
- **Código 200 OK:** La información de las transmisiones UFTP activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UFTP activas en formato JSON.
```json
{
"3427": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"4966": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Cancelar Transmisión UFTP
Se cancelará la transmisión por UFTP activa de la imagen especificada como parámetro, deteniendo el proceso "uftp" asociado a dicha imagen.
Se puede hacer con el script "**stopUFTP.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/uftp/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UFTP.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UFTP activas para la imagen especificada.
- **Código 200 OK:** La transmisión UFTP se ha cancelado exitosamente.
---
### Enviar una Imagen mediante P2P
Se enviará la imagen especificada mediante "P2P", iniciando el tracker y el seeder (que harán tracking y seed de los torrents contenidos en el directorio de imágenes).
Se puede hacer con los scripts "**runTorrentTracker.py**" y "**runTorrentSeeder.py**", que deben ser llamados por el endpoint.
**NOTA**: Estos scripts no reciben parámetros, pero es necesario que el endpoint compruebe si la imagen a enviar existe antes de llamarlos, por lo que se le debe enviar el ID de la imagen (que corresponde al contenido del archivo "full.sum") 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 se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d"}' http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al intentar enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante P2P.
---
### Cancelar Transmisiones P2P
Se cancelarán las transmisiones P2P activas en el ogRepository al que se envíe la orden, deteniendo los procesos "bttrack" y "btlaunchmany.bittornado".
Se puede hacer con el script "**stopP2P.py**", que debe ser llamado por el endpoint.
**NOTA**: No he encontrado la forma de detener la transmisión de una imagen concreta, ya que "bttrack" y "btlaunchmany.bittornado" hacen tracking y seed (respectivamente) de todos los torrents existentes en la raíz del directorio especificado.
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar las transmisiones P2P.
- **Código 200 OK:** Las transmisiones P2P se han cancelado exitosamente.
---
### Transferir una Imagen entre Repositorios
@ -377,31 +582,7 @@ curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 200 OK:** Se está haciendo backup de la imagen.
---
### Crear archivos auxiliares
Se crearán los archivos ".sum", ".full.sum", ".size" y ".torrent", para la imagen especificada como parámetro.
Se puede hacer con el script "**createTorrentSum.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este parámetro no puede obtenerse en la API, a partir del ID de imagen (como en otros casos), porque el ID corresponde al contenido del archivo "full.sum" asociado (que no estará creado hasta que no se ejecute este script).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar cierto tiempo, por lo que solo informa de que los archivos auxiliares se están creando, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
**URL:** `/ogrepository/v1/images/torrentsum`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **image**: Nombre de la imagen (con extensión).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img"}' http://example.com/ogrepository/v1/images/torrentsum
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al crear los archivos auxiliares.
- **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
### Convertir Imagen Virtual a Imagen OpenGnsys
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.
@ -425,6 +606,55 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 400 Bad Request:** No se ha encontrado la imagen virtual especificada.
- **Código 200 OK:** La imagen virtual se está convirtiendo.
---
### Convertir Imagen OpenGnsys a Imagen Virtual
Se convertirá la imagen "img" especificada en una imagen virtual con la extensión especificada ("vdi", "vmdk", etc), guardándola en la ruta "opt/opengnsys/ogrepository/images_virtual/export".
Se puede hacer con el script "**convertIMGtoVM.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y la extensión del disco virtual destino ("vdi", "vmdk", etc) como segundo parámetro. El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero la extensión del disco virtual debe enviarse en el JSON.
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está convirtiendo a virtual, 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:** PUT
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **vm_extension**: Extensión del disco virtual destino ("vdi", "vmdk", etc).
**Ejemplo de Solicitud:**
```bash
curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "vm_extension":"vdi"}' http://example.com/ogrepository/v1/images/virtual
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al convertir la imagen a virtual.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está convirtiendo a virtual.
---
### Crear archivos auxiliares
Se crearán los archivos ".sum", ".full.sum", ".size" y ".torrent", para la imagen especificada como parámetro.
Se puede hacer con el script "**createTorrentSum.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este parámetro no puede obtenerse en la API, a partir del ID de imagen (como en otros casos), porque el ID corresponde al contenido del archivo "full.sum" asociado (que no estará creado hasta que no se ejecute este script).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar cierto tiempo, por lo que solo informa de que los archivos auxiliares se están creando, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
**URL:** `/ogrepository/v1/images/torrentsum`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **image**: Nombre de la imagen (con extensión).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img"}' http://example.com/ogrepository/v1/images/torrentsum
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al crear los archivos auxiliares.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** Los archivos auxiliares se están creando.
---
### Enviar paquete Wake On Lan
@ -448,209 +678,4 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 500 Internal Server Error:** Ocurrió un error al enviar el paquete Wake On Lan.
- **Código 200 OK:** El paquete Wake On Lan se ha enviado exitosamente.
---
### Enviar una Imagen mediante UDPcast
Se enviará la imagen especificada por Multicast, mediante la aplicación UDPcast.
Se puede hacer con el script "**sendFileMcast.py**", que a su vez llama al binario "**udp-sender**", que es quien realmente realiza el envío.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **method**: Modalidad half-duplex o full-duplex ("half" o "full").
- **ip**: IP Multicast.
- **bitrate**: Velocidad de transmisión (en Mbps).
- **nclients**: Número mínimo de clientes.
- **maxtime**: Tiempo máximo de espera.
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "method":"full", "ip":"239.194.17.2", "bitrate":"70M", "nclients":"20", "maxtime":"120"}' http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UDPcast.
---
### Enviar una Imagen mediante UFTP
Se enviará la imagen especificada por Unicast o Multicast, mediante el protocolo "UFTP".
Se puede hacer con el script "**sendFileUFTP.py**", que requiere que previamente los clientes ogLive destino se pongan en escucha con un daemon "UFTPD" (ejecutando el script "**listenUFTPD.py**"). Esto funciona al revés que "UDPcast", ya que primero se debe ejecutar un comando en los clientes, y luego en el servidor.
**NOTA**: El script requiere que se le pase el nombre de la imagen (con extensión) como primer parámetro, y los datos de transferencia como segundo parámetro (en una cadena, con los datos separados por dos puntos). El primer parámetro se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum"), pero los datos de transferencia deben enviarse desde ogCore (y luego son tratados en la API, para construir la cadena correspondiente al parámetro).
**NOTA2**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **port**: Puerto Multicast.
- **ip**: IP Unicast/Multicast.
- **bitrate**: Velocidad de transmisión (con "K" para Kbps, "M" para Mbps o "G" para Gbps).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "port":"9000", "ip":"239.194.17.2", "bitrate":"1G"}' http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante UFTP.
---
### Enviar una Imagen mediante P2P
Se enviará la imagen especificada mediante "P2P", iniciando el tracker y el seeder (que harán tracking y seed de los torrents contenidos en el directorio de imágenes).
Se puede hacer con los scripts "**runTorrentTracker.py**" y "**runTorrentSeeder.py**", que deben ser llamados por el endpoint.
**NOTA**: Estos scripts no reciben parámetros, pero es necesario que el endpoint compruebe si la imagen a enviar existe antes de llamarlos, por lo que se le debe enviar el ID de la imagen (que corresponde al contenido del archivo "full.sum") 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 se está enviando, y abre un proceso paralelo (pero no avisa a ogCore de su finalización, porque no puede comprobar cuando acaba la tarea de restauración de la imagen).
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d"}' http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al intentar enviar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está enviando mediante P2P.
---
### Ver Estado de Transmisiones UDPcast
Se devolverá el pid de los procesos de transferencias UDPcast activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUDPcastInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/udpcast`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UDPcast.
- **Código 400 Bad Request:** No se han encontrado transmisiones UDPcast activas.
- **Código 200 OK:** La información de las transmisiones UDPcast activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UDPcast activas en formato JSON.
```json
{
"6720": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"6721": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Ver Estado de Transmisiones UFTP
Se devolverá el pid de los procesos de transferencias UFTP activas, y sus imágenes asociadas (con nombre e ID), en formato JSON, o un mensaje informativo si no hay procesos activos, o si se produce un error.
Se puede hacer con el script "**getUFTPInfo.py**", que debe ser llamado por el endpoint.
**URL:** `/ogrepository/v1/uftp`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
```bash
curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al comprobar las transmisiones UFTP.
- **Código 400 Bad Request:** No se han encontrado transmisiones UFTP activas.
- **Código 200 OK:** La información de las transmisiones UFTP activas se obtuvo exitosamente.
- **Contenido:** Información de las transmisiones UFTP activas en formato JSON.
```json
{
"3427": {
"image_id": "22735b9070e4a8043371b8c6ae52b90d",
"image_name": "Ubuntu20.img"
},
"4966": {
"image_id": "9e7cd32c606ebe5bd39ba212ce7aeb02",
"image_name": "Windows10.img"
}
}
```
---
### Cancelar Transmisión UDPcast
Se cancelará la transmisión por UDPcast activa de la imagen especificada como parámetro, deteniendo el proceso "udp-sender" asociado a dicha imagen.
Se puede hacer con el script "**stopUDPcast.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/udpcast/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/udpcast/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UDPcast.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UDPcast activas para la imagen especificada.
- **Código 200 OK:** La transmisión UDPcast se ha cancelado exitosamente.
---
### Cancelar Transmisión UFTP
Se cancelará la transmisión por UFTP activa de la imagen especificada como parámetro, deteniendo el proceso "uftp" asociado a dicha imagen.
Se puede hacer con el script "**stopUFTP.py**", que debe ser llamado por el endpoint.
**NOTA**: La versión actual de este script requiere que se le pase el nombre de la imagen (con extensión) como parámetro. Este dato se obtiene en la API, a partir del ID de la imagen (que corresponde al contenido del archivo "full.sum").
**URL:** `/ogrepository/v1/uftp/images/{ID_img}`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/uftp/images/22735b9070e4a8043371b8c6ae52b90d
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar la transmisión UFTP.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 400 Bad Request:** No hay transmisiones UFTP activas para la imagen especificada.
- **Código 200 OK:** La transmisión UFTP se ha cancelado exitosamente.
---
### Cancelar Transmisiones P2P
Se cancelarán las transmisiones P2P activas en el ogRepository al que se envíe la orden, deteniendo los procesos "bttrack" y "btlaunchmany.bittornado".
Se puede hacer con el script "**stopP2P.py**", que debe ser llamado por el endpoint.
**NOTA**: No he encontrado la forma de detener la transmisión de una imagen concreta, ya que "bttrack" y "btlaunchmany.bittornado" hacen tracking y seed (respectivamente) de todos los torrents existentes en la raíz del directorio especificado.
**URL:** `/ogrepository/v1/p2p`
**Método HTTP:** DELETE
**Ejemplo de Solicitud:**
```bash
curl -X DELETE -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/p2p
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al cancelar las transmisiones P2P.
- **Código 200 OK:** Las transmisiones P2P se han cancelado exitosamente.
---

View File

@ -460,6 +460,7 @@ def check_virtual_image_conversion(image_name, job_id):
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).
Lo utiliza el endpoint "Convertir imagen virtual a imagen OpenGnsys".
"""
journal.send("Running function 'check_virtual_image_conversion'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -511,6 +512,63 @@ def check_virtual_image_conversion(image_name, job_id):
# ---------------------------------------------------------
def check_virtual_image_reconversion(image_name, vm_extension, job_id):
""" Cada minuto comprueba si existe la imagen virtual exportada y si ya no existe ninguno de los archivos que se crean en el proceso (o si ya no existen estos archivos ni la imagen exportada).
En el primer caso, le comunicará a ogCore que la conversión ha sido exitosa y saldrá de bucle, y en el segundo caso, 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).
Lo utiliza el endpoint "Convertir imagen OpenGnsys a imagen virtual".
"""
journal.send("Running function 'check_virtual_image_reconversion'...", 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 virtual (una vez exportada), y la ruta de exportación:
virtual_image_file_path = f"{vm_path}export/{image_name}.{vm_extension}"
virtual_export_path = f"{vm_path}export/"
# Creamos un bucle infinito:
while True:
# Si ya no existen los archivos que se crean durante la conversión, pero si la imagen virtual exportada, respondemos a ogCore (con "success: True") y salimos del bucle:
if not os.path.exists(f"{virtual_export_path}disk.raw") and not os.path.exists(f"{virtual_export_path}{image_name}.img") and os.path.exists(virtual_image_file_path):
journal.send("Task finalized (Image converted to virtual)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'INFO', 'operation':'Run function check_virtual_image_reconversion', 'desc':'Image converted to virtual'}", 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 existen los archivos que se crean durante la conversión, pero tampoco la imagen virtual exportada, respondemos a ogCore (con "success: False") y salimos del bucle:
elif not os.path.exists(f"{virtual_export_path}disk.raw") and not os.path.exists(f"{virtual_export_path}{image_name}.img") and not os.path.exists(virtual_image_file_path):
journal.send("Image conversion to virtual failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'ERROR', 'operation':'Run function check_virtual_image_reconversion', 'desc':'Image conversion to virtual 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,
@ -577,19 +635,19 @@ def remove_remote_files(sftp_client, remote_path, image_name, extensions):
# ---------------------------------------------------------
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"
def check_free_space(image_name_full, path):
""" Comprueba si hay suficiente espacio en disco para convertir la imagen que recibe como parámetro
(4 veces el tamaño del archivo), devolviendo "True" si lo hay, y "False" si no lo hay.
Lo utilizan los endpoints "Convertir imagen virtual a imagen OpenGnsys" y "Convertir imagen OpenGnsys a imagen virtual".
"""
# Obtenemos el tamaño de la imagen virtual:
vm_size = int(os.path.getsize(f"{vm_path}{vm_image_name_full}"))
# Obtenemos el tamaño de la imagen:
img_size = int(os.path.getsize(f"{path}{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):
# Si no hay suficiente espacio libre en disco (4 veces el tamaño de la imagen), devolvemos "False", y en caso contrario "True":
if free_space < (img_size * 4):
return False
else:
return True
@ -692,7 +750,7 @@ def get_repo_image_info(imageId):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(imageId)
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/getRepoInfo.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
@ -788,7 +846,7 @@ def check_image(imageId):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(imageId, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/checkImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
@ -851,7 +909,7 @@ def delete_image(imageId):
# Almacenams el método de eliminación ("trash" o "permanent")
method = request.values.get('method')
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
if method == "permanent":
cmd = ['python3', f"{script_path}/deleteImage.py", f"{param_dict['name']}.{param_dict['extension']}", '-p']
@ -920,7 +978,7 @@ def recover_image():
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(image_id, "trash")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/recoverImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
@ -974,7 +1032,7 @@ def delete_trash_image(imageId):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(imageId, "trash")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/deleteTrashImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
@ -1139,7 +1197,7 @@ def backup_image():
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la ruta de la imagen, o para devover un error si no se ha encontrado la imagen (o si está bloqueada):
# Evaluamos los parámetros obtenidos, para construir la ruta de la imagen, o para devolver un error si no se ha encontrado la imagen (o si está bloqueada):
if param_dict:
image_name = f"{param_dict['name']}.{param_dict['extension']}"
@ -1362,10 +1420,10 @@ def send_udpcast():
nclients = json_data.get("nclients")
maxtime = json_data.get("maxtime")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/sendFileMcast.py", f"{param_dict['name']}.{param_dict['extension']}", f"{port}:{method}:{ip}:{bitrate}:{nclients}:{maxtime}"]
else:
@ -1435,10 +1493,10 @@ def send_uftp():
ip = json_data.get("ip")
bitrate = json_data.get("bitrate")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/sendFileUFTP.py", f"{param_dict['name']}.{param_dict['extension']}", f"{port}:{ip}:{bitrate}"]
else:
@ -1504,10 +1562,10 @@ def send_p2p():
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir las llamadas a los scripts, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir las llamadas a los scripts, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd_tracker = ['sudo', 'python3', f"{script_path}/runTorrentTracker.py"] # Este script si que requiere ser ejecutado con "sudo"
cmd_seeder = ['sudo', 'python3', f"{script_path}/runTorrentSeeder.py"] # Este script si que requiere ser ejecutado con "sudo"
@ -1664,7 +1722,7 @@ def stop_udpcast(imageId):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(imageId, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/stopUDPcast.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
@ -1737,10 +1795,10 @@ def stop_uftp(imageId):
"""
journal.send("Running endpoint 'Cancelar Transmisión UFTP'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(imageId, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
cmd = ['python3', f"{script_path}/stopUFTP.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
@ -1845,15 +1903,15 @@ def stop_p2p():
# ---------------------------------------------------------
# 21 - Endpoint "Convertir imagen virtual" (ASINCRONO):
# 21 - Endpoint "Convertir imagen virtual a imagen OpenGnsys" (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
""" Este endpoint convierte la imagen virtual especificada como 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")
journal.send("Running endpoint 'Convertir imagen virtual a imagen OpenGnsys'...", 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)
@ -1886,8 +1944,8 @@ def convert_virtual_image():
"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)
# Comprobamos si hay espacio suficiente en disco para convertir la imagen virtual (4 veces su tamaño):
enough_free_space = check_free_space(vm_image_name_full, vm_path)
# Si no hay suficiente espacio libre en disco, devolvemos un error:
if enough_free_space == False:
@ -1907,7 +1965,7 @@ def convert_virtual_image():
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))}"
job_id = f"ConvertImageFromVirtual_{''.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:
@ -1949,6 +2007,111 @@ def convert_virtual_image():
}), 500
# ---------------------------------------------------------
# 22 - Endpoint "Convertir imagen OpenGnsys a imagen virtual" (ASINCRONO):
@app.route("/ogrepository/v1/images/virtual", methods=['PUT'])
def convert_image_to_virtual():
""" Este endpoint convierte la imagen de OpenGnsys especificada como parámetro en una imagen virtual con la extensión especificada (".vdi", ".vmdk", etc).
Para ello, ejecuta el script "convertIMGtoVM.py", con el nombre de la imagen "img" como primer parámetro,
y la extensión del disco virtual destino (sin punto) como segundo parámetro.
"""
journal.send("Running endpoint 'Convertir imagen OpenGnsys a imagen virtual'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Almacenamos los parámetros enviados en el JSON,:
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
vm_extension = json_data.get("vm_extension").lower().lstrip('.')
# Obtenemos el nombre y la extensión de la imagen:
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devolver un error si no se ha encontrado la imagen:
if param_dict:
image_name_full = f"{param_dict['name']}.{param_dict['extension']}"
image_name = f"{param_dict['name']}"
cmd = ['python3', f"{script_path}/convertIMGtoVM.py", image_name_full, vm_extension]
else:
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 endpoint convert_image_to_virtual', 'desc':'Warning: Image not found'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
return jsonify({
"success": False,
"error": "Image not found"
}), 400
# Comprobamos si ya existe una imagen virtual exportada con el mismo nombre que la imagen "img" y la extensión especificada, llamando a la función "check_file_exists":
vm_image_exists = check_file_exists(f"{vm_path}export/{image_name}.{vm_extension}")
# Si existe una imagen con el mismo nombre que la imagen virtual (salvo por la extensión), devolvemos un error:
if vm_image_exists == True:
journal.send("There is an exported virtual image with the same name as the image", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'WARNING', 'http_code':'400', 'operation':'Run endpoint convert_image_to_virtual', 'desc':'Warning: There is an exported virtual image with the same name as the image'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
return jsonify({
"success": False,
"exception": "There is an exported virtual image with the same name as the image"
}), 400
# Comprobamos si hay espacio suficiente en disco para convertir la imagen "img" a virtual (4 veces su tamaño):
enough_free_space = check_free_space(image_name_full, repo_path)
# 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_image_to_virtual', '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
try:
journal.send("Running script 'convertIMGtoVM.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Ejecutamos el script "convertIMGtoVM.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"ConvertImageToVirtual_{''.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 'convertIMGtoVM.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 convertIMGtoVM.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_reconversion" en un hilo paralelo
# (para que compruebe si la imagen se ha acabado de convertir exitosamente):
journal.send("Calling function 'check_virtual_image_reconversion'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
threading.Thread(target=check_virtual_image_reconversion, args=(image_name, vm_extension, job_id,)).start()
# Informamos que la imagen se está convirtiendo, y salimos del endpoint:
return jsonify({
"success": True,
"output": "Converting image to virtual...",
"job_id": job_id
}), 200
else:
journal.send("Script 'convertIMGtoVM.py' result KO (Image conversion to virtual failed)", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script convertIMGtoVM.py', 'desc':'Result KO (Error: Image conversion to virtual failed)'}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
return jsonify({
"success": False,
"error": "Image conversion to virtual failed"
}), 500
except subprocess.CalledProcessError as error:
journal.send(f"Script 'convertIMGtoVM.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 convertIMGtoVM.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 'convertIMGtoVM.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 convertIMGtoVM.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
# --------------------------------------------------------------------------------------------

View File

@ -16,6 +16,7 @@ tags:
- name: "Transferencia de Imágenes (UFTP)"
- name: "Transferencia de Imágenes (P2P)"
- name: "Transferencia entre Repositorios y Backup"
- name: "Importar y Exportar Máquinas Virtuales"
- name: "Varios"
@ -1522,6 +1523,224 @@ paths:
type: string
example: "(Exception description)"
# -----------------------------------------------------------------------------------------------------------
# Apartado "Importar y Exportar a Máquinas Virtuales"
# -----------------------------------------------------------------------------------------------------------
/ogrepository/v1/images/virtual:
post:
summary: "Convertir Imagen Virtual a Imagen OpenGnsys"
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:
- "Importar y Exportar Máquinas Virtuales"
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)"
# -----------------------------------------------------------------------------------------------------------
#/ogrepository/v1/images/virtual:
put:
summary: "Convertir Imagen OpenGnsys a Imagen Virtual"
description: |
Este endpoint convierte la imagen "img" especificada como primer parámetro en una imagen virtual con la extensión especificada como segundo parámetro ("vdi", "vmdk", etc), guardándola en la ruta "opt/opengnsys/ogrepository/images_virtual/export".
Utiliza el script "**convertIMGtoVM.py**", que recibe como parámetros el nombre de la imagen, y la extensión del disco virtual destino ("vdi", "vmdk", etc).
**NOTA**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está convirtiendo a virtual, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
tags:
- "Importar y Exportar Máquinas Virtuales"
parameters:
- name: JSON
in: body
required: true
description: |
* **ID_img** - Identificador de la imagen, correspondiente al contenido del archivo 'full.sum'
* **vm_extension** - Extensión del disco virtual destino ("vdi", "vmdk", etc)
schema:
type: object
properties:
ID_img:
type: string
example: "22735b9070e4a8043371b8c6ae52b90d"
vm_extension:
type: string
example: "vdi"
responses:
"200":
description: "La imagen se está convirtiendo a virtual."
schema:
type: object
properties:
success:
type: boolean
example: true
output:
type: string
example: "Converting image to virtual..."
"400 (Image not found)":
description: "No se ha encontrado la imagen."
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "Image not found"
"400 (Name incorrect)":
description: "Ya existe una imagen virtual exportada con el mismo nombre que la imagen."
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "There is an exported virtual image with the same name as the 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 a virtual."
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
example: "Image conversion to virtual failed"
"500 (Error)":
description: "Error al convertir la imagen a 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 a virtual."
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
# -----------------------------------------------------------------------------------------------------------
# Apartado "Varios"
# -----------------------------------------------------------------------------------------------------------
@ -1657,112 +1876,4 @@ 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)"
# -----------------------------------------------------------------------------------------------------------

View File

@ -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, check_virtual_image_conversion, check_free_space
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, check_virtual_image_reconversion
# --------------------------------------------------------------------------------------------
@ -591,15 +591,15 @@ class RepoApiTestCase(unittest.TestCase):
self.assertIn('Image not found', json.loads(response.data)['exception'])
# ------------------------------------------------------------------- Tests "Convertir imagen virtual"
# ------------------------------------------------------------------- Tests "Convertir imagen virtual a imagen OpenGnsys"
@patch('repo_api.subprocess.Popen')
@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".
""" Método de prueba del endpoint "Convertir imagen virtual a imagen OpenGnsys".
"""
print("Testing endpoint 'Convertir imagen virtual'...")
print("Testing endpoint 'Convertir imagen virtual a imagen OpenGnsys'...")
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')
@ -611,10 +611,10 @@ class RepoApiTestCase(unittest.TestCase):
@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"
""" Método de prueba del endpoint "Convertir imagen virtual a imagen OpenGnsys"
(en caso de error "500").
"""
print("Testing endpoint 'Convertir imagen virtual' (error 500)...")
print("Testing endpoint 'Convertir imagen virtual a imagen OpenGnsys' (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)
@ -625,10 +625,10 @@ class RepoApiTestCase(unittest.TestCase):
@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"
""" Método de prueba del endpoint "Convertir imagen virtual a imagen OpenGnsys"
(en caso de error "400", por imagen virtual inexistente).
"""
print("Testing endpoint 'Convertir imagen virtual' (error 400, no virtual image)...")
print("Testing endpoint 'Convertir imagen virtual a imagen OpenGnsys' (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)
@ -637,10 +637,10 @@ class RepoApiTestCase(unittest.TestCase):
@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"
""" Método de prueba del endpoint "Convertir imagen virtual a imagen OpenGnsys"
(en caso de error "400", por nombre incorrecto).
"""
print("Testing endpoint 'Convertir imagen virtual' (error 400, name incorrect)...")
print("Testing endpoint 'Convertir imagen virtual a imagen OpenGnsys' (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)
@ -649,16 +649,90 @@ class RepoApiTestCase(unittest.TestCase):
@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"
""" Método de prueba del endpoint "Convertir imagen virtual a imagen OpenGnsys"
(en caso de error "400", por falta de espacio en disco).
"""
print("Testing endpoint 'Convertir imagen virtual' (error 400, not enough space)...")
print("Testing endpoint 'Convertir imagen virtual a imagen OpenGnsys' (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 "Convertir imagen OpenGnsys a imagen virtual"
@patch('repo_api.subprocess.Popen')
@patch('repo_api.get_image_params')
@patch('repo_api.check_virtual_image_reconversion')
def test_convert_image_to_virtual(self, mock_popen, mock_get_image_params, mock_check_virtual_image_reconversion):
""" Método de prueba del endpoint "Convertir imagen OpenGnsys a imagen virtual".
"""
print("Testing endpoint 'Convertir imagen OpenGnsys a imagen virtual'...")
mock_get_image_params.return_value = {'name': 'test4unittest', 'extension': 'img'}
mock_popen.return_value = MagicMock(returncode=None)
mock_check_virtual_image_reconversion.return_value = AsyncMock(returncode=None)
response = self.app.put('/ogrepository/v1/images/virtual', data=json.dumps({"ID_img": "test_image_id", "vm_extension":"vdi"}), content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertIn('Converting image to virtual...', json.loads(response.data)['output'])
@patch('repo_api.check_virtual_image_reconversion')
@patch('repo_api.get_image_params')
@patch('repo_api.subprocess.Popen')
def test_convert_image_to_virtual_error500(self, mock_popen, mock_get_image_params, mock_check_virtual_image_reconversion):
""" Método de prueba del endpoint "Convertir imagen OpenGnsys a imagen virtual"
(en caso de error "500").
"""
print("Testing endpoint 'Convertir imagen OpenGnsys a imagen virtual' (error 500)...")
mock_get_image_params.return_value = {'name': 'test4unittest', 'extension': 'img'}
mock_popen.side_effect = Exception("Error al convertir la imagen a virtual")
mock_check_virtual_image_reconversion.return_value = AsyncMock(returncode=None)
response = self.app.put('/ogrepository/v1/images/virtual', data=json.dumps({"ID_img": "test_image_id", "vm_extension":"vdi"}), content_type='application/json')
self.assertEqual(response.status_code, 500)
self.assertIn('Error al convertir la imagen a virtual', response.data.decode())
@patch('repo_api.get_image_params')
def test_convert_image_to_virtual_error400_no_image(self, mock_get_image_params):
""" Método de prueba del endpoint "Convertir imagen OpenGnsys a imagen virtual"
(en caso de error "400", por imagen virtual inexistente).
"""
print("Testing endpoint 'Convertir imagen OpenGnsys a imagen virtual' (error 400, no image)...")
mock_get_image_params.return_value = None
response = self.app.put('/ogrepository/v1/images/virtual', data=json.dumps({"ID_img": "test_image_id", "vm_extension":"vdi"}), content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertIn("Image not found", json.loads(response.data)['error'])
@patch('repo_api.check_file_exists')
@patch('repo_api.get_image_params')
def test_convert_image_to_virtual_error400_name_incorrect(self, mock_get_image_params, mock_check_file_exists):
""" Método de prueba del endpoint "Convertir imagen OpenGnsys a imagen virtual"
(en caso de error "400", por nombre incorrecto).
"""
print("Testing endpoint 'Convertir imagen OpenGnsys a imagen virtual' (error 400, name incorrect)...")
mock_get_image_params.return_value = {'name': 'test4unittest', 'extension': 'img'}
mock_check_file_exists.return_value = True
response = self.app.put('/ogrepository/v1/images/virtual', data=json.dumps({"ID_img": "test_image_id", "vm_extension":"vdi"}), content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertIn('There is an exported virtual image with the same name as the image', json.loads(response.data)['exception'])
@patch('repo_api.check_free_space')
@patch('repo_api.get_image_params')
def test_convert_image_to_virtual_error400_not_enough_space(self, mock_get_image_params, mock_check_free_space):
""" Método de prueba del endpoint "Convertir imagen OpenGnsys a imagen virtual"
(en caso de error "400", por falta de espacio en disco).
"""
print("Testing endpoint 'Convertir imagen OpenGnsys a imagen virtual' (error 400, not enough space)...")
mock_get_image_params.return_value = {'name': 'test4unittest', 'extension': 'img'}
mock_check_free_space.return_value = False
response = self.app.put('/ogrepository/v1/images/virtual', data=json.dumps({"ID_img": "test_image_id", "vm_extension":"vdi"}), 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"

View File

@ -0,0 +1,452 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script convierte la imagen de OpengGnsys especificada como primer parámetro en una imagen virtual con la extensión especificada como segundo parámetro ("vdi", "vmdk", etc).
La imagen virtual se exporta a la ruta "/opt/opengnsys/ogrepository/images_virtual/export/", desde donde puede ser descargada para importarla en VirtualBox, VMware, etc.
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 a convertir (sin ruta).
- Ejemplo1: Ubuntu.img
- Ejemplo2: Windows.img
sys.argv[2] - Extensión del disco virtual destino (sin punto).
- Ejemplo1: vdi
- Ejemplo2: vmdk
Sintaxis
----------
./comvertIMGtoVM.py image_name virtual_extension
Ejemplos
---------
./comvertIMGtoVM.py Ubuntu.img vdi
./comvertIMGtoVM.py Windows.img vmdk
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import sys
import os
import pwd
import grp
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_export_path = '/opt/opengnsys/ogrepository/images_virtual/export/' # No borrar la barra final
partclone_logfile = '/opt/opengnsys/ogrepository/log/partclone.log'
raw_img = '/opt/opengnsys/ogrepository/images_virtual/export/disk.raw'
mount_point = "/mnt/recovery"
# --------------------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------------------
def show_help():
""" Imprime la ayuda, cuando se ejecuta el script con el parámetro "help".
"""
help_text = f"""
Sintaxis: {script_name} image_name virtual_extension
Ejemplo1: {script_name} Ubuntu.img vdi
Ejemplo2: {script_name} Windows.img vmdk
"""
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 create_export_folder():
""" Crea el directorio '/opt/opengnsys/ogrepository/images_virtual/export/', y le asigna propietarios y permisos.
Evidentemente, esta función solo es llamada cuando no existe el directorio.
"""
# Obtenemos el UID del usuario "opengnsys" y el GID del grupo "opengnsys":
uid = pwd.getpwnam('opengnsys').pw_uid
gid = grp.getgrnam('opengnsys').gr_gid
# Creamos el directorio '/opt/opengnsys/ogrepository/images_virtual/export/':
os.mkdir(vm_export_path)
# Asignamos el usuario y el grupo propietarios del directorio:
os.chown(vm_export_path, uid, gid)
# Asignamos permisos "755" al directorio :
os.chmod(vm_export_path, 0o755)
def create_raw_disk(raw_size):
""" Crea un disco "RAW", sobre el que luego se creará una tabla de particiones, mediante "qemu-img create".
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
"""
try:
journal.send("convertIMGtoVM.py: Running command 'qemu-img create'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
result = subprocess.run(['qemu-img', 'create', '-f', 'raw', raw_img, raw_size], 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"convertIMGtoVM.py: 'qemu-img create' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'qemu-img create' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
def create_partition_table(partition_size):
""" Crea una tabla de particiones en el disco "RAW", mediante "fdisk".
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
"""
try:
journal.send("convertIMGtoVM.py: Running command 'fdisk'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Construimos el parámetro "input", que debe codificarse en bytes (con "encode"):
input_data = f"o\nn\np\n1\n\n+{partition_size}\nw".encode()
result = subprocess.run(['fdisk', raw_img], input=input_data, 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"convertIMGtoVM.py: 'fdisk' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'fdisk' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
def associate_loop_device():
""" Asocia el disco "RAW" a un dispositivo loopback, mediante "losetup".
Si se ejecuta correctamente retorna "True", el dispositivo loopback y la partición de destino, y si da error retorna "False", "None" y "None".
"""
try:
journal.send("convertIMGtoVM.py: Running command 'losetup'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
result = subprocess.run(['losetup', '-P', '--find', '--show', raw_img], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Si el resultado de la ejecución es correcto, obtenemos el dispositivo loopback y la partición de destino, y los retornamos:
if result.returncode == 0:
# Obtenemos la partición de destino, y la imprimimos en el log, junto con el dispositivo loopback:
loop_device = result.stdout.strip()
dest_partition = f"{loop_device}p1"
journal.send(f"convertIMGtoVM.py: Loopback device obtained: {loop_device}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send(f"convertIMGtoVM.py: Destination partition obtained: {dest_partition}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return True, loop_device, dest_partition
else:
return False, None, None
# Si se produce un error o una excepción lo imprimimos en el log, y retornamos "False", "None" y "None" (entrará aquí si el return code no es "0"):
except subprocess.CalledProcessError as error:
journal.send(f"convertIMGtoVM.py: 'losetup' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False, None, None
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'losetup' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False, None, None
def decompress_image(image_name_full):
""" Descomprime la imagen "IMG" origen, con "lzop".
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
"""
try:
journal.send("convertIMGtoVM.py: Running command 'lzop'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
result = subprocess.run(['lzop', '-d', f"{repo_path}{image_name_full}", '-o', f"{vm_export_path}{image_name_full}"], 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"convertIMGtoVM.py: 'lzop' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'lzop' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
def restore_image(image_name_full, dest_partition):
""" Restaura la imagen descomprimida en la partición de destino (que hemos obtenido con "losetup").
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
"""
try:
journal.send("convertIMGtoVM.py: Running command 'partclone.restore'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
result = subprocess.run(['partclone.restore', '-s', f"{vm_export_path}{image_name_full}", '-o', dest_partition, '-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"convertIMGtoVM.py: 'partclone.restore' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'partclone.restore' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
def mount_partition(dest_partition):
""" Crea un directorio de montaje, y monta allí la partición de destino (que hemos obtenido con "losetup").
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
NOTA: Los comandos "mkdir" y "mount" deben ejecutarse con "sudo", o darán error.
"""
try:
# Creamos el directorio de montaje:
subprocess.run(['sudo', 'mkdir', '-p', mount_point], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'mkdir' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
try:
# Montamos la partición en el directorio de montaje:
journal.send("convertIMGtoVM.py: Running command 'mount'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
result = subprocess.run(['sudo', 'mount', dest_partition, mount_point], 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"convertIMGtoVM.py: 'mount' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'mount' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
def install_grub(loop_device):
""" Instala GRUB en el dispositivo loopback.
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
NOTA: Los comandos deben ejecutarse con "sudo", o darán error.
"""
try:
journal.send("convertIMGtoVM.py: Installing GRUB...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Ejecutamos los comandos necesarios para instalar GRUB:
subprocess.run(['sudo', 'mount', '--bind', '/dev', f"{mount_point}/dev"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'mount', '--bind', '/proc', f"{mount_point}/proc"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'mount', '--bind', '/sys', f"{mount_point}/sys"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'chroot', mount_point, 'grub-install', '--target=i386-pc', loop_device], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'chroot', mount_point, 'grub-mkconfig', '-o', '/boot/grub/grub.cfg'], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Como no ha habido errores, retornamos "True":
return True
# 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"convertIMGtoVM.py: GRUB install error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
except Exception as error:
journal.send(f"convertIMGtoVM.py: GRUB install exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
def umount_partitions(loop_device):
""" Desmonta todas las particiones montadas previamente, con "umount" y "losetup".
No retorna "True" o "False", porque este paso no afecta a la conversión de la imagen.
NOTA: Los comandos deben ejecutarse con "sudo", o darán error.
"""
try:
journal.send("convertIMGtoVM.py: Umounting partitions...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Desmontamos las particiones, con "umount" y "losetup":
subprocess.run(['sudo', 'umount', f"{mount_point}/dev"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'umount', f"{mount_point}/proc"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'umount', f"{mount_point}/sys"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'umount', '-l', mount_point], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(['sudo', 'losetup', '-d', loop_device], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Como no ha habido errores, imprimimos un mensaje en el log:
journal.send("convertIMGtoVM.py: Umount partitions OK", PRIORITY=journal.LOG_INFO, 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"convertIMGtoVM.py: 'umount' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'umount' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
def convert_image(image_name, vm_extension):
""" Convierte el disco "RAW" en un disco virtual, con la extensión especificada como parámetro.
Si se ejecuta correctamente retorna "True", y si da error retorna "False".
"""
try:
journal.send("convertIMGtoVM.py: Running command 'qemu-img convert'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
result = subprocess.run(['qemu-img', 'convert', '-f', 'raw', '-O', vm_extension, raw_img, f"{vm_export_path}{image_name}.{vm_extension}"], 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"convertIMGtoVM.py: 'qemu-img convert' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
except Exception as error:
journal.send(f"convertIMGtoVM.py: 'qemu-img convert' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return False
def erase_image_file(file2del):
""" Borra el archivo correspondiente a la ruta "file2del".
No retorna "True" o "False", porque este paso no afecta a la conversión de la imagen.
"""
journal.send(f"convertIMGtoVM.py: Erasing file {file2del}...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Si existe el archivo "file2del", lo borramos:
if os.path.exists(file2del):
os.remove(file2del)
# --------------------------------------------------------------------------------------------
# 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 la extensión del disco virtual de destino (desde los parámetros), y extraemos el nombre sin extensión:
image_name_full = sys.argv[1]
image_name = image_name_full.split('.')[0]
vm_extension = sys.argv[2].lower()
# Obtenemos el tamaño de la imagen, y calculamos el tamaño a asignar al disco "RAW" y a la partición:
image_size = os.path.getsize(f"{repo_path}{image_name_full}")
raw_size = image_size * 6.5 # El disco "RAW" debe ser "6,5" veces más grande que la imagen
partition_size = (90 * raw_size) / 100 # La partición debe tener el 90% del tamaño del disco "RAW"
# Si no existe el directorio '/opt/opengnsys/ogrepository/images_virtual/export/', lo creamos:
if not os.path.exists(vm_export_path):
journal.send("convertIMGtoVM.py: Creating export folder...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
create_export_folder()
# Creamos el disco "RAW" (con "qemu-img create"):
create_raw = create_raw_disk(f"{raw_size / 1024 / 1024 / 1024}G")
if create_raw == False:
journal.send("convertIMGtoVM.py: RAW disk creation failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
sys.exit(2)
else:
journal.send("convertIMGtoVM.py: RAW disk creation OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Creamos una tabla de particiones en el disco "RAW" (con "fdisk"):
create_table = create_partition_table(f"{int(partition_size / 1024 / 1024 / 1024)}G")
if create_table == False:
journal.send("convertIMGtoVM.py: Partition table creation failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
sys.exit(3)
else:
journal.send("convertIMGtoVM.py: Partition table creation OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Asociamos el disco "RAW" a un dispositivo loopback, y almacenamos este y la partición de destino
assoc_loop_result, loop_device, dest_partition = associate_loop_device()
if assoc_loop_result == False:
journal.send("convertIMGtoVM.py: Loop device association failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
sys.exit(4)
else:
journal.send("convertIMGtoVM.py: Loop device association OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Descomprimimos la imagen original (con "lzop"):
image_decompressed = decompress_image(image_name_full)
if image_decompressed == False:
journal.send("convertIMGtoVM.py: Image decompression failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
erase_image_file(f"{vm_export_path}{image_name_full}") # Como ha fallado, borramos la imagen descomprimida
sys.exit(5)
else:
journal.send("convertIMGtoVM.py: Image decompression OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Restauramos la imagen descomprimida en la partición de destino (con "partclone.restore"):
image_restored = restore_image(image_name_full, dest_partition)
if image_restored == False:
journal.send("convertIMGtoVM.py: Image restoration failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
erase_image_file(f"{vm_export_path}{image_name_full}") # Como ha fallado, borramos la imagen descomprimida
sys.exit(6)
else:
journal.send("convertIMGtoVM.py: Image restoration OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(f"{vm_export_path}{image_name_full}") # Si ha ido OK también borramos la imagen descomprimida, porque ya no la necesitamos.
# Montamos la partición de destino (con "mount"):
partition_mounted = mount_partition(dest_partition)
if partition_mounted == False:
journal.send("convertIMGtoVM.py: Partition mount failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
sys.exit(7)
else:
journal.send("convertIMGtoVM.py: Partition mount OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Instalamos GRUB en el dispositivo loopback (con "mount" y "chroot"):
grub_installed = install_grub(loop_device)
if grub_installed == False:
journal.send("convertIMGtoVM.py: GRUB install failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
umount_partitions(loop_device) # Como ha fallado, desmontamos todas las particiones
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
sys.exit(8)
else:
journal.send("convertIMGtoVM.py: GRUB install OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Desmontamos las particiones montadas previamente (con "umount" y "losetup"):
umount_partitions(loop_device)
# Convertimos el disco "RAW" en un disco virtual (con "qemu-img convert"):
image_conversion = convert_image(image_name, vm_extension)
if image_conversion == False:
journal.send("convertIMGtoVM.py: Image conversion failed", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
erase_image_file(raw_img) # Como ha fallado, borramos el disco "RAW"
erase_image_file(f"{vm_export_path}{image_name}.{vm_extension}") # Como ha fallado, borramos la imagen virtual (por si ha creado algo)
sys.exit(9)
else:
erase_image_file(raw_img) # Si ha funcionado OK, también borramos el disco "RAW"
journal.send("convertIMGtoVM.py: Image conversion OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------