refs #631 - Modify API and Swagger

pull/1/head
Gerardo GIl Elizeire 2024-10-31 17:25:43 +01:00
parent 43846b0382
commit e7c5de8628
2 changed files with 296 additions and 133 deletions

View File

@ -8,7 +8,10 @@ Responde a peticiones HTTP (en principio, enviadas desde ogCore) mediante endpoi
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").
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")
"""
# --------------------------------------------------------------------------------------------
@ -20,8 +23,15 @@ import os
import subprocess
import json
from time import sleep
import paramiko
import logging
import threading
import requests
# Imports para Swagger:
from flasgger import Swagger
import yaml
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
@ -31,6 +41,12 @@ script_path = '/opt/opengnsys/bin'
repo_file = '/opt/opengnsys/etc/repoinfo.json'
trash_file = '/opt/opengnsys/etc/trashinfo.json'
"""
repo_path = '/home/user/images/'
script_path = '/home/user'
repo_file = '/home/user/jsons/repoinfo.json'
trash_file = '/home/user/jsons/trashinfo.json'
"""
# --------------------------------------------------------------------------------------------
# FUNCTIONS
@ -40,9 +56,14 @@ trash_file = '/opt/opengnsys/etc/trashinfo.json'
# Creamos una instancia de la aplicación Flask:
app = Flask(__name__)
# Configuración Swagger:
with open("swagger.yaml", "r") as file:
swagger_template = yaml.safe_load(file)
swagger = Swagger(app, template=swagger_template)
# ---------------------------------------------------------
@ -123,13 +144,104 @@ def search_process(process, string_to_search):
print(f"Unexpected error: {error_description}")
# ---------------------------------------------------------
def check_lock_local(image_file_path):
""" 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, y salimos del bucle:
if not os.path.exists(f"{image_file_path}.lock"):
app.logger.info("Task finalized (no .lock file)") # De momento solamente imprimimos un mensaje en la API (pero debe llamar a un endpoint)
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):
""" 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, y salimos del bucle:
app.logger.info("Task finalized (no .lock file)") # De momento solamente imprimimos un mensaje en la API (pero debe llamar a un endpoint)
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):
""" 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, y salimos del bucle:
else:
app.logger.info("Task finalized (all auxilar files created)") # De momento solamente imprimimos un mensaje en la API (pero debe llamar a un endpoint)
# 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"Image_ID: {image_id}")
break
# Esperamos 10 segundos para volver a realizar la comprobación:
sleep(10)
# --------------------------------------------------------------------------------------------
# ENDPOINTS
# --------------------------------------------------------------------------------------------
# 1 - Endpoint "Obtener Información de Estado de ogRepository":
# 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.
@ -160,7 +272,7 @@ def get_repo_status():
# ---------------------------------------------------------
# 2 - Endpoint "Obtener Información de todas las Imágenes":
# 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.
@ -191,7 +303,7 @@ def get_repo_info():
# ---------------------------------------------------------
# 3 - Endpoint "Obtener Información de una Imagen concreta":
# 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.
@ -238,7 +350,7 @@ def get_repo_image_info(imageId):
# ---------------------------------------------------------
# 4 - Endpoint "Actualizar Información del Repositorio":
# 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".
@ -269,7 +381,7 @@ def update_repo_info():
# ---------------------------------------------------------
# 5 - Endpoint "Chequear Integridad de Imagen":
# 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,
@ -323,7 +435,7 @@ def check_image(imageId):
# ---------------------------------------------------------
# 6 - Endpoint "Eliminar una Imagen":
# 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),
@ -379,7 +491,7 @@ def delete_image(imageId):
# ---------------------------------------------------------
# 7 - Endpoint "Recuperar una Imagen":
# 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),
@ -431,7 +543,7 @@ def recover_image():
# ---------------------------------------------------------
# 8 - Endpoint "Eliminar una Imagen de la Papelera":
# 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.
@ -478,7 +590,7 @@ def delete_trash_image(imageId):
# ---------------------------------------------------------
# 9 - Endpoint "Importar una Imagen":
# 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),
@ -493,26 +605,39 @@ def import_image():
remote_ip = json_data.get("repo_ip")
remote_user = json_data.get("user")
# Evaluamos los parámetros obtenidos, para construir la llamada al script:
# Evaluamos los parámetros obtenidos, para construir la ruta de la imagen:
if ou_subdir == "none":
cmd = ['sudo', 'python3', f"{script_path}/importImage.py", f"{repo_path}{image_name}", remote_ip, remote_user]
image_file_path = f"{repo_path}{image_name}"
else:
cmd = ['sudo', 'python3', f"{script_path}/importImage.py", f"{repo_path}{ou_subdir}/{image_name}", remote_ip, remote_user]
image_file_path = f"{repo_path}{ou_subdir}/{image_name}"
# 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:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# 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')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
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,)).start()
# Informamos que la imagen se está importando, y salimos del endpoint:
return jsonify({
"success": True,
"output": "Image imported successfully"
"output": "Importing image..."
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
"error": "Image import failed"
}), 500
except subprocess.CalledProcessError as error:
return jsonify({
"success": False,
"process exception": str(error)
}), 500
except Exception as error_description:
if "exit status 2" in str(error_description):
@ -540,7 +665,7 @@ def import_image():
# ---------------------------------------------------------
# 10 - Endpoint "Exportar una Imagen":
# 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),
@ -557,33 +682,46 @@ def export_image():
# 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:
# Evaluamos los parámetros obtenidos, para construir la ruta de la imagen, 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]
image_file_path = f"{param_dict['subdir']}/{param_dict['name']}.{param_dict['extension']}"
else:
cmd = ['sudo', 'python3', f"{script_path}/exportImage.py", f"{param_dict['name']}.{param_dict['extension']}", remote_ip, remote_user]
image_file_path = f"{param_dict['name']}.{param_dict['extension']}"
else:
return jsonify({
"success": False,
"error": "Image not found"
}), 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:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# 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')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
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,)).start()
# Informamos que la imagen se está exportando, y salimos del endpoint:
return jsonify({
"success": True,
"output": "Image exported successfully"
"output": "Exporting image"
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
}), 500
except subprocess.CalledProcessError as error:
return jsonify({
"success": False,
"process exception": str(error)
}), 500
except Exception as error_description:
if "exit status 3" in str(error_description):
return jsonify({
@ -610,7 +748,7 @@ def export_image():
# ---------------------------------------------------------
# 11 - Endpoint "Crear archivos auxiliares":
# 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.
@ -621,21 +759,29 @@ def create_torrent_sum():
image_name = json_data.get("image")
ou_subdir = json_data.get("ou_subdir")
# Evaluamos los parámetros obtenidos, para construir la llamada al script:
# Evaluamos los parámetros obtenidos, para construir la ruta de la imagen (relativa a "repo_path"):
if ou_subdir == "none":
cmd = ['sudo', 'python3', f"{script_path}/createTorrentSum.py", image_name]
image_file_path = image_name
else:
cmd = ['sudo', 'python3', f"{script_path}/createTorrentSum.py", f"{ou_subdir}/{image_name}"]
image_file_path = f"{ou_subdir}/{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:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='UTF8')
# 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')
# Evaluamos el resultado de la ejecución, y devolvemos una respuesta:
if result.returncode == 0:
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}",)).start()
# Informamos que los archivos auxiliares se están creando, y salimos del endpoint:
return jsonify({
"success": True,
"output": "Files created successfully"
"output": "Creating auxiliar files..."
}), 200
else:
return jsonify({
@ -663,7 +809,7 @@ def create_torrent_sum():
# ---------------------------------------------------------
# 12 - Endpoint "Enviar paquete Wake On Lan":
# 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.
@ -699,7 +845,7 @@ def send_wakeonlan():
# ---------------------------------------------------------
# 13 - Endpoint "Enviar una Imagen mediante UDPcast":
# 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.
@ -731,19 +877,28 @@ def send_udpcast():
}), 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')
# 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 == 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"
if result.returncode is None and process_running == True:
return jsonify({
"success": True,
"output": "Sending image..."
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
"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({
@ -755,7 +910,7 @@ def send_udpcast():
# ---------------------------------------------------------
# 14 - Endpoint "Enviar una Imagen mediante UFTP":
# 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.
@ -785,19 +940,28 @@ def send_uftp():
}), 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')
# 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 == 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"").
if result.returncode is None and process_running == True:
return jsonify({
"success": True,
"output": "Image sended successfully"
"output": "Sending image..."
}), 200
else:
return jsonify({
"success": False,
"error": result.stderr
"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({
@ -809,7 +973,7 @@ def send_uftp():
# ---------------------------------------------------------
# 15 - Endpoint "Enviar una Imagen mediante P2P":
# 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 en el que esté situada la imagen que recibe como parámetro.
@ -866,7 +1030,7 @@ def send_p2p():
# ---------------------------------------------------------
# 16 - Endpoint "Ver Estado de Transmisiones UDPcast":
# 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,
@ -909,7 +1073,7 @@ def get_udpcast_info():
# ---------------------------------------------------------
# 17 - Endpoint "Ver Estado de Transmisiones UFTP":
# 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,
@ -952,7 +1116,7 @@ def get_uftp_info():
# ---------------------------------------------------------
# 18 - Endpoint "Cancelar Transmisión UDPcast":
# 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.
@ -1014,7 +1178,7 @@ def stop_udpcast(imageId):
# ---------------------------------------------------------
# 19 - Endpoint "Cancelar Transmisión UFTP":
# 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.
@ -1076,7 +1240,7 @@ def stop_uftp(imageId):
# ---------------------------------------------------------
# 20 - Endpoint "Cancelar Transmisiones P2P":
# 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).

View File

@ -1,6 +1,6 @@
swagger: "2.0"
info:
title: "Ogrepository API"
title: "OgRepository API"
version: "1.0"
description: |
API de ogRepository, programada en Flask.
@ -21,7 +21,7 @@ paths:
tags:
- "Estado de ogRepository"
responses:
200:
"200":
description: "La información de estado se obtuvo exitosamente."
schema:
type: object
@ -95,7 +95,7 @@ paths:
btlaunchmany:
type: string
example: "stopped"
500:
"500":
description: "Error al consultar y/o devolver la información de estado."
schema:
type: object
@ -118,7 +118,7 @@ paths:
tags:
- "Imágenes de Repositorio"
responses:
200:
"200":
description: "La información de las imágenes se obtuvo exitosamente."
schema:
type: object
@ -266,7 +266,7 @@ paths:
fullsum:
type: string
example: "22735b9070e4a8043371b8c6ae52b90d"
500:
"500":
description: "Error al consultar y/o devolver la información de las imágenes."
schema:
type: object
@ -287,7 +287,7 @@ paths:
tags:
- "Imágenes de Repositorio"
responses:
200:
"200":
description: "La actualización de la información del repositorio se realizó exitosamente."
schema:
type: object
@ -298,7 +298,7 @@ paths:
output:
type: string
example: "Repository info updated successfully"
500:
"500":
description: "Error al actualizar la información de las imágenes."
schema:
type: object
@ -326,7 +326,7 @@ paths:
type: string
description: "El ID de la imagen (corresponde al hash MD5 'fullsum' de la imagen)"
responses:
200:
"200":
description: "La información de la imagen se obtuvo exitosamente."
schema:
type: object
@ -375,7 +375,7 @@ paths:
fullsum:
type: string
example: "33575b9070e4a8043371b8c6ae52b80e"
400:
"400":
description: "No se ha encontrado la imagen especificada."
schema:
type: object
@ -386,7 +386,7 @@ paths:
error:
type: string
example: "Image not found"
500:
"500":
description: "Error al consultar y/o devolver la información de la imagen."
schema:
type: object
@ -418,7 +418,7 @@ paths:
type: string
description: "Método de eliminación (puede ser 'trash' para enviar a la papelera o 'permanent' para eliminar definitivamente)"
responses:
200:
"200":
description: "La imagen se eliminó exitosamente."
schema:
type: object
@ -429,7 +429,7 @@ paths:
output:
type: string
example: "Image deleted successfully"
400:
"400":
description: "No se ha encontrado la imagen especificada."
schema:
type: object
@ -440,7 +440,7 @@ paths:
error:
type: string
example: "Image not found (inexistent or deleted)"
500:
"500":
description: >
Error al eliminar la imagen. Puede ocurrir debido a:
- Un error de ejecución del script (con salida no 0).
@ -454,7 +454,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la eliminación de la imagen."
schema:
type: object
@ -481,7 +481,7 @@ paths:
type: string
description: "El ID de la imagen (corresponde al hash MD5 'fullsum' de la imagen)"
responses:
200:
"200":
description: >
La imagen se ha chequeado exitosamente. El chequeo puede devolver dos resultados:
- Si pasa la verificación de integridad.
@ -495,7 +495,7 @@ paths:
output:
type: string
example: "Image file passed the Integrity Check correctly"
400:
"400":
description: "No se ha encontrado la imagen especificada."
schema:
type: object
@ -506,7 +506,7 @@ paths:
error:
type: string
example: "Image not found (inexistent or deleted)"
500:
"500":
description: >
Error al chequear la imagen. Puede ocurrir debido a una excepción inesperada.
schema:
@ -518,7 +518,7 @@ paths:
exception:
type: string
example: "Generic error description for unexpected exceptions."
200 (KO):
"200 (KO)":
description: "La imagen se ha chequeado correctamente, pero no ha pasado el test de integridad."
schema:
type: object
@ -550,7 +550,7 @@ paths:
type: string
example: "image_id"
responses:
200:
"200":
description: "La imagen se recuperó exitosamente."
schema:
type: object
@ -561,7 +561,7 @@ paths:
output:
type: string
example: "Image recovered successfully"
400:
"400":
description: "No se ha encontrado la imagen especificada."
schema:
type: object
@ -572,7 +572,7 @@ paths:
error:
type: string
example: "Image not found (inexistent or recovered previously)"
500:
"500":
description: >
Error al recuperar la imagen. Puede ocurrir debido a:
- Un error de ejecución del script (con salida no 0).
@ -586,7 +586,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la recuperación de la imagen."
schema:
type: object
@ -613,7 +613,7 @@ paths:
type: string
description: "El ID de la imagen (corresponde al hash MD5 'fullsum' de la imagen en la papelera)"
responses:
200:
"200":
description: "La imagen se eliminó exitosamente."
schema:
type: object
@ -624,7 +624,7 @@ paths:
output:
type: string
example: "Image deleted successfully"
400:
"400":
description: "No se ha encontrado la imagen especificada en la papelera."
schema:
type: object
@ -635,7 +635,7 @@ paths:
error:
type: string
example: "Image not found at trash"
500:
"500":
description: >
Error al eliminar la imagen desde la papelera. Puede ocurrir debido a:
- Un error de ejecución del script (con salida no 0).
@ -649,7 +649,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la eliminación de la imagen desde la papelera."
schema:
type: object
@ -694,7 +694,7 @@ paths:
description: "Usuario para acceder al repositorio remoto"
example: "user_name"
responses:
200:
"200":
description: "La imagen se ha importado exitosamente."
schema:
type: object
@ -705,7 +705,7 @@ paths:
output:
type: string
example: "Image imported successfully"
400:
"400":
description: "Error de conexión o imagen no disponible en el servidor remoto."
schema:
type: object
@ -716,7 +716,7 @@ paths:
exception:
type: string
example: "Can't connect to remote server | Remote image not found | Remote image is locked"
500:
"500":
description: >
Error interno al importar la imagen. Puede ocurrir debido a un problema en la ejecución del script o una excepción inesperada.
schema:
@ -728,7 +728,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la importación de la imagen."
schema:
type: object
@ -767,7 +767,7 @@ paths:
description: "Usuario para acceder al repositorio remoto"
example: "user_name"
responses:
200:
"200":
description: "La imagen se ha exportado exitosamente."
schema:
type: object
@ -778,7 +778,7 @@ paths:
output:
type: string
example: "Image exported successfully"
400:
"400":
description: "Error de conexión o imagen no disponible en el servidor remoto."
schema:
type: object
@ -789,7 +789,7 @@ paths:
exception:
type: string
example: "Image is locked | Can't connect to remote server | Image already exists on remote server"
500:
"500":
description: >
Error interno al exportar la imagen. Puede ocurrir debido a un problema en la ejecución del script o una excepción inesperada.
schema:
@ -801,7 +801,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la exportación de la imagen."
schema:
type: object
@ -836,7 +836,7 @@ paths:
description: "Subdirectorio correspondiente a la OU (o 'none' si no es el caso)"
example: "none"
responses:
200:
"200":
description: "Los archivos se han creado exitosamente."
schema:
type: object
@ -847,7 +847,7 @@ paths:
output:
type: string
example: "Files created successfully"
400:
"400":
description: "Error de conexión o imagen no disponible en el servidor remoto."
schema:
type: object
@ -858,7 +858,7 @@ paths:
exception:
type: string
example: "Image not found | Image is locked"
500:
"500":
description: >
Error interno al crear los archivos auxiliares. Puede ocurrir debido a un problema en la ejecución del script o una excepción inesperada.
schema:
@ -870,7 +870,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la creación de los archivos auxiliares."
schema:
type: object
@ -906,7 +906,7 @@ paths:
description: "Dirección MAC del equipo a encender"
example: "00:19:99:5c:bb:bb"
responses:
200:
"200":
description: "El paquete Wake On Lan se ha enviado exitosamente."
schema:
type: object
@ -917,7 +917,7 @@ paths:
output:
type: string
example: "Wake On Lan packet sent successfully"
500:
"500":
description: >
Error interno al enviar el paquete Wake On Lan. Puede ocurrir debido a un problema en la ejecución del script o una excepción inesperada.
schema:
@ -929,7 +929,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante el envío del paquete Wake On Lan."
schema:
type: object
@ -950,7 +950,7 @@ paths:
tags:
- "Transferencia de Imágenes"
responses:
200:
"200":
description: "La información de las transmisiones UDPcast activas se obtuvo exitosamente."
schema:
type: object
@ -969,7 +969,7 @@ paths:
image_name:
type: string
example: "Ubuntu20.img"
400:
"400":
description: "No se han encontrado transmisiones UDPcast activas."
schema:
type: object
@ -980,7 +980,7 @@ paths:
exception:
type: string
example: "No UDPCast active transmissions"
500:
"500":
description: >
Error al comprobar las transmisiones UDPcast activas, posiblemente debido a un error inesperado durante la ejecución del script.
schema:
@ -992,7 +992,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la comprobación de transmisiones UDPcast activas."
schema:
type: object
@ -1046,7 +1046,7 @@ paths:
description: "Tiempo máximo de espera en segundos"
example: 120
responses:
200:
"200":
description: "La imagen se ha enviado exitosamente mediante UDPcast."
schema:
type: object
@ -1057,7 +1057,7 @@ paths:
output:
type: string
example: "Image sent successfully"
400:
"400":
description: "No se ha encontrado la imagen especificada."
schema:
type: object
@ -1068,7 +1068,7 @@ paths:
error:
type: string
example: "Image not found"
500:
"500":
description: >
Error interno al enviar la imagen mediante UDPcast. Puede ocurrir debido a un problema en la ejecución del script o una excepción inesperada.
schema:
@ -1080,7 +1080,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante el envío de la imagen mediante UDPcast."
schema:
type: object
@ -1101,7 +1101,7 @@ paths:
tags:
- "Transferencia de Imágenes"
responses:
200:
"200":
description: "La información de las transmisiones UFTP activas se obtuvo exitosamente."
schema:
type: object
@ -1120,7 +1120,7 @@ paths:
image_name:
type: string
example: "Ubuntu20.img"
400:
"400":
description: "No se han encontrado transmisiones UFTP activas."
schema:
type: object
@ -1131,7 +1131,7 @@ paths:
exception:
type: string
example: "No UFTP active transmissions"
500:
"500":
description: >
Error al comprobar las transmisiones UFTP activas, posiblemente debido a un error inesperado durante la ejecución del script.
schema:
@ -1143,7 +1143,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante la comprobación de transmisiones UFTP activas."
schema:
type: object
@ -1186,7 +1186,7 @@ paths:
description: "Velocidad de transmisión (con 'K' para Kbps, 'M' para Mbps, o 'G' para Gbps)"
example: "1G"
responses:
200:
"200":
description: "La imagen se ha enviado exitosamente mediante UFTP."
schema:
type: object
@ -1197,7 +1197,7 @@ paths:
output:
type: string
example: "Image sent successfully"
400:
"400":
description: "No se ha encontrado la imagen especificada."
schema:
type: object
@ -1208,7 +1208,7 @@ paths:
error:
type: string
example: "Image not found"
500:
"500":
description: >
Error interno al enviar la imagen mediante UFTP. Puede ocurrir debido a un problema en la ejecución del script o una excepción inesperada.
schema:
@ -1220,7 +1220,7 @@ paths:
error:
type: string
example: "Script execution error description."
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante el envío de la imagen mediante UFTP."
schema:
type: object
@ -1253,7 +1253,7 @@ paths:
type: string
example: "image_id"
responses:
200:
"200":
description: "La imagen se está enviando exitosamente a través de P2P."
schema:
type: object
@ -1264,7 +1264,7 @@ paths:
output:
type: string
example: "Tracker and Seeder serving image correctly"
400:
"400":
description: "No se ha encontrado la imagen especificada."
schema:
type: object
@ -1275,7 +1275,7 @@ paths:
error:
type: string
example: "Image not found"
500:
"500":
description: >
Error al intentar iniciar el tracker o el seeder para el envío P2P.
Puede ocurrir si alguno de los procesos no se inicia correctamente o si ocurre una excepción inesperada.
@ -1288,7 +1288,7 @@ paths:
error:
type: string
example: "Tracker or Seeder (or both) not running"
500 (Exception):
"500 (Exception)":
description: "Error inesperado durante el proceso de envío P2P."
schema:
type: object
@ -1307,7 +1307,7 @@ paths:
tags:
- "Transferencia de Imágenes"
responses:
200:
"200":
description: "Las transmisiones P2P se han cancelado exitosamente."
schema:
type: object
@ -1318,7 +1318,7 @@ paths:
output:
type: string
example: "P2P transmissions canceled successfully"
500 (Error del script):
"500 (Error del script)":
description: "Error en la ejecución del script durante la cancelación de las transmisiones P2P."
schema:
type: object
@ -1329,7 +1329,7 @@ paths:
error:
type: string
example: "Detailed error message from script stderr output."
500 (Excepción general):
"500 (Excepción general)":
description: "Excepción inesperada durante la cancelación de las transmisiones P2P."
schema:
type: object
@ -1357,7 +1357,7 @@ paths:
description: "Identificador de la imagen (correspondiente al contenido del archivo 'full.sum' asociado)"
example: "image_id"
responses:
200:
"200":
description: "La transmisión UDPcast se ha cancelado exitosamente."
schema:
type: object
@ -1368,7 +1368,7 @@ paths:
output:
type: string
example: "Image transmission canceled successfully"
400:
"400":
description: "No se ha encontrado la imagen especificada o no hay transmisiones UDPcast activas para la imagen especificada."
schema:
type: object
@ -1379,7 +1379,7 @@ paths:
exception:
type: string
example: "No UDPCast active transmissions for specified image"
500 (Error del script):
"500 (Error del script)":
description: "Error en la ejecución del script durante la cancelación de la transmisión."
schema:
type: object
@ -1390,7 +1390,7 @@ paths:
error:
type: string
example: "Detailed error message from script stderr output."
500 (Error en verificación):
"500 (Error en verificación)":
description: "Error inesperado al verificar las transmisiones UDPcast activas."
schema:
type: object
@ -1401,7 +1401,7 @@ paths:
exception:
type: string
example: "Unexpected error checking UDPcast transmissions"
500 (Error en finalización):
"500 (Error en finalización)":
description: "Error inesperado al finalizar la transmisión UDPcast."
schema:
type: object
@ -1412,7 +1412,7 @@ paths:
exception:
type: string
example: "Unexpected error finalizing UDPcast transmission"
500 (Excepción general):
"500 (Excepción general)":
description: "Excepción inesperada durante la cancelación de la transmisión UDPcast."
schema:
type: object
@ -1440,7 +1440,7 @@ paths:
description: "Identificador de la imagen (correspondiente al contenido del archivo 'full.sum' asociado)"
example: "image_id"
responses:
200:
"200":
description: "La transmisión UFTP se ha cancelado exitosamente."
schema:
type: object
@ -1451,7 +1451,7 @@ paths:
output:
type: string
example: "Image transmission canceled successfully"
400:
"400":
description: "No se ha encontrado la imagen especificada o no hay transmisiones UFTP activas para la imagen especificada."
schema:
type: object
@ -1462,7 +1462,7 @@ paths:
exception:
type: string
example: "No UFTP active transmissions for specified image"
500 (Error del script):
"500 (Error del script)":
description: "Error en la ejecución del script durante la cancelación de la transmisión."
schema:
type: object
@ -1473,7 +1473,7 @@ paths:
error:
type: string
example: "Detailed error message from script stderr output."
500 (Error en verificación):
"500 (Error en verificación)":
description: "Error inesperado al verificar las transmisiones UFTP activas."
schema:
type: object
@ -1484,7 +1484,7 @@ paths:
exception:
type: string
example: "Unexpected error checking UFTP transmissions"
500 (Error en finalización):
"500 (Error en finalización)":
description: "Error inesperado al finalizar la transmisión UFTP."
schema:
type: object
@ -1495,7 +1495,7 @@ paths:
exception:
type: string
example: "Unexpected error finalizing UFTP transmission"
500 (Excepción general):
"500 (Excepción general)":
description: "Excepción inesperada durante la cancelación de la transmisión UFTP."
schema:
type: object
@ -1507,4 +1507,3 @@ paths:
type: string
example: "General error description for unexpected exceptions"