ogrepository/bin/sendFileMcast.py

195 lines
8.9 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script envía mediante UDPCast la imagen recibida como primer parámetro, con los datos Multicast especificados en el segundo parámetro.
En principio, debería hacer lo mismo que el script bash original (cuyo nombre es "sendFileMcast", a secas).
Parámetros
------------
sys.argv[1] - Nombre completo de la imagen a enviar (con o sin ruta).
- Ejemplo1: image1.img
- Ejemplo2: /opt/opengnsys/ogrepository/images/image1.img
sys.argv[2] - Parámetros Multicast (en formato "Port:Duplex:IP:Mpbs:Nclients:Timeout")
- Ejemplo: 9000:full:239.194.17.2:70M:20:120
Sintaxis
----------
./sendFileMcast.py image_name|/image_path/image_name Port:Duplex:IP:Mpbs:Nclients:Timeout
Ejemplos
---------
./sendFileMcast.py image1.img 9000:full:239.194.17.2:70M:20:120
./sendFileMcast.py /opt/opengnsys/ogrepository/images/image1.img 9000:full:239.194.17.2:70M:20:120
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import os
import sys
import subprocess
from systemd import journal
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
script_name = os.path.basename(__file__)
repo_path = '/opt/opengnsys/ogrepository/images/' # No borrar la barra final
bin_path = '/opt/opengnsys/ogrepository/bin/' # No borrar la barra final
repo_iface_script = '/opt/opengnsys/ogrepository/bin/getRepoIface.py'
log_file = '/opt/opengnsys/ogrepository/log/udpcast.log'
# --------------------------------------------------------------------------------------------
# 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 Port:Duplex:IP:Mpbs:Nclients:Timeout
Ejemplo1: {script_name} image1.img 9000:full-duplex:239.194.17.2:70M:20:120
Ejemplo2: {script_name} /opt/opengnsys/ogrepository/images/image1.img 9000:full-duplex:239.194.17.2:70M:20:120
"""
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 la función con más o menos de 2 parámetros, se muestra un mensaje de error, y se sale del script:
elif len(sys.argv) != 3:
print(f"{script_name} Error: Formato incorrecto: Se debe especificar 2 parámetros (image_name|/image_path/image_name Port:Duplex:IP:Mpbs:Nclients:Timeout)")
sys.exit(1)
# Si en el segundo parámetro no hay 6 elementos (separados por ":"), se muestra un mensaje de error, y se sale del script:
param_list = sys.argv[2].split(':')
if len(param_list) != 6:
print(f"{script_name} Error: Datos Multicast incorrectos: \"{sys.argv[2]}\" (se debe especificar \"Port:Duplex:IP:Mpbs:Nclients:Timeout\")")
sys.exit(3)
def build_file_path():
""" Construye la ruta completa al archivo a enviar
(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 get_repo_iface():
""" Obtiene y retorna la interfaz del repositorio, ejecutando el script "getRepoIface.py".
Como se ve, es necesario que el script se ejecute como sudo, o dará error.
"""
try:
journal.send("sendFileMcast.py: Running script 'getRepoIface.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
result = subprocess.run(['sudo', 'python3', repo_iface_script], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
repo_iface = result.stdout.decode().strip() # Es necesario poner "strip", o dará error.
return repo_iface
except subprocess.CalledProcessError as error:
journal.send(f"sendFileMcast.py: 'getRepoIface.py' error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
print(f"Error Output: {error.stderr.decode()}")
sys.exit(3)
except Exception as error:
journal.send(f"sendFileMcast.py: 'getRepoIface.py' exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
print(f"Se ha producido un error inesperado: {error}")
sys.exit(4)
# --------------------------------------------------------------------------------------------
# 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 enviar:
file_path = build_file_path()
# Si el fichero no es accesible, devolvermos un error, y salimos del script:
if not os.path.isfile(file_path):
print(f"{script_name} Error: Fichero \"{file_path}\" no accesible")
sys.exit(2)
# Almacenamos los elementos del segundo parámetro en variables (su formato es "Port:Duplex:IP:Mpbs:Nclients:Timeout"):
param_list = sys.argv[2].split(':')
port, method, ip, bitrate, nclients, maxtime = param_list
# Retocamos las variables "method" y "bitrate":
method = '--half-duplex' if 'half' in method.lower() else '--full-duplex'
bitrate = bitrate.lower()
# Creamos la variable "cerror" (no sé que hace, pero estaba en el script original):
cerror = "8x8/128"
# Obtenemos y almacenamos la interfaz del repositorio, mediante el script "getRepoIface.py":
journal.send("sendFileMcast.py: Getting repository interface...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
repo_iface = get_repo_iface()
# Creamos una lista con el comando a enviar (esto es requerido por la función "subprocess.run").
splitted_cmd = [
os.path.join(bin_path, 'udp-sender'),
'--nokbd',
'--retries-until-drop', '65',
'--portbase', port,
method,
'--interface', repo_iface,
'--mcast-data-address', ip,
'--fec', cerror,
'--max-bitrate', bitrate,
'--ttl', '16',
'--min-clients', nclients,
'--max-wait', maxtime, # Esto hace que espere 120 segundos desde la conexión del primer cliente para empezar la transmisión.
#'--autostart', '20', # Esto hace que empiece la transmisión automáticamente después de enviar 20 paquetes "hello".
'--log', log_file,
'--file', file_path
]
# Imprimimos el comando con espacios (como realmente se enviará):
print(f"Sending command: {' '.join(splitted_cmd)}")
# Ejecutamos el comando en el sistema, e imprimimos el resultado:
try:
journal.send(f"sendFileMcast.py: Running command: {' '.join(splitted_cmd)}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
result = subprocess.run(splitted_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
journal.send(f"sendFileMcast.py: Command ReturnCode: {result.returncode}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
print(f"ReturnCode: {result.returncode}")
except subprocess.CalledProcessError as error:
journal.send(f"sendFileMcast.py: Command error: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
print(f"ReturnCode: {error.returncode}")
print(f"Error Output: {error.stderr.decode()}")
except Exception as error:
journal.send(f"sendFileMcast.py: Command exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
print(f"Se ha producido un error inesperado: {error}")
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------