diff --git a/README.md b/README.md index b1a56dd..b8d802c 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,15 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará ### Tabla de Contenido: 1. [Obtener Información de todas las Imágenes](#obtener-información-de-todas-las-imágenes) - `GET /ogrepository/v1/images` -2. [Obtener Información de una Imagen concreta](#obtener-información-de-una-imagen-concreta) - `GET /ogrepository/v1/images/{name}` +2. [Obtener Información de una Imagen concreta](#obtener-información-de-una-imagen-concreta) - `GET /ogrepository/v1/images/get-info` 3. [Actualizar Información del Repositorio](#actualizar-información-del-repositorio) - `PUT /ogrepository/v1/images` -4. [Eliminar una Imagen](#eliminar-una-imagen) - `DELETE /ogrepository/v1/images/{name}` -5. [Recuperar una Imagen](#recuperar-una-imagen) - `POST /ogrepository/v1/images/{name}` +4. [Eliminar una Imagen](#eliminar-una-imagen) - `DELETE /ogrepository/v1/images/delete-image` +5. [Recuperar una Imagen](#recuperar-una-imagen) - `POST /ogrepository/v1/images/recover-image` 6. [Importar una Imagen](#importar-una-imagen) - `POST /ogrepository/v1/images/import-image` -7. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/images/{protocol}` -8. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/images/{protocol}` +7. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/images/send-udpcast` +8. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/images/send-uftp` 9. [Crear archivo ".torrent"](#crear-archivo-torrent) - `POST /ogrepository/v1/images/create-torrent` -10. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/images/{protocol}` +10. [Enviar una Imagen mediante P2P](#enviar-una-imagen-mediante-p2p) - `POST /ogrepository/v1/images/send-p2p` 11. [Chequear integridad de Imagen](#chequear-integridad-de-imagen) - `GET /ogrepository/v1/images/check-image` 12. [Exportar una Imagen](#exportar-una-imagen) - `POST /ogrepository/v1/images/export-image` 13. [Definir Imagen Global](#definir-imagen-global) - `PUT /ogrepository/v1/images/set-global` @@ -95,16 +95,17 @@ 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/get-info` **Método HTTP:** GET -**Parámetros de la URL:** -- `{name}`: Nombre de la imagen. +**Cuerpo de la Solicitud (JSON):** +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **Ejemplo de Solicitud:** ```bash -curl -X GET -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/images/Windows10 +curl -X GET -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img","ou_subdir":"none"}' http://example.com/ogrepository/v1/images/get-info ``` **Respuestas:** @@ -156,17 +157,18 @@ 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/delete-image` **Método HTTP:** DELETE **Cuerpo de la Solicitud (JSON):** +- **image**: Nombre de la imagen (con extensión). - **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). - **method**: Método de eliminación (puede ser "trash" o "permanent"). **Ejemplo de Solicitud:** ```bash -curl -X DELETE -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ou_subdir":"none", "method":"trash"}' http://example.com/ogrepository/v1/images/Windows10.img +curl -X DELETE -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none", "method":"trash"}' http://example.com/ogrepository/v1/images/delete-image ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al eliminar la imagen. @@ -179,16 +181,17 @@ 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/recover-image` **Método HTTP:** POST **Cuerpo de la Solicitud (JSON):** +- **image**: Nombre de la imagen (con extensión). - **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **Ejemplo de Solicitud:** ```bash -curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"ou_subdir":"none"}' http://example.com/ogrepository/v1/images/Windows10.img +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/recover-image ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al recuperar la imagen. @@ -207,12 +210,13 @@ Se puede intentar hacer con el script "**importimage**", que actualmente no se u **Cuerpo de la Solicitud (JSON):** - **user**: Usuario con el que acceder al repositorio remoto (por defecto, usuario local). - **repo**: IP o hostname del repositorio remoto. -- **image**: Nombre de la imagen a importar. +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **Ejemplo de Solicitud:** ```bash -curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"user":"user_name", "repo":"192.168.56.100", "image":"Windows10"}' http://example.com/ogrepository/v1/images/import-image +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"user":"user_name", "repo":"192.168.56.100", "image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/import-image ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al importar la imagen. @@ -225,14 +229,12 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d Se enviará una imagen 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. -**URL:** `/ogrepository/v1/images/{protocol}` +**URL:** `/ogrepository/v1/images/send-udpcast` **Método HTTP:** POST -**Parámetros de la URL:** -- `{protocol}`: Protocolo que se utilizará para enviar la imagen (en este caso, "udpcast"). - **Cuerpo de la Solicitud (JSON):** -- **image**: Nombre completo de la imagen a enviar, con extensión (con o sin ruta). +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). - **port**: Puerto Multicast. - **method**: Modalidad half-duplex o full-duplex. - **ip**: IP Multicast. @@ -243,7 +245,7 @@ Se puede hacer con el script "**sendFileMcast.py**", que a su vez llama al binar **Ejemplo de Solicitud:** ```bash -curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "port":"9000", "method":"full", "ip":"239.194.17.2", "bitrate":"70M", "nclients":"20", "maxtime":"120"}' http://example.com/ogrepository/v1/images/udpcast +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none", "port":"9000", "method":"full", "ip":"239.194.17.2", "bitrate":"70M", "nclients":"20", "maxtime":"120"}' http://example.com/ogrepository/v1/images/send-udpcast ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen. @@ -257,14 +259,12 @@ 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/send-uftp` **Método HTTP:** POST -**Parámetros de la URL:** -- `{protocol}`: Protocolo que se utilizará para enviar la imagen (en este caso, "uftp"). - **Cuerpo de la Solicitud (JSON):** -- **image**: Nombre completo de la imagen a enviar, con extensión (con o sin ruta). +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). - **port**: Puerto Multicast. - **ip**: IP Unicast/Multicast. - **bitrate**: Velocidad de transmisión (con "K" para Kbps, "M" para Mbps o "G" para Gbps). @@ -272,7 +272,7 @@ Se puede hacer con el script "**sendFileUFTP.py**", que requiere que previamente **Ejemplo de Solicitud:** ```bash -curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "port":"9000", "ip":"239.194.17.2", "bitrate":"1G"}' http://example.com/ogrepository/v1/images/uftp +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none", "port":"9000", "ip":"239.194.17.2", "bitrate":"1G"}' http://example.com/ogrepository/v1/images/send-uftp ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen. @@ -290,12 +290,13 @@ Se debe crear un script que realice dicha tarea, porque actualmente se hace medi **Método HTTP:** POST **Cuerpo de la Solicitud (JSON):** -- **image**: Nombre completo de la imagen a la que asociar un archivo torrent. +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **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/create-torrent +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/create-torrent ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al crear el archivo torrent. @@ -308,19 +309,17 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d 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" (como se había propuesto), se debería crear nuevos scripts. -**URL:** `/ogrepository/v1/images/{protocol}` +**URL:** `/ogrepository/v1/images/send-p2p` **Método HTTP:** POST -**Parámetros de la URL:** -- `{protocol}`: Protocolo que se utilizará para enviar la imagen (en este caso, "p2p"). - **Cuerpo de la Solicitud (JSON):** -- **image**: Nombre completo de la imagen a enviar, con extensión (con o sin ruta). +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **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/p2p +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/send-p2p ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al enviar la imagen. @@ -337,12 +336,13 @@ Para esto, entiendo que se debe crear un script que compare el contenido de los **Método HTTP:** GET **Cuerpo de la Solicitud (JSON):** -- **image**: Nombre completo de la imagen a chequear, con extensión (con o sin ruta). +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **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/check-image +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/check-image ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al chequear la imagen. @@ -363,12 +363,13 @@ Se debe crear un script que realice dicha tarea (o se puede intentar utilizar el **Cuerpo de la Solicitud (JSON):** - **repo**: IP o hostname del repositorio remoto. - **ou**: Unidad Organizativa del repositorio remoto. -- **image**: Nombre de la imagen a exportar. +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **Ejemplo de Solicitud:** ```bash -curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"repo":"192.168.56.200", "ou":"OU_Ejemplo", "image":"Windows10"}' http://example.com/ogrepository/v1/images/export-image +curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"repo":"192.168.56.200", "ou":"OU_Ejemplo", "image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/export-image ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al exportar la imagen. @@ -387,12 +388,13 @@ Además, deberá llamarse a un script que exporte dicha imagen a los demás repo **Método HTTP:** PUT **Cuerpo de la Solicitud (JSON):** -- **image**: Nombre completo de la imagen a definir como global, con extensión (con o sin ruta). +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **Ejemplo de Solicitud:** ```bash -curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img"}' http://example.com/ogrepository/v1/images/set-global +curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/set-global ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al definir la imagen. @@ -411,12 +413,13 @@ Este endpoint deberá ser llamado en todos los repositorios gestionados por el m **Método HTTP:** PUT **Cuerpo de la Solicitud (JSON):** -- **image**: Nombre completo de la imagen a definir como local, con extensión (con o sin ruta). +- **image**: Nombre de la imagen (con extensión). +- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso). **Ejemplo de Solicitud:** ```bash -curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img"}' http://example.com/ogrepository/v1/images/set-local +curl -X PUT -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/set-local ``` **Respuestas:** - **Código 500 Internal Server Error:** Ocurrió un error al definir la imagen. diff --git a/py_scripts/updateRepoInfo.py b/py_scripts/updateRepoInfo.py index 0b5221a..c6a0514 100644 --- a/py_scripts/updateRepoInfo.py +++ b/py_scripts/updateRepoInfo.py @@ -6,8 +6,7 @@ Este script actualiza la información de las imágenes del repositorio, reflejá añadiendo información de las imágenes nuevas, y borrando la información de las imágenes que fueron eliminadas. La información es obtenida desde archivos ".info", que originalmente eran eliminados, pero que en esta versión son renombrados a ".info.checked", obteniendo la misma funcionalidad. - -En principio, debería hacer lo mismo que el script bash original (cuyo nombre es "checkrepo"), salvo por el detalle que acabamos de comentar. +Al acabar, llama al script "updateTrashInfo.py", para actualizar también la información de la papelera del repositorio. No recibe ningún parámetro, y debería ser llamado por el propio ogRepoitory cada vez que se elimine una imagen (desde el script "deleteImage.py"), y por ogCore u ogLive cada vez que se cree una imagen. @@ -30,6 +29,8 @@ import shutil repo_path = '/opt/opengnsys/images' info_file = '/opt/opengnsys/etc/repoinfo.json' +#update_trash_script = '/opt/opengnsys/py_scripts/updateTrashInfo.py' +update_trash_script = '/home/administrador/updateTrashInfo.py' # -------------------------------------------------------------------------------------------- @@ -236,6 +237,21 @@ def remove_from_json(): +def update_trash_info(): + """ Actualiza la información de la papelera del repositorio, ejecutando el script "updateTrashInfo.py". + Como se ve, es necesario que el script se ejecute como sudo, o dará error. + """ + try: + result = subprocess.run(['sudo', 'python3', update_trash_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 # -------------------------------------------------------------------------------------------- @@ -272,6 +288,9 @@ def main(): print("Removing deleted images...") remove_from_json() + # Actualizamos la información de la papelera, ejecutando el script "updateTrashInfo.py": + print("Updating Trash Info...") + update_trash_info() # -------------------------------------------------------------------------------------------- diff --git a/py_scripts/updateTrashInfo.py b/py_scripts/updateTrashInfo.py new file mode 100644 index 0000000..233f6dd --- /dev/null +++ b/py_scripts/updateTrashInfo.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Este script actualiza la información de las imágenes de la papelera del repositorio, reflejándola en el archivo "/opt/opengnsys/etc/trashinfo.json", + añadiendo información de las nuevas imágenes eliminadas, y borrando la información de las imágenes que fueron restauradas (o que ya no están en la papelera). +La información es obtenida desde archivos ".info.checked", cuyo nombre original era ".info" + (pero que fueron renombrados al ser insertada su información en el archivo ""/opt/opengnsys/etc/repoinfo.json", antes de ser eliminadas). + +No recibe ningún parámetro, y no necesita ser llamado explícitamente (porque lo llama el script "updateRepoInfo.py")". +""" + + +# -------------------------------------------------------------------------------------------- +# IMPORTS +# -------------------------------------------------------------------------------------------- + +import os +import json +import subprocess +import shutil + + +# -------------------------------------------------------------------------------------------- +# VARIABLES +# -------------------------------------------------------------------------------------------- + +trash_path = '/opt/opengnsys/images_trash' +info_file = '/opt/opengnsys/etc/trashinfo.json' + + +# -------------------------------------------------------------------------------------------- +# FUNCTIONS +# -------------------------------------------------------------------------------------------- + + +def create_empty_json(): + """ Esta función crea el archivo "trashinfo.json", con la estructura básica que debe contener. + Evidentemente, solo es llamada cuando no existe el archivo. + """ + # Creamos un diccionario con la estructura básica que debe tener el archivo json: + json_data = {"directory": trash_path, "images": [], "ous": []} + + # Abrimos el archivo json en modo escritura (creándolo si no existe, como es el caso), + # y le añadimos la estructura almacenada en el diccionario "json_data": + with open(info_file, 'w') as file: + json.dump(json_data, file, indent=2) + + + +def check_files(): + """ Esta función recorre el directorio de imágenes, para buscar archivos con extensiones ".img" o ".dsk". + Llama a la función "add_to_json" para que esta añada información de las imágenes no bloqueadas (sin archivo ".lock"), + que además tengan un archivo ".info.checked" asociado (siempre que este no se haya modificado antes que la propia imagen). + """ + # Iteramos recursivamente todos los archivos y directorios de "/opt/opengnsys/images_trash", + # y luego iteramos todos los archivos encontrados (en una iteración anidada): + for root, dirs, files in os.walk(trash_path): + for file in files: + # Si la imagen actual tiene extensión ".img" o ".dsk", construimos la ruta completa ("img_path"): + if file.endswith((".img", ".dsk")): + img_path = os.path.join(root, file) + # Si existe un archivo ".lock" asociado a la imagen actual, pasamos a la siguiente: + if os.path.exists(f"{img_path}.lock"): + continue + # Comprobamos si existe un archivo ".info.checked" asociado a la imagen actual: + info_file = f"{img_path}.info.checked" + if os.path.exists(info_file): + # Si la fecha de modificación del archivo ".info.checked" es anterior a la de la imagen, lo eliminamos (porque estará desactualizado): + if os.path.getmtime(info_file) < os.path.getmtime(img_path): + os.remove(info_file) + print(f"Warning: Deleted outdated file {info_file}") + # En caso contrario, almacenamos el contenido del archivo ".info.checked" (del tipo "PARTCLONE:LZOP:EXTFS:8500000:Ubuntu_20") en la variable "data": + else: + with open(info_file, 'r') as file: + data = file.read() + # Llamamos a la función "add_to_json", para que inserte la información de la imagen en el archivo json + # (pasándole el nombre de la imagen, la extensión, y los datos extraídos del archivo ".info.checked"): + img_name = os.path.relpath(img_path, trash_path) + add_to_json(os.path.splitext(img_name)[0], os.path.splitext(img_name)[1][1:], data) + + + +def check_dirs(): + """ Esta función recorre el directorio de imágenes, para buscar archivos con nombre "ogimg.info". + Llama a la función "add_to_json" para que esta añada información de las imágenes no bloqueadas (sin archivo ".lock"), + que además tengan un archivo "ogimg.info" asociado (pero no sé que tipo de imágenes son esas). + """ + # Iteramos recursivamente todos los archivos y directorios de "/opt/opengnsys/images_trash", + # y luego iteramos todos los archivos encontrados (en una iteración anidada): + for root, dirs, files in os.walk(trash_path): + for file in files: + # Si el nombre del archivo actual es "ogimg.info", construimos su ruta completa ("info_path"), y la ruta del directorio ("img_path"): + if file == "ogimg.info": + info_path = os.path.join(root, file) + img_path = os.path.dirname(info_path) + # Si existe un archivo ".lock" asociado a la imagen actual, o si la ruta coincide con "trash_path", pasamos a la siguiente: + if img_path == trash_path or os.path.exists(f"{img_path}.lock"): + continue + # Almacenamos el contenido del archivo "ogimg.info", en la variable "lines": + with open(info_path, 'r') as file: + lines = file.readlines() + # Iteramos las lineas obtenidas, para extraer el tipo de sistema de archivos ("fstype") y el tamaño de los datos ("sizedata"), + # y con ello construimos una cadena "rsync::{fstype}:{sizedata}:" (que almacenamos en "data"): + for line in lines: + if line.startswith("# fstype"): + fstype = line.split("=")[1].strip() + elif line.startswith("# sizedata"): + sizedata = line.split("=")[1].strip() + data = f"rsync::{fstype}:{sizedata}:" + # Llamamos a la función "add_to_json", para que inserte la información de la imagen en el archivo json + # (pasándole el nombre de la imagen, el tipo "dir"", y los datos extraídos del archivo "ogimg.info"): + img_name = os.path.relpath(img_path, trash_path) + add_to_json(img_name, "dir", data) + + + +def add_to_json(image_name, image_type, data): + """ Esta función añade al archivo "trashinfo.json" la información de las imágenes que aun no ha sido introducida en él (imágenes eliminadas recientemente, básicamente). + El procedimiento es diferente para las imágenes "normales" y para las imágenes basadas en OU. + """ + # Almacenamos el contenido de la variable "data" (del tipo "PARTCLONE:LZOP:EXTFS:8500000:Ubuntu_20") en variables separadas: + clonator, compressor, fstype, datasize, client = data.split(":") + + # Si la imagen está dentro de una OU (directorio), extraemos el nombre de la OU y de la imagen: + ou_name = None + if '/' in image_name: + ou_name = os.path.dirname(image_name) + image_name = os.path.basename(image_name) + + # Creamos un diccionario con los datos de la imagen, que luego insertaremos en el json: + json_data = { + "name": image_name, + "type": image_type.lower(), + "clientname": client.strip('\n'), # Eliminamos el salto de linea + "clonator": clonator.lower(), + "compressor": compressor.lower(), + "filesystem": fstype.upper(), + "datasize": int(datasize) * 1024 # Convertimos el valor a bytes (desde KB) + } + # Almacenamos el contenido del archivo "trashinfo.json" en la variable "info_data": + with open(info_file, 'r') as file: + info_data = json.load(file) + + # Comprobamos si las claves "info_data" (o sea, del archivo json) son las correctas: + if set(info_data.keys()) == {"directory", "images", "ous"}: + + # Actualizamos la información de las imágenes "normales" (no basadas en OU): + if ou_name is None: + img_index = next((i for i, img in enumerate(info_data["images"]) if img["name"] == image_name), None) + if img_index is not None: + # Update if image data changes + if info_data["images"][img_index] != json_data: + info_data["images"][img_index] = json_data + else: + # Append a new entry + info_data["images"].append(json_data) + + # Actualizamos la información de las imágenes basadas en OU: + else: + ou_index = next((i for i, ou in enumerate(info_data["ous"]) if ou["subdir"] == ou_name), None) + if ou_index is None: + # Append a new OU entry + info_data["ous"].append({"subdir": ou_name, "images": [json_data]}) + else: + img_index = next((i for i, img in enumerate(info_data["ous"][ou_index]["images"]) if img["name"] == image_name), None) + if img_index is not None: + # Update if image data changes + if info_data["ous"][ou_index]["images"][img_index] != json_data: + info_data["ous"][ou_index]["images"][img_index] = json_data + else: + # Append a new entry + info_data["ous"][ou_index]["images"].append(json_data) + + # Si las claves de "info_data" no son las correctas, creamos toda la estructura: + else: + if ou_name is None: + info_data = {"directory": trash_path, "images": [json_data], "ous": []} + else: + info_data = {"directory": trash_path, "images": [], "ous": [{"subdir": ou_name, "images": [json_data]}]} + + # Sobreescribimos el archivo "trashinfo.json", con el contenido de "info_data" (que estará actualizado): + with open(info_file, 'w') as file: + json.dump(info_data, file, indent=2) + + + +def remove_from_json(): + """ Esta función carga el contenido del archivo "trashinfo.json", y comprueba la existencia de las imágenes especificadas allí. + Elimina las claves correspondientes a imágenes inexistentes en la papelera, y posteriormente sobreescribe el archivo "trashinfo.json". + """ + # Almacenamos el contenido del archivo "trashinfo.json", en la variable "json_data": + with open(info_file, 'r') as file: + json_data = json.load(file) + + # Iteramos las imágenes de la clave "images" de "json_data", construimos sus rutas y comprobamos su existencia + # (si no existen, las eliminamos de "json_data"): + for i, image in enumerate(json_data.get("images", [])): + img_name = image["name"] + img_type = image["type"] + if img_type != "dir": + img_name = f"{img_name}.{img_type}" + img_path = os.path.join(trash_path, img_name) + if not os.path.exists(img_path) and not os.path.exists(f"{img_path}.lock"): + json_data["images"].pop(i) + + # Iteramos las imágenes de la clave "ous" de "json_data", construimos sus rutas y comprobamos su existencia + # (si no existen, las eliminamos de "json_data"): + for j, ou in enumerate(json_data.get("ous", [])): + ou_name = ou["subdir"] + ou_path = os.path.join(trash_path, ou_name) + if not os.path.exists(ou_path): + json_data["ous"].pop(j) + else: + for i, image in enumerate(ou.get("images", [])): + img_name = image["name"] + img_type = image["type"] + if img_type != "dir": + img_name = f"{img_name}.{img_type}" + img_path = os.path.join(ou_path, img_name) + if not os.path.exists(img_path) and not os.path.exists(f"{img_path}.lock"): + ou["images"].pop(i) + + # Sobreescribimos el archivo "trashinfo.json", con el contenido de "json_data" + # (una vez eliminadas las imágenes inexistentes): + with open(info_file, 'w') as file: + json.dump(json_data, file, indent=2) + + + +# -------------------------------------------------------------------------------------------- +# MAIN +# -------------------------------------------------------------------------------------------- + + +def main(): + """ + """ + # Comprobamos si tenemos permisos de escritura sobre el directorio que contiene el archivo "trashinfo.json" + # ("/opt/opengnsys/etc"), y en caso contrario lanzamos una excepción: + if not os.access(os.path.dirname(info_file), os.W_OK): + raise PermissionError(f"Cannot access {info_file}") + + # Comprobamos si existe el archivo "trashinfo.json", y en caso contrario lo creamos: + if not os.path.exists(info_file): + print("Creating empty json file...") + create_empty_json() + + # Comprobamos si tenemos permisos de escritura sobre el archivo, y en caso contrario lanzamos un error: + if not os.access(info_file, os.W_OK): + raise PermissionError(f"Cannot access {info_file}") + + # Llamamos a la función "check_files", para añadir al archivo json las imágenes ".img" o ".dsk" aun no añadidas + # (que son las que tienen asociado un archivo ".info.checked"): + print("Checking file images...") + check_files() + + # Llamamos a la función "check_dirs", para añadir al archivo json las imágenes aun no añadidas que tienen asociado un archivo "ogimg.info" + # (no sé qué imágenes son estas, pero de alguna forma están basadas en directorios): + print("Checking dir images...") + check_dirs() + + # Llamamos a la función "remove_from_json", para eliminar del archivo json las imágenes que ya no están en la papelera: + print("Removing inexistent images...") + remove_from_json() + + + +# -------------------------------------------------------------------------------------------- + +if __name__ == "__main__": + main() + +# --------------------------------------------------------------------------------------------