oginstaller/non_graf_installer/python-installer/oginstaller-v3.py

303 lines
14 KiB
Python

import npyscreen
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
CONFIGS_DIR = "/tmp/oginstall"
os.makedirs(CONFIGS_DIR, exist_ok=True)
REPO_URL = "https://ognproject.evlt.uma.es/gitea/opengnsys/ogcore.git"
def get_network_interfaces():
"""Obtiene los nombres de las interfaces de red disponibles en el servidor."""
try:
# Listar las interfaces de red desde /sys/class/net
interfaces = os.listdir('/sys/class/net')
# Filtrar interfaces válidas (excluyendo interfaces virtuales como 'lo')
valid_interfaces = [iface for iface in interfaces if not iface.startswith('lo')]
return ','.join(valid_interfaces) # Devuelve las interfaces separadas por comas
except Exception as e:
# En caso de error, devolver un valor por defecto
print(f"Error al obtener las interfaces de red: {e}")
return "eth0" # Valor por defecto
def get_available_versions():
"""Obtiene la lista de versiones desde el archivo JSON remoto."""
try:
url = "https://ognproject.evlt.uma.es/debian-opengnsys/versions.json"
# Redirigir la salida de la descarga a /dev/null
response = requests.get(url, timeout=10)
response.raise_for_status() # Lanza una excepción si la respuesta no es 200 OK
data = response.json()
return data.get("versions", [])
except requests.RequestException:
# Silenciar errores y devolver una lista vacía
return []
class ComponentSelectionForm(npyscreen.ActionForm):
def create(self):
self.components = self.add(npyscreen.TitleMultiSelect, max_height=6, name="Selecciona los componentes",
values=["ogCore", "ogGui", "ogDhcp", "ogBoot", "ogRepository"], scroll_exit=True)
self.versions = get_available_versions() # Obtener las versiones desde el archivo JSON
self.tag = self.add(npyscreen.TitleSelectOne, max_height=10, name="Selecciona la versión",
values=self.versions, scroll_exit=True)
def on_ok(self):
npyscreen.blank_terminal()
selected_components = [self.components.values[i].lower() for i in self.components.value] # Convertir a minúsculas
if not selected_components:
npyscreen.notify_confirm("Debes seleccionar al menos un componente.", title="Error")
return
if not self.tag.value:
npyscreen.notify_confirm("Debes seleccionar una versión.", title="Error")
return
selected_tag = self.versions[self.tag.value[0]] # Usar la versión seleccionada
self.parentApp.selected_components = selected_components
self.parentApp.selected_tag = selected_tag
self.parentApp.current_component_index = 0
self.parentApp.configurations = {} # Almacena los valores configurados
self.parentApp.switchForm(selected_components[0]) # Ya están en minúsculas
class ComponentForm(npyscreen.ActionForm):
component_name = None
def create(self):
self.fields = {}
def beforeEditing(self):
npyscreen.blank_terminal()
self.fields.clear()
self._recreate_form()
def _recreate_form(self):
"""Limpia y recrea los widgets del formulario."""
self._clear_widgets()
self.configure_fields()
def configure_fields(self):
"""Método para definir los campos de configuración para cada componente"""
pass
def _clear_widgets(self):
"""Limpia todos los widgets del formulario."""
self._widgets__ = []
self._widgets_by_id__ = {}
self._contained_widgets = []
def on_ok(self):
npyscreen.blank_terminal()
component_config = {}
for key, field_data in self.fields.items():
component_config[key] = field_data["widget"].value
self.parentApp.configurations[self.component_name] = component_config
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)
def on_cancel(self):
if npyscreen.notify_yes_no("¿Estás seguro de que deseas salir?", title="Confirmación"):
self.parentApp.setNextForm(None)
class OgCoreForm(ComponentForm):
component_name = "ogcore"
def configure_fields(self):
self.fields["adminUser"] = {"widget": self.add(npyscreen.TitleText, name="Usuario administrador:", value="ogadmin")}
self.fields["adminPass"] = {"widget": self.add(npyscreen.TitlePassword, name="Contraseña:", value="12345678")}
class OgGuiForm(ComponentForm):
component_name = "oggui"
def configure_fields(self):
self.fields["ogcoreUrl"] = {"widget": self.add(npyscreen.TitleText, name="URL API OgCore:", value="https://127.0.0.1:8443")}
self.fields["ogmercureUrl"] = {"widget": self.add(npyscreen.TitleText, name="Mercure URL:", value="https://127.0.0.1:3000/.well-known/mercure")}
class OgDhcpForm(ComponentForm):
component_name = "ogdhcp"
interfaces = get_network_interfaces()
def configure_fields(self):
self.fields["interfaces"] = {"widget": self.add(npyscreen.TitleText, name="Interfaces ({}):".format(self.interfaces), value=self.interfaces)}
self.fields["ip"] = {"widget": self.add(npyscreen.TitleText, name="IP del servidor DHCP:", value="192.168.2.2")}
self.fields["ogbootIP"] = {"widget": self.add(npyscreen.TitleText, name="IP del servidor Boot:", value="192.168.2.2")}
class OgBootForm(ComponentForm):
component_name = "ogboot"
def configure_fields(self):
self.fields["ip"] = {"widget": self.add(npyscreen.TitleText, name="IP del servidor Boot:", value="192.168.2.2")}
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="https://192.168.2.2:8443")}
self.fields["ogliveUrl"] = {"widget": self.add(npyscreen.TitleText, name="URL OgLive:", value="https://ognproject.evlt.uma.es/oglive/...")}
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")}
class OgRepositoryForm(ComponentForm):
component_name = "ogrepository"
def configure_fields(self):
self.fields["ogrepoIp"] = {"widget": self.add(npyscreen.TitleText, name="IP del Repositorio:", value="192.168.2.2")}
self.fields["ogcoreIp"] = {"widget": self.add(npyscreen.TitleText, name="IP de OgCore:", value="192.168.2.2")}
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."""
log_file_path = os.path.join(CONFIGS_DIR, "installation.log")
installed_packages = [] # Lista de paquetes instalados correctamente
failed_packages = [] # Lista de paquetes que fallaron
try:
with open(log_file_path, "w") as log_file:
total_packages = len(components)
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")
# 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
)
# 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()
# 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
break # Detener la instalación si ocurre un error
else:
log_file.write(f"Paquete {package} instalado correctamente.\n")
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")
# Mostrar un resumen al final
print("\n--- Resumen de la instalación ---")
print(f"Paquetes instalados correctamente: {len(installed_packages)}")
for pkg in installed_packages:
print(f" - {pkg}")
if failed_packages:
print(f"\nPaquetes que fallaron: {len(failed_packages)}")
for pkg in failed_packages:
print(f" - {pkg}")
else:
print("\nTodos los paquetes se instalaron correctamente.")
print(f"\nConsulta el archivo de registro para más detalles: {log_file_path}")
class MyApp(npyscreen.NPSAppManaged):
def onStart(self):
self.addForm("MAIN", ComponentSelectionForm)
self.addForm("ogcore", OgCoreForm)
self.addForm("oggui", OgGuiForm)
self.addForm("ogdhcp", OgDhcpForm)
self.addForm("ogboot", OgBootForm)
self.addForm("ogrepository", OgRepositoryForm)
def generate_debconf(self):
# Comprobar si la clave pública ya existe
key_path = "/etc/apt/trusted.gpg.d/opengnsys.gpg"
if os.path.exists(key_path):
# Silenciar este mensaje
pass
else:
# Añadir la clave pública
try:
subprocess.run(
'curl -k -L https://ognproject.evlt.uma.es/debian-opengnsys/public.key | gpg --dearmour -o /etc/apt/trusted.gpg.d/opengnsys.gpg',
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True
)
except subprocess.CalledProcessError:
# Silenciar errores
return
# Añadir el repositorio
try:
selected_tag = self.selected_tag # Obtener el tag seleccionado
repo_line = f'deb http://ognproject.evlt.uma.es/debian-opengnsys/opengnsys-devel/{selected_tag} noble main'
with open('/etc/apt/sources.list.d/opengnsys.list', 'w') as repo_file:
repo_file.write(repo_line + '\n')
except Exception:
# Silenciar errores
return
# Actualizar los repositorios
try:
subprocess.run('apt-get update', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
except subprocess.CalledProcessError:
# Silenciar errores
return
# Generar configuraciones para debconf
output_file = os.path.join(CONFIGS_DIR, "configurations.txt")
try:
with open(output_file, "w") as f:
f.write("\n--- Configuraciones para debconf-set-selections ---\n")
for component, config in self.configurations.items():
for key, value in config.items():
field_type = "password" if "Pass" in key else "string"
line = f'echo "{component} opengnsys/{component}_{key} {field_type} {value}" | debconf-set-selections\n'
f.write(line)
# Ejecutar la línea directamente y redirigir la salida a /dev/null
subprocess.run(
line, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
except Exception:
# Silenciar errores
pass
# 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)
if __name__ == "__main__":
MyApp().run()