ogrepository/api/repo_api.py

1009 lines
44 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
API de ogRepository, programada en Flask.
Responde a peticiones HTTP (en principio, enviadas desde ogCore) mediante endpoints, que a su vez ejecutan los scripts Python almacenados en ogRepo.
En ciertos casos, transforma los parámetros recibidos desde el portal, para adaptarlos a los que es necesario enviar a los scripts
(por ejemplo, a partir del ID de una imagen obtiene su nombre, su extensión y el subdirectorio de OU).
Librerías Python requeridas: Flask (se puede instalar con "sudo apt install python3-flask").
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
from flask import Flask, jsonify, request
import subprocess
import json
from time import sleep
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
repo_path = '/opt/opengnsys/images/'
script_path = '/opt/opengnsys/bin'
repo_file = '/opt/opengnsys/etc/repoinfo.json'
trash_file = '/opt/opengnsys/etc/trashinfo.json'
# --------------------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------------------
# Creamos una instancia de la aplicación Flask:
app = Flask(__name__)
# ---------------------------------------------------------
def get_image_params(image_id, search='all'):
""" A partir de un ID de imagen (que corresponde al "fullsum"), busca la imagen en el repositorio y/o en la papelera (dependiendo del parámetro "search").
Si encuentra la imagen devuelve su nombre, su extensión y el subdirectorio de OU (si fuera el caso) en un diccionario,
y si no encuentra la imagen especificada retorna "None".
El parámtro "search" tiene el valor predeterminado "all" (que hará que busque tanto en el repo como en la papelera),
pero se le puede pasar el valor "repo" (para que busque solo en el repo) o "trash" (para que busque solo en la papelera).
"""
# Creamos un diccionario vacío, para almacenar los resultados:
result = {}
# Abrimos y almacenamos el archivo "repoinfo.json" (solo si se ha de buscar en el repo):
if search == 'all' or search == 'repo':
with open(repo_file, 'r') as file:
repo_data = json.load(file)
# Iteramos la clave "images" y buscamos la imagen (y si la encontramos almacenamos el nombre y la extension):
for image in repo_data.get('images', []):
if image.get('fullsum') == image_id:
result['name'] = image.get('name')
result['extension'] = image.get('type')
return result
# Iteramos la clave "ous" y su sublclave "images" y buscamos la imagen (y si la encontramos almacenamos el nombre y la extension):
for ou in repo_data.get('ous', []):
for image in ou.get('images', []):
if image.get('fullsum') == image_id:
result['name'] = image.get('name')
result['extension'] = image.get('type')
result['subdir'] = ou.get('subdir')
return result
# Abrimos y almacenamos el archivo "trashinfo.json" (solo si se ha de buscar en la papelera):
if search == 'all' or search == 'trash':
with open(trash_file, 'r') as file:
trash_data = json.load(file)
# Iteramos la clave "images" y buscamos la imagen (y si la encontramos almacenamos el nombre y la extension):
for image in trash_data.get('images', []):
if image.get('fullsum') == image_id:
result['name'] = image.get('name')
result['extension'] = image.get('type')
return result
# Iteramos la clave "ous" y su sublclave "images" y buscamos la imagen (y si la encontramos almacenamos el nombre y la extension):
for ou in trash_data.get('ous', []):
for image in ou.get('images', []):
if image.get('fullsum') == image_id:
result['name'] = image.get('name')
result['extension'] = image.get('type')
result['subdir'] = ou.get('subdir')
return result
# Si no encontramos la imagen, retornamos "None":
return None
# ---------------------------------------------------------
def search_process(process, string_to_search):
""" Busca procesos que contengan el valor del parámetro "process" y el valor del parámetro "string_to_search" (la ruta de la imagen, normalmente).
Si encuentra alguno retorna "True", y si no encuentra ninguno retorna "False".
"""
try:
# Obtenemos todos los procesos que están corriendo, y almacenamos la salida y los errores:
result = subprocess.Popen(['ps', '-aux'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
out, error = result.communicate()
# Almacenamos en una lista los procesos que contengan el proceso del parámetro y la cadena a buscar:
process_list = [line for line in out.split('\n') if process in line and string_to_search in line]
# Si hemos encontrado algún proceso que cumpla las condiciones, retornamos "True", y si no retornamos "False":
if process_list != []:
return True
else:
return False
# Si se ha producido una excepción, imprimimos el error:
except Exception as error_description:
print(f"Unexpected error: {error_description}")
# --------------------------------------------------------------------------------------------
# ENDPOINTS
# --------------------------------------------------------------------------------------------
# 1 - Endpoint "Obtener Información de Estado de ogRepository":
@app.route("/ogrepository/v1/status", methods=['GET'])
def get_repo_status():
""" Este endpoint devuelve información de CPU, memoria RAM, disco duro y el estado de ciertos servicios y procesos de ogRepository, en formato json.
Para ello, ejecuta el script "getRepoStatus.py", que no recibe parámetros.
"""
try:
# Ejecutamos el script "getRepoStatus.py", y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/getRepoStatus.py"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos la respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": json.loads(result.stdout)
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 2 - Endpoint "Obtener Información de todas las Imágenes":
@app.route("/ogrepository/v1/images", methods=['GET'])
def get_repo_info():
""" Este endpoint devuelve información de todas las imágenes contenidas en el repositorio (incluída la papelera), en formato json.
Para ello, ejecuta el script "getRepoInfo.py", con los parámetros "all" y "none".
"""
try:
# Ejecutamos el script "getRepoInfo.py" (con los parámetros "all" y "none"), y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/getRepoInfo.py", 'all', 'none'], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos la respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": json.loads(result.stdout)
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 3 - Endpoint "Obtener Información de una Imagen concreta":
@app.route("/ogrepository/v1/images/<string:imageId>", methods=['GET'])
def get_repo_image_info(imageId):
""" Este endpoint devuelve información de la imagen especificada como parámetro, en formato json.
Para ello, ejecuta el script "getRepoInfo.py", con el nombre de la imagen como primer parámetro,
y el subdirectorio correspondiente a la OU (o "none") como segundo parámetro.
"""
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(imageId)
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/getRepoInfo.py", f"{param_dict['name']}.{param_dict['extension']}", param_dict['subdir']]
else:
cmd = ['sudo', 'python3', f"{script_path}/getRepoInfo.py", f"{param_dict['name']}.{param_dict['extension']}", 'none']
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
try:
# Ejecutamos el script "getRepoInfo.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos la respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": json.loads(result.stdout)
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 4 - Endpoint "Actualizar Información del Repositorio":
@app.route("/ogrepository/v1/images", methods=['PUT'])
def update_repo_info():
""" Este endpoint actualiza la información del repositorio y de la papelera, reflejándola en los archivos "repoinfo.json" y "trashinfo.josn".
Para ello, ejecuta el script "updateRepoInfo.py", que a su vez ejecuta el script "updateTrashInfo.py".
"""
try:
# Ejecutamos el script "updateRepoInfo.py" (sin parámetros), y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/updateRepoInfo.py"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Repository info updated successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 5 - Endpoint "Chequear Integridad de Imagen":
@app.route("/ogrepository/v1/status/images/<string:imageId>", methods=['GET'])
def check_image(imageId):
""" Este endpoint comprueba la integridad de la imagen especificada como parámetro,
comparando el tamaño y el hash MD5 del último MB con los valores almacenados en los archivos "size" y "sum".
Para ello, ejecuta el script "checkImage.py", con el nombre de la imagen como único parámetro,
incluyendo el subdirectorio correspondiente a la OU (si fuera el caso).
"""
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(imageId, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/checkImage.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/checkImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
return jsonify({
"success": False,
"error": "Image not found (inexistent or deleted)"
}), 400
try:
# Ejecutamos el script "checkImage.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos la respuesta:
if result.returncode == 0:
if "Error" in result.stdout:
return jsonify({
"success": True,
"output": "Image file didn't pass the Integrity Check"
}), 200
else:
return jsonify({
"success": True,
"output": "Image file passed the Integrity Check correctly"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 6 - Endpoint "Eliminar una Imagen":
@app.route("/ogrepository/v1/images/<string:imageId>", methods=['DELETE'])
def delete_image(imageId):
""" Este endpoint elimina la imagen especificada como parámetro (y todos sus archivos asociados),
moviéndolos a la papelera o eliminándolos permanentemente (dependiendo del parámetro "method").
Para ello, ejecuta el script "deleteImage.py", con el nombre de la imagen como primer parámetro,
incluyendo el subdirectorio correspondiente a la OU (si fuera el caso), y el parámetro opcional "-p".
"""
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(imageId, "repo")
# Almacenams el método de eliminación ("trash" o "permanent")
method = request.values.get('method')
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
if method == "trash":
cmd = ['sudo', 'python3', f"{script_path}/deleteImage.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/deleteImage.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}", '-p']
else:
if method == "trash":
cmd = ['sudo', 'python3', f"{script_path}/deleteImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/deleteImage.py", f"{param_dict['name']}.{param_dict['extension']}", '-p']
else:
return jsonify({
"success": False,
"error": "Image not found (inexistent or deleted)"
}), 400
try:
# Ejecutamos el script "deleteImage.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Image deleted successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 7 - Endpoint "Recuperar una Imagen":
@app.route("/ogrepository/v1/trash/images", methods=['POST'])
def recover_image():
""" Este endpoint recupera la imagen especificada como parámetro (y todos sus archivos asociados),
moviéndolos nuevamente al repositorio de imágenes, desde la papelera.
Para ello, ejecuta el script "recoverImage.py", con el nombre de la imagen como único parámetro,
incluyendo el subdirectorio correspondiente a la OU (si fuera el caso).
"""
# Almacenamos el parámetro "ID_img" (enviado por JSON):
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(image_id, "trash")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/recoverImage.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/recoverImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
return jsonify({
"success": False,
"error": "Image not found (inexistent or recovered previously)"
}), 400
try:
# Ejecutamos el script "recoverImage.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Image recovered successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 8 - Endpoint "Eliminar una Imagen de la Papelera":
@app.route("/ogrepository/v1/trash/images/<string:imageId>", methods=['DELETE'])
def delete_trash_image(imageId):
""" Este endpoint elimina permanentemente la imagen especificada como parámetro (y todos sus archivos asociados), desde la papelera.
Para ello, ejecuta el script "deleteTrashImage.py", con el nombre de la imagen como único parámetro,
incluyendo el subdirectorio correspondiente a la OU (si fuera el caso).
"""
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(imageId, "trash")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/deleteTrashImage.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/deleteTrashImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
return jsonify({
"success": False,
"error": "Image not found at trash"
}), 400
try:
# Ejecutamos el script "deleteTrashImage.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Image deleted successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 9 - Endpoint "Importar una Imagen":
@app.route("/ogrepository/v1/repo/images", methods=['POST'])
def import_image():
""" Este endpoint importa la imagen especificada como primer parámetro (y todos sus archivos asociados),
desde un servidor remoto al servidor local.
Para ello, ejecuta el script "importImage.py", con el nombre de la imagen como primer parámetro (con subdirectorio de OU, si es el caso),
la IP o hostname del servidor remoto como segundo parámetro, y el usuario con el que conectar al servidor como tercer parámetro.
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
image_name = json_data.get("image")
ou_subdir = json_data.get("ou_subdir")
remote_ip = json_data.get("repo_ip")
remote_user = json_data.get("user")
# Evaluamos los parámetros obtenidos, para construir la llamada al script:
if ou_subdir == "none":
cmd = ['sudo', 'python3', f"{script_path}/importImage.py", f"{repo_path}{image_name}", remote_ip, remote_user]
else:
cmd = ['sudo', 'python3', f"{script_path}/importImage.py", f"{repo_path}{ou_subdir}/{image_name}", remote_ip, remote_user]
try:
# Ejecutamos el script "importImage.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Image imported successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
if "exit status 2" in str(error_description):
return jsonify({
"success": False,
"exception": "Can't connect to remote server"
}), 400
elif "exit status 3" in str(error_description):
return jsonify({
"success": False,
"exception": "Remote image not found"
}), 400
elif "exit status 4" in str(error_description):
return jsonify({
"success": False,
"exception": "Remote image is locked"
}), 400
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 10 - Endpoint "Exportar una Imagen":
@app.route("/ogrepository/v1/repo/images", methods=['PUT'])
def export_image():
""" Este endpoint exporta la imagen especificada como primer parámetro (y todos sus archivos asociados),
desde el servidor local a un servidor remoto.
Para ello, ejecuta el script "exportImage.py", con el nombre de la imagen como primer parámetro (con subdirectorio de OU, si es el caso),
la IP o hostname del servidor remoto como segundo parámetro, y el usuario con el que conectar al servidor como tercer parámetro.
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
remote_ip = json_data.get("repo_ip")
remote_user = json_data.get("user")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/exportImage.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}", remote_ip, remote_user]
else:
cmd = ['sudo', 'python3', f"{script_path}/exportImage.py", f"{param_dict['name']}.{param_dict['extension']}", remote_ip, remote_user]
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
try:
# Ejecutamos el script "exportImage.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Image exported successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
if "exit status 3" in str(error_description):
return jsonify({
"success": False,
"exception": "Image is locked"
}), 400
elif "exit status 4" in str(error_description):
return jsonify({
"success": False,
"exception": "Can't connect to remote server"
}), 400
elif "exit status 5" in str(error_description):
return jsonify({
"success": False,
"exception": "Image already exists on remote server"
}), 400
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 11 - Endpoint "Crear archivos auxiliares":
@app.route("/ogrepository/v1/images/torrentsum", methods=['POST'])
def create_torrent_sum():
""" Este endpoint crea los archivos ".size", ".sum", ".full.sum" y ".torrent" para la imagen que recibe como parámetro.
Para ello, ejecuta el script "createTorrentSum.py", con el nombre de la imagen como único parámetro (con subdirectorio de OU, si es el caso).
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
image_name = json_data.get("image")
ou_subdir = json_data.get("ou_subdir")
# Evaluamos los parámetros obtenidos, para construir la llamada al script:
if ou_subdir == "none":
cmd = ['sudo', 'python3', f"{script_path}/createTorrentSum.py", image_name]
else:
cmd = ['sudo', 'python3', f"{script_path}/createTorrentSum.py", f"{ou_subdir}/{image_name}"]
try:
# Ejecutamos el script "createTorrentSum.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Files created successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
if "exit status 2" in str(error_description):
return jsonify({
"success": False,
"exception": "Image not found"
}), 400
elif "exit status 3" in str(error_description):
return jsonify({
"success": False,
"exception": "Image is locked"
}), 400
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 12 - Endpoint "Enviar paquete Wake On Lan":
@app.route("/ogrepository/v1/wol", methods=['POST'])
def send_wakeonlan():
""" Este endpoint envía un paquete mágico Wake On Lan a la dirección MAC especificada, a través de la IP de broadcast especificadac.
Para ello, ejecuta el script "sendWakeOnLan.py", con la IP de broadcast como primer parámetro, y la MAC como segundo parámetro.
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
broadcast_ip = json_data.get("broadcast_ip")
mac = json_data.get("mac")
try:
# Ejecutamos el script "sendWakeOnLan.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/sendWakeOnLan.py", broadcast_ip, mac], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Wake On Lan packet sended successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 13 - Endpoint "Enviar una Imagen mediante UDPcast":
@app.route("/ogrepository/v1/udpcast", methods=['POST'])
def send_udpcast():
""" Este endpoint envía mediante UDPcast la imagen que recibe como primer parámetro, con los datos de transferencia que recibe en los demás parámetros.
Para ello, ejecuta el script "sendFileMcast.py", con la imagen como primer parámetro, y los demás en una cadena (como segundo parámetro).
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
port = json_data.get("port")
method = json_data.get("method")
ip = json_data.get("ip")
bitrate = json_data.get("bitrate")
nclients = json_data.get("nclients")
maxtime = json_data.get("maxtime")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/sendFileMcast.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}", f"{port}:{method}:{ip}:{bitrate}:{nclients}:{maxtime}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/sendFileMcast.py", f"{param_dict['name']}.{param_dict['extension']}", f"{port}:{method}:{ip}:{bitrate}:{nclients}:{maxtime}"]
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
try:
# Ejecutamos el script "sendFileMcast.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0: # NOTA: Devolverá "0" cuando finalize la transmisión, lo que finalizará también el proceso asociado,
return jsonify({ # pero si ningún cliente se conecta, el proceso quedará corriendo, y el script no devolverá nada.
"success": True, # Esto podría paliarse utilizando el parámetro "--autostart", pero creo que no será necesario.
"output": "Image sended successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 14 - Endpoint "Enviar una Imagen mediante UFTP":
@app.route("/ogrepository/v1/uftp", methods=['POST'])
def send_uftp():
""" Este endpoint envía mediante UFTP la imagen que recibe como primer parámetro, con los datos de transferencia que recibe en los demás parámetros.
Para ello, ejecuta el script "sendFileUFTP.py", con la imagen como primer parámetro, y los demás en una cadena (como segundo parámetro).
NOTA: Es necesario que los clientes se hayan puesto en escucha previamente (ejecutando el script "listenUFTPD.py").
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
port = json_data.get("port")
ip = json_data.get("ip")
bitrate = json_data.get("bitrate")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/sendFileUFTP.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}", f"{port}:{ip}:{bitrate}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/sendFileUFTP.py", f"{param_dict['name']}.{param_dict['extension']}", f"{port}:{ip}:{bitrate}"]
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
try:
# Ejecutamos el script "sendFileUFTP.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0: # NOTA: Devolverá "0" cuando finalize la transmisión, lo que finalizará también el proceso asociado,
return jsonify({ # pero si ningún cliente se conecta, el proceso finalizará automáticamente (y tambien devolverá "0"").
"success": True,
"output": "Image sended successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 15 - Endpoint "Enviar una Imagen mediante P2P":
@app.route("/ogrepository/v1/p2p", methods=['POST'])
def send_p2p():
""" Este endpoint inicia el tracker "bttrack" y el seeder "bittornado", en el directorio en el que esté situada la imagen que recibe como parámetro.
Para ello, ejecuta los scripts "runTorrentTracker.py" y "runTorrentSeeder.py", pasándoles el subdirectorio de OU de la imagen (o "none" si no es el caso).
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(image_id, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd_tracker = ['sudo', 'python3', f"{script_path}/runTorrentTracker.py", param_dict['subdir']]
cmd_seeder = ['sudo', 'python3', f"{script_path}/runTorrentSeeder.py", param_dict['subdir']]
base_path = f"{repo_path}{param_dict['subdir']}"
else:
cmd_tracker = ['sudo', 'python3', f"{script_path}/runTorrentTracker.py", 'none']
cmd_seeder = ['sudo', 'python3', f"{script_path}/runTorrentSeeder.py", 'none']
base_path = repo_path.rstrip('/') # Le quito la última barra para poder buscar correctamente en los procesos
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
# Ejecutamos los scripts "runTorrentSeeder.py" y "runTorrentSeeder.py" (con los parámetros almacenados).
# NOTA: No almacenamos la salida ni comprobamos los errores, porque los procesos quedarán corriendo hasta que se finalicen manualmente,
# por lo que no podemos comprobar el returncode (luego comprobaremos si los procesos se han iniciado correctamente).
subprocess.Popen(cmd_tracker)
subprocess.Popen(cmd_seeder)
# Comprobamos si el tracker y el seeder están corriendo, y si apuntan al directorio que le hemos pasado
# (esperamos 10 segundos antes de hacerlo, porque los procesos no se inician inmediatamente):
sleep(10)
tracker_running = search_process('bttrack', base_path)
seeder_running = search_process('btlaunchmany', base_path)
# Evaluamos las comprobaciones anteriores, para devolver la respuesta que corresponda:
if tracker_running and seeder_running:
return jsonify({
"success": True,
"output": "Tracker and Seeder serving image correctly"
}), 200
else:
return jsonify({
"success": False,
"error": "Tracker or Seeder (or both) not running"
}), 500
# ---------------------------------------------------------
# 16 - Endpoint "Ver Estado de Transmisiones UDPcast":
@app.route("/ogrepository/v1/udpcast", methods=['GET'])
def get_udpcast_info():
""" Este endpoint devuelve información sobre los procesos de "udp-sender" activos, en formato json,
lo que en la práctica permite comprobar las transferencias UDPcast activas.
Para ello, ejecuta el script "getUDPcastInfo.py", que no recibe parámetros.
"""
try:
# Ejecutamos el script "getUDPcastInfo.py", y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/getUDPcastInfo.py"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos la respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": json.loads(result.stdout)
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
if "exit status 1" in str(error_description):
return jsonify({
"success": False,
"exception": "No UDPCast active transmissions"
}), 400
elif "exit status 2" in str(error_description):
return jsonify({
"success": False,
"exception": "Unexpected error checking UDPcast transmissions"
}), 500
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 17 - Endpoint "Cancelar Transmisión UDPcast":
@app.route("/ogrepository/v1/udpcast/images/<string:imageId>", methods=['DELETE'])
def stop_udpcast(imageId):
""" Este endpoint cancela la transmisión UDPcast de la imagen que recibe como parámetro, finalizando el proceso "udp-sender" asociado.
Para ello, ejecuta el script "stopUDPcast.py", pasándole el nombre de la imagen (con subdirectorio de OU, si procede).
"""
# Obtenemos el nombre y la extensión de la imagen (y el subdirectorio de OU, si fuera el caso):
param_dict = get_image_params(imageId, "repo")
# Evaluamos los parámetros obtenidos, para construir la llamada al script, o para devover un error si no se ha encontrado la imagen:
if param_dict:
if 'subdir' in param_dict:
cmd = ['sudo', 'python3', f"{script_path}/stopUDPcast.py", f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}"]
else:
cmd = ['sudo', 'python3', f"{script_path}/stopUDPcast.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
try:
# Ejecutamos el script "stopUDPcast.py" (con los parámetros almacenados), y almacenamos el resultado:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "Image transmission canceled successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
if "exit status 3" in str(error_description):
return jsonify({
"success": False,
"exception": "No UDPCast active transmissions for specified image"
}), 400
elif "exit status 4" in str(error_description):
return jsonify({
"success": False,
"exception": "Unexpected error checking UDPcast transmissions"
}), 500
elif "exit status 5" in str(error_description):
return jsonify({
"success": False,
"exception": "Unexpected error finalizing UDPcast transmission"
}), 500
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 18 - Endpoint "Cancelar Transmisiones P2P":
@app.route("/ogrepository/v1/p2p", methods=['DELETE'])
def stop_p2p():
""" Este endpoint cancela las transmisiones P2P activas, finalizando los procesos "btlaunchmany.bittornado" (seeder) y "bttrack" (tracker).
Para ello, ejecuta el script "stopP2P.py", que no recibe parámetros.
"""
try:
# Ejecutamos el script "stopP2P.py", y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/stopP2P.py"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Evaluamos el resultado de la ejecución, y devolvemos la respuesta:
if result.returncode == 0:
return jsonify({
"success": True,
"output": "P2P transmissions canceled successfully"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# --------------------------------------------------------------------------------------------
# Ejecutamos la aplicación, en el puerto "8006":
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8006)
# --------------------------------------------------------------------------------------------