ogrepository/api/repo_api.py

1369 lines
58 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 ogRepository.
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 y su extensión).
Librerías Python requeridas: - flask (se puede instalar con "sudo apt install python3-flask")
- paramiko (se puede instalar con "sudo apt install python3-paramiko")
- requests (se puede instalar con "sudo apt install python3-requests") - No tengo claro que para este paquete sea necesario
- flasgger (se puede instalar con "sudo apt install python3-flasgger")
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
from flask import Flask, jsonify, request
import os
import subprocess
import json
from time import sleep
import paramiko
import logging
import threading
import requests
import random
# Imports para Swagger:
from flasgger import Swagger
import yaml
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
repo_path = '/opt/opengnsys/ogrepository/images/' # No borrar la barra final
script_path = '/opt/opengnsys/ogrepository/bin'
repo_file = '/opt/opengnsys/ogrepository/etc/repoinfo.json'
trash_file = '/opt/opengnsys/ogrepository/etc/trashinfo.json'
config_file = '/opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg'
# --------------------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------------------
# Creamos una instancia de la aplicación Flask:
app = Flask(__name__)
# Cargamos el contenido del archivo "swagger.yaml":
with open("swagger.yaml", "r") as file:
swagger_template = yaml.safe_load(file)
# Así cambiamos el nombre de la página (por defecto, es 'Flasgger'):
swagger_config = Swagger.DEFAULT_CONFIG
swagger_config['title'] = 'ogRepository API'
swagger = Swagger(app, template=swagger_template)
# ---------------------------------------------------------
def get_IPcore():
""" Obtiene el valor asociado a la variable "IPcore", desde el archivo '/opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg'.
Retorna la IP encontrada (que corresponde a la IP de ogCore), o un error (si no la encuentra).
"""
IPcore = None
with open(config_file, 'r') as file:
for line in file:
if line.startswith('IPcore'):
IPcore = line.split('=')[1].strip()
return IPcore
if IPcore is None:
return "IP no encontrada en el archivo de configuración"
# Almacenamos la IP de ogCore:
ogcore_ip = get_IPcore()
# ---------------------------------------------------------
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 y su extensión 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, y si el archivo tiene contenido):
if (search == 'all' or search == 'repo') and os.path.getsize(repo_file) > 0:
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
# Abrimos y almacenamos el archivo "trashinfo.json" (solo si se ha de buscar en la papelera, y si el archivo tiene contenido):
if (search == 'all' or search == 'trash') and os.path.getsize(trash_file) > 0:
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
# 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}")
# ---------------------------------------------------------
def check_remote_connection(remote_ip, remote_user):
""" Comprueba la conexión SSH/SFTP con el servidor remoto que recibe como primer parámetro.
Se utiliza para chequear la conexión antes de importar o exportar una imagen.
"""
try:
# 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())
# Conectamos con el equipo remoto por SSH:
ssh_client.connect(remote_ip, 22, remote_user) # Así se hace con claves
#ssh_client.connect(remote_ip, 22, remote_user, 'opengnsys') # Así se haría con password
# Iniciamos un cliente SFTP:
sftp_client = ssh_client.open_sftp()
# Cerramos el cliente SSH y el cliente SFTP:
ssh_client.close()
sftp_client.close()
# Retornamos "True", porque hemos conseguido conectar:
return True
# Si se produce una excepción, retornamos "False":
except Exception:
return False
# ---------------------------------------------------------
def check_remote_image(remote_ip, remote_user, image_file_path):
""" Conecta con el servidor remoto que recibe como primer parámetro,
para comprobar si la imagen que recibe como tercer parámetro existe, o si está bloqueada.
Se utiliza para chequear la imagen antes de importarla.
"""
# 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())
# Conectamos con el equipo remoto por SSH:
ssh_client.connect(remote_ip, 22, remote_user) # Así se hace con claves
#ssh_client.connect(remote_ip, 22, remote_user, 'opengnsys') # Así se haría con password
# Iniciamos un cliente SFTP:
sftp_client = ssh_client.open_sftp()
# Si la imagen no existe, retornamos el mensaje correspondiente:
if not os.path.exists(image_file_path):
return "Remote image not found"
# Si la imagen existe pero está bloqueada, retornamos el mensaje correspondiente:
if os.path.exists(f"{image_file_path}.lock"):
return "Remote image is locked"
# Cerramos el cliente SSH y el cliente SFTP:
ssh_client.close()
sftp_client.close()
# ---------------------------------------------------------
def check_lock_local(image_file_path, job_id):
""" Cada minuto comprueba si existe un archivo ".lock" asociado a la imagen que recibe como parámetro
(lo que significará que hay una tarea en curso), en el repositorio local.
Cuando no encuentre el archivo ".lock" lo comunicará a ogCore, llamando a un endpoint,
y dejará de realizar la comprobación (saliendo del bucle).
"""
# Esperamos 30 segundos, para dar tiempo a que se cree el archivo ".lock":
sleep(30)
# Creamos un bucle infinito:
while True:
# Si ya no existe el archivo ".lock", imprimimos un mensaje en la API, respondemos a ogCore y salimos del bucle:
if not os.path.exists(f"{image_file_path}.lock"):
app.logger.info("Task finalized (no .lock file)")
app.logger.info(f"Job_ID: {job_id}")
# Almacenamos en un diccionario los datos a enviar a ogCore:
data = {
'job_id': job_id
}
# Llamamos al endpoint de ogCore, enviando los datos del diccionario:
recall_ogcore(data)
break
# Si aun existe el archivo ".lock", imprimimos un mensaje en la API:
else:
app.logger.info("Task in process (.lock file exists)")
# Esperamos 1 minuto para volver a realizar la comprobación:
sleep(60)
# ---------------------------------------------------------
def check_lock_remote(image_file_path, remote_host, remote_user, job_id):
""" Cada minuto comprueba si existe un archivo ".lock" asociado a la imagen que recibe como parámetro
(lo que significará que hay una tarea en curso), en un repositorio remoto (al que conecta por SSH/SFTP).
Cuando no encuentre el archivo ".lock" lo comunicará a ogCore, llamando a un endpoint,
y dejará de realizar la comprobación (saliendo del bucle).
"""
# 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())
# Conectamos con el equipo remoto por SSH:
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
# Iniciamos un cliente SFTP:
sftp_client = ssh_client.open_sftp()
# Esperamos 30 segundos, para dar tiempo a que se cree el archivo ".lock":
sleep(30)
# Creamos un bucle infinito:
while True:
try:
# Si aun existe el archivo ".lock", imprimimos un mensaje en la API:
sftp_client.stat(f"{image_file_path}.lock")
app.logger.info("Task in process (.lock file exists)")
except IOError:
# Si ya no existe el archivo ".lock", imprimimos un mensaje en la API, respondemos a ogCore y salimos del bucle:
app.logger.info("Task finalized (no .lock file)")
app.logger.info(f"Job_ID: {job_id}")
# Almacenamos en un diccionario los datos a enviar a ogCore:
data = {
'job_id': job_id
}
# Llamamos al endpoint de ogCore, enviando los datos del diccionario:
recall_ogcore(data)
break
# Esperamos 1 minuto para volver a realizar la comprobación:
sleep(60)
# Ya fuera del bucle, cerramos el cliente SSH y el cliente SFTP:
ssh_client.close()
sftp_client.close()
# ---------------------------------------------------------
def check_aux_files(image_file_path, job_id):
""" Cada 10 segundos comprueba si se han creado todos los archivos auxiliares de la imagen que recibe como parámetro,
en cuyo caso lo comunicará a ogCore, llamando a un endpoint, y dejará de realizar la comprobación.
También obtiene el valor del archivo ".full.sum" (que corresonde al ID), y se lo comunica a ogCore.
"""
# Creamos un bucle infinito:
while True:
# Si faltan archivos auxiliares por crear, imprimimos un mensaje en la API:
if not os.path.exists(f"{image_file_path}.size") or not os.path.exists(f"{image_file_path}.sum") or not os.path.exists(f"{image_file_path}.full.sum") or not os.path.exists(f"{image_file_path}.torrent") or not os.path.exists(f"{image_file_path}.info.checked"):
app.logger.info("Task in process (auxiliar files remaining)")
# Si ya se han creado todos los archivos auxiliares, imprimimos un mensaje en la API, respondemos a ogCore y salimos del bucle:
else:
app.logger.info("Task finalized (all auxilar files created)")
# Obtenemos el valor del archivo "full.sum", que corresponde al ID, y lo imprimimos:
with open(f"{image_file_path}.full.sum", 'r') as file:
image_id = file.read().strip('\n')
app.logger.info(f"Job_ID: {job_id}")
app.logger.info(f"Image_ID: {image_id}")
# Almacenamos en un diccionario los datos a enviar a ogCore:
data = {
'job_id': job_id,
'image_id': image_id
}
# Llamamos al endpoint de ogCore, enviando los datos del diccionario:
recall_ogcore(data)
break
# Esperamos 10 segundos para volver a realizar la comprobación:
sleep(10)
# ---------------------------------------------------------
def recall_ogcore(data):
""" Hace una petición HTTP de tipo POST a un endpoint de ogCore, enviando datos que dependen del caso.
Se utiliza para informar a ogCore del resultado de una tarea asíncrona,
que estaba corriendo en un proceso independiente (no controlado por los endpoints).
"""
# Almacenamos la URL del endpoint de ogCore (prueba):
#endpoint_url = f"http://{ogcore_ip}:8006/ogcore/v1/test"
endpoint_url = f"https://{ogcore_ip}:8443/og-repository/webhook"
# Almacenamos los headers, y convertiomos "data" a JSON:
headers = {'content-type': 'application/json'}
data = json.dumps(data)
# Hacemos una petición POST al endpoint, enviando lo almacenado en "data":
response = requests.post(endpoint_url, data=data, headers=headers, verify=False)
# Imprimimos el código de estado de la petición y la respuesta de ogCore:
app.logger.info(f"HTTP Status Code: {response.status_code}")
app.logger.info(f"HTTP Response: {response.text}")
# --------------------------------------------------------------------------------------------
# ENDPOINTS
# --------------------------------------------------------------------------------------------
# 1 - Endpoint "Obtener Información de Estado de ogRepository" (SINCRONO):
@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" (SINCRONO):
@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 el parámetro "all".
"""
try:
# Ejecutamos el script "getRepoInfo.py" (con el parámetro "all"), y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/getRepoInfo.py", 'all'], 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" (SINCRONO):
@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 parámetro.
"""
# Obtenemos el nombre y la extensión de la imagen:
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:
cmd = ['sudo', 'python3', f"{script_path}/getRepoInfo.py", f"{param_dict['name']}.{param_dict['extension']}"]
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" (SINCRONO):
@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" (SINCRONO):
@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.
"""
# Obtenemos el nombre y la extensión de la imagen:
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:
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" (SINCRONO):
@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, y el parámetro opcional "-p".
"""
# Obtenemos el nombre y la extensión de la imagen:
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 method == "permanent":
cmd = ['sudo', 'python3', f"{script_path}/deleteImage.py", f"{param_dict['name']}.{param_dict['extension']}", '-p']
elif method == "trash":
cmd = ['sudo', 'python3', f"{script_path}/deleteImage.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
return jsonify({
"success": False,
"error": "Incorrect method (must be 'permanent' or 'trash')"
}), 400
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" (SINCRONO):
@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.
"""
# 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:
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:
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" (SINCRONO):
@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.
"""
# Obtenemos el nombre y la extensión de la imagen:
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:
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" (ASINCRONO):
@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,
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")
remote_ip = json_data.get("repo_ip")
remote_user = json_data.get("user")
# Comprobamos la conexión con el equipo remoto, y si falla salimos del endpoint, retornando un error:
connection_OK = check_remote_connection(remote_ip, remote_user)
if connection_OK == False:
return jsonify({
"success": False,
"exception": "Can't connect to remote server"
}), 400
# Construimos la ruta de la imagen:
image_file_path = f"{repo_path}{image_name}"
# Comprobamos si la imagen remota no existe o está bloqueada, en cuyos casos salimos del endpoint y retornamos el error correspondiente:
check_image = check_remote_image(remote_ip, remote_user, image_file_path)
if check_image == "Remote image not found":
return jsonify({
"success": False,
"exception": "Remote image not found"
}), 400
elif check_image == "Remote image is locked":
return jsonify({
"success": False,
"exception": "Remote image is locked"
}), 400
# Construimos la llamada al script:
cmd = ['sudo', 'python3', f"{script_path}/importImage.py", image_file_path, remote_ip, remote_user]
try:
# Ejecutamos el script "importImage.py" (con los parámetros almacenados), y almacenamos el resultado (pero sin esperar a que termine el proceso):
result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Generamos el ID para identificar el trabajo asíncrono:
job_id = f"ImportImage_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode is None:
# Si el resultado es correcto, llamamos a la función "check_lock_local" en un hilo paralelo
# (para que compruebe si la imagen se ha acabado de importar exitosamente):
threading.Thread(target=check_lock_local, args=(image_file_path, job_id,)).start()
# Informamos que la imagen se está importando, y salimos del endpoint:
return jsonify({
"success": True,
"output": "Importing image...",
"job_id": job_id
}), 200
else:
return jsonify({
"success": False,
"error": "Image import failed"
}), 500
except subprocess.CalledProcessError as error:
return jsonify({
"success": False,
"process exception": str(error)
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 10 - Endpoint "Exportar una Imagen" (ASINCRONO):
@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,
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 ruta de la imagen, o para devover un error si no se ha encontrado la imagen (o si está bloqueada):
if param_dict:
image_file_path = f"{param_dict['name']}.{param_dict['extension']}"
# Si la imagen existe pero está bloqueada, devolvemos un error:
if os.path.exists(f"{repo_path}{image_file_path}.lock"):
return jsonify({
"success": False,
"exception": "Image is locked"
}), 400
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
# Comprobamos la conexión con el equipo remoto, y si falla salimos del endpoint, retornando un error:
connection_OK = check_remote_connection(remote_ip, remote_user)
if connection_OK == False:
return jsonify({
"success": False,
"exception": "Can't connect to remote server"
}), 400
# Construimos la llamada al script:
cmd = ['sudo', 'python3', f"{script_path}/exportImage.py", image_file_path, remote_ip, remote_user]
try:
# Ejecutamos el script "exportImage.py" (con los parámetros almacenados), y almacenamos el resultado (pero sin esperar a que termine el proceso):
result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Generamos el ID para identificar el trabajo asíncrono:
job_id = f"ExportImage_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode is None:
# Si el resultado es correcto, llamamos a la función "check_lock_remote" en un hilo paralelo
# (para que compruebe si la imagen se ha acabado de exportar exitosamente):
threading.Thread(target=check_lock_remote, args=(f"{repo_path}{image_file_path}", remote_ip, remote_user, job_id,)).start()
# Informamos que la imagen se está exportando, y salimos del endpoint:
return jsonify({
"success": True,
"output": "Exporting image...",
"job_id": job_id
}), 200
else:
return jsonify({
"success": False,
"error": "Export image failed"
}), 500
except subprocess.CalledProcessError as error:
return jsonify({
"success": False,
"process exception": str(error)
}), 500
except Exception as error_description:
if "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" (ASINCRONO):
@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.
"""
# Almacenamos los parámetros enviados en el JSON:
json_data = json.loads(request.data)
image_name = json_data.get("image")
# Si la imagen no existe, retornamos un error y salimos del endpoint:
if not os.path.exists(f"{repo_path}{image_name}"):
return jsonify({
"success": False,
"exception": "Image not found"
}), 400
# Construimos la ruta de la imagen (relativa a "repo_path"):
image_file_path = image_name
# Construimos la llamada al script:
cmd = ['sudo', 'python3', f"{script_path}/createTorrentSum.py", image_file_path]
try:
# Ejecutamos el script "createTorrentSum.py" (con los parámetros almacenados), y almacenamos el resultado (pero sin esperar a que termine el proceso):
result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Generamos el ID para identificar el trabajo asíncrono:
job_id = f"CreateAuxiliarFiles_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode is None:
# Si el resultado es correcto, llamamos a la función "check_aux_files" en un hilo paralelo
# (para que compruebe si se han creado todos los archivos auxiliares exitosamente):
threading.Thread(target=check_aux_files, args=(f"{repo_path}{image_file_path}", job_id,)).start()
# Informamos que los archivos auxiliares se están creando, y salimos del endpoint:
return jsonify({
"success": True,
"output": "Creating auxiliar files...",
"job_id": job_id
}), 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" (SINCRONO):
@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" (ASINCRONO):
@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:
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 (pero sin esperar a que termine el proceso):
result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Comprobamos si está corriendo el proceso correspondiente de "udp-sender" (esperando 5 segundos para darle tiempo a iniciarse):
sleep(5)
process_running = search_process('udp-sender', f"{param_dict['name']}.{param_dict['extension']}")
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode is None and process_running == True:
return jsonify({
"success": True,
"output": "Sending image..."
}), 200
else:
return jsonify({
"success": False,
"error": "Image send failed"
}), 500
except subprocess.CalledProcessError as error:
return jsonify({
"success": False,
"process exeption": str(error)
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 14 - Endpoint "Enviar una Imagen mediante UFTP" (ASINCRONO):
@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:
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 (pero sin esperar a que termine el proceso):
result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# Comprobamos si está corriendo el proceso correspondiente de "uftp" (esperando 5 segundos para darle tiempo a iniciarse):
sleep(5)
process_running = search_process('uftp', f"{param_dict['name']}.{param_dict['extension']}")
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode is None and process_running == True:
return jsonify({
"success": True,
"output": "Sending image..."
}), 200
else:
return jsonify({
"success": False,
"error": "Image send failed"
}), 500
except subprocess.CalledProcessError as error:
return jsonify({
"success": False,
"process exeption": str(error)
}), 500
except Exception as error_description:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 15 - Endpoint "Enviar una Imagen mediante P2P" (ASINCRONO):
@app.route("/ogrepository/v1/p2p", methods=['POST'])
def send_p2p():
""" Este endpoint inicia el tracker "bttrack" y el seeder "bittornado", en el directorio de imágenes (sirviendo todas las imágenes).
Para ello, ejecuta los scripts "runTorrentTracker.py" y "runTorrentSeeder.py", que no reciben parámetros.
"""
# 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 las llamadas a los scripts, o para devover un error si no se ha encontrado la imagen:
if param_dict:
cmd_tracker = ['sudo', 'python3', f"{script_path}/runTorrentTracker.py"]
cmd_seeder = ['sudo', 'python3', f"{script_path}/runTorrentSeeder.py"]
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", que no reciben parámetros.
# 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" (SINCRONO):
@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
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 17 - Endpoint "Ver Estado de Transmisiones UFTP" (SINCRONO):
@app.route("/ogrepository/v1/uftp", methods=['GET'])
def get_uftp_info():
""" Este endpoint devuelve información sobre los procesos de "uftp" activos, en formato json,
lo que en la práctica permite comprobar las transferencias UFTP activas.
Para ello, ejecuta el script "getUFTPInfo.py", que no recibe parámetros.
"""
try:
# Ejecutamos el script "getUFTPInfo.py", y almacenamos el resultado:
result = subprocess.run(['sudo', 'python3', f"{script_path}/getUFTPInfo.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 UFTP active transmissions"
}), 400
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 18 - Endpoint "Cancelar Transmisión UDPcast" (SINCRONO):
@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.
"""
# Obtenemos el nombre y la extensión de la imagen:
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:
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
# ---------------------------------------------------------
# 19 - Endpoint "Cancelar Transmisión UFTP" (SINCRONO):
@app.route("/ogrepository/v1/uftp/images/<string:imageId>", methods=['DELETE'])
def stop_uftp(imageId):
""" Este endpoint cancela la transmisión UFTP de la imagen que recibe como parámetro, finalizando el proceso "uftp" asociado.
Para ello, ejecuta el script "stopUFTP.py", pasándole el nombre de la imagen.
"""
# 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:
cmd = ['sudo', 'python3', f"{script_path}/stopUFTP.py", f"{param_dict['name']}.{param_dict['extension']}"]
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 400
try:
# Ejecutamos el script "stopUFTP.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 UFTP active transmissions for specified image"
}), 400
elif "exit status 4" in str(error_description):
return jsonify({
"success": False,
"exception": "Unexpected error checking UFTP transmissions"
}), 500
elif "exit status 5" in str(error_description):
return jsonify({
"success": False,
"exception": "Unexpected error finalizing UFTP transmission"
}), 500
else:
return jsonify({
"success": False,
"exception": str(error_description)
}), 500
# ---------------------------------------------------------
# 20 - Endpoint "Cancelar Transmisiones P2P" (SINCRONO):
@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)
# --------------------------------------------------------------------------------------------