#!/usr/bin/env python3 ################################################################################# ##### ogBoot installer script ######## ##### Autor: Antonio Emmanuel Guerrero Silva ######## ################################################################################# import platform, os, sys, subprocess, datetime, shutil, pwd, glob, zipfile, urllib.request, logging, distro global PACKAGES_TO_INSTALL global UBUNTU_OS_VERSION global PYTHON_VERSION global INSTALL_OGBOOT_TARGET global PROGRAM_DIR global OPENGNSYS_SERVER # Reload variables from bashrc subprocess.run(['.', '~/.bashrc'], shell=True) # Leer variables de entorno #env_ogCore_ServerIP = os.environ.get('OGCORE_SERVER_IP') #"172.17.8.82" #env_ogCore_Server = os.environ.get('OGCORE_SERVER') #"opengnsys.es" #env_ogCore_Dir = os.environ.get('OGCORE_DIR') #{INSTALL_OPENGNSYS_TARGET} #env_ogBoot_Dir = os.environ.get('OGBOOT_DIR') #{INSTALL_OGBOOT_TARGET} #env_ogBoot_GitRepo = os.environ.get('OGBOOT_GIT_REPO') #"ssh://git@ognproject.evlt.uma.es:21987/opengnsys/ogboot.git" # Leer variables de entorno env_ogCore_ServerIP = "172.17.8.82" #os.getenv('OGCORE_SERVER_IP') #"172.17.8.82" env_ogCore_Server = "opengnsys.es" #os.getenv('OGCORE_SERVER') #"opengnsys.es" env_ogCore_Dir = "/opt/opengnsys" #os.getenv('OGCORE_DIR') #{INSTALL_OPENGNSYS_TARGET} env_ogBoot_Dir = "/opt/ogboot" #os.getenv('OGBOOT_DIR') #{INSTALL_OGBOOT_TARGET} env_ogBoot_GitRepo = "ssh://git@ognproject.evlt.uma.es:21987/opengnsys/ogboot.git" #os.getenv('OGBOOT_GIT_REPO') #"ssh://git@ognproject.evlt.uma.es:21987/opengnsys/ogboot.git" PROGRAM = os.path.splitext(os.path.basename(sys.argv[0]))[0] PROGRAM_DIR = os.path.dirname(os.path.realpath(sys.argv[0])) PROGRAM_NAME = os.path.basename(sys.argv[0]) SERVER_OPENGNSYS = env_ogCore_ServerIP OPENGNSYS_SERVER = env_ogCore_Server INSTALL_TARGET = env_ogBoot_Dir INSTALL_OPENGNSYS_TARGET = env_ogCore_Dir INSTALL_OGBOOT_TARGET = env_ogBoot_Dir GIT_REPO = env_ogBoot_GitRepo IPXE_DIR = "/tmp/ipxe" WORKDIR="/tmp/ogboot_installer" TFTPSERV = "tftpd-hpa" SAMBASERV = "smbd" SAMBACFGDIR = "/etc/samba" TFTPCFGDIR = "/var/lib/tftpboot" INETDCFGDIR = "/etc/xinetd.d/" DEFAULTDEV = "" PACKAGES_TO_INSTALL = ["htop"] DEVICE = [] SERVERIP = [] NETIP = [] NETMASK = [] NETBROAD = [] ROUTERIP = [] BRANCH = sys.argv[1] if len(sys.argv) > 1 else "main" INETDSERV = "xinetd" OPENGNSYS_CLIENT_PASSWD="og" OPENGNSYS_CLIENT_USER="opengnsys" UBUNTU_OS_VERSION = "22" PYTHON_VERSION = 3 if os.path.isdir(f"{PROGRAM_DIR}/../installer"): REMOTE = 0 else: REMOTE = 1 # Create log file with sudo and set owner to ogboot log_file = f'/var/log/{PROGRAM}.log' os.makedirs(os.path.dirname(log_file), exist_ok=True) subprocess.run(['sudo', 'touch', log_file]) #subprocess.run(['sudo', 'chown', 'ogboot', log_file]) subprocess.run(['sudo', 'chmod', '777', log_file]) #Configurar el registro logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s\t%(message)s', filename=f'/var/log/{PROGRAM}.log', filemode='a') logger = logging.getLogger() console_handler = logging.StreamHandler() console_handler.setLevel(logging.DEBUG) formatter = logging.Formatter('%(levelname)s\t%(message)s') console_handler.setFormatter(formatter) logger.addHandler(console_handler) ############################################################################### ###::::::::::::::::::::::::::::::: UTILS :::::::::::::::::::::::::::::::::::### ############################################################################### def check_python_version(): try: python_version = platform.python_version() python_version_tuple = tuple(map(int, python_version.split('.'))) if python_version_tuple >= (PYTHON_VERSION, 0): logger.info(f"Python version installed: {python_version}") else: logger.error(f"Python version is lower than required. Installed version: {python_version}" + ".") exit() except Exception as e: logger.error(f"Problem verifying Python version: {e}") exit() def check_distribution(): if os.path.exists("/etc/os-release"): with open("/etc/os-release", "r") as file: for line in file: if line.startswith("VERSION"): VERSION = line.split("=")[1].strip().strip('"') break if VERSION.startswith(UBUNTU_OS_VERSION + "."): return logger.info(f"The ogBoot installation has been tested with full functionality on Ubuntu. {UBUNTU_OS_VERSION}") go_on = input("Would you like to continue? [s/N]: ") if go_on.upper() != "S": logger.error("Leaving the installation.") exit() def cidr2mask(cidr): args = [5 - (cidr // 8), 255, 255, 255, 255, (255 << (8 - (cidr % 8))) & 255, 0, 0, 0] if args[0] > 1: args = args[args[0]:] else: args = args[1:] return f"{args[0]}.{args[1]}.{args[2]}.{args[3]}" def downloadCode(url): if len(url) != 1: logger.error("Invalid number of parameters") exit(1) subprocess.run(["GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=accept-new git archive --remote=" + url + " --format zip --output opengnsys.zip --prefix=opengnsys/ " + BRANCH + " && unzip opengnsys.zip"], shell=True) if subprocess.returncode != 0: logger.error("Error getting OpenGnsys code from " + url) return 1 subprocess.run(["rm -f opengnsys.zip"], shell=True) logger.info("downloadCode(): code was downloaded") return 0 ############################################################################### ###:::::::::::::::::::::::::::::: INSTALL ::::::::::::::::::::::::::::::::::### ############################################################################### def is_installed(package): try: subprocess.run(["dpkg", "-s", package], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) return True except subprocess.CalledProcessError: return False def get_missing_packages(): global PACKAGES_TO_INSTALL faltantes = [] OSDISTRIB = distro.name() OSVERSION = distro.version() logger.info(f"OSDISTRIB: {OSDISTRIB}") logger.info(f"OSVERSION: {OSVERSION}") match OSDISTRIB.lower(): case "ubuntu": match OSVERSION: case "20.04": PACKAGES = ["vim", "curl", "htop"] case "18.04": PACKAGES = ["nano", "wget", "tree"] case "22.04": PACKAGES_TO_INSTALL = ["xorriso", "genisoimage", "syslinux", "liblzma-dev", "nginx", "arp-scan", "automake", "build-essential", "btrfs-progs", "composer", "curl", "ctorrent", "debootstrap", "g++-multilib", "gawk", "gettext", "graphviz", "grub-efi-amd64-signed", "jq", "libapache2-mod-php", "libdbi-dev", "libdbi1", "libev-dev", "libjansson-dev", "liblz4-tool", "libssl-dev", "moreutils", "netpipes", "php", "php-bcmath", "php-cli", "php-curl", "php-fpm", "php-gd", "php-json", "php-ldap", "php-mbstring", "php-mysql", "php8.1-common", "php-pear", "php-xml", "php-zip", "procps", "coreutils", "rsync", "samba", "schroot", "shim-signed", "squashfs-tools", "subversion", "tftpd-hpa", "udpcast", "unzip", "wakeonlan", "wget", "xinetd", "jq", "moreutils", "net-tools"] case _: PACKAGES = ["bash", "nc", "rsync"] case "suse": match OSVERSION: case "15": PACKAGES = ["zypper", "mc", "gcc"] case "12": PACKAGES = ["perl", "tar", "man"] case _: PACKAGES = ["openssl", "ncurses", "zip"] case "redhat": match OSVERSION: case "8": PACKAGES = ["yum", "tar", "perl"] case "7": PACKAGES = ["bash", "rpm", "tcpdump"] case _: PACKAGES = ["grep", "sed", "awk"] case _: logger.error("Distribution not supported by ogBoot.") exit(1) for package in PACKAGES_TO_INSTALL: if not is_installed(package): faltantes.append(package) logger.info(f"::::::Package to install: {package}...") return faltantes def install_packages(missing, log_packages_file=f"/tmp/installed_packages.log"): if not missing: logger.warning("All packages are already installed.") return logger.info("Upgrading the system...") original_debian_frontend = os.environ.get('DEBIAN_FRONTEND') try: os.environ['DEBIAN_FRONTEND'] = 'noninteractive' subprocess.run(["sudo", "apt-get", "update"], check=True) with open(log_packages_file, "a") as log: for package in missing: subprocess.run(["sudo", "apt-get", "install", "--allow-change-held-packages", "-y", package], check=True) logger.info(f"{package} installed correctly.") log.write(package + "\n") logger.info("All missing packages have been installed.") finally: # Restaurar el valor original de DEBIAN_FRONTEND if original_debian_frontend is not None: os.environ['DEBIAN_FRONTEND'] = original_debian_frontend else: del os.environ['DEBIAN_FRONTEND'] def autoConfigure(): #distribution = platform.linux_distribution() #distribution = distro.linux_distribution() #OSDISTRIB = distribution[0] OSDISTRIB = distro.name() OSVERSION = distro.version() #OSVERSION = distribution[1] logger.info(f"OSDISTRIB: {OSDISTRIB}") logger.info(f"OSVERSION: {OSVERSION}") # Configuración según la distribución OSDISTRIB def generate_config_url(): with open('/etc/os-release', 'r') as file: lines = file.readlines() codename = None version = None for line in lines: if line.startswith('VERSION_CODENAME') or line.startswith('UBUNTU_CODENAME'): codename = line.split('=')[1].strip().strip('"') elif line.startswith('VERSION_ID'): version = line.split('=')[1].strip().strip('"') arch = os.uname().machine return f"https://dl.cloudsmith.io/public/isc/kea-2-0/config.deb.txt?distro=ubuntu&codename={codename}&version={version}&arch={arch}" def downloadComposer(): #logger.info("Downloading composer.phar...") os.makedirs(os.path.join(WORKDIR, "ogboot", "bin"), mode=0o750, exist_ok=True) subprocess.run(["curl", "-sS", "-o", os.path.join(WORKDIR, "ogboot", "bin", "composer.phar"), "https://getcomposer.org/installer"], check=True) if not os.path.isfile(os.path.join(WORKDIR, "ogboot", "bin", "composer.phar")): logger.error("Failed to download composer.phar") return 1 logger.info(f"composer.phar downloaded to {INSTALL_OGBOOT_TARGET}/bin") return 0 def install_swagger_ui(): # Define la URL del archivo de Swagger UI que quieres descargar swagger_ui_url = "https://github.com/swagger-api/swagger-ui/archive/refs/heads/master.zip" # Define la ruta donde quieres descomprimir Swagger UI swagger_ui_path = "/tmp/swagger-ui" # Define la ruta de destino para los archivos de Swagger UI destination_path = os.path.join(INSTALL_TARGET, "public") # Crea los directorios si no existen os.makedirs(swagger_ui_path, exist_ok=True) os.makedirs(destination_path, exist_ok=True) # Descarga el archivo de Swagger UI urllib.request.urlretrieve(swagger_ui_url, "/tmp/swagger-ui.zip") # Descomprime el archivo de Swagger UI en la ruta especificada with zipfile.ZipFile("/tmp/swagger-ui.zip", "r") as zip_ref: zip_ref.extractall(swagger_ui_path) # Copia los archivos de Swagger UI al directorio de destino for file_path in glob.glob(os.path.join(swagger_ui_path, "swagger-ui-master", "dist", "*")): shutil.copy(file_path, destination_path) # Elimina el archivo descargado y el directorio temporal os.remove("/tmp/swagger-ui.zip") shutil.rmtree(swagger_ui_path) # Genera el archivo swagger.json os.system(f"{os.path.join(INSTALL_TARGET, 'vendor', 'bin', 'openapi')} {INSTALL_TARGET}/src/OgBootBundle/Controller/ -o {destination_path}/swagger.json") logger.info(f"Swagger UI installed on {destination_path}.") def og_core_create_user(OPENGNSYS_CLIENT_USER): try: pwd.getpwnam(OPENGNSYS_CLIENT_USER) logger.warning(f"User {OPENGNSYS_CLIENT_USER} already exists") except KeyError: subprocess.run(["sudo", "useradd", "-m", OPENGNSYS_CLIENT_USER]) logger.info(f"User {OPENGNSYS_CLIENT_USER} created successfully.") def og_boot_create_dirs(): if os.path.exists(INSTALL_OGBOOT_TARGET): if not os.path.isdir(INSTALL_OGBOOT_TARGET): raise NotADirectoryError(f"{INSTALL_OGBOOT_TARGET} exists and is not a directory.") else: logger.warning(f"{INSTALL_OGBOOT_TARGET} directory already exists.") else: try: # Crear los directorios necesarios os.makedirs(INSTALL_OGBOOT_TARGET, mode=0o750, exist_ok=True) os.makedirs(os.path.join(INSTALL_OGBOOT_TARGET, "client"), mode=0o750, exist_ok=True) os.makedirs("/opt/ogboot/tftpboot/", mode=0o750, exist_ok=True) os.makedirs("/opt/ogboot/tftpboot/ipxe_scripts", mode=0o750, exist_ok=True) os.makedirs("/opt/ogboot/tftpboot/ipxe_scripts/templates", mode=0o750, exist_ok=True) # Cambiar el propietario de los directorios subprocess.run(["sudo", "chown", "-R", "ogboot:ogboot", INSTALL_OGBOOT_TARGET]) subprocess.run(["sudo", "chown", "-R", "ogboot:ogboot", "/opt/ogboot/tftpboot/"]) logger.info(f"{INSTALL_OGBOOT_TARGET} directory created successfully.") except OSError: logger.error("Error while creating directory paths!") exit(1) def og_boot_symfony_install(path_opengnsys_base): logger.info("Creating Symfony application skeleton...") downloadComposer() # Copiar los archivos .env y composer.json primero logger.info(f"Copying files (.env and composer.json) from {WORKDIR}/ogboot/.env to {path_opengnsys_base}/.env") shutil.copy(f"{WORKDIR}/ogboot/.env", os.path.join(path_opengnsys_base, ".env")) shutil.copy(f"{WORKDIR}/ogboot/composer.json", os.path.join(path_opengnsys_base, "composer.json")) logger.info(f".env and composer.json files copied to {path_opengnsys_base}") def og_boot_copy_files(path_opengnsys_base): bin_source = os.path.join(WORKDIR, "ogboot/bin") bin_dest = os.path.join(path_opengnsys_base, "bin") src_source = os.path.join(WORKDIR, "ogboot/src") src_dest = os.path.join(path_opengnsys_base, "src") config_source = os.path.join(WORKDIR, "ogboot/config") config_dest = os.path.join(path_opengnsys_base, "config") lib_source = os.path.join(WORKDIR, "ogboot/lib") lib_dest = os.path.join(path_opengnsys_base, "lib") if os.path.exists(bin_dest): shutil.rmtree(bin_dest) shutil.copytree(bin_source, bin_dest) if os.path.exists(src_dest): shutil.rmtree(src_dest) shutil.copytree(src_source, src_dest) if os.path.exists(config_dest): shutil.rmtree(config_dest) shutil.copytree(config_source, config_dest) if os.path.exists(lib_dest): shutil.rmtree(lib_dest) shutil.copytree(lib_source, lib_dest) os.makedirs(os.path.join(INSTALL_TARGET, "etc"), mode=0o750, exist_ok=True) os.makedirs(os.path.join(INSTALL_TARGET, "client"), mode=0o750, exist_ok=True) subprocess.run(["sudo", "chmod", "-R", "755", path_opengnsys_base]) subprocess.run(["sudo", "chown", "-R", "ogboot:ogboot", path_opengnsys_base]) def og_boot_composer_install(path_opengnsys_base): # Ejecutar Composer como el usuario 'ogboot' para instalar el proyecto Symfony os.chdir(os.path.join(path_opengnsys_base, 'bin')) result = subprocess.run(["sudo", "-u", "ogboot", "php", os.path.join(path_opengnsys_base, "bin", "composer.phar"), "install", "--no-interaction", "--working-dir", path_opengnsys_base]) if result.returncode != 0: logger.error("Error creating Symfony project using Composer") return os.chdir(path_opengnsys_base) result = subprocess.run(["sudo", "-u", "ogboot", "php", os.path.join(path_opengnsys_base, "bin", "composer.phar"), "install", "--no-interaction", "--working-dir", path_opengnsys_base]) # Eliminar composer.lock si existe composer_lock_path = os.path.join(path_opengnsys_base, "composer.lock") if os.path.exists(composer_lock_path): os.remove(composer_lock_path) install_swagger_ui() # Instalar Swagger UI logger.info("Application skeleton created and composer.lock file removed.") #def createDirs(INSTALL_TARGET): # if not os.path.exists(INSTALL_TARGET): # try: # os.makedirs(INSTALL_TARGET) # os.makedirs(os.path.join(INSTALL_TARGET, "client")) # logger.info(f"{INSTALL_TARGET} directory created successfully.") # except OSError: # logger.error("Error while creating directory paths!") # exit(1) # else: # logger.info(f"Directory {INSTALL_TARGET} already exists.") ############################################################################### ###:::::::::::::::::::::::::::: CONFIGURE ::::::::::::::::::::::::::::::::::### ############################################################################### def install_isc_kea(): url = generate_config_url() print(f"URL generada: {url}") print("Ejecutando curl para obtener el archivo de configuración del repositorio KEA...") subprocess.run(["curl", "-1sLf", url, ">", "/etc/apt/sources.list.d/isc-kea-2-0.list"], check=True) print("El archivo de configuración del repositorio KEA ha sido guardado en /etc/apt/sources.list.d/isc-kea-2-0.list") print("Descargando y agregando la clave GPG del repositorio KEA...") subprocess.run(["curl", "-1sLf", "https://dl.cloudsmith.io/public/isc/kea-2-0/gpg.8029D4AFA58CBB5E.key", "|", "gpg", "--dearmor", ">>", "/usr/share/keyrings/isc-kea-2-0-archive-keyring.gpg"], check=True) print("La clave GPG del repositorio KEA ha sido descargada y agregada correctamente") print("Ejecutando apt-get update...") subprocess.run(["apt-get", "update"], check=True) print("Comprobando disponibilidad de los paquetes...") if subprocess.run(["apt-cache", "show", "isc-kea-common"], capture_output=True).returncode == 0: print("El paquete isc-kea-common está disponible para ser instalado.") print("Descargando e instalando el paquete isc-kea-common...") subprocess.run(["apt-get", "install", "-y", "isc-kea-common"], check=True) print("El paquete isc-kea-common ha sido descargado e instalado correctamente.") else: print("El paquete isc-kea-common no está disponible en los repositorios apt.") if subprocess.run(["apt-cache", "show", "isc-kea-dhcp4-server"], capture_output=True).returncode == 0: print("El paquete isc-kea-dhcp4-server está disponible para ser instalado.") print("Descargando e instalando el paquete isc-kea-dhcp4-server...") subprocess.run(["apt-get", "install", "-y", "isc-kea-dhcp4-server"], check=True) print("El paquete isc-kea-dhcp4-server ha sido descargado e instalado correctamente.") else: print("El paquete isc-kea-dhcp4-server no está disponible en los repositorios apt.") if subprocess.run(["apt-cache", "show", "isc-kea-dhcp6-server"], capture_output=True).returncode == 0: print("El paquete isc-kea-dhcp6-server está disponible para ser instalado.") print("Descargando e instalando el paquete isc-kea-dhcp6-server...") subprocess.run(["apt-get", "install", "-y", "isc-kea-dhcp6-server"], check=True) print("El paquete isc-kea-dhcp6-server ha sido descargado e instalado correctamente.") else: print("El paquete isc-kea-dhcp6-server no está disponible en los repositorios apt.") if subprocess.run(["apt-cache", "show", "isc-kea-dhcp-ddns-server"], capture_output=True).returncode == 0: print("El paquete isc-kea-dhcp-ddns-server está disponible para ser instalado.") print("Descargando e instalando el paquete isc-kea-dhcp-ddns-server...") subprocess.run(["apt-get", "install", "-y", "isc-kea-dhcp-ddns-server"], check=True) print("El paquete isc-kea-dhcp-ddns-server ha sido descargado e instalado correctamente.") else: print("El paquete isc-kea-dhcp-ddns-server no está disponible en los repositorios apt.") if subprocess.run(["apt-cache", "show", "isc-kea-ctrl-agent"], capture_output=True).returncode == 0: print("El paquete isc-kea-ctrl-agent está disponible para ser instalado.") print("Descargando e instalando el paquete isc-kea-ctrl-agent...") subprocess.run(["apt-get", "install", "-y", "isc-kea-ctrl-agent"], check=True) print("El paquete isc-kea-ctrl-agent ha sido descargado e instalado correctamente.") else: print("El paquete isc-kea-ctrl-agent no está disponible en los repositorios apt.") def install_kea(): print(f"{install_kea.__name__}(): Instalación de muestra para el servicio DHCP de Kea.") def backupFile(file): if not os.path.isfile(file): logger.warning(f"File {file} doesn't exist") return logger.info(f"Making backup of {file}") shutil.copy2(file, f"{file}-LAST") dateymd = datetime.datetime.now().strftime("%Y%m%d") backup_file = f"{file}-{dateymd}" if not os.path.isfile(backup_file): shutil.copy2(file, backup_file) logger.info(f"Backup of {file} successful") def isc_keaDhcpConfigure(): print(f"{isc_keaDhcpConfigure.__name__}(): Configuración de muestra para el servicio DHCP de Kea.") errcode = 0 i = 0 interfaces = "[" #DEVICE = ["eth0", "eth1"] # Define the DEVICE variable with appropriate values #NETMASK = "255.255.255.0" # Replace with the actual value of NETMASK #NETIP = "192.168.0.1" # Replace with the actual value of NETIP #ROUTERIP = "192.168.0.1" # Replace with the actual value of ROUTERIP DNSIP = "192.168.0.1" # Replace with the actual value of DNSIP #SERVERIP = "192.168.0.1" # Replace with the actual value of SERVERIP # Rest of the code... # Define the NETMASK variable # Construir la lista de interfaces print(NETIP) print(NETMASK) #CIDR = mask2cidr(NETMASK) #print(CIDR) # Salida: 24 print(ROUTERIP) print(DNSIP) with open("/etc/kea/kea-dhcp4.conf", "w") as file: file.write( f""" "interfaces": {interfaces}, "SERVERIP": "{SERVERIP}", "NETIP": "{NETIP}", "NETMASK": "{NETMASK}", "ROUTERIP": "{ROUTERIP}", "DNSIP": "{DNSIP}" """ ) # Si hubo errores al configurar, muestra un mensaje de error y sale de la función if errcode != 0: print(f"{isc_keaDhcpConfigure.__name__}(): Error al configurar el servicio DHCP de Kea.") return 1 print(f"{isc_keaDhcpConfigure.__name__}(): Configuración de muestra para el servicio DHCP de Kea configurada en \"/etc/kea/kea-dhcp4.conf\".") return 0 def testPxe(): logger.info(f"Checking TFTP service... please wait.") subprocess.run(["echo", "test"], stdout=open(f"{TFTPCFGDIR}/testpxe", "w")) try: subprocess.run(["tftp", "-v", "127.0.0.1", "-c", "get", "testpxe", "/tmp/testpxe"], check=True) logger.info("TFTP service is OK.") except subprocess.CalledProcessError: logger.error("TFTP service is down.") os.remove(f"{TFTPCFGDIR}/testpxe") def run_command(command): result = subprocess.run(command, shell=True, text=True, capture_output=True) if result.returncode != 0: print(f"Error executing command: {command}") print(result.stderr) exit(1) print(result.stdout) def tftpConfigure(): # Update the package list #print("Updating package list...") run_command("sudo apt update") run_command("sudo DEBIAN_FRONTEND=noninteractive apt install -y build-essential") # Install tftpd-hpa in non-interactive mode print("Installing tftpd-hpa...") run_command("sudo DEBIAN_FRONTEND=noninteractive apt install -y tftpd-hpa") # Configure tftpd-hpa print("Configuring tftpd-hpa...") tftpd_config = """ # /etc/default/tftpd-hpa TFTP_USERNAME="tftp" TFTP_DIRECTORY="/var/lib/tftpboot" TFTP_ADDRESS="0.0.0.0:69" TFTP_OPTIONS="--secure" """ with open("/tmp/tftpd-hpa", "w") as config_file: config_file.write(tftpd_config) run_command("sudo mv /tmp/tftpd-hpa /etc/default/tftpd-hpa") # Create and set permissions for the TFTP directory print("Creating and setting permissions for the TFTP directory...") run_command("sudo mkdir -p /var/lib/tftpboot") run_command("sudo chown -R tftp:tftp /var/lib/tftpboot") run_command("sudo chmod -R 755 /var/lib/tftpboot") # Restart the tftpd-hpa service print("Restarting tftpd-hpa service...") run_command("sudo systemctl restart tftpd-hpa") # Check the status of the tftpd-hpa service print("Checking tftpd-hpa service status...") run_command("sudo systemctl status tftpd-hpa") # if TFTPSERV: # logger.info(f"TFTPSERV is configured: {TFTPSERV}") # inetd_cfg_path = f"{INETDCFGDIR}/{TFTPSERV}" # if os.path.isfile(inetd_cfg_path): # logger.error(f"The inetd configuration file exists: {inetd_cfg_path}") # with open(inetd_cfg_path, "r+") as file: # content = file.read() # new_content = content.replace("disable.*", "disable = no") # file.seek(0) # file.write(new_content) # file.truncate() # logger.info(f"Modified inetd configuration file: {inetd_cfg_path}") # else: # service = TFTPSERV # logger.info(f"Enabling and starting the service {service}.service") # subprocess.run(["systemctl", "enable", f"{service}.service"], check=True) # subprocess.run(["systemctl", "start", f"{service}.service"], check=True) # service = INETDSERV # logger.info(f"Enabling and starting the service {service}.service") #subprocess.run(["systemctl", "enable", f"{service}.service"], check=True) # subprocess.run(["systemctl", "start", f"{service}.service"], check=True) #Crear directorio /var/lib/tftpboot if not os.path.exists(TFTPCFGDIR): os.makedirs(TFTPCFGDIR, mode=0o750, exist_ok=True) logger.info(f"Directory {TFTPCFGDIR} created.") else: logger.warning(f"Directory {TFTPCFGDIR} exist.") #Descargar oglive logger.info("Downloading oglive...") #Temporalmente se copia desde ~/oglive # copiar desde el montaje /mnt/srv/artefactos/ogboot/tftpboot/ logger.error(f"___________/home/qindel/ogboot/installer___________________Intentando copiar desde ...{PROGRAM_DIR} a {TFTPCFGDIR}") subprocess.run(["cp", "-r", f"/tmp/tftpboot/ogLive-5.11.0-r20210413", f"{TFTPCFGDIR}/"]) subprocess.run(["cp", "-r", f"/tmp/tftpboot/ipxe", f"{TFTPCFGDIR}/"]) #Crear enlace simbólico de oglive-5.11.0-r20210413 a /var/lib/tftpboot/ogLive subprocess.run(["ln", "-s", f"{TFTPCFGDIR}/ogLive-5.11.0-r20210413", "{TFTPCFGDIR}/ogLive"]) #Crear enlace simbólico de /var/lib/tftpboot/ogLive a /var/lib/tftpboot/ogLive/ogclient subprocess.run(["ln", "-s", "{TFTPCFGDIR}/ogLive", "{TFTPCFGDIR}/ogclient"]) symlink_target = f"{INSTALL_OGBOOT_TARGET}/tftpboot" logger.info(f"Creating symbolic link from {TFTPCFGDIR} to {symlink_target}") #if not os.path.exists(symlink_target): # os.symlink("{TFTPCFGDIR}", symlink_target) # os.chown(symlink_target, pwd.getpwnam("ogboot").pw_uid, pwd.getpwnam("ogboot").pw_gid) #else: # logger.warning(f"The symbolic link already exists: {symlink_target}") # Create the symbolic link logger.info("Creating symbolic link from /opt/ogboot/tftpboot to /var/lib/tftpboot...") if os.path.islink(symlink_target): run_command(f"sudo rm -rf {symlink_target}") elif os.path.exists(symlink_target): run_command(f"sudo ln -s {TFTPCFGDIR} {symlink_target}") def servicesCompilation(): hayErrores = 0 process = subprocess.run(["make"], cwd=f"{WORKDIR}/ogboot/sources/clients/ogAdmClient") subprocess.run(["mv", f"{WORKDIR}/ogboot/sources/clients/ogAdmClient/ogAdmClient", f"{WORKDIR}/ogboot/client/shared/bin"]) if process.returncode != 0: logger.info(f"{servicesCompilation.__name__}(): error while compiling OpenGnsys Admin Client") hayErrores = 1 return hayErrores def copyInterfaceAdm(): hayErrores = 0 cp_process = subprocess.run(["cp", "-ar", f"{WORKDIR}/ogboot/sources/interface", f"{INSTALL_TARGET}/client/interfaceAdm"]) if cp_process.returncode != 0: logger.error(f"Error while copying Administration Interface Folder") hayErrores = 1 return hayErrores def copyClientFiles(): errstatus = 0 logger.info(f"Copying OpenGnsys Client files.") source_files = glob.glob(f"{WORKDIR}/ogboot/client/shared/*") # Copy each file individually for file in source_files: cp_process = subprocess.run(["cp", "-a", file, f"{INSTALL_TARGET}/client"]) if cp_process.returncode != 0: logger.error(f"Error while copying client structure: {file}") errstatus = 1 logger.info(f"Copying OpenGnsys Cloning Engine files.") os.makedirs(f"{INSTALL_TARGET}/client/lib/engine/bin", mode=0o750, exist_ok=True) engine_files = glob.glob(f"{WORKDIR}/ogboot/client/engine/*.lib*") # Copiar cada archivo individualmente for file in engine_files: cp_engine_process = subprocess.run(["cp", "-a", file, f"{INSTALL_TARGET}/client/lib/engine/bin"]) if cp_engine_process.returncode != 0: logger.error(f"Error while copying engine files: {file}") errstatus = 1 if errstatus == 0: logger.info(f"Client copy files success.") else: logger.error(f"Client copy files with errors") return errstatus def cidr2mask(bits): args = [5 - (bits // 8), 255, 255, 255, 255, (255 << (8 - (bits % 8))) & 255, 0, 0, 0] if args[0] > 1: args = args[args[0]:] else: args = args[1:] return ".".join(str(arg) for arg in args) def mask2cidr(mask): addr = mask.split(".") cidr = 0 for i in addr: cidr += bin(int(i)).count("1") return cidr def getNetworkSettings(): DHCPNET = "127.0.0.1" #print("getNetworkSettings(): Detecting network parameters.") output = subprocess.check_output(["ip", "-o", "link", "show", "up"]).decode("utf-8") lines = output.strip().split("\n") for line in lines: dev = line.split(":")[1].split("@")[0].strip() output = subprocess.check_output(["ip", "-o", "addr", "show", "dev", dev]).decode("utf-8") addr_lines = output.strip().split("\n") for addr_line in addr_lines: addr_parts = addr_line.split() cidr_netmask = addr_parts[3].split("/")[1] cidr_netmask = int(cidr_netmask) if cidr_netmask >= 0 and cidr_netmask <= 32: netmask = ".".join([str((0xffffffff << (32 - cidr_netmask) >> i) & 0xff) for i in [24, 16, 8, 0]]) if addr_parts[2] == "inet": DEVICE.append(dev) SERVERIP.append(addr_parts[3].split("/")[0]) NETMASK.append(netmask) NETBROAD.append(addr_parts[5]) NETIP.append(addr_parts[3].split("/")[0]) ROUTERIP.append(subprocess.check_output(["ip", "route", "list", "default"]).decode("utf-8").split()[2]) if DHCPNET == NETIP[-1]: DEFAULTDEV = dev else: DEFAULTDEV = DEFAULTDEV if "DEFAULTDEV" in locals() else dev if len(NETIP) == 0 or len(NETMASK) == 0: return (1) else: return (0) def openGnsysConfigure(): i = 0 dev = "" CONSOLEURL = "" CONSOLEURL = f"https://{SERVER_OPENGNSYS}/opengnsys" with open(f"{WORKDIR}/ogboot/etc/ogAdmClient.cfg") as file: content = file.read() content = content.replace("SERVERIP", SERVER_OPENGNSYS) content = content.replace("OPENGNSYSURL", CONSOLEURL) with open(f"{INSTALL_TARGET}/client/etc/ogAdmClient-ens160.cfg", "w") as outfile: outfile.write(content) os.symlink(f"{INSTALL_TARGET}/client/etc/ogAdmClient-ens160.cfg", f"{INSTALL_TARGET}/client/etc/ogAdmClient.cfg") TZ = subprocess.check_output(["timedatectl", "status"]).decode().split("\n")[2].split(":")[1].strip() with open(f"{INSTALL_TARGET}/client/etc/engine.cfg", "a") as file: file.write(f"# OpenGnsys Server timezone.\nTZ=\"{TZ.replace(' ', '')}\"\n") logger.info(f"OpenGnsys config files created.") def mount_NFS(): if subprocess.call(["sudo", "mount", "-t", "nfs", "ognartefactos.evlt.uma.es:/", "/mnt"]) == 0: logger.info("Properly mounted NFS system.") else: logger.error("Could not mount the NFS system.") exit(1) logger.info("copy ipxe::::::::::::::::::::::::::::") #subprocess.call(["ls", "/mnt/"]) subprocess.call(["sudo", "cp", "-r", "/mnt/srv/artefactos/ogboot/tftpboot/", "/tmp"]) if not os.path.exists(IPXE_DIR + "/tftpboot"): os.makedirs(IPXE_DIR + "/tftpboot") #os.chdir(f"{IPXE_DIR}/tftpboot") subprocess.call(["sudo", "cp", "-r", "/mnt/srv/artefactos/ogboot/ipxe/", "/tmp"]) if not os.path.exists(IPXE_DIR + "/src"): os.makedirs(IPXE_DIR + "/src") os.chdir(f"{IPXE_DIR}/src") if subprocess.call(["sudo", "make", "-s", "-j", "4"]) == 0: logger.info(f"Directory {IPXE_DIR}/src correctly mounted.") else: logger.error(f"ERROR\tCould not mount the directory {IPXE_DIR}/src.") exit(1) if not os.path.exists("/opt/opengnsys"): os.symlink("/opt/ogboot/", "/opt/opengnsys") logger.info("Symbolic link created successfully.") #logger.info(f"copy ipxe(2):::::::::::::::::::::::::::: EMBED={INSTALL_OPENGNSYS_TARGET}/tftpboot/ipxe_scripts/dhcp_boot.ipxe") #if subprocess.call(["sudo", "make", "-s", "bin/undionly.kpxe", f"EMBED={INSTALL_OPENGNSYS_TARGET}/tftpboot/ipxe_scripts/dhcp_boot.ipxe"]) == 0: # logger.info("Boot file mounted correctly.") #else: # logger.error("Failed to mount boot file.") # exit(1) logger.info("copy ipxe(3)::::::::::::::::::::::::::::") subprocess.call(["sudo", "cp", "bin/undionly.kpxe", f"{INSTALL_OPENGNSYS_TARGET}/tftpboot"]) if subprocess.call(["sudo", "make", "-s", "bin-x86_64-efi/ipxe.efi", f"EMBED={INSTALL_OPENGNSYS_TARGET}/tftpboot/ipxe_scripts/dhcp_boot.ipxe"]) == 0: logger.info("Properly constructed EFI file.") else: logger.error("Could not build EFI file.") exit(1) subprocess.call(["sudo", "cp", "bin-x86_64-efi/ipxe.efi", f"{INSTALL_OPENGNSYS_TARGET}/tftpboot"]) def generate_ipxe_script(): ip_address_server = subprocess.check_output(["ifconfig", "ens160"]).decode().split("\n")[1].split()[1] template = os.path.join(WORKDIR, "ogboot/etc/dhcp_boot.ipxe.tmpl") ipxe_output = f"{INSTALL_OGBOOT_TARGET}/tftpboot/ipxe_scripts/dhcp_boot.ipxe" os.makedirs(os.path.dirname(ipxe_output), mode=0o750, exist_ok=True) shutil.copy(template, ipxe_output) with open(ipxe_output, "r") as ipxe_file: ipxe_content = ipxe_file.read() ipxe_content = ipxe_content.replace("__SERVERIP__", ip_address_server) with open(ipxe_output, "w") as ipxe_file: ipxe_file.write(ipxe_content) # Reemplazar SERVERIP con la dirección IP en la plantilla y guardarla en el archivo de salida # with open(template, "r") as tmpl_file: # template_content = tmpl_file.read() # ipxe_content = template_content.replace("SERVERIP", ip_address_server) # with open(ipxe_output, "w") as ipxe_file: # ipxe_file.write(ipxe_content) template_default = os.path.join(WORKDIR, "ogboot/tftpboot/ipxe_scripts/default.ipxe") default_output = os.path.join(INSTALL_OGBOOT_TARGET, "tftpboot/ipxe_scripts/default.ipxe") with open(template_default, "r") as default_tmpl_file: default_template_content = default_tmpl_file.read() default_ipxe_content = default_template_content.replace("__SERVERIP__", ip_address_server) with open(default_output, "w") as default_ipxe_file: default_ipxe_file.write(default_ipxe_content) logger.info("ipxe files created correctly.") def smbConfigure(): #logger.info(f"{smbConfigure.__name__}(): Configuring Samba service.") backupFile(f"{SAMBACFGDIR}/smb.conf") # Copiar plantilla de recursos para OpenGnsys with open(os.path.join(WORKDIR, 'ogboot/etc/smb-ogboot.conf.tmpl'), 'r') as tmpl_file: template = tmpl_file.read() replaced_template = template.replace('__OGBOOTDIR__', INSTALL_OPENGNSYS_TARGET) with open(os.path.join(SAMBACFGDIR, 'smb-ogboot.conf'), 'w') as conf_file: conf_file.write(replaced_template) # Configurar y recargar Samba" subprocess.run(["perl", "-pi", "-e", "s/WORKGROUP/OPENGNSYS/; s/server string \=.*/server string \= ogBoot Samba Server/", f"{SAMBACFGDIR}/smb.conf"]) if "smb-ogboot" not in open(f"{SAMBACFGDIR}/smb.conf").read(): with open(f"{SAMBACFGDIR}/smb.conf", "a") as file: file.write(f"include = {SAMBACFGDIR}/smb-ogboot.conf\n") service = SAMBASERV subprocess.run(["systemctl", "enable", f"{service}.service"]) subprocess.run(["systemctl", "restart", f"{service}.service"]) # Comprobar si se ha configurado correctamente Samba if subprocess.run(["systemctl", "is-active", f"{service}.service"]).returncode == 0: logger.info(f"{service} service started successfully.") else: logger.error(f"Failed to start {service} service.") return 1 # Establecer la contraseña para el usuario opengnsys smbpasswd_command = f"(echo {OPENGNSYS_CLIENT_PASSWD}; echo {OPENGNSYS_CLIENT_PASSWD}) | sudo smbpasswd -s -a {OPENGNSYS_CLIENT_USER}" if "Python 3.7" in {PYTHON_VERSION}: result = subprocess.run(smbpasswd_command, shell=True, capture_output=True, text=True) if result.returncode == 0: logger.info(f"The password for the user {OPENGNSYS_CLIENT_USER} has been set correctly..") else: logger.error(f"Error setting password: {result.stderr}") else: process = subprocess.Popen(smbpasswd_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() if process.returncode == 0: logger.info(f"The password for the user {OPENGNSYS_CLIENT_USER} has been set correctly..") else: logger.error(f"Error setting password: {stderr}") logger.info(f"Added Samba configuration.") return 0 def get_network_interfaces(): interfaces = [] with open('/proc/net/dev') as f: for line in f: if ':' in line: parts = line.split(':') if len(parts) > 1: if parts[0].strip() != "lo": interface = parts[0].strip() # Accessing the first part as the interface name interfaces.append(interface) return interfaces print(get_network_interfaces()) #exit (0) def setup_nginx(): try: # Obtener la IP del servidor ip_address_server = subprocess.check_output(["ifconfig", "ens160"]).decode().split("\n")[1].split()[1] # Leer y modificar la plantilla de configuración de nginx template_path = os.path.join(WORKDIR, "ogboot/etc/nginxServer.conf.tmpl") with open(template_path, 'r') as nginx_file: nginx_content = nginx_file.read() nginx_content = nginx_content.replace("__SERVERIP__", ip_address_server) # Ruta de destino para la configuración de nginx nginx_output = "/etc/nginx/sites-available/ogboot.conf" with open(nginx_output, 'w') as nginx_file: nginx_file.write(nginx_content) logger.info("Nginx configuration file created successfully.") # Crear el enlace simbólico en sites-enabled subprocess.run(["sudo", "ln", "-sf", nginx_output, "/etc/nginx/sites-enabled/ogboot.conf"]) logger.info("Symbolic link for nginx configuration created successfully.") # Reiniciar el servicio de samba subprocess.run(["sudo", "systemctl", "restart", "smbd.service"]) logger.info("Samba service restarted successfully.") except subprocess.CalledProcessError as e: logger.error(f"Subprocess error: {e}") exit(1) except OSError as e: logger.error(f"OS error: {e}") exit(1) def install_oglive_daemon(): INSTALL_TARGET = "/opt/ogboot" DAEMON_SCRIPT_SRC = os.path.join(WORKDIR, "oglive_daemon.py") DAEMON_SCRIPT_DEST = os.path.join(INSTALL_TARGET, "bin", "oglive_daemon.py") SERVICE_FILE_SRC = os.path.join(WORKDIR, "ogboot/etc/oglive_daemon.service") SERVICE_FILE_DEST = "/etc/systemd/system/oglive_daemon.service" shutil.copyfile(DAEMON_SCRIPT_SRC, DAEMON_SCRIPT_DEST) logger.info(f"Copied {DAEMON_SCRIPT_SRC} to {DAEMON_SCRIPT_DEST}") os.chmod(DAEMON_SCRIPT_DEST, 0o755) logger.info(f"Set executable permissions for {DAEMON_SCRIPT_DEST}") shutil.copyfile(SERVICE_FILE_SRC, SERVICE_FILE_DEST) logger.info(f"Copied {SERVICE_FILE_SRC} to {SERVICE_FILE_DEST}") subprocess.run(["systemctl", "daemon-reload"], check=True) logger.info("Reloaded systemd daemon") subprocess.run(["systemctl", "enable", "oglive_daemon.service"], check=True) logger.info("Enabled oglive_daemon service") subprocess.run(["systemctl", "start", "oglive_daemon.service"], check=True) logger.info("Started oglive_daemon service") ############################################################################### ###:::::::::::::::::::::::::::::::: MAIN :::::::::::::::::::::::::::::::::::### ############################################################################### logger.info(f"Starting installation of ogBoot.") if os.geteuid() != 0: logger.error("This program must be run with root privileges..") exit(1) if os.path.exists(os.path.join(INSTALL_OGBOOT_TARGET, "/doc/")): logger.warning(f"ogBoot is already installed. Run {INSTALL_OGBOOT_TARGET}/lib/ogboot_devel_update.py” with root privileges to update..") exit(2) logger.info("Verifying Python distribution and version.") check_distribution() check_python_version() logger.info("Checking the operating system.") autoConfigure() logger.info("Installing necessary packages.") Missing = get_missing_packages () install_packages(Missing) logger.info("Obtaining the default network configuration.") if getNetworkSettings() != 0: logger.error("Error reading default network settings.") exit(1) logger.info("Configuring package repositories.") if REMOTE == 1: downloadCode(GIT_REPO) if os.system("echo $?") != 0: logger.error("Error while getting code from the repository") exit(1) else: if not os.path.exists(f"{WORKDIR}/ogboot"): logger.warning(f"Does not exist {WORKDIR}/ogboot") if not os.path.exists(WORKDIR): os.makedirs(WORKDIR, mode=0o750, exist_ok=True) logger.info(f"ogBoot directory not found, create a symbolic link to the directory where the code is located {WORKDIR} To {os.path.dirname(PROGRAM_DIR)}") os.symlink(os.path.dirname(PROGRAM_DIR), f"{WORKDIR}/ogboot") #logger.info("Creating directories.") #createDirs(INSTALL_TARGET) #if os.system("echo $?") != 0: # logger.error("Error while creating directory paths!") # exit(1) logger.info("Creating ogBoot project.") og_core_create_user("ogboot") logger.info("Creating directories.") og_boot_create_dirs() logger.info("Installing Symfony.") og_boot_symfony_install(INSTALL_OGBOOT_TARGET) logger.info("Copying installation files.") og_boot_copy_files(INSTALL_OGBOOT_TARGET) #if os.system("echo $?") != 0: # logger.error("Error while creating skeleton directory!") # exit(1) logger.info("Configuring IPXE services") generate_ipxe_script() logger.info("Setting up NFS system") mount_NFS() logger.info("Configuring TFTP service.") tftpConfigure() logger.info("Compiling OpenGnsys services source code") servicesCompilation() if subprocess.run(["echo", "$?"]).returncode != 0: logger.error("Error while compiling OpenGnsys services") exit(1) logger.info("Copy folder Interface between administration and cloning engine") copyInterfaceAdm() if subprocess.run(["echo", "$?"]).returncode != 0: logger.error("Error while copying Administration Interface") exit(1) logger.info("Create the structure of the accesses to the server from the client (shared)") copyClientFiles() if subprocess.run(["echo", "$?"]).returncode != 0: logger.error("Error creating client structure") logger.info("Setup nginx") setup_nginx() if subprocess.run(["echo", "$?"]).returncode != 0: logger.error("Error setup nginx") logger.info("Configure ogboot daemon") install_oglive_daemon() if subprocess.run(["echo", "$?"]).returncode != 0: logger.error("Error configure ogboot daemon") logger.info("Configuring ogCore") openGnsysConfigure() logger.info("Configuring Samba") smbConfigure() logger.info(f"ogBoot installation finished.") logging.shutdown() console_handler.close()