refs #631 - Add importImage.py

pull/1/head
Gerardo GIl Elizeire 2024-08-23 14:16:41 +02:00
parent 521220f62c
commit 5f88ba99d8
2 changed files with 231 additions and 87 deletions

View File

@ -30,11 +30,8 @@ El presente documento detalla los endpoints de la API, con sus respectivos pará
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/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`
14. [Definir Imagen Local](#definir-imagen-local) - `PUT /ogrepository/v1/images/set-local`
15. [Ver Estado de Transmisiones Multicast-P2P](#ver-estado-de-transmisiones-multicast-p2p) -
16. [Cancelar Transmisión Multicast-P2P](#cancelar-transmisión-multicast-p2p) -
12. [Ver Estado de Transmisiones Multicast-P2P](#ver-estado-de-transmisiones-multicast-p2p) -
13. [Cancelar Transmisión Multicast-P2P](#cancelar-transmisión-multicast-p2p) -
---
### Obtener Información de todas las Imágenes
@ -202,21 +199,21 @@ 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 intentar hacer con el script "**importimage**", que actualmente no se utiliza, pero seguramente habrá que modificarlo.
Se puede hacer con el script "**importImage.py**", que hemos programado recientemente.
**URL:** `/ogrepository/v1/images/import-image`
**Método HTTP:** POST
**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 (con extensión).
- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso).
- **ou_subdir**: Subdirectorio correspondiente a la OU (o "none" si no es el caso).
- **repo**: IP o hostname del repositorio remoto.
- **user**: Usuario con el que acceder al repositorio remoto.
**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.img", "ou_subdir":"none"}' http://example.com/ogrepository/v1/images/import-image
curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d '{"image":"Windows10.img", "ou_subdir":"none", "repo":"192.168.56.100", "user":"user_name"}' http://example.com/ogrepository/v1/images/import-image
```
**Respuestas:**
- **Código 500 Internal Server Error:** Ocurrió un error al importar la imagen.
@ -350,82 +347,6 @@ curl -X POST -H "Authorization: $API_KEY" -H "Content-Type: application/json" -d
- **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.
---
### 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 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`
**Método HTTP:** POST
**Cuerpo de la Solicitud (JSON):**
- **repo**: IP o hostname del repositorio remoto.
- **ou**: Unidad Organizativa del repositorio remoto.
- **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.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.
- **Código 400 Bad Request:** No se ha encontrado la imagen y/o el equipo remoto especificados.
- **Código 200 OK:** La imagen se ha exportado exitosamente.
---
### 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 "**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`
**Método HTTP:** PUT
**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 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.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La definición se realizó exitosamente.
---
### Definir Imagen Local
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`
**Método HTTP:** PUT
**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 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.
- **Código 400 Bad Request:** No se ha encontrado la imagen especificada.
- **Código 200 OK:** La definición se realizó exitosamente.
---
### Ver Estado de Transmisiones Multicast-P2P

View File

@ -0,0 +1,223 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script importa la imagen especificada como primer parámetro (y sus archivos asociados), desde el repositorio remoto especificado como segundo parámetro,
con las credenciales del usuario especificado como tercer parámetro (en principio, mediante claves).
Es muy similar al script bash original (cuyo nombre es "importimage", a secas), pero con ciertas diferencias.
Al acabar, llama al script "updateRepoInfo.py", para actualizar la información del repositorio.
Librerías Python requeridas: "paramiko" (se puede instalar con "sudo apt install python3-paramiko")
Parámetros
------------
sys.argv[1] - Nombre completo de la imagen a importar (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
sys.argv[2] - IP o hostname del repositorio remoto.
- Ejemplo1: 192.168.56.100
- Ejemplo2: remote_repo
sys.argv[3] - Usuario con el que conectar al repositorio remoto.
- Ejemplo1: remote_user
- Ejemplo2: root
Sintaxis
----------
./importImage.py [ou_subdir/]image_name|/image_path/image_name remote_host remote_user
Ejemplos
---------
./importImage.py image1.img 192.168.56.100 user
./importImage.py /opt/opengnsys/images/image1.img 192.168.56.100 user
./importImage.py ou_subdir/image1.img remote_hostname user
./importImage.py /ou_subdir/image1.img remote_hostname root
./importImage.py /opt/opengnsys/images/ou_subdir/image1.img remote_hostname root
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import warnings
warnings.filterwarnings("ignore")
import os
import sys
import subprocess
import paramiko
import warnings
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
script_name = os.path.basename(__file__)
repo_path = '/opt/opengnsys/images/'
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 remote_host remote_user
Ejemplo1: {script_name} image1.img 192.168.56.100 user
Ejemplo2: {script_name} /opt/opengnsys/images/image1.img 192.168.56.100 user
Ejemplo3: {script_name} ou_subdir/image1.img remote_hostname user
Ejemplo4: {script_name} /ou_subdir/image1.img remote_hostname root
Ejemplo5: {script_name} /opt/opengnsys/images/ou_subdir/image1.img remote_hostname root
"""
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 3 parámetros, se muestra un error y la ayuda, y se sale del script:
elif len(sys.argv) != 4:
print(f"{script_name} Error: Formato incorrecto: Se debe especificar 3 parámetros")
show_help()
sys.exit(1)
def build_file_path():
""" Construye la ruta completa al archivo a importar
(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 import_image(file_path, remote_host, remote_user):
""" Conecta al repositorio remoto por SSH e inicia un cliente SFTP.
Comprueba que la imagen no esté bloqueada, en cuyo caso la descarga (junto con sus archivos asociados).
"""
# 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.checked']
# Iniciamos un cliente SSH:
ssh_client = paramiko.SSHClient()
# Establecemos la política por defecto para localizar la llave del host localmente:
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Intentamos conectar con el equipo remoto por SSH, e iniciar un cliente SFTP,
try: # y en caso de fallar devolvemos un error y salimos del script:
ssh_client.connect(remote_host, 22, remote_user) # Así se hace con claves
#ssh_client.connect(remote_host, 22, remote_user, 'opengnsys') # Así se haría con password
sftp_client = ssh_client.open_sftp()
except Exception as error_description:
print(f"Connection has returned an exception: {error_description}")
sys.exit(2)
# Comprobamos si la imagen existe en el equipo remoto, y en caso contrario devolvemos un error y salimos del script:
try:
sftp_client.stat(file_path)
except IOError:
print("Remote image doesn't exist")
sys.exit(3)
# Comprobamos si la imagen remota está bloqueada, en cuyo caso devolvemos un error y salimos del script,
try: # y en caso contrario la importamos (junto con todos sus archivos asociados):
sftp_client.stat(f"{file_path}.lock")
print("Remote image is locked.")
sys.exit(4)
except IOError:
print("Importing remote image...")
open(f"{file_path}.lock", "w").close() # Creamos un archivo de bloqueo
for ext in extensions:
sftp_client.get(f"{file_path}{ext}", f"{file_path}{ext}")
# Cerramos el cliente SSH y el cliente SFTP:
ssh_client.close()
sftp_client.close()
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(2)
except Exception as error:
print(f"Se ha producido un error inesperado: {error}")
sys.exit(3)
# --------------------------------------------------------------------------------------------
# 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 importar:
file_path = build_file_path()
# Almacenamos la IP/hostname del repositorio remoto, y el usuario remoto (desde los parámetros):
remote_host = sys.argv[2]
remote_user = sys.argv[3]
# Evaluamos si la ruta de la imagen tiene 5 barras, en cuyo caso corresponderá a una imagen basada en OU,
# y almacenamos el nombre del directorio correspondiente a la OU:
if file_path.count('/') == 5:
ou_subdir = file_path.split('/')[4]
# Si no existe un directorio correspondiente a la OU en el repo local, lo creamos:
if not os.path.exists(f"{repo_path}{ou_subdir}"):
os.mkdir(f"{repo_path}{ou_subdir}", 0o755)
# Importamos la imagen del repositorio remoto:
import_image(file_path, remote_host, remote_user)
# Eliminamos el archivo de bloqueo:
os.remove(f"{file_path}.lock")
# Actualizamos la información del repositorio, ejecutando el script "updateRepoInfo.py":
print("Updating Repository Info...")
update_repo_info()
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------