refs #1733 - Add 'Rename Image' functionality

pull/36/head
Gerardo GIl Elizeire 2025-03-25 14:12:03 +01:00
parent 6ba0788bc0
commit 311ecc7f3a
7 changed files with 423 additions and 5 deletions

View File

@ -1,5 +1,13 @@
# Changelog
## [0.8.0] - 2025-03-25
### Added
- Rename Image functionality
## [0.7.3] - 2025-03-19
### Changed
- Fix permissions problem and authorized_keys file in debian package

View File

@ -74,8 +74,9 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
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`
21. [Renombrar una Imagen](#renombrar-una-imagen) - `POST /ogrepository/v1/images/rename`
22. [Crear archivos auxiliares](#crear-archivos-auxiliares) - `POST /ogrepository/v1/images/torrentsum`
23. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
---
### Obtener Información de Estado de ogRepository
@ -677,6 +678,30 @@ curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está convirtiendo a virtual.
---
### Renombrar una Imagen
Se renombrará la imagen especificada como primer parámetro (y todos sus archivos asociados), asignando el nombre especificado como segundo parámetro.
Se puede hacer con el script "**renameImage.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (sin extensión) como primer parámetro, y el nuevo nombre a asignar 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 el nuevo nombre a asignar debe enviarse en el JSON.
**URL:** `/ogrepository/v1/images/rename`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **image_new_name**: Nuevo nombre a asignar a la imagen.
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "image_new_name":"Ubuntu_BKP"}' http://example.com/ogrepository/v1/images/rename
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al renombrar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se ha renombrado exitosamente.
---
### Crear archivos auxiliares

View File

@ -28,8 +28,9 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
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`
21. [Renombrar una Imagen](#renombrar-una-imagen) - `POST /ogrepository/v1/images/rename`
22. [Crear archivos auxiliares](#crear-archivos-auxiliares) - `POST /ogrepository/v1/images/torrentsum`
23. [Enviar paquete Wake On Lan](#enviar-paquete-wake-on-lan) - `POST /ogrepository/v1/wol`
---
### Obtener Información de Estado de ogRepository
@ -631,6 +632,30 @@ curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se está convirtiendo a virtual.
---
### Renombrar una Imagen
Se renombrará la imagen especificada como primer parámetro (y todos sus archivos asociados), asignando el nombre especificado como segundo parámetro.
Se puede hacer con el script "**renameImage.py**", que debe ser llamado por el endpoint.
**NOTA**: El script requiere que se le pase el nombre de la imagen (sin extensión) como primer parámetro, y el nuevo nombre a asignar 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 el nuevo nombre a asignar debe enviarse en el JSON.
**URL:** `/ogrepository/v1/images/rename`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **ID_img**: Identificador de la imagen (correspondiente al contenido del archivo "full.sum" asociado).
- **image_new_name**: Nuevo nombre a asignar a la imagen.
**Ejemplo de Solicitud:**
```bash
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ID_img":"22735b9070e4a8043371b8c6ae52b90d", "image_new_name":"Ubuntu_BKP"}' http://example.com/ogrepository/v1/images/rename
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al renombrar la imagen.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La imagen se ha renombrado exitosamente.
---
### Crear archivos auxiliares

View File

@ -2019,7 +2019,7 @@ def convert_image_to_virtual():
"""
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,:
# 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('.')
@ -2112,6 +2112,78 @@ def convert_image_to_virtual():
}), 500
# ---------------------------------------------------------
# 22 - Endpoint "Renombrar una Imagen" (SINCRONO):
@app.route("/ogrepository/v1/images/rename", methods=['PUT'])
def rename_image():
""" Este endpoint renombra la imagen especificada como primer parámetro (y todos sus archivos asociados), asignando el nombre especificado como segundo parámetro.
Para ello, ejecuta el script "renameImage.py", con el nombre original de la imagen como primer parámetro, y el nuevo nombre a asignar como segundo parámetro.
"""
journal.send("Running endpoint 'Renombrar una Imagen'...", 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")
image_new_name = json_data.get("image_new_name").rstrip('.img')
# 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_original_name = f"{param_dict['name']}"
cmd = ['python3', f"{script_path}/renameImage.py", image_original_name, image_new_name]
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 rename_image', '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 con el mismo nombre que se quiere asignar, llamando a la función "check_file_exists":
image_newname_exists = check_file_exists(f"{repo_path}{image_new_name}.img")
# Si existe una imagen con el mismo nombre que se quiere asignar, devolvemos un error:
if image_newname_exists == True:
journal.send("There is an image with the name to assign", 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 image with the name to assign'}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api")
return jsonify({
"success": False,
"exception": "There is an image with the name to assign"
}), 400
try:
journal.send("Running script 'renameImage.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Ejecutamos el script "renameImage.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos la respuesta:
if result.returncode == 0:
journal.send("Script 'renameImage.py' result OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'INFO', 'http_code':'200', 'operation':'Run script renameImage.py', 'desc':'Result OK (ReturnCode: 0)'}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
return jsonify({
"success": True,
"output": "Image renamed successfully"
}), 200
else:
journal.send(f"Script 'renameImage.py' result KO (Error: {result.stderr})", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send(f"{{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script renameImage.py', 'desc':'Result KO (Error: {result.stderr})'}}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
journal.send(f"Script 'renameImage.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 renameImage.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

@ -1876,4 +1876,87 @@ paths:
type: string
example: "(Exception description)"
# -----------------------------------------------------------------------------------------------------------
/ogrepository/v1/images/rename:
put:
summary: "Renombrar una Imagen"
description: |
Este endpoint renombra la imagen especificada como primer parámetro (y todos sus archivos asociados), asignando el nombre especificado como segundo parámetro.
Utiliza el script "**renameImage.py**", que recibe como parámetros el nombre original y el nuevo nombre a asignar.
tags:
- "Varios"
parameters:
- name: JSON
in: body
required: true
description: |
* **ID_img** - Identificador de la imagen, correspondiente al contenido del archivo 'full.sum'
* **image_new_name** - Nuevo nombre a asignar a la imagen
schema:
type: object
properties:
ID_img:
type: string
example: "22735b9070e4a8043371b8c6ae52b90d"
image_new_name:
type: string
example: "Ubuntu_BKP"
responses:
"200":
description: "La imagen se ha renombrado exitosamente."
schema:
type: object
properties:
success:
type: boolean
example: true
output:
type: string
example: "Image renamed successfully"
"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 con el nombre a asignar."
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "There is an image with the name to assign"
"500 (Error)":
description: "Error al renombrar la imagen."
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
example: "(Error description)"
"500 (Exception)":
description: "Excepción inesperada al renombrar la imagen."
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
# -----------------------------------------------------------------------------------------------------------

View File

@ -1109,6 +1109,62 @@ class RepoApiTestCase(unittest.TestCase):
self.assertIn('Error al cancelar las transmisiones P2P', response.data.decode())
# ------------------------------------------------------------------- Tests "Renombrar una Imagen"
@patch('repo_api.get_image_params')
@patch('repo_api.subprocess.run')
def test_rename_image(self, mock_subprocess, mock_get_image_params):
""" Método de prueba del endpoint "Renombrar una Imagen".
"""
print("Testing endpoint 'Renombrar una Imagen'...")
mock_get_image_params.return_value = {'name': 'test4unittest', 'extension': 'img'}
mock_subprocess.return_value = MagicMock(returncode=0)
response = self.app.put('/ogrepository/v1/images/rename', data=json.dumps({"ID_img":"test_image_id", "image_new_name":"new_name"}), content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertIn('Image renamed successfully', json.loads(response.data)['output'])
@patch('repo_api.get_image_params')
@patch('repo_api.subprocess.run')
def test_rename_image_error500(self, mock_subprocess, mock_get_image_params):
""" Método de prueba del endpoint "Renombrar una Imagen"
(en caso de error "500").
"""
print("Testing endpoint 'Renombrar una Imagen' (error 500)...")
mock_get_image_params.return_value = {'name': 'test4unittest', 'extension': 'img'}
mock_subprocess.side_effect = Exception("Error al renombrar la imagen")
response = self.app.put('/ogrepository/v1/images/rename', data=json.dumps({"ID_img":"test_image_id", "image_new_name":"new_name"}), content_type='application/json')
self.assertEqual(response.status_code, 500)
self.assertIn('Error al renombrar la imagen', response.data.decode())
@patch('repo_api.get_image_params')
def test_rename_image_error400_no_image(self, mock_get_image_params):
""" Método de prueba del endpoint "Renombrar una Imagen"
(en caso de error "400", por imagen inexistente).
"""
print("Testing endpoint 'Renombrar una Imagen' (error 400, no image)...")
mock_get_image_params.return_value = None
response = self.app.put('/ogrepository/v1/images/rename', data=json.dumps({"ID_img":"test_image_id", "image_new_name":"new_name"}), 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_rename_image_error400_name_incorrect(self, mock_get_image_params, mock_check_file_exists):
""" Método de prueba del endpoint "Renombrar una Imagen"
(en caso de error "400", por nombre incorrecto).
"""
print("Testing endpoint 'Renombrar una Imagen' (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/rename', data=json.dumps({"ID_img":"test_image_id", "image_new_name":"new_name"}), content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertIn('There is an image with the name to assign', json.loads(response.data)['exception'])
# --------------------------------------------------------------------------------------------

149
bin/renameImage.py 100644
View File

@ -0,0 +1,149 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script renombra la imagen especificada como primer parámetro (y todos sus archivos asociados), asignando el nombre especificado como segundo parámetro.
Una vez renombrados los archivos, llama al script "updateRepoInfo.py", para actualizar la información del repositorio.
Parámetros
------------
sys.argv[1] - Nombre de la imagen a renombrar (sin extensión y sin ruta).
- Ejemplo1: Ubuntu24
- Ejemplo2: Windows10
sys.argv[2] - Nuevo nombre a asignar a la imagen (sin extensión y sin ruta).
- Ejemplo1: Ubuntu24_BKP
- Ejemplo2: Windows10_BKP
Sintaxis
----------
./renameImage.py image_to_rename image_new_name
Ejemplos
---------
./renameImage.py Ubuntu24 Ubuntu24_BKP
./renameImage.py Windows10 Windows10_BKP
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import os
import sys
import subprocess
from systemd import journal
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
script_name = os.path.basename(__file__)
repo_path = '/opt/opengnsys/ogrepository/images/' # No borrar la barra final
update_repo_script = '/opt/opengnsys/ogrepository/bin/updateRepoInfo.py'
# --------------------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------------------
def show_help():
""" Imprime la ayuda, cuando se ejecuta el script con el parámetro "help".
"""
help_text = f"""
Sintaxis: {script_name} image_to_rename image_new_name
Ejemplo1: {script_name} Ubuntu24 Ubuntu24_BKP
Ejemplo2: {script_name} Windows10 Windows10_BKP
"""
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ámetros, 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 rename_image(image_original_name, image_new_name):
""" Asigna el nuevo nombre especificado a la imagen y a todos los archivos asociados.
Al archivo ".info.checked" también le cambia la extensión a ".info",
para poder actualizar la información del repositorio posteriormente.
"""
try:
journal.send("renameImage.py: Renaming image files...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Creamos una lista con las extensiones de los archivos de la imagen (menos "info.checked"):
extensions = ['.img', '.img.full.sum', '.img.size', '.img.sum', '.img.torrent']
# Renombramos cada uno de los archivos (menos "info.checked"):
for ext in extensions:
os.rename(f"{repo_path}{image_original_name}{ext}", f"{repo_path}{image_new_name}{ext}")
# Renombramos separadamente el archivo "info.checked" (porque también le cambiamos la extensión a "info"):
os.rename(f"{repo_path}{image_original_name}.img.info.checked", f"{repo_path}{image_new_name}.img.info")
except Exception as error:
journal.send(f"renameImage.py: Rename Image exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
sys.exit(2)
def update_repo_info():
""" Actualiza la información del repositorio, ejecutando el script "updateRepoInfo.py".
"""
try:
journal.send("renameImage.py: Running script 'updateRepoInfo.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
result = subprocess.run(['python3', update_repo_script], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as error:
journal.send(f"renameImage.py: 'updateRepoInfo.py' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
sys.exit(3)
except Exception as error:
journal.send(f"renameImage.py: 'updateRepoInfo.py' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
sys.exit(4)
# --------------------------------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------------------------------
def main():
"""
"""
# Evaluamos si se ha enviado la cantidad correcta de parámetros, y en el formato correcto:
check_params()
# Almacenamos el nombre original de la imagen, y el nuevo nombre a asignar (desde los parámetros):
image_original_name = sys.argv[1]
image_new_name = sys.argv[2]
# Renombramos la imagen y sus archivos asociados:
rename_image(image_original_name, image_new_name)
# Actualizamos la información del repositorio, ejecutando el script "updateRepoInfo.py":
update_repo_info()
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------