Compare commits

..

No commits in common. "main" and "add_python_scripts" have entirely different histories.

16 changed files with 359 additions and 2205 deletions

2
.gitignore vendored
View File

@ -7,5 +7,3 @@ debian/files
debian/*.debhelper
debian/*.debhelper.log
debian/debhelper-build-stamp
.venv/
.vscode/

View File

@ -1,83 +1,5 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.10.7] - 2026-09-17
### Fixed
- **createTorrentSum**: Corregido error (`OSError: [Errno 22] Invalid argument`) al calcular la suma de verificación MD5 de los ficheros de imagen cuando estos ocupan menos de 1 MB, ajustando dinámicamente el `seek_offset` al tamaño real del fichero.
## [0.10.6] - 2026-07-27
### Added
- API de Repositorios Git (`repo_api.py`): Implementado el endpoint `DELETE /ogrepository/v1/git/repositories/<repo>` que sustituye al antiguo código "Not implemented". Ahora se encarga de eliminar el repositorio correctamente de la base de datos de Forgejo y de purgar el directorio `.git` del disco duro.
### Changed
- **createTorrentSum**: Optimizada la creación de ficheros torrent (`mktorrent`) calculando el tamaño de pieza (`-l`) de forma dinámica según el tamaño de la imagen, evitando archivos `.torrent` con cientos de miles de piezas que saturaban el tracker en imágenes muy grandes. Se ha incrementado `target_pieces` a 200.000 para garantizar que las piezas no superen los 2 MB en imágenes de hasta 400 GB.
## [0.10.5] - 2026-04-14
### Fixed
- Force 755 permissions on `/opt/opengnsys` in `preinst` to prevent `oggit` user from being locked out when `useradd` creates the directory with a restrictive umask (e.g. 027), which caused `opengnsys-forgejo` to fail to start during installation before `postinst` could apply the correct permissions
- Ensure forgejo working directory (`var/lib/forgejo/work`) is created in `postinst` before setting ownership, and restart `opengnsys-forgejo` after fixing permissions so it recovers from a failed state caused by the ordering between dependency and package configuration
## [0.10.4] - 2025-09-30
### Fixed
- Set safe.directory just once
## [0.10.3] - 2025-09-18
### Changed
- Create tags via a query to the forgejo API
## [0.10.2] - 2025-09-01
### Fixed
- Corregida la logica en el endpoint create torrent sum
- Mejoras en los mensajes de logs
- Mejoras en la validación de los archivos info e info.checked
## [0.10.1] - 2025-08-25
### Fixed
- Import images problems when there is not info.checked file (#2723, #2726)
- Improves logging (#2727)
## [0.10.0] - 2025-07-07
### Added
- OgGit functionality (#2371, #2363, #2363, #2317)
### Removed
- Removed unused BitTorrent-related packages and logic
## [0.9.0] - 2025-06-25
### Added
- Changed old tools for tools non dependant of Pyhton2 in repo
- mktorrent to handle creation of torrent files
- aria2c as torrent client for initial seeding
- opentracker as torrent tracker tool
## [0.8.2] - 2025-06-01
### Changed
- Modified sudoersfile to start torrent
## [0.8.1] - 2025-04-01
### Changed

View File

@ -49,32 +49,6 @@ pipeline {
}
}
stage('Generate Changelog (Nightly)'){
when {
branch 'main'
}
steps {
script {
def devName = params.DEV_NAME ? params.DEV_NAME : env.DEFAULT_DEV_NAME
def devEmail = params.DEV_EMAIL ? params.DEV_EMAIL : env.DEFAULT_DEV_EMAIL
generateDebianChangelog(env.BUILD_DIR, devName, devEmail,"nightly")
}
}
}
stage('Generate Changelog (Devel)') {
when {
branch 'devel'
}
steps {
script {
def devName = params.DEV_NAME ? params.DEV_NAME : env.DEFAULT_DEV_NAME
def devEmail = params.DEV_EMAIL ? params.DEV_EMAIL : env.DEFAULT_DEV_EMAIL
generateDebianChangelog(env.BUILD_DIR, devName, devEmail, "devel")
}
}
}
stage('Build') {
steps {
script {
@ -97,57 +71,17 @@ pipeline {
}
}
}
stage ('Publish to Debian Repository (Nightly)') {
when {
branch 'main'
}
agent { label 'debian-repo' }
steps {
script {
// Construir el patrón de versión esperado en el nombre del paquete
def versionPattern = "-${env.BUILD_NUMBER}~nightly"
publicarEnAptly('/var/tmp/opengnsys/debian-repo/ogrepository', 'nightly', versionPattern)
}
}
}
stage ('Publish to Debian Repository (Devel)') {
when {
branch 'devel'
}
agent { label 'debian-repo' }
steps {
script {
def versionPattern = "-${env.BUILD_NUMBER}~devel"
publicarEnAptly('/var/tmp/opengnsys/debian-repo/ogrepository', 'devel', versionPattern)
}
}
}
}
post {
success {
script {
// Solo lanzar cuando el build sea exitoso y en la rama main
if (env.BRANCH_NAME == 'main') {
build job: 'Aptly publish nightly repository',
wait: false,
parameters: [
string(name: 'TRIGGERED_BY', value: "${env.JOB_NAME}-${env.BUILD_NUMBER}")
]
}
if (env.BRANCH_NAME == 'devel') {
build job: 'Aptly publish develop repository',
wait: false,
parameters: [
string(name: 'TRIGGERED_BY', value: "${env.JOB_NAME}-${env.BUILD_NUMBER}")
]
}
}
}
always {
notifyBuildStatus('opengnsys@qindel.com')
notifyBuildStatus('narenas@qindel.com')
}
}
}
// stage ('Publish to Debian Repository') {
// agent { label 'debian-repo' }
// steps {
// sh "aptly repo add opengnsys-devel /var/tmp/opengnsys/debian-repo/*.deb"
// }
// }

File diff suppressed because it is too large Load Diff

View File

@ -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"
@ -1960,721 +1960,3 @@ 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.
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
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}:
delete:
summary: "Eliminar repositorio"
description: |
Elimina un repositorio de Git
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
responses:
"200":
description: "Repositorio eliminado"
schema:
type: object
properties:
status:
type: string
example: deleted
"404":
description: "Repositorio no encontrado"
schema:
type: object
properties:
status:
type: string
example: not found
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
/ogrepository/v1/git/repositories/{repository}/sync:
post:
summary: "Sincronizar con repositorio remoto"
description: |
Sincroniza un repositorio de Git con uno remoto.
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
- name: JSON
in: body
required: true
description: |
* **remote_repository** - Reposition remoto al que hacer push
responses:
"200":
description: "Repositorio en proceso de sincronización"
schema:
type: object
properties:
success:
type: boolean
example: True
output:
type: string
example: "Synchronizing..."
job_id:
type: string
example: "GitSync_02345"
"404":
description: "Repositorio no encontrado"
schema:
type: object
properties:
status:
type: string
example: not found
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
/ogrepository/v1/git/repositories/{repository}/backup:
post:
summary: "Sincronizar con repositorio remoto"
description: |
Sincroniza un repositorio de Git con uno remoto.
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
- name: JSON
in: body
required: true
description: |
* **ssh_server** - Servidor SSH al que enviar el backup
* **ssh_port** - Puerto del servidor SSH al que enviar el backup
* **ssh_user** - Usuario del servidor SSH al que enviar el backup
* **filename** - Nombre de archivo del backup
responses:
"200":
description: "Repositorio en proceso de sincronización"
schema:
type: object
properties:
success:
type: boolean
example: True
output:
type: string
example: "Backing up..."
job_id:
type: string
example: "GitBackup_02345"
"404":
description: "Repositorio no encontrado"
schema:
type: object
properties:
status:
type: string
example: not found
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
/ogrepository/v1/git/repositories/{repository}/compact:
post:
summary: "Compactar repositorio"
description: |
Compacta un repositorio de Git para optimizar el rendimiento y liberar espacio
tags:
- "Git"
parameters:
- name: repository
in: path
required: true
type: string
description: "Nombre de repositorio"
responses:
"200":
description: "Repositorio en proceso de compactación"
schema:
type: object
properties:
success:
type: boolean
example: True
output:
type: string
example: "Compacting..."
job_id:
type: string
example: "GitGC_02345"
"404":
description: "Repositorio no encontrado"
schema:
type: object
properties:
status:
type: string
example: not found
"500":
description: "Excepción"
schema:
type: object
properties:
success:
type: boolean
example: false
exception:
type: string
example: "(Exception description)"
/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.
**Filtrado de commits:**
Por defecto algunos commits que no son utiles para restaurar una imagen se ocultan.
Pasar filter_commits=0 para desactivar esta funcionalidad y ver todos los commits
del repositorio.
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)"
- name: filter_commits
in: query
required: false
type: int
description: "Si aplicar filtrado de commits. Por defecto 1."
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)"

View File

@ -96,13 +96,8 @@ def get_md5_sum(file_path, megabytes=1):
Se utiliza para crear el archivo ".sum" (para transferencias Unicast y Multicast).
"""
hash_md5 = hashlib.md5()
file_size = os.path.getsize(file_path)
seek_offset = -megabytes * 1024 * 1024
if file_size < (-seek_offset):
seek_offset = -file_size
with open(file_path, "rb") as f:
f.seek(seek_offset, os.SEEK_END)
f.seek(-megabytes * 1024 * 1024, os.SEEK_END)
data = f.read(megabytes * 1024 * 1024)
hash_md5.update(data)
return hash_md5.hexdigest()
@ -139,18 +134,8 @@ def create_torrent(file_path, torrent_file, datafullsum):
repo_ip = get_IPlocal()
tracker_url = f"http://{repo_ip}:6969/announce"
import math
file_size = os.path.getsize(file_path)
target_pieces = 200000
piece_size_target = file_size / target_pieces
if piece_size_target <= 262144:
l_val = 18
else:
l_val = math.ceil(math.log(piece_size_target, 2))
l_val = max(18, min(26, l_val))
# Creamos una litas para ejecutar el comando mktorrent para crear el archivo torrent
splitted_cmd = f"nice -n 0 mktorrent -l {l_val} -a {tracker_url} -c {datafullsum} -o {torrent_file} {file_path}".split()
# Creamos una lista con el comando para crear el torrrent, y lo imprimimos con espacios:
splitted_cmd = f"nice -n 0 ctorrent -t {file_path} -u {tracker_url} -s {torrent_file} -c {datafullsum} -l 4194304".split()
print(f"Sending command: {' '.join(splitted_cmd)}")
# Ejecutamos el comando en el sistema, e imprimimos el resultado:
@ -199,17 +184,12 @@ def main():
# Obtenemos la ruta completa de la imagen:
file_path = build_file_path()
# Si no existe el archivo de imagen o el .info, imprimimos un mensaje de error y salimos del script:
if not os.path.exists(file_path) or not (os.path.exists(f"{file_path}.info.checked") or os.path.exists(f"{file_path}.info")):
# Si no existe el archivo de imagen, imprimimos un mensaje de error y salimos del script:
if not os.path.exists(file_path):
journal.send(f"createTorrentSum.py: Image file '{file_path}' not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Image file '{file_path}' doesn't exist")
if not os.path.exists(f"{file_path}.info.checked") and not os.path.exists(f"{file_path}.info"):
journal.send(f"createTorrentSum.py: Neither '{file_path}.info.checked' nor '{file_path}.info' found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Neither info file '{file_path}.info.checked' nor '{file_path}.info' exists")
journal.send("createTorrentSum.py: Image not found", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print("Image file doesn't exist")
sys.exit(2)
# Si la imagen está bloqueada, imprimimos un mensaje de error y salimos del script:
if os.path.exists(f"{file_path}.lock"):
journal.send("createTorrentSum.py: Image is locked", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
@ -277,7 +257,6 @@ def main():
# Actualizamos la información del repositorio, ejecutando el script "updateRepoInfo.py":
journal.send("createTorrentSum.py: Updating repository info...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print("Updating Repository Info...")
if os.path.exists(f"{file_path}.info"):
update_repo_info()

View File

@ -106,11 +106,11 @@ def main():
total_disk, used_disk, free_disk, percent_disk = get_disk_info()
# Obtenemos el estado de los servicios listados, que almacenamos en un diccionario:
service_list = ['ssh', 'smbd', 'opentracker']
service_list = ['ssh', 'smbd', 'rsync']
services_status = {service: get_service_status(service) for service in service_list}
# Obtenemos el estado de los procesos listados, que almacenamos en un diccionario:
process_list = ['udp-sender', 'uftp', 'aria2c']
process_list = ['udp-sender', 'uftp', 'bttrack', 'btlaunchmany.bittornado']
process_status = {process: get_process_status(process) for process in process_list}
# Creamos un diccionario con toda la información obtenida:

View File

@ -16,8 +16,6 @@ No recibe ningún parámetro.
import os
import sys
import subprocess
import psutil
import re
from systemd import journal
@ -33,73 +31,29 @@ repo_path = '/opt/opengnsys/ogrepository/images' # En este caso, no lleva barra
# --------------------------------------------------------------------------------------------
def run_bittornado(repo_path):
""" Ejecuta el comando "btlaunchmany.bittornado", con sus parámetros correspondientes.
Además, captura el resultado y los posibles errores, y los imprime.
"""
# Creamos una lista con el comando "btlaunchmany.bittornado" y sus parámetros, y lo imprimimos con espacios:
splitted_cmd = f"btlaunchmany.bittornado {repo_path}".split()
print(f"Sending command: {' '.join(splitted_cmd)}")
journal.send(f"runTorrentSeeder.py: Running command: {' '.join(splitted_cmd)}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# def run_bittornado(repo_path):
# """ Ejecuta el comando "btlaunchmany.bittornado", con sus parámetros correspondientes.
# Además, captura el resultado y los posibles errores, y los imprime.
# """
# # Creamos una lista con el comando "btlaunchmany.bittornado" y sus parámetros, y lo imprimimos con espacios:
# splitted_cmd = f"btlaunchmany.bittornado {repo_path}".split()
# print(f"Sending command: {' '.join(splitted_cmd)}")
# journal.send(f"runTorrentSeeder.py: Running command: {' '.join(splitted_cmd)}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# # Ejecutamos el comando "btlaunchmany.bittornado" en el sistema, e imprimimos el resultado:
# try:
# result = subprocess.run(splitted_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# journal.send(f"runTorrentSeeder.py: Command ReturnCode: {result.returncode}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# print(f"Bittornado ReturnCode: {result.returncode}")
# except subprocess.CalledProcessError as error:
# journal.send("runTorrentSeeder.py: Process finalized", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# print(f"Bittornado ReturnCode: {error.returncode}")
# print(f"Bittornado Error Output: {error.stderr.decode()}")
# except Exception as error:
# journal.send(f"runTorrentSeeder.py: Command exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# print(f"Unexpected bittornado error: {error}")
def run_aria2c_seeder(image_name):
"""Lanza aria2c como seeder puro para una imagen concreta ya existente."""
repo_path = '/opt/opengnsys/ogrepository/images'
torrent_file = os.path.join(repo_path, f"{image_name}.img.torrent")
image_file = os.path.join(repo_path, f"{image_name}.img")
# Verificación básica
if not os.path.exists(torrent_file):
print(f"Torrent file not found: {torrent_file}")
journal.send(f"Seeder error: Torrent file not found: {torrent_file}",
PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
return
if not os.path.exists(image_file):
print(f"Image file not found: {image_file}")
journal.send(f"Seeder error: Image file not found: {image_file}",
PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
return
# Comando aria2c como seeder puro
cmd = [
'aria2c',
'--enable-peer-exchange=true',
'--bt-seed-unverified=true',
'--check-integrity=true',
'--seed-ratio=0.0',
'--dir=' + repo_path,
torrent_file
]
journal.send(f"Launching aria2c seeder for {image_name}",
PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
journal.send(f"Command: {' '.join(cmd)}",
PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
print("Running command:", ' '.join(cmd))
# Ejecutamos el comando "btlaunchmany.bittornado" en el sistema, e imprimimos el resultado:
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
journal.send(f"aria2c seeder failed: {e}",
PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
print(f"Seeder process exited with code {e.returncode}")
result = subprocess.run(splitted_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
journal.send(f"runTorrentSeeder.py: Command ReturnCode: {result.returncode}", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Bittornado ReturnCode: {result.returncode}")
except subprocess.CalledProcessError as error:
journal.send("runTorrentSeeder.py: Process finalized", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Bittornado ReturnCode: {error.returncode}")
print(f"Bittornado Error Output: {error.stderr.decode()}")
except Exception as error:
journal.send(f"runTorrentSeeder.py: Command exception: {error}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Unexpected bittornado error: {error}")
# --------------------------------------------------------------------------------------------
# MAIN
@ -110,40 +64,16 @@ def main():
"""
"""
# Finalizamos el proceso "btlaunchmany.bittornado" (en caso de que estuviera corriendo):
if len(sys.argv) != 2:
print("Usage: runTorrentSeeder.py <image_name>")
journal.send("runTorrentSeeder.py: Invalid number of arguments. Expected 1 argument: <image_name>",
PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
sys.exit(1)
image_name = sys.argv[1]
torrent_file = os.path.join(repo_path, f"{image_name}.img.torrent")
found = False
# Matamos los procesos de aria2c que sirvan la imagen en concreto. Chequeamos todos los procesos
journal.send(f"runTorrentSeeder.py: looking for aria2c processes for {image_name}.torrent",
PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
for proc in psutil.process_iter(['pid','name','cmdline']):
try:
if proc.info['name'] != 'aria2c':
continue
journal.send("runTorrentSeeder.py: Killing process 'btlaunchmany.bittornado'...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
subprocess.run(f"pkill btlaunchmany".split(), check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except Exception as error_description:
journal.send("runTorrentSeeder.py: No 'btlaunchmany.bittornado' process running", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"No btlaunchmany.bittornado process running? Returned error: {error_description}")
if any(arg.endswith(torrent_file) for arg in proc.info['cmdline']):
proc.terminate()
found = True
print(f"Killed aria2c process with PID {proc.info['pid']} for {image_name}.torrent")
journal.send(f"runTorrentSeeder.py: Killed aria2c process with PID {proc.info['pid']} for {image_name}.torrent",
PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
# Ejecutamos el comando "btlaunchmany.bittornado" (para hacer seed de los torrents):
run_bittornado(repo_path)
if not found:
print(f"No aria2c process found for {image_name}.torrent")
journal.send(f"runTorrentSeeder.py: No aria2c process found for {image_name}.torrent",
PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
# Lanzamos aria2c como seeder para la imagen proporcionada
run_aria2c_seeder(image_name)
# --------------------------------------------------------------------------------------------

View File

@ -67,7 +67,6 @@ def check_files():
continue
# Comprobamos si existe un archivo ".info" asociado a la imagen actual:
info_file = f"{img_path}.info"
try:
if os.path.exists(info_file):
# Si la fecha de modificación del archivo ".info" es anterior a la de la imagen, lo eliminamos (porque estará desactualizado):
if os.path.getmtime(info_file) < os.path.getmtime(img_path):
@ -81,36 +80,20 @@ def check_files():
with open(f"{img_path}.size", 'r') as file:
size = file.read().strip('\n')
with open(f"{img_path}.sum", 'r') as file:
sum = file.read().strip('\n')
_sum = file.read().strip('\n')
with open(f"{img_path}.full.sum", 'r') as file:
fullsum = file.read().strip('\n')
# Llamamos a la función "add_to_json", para que inserte la información de la imagen en el archivo json
# (pasándole el nombre de la imagen, la extensión, y los datos extraídos del archivo ".info"):
img_name = os.path.relpath(img_path, repo_path)
add_to_json(os.path.splitext(img_name)[0], os.path.splitext(img_name)[1][1:], info_data, size, _sum, fullsum)
# Renombramos el archivo ".info" a ".info.checked", para que ya no se añada la información que contiene
# (originalmente se eliminaba el archivo, pero creo que es mejor mantenerlo):
os.rename(info_file, f"{info_file}.checked")
# Llamamos a la función "add_to_json", para que inserte la información de la imagen en el archivo json
# (pasándole el nombre de la imagen, la extensión, y los datos extraídos del archivo ".info"):
img_name = os.path.relpath(img_path, repo_path)
add_to_json(os.path.splitext(img_name)[0], os.path.splitext(img_name)[1][1:], info_data, size, sum, fullsum)
except (IOError, OSError, PermissionError) as e:
# Si hay algún error leyendo los archivos, registramos el error y continuamos con la siguiente imagen
journal.send(f"updateRepoInfo.py: Error processing files for {img_path}: {str(e)}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Warning: Could not process files for {item}: {str(e)}")
continue
except ValueError as e:
# Si hay algún error con el formato de los datos en los archivos
journal.send(f"updateRepoInfo.py: Data format error for {img_path}: {str(e)}", PRIORITY=journal.LOG_WARNING, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Warning: Data format error for {item}: {str(e)}")
continue
except Exception as e:
# Para cualquier otra excepción inesperada
journal.send(f"updateRepoInfo.py: Unexpected error processing {img_path}: {str(e)}", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api_DEBUG")
print(f"Error: Unexpected error processing {item}: {str(e)}")
continue
def add_to_json(image_name, image_type, data, size, sum, fullsum):
def add_to_json(image_name, image_type, data, size, _sum, fullsum):
""" Esta función añade al archivo "repoinfo.json" la información de las imágenes que aun no ha sido introducida en él (imágenes nuevas, básicamente).
"""
# Almacenamos el contenido de la variable "data" (del tipo "PARTCLONE:LZOP:EXTFS:8500000:Ubuntu_20") en variables separadas:
@ -126,7 +109,7 @@ def add_to_json(image_name, image_type, data, size, sum, fullsum):
"filesystem": fstype.upper(),
"datasize": int(datasize) * 1024, # Convertimos el valor a bytes (desde KB)
"size": int(size),
"sum": sum,
"sum": _sum,
"fullsum": fullsum
}
# Almacenamos el contenido del archivo "repoinfo.json" en la variable "info_data"

40
debian/changelog vendored
View File

@ -1,43 +1,3 @@
ogrepository (1.1.5) UNRELEASED; urgency=medium
* refs #2363 Fix breaking forgejo permissions
-- OpenGnsys <opengnsys@opengnsys.com> Tue, 1 Jul 2025 16:05:12 +0000
ogrepository (1.1.4) UNRELEASED; urgency=medium
* refs #2363 Reduce number of required dependencies
-- OpenGnsys <opengnsys@opengnsys.com> Tue, 30 Jun 2025 15:50:11 +0000
ogrepository (1.1.3) UNRELEASED; urgency=medium
* refs #2317 Fix performance problems, remove stats
* refs #2298 Add commmits endpoint
-- OpenGnsys <opengnsys@opengnsys.com> Tue, 30 Jun 2025 15:50:11 +0000
ogrepository (1.1.2) UNRELEASED; urgency=medium
* Fix path handling when there are no repositories
* Add another search path for installer module
* Fix path handling
* Add SSH key endpoint
* Update dependencies
-- OpenGnsys <opengnsys@opengnsys.com> Tue, 30 Jun 2025 15:20:23 +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

5
debian/control vendored
View File

@ -8,7 +8,6 @@ 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, mktorrent, aria2 , opengnsys-opentracker, opengnsys-forgejo, python3-git
Depends: ${misc:Depends}, git, python3, python3-pip, python3-flask, python3-paramiko, python3-psutil, python3-flasgger, samba, gunicorn, wakeonlan , lzop , partclone , qemu-utils , udpcast, uftp
Description: Ogrepsoitory Package
This package provides Ogrepository service.
This package provides Ogrepsoitory service.

View File

@ -1,6 +1,7 @@
api/* /opt/opengnsys/ogrepository/api/
bin/* /opt/opengnsys/ogrepository/bin/
etc/* /opt/opengnsys/ogrepository/etc/
installer/files/ctorrent.sources /etc/apt/sources.list.d/
installer/files/ogrepo-api.service /etc/systemd/system/
installer/files/ogrepo-smb.conf /etc/samba/
installer/ssh-keys/* /opt/opengnsys/ogrepository/etc/

View File

@ -5,25 +5,6 @@ set -x
. /usr/share/debconf/confmodule
restore_config_if_modified() {
local new="$1"
local backup="$1.bak.upgrade_package"
if [ -f "$backup" ]; then
if ! cmp -s "$new" "$backup"; then
echo ">>> Archivo modificado por el usuario detectado en $new"
echo " - Guardando archivo nuevo como ${new}.new"
mv -f "$new" "${new}.new"
echo " - Restaurando archivo anterior desde backup"
mv -f "$backup" "$new"
else
echo ">>> El archivo $new no ha cambiado desde la última versión, eliminando backup"
rm -f "$backup"
fi
fi
}
# Cargar variables de configuración
db_get opengnsys/ogrepository_ogrepoIp
OGREPO_IP="$RET"
@ -40,7 +21,18 @@ USER="opengnsys"
# Provisionar base de datos si es necesario en caso de instalación.
# Detectar si es una instalación nueva o una actualización
if [ "$1" = "configure" ] && [ -z "$2" ]; then
systemd-run --no-block /bin/bash -c "
sleep 10;
apt update -y;
for pkg in bittorrent bittornado ctorrent; do
if ! dpkg -l | grep -qw \"\$pkg\"; then
apt install -y \"\$pkg\"
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
@ -70,7 +62,7 @@ if [ "$1" = "configure" ] && [ -z "$2" ]; then
fi
(echo "$SAMBA_PASS"; echo "$SAMBA_PASS") | smbpasswd -a $SAMBA_USER
fi
systemctl enable ogrepo-api
# Configure Repo
cp /opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg.tmpl /opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg
@ -91,45 +83,12 @@ elif [ "$1" = "configure" ] && [ -n "$2" ]; then
cp /opt/opengnsys/ogrepository/etc/opengnsys.pub $OPENGNSYS_HOME/.ssh/id_ed25519.pub
cat $OPENGNSYS_HOME/.ssh/id_ed25519.pub >> $OPENGNSYS_HOME/.ssh/authorized_keys
restore_config_if_modified "/opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg"
restore_config_if_modified "/opt/opengnsys/ogrepository/etc/repoinfo.json"
restore_config_if_modified "/opt/opengnsys/ogrepository/etc/trashinfo.json"
restore_config_if_modified "/etc/samba/smb.conf"
restore_config_if_modified "/etc/samba/ogrepo-smb.conf"
restore_config_if_modified "/etc/sudoers.d/opengnsys-repository"
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/*
# oggit -- these are from opengnsys-forgejo, and we're overwriting their
# permissions above.
#
# Ensure the forgejo working directory exists and has the right ownership
# before the service tries to start. This is needed because opengnsys-forgejo
# is configured (and starts its service) before ogrepository's postinst runs,
# so without this the service fails with CHDIR/Permission denied.
if getent passwd oggit > /dev/null 2>&1; then
mkdir -p /opt/opengnsys/ogrepository/var/lib/forgejo/work
mkdir -p /opt/opengnsys/ogrepository/var/lib/forgejo/data
chown -R oggit:oggit /opt/opengnsys/ogrepository/var/lib/forgejo/
fi
if [ -d "/opt/opengnsys/ogrepository/oggit/" ] ; then
chown -R oggit:oggit /opt/opengnsys/ogrepository/oggit/
fi
# Restart forgejo now that permissions are correct (it may have failed during
# opengnsys-forgejo's own postinst due to the chown above overwriting its dirs)
if getent passwd oggit > /dev/null 2>&1; then
systemctl reset-failed opengnsys-forgejo 2>/dev/null || true
systemctl restart opengnsys-forgejo 2>/dev/null || true
fi
chmod 755 /opt/opengnsys/ogrepository/bin/
# Install http server stuff
# Reiniciar servicios si es necesario
@ -139,8 +98,39 @@ systemctl daemon-reload
systemctl enable ogrepo-api
systemctl restart ogrepo-api
systemctl restart smbd
systemctl enable --now opentracker || true
systemctl restart opentracker || true
# echo "ogrepository-trigger" > /var/lib/dpkg/triggers/ogrepository.trigger
# POST_INVOKE_SCRIPT="/var/lib/dpkg/info/ogrepository.trigger"
# echo "Registrando post-invoke para instalación de paquetes adicionales..."
# cat <<EOF > "$POST_INVOKE_SCRIPT"
# #!/bin/sh
# set -e
# echo "Ejecutando post-trigger: Instalando bittorrent, bittornado y ctorrent..."
# export DEBIAN_FRONTEND=noninteractive
# apt-get update
# apt-get install -y bittorrent bittornado ctorrent
# echo "Instalación de paquetes adicionales completada."
# exit 0
# EOF
# chmod +x "$POST_INVOKE_SCRIPT"
# # Intentar ejecutar el script ahora si no hay otro proceso de apt corriendo
# if ! pgrep -x "apt" > /dev/null; then
# echo "Ejecutando instalación de paquetes adicionales inmediatamente..."
# "$POST_INVOKE_SCRIPT"
# else
# echo "Apt está en uso, los paquetes se instalarán después."
# fi
# find /opt/opengnsys/ogrepository -type f -name "*.py" -exec chmod +x {} \;
# dpkg-trigger --by-package=ogrepository ogrepository-trigger
exit 0

View File

@ -2,18 +2,8 @@
set -e
backup_file_if_exists() {
local original="$1"
local backup="$1.bak.upgrade_package"
if [ -e "$original" ]; then
echo " - Guardando backup de $original en $backup"
cp -a "$original" "$backup"
fi
}
# KEY_FILE="/usr/share/keyrings/debian-archive-buster-stable.gpg"
# REPO_FILE="/etc/apt/sources.list.d/buster.list"
KEY_FILE="/usr/share/keyrings/debian-archive-buster-stable.gpg"
REPO_FILE="/etc/apt/sources.list.d/buster.list"
# Asegurarse de que el usuario exista
USER="opengnsys"
@ -24,30 +14,16 @@ else
echo "Creando el usuario $USER con home en $HOME_DIR."
useradd -m -d "$HOME_DIR" -s /bin/bash "$USER"
fi
# Forzar 755 en /opt/opengnsys independientemente del umask del sistema.
# useradd -m puede crear el directorio con 750 si el umask es 027, lo que
# impide que el usuario oggit (de opengnsys-forgejo) atraviese el árbol
# antes de que el postinst de ogrepository aplique su propio chmod.
mkdir -p "$HOME_DIR"
chmod 755 "$HOME_DIR"
echo "Añadiendo el repositorio de Debian Buster en $REPO_FILE..."
# Crear el directorio si no existe
mkdir -p "$(dirname "$REPO_FILE")"
# Crear el archivo de repositorio si no existe
# if [ ! -f "$REPO_FILE" ]; then
# echo "deb [signed-by=$KEY_FILE] http://ftp.de.debian.org/debian buster main" > "$REPO_FILE"
# else
# echo "El repositorio ya está configurado en $REPO_FILE"
# fi
backup_file_if_exists "/opt/opengnsys/ogrepository/etc/ogAdmRepo.cfg"
backup_file_if_exists "/opt/opengnsys/ogrepository/etc/repoinfo.json"
backup_file_if_exists "/opt/opengnsys/ogrepository/etc/trashinfo.json"
backup_file_if_exists "/etc/samba/smb.conf"
backup_file_if_exists "/etc/samba/ogrepo-smb.conf"
backup_file_if_exists "/etc/sudoers.d/ogrepository"
if [ ! -f "$REPO_FILE" ]; then
echo "deb [signed-by=$KEY_FILE] http://ftp.de.debian.org/debian buster main" > "$REPO_FILE"
else
echo "El repositorio ya está configurado en $REPO_FILE"
fi
exit 0

View File

@ -10,25 +10,6 @@ Cmnd_Alias MOUNT_RECOVERY = \
/usr/bin/umount /mnt/recovery/sys, \
/usr/bin/umount -l /mnt/recovery
Cmnd_Alias OGBOOT = \
/opt/bin/oglivecli, \
/usr/bin/chmod, \
/usr/bin/chown, \
/usr/bin/md5sum, \
/usr/bin/smbpasswd, \
/usr/bin/cat, \
/usr/bin/tee, \
/usr/bin/sed, \
/usr/bin/gzip, \
/usr/bin/lz4, \
/usr/bin/cpio, \
/usr/bin/find, \
/bin/tee, \
/usr/bin/dd, \
/usr/bin/mkfs.ext4, \
/usr/bin/rsync, \
/opt/opengnsys/ogboot/lib/*.iso /mnt
Cmnd_Alias CHROOT_GRUB = \
/usr/sbin/chroot /mnt/recovery /usr/sbin/grub-install --target=i386-pc *, \
/usr/sbin/chroot /mnt/recovery /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg
@ -44,7 +25,5 @@ Cmnd_Alias KILL_BT = \
/usr/bin/pkill -9 bttrack, \
/usr/bin/kill -9 *
Cmnd_Alias PYTHON_OGREPO = /usr/bin/python3 /opt/opengnsys/ogrepository/bin/*
# Permitir al usuario opengnsys ejecutar estos comandos sin contraseña
opengnsys ALL=(root) NOPASSWD: MOUNT_RECOVERY, CHROOT_GRUB, LOOP_KPARTX, OGBOOT, KILL_BT, PYTHON_OGREPO
opengnsys ALL=(root) NOPASSWD: MOUNT_RECOVERY, CHROOT_GRUB, LOOP_KPARTX, KILL_BT

View File

@ -0,0 +1,6 @@
Types: deb
URIs: http://ftp.de.debian.org/debian
Suites: buster
Components: main
Signed-By: /usr/share/keyrings/debian-archive-buster-stable.gpg