refs #631 - Add recoverImage.py

pull/1/head
Gerardo GIl Elizeire 2024-08-20 13:05:51 +02:00
parent 9e5edcdbdf
commit ccd0ebd8ef
2 changed files with 223 additions and 25 deletions

View File

@ -42,7 +42,7 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
Se devolverá la informacion contenida en el archivo "**/opt/opengnsys/etc/repoinfo.json**" (que corresponde a todas las imágenes monolíticas almacenadas en el repositorio).
Se debe crear un script que devuelva dicha información, porque actualmente no hay ninguno.
**URL:** `/ogrepository/v1/images`
**URL:** `/ogrepository/v1/images`
**Método HTTP:** GET
**Ejemplo de Solicitud:**
@ -95,7 +95,7 @@ curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/imag
Se devolverá la informacion contenida en el archivo "**/opt/opengnsys/etc/repoinfo.json**" (correspondiente a la imagen especificada).
Se debe crear un script que devuelva dicha información, porque actualmente no hay ninguno.
**URL:** `/ogrepository/v1/images/{name}`
**URL:** `/ogrepository/v1/images/{name}`
**Método HTTP:** GET
**Parámetros de la URL:**
@ -138,7 +138,7 @@ Se actualizará la información de las imágenes almacenadas en el repositorio,
Se puede hacer con el script "**updateRepoInfo.py**", que hemos programado recientemente (y que es similar al script bash original "**checkrepo**").
Este endpoint es llamado por el script "**deleteImage.py**" (para actualizar la información cada vez que se elimine una imagen), y creemos que también debe ser llamado por ogCore u ogLive cada vez que se haya creado una imagen.
**URL:** `/ogrepository/v1/images`
**URL:** `/ogrepository/v1/images`
**Método HTTP:** PUT
**Ejemplo de Solicitud:**
@ -156,7 +156,7 @@ curl -X PUT -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/imag
Se eliminará la imagen especificada como parámetro, pudiendo eliminarla permanentemente o enviarla a la papelera.
Se puede hacer con el script "**deleteimage.py**", que hemos programado recientemente (y que incluye la funcionalidad "papelera"), y que a su vez llama al script "**updateRepoInfo.py**", para actualizar la información del repositorio.
**URL:** `/ogrepository/v1/images/{name}`
**URL:** `/ogrepository/v1/images/{name}`
**Método HTTP:** DELETE
**Cuerpo de la Solicitud (JSON):**
@ -179,7 +179,7 @@ curl -X DELETE -H "Authorization: $API_KEY" -H "Content-Type: application/json"
Se recuperará la imagen especificada como parámetro, desde la papelera.
Se puede hacer con el script "**recoverImage.py**", que hemos programado recientemente, y que a su vez llama al script "**updateRepoInfo.py**", para actualizar la información del repositorio.
**URL:** `/ogrepository/v1/images/{name}`
**URL:** `/ogrepository/v1/images/{name}`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
@ -199,9 +199,9 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
### Importar una Imagen
Se importará una imagen de un repositorio remoto al repositorio local.
Se puede hacer con el script "**importimage**", que actualmente no se utiliza.
Se puede intentar hacer con el script "**importimage**", que actualmente no se utiliza, pero seguramente habrá que modificarlo.
**URL:** `/ogrepository/v1/images/import-image`
**URL:** `/ogrepository/v1/images/import-image`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
@ -223,9 +223,9 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
### Enviar una Imagen mediante UDPcast
Se enviará una imagen por Multicast, mediante la aplicación UDPcast.
Se puede hacer con el script "**sendFileMcast**", que a su vez llama al binario "**udp-sender**", que es quien realmente realiza el envío.
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.
**URL:** `/ogrepository/v1/images/{protocol}`
**URL:** `/ogrepository/v1/images/{protocol}`
**Método HTTP:** POST
**Parámetros de la URL:**
@ -257,7 +257,7 @@ Se enviará una imagen 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**").
**NOTA**: Los envíos mediante "UFTP" funcionan al revés que los envíos mediante "UDPcast" (con este último, primero se debe ejecutar un comando en el servidor, y luego en los clientes).
**URL:** `/ogrepository/v1/images/{protocol}`
**URL:** `/ogrepository/v1/images/{protocol}`
**Método HTTP:** POST
**Parámetros de la URL:**
@ -286,7 +286,7 @@ Se creará un archivo ".torrent" para la imagen especificada como parámetro.
Se debe crear un script que realice dicha tarea, porque actualmente se hace mediante el script "**torrent-creator**", que se ejecuta por crontab a cada minuto (y crea un archivo ".torrent" por cada imagen que no tenga uno asociado).
**NOTA**: Puede que sea preferible que esta acción la realice el propio ogLive al crear una imagen, ya que también tiene las herramientas para hacerlo.
**URL:** `/ogrepository/v1/images/create-torrent`
**URL:** `/ogrepository/v1/images/create-torrent`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
@ -306,9 +306,9 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
### Enviar una Imagen mediante P2P
Se debe hacer tracking de los torrents almacenados en ogRepository, e iniciar la transferencia de la imagen especificada (además, los clientes deben disponer del torrent asociado, y añadirlo a su cliente torrent).
No tengo claro cómo se haría con los scripts existentes (que utilizan "bttrack" y "ctorrent"), pero si usáramos "opentracker" y "transmission", se debería crear nuevos scripts.
No tengo claro cómo se haría con los scripts existentes (que utilizan "bttrack" y "ctorrent"), pero si usáramos "opentracker" y "transmission" (como se había propuesto), se debería crear nuevos scripts.
**URL:** `/ogrepository/v1/images/{protocol}`
**URL:** `/ogrepository/v1/images/{protocol}`
**Método HTTP:** POST
**Parámetros de la URL:**
@ -331,9 +331,9 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
### Chequear Integridad de Imagen
Se comprobará la integridad de todos los ficheros asociados a la imagen especificada como parámetro.
Para esto, entiendo que se debe crear un script que compare el contenido de los ficheros "**.sum**" y "**.full.sum**" con una nueva extracción del checksum de la imagen, pero no veo como comprobar la integridad de todos los archivos asociados.
Para esto, entiendo que se debe crear un script que compare el contenido de los ficheros "**.sum**" y "**.full.sum**" con una nueva obtención del checksum de la imagen, pero no veo como comprobar la integridad de todos los archivos asociados.
**URL:** `/ogrepository/v1/images/check-image`
**URL:** `/ogrepository/v1/images/check-image`
**Método HTTP:** GET
**Cuerpo de la Solicitud (JSON):**
@ -354,11 +354,10 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
### Exportar una Imagen
Se exportará una imagen del repositorio local a un repositorio remoto.
Se debe crear un script que realice dicha tarea (o se puede utilizar el script "**importimage**", que realiza la acción contraria, pero que actualmente no se utiliza).
Se debe crear un script que realice dicha tarea (o se puede intentar utilizar el script "**importimage**", que realiza la acción contraria, pero que actualmente no se utiliza).
**NOTA**: Aunque no se indica en el pliego, entendemos que también será necesario especificar credenciales de acceso al repositorio remoto como parámetros de entrada.
**URL:** `/ogrepository/v1/images/export-image`
**URL:** `/ogrepository/v1/images/export-image`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
@ -380,11 +379,11 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
### Definir Imagen Global
Se marcará como "global" la imagen especificada como parámetro.
En principio, esto requerirá agregar una nueva clave al archivo "**/opt/opengnsys/etc/repoinfo.json**" (por ejemplo, "scope"), modificando el script "**checkrepo**", para que realice dicha modificación.
También debe crearse un script que realice la definición de la imagen especificada (modificando el valor de la nueva clave del archivo "**/opt/opengnsys/etc/repoinfo.json**", de "local" a "global", por ejemplo).
En principio, esto requerirá agregar una nueva clave al archivo "**/opt/opengnsys/etc/repoinfo.json**" (por ejemplo, "scope"), modificando el script "**updateRepoInfo.py**", para que realice dicha modificación.
También debe crearse un script que realice la definición de la imagen especificada (modificando el valor de la nueva clave del archivo "**/opt/opengnsys/etc/repoinfo.json**", de "local" a "global", por ejemplo).
Además, deberá llamarse a un script que exporte dicha imagen a los demás repositorios gestionados por el servidor de administración (que aun no está creado).
**URL:** `/ogrepository/v1/images/set-global`
**URL:** `/ogrepository/v1/images/set-global`
**Método HTTP:** PUT
**Cuerpo de la Solicitud (JSON):**
@ -403,12 +402,12 @@ curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
---
### Definir Imagen Local
Se marcará como "local" la imagen especificada como parámetro, que previamente habría sido marcada como "global" (ya que de forma predeterminada, todas las imágenes estarán marcadas como "local").
Como comentábamos en el endpoint precedentte, esto requerirá agregar una nueva clave al archivo "**/opt/opengnsys/etc/repoinfo.json**" (por ejemplo, "scope"), modificando el script "**checkrepo**", para que realice dicha modificación.
Se marcará como "local" la imagen especificada como parámetro, que previamente habría sido marcada como "global" (ya que entiendo que de forma predeterminada, todas las imágenes estarán marcadas como "local").
Como comentábamos en el endpoint precedentte, esto requerirá agregar una nueva clave al archivo "**/opt/opengnsys/etc/repoinfo.json**" (por ejemplo, "scope"), modificando el script "**updateRepoInfo.py**", para que realice dicha modificación.
También debe crearse un script que realice la definición de la imagen especificada (modificando el valor de la nueva clave del archivo "**/opt/opengnsys/etc/repoinfo.json**", de "global" a "local", por ejemplo).
Este endpoint deberá ser llamado en todos los repositorios gestionados por el mismo servidor de administración (para que todos hagan la modificación).
**URL:** `/ogrepository/v1/images/set-local`
**URL:** `/ogrepository/v1/images/set-local`
**Método HTTP:** PUT
**Cuerpo de la Solicitud (JSON):**
@ -429,7 +428,7 @@ curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
Se devolverá información del estado de las transmisiones existentes, con un identificador de cada sesión multicast o P2P, y la imagen asociada.
Se debe estudiar como realizar esta tarea para cada uno de los protocolos de transmisión, ya que cada uno tiene sus particularidades, y habitualmente no tienen comandos asociados para comprobar el estado de las transmisiones.
Y tampoco está claro que protocolo se utilizará para transimisiones Multicast (¿"UDPcast", "UFTP", o ambos?), ni qué programas se utilizarán para P2P (¿"ctorrent/bttrack" u "opentracker/Transmission"?).
Y tampoco está claro qué protocolo se utilizará para transimisiones Multicast (¿"UDPcast", "UFTP", o ambos?), ni qué programas se utilizarán para P2P (¿"ctorrent/bttrack" u "opentracker/Transmission"?).
**NOTA**: Posiblemente deba crearse un endpoint específico para cada uno de los protocolos que se utilicen.
---

