From 7859bb65fadcceee496fd5e82c30e506e47e8334 Mon Sep 17 00:00:00 2001 From: ggil Date: Tue, 3 Sep 2024 16:34:55 +0200 Subject: [PATCH] refs #631 - Add checkImage.py --- README.md | 75 ++++++++--------- py_scripts/checkImage.py | 168 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 37 deletions(-) create mode 100644 py_scripts/checkImage.py diff --git a/README.md b/README.md index 93c1c01..00a5cd0 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,16 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará 1. [Obtener Información de todas las Imágenes](#obtener-información-de-todas-las-imágenes) - `GET /ogrepository/v1/images/get-info` 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/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. [Crear archivos "sum" y "torrent"](#crear-archivos-sum-y-torrent) - `POST /ogrepository/v1/images/create-torrentsum` -8. [Iniciar el Tracker P2P](#iniciar-el-tracker-p2p) - `POST /ogrepository/v1/images/run-tracker` -9. [Iniciar el Seeder P2P](#iniciar-el-seeder-p2p) - `POST /ogrepository/v1/images/run-seeder` -10. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/images/send-udpcast` -11. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/images/send-uftp` -12. [Chequear integridad de Imagen](#chequear-integridad-de-imagen) - `GET /ogrepository/v1/images/check-image` +3. [Actualizar Información del Repositorio](#actualizar-información-del-repositorio) - `PUT /ogrepository/v1/images` +4. [Chequear integridad de Imagen](#chequear-integridad-de-imagen) - `GET /ogrepository/v1/images/check-image` +5. [Eliminar una Imagen](#eliminar-una-imagen) - `DELETE /ogrepository/v1/images/delete-image` +6. [Recuperar una Imagen](#recuperar-una-imagen) - `POST /ogrepository/v1/images/recover-image` +7. [Importar una Imagen](#importar-una-imagen) - `POST /ogrepository/v1/images/import-image` +8. [Crear archivos ".sum", ".full.sum", ".size" y ".torrent"](#crear-archivos-sum-fullsum-size-y-torrent) - `POST /ogrepository/v1/images/create-torrentsum` +9. [Iniciar el Tracker P2P](#iniciar-el-tracker-p2p) - `POST /ogrepository/v1/images/run-tracker` +10. [Iniciar el Seeder P2P](#iniciar-el-seeder-p2p) - `POST /ogrepository/v1/images/run-seeder` +11. [Enviar una Imagen mediante UDPcast](#enviar-una-imagen-mediante-udpcast) - `POST /ogrepository/v1/images/send-udpcast` +12. [Enviar una Imagen mediante UFTP](#enviar-una-imagen-mediante-uftp) - `POST /ogrepository/v1/images/send-uftp` 13. [Ver Estado de Transmisiones Multicast-P2P](#ver-estado-de-transmisiones-multicast-p2p) - 14. [Cancelar Transmisión Multicast-P2P](#cancelar-transmisión-multicast-p2p) - @@ -217,6 +217,31 @@ curl -X PUT -H "Authorization: $API_KEY" http://example.com/ogrepository/v1/imag - **Código 500 Internal Server Error:** Ocurrió un error al actualizar la información de las imágenes. - **Código 200 OK:** La actualización se realizó exitosamente. +--- +### Chequear Integridad de Imagen + +Se comprobará la integridad del fichero de imagen especificado como parámetro. +Se puede hacer con el script "**checkImage.py**", que compara el tamaño actual del archivo con el almacenado en el archivo "**.size**", y el hash MD5 del último MB del archivo con el almacenado en el archivo "**.sum**". +**NOTA**: En lugar del archivo "**.sum**", se ppodría usar el archivo "**.full.sum**" (que contiene el hash MD5 de todo el archivo), pero en ese caso la comprobación tardaría un poco, dependiendo del tamaño de la imagen. + +**URL:** `/ogrepository/v1/images/check-image` +**Método HTTP:** GET + +**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 '{"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. +- **Código 400 Bad Request:** No se ha encontrado la imagen especificada. +- **Código 200 OK:** La imagen se ha chequeado exitosamente. +- **Código 200 KO:** La imagen se ha chequeado correctamente, pero no ha pasado el test. + --- ### Eliminar una Imagen @@ -290,9 +315,9 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d - **Código 200 OK:** La imagen se ha importado exitosamente. --- -### Crear archivos "sum" y "torrent" +### Crear archivos ".sum", ".full.sum", ".size" y ".torrent" -Se crearán los archivos ".sum", ".full.sum" y ".torrent", para la imagen especificada como parámetro. +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 hemos programado recientemente. **URL:** `/ogrepository/v1/images/create-torrentsum` @@ -302,7 +327,7 @@ Se puede hacer con el script "**createTorrentSum.py**", que hemos programado rec - **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:** +**Ejemplo de Solicitud:** ```bash 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-torrentsum @@ -408,30 +433,6 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d - **Código 400 Bad Request:** No se ha encontrado la imagen especificada. - **Código 200 OK:** La imagen se ha enviado exitosamente. ---- -### 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 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` -**Método HTTP:** GET - -**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 '{"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. -- **Código 400 Bad Request:** No se ha encontrado la imagen especificada. -- **Código 200 OK:** La imagen se ha chequeado exitosamente. -- **Código 200 KO:** La imagen se ha chequeado correctamente, pero no ha pasado el test. - --- ### Ver Estado de Transmisiones Multicast-P2P diff --git a/py_scripts/checkImage.py b/py_scripts/checkImage.py new file mode 100644 index 0000000..d93443b --- /dev/null +++ b/py_scripts/checkImage.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Este script comprueba la integridad de la imagen que recibe como parámetro, volviendo a calcular el tamaño del archivo y el hash MD5 del último MB, + y comparándolos con los valores almacenados en el archivo ".size" y en el archivo ".sum", respectivamente. + + Parámetros +------------ +sys.argv[1] - Nombre completo de la imagen a chequear (con o sin ruta), pero incluyendo el subdirectorio correspondiente a la OU, si es el caso. + - Ejemplo1: image1.img + - Ejemplo2: /opt/opengnsys/images/image1.img + - Ejemplo3: ou_subdir/image1.img + - Ejemplo4: /ou_subdir/image1.img + - Ejemplo5: /opt/opengnsys/images/ou_subdir/image1.img + + Sintaxis +---------- +./checkImage.py [ou_subdir/]image_name|/image_path/image_name + + Ejemplos + --------- +./checkImage.py image1.img +./checkImage.py /opt/opengnsys/images/image1.img +./checkImage.py ou_subdir/image1.img +./checkImage.py /ou_subdir/image1.img +./checkImage.py /opt/opengnsys/images/ou_subdir/image1.img +""" + +# -------------------------------------------------------------------------------------------- +# IMPORTS +# -------------------------------------------------------------------------------------------- + +import os +import sys +import hashlib + + +# -------------------------------------------------------------------------------------------- +# VARIABLES +# -------------------------------------------------------------------------------------------- + +script_name = os.path.basename(__file__) +repo_path = '/opt/opengnsys/images/' + + +# -------------------------------------------------------------------------------------------- +# 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/image1.img + Ejemplo3: {script_name} ou_subdir/image1.img + Ejemplo4: {script_name} /ou_subdir/image1.img + Ejemplo5: {script_name} /opt/opengnsys/images/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 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 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(1) + + +def build_file_path(): + """ Construye la ruta completa del archivo a chequear + (agregando "/opt/opengnsys/images/" 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 "repo_path" + # (porque corresponderá al subdirectorio de una OU), eliminamos la barra: + if param_path.startswith('/') and not param_path.startswith(repo_path): + param_path = param_path.lstrip('/') + # Construimos la ruta completa: + if not param_path.startswith(repo_path): + file_path = os.path.join(repo_path, param_path) + else: + file_path = param_path + return file_path + + +def get_md5_sum(file_path, megabytes=1): + """ Calcula y retorna el hash MD5 del último MB del archivo de imagen que recibe como parámetro. + Se utiliza para comprobar el valor del archivo ".sum". + """ + hash_md5 = hashlib.md5() + with open(file_path, "rb") as f: + f.seek(-megabytes * 1024 * 1024, os.SEEK_END) + data = f.read(megabytes * 1024 * 1024) + hash_md5.update(data) + return hash_md5.hexdigest() + + + +# -------------------------------------------------------------------------------------------- +# 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 chequear: + file_path = build_file_path() + + # Si no existe el archivo de imagen, imprimimos un mensaje de error y salimos del script: + if not os.path.exists(file_path): + print("Image file doesn't exist") + sys.exit(2) + + # Comprobamos si existe el archivo ".sum", y si su contenido coincide con el valor real: + if os.path.exists(f"{file_path}.sum"): + sumOK = False + actual_datasum = get_md5_sum(file_path) + with open(f"{file_path}.sum", 'r') as file: + file_datasum = file.read().strip('\n') + if actual_datasum == file_datasum: + sumOK = True + else: + print("Sum file doesn't exist") + sys.exit(3) + + # Comprobamos si existe el archivo ".size", y si su contenido coincide con el valor real: + if os.path.exists(f"{file_path}.size"): + sizeOK = False + actual_size = os.path.getsize(file_path) + with open(f"{file_path}.size", 'r') as file: + file_datasize = int(file.read().strip('\n')) + if actual_size == file_datasize: + sizeOK = True + else: + print("Size file doesn't exist") + sys.exit(4) + + # Evaluamos los resultados, e imprimimos un mensaje "OK" o "KO": + if sumOK == True and sizeOK == True: + print("Image file passed the Integrity Check correctly") + else: + print("Error: Image file didn't pass the Integrity Check") + + + +# -------------------------------------------------------------------------------------------- + +if __name__ == "__main__": + main() + +# --------------------------------------------------------------------------------------------