1009 lines
44 KiB
Python
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)
|
|
|
|
|
|
# --------------------------------------------------------------------------------------------
|