ogrepository/bin/importImage.py

218 lines
9.3 KiB
Python

#!/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).
- Ejemplo1: image1.img
- Ejemplo2: /opt/opengnsys/ogrepository/images/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 image_name|/image_path/image_name remote_host remote_user
Ejemplos
---------
./importImage.py image1.img 192.168.56.100 user
./importImage.py /opt/opengnsys/ogrepository/images/image1.img remote_hostname user
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import warnings
warnings.filterwarnings("ignore")
import os
import sys
import subprocess
import paramiko
import warnings
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_name|/image_path/image_name remote_host remote_user
Ejemplo1: {script_name} image1.img 192.168.56.100 user
Ejemplo2: {script_name} /opt/opengnsys/ogrepository/images/image1.img remote_hostname user
"""
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/ogrepository/images/" si no se ha especificado en el parámetro).
"""
param_path = sys.argv[1]
# 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 = ['', '.size', '.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:
journal.send(f"importImage.py: Connection exception: {error_description}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
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:
journal.send("importImage.py: Remote image not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
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")
journal.send("importImage.py: Remote image is locked", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
print("Remote image is locked.")
sys.exit(4)
except IOError:
print("Importing remote image...")
journal.send("importImage.py: Creating '.lock' file...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
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:
journal.send("importImage.py: Closing remote connection...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
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:
journal.send("importImage.py: Running script 'updateRepoInfo.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
result = subprocess.run(['sudo', 'python3', update_repo_script], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as error:
journal.send(f"importImage.py: 'updateRepoInfo.py' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
print(f"Error Output: {error.stderr.decode()}")
sys.exit(2)
except Exception as error:
journal.send(f"importImage.py: 'updateRepoInfo.py' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
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]
# Importamos la imagen del repositorio remoto:
import_image(file_path, remote_host, remote_user)
# Renombramos el archivo ".info.checked" a ".info", para que lo pille el script "updateRepoInfo.py":
journal.send("importImage.py: Renaming '.info' file to '.info.checked'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
os.rename(f"{file_path}.info.checked", f"{file_path}.info")
# Eliminamos el archivo de bloqueo:
journal.send("importImage.py: Removing '.lock' file...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
os.remove(f"{file_path}.lock")
# Actualizamos la información del repositorio, ejecutando el script "updateRepoInfo.py":
journal.send("importImage.py: Updating repository info...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
print("Updating Repository Info...")
update_repo_info()
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------