oginstaller get oglives list from server or return default value

main
Nicolas Arenas 2025-03-22 12:20:11 +01:00
parent 36b8ee0250
commit ee6c7c2003
2 changed files with 151 additions and 58 deletions

View File

@ -3,7 +3,6 @@ import os
from git import Repo
import subprocess # Importar el módulo subprocess
import requests # Importar el módulo requests
from tqdm import tqdm # Importar tqdm para la barra de progreso
import time # Importar time para simular el progreso
import threading # Importar threading para leer el log en tiempo real
import socket
@ -64,6 +63,25 @@ def get_default_ip():
print(f"Error al obtener la IP por defecto: {e}")
return "192.168.2.2" # Valor por defecto
def get_oglive_list():
"""Obtiene la lista de valores de oglives desde la URL."""
try:
# Realizar la solicitud HTTP
response = requests.get("https://ognproject.evlt.uma.es/oglive/", timeout=10)
response.raise_for_status() # Lanza una excepción si la respuesta no es 200 OK
# Extraer los enlaces del contenido HTML
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, "html.parser")
links = [a["href"] for a in soup.find_all("a", href=True) if "ogLive" in a["href"]]
# Ordenar los enlaces por la parte después del guion bajo
sorted_links = sorted(links, key=lambda x: x.split("_")[1] if "_" in x else x, reverse=True)
return sorted_links
except Exception as e:
print(f"Error al obtener la lista de oglives: {e}")
return [] # Devolver una lista vacía en caso de error
# Variable global para la IP por defecto
DEFAULT_IP = get_default_ip()
@ -150,7 +168,7 @@ class OgGuiForm(ComponentForm):
def configure_fields(self):
self.fields["ogcoreUrl"] = {"widget": self.add(npyscreen.TitleText, name="URL API OgCore:", value="https://{}:8443".format(DEFAULT_IP))}
self.fields["ogmercureUrl"] = {"widget": self.add(npyscreen.TitleText, name="Mercure URL:", value="https://{} gr:3000/.well-known/mercure".format(DEFAULT_IP))}
self.fields["ogmercureUrl"] = {"widget": self.add(npyscreen.TitleText, name="Mercure URL:", value="https://{}:3000/.well-known/mercure".format(DEFAULT_IP))}
class OgDhcpForm(ComponentForm):
component_name = "ogdhcp"
@ -217,13 +235,57 @@ class OgBootForm(ComponentForm):
component_name = "ogboot"
def configure_fields(self):
# Obtener la lista de oglives
oglives = get_oglive_list()
if not oglives:
oglives = ["https://ognproject.evlt.uma.es/oglive/ogLive-noble-6.8.0-31-generic-amd64-r20250116.538e3fa_20250120.iso"]
npyscreen.notify_confirm("No se pudo obtener la lista de oglives. Usando un valor por defecto.", title="Error")
# Campo para seleccionar un oglive
self.fields["ogliveUrl"] = {
"widget": self.add(
npyscreen.TitleSelectOne,
name="Selecciona un OgLive:",
values=oglives,
scroll_exit=True,
max_height=10 # Limitar la altura para listas largas
)
}
# Otros campos
self.fields["ip"] = {"widget": self.add(npyscreen.TitleText, name="IP del servidor Boot:", value=DEFAULT_IP)}
self.fields["port"] = {"widget": self.add(npyscreen.TitleText, name="Puerto Boot:", value="8082")}
self.fields["ogcoreUrl"] = {"widget": self.add(npyscreen.TitleText, name="URL OgCore:", value=f"https://{DEFAULT_IP}:8443")}
self.fields["ogliveUrl"] = {"widget": self.add(npyscreen.TitleText, name="URL OgLive:", value="https://ognproject.evlt.uma.es/oglive/ogLive-noble-6.8.0-31-generic-amd64-r20250116.538e3fa_20250120.iso")}
self.fields["sambaUser"] = {"widget": self.add(npyscreen.TitleText, name="Usuario Samba:", value="opengnsys")}
self.fields["sambaUserPass"] = {"widget": self.add(npyscreen.TitlePassword, name="Contraseña Samba:", value="og")}
def on_ok(self):
# Obtener el oglive seleccionado
selected_oglive_index = self.fields["ogliveUrl"]["widget"].value
if selected_oglive_index:
selected_oglive = self.fields["ogliveUrl"]["widget"].values[selected_oglive_index[0]]
else:
selected_oglive = None
# Guardar las configuraciones
self.parentApp.configurations[self.component_name] = {
"ogliveUrl": selected_oglive,
"ip": self.fields["ip"]["widget"].value,
"port": self.fields["port"]["widget"].value,
"ogcoreUrl": self.fields["ogcoreUrl"]["widget"].value,
"sambaUser": self.fields["sambaUser"]["widget"].value,
"sambaUserPass": self.fields["sambaUserPass"]["widget"].value,
}
# Continuar con el siguiente formulario
self.parentApp.current_component_index += 1
if self.parentApp.current_component_index < len(self.parentApp.selected_components):
next_component = self.parentApp.selected_components[self.parentApp.current_component_index]
self.parentApp.switchForm(next_component)
else:
self.parentApp.generate_debconf()
self.parentApp.setNextForm(None)
class OgRepositoryForm(ComponentForm):
component_name = "ogrepository"
@ -233,8 +295,50 @@ class OgRepositoryForm(ComponentForm):
self.fields["sambaUser"] = {"widget": self.add(npyscreen.TitleText, name="Usuario Samba:", value="opengnsys")}
self.fields["sambaUserPass"] = {"widget": self.add(npyscreen.TitlePassword, name="Contraseña Samba:", value="og")}
def install_components(components, selected_tag):
"""Instala los componentes seleccionados usando el tag especificado."""
class InstallationProgressForm(npyscreen.FormBaseNew):
"""Formulario para mostrar el progreso de instalación y el log en tiempo real."""
def create(self):
# Crear la parte superior para el progreso de instalación
self.progress_box = self.add(
npyscreen.BoxTitle,
name="Progreso de instalación",
max_height=int(self.lines * 0.5), # Mitad superior
scroll_exit=True
)
# Crear la parte inferior para el log en tiempo real
self.log_box = self.add(
npyscreen.BoxTitle,
name="Log en tiempo real",
rely=int(self.lines * 0.5), # Mitad inferior
scroll_exit=True
)
def update_progress(self, message, current=0, total=0):
"""Actualiza el progreso de instalación en la parte superior."""
if total > 0:
# Crear una barra de progreso personalizada
progress_percentage = int((current / total) * 100)
bar_length = 30 # Longitud de la barra
filled_length = int(bar_length * current // total)
bar = f"[{'=' * filled_length}{' ' * (bar_length - filled_length)}] {progress_percentage}%"
self.progress_box.values.append(bar)
self.progress_box.values.append(message)
self.progress_box.display()
def update_log(self, log_lines):
"""Actualiza el log en tiempo real en la parte inferior."""
# Limpiar caracteres especiales de las líneas del log
cleaned_lines = [self._clean_text(line) for line in log_lines]
self.log_box.values = cleaned_lines[-self.log_box.height:] # Mostrar solo las últimas líneas
self.log_box.display()
def _clean_text(self, text):
"""Elimina caracteres especiales o no imprimibles del texto."""
return ''.join(c if c.isprintable() else '?' for c in text)
def install_components_with_ui(form, components, selected_tag):
"""Instala los componentes seleccionados mostrando el progreso y el log en tiempo real."""
log_file_path = os.path.join(CONFIGS_DIR, "installation.log")
installed_packages = [] # Lista de paquetes instalados correctamente
failed_packages = [] # Lista de paquetes que fallaron
@ -245,61 +349,44 @@ def install_components(components, selected_tag):
try:
with open(log_file_path, "w") as log_file:
total_packages = len(components)
# Hilo para leer el log en tiempo real
def tail_log():
with open(log_file_path, "r") as log_reader:
log_reader.seek(0, os.SEEK_END) # Ir al final del archivo
while True:
line = log_reader.readline()
if line:
form.update_log(log_reader.readlines())
time.sleep(0.1)
log_thread = threading.Thread(target=tail_log, daemon=True)
log_thread.start()
for index, package in enumerate(components, start=1):
# Mostrar solo el progreso en la terminal
print(f"Instalando paquete {index}/{total_packages}: {package}")
log_file.write(f"\n--- Instalando paquete {index}/{total_packages}: {package} ---\n")
# Actualizar el progreso en la parte superior con barra de progreso
form.update_progress(f"Instalando paquete {index}/{total_packages}: {package}", current=index, total=total_packages)
# Crear una barra de progreso para el paquete
with tqdm(total=100, desc=f"Instalando {package}", unit="%", ncols=80) as progress_bar:
# Ejecutar el comando y redirigir toda la salida al archivo de registro
install_command = f"DEBIAN_FRONTEND=noninteractive apt-get install -y {package}"
process = subprocess.Popen(
install_command, shell=True, text=True, stdout=log_file, stderr=log_file
)
install_command = f"DEBIAN_FRONTEND=noninteractive apt-get install -y {package}"
process = subprocess.Popen(
install_command, shell=True, text=True, stdout=log_file, stderr=log_file
)
# Función para leer el log en tiempo real y actualizar la barra de progreso
def monitor_log():
with open(log_file_path, "r") as log_reader:
log_reader.seek(0, os.SEEK_END) # Ir al final del archivo
while process.poll() is None:
line = log_reader.readline()
if not line:
time.sleep(0.1) # Esperar si no hay nuevas líneas
continue
# Actualizar la barra de progreso según las cadenas detectadas
if "Se necesita descargar" in line:
progress_bar.n = 10
elif "Preparando para desempaquetar" in line:
progress_bar.n = 30
elif "Configurando" in line and package in line:
progress_bar.n = 40
progress_bar.refresh()
# Esperar a que el proceso de instalación termine
process.wait()
# Iniciar el hilo para monitorear el log
log_thread = threading.Thread(target=monitor_log, daemon=True)
log_thread.start()
# Esperar a que el proceso de instalación termine
process.wait()
# Completar la barra de progreso
progress_bar.n = 100
progress_bar.refresh()
# Registrar errores en el archivo de registro
if process.returncode != 0:
error_message = f"Error al instalar el paquete {package}. Consulta el archivo de registro: {log_file_path}"
print(error_message)
log_file.write(f"\n{error_message}\n")
failed_packages.append(package) # Agregar a la lista de fallos
else:
log_file.write(f"Paquete {package} instalado correctamente.\n")
installed_packages.append(package) # Agregar a la lista de éxitos
# Registrar errores en el archivo de registro
if process.returncode != 0:
error_message = f"Error al instalar el paquete {package}. Consulta el archivo de registro: {log_file_path}"
form.update_progress(error_message)
failed_packages.append(package) # Agregar a la lista de fallos
else:
form.update_progress(f"Paquete {package} instalado correctamente.")
installed_packages.append(package) # Agregar a la lista de éxitos
except Exception as e:
with open(log_file_path, "a") as log_file:
log_file.write(f"\nError durante la instalación de los paquetes: {e}\n")
failed_packages.append("Error general durante la instalación")
form.update_progress(f"Error durante la instalación: {e}")
failed_packages.append("Error general durante la instalación")
# Registrar el tiempo de finalización
end_time = time.time()
@ -322,6 +409,9 @@ def install_components(components, selected_tag):
# Mostrar el resumen en una ventana emergente
npyscreen.notify_confirm(summary, title="Resumen de la instalación", wide=True)
# Mostrar el resumen en la parte superior (opcional, si quieres mantenerlo en el formulario)
form.update_progress(summary)
class MyApp(npyscreen.NPSAppManaged):
def onStart(self):
self.addForm("MAIN", ComponentSelectionForm)
@ -330,6 +420,7 @@ class MyApp(npyscreen.NPSAppManaged):
self.addForm("ogdhcp", OgDhcpForm)
self.addForm("ogboot", OgBootForm)
self.addForm("ogrepository", OgRepositoryForm)
self.addForm("INSTALLATION_PROGRESS", InstallationProgressForm)
def generate_debconf(self):
# Comprobar si la clave pública ya existe
@ -387,8 +478,10 @@ class MyApp(npyscreen.NPSAppManaged):
# Silenciar el mensaje de configuraciones guardadas
# print(f"\nConfiguraciones guardadas en: {output_file}")
# Llamar a la función de instalación después de generar las configuraciones
install_components(self.selected_components, self.selected_tag)
# Llamar al formulario de progreso
form = self.getForm("INSTALLATION_PROGRESS")
self.switchForm("INSTALLATION_PROGRESS")
install_components_with_ui(form, self.selected_components, self.selected_tag)
if __name__ == "__main__":
MyApp().run()

View File

@ -1,4 +1,4 @@
GitPython
npyscreen
requests
tqdm
bs4