View File

@ -0,0 +1,199 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script recupera la imagen que recibe como parámetro (y todos sus archivos asociados), moviendo los archivos a "/opt/opengnsys/images", desde la papelera
(respetando el subdirectorio correspondiente a la OU, si fuera el caso).
Llama al script "updateRepoInfo.py", para actualizar la información del repositorio.
Parámetros
------------
sys.argv[1] - Nombre completo de la imagen a recuperar (con o sin ruta), pero incluyendo el subdirectorio correspondiente a la OU, si es el caso.
- Ejemplo1: image1.img
- Ejemplo2: /opt/opengnsys/images_trash/image1.img
- Ejemplo3: ou_subdir/image1.img
- Ejemplo4: /ou_subdir/image1.img
- Ejemplo5: /opt/opengnsys/images_trash/ou_subdir/image1.img
Sintaxis
----------
./recoverImage.py [ou_subdir/]image_name|/image_path/image_name
Ejemplos
---------
./recoverImage.py image1.img
./recoverImage.py /opt/opengnsys/images_trash/image1.img
./recoverImage.py ou_subdir/image1.img
./recoverImage.py /ou_subdir/image1.img
./recoverImage.py /opt/opengnsys/images_trash/ou_subdir/image1.img
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import os
import sys
import shutil
import subprocess
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
script_name = os.path.basename(__file__)
repo_path = '/opt/opengnsys/images/'
trash_path = '/opt/opengnsys/images_trash/'
update_repo_script = '/opt/opengnsys/py_scripts/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} [ou_subdir/]image_name|/image_path/image_name
Ejemplo1: {script_name} image1.img
Ejemplo2: {script_name} /opt/opengnsys/images_trash/image1.img
Ejemplo3: {script_name} ou_subdir/image1.img
Ejemplo4: {script_name} /ou_subdir/image1.img
Ejemplo5: {script_name} /opt/opengnsys/images_trash/ou_subdir/image1.img
"""
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 sin parámetros, se muestra un error y la ayuda, y se sale del script:
if len(sys.argv) == 1:
print(f"{script_name} Error: Formato incorrecto: Se debe especificar al menos 1 parámetro")
show_help()
sys.exit(0)
# Si se ejecuta el script con el parámetro "help", se muestra la ayuda, y se sale del script:
elif len(sys.argv) == 2 and sys.argv[1] == "help":
show_help()
sys.exit(1)
# Si se ejecuta el script con más de 1 parámetro, se muestra un error y la ayuda, y se sale del script:
elif len(sys.argv) > 2:
print(f"{script_name} Error: Formato incorrecto: Se debe especificar 1 parámetro")
show_help()
sys.exit(2)
def build_file_path():
""" Construye la ruta completa al archivo a recuperar
(agregando "/opt/opengnsys/images_trash/" si no se ha especificado en el parámetro).
"""
param_path = sys.argv[1]
# Si la ruta comienza con una barra, pero que no corresponde a "trash_path"
# (porque corresponderá al subdirectorio de una OU), eliminamos la barra:
if param_path.startswith('/') and not param_path.startswith(trash_path):
param_path = param_path.lstrip('/')
# Construimos la ruta completa:
if not param_path.startswith(trash_path):
file_path = os.path.join(trash_path, param_path)
else:
file_path = param_path
return file_path
def recover_normal_image(file_path, extensions):
""" Recupera la imagen "normal" que recibe en el parámetro "file_path", y todos sus archivos asociados,
moviéndolos a "/opt/opengnsys/images" (desde la papelera).
"""
# Iteramos las extensiones de los archivos, y construimos la ruta completa de cada uno de ellos:
for ext in extensions:
file_to_recover = f"{file_path}{ext}"
# Si el archivo actual existe, lo movemos a "/opt/opengnsys/images" (recuperándolo desde la papelera):
if os.path.exists(file_to_recover):
# Si la extensión del archivo actual es ".info.checked" la renombramos a ".info" (para que lo pille "updateRepoInfo"):
if ext == '.info.checked':
os.rename(file_to_recover, file_to_recover.strip('.checked'))
file_to_recover = file_to_recover.strip('.checked')
shutil.move(file_to_recover, repo_path)
def recover_ou_image(file_path, extensions):
""" Recupera la imagen basada en OU que recibe en el parámetro "file_path", y todos sus archivos asociados,
moviéndolos a "/opt/opengnsys/images" (desde la papelera), respetando el subdirectorio correspondiente a la OU.
"""
# Iteramos las extensiones de los archivos, y construimos la ruta completa de cada uno de ellos:
for ext in extensions:
file_to_recover = f"{file_path}{ext}"
# Si el archivo actual existe, lo movemos a "/opt/opengnsys/images/ou_subdir":
if os.path.exists(file_to_recover):
# Si la extensión del archivo actual es ".info.checked" la renombramos a ".info" (para que lo pille "updateRepoInfo"):
if ext == '.info.checked':
os.rename(file_to_recover, file_to_recover.strip('.checked'))
file_to_recover = file_to_recover.strip('.checked')
ou_subdir = file_to_recover.split('/')[4]
# Comprobamos si en "repo_path" existe un subdirectorio correspondiente a la OU (y si no existe lo creamos):
if not os.path.exists(f"{repo_path}{ou_subdir}"):
os.mkdir(f"{repo_path}{ou_subdir}")
shutil.move(file_to_recover, f"{repo_path}{ou_subdir}")
def update_repo_info():
""" Actualiza la información del repositorio, ejecutando el script "updateRepoInfo.py".
Como se ve, es necesario que el script se ejecute como sudo, o dará error.
"""
try:
result = subprocess.run(['sudo', 'python3', update_repo_script], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as error:
print(f"Error Output: {error.stderr.decode()}")
sys.exit(3)
except Exception as error:
print(f"Se ha producido un error inesperado: {error}")
sys.exit(4)
# --------------------------------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------------------------------
def main():
"""
"""
# Evaluamos si se ha enviado la cantidad correcta de parámetros, y en el formato correcto:
check_params()
# Obtenemos la ruta completa al archivo a eliminar:
file_path = build_file_path()
# Creamos una lista con las extensiones de los archivos asociados a la imagen
# (incluyendo ninguna extensión, que corresponde a la propia imagen):
extensions = ['', '.sum', '.full.sum', '.torrent', '.info', '.info.checked']
# Evaluamos la cantidad de barras que hay en la ruta de la imagen, para diferenciar entre imágenes "normales" y basadas en OU
# (y llamamos a la función correspondiente para recuperarla):
if file_path.count('/') == 4:
print("Recovering normal image...")
recover_normal_image(file_path, extensions)
elif file_path.count('/') == 5:
print("Recovering OU based image...")
recover_ou_image(file_path, extensions)
# Actualizamos la información del repositorio, ejecutando el script "updateRepoInfo.py":
print("Updating Repository Info...")
update_repo_info()
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------