Compare commits

...

29 Commits

Author SHA1 Message Date
Vadim Trochinsky 813cae1a7e ref #2317 Fix performance problem, remove stats 2025-06-25 15:17:45 +02:00
Vadim Trochinsky 6887e68b22 Better error handling for bad tag names 2025-06-24 16:35:29 +02:00
Vadim Trochinsky ee300b846b Add commit listing 2025-06-23 16:27:35 +02:00
Vadim vtroshchinskiy c403668209 Update changelog 2025-06-17 21:42:29 +00:00
Vadim vtroshchinskiy eb1abb46c3 update dependencies 2025-06-16 23:42:19 +02:00
Vadim vtroshchinskiy f19e1f3c23 Update changelog 2025-06-16 21:25:51 +00:00
Vadim Trochinsky 61b74de93c Add SSH key endpoint 2025-06-16 15:56:28 +02:00
Vadim vtroshchinskiy 682ff22de1 Update changelog
ogrepository/pipeline/head This commit looks good Details
2025-06-06 08:16:09 +00:00
Vadim vtroshchinskiy dab9244a95 Fix path 2025-06-06 10:15:53 +02:00
Vadim vtroshchinskiy 5b525f07fa Update changelog 2025-06-06 08:12:32 +00:00
Vadim vtroshchinskiy 97354a9112 Add another search path for installer module 2025-06-06 10:11:57 +02:00
Vadim vtroshchinskiy ca30277b02 Update changelog 2025-06-06 06:55:25 +00:00
Vadim vtroshchinskiy 3817a359a4 Fix path handling when there are no repositories 2025-06-06 08:54:45 +02:00
Vadim vtroshchinskiy 4734f52aba Update changelog 2025-06-05 20:54:28 +00:00
Vadim vtroshchinskiy 1dba61bf64 Update changelog 2025-06-05 20:54:12 +00:00
Vadim vtroshchinskiy 1775268b04 update changelog 2025-06-05 09:56:22 +02:00
Vadim vtroshchinskiy b343bce98f Fix base path handling 2025-06-05 09:47:59 +02:00
Vadim vtroshchinskiy e3a416006f Package build fixes 2025-06-04 16:48:08 +02:00
Vadim vtroshchinskiy ed2d0a785c Cambiar endpoint de crear tags 2025-05-12 11:50:26 +02:00
Vadim vtroshchinskiy 990d11051e Fix repository already exists error code 2025-05-12 09:15:16 +02:00
Vadim vtroshchinskiy 52fd7d8064 Use annotated tags 2025-05-09 13:33:00 +02:00
Vadim vtroshchinskiy 25877533e8 Swagger fix: use the right name in example 2025-05-09 12:51:39 +02:00
Vadim vtroshchinskiy f2a90ce574 Backup endpoint 2025-05-07 16:27:05 +02:00
Vadim vtroshchinskiy 2c869ea524 Add git repo synchronization 2025-05-07 09:52:06 +02:00
Vadim vtroshchinskiy 9bbd9de285 Fix error logging, wrong constant 2025-05-07 09:18:13 +02:00
Vadim vtroshchinskiy 4e645bd619 Fix installer module loading 2025-05-07 09:18:01 +02:00
Vadim vtroshchinskiy b8e50585cd Add git repository GC function 2025-05-06 10:04:10 +02:00
Vadim vtroshchinskiy 371a440b9a Swagger fixes 2025-04-30 09:50:16 +02:00
Vadim vtroshchinskiy f055aaf69d Git implementation and Swagger 2025-04-28 11:39:19 +02:00
5 changed files with 1333 additions and 54 deletions

View File

