ogrepository/bin/importImage.py

227 lines
9.4 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), pero incluyendo el subdirectorio correspondiente a la OU, si es el caso.
- Ejemplo1: image1.img
- Ejemplo2: /opt/opengnsys/ogrepository/images/image1.img
- Ejemplo3: ou_subdir/image1.img
- Ejemplo4: /ou_subdir/image1.img
- Ejemplo5: /opt/opengnsys/ogrepository/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/ogrepository/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/ogrepository/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/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} [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/ogrepository/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/ogrepository/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 = ['', '.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:
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 6 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('/') == 6:
ou_subdir = file_path.split('/')[5]
# 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)
# Renombramos el archivo ".info.checked" a ".info", para que lo pille el script "updateRepoInfo.py":
os.rename(f"{file_path}.info.checked", f"{file_path}.info")
# 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()
# --------------------------------------------------------------------------------------------