diff --git a/component-installer.sh b/component-installer.sh index 7cb7128..34819a3 100644 --- a/component-installer.sh +++ b/component-installer.sh @@ -82,7 +82,7 @@ OGBOOT_REPO="$OPENGNSYS_BASE_URL/ogboot.git" OGCORE_REPO="$OPENGNSYS_BASE_URL/ogcore.git" OGDHCP_REPO="$OPENGNSYS_BASE_URL/ogdhcp.git" OGGUI_REPO="$OPENGNSYS_BASE_URL/oggui.git" -OGREPOSITORY_REPO="$OPENGNSYS_BASE_URL/ogrepo.git" +OGREPOSITORY_REPO="$OPENGNSYS_BASE_URL/ogrepository.git" export GIT_SSL_NO_VERIFY=1 echo ======================================== > /etc/issue diff --git a/component-installer/component-installer.sh b/component-installer/component-installer.sh new file mode 100644 index 0000000..54a3125 --- /dev/null +++ b/component-installer/component-installer.sh @@ -0,0 +1,194 @@ +#!/usr/bin/bash +set -x +# Paso 1: Seleccionar los componentes +# Los componentes a instalar se encuentran en el directorio /tmp/opengnsys-installer-configs +# Set configuration + + +COMPONENTS="ogCore ogGui ogDhcp ogBoot ogRepository" +CONFIGS_DIR=/tmp/oginstall +# PAT_FILE=/opengnsys-installer/pat.txt +# PAT=$(cat $PAT_FILE | tr -d '\n\r\t') + +# OPENGNSYS_BASE_URL="https://$PAT@ognproject.evlt.uma.es/gitea/opengnsys"ls +OPENGNSYS_BASE_URL="https://ognproject.evlt.uma.es/gitea/opengnsys" + +OGBOOT_REPO="$OPENGNSYS_BASE_URL/ogboot.git" +OGCORE_REPO="$OPENGNSYS_BASE_URL/ogcore.git" +OGDHCP_REPO="$OPENGNSYS_BASE_URL/ogdhcp.git" +OGGUI_REPO="$OPENGNSYS_BASE_URL/oggui.git" +OGREPOSITORY_REPO="$OPENGNSYS_BASE_URL/ogrepository.git" +export GIT_SSL_NO_VERIFY=1 + + +## Functions + +function install_docker() { + apt-get -y update + apt-get -y install ca-certificates curl + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + chmod a+r /etc/apt/keyrings/docker.asc + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + tee /etc/apt/sources.list.d/docker.list > /dev/null + apt-get -y update + apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + systemctl enable docker +} + +function install_ogcore_docker() { +cat < /etc/systemd/system/ogcore.service +[Unit] +Description=Servicio para ejecutar Docker Compose de ogCore +After=docker.service +Requires=docker.service + +[Service] +WorkingDirectory=/opt/opengnsys/ogCore/repo/ +ExecStart=/usr/bin/docker compose -f /opt/opengnsys/ogCore/etc/docker-compose-deploy.yml up +ExecStartPost=/opt/opengnsys/ogCore/bin/provision_ogcore.sh +ExecStop=/usr/bin/docker compose -f /opt/opengnsys/ogCore/etc/docker-compose-deploy.yml stop +Restart=always +TimeoutStartSec=600 + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload + systemctl enable --now ogcore +} + + +function install_oggui_docker() { + # Sacar la IP del ogCore de la configuración + oggui_version=$(jq -r '.release' /opt/opengnsys/ogGui/installer/config.json) + # Exportar los valores como variables de entorno + ENV_DIR=/opt/opengnsys/ogGui/etc/ + ENV_FILE=$ENV_DIR/.env + + cat < /etc/systemd/system/oggui-app.service +[Unit] +Description=Servicio para contenedor Docker de OgGui +After=docker.service +Requires=docker.service + +[Service] +Restart=always +ExecStartPre=/opt/opengnsys/ogGui/bin/provision_oggui.sh +ExecStart=/usr/bin/docker run --rm --name ogGui-app -p 4200:4200 -v $ENV_FILE:/app/.env opengnsys/oggui:$oggui_version +ExecStop=/usr/bin/docker stop ogGui-app +TimeoutStartSec=600 + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload + systemctl enable --now oggui-app + +} + +function git_checkout_release() { + git clone --no-checkout "$1" "$2" + cd "$2" || exit + git checkout tags/"$3" + cd - || exit +} + + + +echo ======================================== > /etc/issue +echo "OpenGnSys Installer" >> /etc/issue +echo "Componentes instalados:" >> /etc/issue + + + +for component in $COMPONENTS +do + config_file="config_${component}.json" + if [ -f $CONFIGS_DIR/$config_file ]; then + echo "Componente $component seleccionado, instalando configuración..." + component_dir=/opt/opengnsys/$component + mkdir -p $component_dir/installer + mkdir -p $component_dir/repo + cp $CONFIGS_DIR/$config_file /opt/opengnsys/$component/installer/config.json + + case $component in + ogCore) + echo "Instalando ogCore..." + OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogCore/installer/config.json) + container_version=$(jq -r '.release' /opt/opengnsys/ogCore/installer/config.json) + git_checkout_release "$OGCORE_REPO" "$component_dir/repo" "$OGCORE_BRANCH" + mkdir -p $component_dir/etc + cp $component_dir/repo/.env $component_dir/etc/ + cp $component_dir/repo/env.json $component_dir/etc/ + mkdir -p $component_dir/etc/ + mkdir -p $component_dir/bin/ + cp $CONFIGS_DIR/provision_ogcore.sh $component_dir/bin/ + chmod 755 $component_dir/bin/provision_ogcore.sh + cp $component_dir/repo/docker-compose-deploy.yml $component_dir/etc/ + sed -i "s/static/$container_version/g" $component_dir/etc/docker-compose-deploy.yml + cat $component_dir/etc/docker-compose-deploy.yml + echo - ogCore >> /etc/issue + install_docker + install_ogcore_docker + ;; + ogGui) + echo "Instalando ogGui..." + OGGUI_BRANCH=$(jq -r '.container_version' /opt/opengnsys/ogGui/installer/config.json) + mkdir -p $component_dir/bin + cp $CONFIGS_DIR/provision_oggui.sh $component_dir/bin/ + chmod 755 $component_dir/bin/provision_oggui.sh + git_checkout_release "$OGGUI_REPO" "$component_dir/repo" "$OGGUI_BRANCH" + echo - ogGui >> /etc/issue + install_docker + install_oggui_docker + ;; + ogDhcp) + echo "Instalando ogDhcp..." + OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogDhcp/installer/config.json) + git_checkout_release "$OGDHCP_REPO" "$component_dir/repo" "$OGCORE_BRANCH" + cp $CONFIGS_DIR/$config_file $component_dir/repo/installer/config_ogdhcp.json + cd $component_dir/repo/installer || exit + chmod 755 ogdhcp_installer.sh + ./ogdhcp_installer.sh + cd - || exit + echo - ogDhcp >> /etc/issue + ;; + ogBoot) + OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogBoot/installer/config.json) + echo "Instalando ogBoot..." + git_checkout_release "$OGBOOT_REPO" "$component_dir/repo" "$OGCORE_BRANCH" + cp $CONFIGS_DIR/$config_file $component_dir/repo/installer/config.json + apt install -y python3 git vim + cd $component_dir/repo/installer || exit + python3 ogboot_installer.py + cd - || exit + echo - ogBoot >> /etc/issue + ;; + ogRepository) + echo "Instalando ogRepository..." + OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogRepository/installer/config.json) + git_checkout_release "$OGREPOSITORY_REPO" "$component_dir/repo" "$OGCORE_BRANCH" + cp $CONFIGS_DIR/$config_file $component_dir/installer/config.json + REPO_IP=$(jq -r '.ogrepository_ip' $component_dir/installer/config.json) + CORE_IP=$(jq -r '.ogcore_server_ip' $component_dir/installer/config.json) + OGUSER=$(jq -r '.ogrepository_samba_user' $component_dir/installer/config.json) + OGPASS=$(jq -r '.ogrepository_samba_pass' $component_dir/installer/config.json) + mkdir -p $component_dir/bin + cp $CONFIGS_DIR/provision_ogrepository.sh $component_dir/bin/ + chmod 755 $component_dir/bin/provision_ogrepository.sh + $component_dir/bin/provision_ogrepository.sh $REPO_IP $CORE_IP $OGUSER $OGPASS $component_dir/repo + echo - ogRepository >> /etc/issue + ;; + *) + echo "Componente $component no reconocido" + ;; + esac + continue + fi +done + +echo ======================================== >> /etc/issue +# rm -f $PAT_FILE diff --git a/component-installer/provision_ogcore.sh b/component-installer/provision_ogcore.sh new file mode 100644 index 0000000..8e7cd18 --- /dev/null +++ b/component-installer/provision_ogcore.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# +set -x + +CONF_DIR=/opt/opengnsys/ogCore/etc/ +cd /opt/opengnsys/ogCore/repo/ + +# Preparar el fichero .yaml +# CONF_DIR=/opt/opengnsys/ogCore/etc/ +# mkdir -p $CONF_DIR + +# Copiar el fichero de configuración a CONF_DIR +# cp docker-compose-deploy.yml $CONF_DIR/ + +if [ -f /opt/opengnsys/ogCore/installer/.deployed ]; then + echo "ogCore ya instalado" + exit 0 +fi + +while ! docker compose -f $CONF_DIR/docker-compose-deploy.yml ps --format json |jq -r '"\(.Name) \(.State)"' |grep -q 'ogcore-php running'; do + sleep 2 +done + +adminuser=$(jq -r '.username' /opt/opengnsys/ogCore/installer/config.json) +adminpass=$(jq -r '.password' /opt/opengnsys/ogCore/installer/config.json) + +docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php composer install +docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console lexik:jwt:generate-keypair --overwrite +docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console doctrine:migrations:migrate --no-interaction +## TODO we need to feed $adminuser and $adminpass to doctrine:fixtures:load somehow +docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console doctrine:fixtures:load --no-interaction + + +# Provision user admin +bearer=$(curl -k -X 'POST' 'https://localhost:8443/auth/login' -H 'accept: application/json' -H 'Content-Type: application/json' -d "{ \"username\": \"ogadmin\", \"password\": \"12345678\" }" | jq .token | sed 's/"//g' ) + + +if [ $adminuser == "ogadmin" ]; then + echo "Cambiando password a ogadmin no puede ser el usuario administrador" + ogadmin_uuid=$(curl -q -k -L https://localhost:8443/users/?username=ogadmin -H 'accept: application/json' -H "Authorization: Bearer $bearer" | jq .[0].uuid | sed 's/"//g') + curl -k -L -X PUT "https://localhost:8443/users/$ogadmin_uuid/reset-password" -H 'accept: application/ld+json' -H 'Content-Type: application/ld+json' -d "{\"currentPassword\": \"12345678\", \"newPassword\": \"$adminpass\", \"repeatNewPassword\": \"$adminpass\"}" -H "Authorization: Bearer $bearer" + touch /opt/opengnsys/ogCore/installer/.deployed + exit 0 +fi + +curl -k -L --location 'https://localhost:8443/users' \ +--header 'Content-Type: application/json' \ +--header "Authorization: Bearer $bearer" \ +--data "{ \"username\": \"$adminuser\", \"password\": \"$adminpass\", \"roles\": [\"ROLE_SUPER_ADMIN\"] }" + +touch /opt/opengnsys/ogCore/installer/.deployed +exit 0 diff --git a/component-installer/provision_oggui.sh b/component-installer/provision_oggui.sh new file mode 100644 index 0000000..7648afb --- /dev/null +++ b/component-installer/provision_oggui.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +set -x + +# preparar el fichero .env +ENV_DIR=/opt/opengnsys/ogGui/etc/ +ENV_FILE=$ENV_DIR/.env +mkdir -p $ENV_DIR + +# Comprobar si ya se ha instalado ogCore +if [ -f /opt/opengnsys/ogGui/installer/.deployed ]; then + echo "ogCore ya instalado" + exit 0 +fi + +# Sacar la IP del ogCore de la configuración +ogcore_ip=$(jq -r '.ogcore_ip' /opt/opengnsys/ogGui/installer/config.json) +export OGCORE_IP="$ogcore_ip" + +# Si no se ha configurado la IP del ogCore, se intenta obtener de la interfaz de red +if [ -z "$ogcore_ip" ]; then + # Obtiene el nombre del interfaz asociado a la ruta por defecto + interface=$(ip route | grep default | awk '{print $5}') + + # Si se encuentra el interfaz, obtiene su dirección IP + if [ -n "$interface" ]; then + ip_address=$(ip -o -4 addr show "$interface" | awk '{print $4}' | cut -d'/' -f1) + ogcore_ip=$ip_address + # Si no se ha configurado la IP del ogCore, se escribe en el fichero .env + echo "NG_APP_BASE_API_URL=https://$ogcore_ip:8443" > $ENV_FILE + exit 0 + else + echo "No se pudo determinar el interfaz asociado a la ruta por defecto." + exit 1 + fi +fi + + +# Si se ha configurado la IP del ogCore, se escribe en el fichero .env +echo "NG_APP_BASE_API_URL=$OGCORE_IP" > $ENV_FILE + +touch /opt/opengnsys/ogGui/installer/.deployed diff --git a/component-installer/provision_ogrepository.sh b/component-installer/provision_ogrepository.sh new file mode 100644 index 0000000..64f9b25 --- /dev/null +++ b/component-installer/provision_ogrepository.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +set -e + + +REPO_IP=${1:-"127.0.0.1"} +CORE_IP=${2:-"127.0.0.1"} +OGUSER=${3:-"opengnsys"} +OGPASS=${4:-"og"} +INSTALL_DIR=/opt/opengnsys/ogrepository +DOWNLOAD_DIR=${5:-"/tmp/ogrepository"} +DEBIAN_FRONTEND=noninteractive +export DEBIAN_FRONTEND +export GIT_SSL_NO_VERIFY + + +check_root() { + if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" 1>&2 + exit 1 + fi +} + +install_uftp() { + apt install uftp -y + systemctl stop uftp + systemctl disable uftp +} + +install_updcast () { + apt install $DOWNLOAD_DIR/packets/udpcast_20230924_amd64.deb +} + +add_user_ogrepository() { + if ! id "$OGUSER" &>/dev/null; then + echo "User ogrepository does not exist, creating it" + useradd -r -s /bin/bash $OGUSER + fi + if [ ! -f /etc/sudoers.d/$OGUSER ]; then + echo "User $OGUSER does not have sudo permissions, adding it" + echo "$OGUSER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/"$OGUSER" + fi + +} + +create_directories() { + mkdir -p $INSTALL_DIR + mkdir -p $INSTALL_DIR/images $INSTALL_DIR/images_trash/ $INSTALL_DIR/bin/ $INSTALL_DIR/etc/ $INSTALL_DIR/log/ $INSTALL_DIR/api/ + chown -R $OGUSER:$OGUSER $INSTALL_DIR +} + +install_dependencies() { + apt update -y + apt install -y git python3 python3-pip python3-flask python3-paramiko python3-psutil python3-flasgger debian-archive-keyring samba gunicorn wakeonlan +} + +install_ext_repo() { + cp $DOWNLOAD_DIR/installer/files/ctorrent.sources /etc/apt/sources.list.d/ctorrent.sources + apt update -y +} + +install_external_packages() { + apt install -y bittorrent bittornado ctorrent +} + +install_ogrepo-api_service() { + cp -r $DOWNLOAD_DIR/installer/files/ogrepo-api.service /etc/systemd/system/ogrepo-api.service + sed -i "s/%%OGREPOSITORY_USER%%/$OGUSER/g" /etc/systemd/system/ogrepo-api.service + systemctl enable --now ogrepo-api +} + +install_files() { + cp -pr $DOWNLOAD_DIR/bin/* $INSTALL_DIR/bin/ + cp -pr $DOWNLOAD_DIR/etc/* $INSTALL_DIR/etc/ + cp -pr $DOWNLOAD_DIR/api/* $INSTALL_DIR/api/ + chown -R $OGUSER:$OGUSER $INSTALL_DIR + chmod 755 $INSTALL_DIR/bin/* + echo IPlocal="$REPO_IP" > $INSTALL_DIR/etc/ogAdmRepo.cfg + echo IPcore="$CORE_IP" >> $INSTALL_DIR/etc/ogAdmRepo.cfg + sudo chown $OGUSER:$OGUSER $INSTALL_DIR/etc/ogAdmRepo.cfg +} + +configure_samba() { + echo "include = /etc/samba/smb.conf.ogrepository" >> /etc/samba/smb.conf + cp $DOWNLOAD_DIR/installer/files/ogrepo-smb.conf /etc/samba/smb.conf.ogrepository + sed -i "s/%%OGREPOSITORY_USER%%/$OGUSER/g" /etc/samba/smb.conf.ogrepository + systemctl restart smbd + # Create default user ogrepository + (echo $OGPASS; echo $OGPASS) | smbpasswd -s -a $OGUSER + +} + +## Main program + +install_dependencies +add_user_ogrepository +install_ext_repo +install_external_packages +install_uftp +install_updcast +create_directories +install_files +install_ogrepo-api_service +configure_samba diff --git a/python-installer/oginstaller-v2.py b/python-installer/oginstaller-v2.py new file mode 100644 index 0000000..646c669 --- /dev/null +++ b/python-installer/oginstaller-v2.py @@ -0,0 +1,217 @@ +import npyscreen +import json +import os +from git import Repo + +CONFIGS_DIR = "/tmp/oginstall" +os.makedirs(CONFIGS_DIR, exist_ok=True) + +REPO_URL = "https://ognproject.evlt.uma.es/gitea/opengnsys/ogcore.git" + +def get_git_tags(): + try: + repo_path = os.path.join(CONFIGS_DIR, "opengnsys_repo") + if not os.path.exists(repo_path): + print("Clonando el repositorio...") + Repo.clone_from(REPO_URL, repo_path) + else: + print("Usando repositorio existente en", repo_path) + + repo = Repo(repo_path) + tags = [tag.name for tag in repo.tags if tag.name.startswith("opengnsys")] + return tags + except Exception as e: + print("Error al obtener los tags:", str(e)) + 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.tags = get_git_tags() + self.tag = self.add(npyscreen.TitleSelectOne, max_height=10, name="Selecciona el tag", + values=self.tags, scroll_exit=True) + + def beforeEditing(self): + npyscreen.blank_terminal() + + def on_ok(self): + npyscreen.blank_terminal() + selected_components = [self.components.values[i] for i in self.components.value] + if not selected_components or not self.tag.value: + npyscreen.notify_confirm("Debes seleccionar al menos un componente y un tag.", title="Error") + return + selected_tag = self.tags[self.tag.value[0]] + self.parentApp.selected_components = selected_components + self.parentApp.selected_tag = selected_tag + self.parentApp.current_component_index = 0 + self.parentApp.switchForm(selected_components[0]) + + def on_cancel(self): + if npyscreen.notify_yes_no("¿Estás seguro de que deseas salir?", title="Confirmación"): + self.parentApp.setNextForm(None) + +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 validate_fields(self): + """Validaciones personalizadas para contraseñas.""" + password_field = None + confirmation_field = None + + # Identificar los campos de contraseña y confirmación + for key, field_data in self.fields.items(): + if field_data.get("is_password_field"): + password_field = field_data["widget"] + if field_data.get("is_password_confirmation"): + confirmation_field = field_data["widget"] + + # Validar contraseñas si ambos campos están definidos + if password_field and confirmation_field: + if password_field.value != confirmation_field.value: + npyscreen.notify_confirm("Las contraseñas no coinciden. Por favor, revísalas.", title="Error") + return False + return True + + def add_password_field(self, key, name, is_confirmation=False, default_value=""): + """Añade un campo de contraseña con metadatos.""" + widget = self.add(npyscreen.TitlePassword, name=name, value=default_value) + self.fields[key] = { + "widget": widget, + "is_password_field": not is_confirmation, + "is_password_confirmation": is_confirmation, + } + + def on_ok(self): + if not self.validate_fields(): + return # Si las validaciones fallan, no proceder + + npyscreen.blank_terminal() + config_data = {"release": self.parentApp.selected_tag} + for key, field_data in self.fields.items(): + config_data[key] = field_data["widget"].value + + config_file = os.path.join(CONFIGS_DIR, f"config_{self.component_name}.json") + with open(config_file, "w") as f: + json.dump(config_data, f) + npyscreen.notify_confirm(f"Configuración de {self.component_name} guardada en {config_file}", title="Confirmación") + + 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.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["username"] = {"widget": self.add(npyscreen.TitleText, name="Usuario administrador (ogadmin):", value="ogadmin")} + self.add_password_field("password", "Contraseña:" , default_value="12345678") + self.add_password_field("confirm_password", "Confirmar Contraseña:", is_confirmation=True, default_value="12345678") + +class OgGuiForm(ComponentForm): + component_name = "ogGui" + + def configure_fields(self): + self.fields["ogcore_ip"] = {"widget": self.add(npyscreen.TitleText, name="URL Api OgCore (https://127.0.0.1:8443):", value="https://127.0.0.1:8443")} + +class OgDhcpForm(ComponentForm): + component_name = "ogDhcp" + + def configure_fields(self): + self.fields["ogbootIP"] = {"widget": self.add(npyscreen.TitleText, name="IP servidor de Boot (127.0.0.1):", value="127.0.0.1")} + self.fields["ogDhcpIP"] = {"widget": self.add(npyscreen.TitleText, name="IP servidor de DHCP (127.0.0.1):", value="127.0.0.1")} + self.fields["ogDhcp_Dir"] = {"widget": self.add(npyscreen.TitleText, name="Directorio de ogdhcp (/opt/opengnsys/ogdhcp):", value="/opt/opengnsys/ogdhcp")} + self.fields["interfaces"] = {"widget": self.add(npyscreen.TitleText, name="Interfaces Boot (eth0,eth1):", value="eth0,eth1")} + + def on_ok(self): + if not self.validate_fields(): + return # Si las validaciones fallan, no proceder + + npyscreen.blank_terminal() + config_data = {"release": self.parentApp.selected_tag} + for key, field_data in self.fields.items(): + if key == "interfaces": + config_data[key] = [iface.strip() for iface in field_data["widget"].value.split(",")] + else: + config_data[key] = field_data["widget"].value + + config_file = os.path.join(CONFIGS_DIR, f"config_{self.component_name}.json") + with open(config_file, "w") as f: + json.dump(config_data, f) + npyscreen.notify_confirm(f"Configuración de {self.component_name} guardada en {config_file}", title="Confirmación") + + 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.setNextForm(None) + +class OgBootForm(ComponentForm): + component_name = "ogBoot" + + def configure_fields(self): + self.fields["ogCore_ServerIP"] = {"widget": self.add(npyscreen.TitleText, name="ogCore IP:", value="")} + self.fields["ogBoot_ServerIP"] = {"widget": self.add(npyscreen.TitleText, name="ogBoot Server IP:", value="")} + self.fields["ogBoot_Dir"] = {"widget": self.add(npyscreen.TitleText, name="ogCore Dir (/opt/opengnsys/ogboot):", value="/opt/opengnsys/ogboot")} + self.fields["ogLive_Default"] = {"widget": self.add(npyscreen.TitleText, name="ogLive por defecto:", value="https://ognproject.evlt.uma.es/oglive/ogLive-noble-6.8.0-31-generic-amd64-r20241128.62778c9_20241129.iso")} + self.fields["ogBootSambaUser"] = {"widget": self.add(npyscreen.TitleText, name="ogBoot Samba User (opengnsys):", value="opengnsys")} + self.add_password_field("ogBootSambaPass", "ogBoot Samba Pass (og):", default_value="og") + self.add_password_field("confirm_ogBootSambaPass", "Confirmar ogBoot Samba Pass (og):", is_confirmation=True, default_value="og") + + +class OgRepositoryForm(ComponentForm): + component_name = "ogRepository" + + def configure_fields(self): + self.fields["ogrepository_ip"] = {"widget": self.add(npyscreen.TitleText, name="ogRepository IP:", value="127.0.0.1")} + self.fields["ogcore_server_ip"] = {"widget": self.add(npyscreen.TitleText, name="ogCoreserver IP(127.0.0.1):", value="127.0.0.1")} + self.fields["ogrepository_samba_user"] = {"widget": self.add(npyscreen.TitleText, name="Samba User:", value="opengnsys")} + self.add_password_field("ogrepository_samba_pass", "Samba Password:", default_value="og") + self.add_password_field("confirm_repository_password", "Confirmar Samba Password:", is_confirmation=True, default_value="og") + +class ConfigApp(npyscreen.NPSAppManaged): + def onStart(self): + self.addForm("MAIN", ComponentSelectionForm, name="Selección de Componentes") + self.addForm("ogCore", OgCoreForm, name="Configuración de ogCore") + self.addForm("ogGui", OgGuiForm, name="Configuración de ogGui") + self.addForm("ogDhcp", OgDhcpForm, name="Configuración de ogDhcp") + self.addForm("ogBoot", OgBootForm, name="Configuración de ogBoot") + self.addForm("ogRepository", OgRepositoryForm, name="Configuración de ogRepository") + self.selected_components = [] + self.selected_tag = "" + self.current_component_index = 0 + +if __name__ == "__main__": + app = ConfigApp() + app.run() diff --git a/python-installer/oginstaller.py b/python-installer/oginstaller.py new file mode 100755 index 0000000..08cb6ba --- /dev/null +++ b/python-installer/oginstaller.py @@ -0,0 +1,220 @@ +import curses +import json +import os +from git import Repo + +CONFIGS_DIR = "/tmp/oginstall" +os.makedirs(CONFIGS_DIR, exist_ok=True) + +REPO_URL = "https://ognproject.evlt.uma.es/gitea/opengnsys/ogcore.git" + +def get_git_tags(): + try: + repo_path = os.path.join(CONFIGS_DIR, "opengnsys_repo") + if not os.path.exists(repo_path): + print("Clonando el repositorio...") + Repo.clone_from(REPO_URL, repo_path) + else: + print("Usando repositorio existente en", repo_path) + + repo = Repo(repo_path) + tags = [tag.name for tag in repo.tags] + if tags: + print("Tags encontrados:", tags) + else: + print("No se encontraron tags con el patrón especificado.") + + return tags + except Exception as e: + print("Error al obtener los tags:", str(e)) + return [] + +def get_password(stdscr, y, x, prompt, default=""): + stdscr.addstr(y, x, prompt, curses.color_pair(1)) + password = "" + masked_password = "" + + stdscr.move(y, x + len(prompt)) # Coloca el cursor después del prompt + + while True: + key = stdscr.getch() + if key in (curses.KEY_BACKSPACE, 127): # Maneja el retroceso + if len(password) > 0: + password = password[:-1] + masked_password = "*" * len(password) + stdscr.move(y, x + len(prompt)) # Mueve el cursor después del prompt + stdscr.addstr(y, x + len(prompt), " " * (len(masked_password) + 1)) # Borra la línea + stdscr.addstr(y, x + len(prompt), masked_password) # Vuelve a mostrar los asteriscos actualizados + elif key == ord("\n"): # Confirmar con Enter + if not password and default: # Si el usuario no ingresó nada, usa el valor predeterminado + password = default + break + elif 32 <= key <= 126: # Rango de caracteres imprimibles + password += chr(key) + masked_password = "*" * len(password) + stdscr.addstr(y, x + len(prompt), masked_password) # Muestra asteriscos + + return password + +def get_input(stdscr, y, x, prompt, default=""): + max_y, max_x = stdscr.getmaxyx() + if x + len(prompt) >= max_x: + raise ValueError("El prompt es demasiado largo para caber en la pantalla.") + + stdscr.addstr(y, x, prompt, curses.color_pair(1)) + input_text = "" + prompt_end_x = x + len(prompt) # Calcula la posición final del prompt + stdscr.move(y, prompt_end_x) # Coloca el cursor después del prompt + + while True: + key = stdscr.getch() + if key in (curses.KEY_BACKSPACE, 127): # Maneja el retroceso + if len(input_text) > 0: + input_text = input_text[:-1] + stdscr.move(y, prompt_end_x) # Mueve el cursor después del prompt + stdscr.clrtoeol() # Limpia la línea desde la posición actual hacia el final + stdscr.addstr(y, prompt_end_x, input_text) # Vuelve a mostrar el texto actualizado + stdscr.move(y, prompt_end_x + len(input_text)) + elif key == ord("\n"): # Confirmar con Enter + if not input_text and default: # Usa el valor predeterminado si está vacío + input_text = default + break + elif 32 <= key <= 126: # Rango de caracteres imprimibles + if prompt_end_x + len(input_text) < max_x - 1: + input_text += chr(key) + stdscr.addstr(y, prompt_end_x, input_text) # Muestra el texto actualizado + stdscr.move(y, prompt_end_x + len(input_text)) # Mueve el cursor al final del texto + + return input_text + +def main(stdscr): + # Inicializar colores + curses.start_color() + curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) + stdscr.bkgd(' ', curses.color_pair(1)) + + curses.curs_set(0) + stdscr.clear() + + # Paso 1: Seleccionar componentes + components = ["ogCore", "ogGui", "ogDhcp", "ogBoot", "ogRepository"] + selected_components = [] + current_index = 0 + + # Mostrar instrucciones y opciones de componentes + stdscr.addstr(1, 2, "Selecciona los componentes (usa Flechas para navegar, Espacio para seleccionar, Enter para continuar):", curses.color_pair(1) | curses.A_BOLD) + + while True: + for idx, comp in enumerate(components): + if comp in selected_components: + stdscr.addstr(idx + 3, 4, f"[X] {comp}", curses.color_pair(1)) + else: + stdscr.addstr(idx + 3, 4, f"[ ] {comp}", curses.color_pair(1)) + stdscr.addstr(current_index + 3, 4, f"> {components[current_index]}", curses.color_pair(1)) + + key = stdscr.getch() + + if key == curses.KEY_UP and current_index > 0: + current_index -= 1 + elif key == curses.KEY_DOWN and current_index < len(components) - 1: + current_index += 1 + elif key == ord(" "): + component = components[current_index] + if component in selected_components: + selected_components.remove(component) + else: + selected_components.append(component) + elif key == ord("\n"): + break + + stdscr.refresh() + + # Menu de selección de releases + tags = get_git_tags() + tag_index = 0 + + stdscr.clear() + + while True: + for idx, tag in enumerate(tags): + if idx == tag_index: + stdscr.addstr(idx + 3, 4, f"> {tag}", curses.color_pair(1)) + else: + stdscr.addstr(idx + 3, 4, f" {tag}", curses.color_pair(1)) + + key = stdscr.getch() + + if key == curses.KEY_UP and tag_index > 0: + tag_index -= 1 + elif key == curses.KEY_DOWN and tag_index < len(tags) - 1: + tag_index += 1 + elif key == ord("\n"): + break + + stdscr.refresh() + + # Configuración específica de cada componente seleccionado + curses.echo() + for component in selected_components: + stdscr.clear() + stdscr.addstr(1, 2, f"Configuración para {component}:", curses.color_pair(1) | curses.A_BOLD) + curses.curs_set(1) + + config_data = {} + if component == "ogCore": + user = get_input(stdscr, 3, 0, "Usuario administrador (ogadmin): ", "ogadmin") + password = get_password(stdscr, 4, 0, "Contraseña (por defecto '12345678'): ", "12345678") + config_data = {"username": user, "password": password, "container_version": tags[tag_index]} + + elif component == "ogGui": + ogcore_ip = get_input(stdscr, 3, 0, "URL Api OgCore (https://127.0.0.1:8443): " , "https://127.0.0.1:8443") + config_data = {"ogcore_ip": ogcore_ip, "container_version": tags[tag_index]} + + elif component == "ogDhcp": + ogbootIP = get_input(stdscr, 3, 0, "IP servidor de Boot (127.0.0.1): ", "127.0.0.1") + ogdhcpIP = get_input(stdscr, 4, 0, "IP servidor de DHCP (127.0.0.1): ", "127.0.0.1") + ogdhcpDir = get_input(stdscr, 5, 0, "Directorio de ogdhcp (/opt/opengnsys/ogdhcp): ", "/opt/opengnsys/ogdhcp") + interfaces = get_input(stdscr, 6, 0, "Interfaces Boot (eth0,eth1): ", "eth0,eth1") + json_array_interfaces = interfaces.split(",") + config_data = {"ogbootIP": ogbootIP, "ogDhcpIP": ogdhcpIP , "ogDhcp_Dir" : ogdhcpDir , "interfaces": json_array_interfaces, "release": tags[tag_index]} + + elif component == "ogBoot": + ogcore_ip = get_input(stdscr, 3, 0, "ogCore Ip Server: ", "") + ogboot_server_ip = get_input(stdscr, 4, 0, "ogBoot Server IP: ", "") + ogcore_dir = get_input(stdscr, 5, 0, "ogCore Dir (/opt/opengnsys/ogboot): ", "/opt/opengnsys/ogboot") + ogLive_default = get_input(stdscr, 6, 0, "ogLive por defecto (ogLive-noble-6.8.0-31-generic-amd64-r20241128.62778c9_20241129): ", "ogLive-noble-6.8.0-31-generic-amd64-r20241128.62778c9_20241129") + ogboot_samba_user = get_input(stdscr, 7, 0, "ogBoot Samba User (opengnsys): ", "opengnsys") + ogboot_samba_pass = get_password(stdscr, 8, 0, "ogBoot Samba Pass (og): ", "og") + config_data = { + "ogCore_ServerIP": ogcore_ip, + "ogBoot_ServerIP": ogboot_server_ip, + "ogBoot_Dir": ogcore_dir, + "ogLive_Default": "https://ognproject.evlt.uma.es/oglive/" + ogLive_default + ".iso", + "ogBootSambaUser": ogboot_samba_user, + "ogBootSambaPass": ogboot_samba_pass, + "release": tags[tag_index] + } + + elif component == "ogRepository": + ogrepository_ip = get_input(stdscr, 3, 0, "ogRepository IP Server (127.0.0.1): ", "") + ogrepository_samba_user = get_input(stdscr, 4, 0, "ogRepository Sambauser (opengnsys): ", "opengnsys") + ogrepository_samba_pass = get_password(stdscr, 5, 0, "ogRepository Sambapass (og): ", "og") + config_data = { + "ogrepository_ip": ogrepository_ip, + "ogrepository_samba_user": ogrepository_samba_user, + "ogrepository_samba_pass": ogrepository_samba_pass, + "release": tags[tag_index] + } + + # Guardar en archivo JSON + config_file = os.path.join(CONFIGS_DIR, f"config_{component}.json") + with open(config_file, "w") as f: + json.dump(config_data, f) + stdscr.clear() + stdscr.addstr(2, 2, f"Configuración de {component} guardada en {config_file}", curses.color_pair(1)) + stdscr.refresh() + stdscr.getch() + + curses.noecho() # Desactivar el eco después de la entrada + +curses.wrapper(main) diff --git a/python-installer/oginstaller.sh b/python-installer/oginstaller.sh new file mode 100755 index 0000000..1523fe1 --- /dev/null +++ b/python-installer/oginstaller.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Setup installer environment + + +BRANCH=${BRANCH:-main} +GIT_SSL_NO_VERIFY=1 +GIT_REPO="https://ognproject.evlt.uma.es/gitea/api/v1/repos/opengnsys/oginstaller/archive/$BRANCH.zip" +export GIT_SSL_NO_VERIFY + + + +install_packages() { + apt-get update + apt-get install -y curl jq unzip python3 python3-git +} + +download_installer() { + + rm -f /tmp/oginstaller.zip + rm -rf /tmp/oginstaller-$BRANCH + rm -rf /tmp/oginstaller + + curl -q -k $GIT_REPO -H 'accept: application/json' -o /tmp/oginstaller.zip + unzip /tmp/oginstaller.zip -d /tmp + mv /tmp/oginstaller /tmp/oginstaller-$BRANCH +} + +extract_installer() { + rm -rf /tmp/oginstall + mkdir -p /tmp/oginstall + cp -r /tmp/oginstaller-$BRANCH/python-installer/* /tmp/oginstall/ + cp -r /tmp/oginstaller-$BRANCH/component-installer/* /tmp/oginstall/ + chmod 755 /tmp/oginstall/*.sh + chmod 755 /tmp/oginstall/*.py +} + +create_questions() { + echo "Creating questions..." + python3 /tmp/oginstall/oginstaller.py +} + +launch_component_installer() { + echo "Launching component installer..." + /tmp/oginstall/component-installer.sh +} + + +install_packages +download_installer +extract_installer +create_questions +launch_component_installer + diff --git a/python-installer/opengnsys_installer.sh b/python-installer/opengnsys_installer.sh new file mode 100755 index 0000000..c228988 --- /dev/null +++ b/python-installer/opengnsys_installer.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Setup installer environment + + +BRANCH=${BRANCH:-main} +GIT_SSL_NO_VERIFY=1 +GIT_REPO="https://ognproject.evlt.uma.es/gitea/api/v1/repos/opengnsys/oginstaller/archive/$BRANCH.zip" +export GIT_SSL_NO_VERIFY + + + +install_packages() { + apt-get update + apt-get install -y curl jq unzip python3 python3-git +} + + +create_python_venv() { + apt-get install -y python3-venv + python3 -m venv /tmp/oginstall/venv + source /tmp/oginstall/venv/bin/activate + pip install -r /tmp/oginstall/requirements.txt +} + +download_installer() { + + rm -f /tmp/oginstaller.zip + rm -rf /tmp/oginstaller-$BRANCH + rm -rf /tmp/oginstaller + + curl -q -k $GIT_REPO -H 'accept: application/json' -o /tmp/oginstaller.zip + unzip /tmp/oginstaller.zip -d /tmp + mv /tmp/oginstaller /tmp/oginstaller-$BRANCH +} + +extract_installer() { + rm -rf /tmp/oginstall + mkdir -p /tmp/oginstall + cp -r /tmp/oginstaller-$BRANCH/python-installer/* /tmp/oginstall/ + cp -r /tmp/oginstaller-$BRANCH/component-installer/* /tmp/oginstall/ + chmod 755 /tmp/oginstall/*.sh + chmod 755 /tmp/oginstall/*.py +} + +create_questions() { + echo "Creating questions..." + python3 /tmp/oginstall/oginstaller-v2.py + deactivate +} + +launch_component_installer() { + echo "Launching component installer..." + /tmp/oginstall/component-installer.sh +} + +clean_tmp() { + rm -rf /tmp/oginstall + rm -rf /tmp/oginstaller-$BRANCH + rm -f /tmp/oginstaller.zip +} + + +install_packages +download_installer +extract_installer +create_python_venv +create_questions +launch_component_installer +clean_tmp diff --git a/python-installer/requirements.txt b/python-installer/requirements.txt new file mode 100644 index 0000000..54171c6 --- /dev/null +++ b/python-installer/requirements.txt @@ -0,0 +1,2 @@ +GitPython +npyscreen