@ -2,16 +2,16 @@
# -*- coding: utf-8 -*-
"""
API de ogRepository, programada en Flask.
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).
(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")
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 es necesario instalarlo en Ubuntu 24
- flasgger (se puede instalar con "sudo apt install python3-flasgger")
- flasgger (se puede instalar con "sudo apt install python3-flasgger")
"""
# --------------------------------------------------------------------------------------------
@ -48,6 +48,22 @@ trash_file = '/opt/opengnsys/ogrepository/etc/trashinfo.json'
config_file = '/opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg'
# --------------------------------------------------------------------------------------------
# GIT
# --------------------------------------------------------------------------------------------
REPOSITORIES_BASE_PATH = "/opt/opengnsys/ogrepository/oggit/git/"
OGGIT_USER = "oggit"
import sys
import git
import pkgutil
import importlib
import paramiko
# --------------------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------------------
@ -73,9 +89,9 @@ 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).
"""
"""
journal.send("Running function 'get_IPcore'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
IPcore = None
with open(config_file, 'r') as file:
for line in file:
@ -327,7 +343,7 @@ def check_remote_backup(image_name, remote_ip, remote_user, remote_path, job_id)
# Creamos un bucle infinito:
while True:
# Comprobamos si ya se ha copiado la imagen y cada uno de sus archivos asociados (y almacenamos el resultado):
try:
try:
for ext in extensions:
sftp_client.stat(f"{remote_path}{image_name}{ext}")
all_files_copied = True
@ -601,7 +617,7 @@ def recall_ogcore(data):
def check_file_exists(file_path):
""" Comprueba la existencia del archivo cuya ruta recibe como parámetro.
Si el archivo existe devuelve "True", y si no devuelve "False".
Si el archivo existe devuelve "True", y si no devuelve "False".
"""
journal.send("Running function 'check_file_exists'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -671,7 +687,7 @@ def get_repo_status():
journal.send("Running script 'getRepoStatus.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Ejecutamos el script "getRepoStatus.py", y almacenamos el resultado:
result = subprocess.run(['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:
journal.send("Script 'getRepoStatus.py' result OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -973,7 +989,7 @@ def recover_image():
# Almacenamos el parámetro "ID_img" (enviado por JSON):
json_data = json.loads(request.data)
image_id = json_data.get("ID_img")
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")
@ -1136,7 +1152,7 @@ def import_image():
if result.returncode is None:
journal.send("Script 'importImage.py' result OK (ReturnCode: None)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'INFO', 'http_code':'200', 'operation':'Run script importImage.py', 'desc':'Result OK (ReturnCode: None)'}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
# 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):
journal.send("Calling function 'check_lock_local'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -1179,7 +1195,7 @@ def import_image():
def backup_image():
""" Este endpoint exporta la imagen especificada como primer parámetro (y todos sus archivos asociados), desde el servidor local a un equipo remoto (que no tiene por qué ser un repositorio).
Para ello, ejecuta el script "backupImage.py", con el nombre de la imagen como primer parámetro, la IP o hostname del equipo remoto como segundo parámetro,
el usuario con el que conectar al equipo remoto como tercer parámetro, y la ruta remota en la que copiar la imagen como cuarto parámetro.
el usuario con el que conectar al equipo remoto como tercer parámetro, y la ruta remota en la que copiar la imagen como cuarto parámetro.
"""
journal.send("Running endpoint 'Hacer backup de una Imagen'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -1260,7 +1276,7 @@ def backup_image():
}), 200
else:
journal.send("Script 'backupImage.py' result KO (Backup image failed)", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script backupImage.py', 'desc':'Result KO (Backup image failed)'}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
journal.send("{'component':'ogRepo', 'severity':'ERROR', 'http_code':'500', 'operation':'Run script backupImage.py', 'desc':'Result KO (Backup image failed)'}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
return jsonify({
"success": False,
"error": "Backup image failed"
@ -1608,7 +1624,7 @@ def send_p2p():
"success": False,
"error": "Tracker or Seeder (or both) not running"
}), 500
# ---------------------------------------------------------
@ -1875,7 +1891,7 @@ def stop_p2p():
journal.send("Running script 'stopP2P.py'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Ejecutamos el script "stopP2P.py", y almacenamos el resultado (este script si que requiere ser ejecutado con "sudo"):
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:
journal.send("Script 'stopP2P.py' result OK (ReturnCode: 0)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -1909,7 +1925,7 @@ def convert_virtual_image():
""" Este endpoint convierte la imagen virtual especificada como parámetro en una imagen "img" como las que se generan desde OpenGnsys
(con "partclone" y "lzop"), por lo que luego puede ser restaurada como cualquier otra imagen del repositorio.
Para ello, ejecuta el script "convertVMtoIMG.py", con el nombre de la imagen virtual como primer parámetro,
y el sistema de archivos de la partición a clonar (en formato "blkid") como segundo parámetro.
y el sistema de archivos de la partición a clonar (en formato "blkid") como segundo parámetro.
"""
journal.send("Running endpoint 'Convertir imagen virtual a imagen OpenGnsys'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -1972,7 +1988,7 @@ def convert_virtual_image():
if result.returncode is None:
journal.send("Script 'convertVMtoIMG.py' result OK (ReturnCode: None)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'INFO', 'http_code':'200', 'operation':'Run script convertVMtoIMG.py', 'desc':'Result OK (ReturnCode: None)'}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
# Si el resultado es correcto, llamamos a la función "check_virtual_image_conversion" en un hilo paralelo
# (para que compruebe si la imagen se ha acabado de convertir exitosamente):
journal.send("Calling function 'check_virtual_image_conversion'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -2015,7 +2031,7 @@ def convert_virtual_image():
def convert_image_to_virtual():
""" Este endpoint convierte la imagen de OpenGnsys especificada como parámetro en una imagen virtual con la extensión especificada (".vdi", ".vmdk", etc).
Para ello, ejecuta el script "convertIMGtoVM.py", con el nombre de la imagen "img" como primer parámetro,
y la extensión del disco virtual destino (sin punto) como segundo parámetro.
y la extensión del disco virtual destino (sin punto) como segundo parámetro.
"""
journal.send("Running endpoint 'Convertir imagen OpenGnsys a imagen virtual'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -2077,7 +2093,7 @@ def convert_image_to_virtual():
if result.returncode is None:
journal.send("Script 'convertIMGtoVM.py' result OK (ReturnCode: None)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
journal.send("{'component':'ogRepo', 'severity':'INFO', 'http_code':'200', 'operation':'Run script convertIMGtoVM.py', 'desc':'Result OK (ReturnCode: None)'}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
# Si el resultado es correcto, llamamos a la función "check_virtual_image_reconversion" en un hilo paralelo
# (para que compruebe si la imagen se ha acabado de convertir exitosamente):
journal.send("Calling function 'check_virtual_image_reconversion'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -2119,7 +2135,7 @@ def convert_image_to_virtual():
@app.route("/ogrepository/v1/images/rename", methods=['PUT'])
def rename_image():
""" Este endpoint renombra la imagen especificada como primer parámetro (y todos sus archivos asociados), asignando el nombre especificado como segundo parámetro.
Para ello, ejecuta el script "renameImage.py", con el nombre original de la imagen como primer parámetro, y el nuevo nombre a asignar como segundo parámetro.
Para ello, ejecuta el script "renameImage.py", con el nombre original de la imagen como primer parámetro, y el nuevo nombre a asignar como segundo parámetro.
"""
journal.send("Running endpoint 'Renombrar una Imagen'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -2185,6 +2201,720 @@ def rename_image():
# -----------------------------------------------------------
# ____ _ _
# / ___(_) |_
# | | _| | __|
# | |_| | | |_
# \____|_|\__|
#
# -----------------------------------------------------------
def git_compact_repository_task(repo, job_id):
journal.send("Running function 'git_compact_repository_task'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
git_repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
git_repo = git.Repo(git_repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path)
git_repo.git.gc()
data = {
'job_id': job_id,
'success': True
}
journal.send(f"Calling function 'recall_ogcore' (JOB_ID: {job_id}, SUCCESS: True)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
recall_ogcore(data)
def git_sync_repository_task(repo, remote_repository, job_id):
journal.send("Running function 'git_sync_repository_task'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
git_repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
git_repo = git.Repo(git_repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path)
# Recreate the remote every time, it might change
if "backup" in git_repo.remotes:
git_repo.delete_remote("backup")
backup_repo = git_repo.create_remote("backup", remote_repository)
pushed_references = backup_repo.push("*:*")
results = []
# This gets returned to the API
for ref in pushed_references:
results = results + [ {"local_ref" : ref.local_ref.name, "remote_ref" : ref.remote_ref.name, "summary" : ref.summary }]
data = {
'job_id': job_id,
'success': True,
'updated_references' : results
}
journal.send(f"Calling function 'recall_ogcore' (JOB_ID: {job_id}, SUCCESS: True)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
recall_ogcore(data)
def git_backup_repository_task(repo, params, job_id):
journal.send("Running function 'git_sync_repository_task'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
git_repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
git_repo = git.Repo(git_repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(params["ssh_server"], params["ssh_port"], params["ssh_user"])
sftp = ssh.open_sftp()
with sftp.file(params["filename"], mode='wb+') as remote_file:
git_repo.archive(remote_file, format="tar.gz")
data = {
'job_id': job_id,
'success': True,
}
journal.send(f"Calling function 'recall_ogcore' (JOB_ID: {job_id}, SUCCESS: True)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
recall_ogcore(data)
def git_add_sshkey_task(oglive, description, job_id):
module = _load_installer()
print(f"Got {module}")
OpengnsysGitInstaller = getattr(module, 'OpengnsysGitInstaller')
installer = OpengnsysGitInstaller()
results = installer.add_ssh_key_from_squashfs(oglive_file = oglive)
keys_added = 0
keys_existed = 0
keys_failed = 0
for status, message in results:
if status == 200 or status == 201:
keys_added = keys_added + 1
elif status == 422:
keys_existed = keys_existed + 1
else:
keys_failed = keys_failed + 1
journal.send(f"Unrecognized reply from forgejo: code {status}, content {message}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
data = {
'job_id': job_id,
'keys_added' : keys_added,
'keys_failed' : keys_failed,
'keys_existed' : keys_existed,
'output' : message
}
journal.send(f"Calling function 'recall_ogcore' (JOB_ID: {job_id}, SUCCESS: True)", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
recall_ogcore(data)
@app.route("/ogrepository/v1/git/repositories", methods=['GET'])
def git_list_repositories():
"""
Retrieve a list of Git repositories.
This endpoint scans the OpenGnsys image path for directories that
appear to be Git repositories (i.e., they contain a "HEAD" file).
It returns a JSON response containing the names of these repositories.
Returns:
Response: A JSON response with a list of repository names or an
error message if the repository storage is not found.
- 200 OK: When the repositories are successfully retrieved.
- 500 Internal Server Error: When the repository storage is not found.
Example JSON response:
{
"repositories": ["repo1", "repo2"]
}
"""
journal.send("Running endpoint 'Obtener Información de todos los repositorios Git'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
if not os.path.isdir(REPOSITORIES_BASE_PATH):
journal.send(f"Can't list repositories. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository storage not found, git functionality may not be installed."}), 500
repos = []
if os.path.exists(os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER)):
# If the base path exists, but the OGGIT_USER subpath doesn't, it means we've got an empty
# install. OgGit is present, but there's no repos yet.
for entry in os.scandir(os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER)):
if entry.is_dir(follow_symlinks=False) and os.path.isfile(os.path.join(entry.path, "HEAD")):
name = entry.name
if name.endswith(".git"):
name = name[:-4]
repos = repos + [name]
journal.send(f"Returning {len(repos)} repositories", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({
"repositories": repos
}), 200
def _load_module(module_name):
# module = importlib.util.find_spec(module_name)
module = importlib.import_module(module_name)
if module is not None:
journal.send(f"Module {module_name} loaded successfully. Got {module}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="oggit-api_DEBUG")
return module
journal.send(f"Module {module_name} failed to load, not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="oggit-api_DEBUG")
return False
def _load_installer():
journal.send(f"Loading oggit installer module", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="oggit-api_DEBUG")
script_dir = os.path.dirname(os.path.realpath(__file__))
system_bin_path = '/opt/opengnsys/ogrepository/oggit/bin/'
system_lib_path = '/opt/opengnsys/ogrepository/oggit/lib/'
devel_lib_path = os.path.join(script_dir, "../../oggit/installer")
if devel_lib_path not in sys.path and os.path.isdir(devel_lib_path):
journal.send(f"Using {devel_lib_path} development library path", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="oggit-api_DEBUG")
sys.path.append(devel_lib_path)
if system_bin_path not in sys.path:
journal.send(f"Using {system_bin_path} system library path", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="oggit-api_DEBUG")
sys.path.append(system_bin_path)
if system_lib_path not in sys.path:
journal.send(f"Using {system_lib_path} system library path", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="oggit-api_DEBUG")
sys.path.append(system_lib_path)
return _load_module("opengnsys_git_installer")
@app.route("/ogrepository/v1/git/repositories", methods=['POST'])
def git_create_repository():
"""
Create a new Git repository.
This endpoint creates a new Git repository with the specified name.
If the repository already exists, it returns a status message indicating so.
Args:
repo (str): The name of the repository to be created.
Returns:
Response: A JSON response with a status message and HTTP status code.
- 409: If the repository already exists.
- 201: If the repository is successfully created.
"""
data = request.json
if data is None:
journal.send(f"Can't create repository, JSON post data missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
repo = data["name"]
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if os.path.isdir(repo_path):
journal.send(f"Can't create repository {repo}, already exists at {repo_path}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"status": "Repository already exists"}), 409
module = _load_installer()
print(f"Got {module}")
OpengnsysGitInstaller = getattr(module, 'OpengnsysGitInstaller')
installer = OpengnsysGitInstaller()
installer.add_forgejo_repo(repo)
journal.send(f"Repository {repo} created", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"status": "Repository created"}), 201
@app.route("/ogrepository/v1/git/repositories", methods=['DELETE'])
def git_delete_repository():
return jsonify({"error" : "Not implemented"}), 500
@app.route("/ogrepository/v1/git/repositories/<string:repo>/sync", methods=['POST'])
def git_sync_repository(repo):
journal.send("Running endpoint 'Sincronizar repositorio Git'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
if not os.path.isdir(REPOSITORIES_BASE_PATH):
journal.send(f"Can't sync repositories. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository storage not found, git functionality may not be installed."}), 500
data = request.json
if data is None:
journal.send(f"Can't sync repository, JSON post data missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if "remote_repository" in data:
remote_repository = data["remote_repository"]
else:
journal.send(f"Can't sync repository, JSON remote_repository parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
job_id = f"GitSync_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
threading.Thread(target=git_sync_repository_task, args=(repo, remote_repository, job_id,)).start()
return jsonify({
"success": True,
"output": "Synchronizing...",
"job_id": job_id
}), 200
@app.route("/ogrepository/v1/git/repositories/<string:repo>/backup", methods=['POST'])
def git_backup_repository(repo):
journal.send("Running endpoint 'Backup de repositorio Git'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
if not os.path.isdir(REPOSITORIES_BASE_PATH):
journal.send(f"Can't backup repositories. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository storage not found, git functionality may not be installed."}), 500
data = request.json
if data is None:
journal.send(f"Can't backup repository, JSON post data missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if not "ssh_server" in data:
journal.send(f"Can't sync repository, JSON ssh_server parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if not "ssh_port" in data:
journal.send(f"Can't sync repository, JSON ssh_port parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if not "ssh_user" in data:
journal.send(f"Can't sync repository, JSON ssh_user parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if not "filename" in data:
journal.send(f"Can't sync repository, JSON filename parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
job_id = f"GitBackup_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
threading.Thread(target=git_backup_repository_task, args=(repo, data, job_id,)).start()
return jsonify({
"success": True,
"output": "Backing up...",
"job_id": job_id
}), 200
@app.route("/ogrepository/v1/git/repositories/<string:repo>/compact", methods=['POST'])
def git_compact_repository(repo):
journal.send("Running endpoint 'Compactar repositorio Git'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
if not os.path.isdir(REPOSITORIES_BASE_PATH):
journal.send(f"Can't list repositories. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository storage not found, git functionality may not be installed."}), 500
job_id = f"GitGC_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
threading.Thread(target=git_compact_repository_task, args=(repo, job_id,)).start()
return jsonify({
"success": True,
"output": "Compacting...",
"job_id": job_id
}), 200
@app.route("/ogrepository/v1/git/repositories/<string:repo>/branches", methods=['GET'])
def git_get_branches(repo):
"""
Retrieve the list of branches for a given repository.
Args:
repo (str): The name of the repository.
Returns:
Response: A JSON response containing a list of branch names or an error message if the repository is not found.
- 200: A JSON object with a "branches" key containing a list of branch names.
- 404: A JSON object with an "error" key containing the message "Repository not found" if the repository does not exist.
"""
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if not os.path.isdir(repo_path):
journal.send(f"Can't list repositories. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository not found"}), 404
git_repo = git.Repo(repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
branches = []
for branch in git_repo.branches:
branches = branches + [branch.name]
journal.send(f"Returning {len(branches)} branches", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return {
"branches": branches
}
@app.route("/ogrepository/v1/git/repositories/<string:repo>/branches/<string:branch>/commits", methods=['GET'])
def git_get_commits(repo, branch):
"""
Retrieve the list of commits in a branch in a given repository.
Args:
repo (str): The name of the repository.
Returns:
Response: A JSON response containing a list of commits
- 200: A JSON object with a "branches" key containing a list of branch names.
- 404: A JSON object with an "error" key containing the message "Repository not found" if the repository does not exist.
"""
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if not os.path.isdir(repo_path):
journal.send(f"Can't list repositories. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository not found"}), 404
max_commits = request.args.get('max_commits') or 100
skip_commits = request.args.get('skip') or 0
git_repo = git.Repo(repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
if not branch in git_repo.branches:
journal.send(f"Branch {branch} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Branch not found"}), 404
# Lookup table for hash/tag
hash_to_tag = {}
for tag in git_repo.tags:
sha = tag.commit.hexsha
if not sha in hash_to_tag:
hash_to_tag[sha] = []
hash_to_tag[sha] = hash_to_tag[sha] + [ tag.name ]
commits = []
for com in git_repo.iter_commits(branch, max_count = max_commits, skip = skip_commits):
tag_list = []
if com.hexsha in hash_to_tag:
tag_list = hash_to_tag[com.hexsha]
commits = commits + [
{
"hexsha" : com.hexsha,
"message" : com.message,
"committed_date" : com.committed_date,
"tags" : tag_list,
"size" : com.size
}
]
journal.send(f"Returning {len(commits)} commits", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return {
"commits": commits
}
@app.route("/ogrepository/v1/git/repositories/<string:repo>/branches", methods=['POST'])
def git_create_branch(repo):
"""Create a given branch in a given repository
Args:
repo (str): The name of the repository.
Returns:
Response: A JSON response containing a creation status
- 201: A JSON object with a "status" key containing "created"
- 404: A JSON object with an "error" key containing the message "Repository not found"
- 409: A JSON object with an "error" key containing the message "Branch already exists"
"""
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if not os.path.isdir(repo_path):
journal.send(f"Can't create branch. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository not found"}), 404
git_repo = git.Repo(repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
data = request.json
if data is None:
journal.send(f"Can't create branch. JSON post data missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if not "commit" in data:
journal.send(f"Can't create branch. Commit parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "commit parameter missing"}), 400
if not "name" in data:
journal.send(f"Can't create branch. Name parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "name parameter missing"}), 400
branch = data["name"]
if branch in git_repo.branches:
journal.send(f"Can't create branch. Already found in repository {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Branch already exists"}), 409
git_repo.create_tag(branch, ref = data["commit"])
journal.send(f"Branch {branch} created in repo {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"status": "created"}), 201
@app.route("/ogrepository/v1/git/repositories/<string:repo>/branches/<string:branch>", methods=['DELETE'])
def git_delete_branch(repo, branch):
"""Delete a given branch in a given repository
Args:
repo (str): The name of the repository.
Returns:
Response: A JSON response containing a list of branch names or an error message if the repository is not found.
- 200: A JSON object with a "status" key containing "deleted"
- 404: A JSON object with an "error" key containing the message "Repository not found" or "Branch not found"
"""
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if not os.path.isdir(repo_path):
journal.send(f"Can't delete branch. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return {"error": "Repository not found"}, 404
git_repo = git.Repo(repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
if not branch in git_repo.branches:
journal.send(f"Can't delete branch. Not found in repository {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Branch not found"}), 404
git_repo.delete_head(branch)
journal.send(f"Branch {branch} deleted in repo {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"status": "deleted"}), 200
@app.route("/ogrepository/v1/git/repositories/<string:repo>/tags", methods=['GET'])
def git_list_tags(repo):
"""
Retrieve the list of tags for a given repository.
Args:
repo (str): The name of the repository.
Returns:
Response: A JSON response containing a list of tag names or an error message if the repository is not found.
- 200: A JSON object with a "branches" key containing a list of tag names.
- 404: A JSON object with an "error" key containing the message "Repository not found" if the repository does not exist.
"""
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if not os.path.isdir(repo_path):
journal.send(f"Can't list repositories. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository not found"}), 404
git_repo = git.Repo(repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
tags = []
for tag in git_repo.tags:
tag_info = {
"name" : tag.name,
"commit" : tag.commit.hexsha,
"committer" : tag.commit.committer.name,
"committed_datetime" : tag.commit.committed_datetime.timestamp()
}
if not tag.tag is None:
tag_info["message"] = tag.tag.message
tag_info["tagged_date"] = tag.tag.tagged_date
tag_info["tagger"] = tag.tag.tagger.name
tag_info["tagger_tz_offset"] = tag.tag.tagger_tz_offset
tags = tags + [ tag_info ]
journal.send(f"Returning {len(tags)} branches", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return {
"tags": tags
}
@app.route("/ogrepository/v1/git/repositories/<string:repo>/tags", methods=['POST'])
def git_create_tag(repo):
"""Create a given tag in a given repository
Args:
repo (str): The name of the repository.
Returns:
Response: A JSON response containing a creation status
- 200: A JSON object with a "status" key containing "created"
- 404: A JSON object with an "error" key containing the message "Repository not found"
- 409: A JSON object with an "error" key containing the message "Tag already exists"
"""
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if not os.path.isdir(repo_path):
journal.send(f"Can't create tag. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Repository not found"}), 404
git_repo = git.Repo(repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
data = request.json
if data is None:
journal.send(f"Can't create tag. JSON post data missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if not "commit" in data:
journal.send(f"Can't create tag. Commit parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "commit parameter missing"}), 400
if not "name" in data:
journal.send(f"Can't create tag. Name parameter missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "name parameter missing"}), 400
commit_message = ""
tag = data["name"]
if "message" in data:
commit_message = data["message"]
if tag in git_repo.tags:
journal.send(f"Can't create tag. Already found in repository {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Tag already exists"}), 409
try:
git_repo.create_tag(tag, ref = data["commit"], message = commit_message)
except git.exc.GitCommandError as ge:
if "not a valid tag name" in ge.stderr:
journal.send(f"Tag name {tag} is invalid", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Invalid tag name"}), 400
else:
journal.send(f"Git error {ge}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Error when performing git command"}), 500
journal.send(f"Tag {tag} created in repo {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"status": "created"}), 200
@app.route("/ogrepository/v1/git/repositories/<string:repo>/tags/<string:tag>", methods=['DELETE'])
def git_delete_tag(repo, tag):
"""Delete a given tag in a given repository
Args:
repo (str): The name of the repository.
Returns:
Response: A JSON response containing a list of tag names or an error message if the repository is not found.
- 200: A JSON object with a "status" key containing "deleted"
- 404: A JSON object with an "error" key containing the message "Repository not found" or "Tag not found"
"""
repo_path = os.path.join(REPOSITORIES_BASE_PATH, OGGIT_USER, repo + ".git")
if not os.path.isdir(repo_path):
journal.send(f"Can't delete tag. Repository storage at {REPOSITORIES_BASE_PATH} not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return {"error": "Repository not found"}, 404
git_repo = git.Repo(repo_path)
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
if not tag in git_repo.tags:
journal.send(f"Can't delete tag. Not found in repository {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error": "Tag not found"}), 404
git_repo.delete_head(tag)
journal.send(f"Tag {tag} deleted in repo {repo}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"status": "deleted"}), 200
@app.route("/ogrepository/v1/git/ssh_key", methods=['POST'])
def git_add_sshkey():
"""Add a SSH key
Args:
ssh_key (str): The SSH key
oglive (str): URL to an oglive image from which to extract the key. May be a local file or HTTP.
description (str): Description for the SSH key
Returns:
Response: A JSON response containing a list of tag names or an error message if the key can't be added.
- 200: A JSON object with a "status" key containing "added"
"""
data = request.json
if data is None:
journal.send(f"Can't add SSH keys, POST data missing", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing"}), 400
if not "description" in data:
data["description"] = ""
if not ("ssh_key" in data or "oglive" in data):
journal.send(f"Can't add SSH keys, either ssh_key or oglive is required", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({"error" : "Parameters missing, specify ssh_key or oglive"}), 400
module = _load_installer()
print(f"Got {module}")
OpengnsysGitInstaller = getattr(module, 'OpengnsysGitInstaller')
installer = OpengnsysGitInstaller()
if "oglive" in data:
job_id = f"GitSshKey_{''.join(random.choice('0123456789abcdef') for char in range(8))}"
threading.Thread(target=git_add_sshkey_task, args=(data["oglive"], data["description"], job_id)).start()
return jsonify({
"success": True,
"output": "Extracting key from ogLive...",
"job_id": job_id
}), 200
else:
status, content = installer.add_forgejo_sshkey(data["ssh_key"], data["description"])
message = "Result unrecognized"
success = False
httpcode = 500
if status == 200 or status == 201:
message = "SSH key added"
success = True
httpcode = 200
elif status == 422:
message = "SSH key already existed"
success = True
httpcode = 200
else:
message = "Unrecognized reply from forgejo"
success = False
httpcode = status
journal.send(f"Unrecognized reply from forgejo: code {status}, content {content}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
return jsonify({
"success": success,
"output": message,
}), httpcode
# --------------------------------------------------------------------------------------------

View File

@ -4,7 +4,7 @@ info:
version: "1.0"
description: |
---
# -----------------------------------------------------------------------------------------------------------
# Esto hace que el Swagger se ordene por los tags (apartados), de la forma especificada:
@ -18,7 +18,7 @@ tags:
- name: "Transferencia entre Repositorios y Backup"
- name: "Importar y Exportar Máquinas Virtuales"
- name: "Varios"
- name: "Git"
# -----------------------------------------------------------------------------------------------------------
# Apartado "Estado de ogRepository"
@ -30,7 +30,7 @@ paths:
summary: "Obtener Información de Estado de ogRepository"
description: >
Este endpoint ejecuta el script "**getRepoStatus.py**" y devuelve su salida en formato JSON,
incluyendo información sobre la CPU, memoria RAM, disco duro, servicios, y procesos específicos de ogRepository, e instalación de ogGit.
incluyendo información sobre la CPU, memoria RAM, disco duro, servicios, y procesos específicos de ogRepository, e instalación de ogGit.
tags:
- "Estado de ogRepository"
responses:
@ -140,14 +140,14 @@ paths:
# -----------------------------------------------------------------------------------------------------------
# Apartado "Información de Imágenes"
# -----------------------------------------------------------------------------------------------------------
/ogrepository/v1/images:
put:
summary: "Actualizar Información del Repositorio"
description: |
Este endpoint actualiza la información de las imágenes almacenadas en el repositorio, reflejándola en los archivos "**repoinfo.json**" y "**trashinfo.json**".
Utiliza el script "**updateRepoInfo.py**", que a su vez llama al script "**updateTrashInfo.py**", para actualizar también la información de la papelera.
No hace falta que se le llame al crear o exportar una imagen, ya que lo llama el endpoint "**Crear archivos auxiliares**" (que sí debe ser llamado en esos casos).
tags:
- "Información de Imágenes"
@ -192,7 +192,7 @@ paths:
get:
summary: "Obtener Información de todas las Imágenes"
description: |
Este endpoint ejecuta el script "**getRepoInfo.py**" con el parámetro "**all**", para devolver información de todas las imágenes almacenadas en el repositorio y en la papelera, que a su vez llama al script "**updateRepoInfo.py**", para actualizar previamente la información del repositorio.
Este endpoint ejecuta el script "**getRepoInfo.py**" con el parámetro "**all**", para devolver información de todas las imágenes almacenadas en el repositorio y en la papelera, que a su vez llama al script "**updateRepoInfo.py**", para actualizar previamente la información del repositorio.
Devuelve detalles como el nombre de la imagen, tipo, nombre del cliente, clonador, compresor, sistema de archivos, tamaño de los datos, tamaño de la imagen, y hashes MD5.
tags:
- "Información de Imágenes"
@ -1257,7 +1257,7 @@ paths:
# -----------------------------------------------------------------------------------------------------------
#/ogrepository/v1/p2p:
#/ogrepository/v1/p2p:
delete:
summary: "Cancelar Transmisiones P2P"
description: |
@ -1311,10 +1311,10 @@ paths:
Este endpoint importa la imagen especificada desde un repositorio remoto al repositorio local (en el que se ejecuta el endpoint).
Utiliza el script "**importImage.py**", que recibe como parámetros el nombre de la imagen, la IP o hostname del servidor remoto, y el usuario con el que conectar al servidor remoto.
que a su vez llama al script "**updateRepoInfo.py**", para actualizar la información del repositorio.
**NOTA**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está importando, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
tags:
- "Transferencia entre Repositorios y Backup"
- "Transferencia entre Repositorios y Backup"
parameters:
- name: JSON
in: body
@ -1436,7 +1436,7 @@ paths:
* **ID_img** - Identificador de la imagen, correspondiente al contenido del archivo 'full.sum'
* **repo_ip** - Dirección IP del servidor remoto
* **user** - Usuario con el que conectar al servidor remoto
* **remote_path** - Ruta remota en la que copiar la imagen
* **remote_path** - Ruta remota en la que copiar la imagen
schema:
type: object
properties:
@ -1457,7 +1457,7 @@ paths:
example: "/home/opengnsys"
responses:
"200":
description: "Se está haciendo backup de la imagen."
description: "Se está haciendo backup de la imagen."
schema:
type: object
properties:
@ -1532,20 +1532,20 @@ paths:
post:
summary: "Convertir Imagen Virtual a Imagen OpenGnsys"
description: |
Este endpoint convierte la imagen virtual especificada como primer parámetro en una imagen "img" como las que se generan desde OpenGnsys, debiendo haberse copiado previamente en la ruta "opt/opengnsys/ogrepository/images_virtual".
Utiliza el script "**convertVMtoIMG.py**", que recibe como parámetros el nombre de la imagen virtual, y el sistema de archivos de la partición a clonar (en formato "blkid").
Se puede comprobar todos los sistemas de archivos aceptados por "blkid" ejecutando el comando "blkid -k".
Este endpoint convierte la imagen virtual especificada como primer parámetro en una imagen "img" como las que se generan desde OpenGnsys, debiendo haberse copiado previamente en la ruta "opt/opengnsys/ogrepository/images_virtual".
Utiliza el script "**convertVMtoIMG.py**", que recibe como parámetros el nombre de la imagen virtual, y el sistema de archivos de la partición a clonar (en formato "blkid").
Se puede comprobar todos los sistemas de archivos aceptados por "blkid" ejecutando el comando "blkid -k".
**NOTA**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen virtual se está convirtiendo, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
tags:
- "Importar y Exportar Máquinas Virtuales"
- "Importar y Exportar Máquinas Virtuales"
parameters:
- name: JSON
in: body
required: true
description: |
* **virtual_image** - Nombre de la imagen virtual, con extensión
* **filesystem** - Sistema de archivos de la partición a clonar, en formato "blkid"
* **virtual_image** - Nombre de la imagen virtual, con extensión
* **filesystem** - Sistema de archivos de la partición a clonar, en formato "blkid"
schema:
type: object
properties:
@ -1640,19 +1640,19 @@ paths:
put:
summary: "Convertir Imagen OpenGnsys a Imagen Virtual"
description: |
Este endpoint convierte la imagen "img" especificada como primer parámetro en una imagen virtual con la extensión especificada como segundo parámetro ("vdi", "vmdk", etc), guardándola en la ruta "opt/opengnsys/ogrepository/images_virtual/export".
Utiliza el script "**convertIMGtoVM.py**", que recibe como parámetros el nombre de la imagen, y la extensión del disco virtual destino ("vdi", "vmdk", etc).
Este endpoint convierte la imagen "img" especificada como primer parámetro en una imagen virtual con la extensión especificada como segundo parámetro ("vdi", "vmdk", etc), guardándola en la ruta "opt/opengnsys/ogrepository/images_virtual/export".
Utiliza el script "**convertIMGtoVM.py**", que recibe como parámetros el nombre de la imagen, y la extensión del disco virtual destino ("vdi", "vmdk", etc).
**NOTA**: Este endpoint es asíncrono, ya que puede tardar mucho tiempo, por lo que solo informa de que la imagen se está convirtiendo a virtual, y abre un proceso paralelo, que avisará a ogCore cuando finalice la tarea (llamando a un endpoint de ogCore).
tags:
- "Importar y Exportar Máquinas Virtuales"
- "Importar y Exportar Máquinas Virtuales"
parameters:
- name: JSON
in: body
required: true
description: |
* **ID_img** - Identificador de la imagen, correspondiente al contenido del archivo 'full.sum'
* **vm_extension** - Extensión del disco virtual destino ("vdi", "vmdk", etc)
* **vm_extension** - Extensión del disco virtual destino ("vdi", "vmdk", etc)
schema:
type: object
properties:
@ -1883,7 +1883,7 @@ paths:
summary: "Renombrar una Imagen"
description: |
Este endpoint renombra la imagen especificada como primer parámetro (y todos sus archivos asociados), asignando el nombre especificado como segundo parámetro.
Utiliza el script "**renameImage.py**", que recibe como parámetros el nombre original y el nuevo nombre a asignar.
Utiliza el script "**renameImage.py**", que recibe como parámetros el nombre original y el nuevo nombre a asignar.
tags:
- "Varios"
parameters:
@ -1960,3 +1960,521 @@ paths:
example: "(Exception description)"
# -----------------------------------------------------------------------------------------------------------
# -----------------------------------------------------------
# ____ _ _
# / ___(_) |_
# | | _| | __|
# | |_| | | |_
# \____|_|\__|
#
# -----------------------------------------------------------
/ogrepository/v1/git/ssh_key:
post:
summary: "Agregar clave de SSH de ogLive"
description: |
Agrega una clave de SSH que va a usarse desde el ogLive
para interactuar con Git.
Es necesario especificar **ssh_key** o **oglive**.
Especificando **ssh_key** se agrega la cclave especificada directamente.
Especificando **oglive** se descarga si es necesario el .iso, se monta, y se extrae la clave.
Esta acción se hace en el fondo, y se devuelve un job_id.
tags:
- "Git"
parameters:
- name: JSON
in: body
required: true
description: |
* **ssh_key** - Clave de SSH (opcional)
* **oglive** - URL a ogLive (opcional, NO USAR DE MOMENTO)
* **description** - Descripcion (opcional)
schema:
type: object
properties:
ssh_key:
type: string
example: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINEOttwhJ+9THRZ1Zv/6QUwPUDq1X7opG9V7EFLVWxQV"
required: False
description:
type: string
example: "OgLive r20250518"
required: False
oglive:
type: string
example: "https://ognproject.evlt.uma.es/oglive/ogLive-noble-6.8.0-31-generic-amd64-r20250518.cf13a6d_20250519.iso"
required: False
responses:
"200":
description: "Exito"
schema:
type: object
properties:
success:
type: boolean
example: True
required: True
output:
type: string
example: "SSH key added"
required: True
job_id:
type: string
example: "GitSshKey_873f353f"
required: False
/ogrepository/v1/git/repositories:
get:
summary: "Obtener lista de repositorios"
description: |
Devuelve una lista de repositorios de Git
tags:
- "Git"
responses:
"200":
description: "Lista de repositorios"
schema:
type: object
properties:
repositories:
type: array
items:
type: string
example: linux
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
post:
summary: "Crear repositorio"
description: |
Crea un repositorio nuevo de Git
tags:
- "Git"
parameters:
- name: JSON
in: body
required: true
description: |
* **name** - Nombre de repositorio
schema:
type: object
properties:
name:
type: string
example: linux
responses:
"201":
description: "Repositorio creado"
schema:
type: object
properties:
status:
type: string
example: "Repository created"
"500 (Exception)":
description: "JSON post data missing"
schema:
type: object
properties:
error:
type: string
example: "Parameters missing"
/ogrepository/v1/git/repositories/{repository}/tags:
get:
summary: "Obtener lista de tags"
description: |
Devuelve una lista de tags de Git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
responses:
"200":
description: "Lista de tags"
schema:
type: object
properties:
tags:
type: array
items:
type: object
properties:
name:
type: string
example: v0.1
commit:
type: string
example: db8e84d5d2548f589ee503c1c6d5003cc6a0d803
committer:
type: string
example: John Smith
committed_datetime:
type: int
example: 1745360193
message:
type: string
example: "Initial release"
required: False
tagged_date:
type: int
example: 1745360194
required: False
tagger:
type: string
example: John Smith
required: False
tagger_tz_offset:
type: int
example: -7200
required: False
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
post:
summary: "Crear tag"
description: |
Crea una tag de git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
- name: JSON
in: body
required: true
description: |
* **name** - Nombre del tag
* **commit** - Commit al que apunta el tag nuevo. Puede ser un nombre de otra rama/tag.
* **message** - Mensaje descriptivo para el tag. Opcional, si no se especifica se asume una cadena vacía.
schema:
type: object
properties:
name:
type: string
example: v1.0
required: True
commit:
type: string
example: HEAD
required: True
message:
type: string
example: Version 1.0
required: False
responses:
"201":
description: "Tag creado"
schema:
type: object
properties:
status:
type: string
example: created
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
"404":
description: "El repositorio no existe"
schema:
type: object
properties:
error:
type: string
example: "Repository not found"
"409":
description: "El tag ya existe"
schema:
type: object
properties:
error:
type: string
example: "Tag already exists"
delete:
summary: "Eliminar tag"
description: |
Elimina un tag de git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
- name: tag
in: path
required: true
type: string
description: "Rama del repositorio"
responses:
"200":
description: "Tag eliminado"
schema:
type: object
properties:
status:
type: string
example: deleted
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
/ogrepository/v1/git/repositories/{repository}/branches/{branch}/commits:
get:
summary: "Obtener lista de commits en una rama"
description: |
Devuelve una lista de commits de Git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
- name: branch
in: path
required: true
type: string
description: "Rama dentro del repositorio"
- name: max_commits
in: query
required: false
type: int
description: "Máximo de commits a obtener"
- name: skip
in: query
required: false
type: int
description: "Commits a saltar (para paginación)"
responses:
"200":
description: "Lista de commits"
schema:
type: object
properties:
commits:
type: array
items:
type: object
properties:
committed_date:
type: int
example: 1745360193
hexsha:
type: string
example: "db8e84d5d2548f589ee503c1c6d5003cc6a0d803"
message:
type: string
example: "Install updates"
size:
type: int
example: 67108864
tags:
type: array
example: ["updates"]
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
"404":
description: "El repositorio o branch no existe"
schema:
type: object
properties:
error:
type: string
example: "Repository not found"
/ogrepository/v1/git/repositories/{repository}/branches:
get:
summary: "Obtener lista de branches"
description: |
Devuelve una lista de branches de Git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
responses:
"200":
description: "Lista de branches"
schema:
type: object
properties:
branches:
type: array
items:
type: string
example: main
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
"404":
description: "El repositorio no existe"
schema:
type: object
properties:
error:
type: string
example: "Repository not found"
"409":
description: "El branch ya existe"
schema:
type: object
properties:
error:
type: string
example: "Branch already exists"
post:
summary: "Crear branch"
description: |
Crea una rama de git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
- name: JSON
in: body
required: true
description: |
* **commit** - Commit al que apunta la rama nueva. Puede ser un nombre de otra rama/tag.
schema:
type: object
properties:
name:
type: string
example: devel
required: True
commit:
type: string
example: HEAD
required: True
responses:
"201":
description: "Rama creada"
schema:
type: array
items:
type: string
example: main
"500":
description: "Git no instalado"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
delete:
summary: "Eliminar branch"
description: |
Elimina una rama de git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
- name: branch
in: path
required: true
type: string
description: "Branch del repositorio"
responses:
"200":
description: "Branch eliminado"
schema:
type: object
properties:
status:
type: string
example: deleted
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"

29
debian/changelog vendored
View File

@ -1,3 +1,32 @@
ogrepository (1.1.1dev7) UNRELEASED; urgency=medium
[ OpenGnsys ]
* UNRELEASED
* UNRELEASED
[ Vadim Troshchinskiy ]
* Fix path handling when there are no repositories
* Add another search path for installer module
* Fix path
[ Vadim Trochinsky ]
* Add SSH key endpoint
[ Vadim Troshchinskiy ]
* update dependencies
-- OpenGnsys <opengnsys@opengnsys.com> Tue, 17 Jun 2025 21:42:29 +0000
ogrepository (1.1.1) unstable; urgency=medium
* OgGit - fix repo path handling
-- Tu Nombre <tuemail@example.com> Jue, 5 Jun 2025 08:43:58 +0000
ogrepository (1.1.0) unstable; urgency=medium
* OgGit
-- Tu Nombre <tuemail@example.com> Tue, 11 Mar 2025 04:43:58 +0000
ogrepository (1.0.0+deb-packages20250311-1) unstable; urgency=medium
* First debian version

2
debian/control vendored
View File

@ -8,7 +8,7 @@ Build-Depends: debhelper-compat (= 12)
Package: ogrepository
Architecture: all
Pre-Depends: debian-archive-keyring , debconf (>= 1.5.0),
Depends: ${misc:Depends}, git, python3, python3-pip, python3-flask, python3-paramiko, python3-psutil, python3-flasgger, samba, gunicorn, wakeonlan , lzop , partclone , qemu-utils , udpcast, uftp
Depends: ${misc:Depends}, git, python3, python3-pip, python3-flask, python3-paramiko, python3-psutil, python3-flasgger, samba, gunicorn, wakeonlan , lzop , partclone , qemu-utils , udpcast, uftp, opengnsys-gitinstaller, opengnsys-forgejo
Description: Ogrepsoitory Package
This package provides Ogrepository service.
X-OG-Release: opengnsys-1.6.0-beta, opengnsys-1.6.1-beta

View File

@ -1,7 +1,7 @@
#!/bin/sh
set -e
set -x
set -x
. /usr/share/debconf/confmodule
@ -19,7 +19,7 @@ SAMBA_PASS="$RET"
USER="opengnsys"
# Provisionar base de datos si es necesario en caso de instalación.
# Provisionar base de datos si es necesario en caso de instalación.
# Detectar si es una instalación nueva o una actualización
@ -33,7 +33,7 @@ for pkg in bittorrent bittornado ctorrent; do
fi
done
"
sed -i "s/%%OGREPOSITORY_USER%%/$SAMBA_USER/g" /etc/systemd/system/ogrepo-api.service
sed -i "s/%%OGREPOSITORY_USER%%/$SAMBA_USER/g" /etc/samba/ogrepo-smb.conf
@ -49,7 +49,7 @@ done
chmod 700 $OPENGNSYS_HOME/.ssh
chmod 600 $OPENGNSYS_HOME/.ssh/id_ed25519
chmod 644 $OPENGNSYS_HOME/.ssh/id_ed25519.pub
# Genera authorized_keys
# Genera authorized_keys
cat $OPENGNSYS_HOME/.ssh/id_ed25519.pub >> $OPENGNSYS_HOME/.ssh/authorized_keys
chmod 600 $OPENGNSYS_HOME/.ssh/authorized_keys
chown -R opengnsys:opengnsys $OPENGNSYS_HOME/.ssh
@ -63,18 +63,18 @@ done
(echo "$SAMBA_PASS"; echo "$SAMBA_PASS") | smbpasswd -a $SAMBA_USER
fi
# Configure Repo
# Configure Repo
cp /opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg.tmpl /opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg
sed -i "s/SERVERIP/$OGREPO_IP/g" /opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg
sed -i "s/OGCOREIP/$OGCORE_IP/g" /opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg
# Copiar sudoers file ogrepository/etc/opengnsys-repository
cp /opt/opengnsys/ogrepository/etc/opengnsys-repository /etc/sudoers.d/opengnsys
cp /opt/opengnsys/ogrepository/etc/opengnsys-repository /etc/sudoers.d/opengnsys
elif [ "$1" = "configure" ] && [ -n "$2" ]; then
echo "Actualización desde la versión $2"
# Recopy static files without configuration
cp /opt/opengnsys/ogrepository/etc/opengnsys-repository /etc/sudoers.d/opengnsys-repository
# Recopy static files without configuration
cp /opt/opengnsys/ogrepository/etc/opengnsys-repository /etc/sudoers.d/opengnsys-repository
OPENGNSYS_HOME=$(getent passwd opengnsys | cut -d: -f6)
# Create .ssh directory
mkdir -p $OPENGNSYS_HOME/.ssh
@ -88,8 +88,10 @@ fi
# Cambiar la propiedad de los archivos al usuario especificado
chown opengnsys:www-data /opt/opengnsys/
chown -R opengnsys:www-data /opt/opengnsys/ogrepository
chmod 755 /opt/opengnsys
chmod 755 /opt/opengnsys/ogrepository/bin/*
# Install http server stuff
# Reiniciar servicios si es necesario
# systemctl restart nombre_del_servicio