ogboot/installer/ogboot_installer.py

1074 lines
42 KiB
Python

#!/usr/bin/env python3
##################################################################################
##### ogBoot installer script ####
##### Developed by: Luis Gerardo Romero García <lromero@qindel.com> ####
##### Antonio Emmanuel Guerrero Silva <aguerrero@qindel.com> ####
##### Last: 2024-07-08 ####
##################################################################################
import platform, os, sys, subprocess, datetime, shutil, pwd, glob, logging, distro, re, json
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])
config_file = os.path.join(PROGRAM_DIR, 'config.json')
with open(config_file, 'r') as f:
config = json.load(f)
OGCORE_IP = config["ogCore_ServerIP"]
INSTALL_OPENGNSYS_TARGET = "/opt/opengnsys"
INSTALL_OGBOOT_TARGET = "/opt/ogboot"
INSTALL_TARGET = "/opt/ogboot"
GIT_REPO = "ssh://git@ognproject.evlt.uma.es:21987/opengnsys/ogboot.git"
OPENGNSYS_CLIENT_USER = config["ogBootSambaUser"]
OPENGNSYS_CLIENT_PASSWD = config["ogBootSambaPass"]
OSDISTRIB = ""
OSVERSION = ""
IPXE_DIR = "/tmp/ogboot_ipxe"
WORKDIR ="/tmp/ogboot_installer"
DEFAULTDEV = ""
PACKAGES_TO_INSTALL = ["htop"]
DEVICE = []
SERVERIP = []
NETIP = []
NETMASK = []
NETBROAD = []
ROUTERIP = []
BRANCH = sys.argv[1] if len(sys.argv) > 1 else "main"
INETDSERV = "xinetd"
UBUNTU_OS_VERSION = "24"
PYTHON_VERSION = 3
if os.path.isdir(f"{PROGRAM_DIR}/../installer"):
REMOTE = 0
else:
REMOTE = 1
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', 'chmod', '775', log_file])
#Configure the log
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? [y/N]: ")
if go_on.upper() != "Y":
logger.error("Leaving the installation.")
exit()
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("downloadCog_boot_create_dirsode(): 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_TO_INSTALL = ["vim", "curl", "htop"]
case "18.04":
PACKAGES_TO_INSTALL = ["nano", "wget", "tree"]
case "22.04":
PACKAGES_TO_INSTALL = ["nfs-common", "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", "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", "samba-common-bin", "schroot", "shim-signed", "squashfs-tools", "subversion", "tftpd-hpa", "udpcast", "unzip", "wakeonlan", "wget", "xinetd", "jq", "moreutils", "net-tools", "isolinux", "syslinux"]
case "24.04":
PACKAGES_TO_INSTALL = ["nfs-common", "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", "libdbi-dev", "libdbi1", "libev-dev", "libjansson-dev", "liblz4-tool", "libssl-dev", "moreutils", "netpipes", "php8.3", "php8.3-bcmath", "php8.3-cli", "php8.3-curl", "php8.3-fpm", "php8.3-gd", "php8.3-ldap", "php8.3-mbstring", "php8.3-mysql", "php8.3-common", "php-pear", "php8.3-xml", "php8.3-zip", "procps", "coreutils", "rsync", "samba", "samba-common-bin", "schroot", "shim-signed", "squashfs-tools", "subversion", "tftpd-hpa", "udpcast", "unzip", "wakeonlan", "wget", "xinetd", "jq", "moreutils", "isolinux", "syslinux", "file"]
case _:
PACKAGES_TO_INSTALL = ["bash", "rsync"]
case "suse":
match OSVERSION:
case "15":
PACKAGES_TO_INSTALL = ["zypper", "mc", "gcc"]
case "12":
PACKAGES_TO_INSTALL = ["perl", "tar", "man"]
case _:
PACKAGES_TO_INSTALL = ["openssl", "ncurses", "zip"]
case "redhat":
match OSVERSION:
case "8":
PACKAGES_TO_INSTALL = ["yum", "tar", "perl"]
case "7":
PACKAGES_TO_INSTALL = ["bash", "rpm", "tcpdump"]
case _:
PACKAGES_TO_INSTALL = ["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="/tmp/installed_packages.log"):
if not missing:
logger.info("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)
subprocess.run(
["sudo", "apt-get", "install", "--allow-change-held-packages", "-y", "--no-install-recommends"] + missing,
check=True
)
with open(log_packages_file, "a") as log:
for package in missing:
logger.info(f"{package} installed correctly.")
log.write(package + "\n")
logger.info("All missing packages have been installed.")
# Check PHP version and install corresponding php-fpm package
php_version = subprocess.check_output(["php", "-v"]).decode("utf-8")
if "PHP 8.1" in php_version:
subprocess.run(["sudo", "apt-get", "install", "-y", "php8.1-fpm"], check=True)
elif "PHP 8.3" in php_version:
subprocess.run(["sudo", "apt-get", "install", "-y", "php8.3-fpm"], check=True)
else:
logger.warning("PHP version not supported.")
finally:
if original_debian_frontend is not None:
os.environ['DEBIAN_FRONTEND'] = original_debian_frontend
else:
del os.environ['DEBIAN_FRONTEND']
def autoConfigure():
global OSDISTRIB, OSVERSION
#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 add_sudoers_permissions():
sudoers_entry = """
ogboot ALL=(ALL) NOPASSWD: /opt/bin/oglivecli
ogboot ALL=(root) NOPASSWD: /usr/bin/mount, /usr/bin/umount, /usr/bin/cp, /usr/bin/chmod, /usr/bin/chown, /usr/bin/md5sum, /usr/bin/smbpasswd, /usr/bin/cat, /usr/bin/tee, /usr/bin/sed, /usr/bin/gzip, /usr/bin/lz4, /usr/bin/cpio, /usr/bin/find, /bin/tee, /usr/bin/dd, /usr/bin/mkfs.ext4, /usr/bin/rsync
ogboot ALL=(root) NOPASSWD: /opt/ogboot/lib/*.iso /mnt
"""
sudoers_file = '/etc/sudoers.d/ogboot'
try:
with open(sudoers_file, 'w') as file:
file.write(sudoers_entry)
print("Sudoers permissions for 'ogboot' added successfully.")
except IOError as e:
print(f"Failed to write to {sudoers_file}: {e}")
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():
global INSTALL_OGBOOT_TARGET
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=0o775, exist_ok=True)
os.makedirs(os.path.join(INSTALL_OGBOOT_TARGET, "client"), mode=0o775, 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(["chown", "-R", "ogboot:ogboot", INSTALL_OGBOOT_TARGET])
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():
global WORKDIR, INSTALL_OGBOOT_TARGET, OGCORE_IP
logger.info("Creating Symfony application skeleton...")
try:
# Copiar los archivos .env y composer.json primero
env_src = os.path.join(f"{WORKDIR}", "ogboot/.env")
composer_src = os.path.join(f"{WORKDIR}", "ogboot/composer.json")
env_dest = os.path.join(f"{INSTALL_OGBOOT_TARGET}", ".env")
composer_dest = os.path.join(f"{INSTALL_OGBOOT_TARGET}", "composer.json")
shutil.copy(env_src, env_dest)
shutil.copy(composer_src, composer_dest)
logger.info(f"Copied environment source {env_src} to {env_dest}")
logger.info(f"Copied composer source {composer_src} to {composer_dest}")
# Cambiar permisos y propietario de los archivos copiados
os.chmod(env_dest, 0o644)
os.chmod(composer_dest, 0o644)
shutil.chown(env_dest, user='ogboot', group='ogboot')
shutil.chown(composer_dest, user='ogboot', group='ogboot')
logger.info(f"Set permissions and owner for {env_dest} and {composer_dest}")
# Añadir la línea OGCORE_API_URL utilizando OGCORE_IP
ogcore_api_url = f'OGCORE_API_URL="https://{OGCORE_IP}"'
with open(env_dest, 'a') as env_file:
env_file.write(f"\n{ogcore_api_url}\n")
logger.info(f"Added OGCORE_API_URL to {env_dest} with IP: {OGCORE_IP}")
except Exception as e:
logger.error(f"An error occurred while copying files or modifying .env: {e}")
raise
def og_boot_copy_files():
global INSTALL_TARGET, WORKDIR
bin_source = os.path.join(WORKDIR, "ogboot/bin")
bin_dest = os.path.join(INSTALL_OGBOOT_TARGET, "bin")
src_source = os.path.join(WORKDIR, "ogboot/src")
src_dest = os.path.join(INSTALL_OGBOOT_TARGET, "src")
config_source = os.path.join(WORKDIR, "ogboot/config")
config_dest = os.path.join(INSTALL_OGBOOT_TARGET, "config")
lib_source = os.path.join(WORKDIR, "ogboot/lib")
lib_dest = os.path.join(INSTALL_OGBOOT_TARGET, "lib")
os.makedirs("/tmp/opt", exist_ok=True)
subprocess.run(["chown", "-R", "ogboot:ogboot", "/tmp/opt"])
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=0o775, exist_ok=True)
os.makedirs(os.path.join(INSTALL_TARGET, "client"), mode=0o775, exist_ok=True)
os.makedirs(os.path.join(INSTALL_TARGET, "public"), mode=0o775, exist_ok=True)
subprocess.run(["chmod", "-R", "775", INSTALL_OGBOOT_TARGET])
subprocess.run(["chown", "-R", "ogboot:ogboot", INSTALL_OGBOOT_TARGET])
def og_boot_composer_install():
# Ejecutar Composer como el usuario 'ogboot' para instalar el proyecto Symfony
result = subprocess.run(["sudo", "-u", "ogboot", "composer", "install", "--no-interaction", "--working-dir", INSTALL_OGBOOT_TARGET])
if result.returncode != 0:
logger.error("Error creating Symfony project using Composer")
return
# Ejecutar Composer como el usuario 'ogboot' para actualizar el paquete doctrine/dbal
result = subprocess.run(["sudo", "-u", "ogboot", "/opt/ogboot/bin/composer.phar", "update", "doctrine/dbal", "--working-dir", INSTALL_OGBOOT_TARGET])
if result.returncode != 0:
logger.error("Error updating doctrine/dbal package using Composer")
return
# Eliminar composer.lock si existe
composer_lock_path = os.path.join(INSTALL_OGBOOT_TARGET, "composer.lock")
if os.path.exists(composer_lock_path):
os.remove(composer_lock_path)
subprocess.call(["chown", "-R", "ogboot:ogboot", f"{INSTALL_OGBOOT_TARGET}/public"])
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 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")
# Obtener la UID y GID del usuario ogboot
def get_ogboot_uid_gid():
try:
user_info = pwd.getpwnam('ogboot')
uid = user_info.pw_uid
gid = user_info.pw_gid
return uid, gid
except KeyError:
raise Exception("El usuario 'ogboot' no existe.")
# Añadir líneas al fstab
def add_fstab_entries(uid, gid):
try:
fstab_entries = [
f'/opt/ogboot/lib/oglive.iso /tmp/opt/ogboot/lib/ogLive iso9660 loop,ro,users,uid={uid},gid={gid},noauto 0 0\n',
f'/var/lib/tftpboot/ogLive/ogclient.sqfs /tmp/ogclient_mount squashfs loop,ro,user,noauto 0 0\n'
]
with open('/etc/fstab', 'r') as fstab:
existing_entries = fstab.readlines()
# Check if the entries already exist in /etc/fstab
if all(entry.strip() not in existing_entries for entry in fstab_entries):
with open('/etc/fstab', 'a') as fstab:
fstab.writelines(fstab_entries)
logger.info("Entradas añadidas a /etc/fstab correctamente.")
else:
logger.info("Las entradas ya existen en /etc/fstab. No se añadieron nuevamente.")
except IOError:
raise Exception("Error al escribir en /etc/fstab.")
# Añadir el usuario ogboot al grupo disk
def add_user_to_disk_group():
try:
subprocess.run(['usermod', '-aG', 'disk', 'ogboot'], check=True)
logger.info("Usuario 'ogboot' añadido al grupo 'disk' correctamente.")
except subprocess.CalledProcessError:
raise Exception("Error al añadir el usuario 'ogboot' al grupo 'disk'.")
def tftpConfigure():
global INETDSERV
TFTPCFGDIR = "/var/lib/tftpboot"
logger.info("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)
shutil.move("/tmp/tftpd-hpa", "/etc/default/tftpd-hpa")
logger.info("\t2-Creating and setting permissions for the TFTP directory...")
os.makedirs("/var/lib/tftpboot", exist_ok=True)
subprocess.run("chown -R tftp:tftp /var/lib/tftpboot", shell=True, text=True, capture_output=True)
subprocess.run("chmod -R 775 /var/lib/tftpboot", shell=True, text=True, capture_output=True)
logger.info("\t3-Setting permissions for /var/lib/tftpboot directory...")
subprocess.run("systemctl restart tftpd-hpa", shell=True, text=True, capture_output=True)
logger.info("Checking tftpd-hpa service status...")
subprocess.run("systemctl status tftpd-hpa", shell=True, text=True, capture_output=True)
if os.path.exists(TFTPCFGDIR):
subprocess.run(["chown", "-R", "tftp:ogboot", TFTPCFGDIR])
logger.info(f"{TFTPCFGDIR} directory permissions modified correctly.")
else:
logger.warning(f"\t1-{TFTPCFGDIR} directory not exist.")
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.lchown(symlink_target, pwd.getpwnam("tftp").pw_uid, pwd.getpwnam("ogboot").pw_gid)
else:
logger.warning(f"The symbolic link already exists: {symlink_target}")
logger.info("Downloading oglive...")
iso_url = "https://ognproject.evlt.uma.es/trac/downloads/ogLive-focal-5.13.0-27-beta-amd64-r20210706.5b4bf5f.iso"
try:
result = subprocess.run(
["/opt/ogboot/bin/oglivecli", "download", iso_url],
check=True,
capture_output=True,
text=True
)
# Comprobar si oglivecli ha fallado
if result.returncode != 0:
try:
# Parsear la salida como JSON
error_output = json.loads(result.stdout)
if error_output.get("status") == "error":
logger.error(f"oglivecli error: {error_output.get('error')}")
except json.JSONDecodeError:
logger.error("Failed to parse oglivecli error output.")
logger.error("Continuing with the installation despite oglivecli failure.")
else:
logger.info("Successful download")
symlink_target_ogLive = f"{INSTALL_OGBOOT_TARGET}/tftpboot/ogLive"
symlink_target_ogclient = f"{INSTALL_OGBOOT_TARGET}/tftpboot/ogclient"
if os.path.exists(symlink_target_ogLive):
subprocess.run(["chown", "-R", f"tftp:ogboot", f"{INSTALL_OGBOOT_TARGET}/tftpboot"], check=True)
os.lchown(symlink_target_ogLive, pwd.getpwnam("tftp").pw_uid, pwd.getpwnam("ogboot").pw_gid)
os.lchown(symlink_target_ogclient, pwd.getpwnam("tftp").pw_uid, pwd.getpwnam("ogboot").pw_gid)
logger.info(f"Changing properties for {symlink_target_ogLive} and {symlink_target_ogclient}")
logger.error(f"{symlink_target_ogLive} link does not exists.")
else:
logger.error(f"{symlink_target_ogLive} link does not exist.")
except subprocess.CalledProcessError as e:
logger.error(f"Subprocess failed: {e}")
logger.error("Continuing with the installation...")
def servicesCompilation():
global WORKDIR
hayErrores = 0
process = subprocess.run(["make"], cwd=f"{WORKDIR}/ogboot/sources/clients/ogAdmClient")
shutil.copy2(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():
global WORKDIR, INSTALL_TARGET
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():
global WORKDIR, INSTALL_TARGET
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=0o775, 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 get_first_network_interface_with_traffic():
with open('/proc/net/dev') as f:
for line in f:
if ':' in line:
parts = line.split(':')
if len(parts) > 1:
interface = parts[0].strip()
if interface != "lo":
traffic_data = parts[1].strip().split()
received_bytes = int(traffic_data[0])
transmitted_bytes = int(traffic_data[8])
if received_bytes > 0 or transmitted_bytes > 0:
return interface
def openGnsysConfigure():
global DEFAULTDEV, INSTALL_TARGET, OGCORE_IP, WORKDIR
i = 0
dev = ""
CONSOLEURL = ""
CONSOLEURL = f"https://{OGCORE_IP}/opengnsys"
with open(f"{WORKDIR}/ogboot/etc/ogAdmClient.cfg") as file:
content = file.read()
content = content.replace("SERVERIP", OGCORE_IP)
content = content.replace("OPENGNSYSURL", CONSOLEURL)
with open(f"{INSTALL_TARGET}/client/etc/ogAdmClient-{DEFAULTDEV}.cfg", "w") as outfile:
outfile.write(content)
if os.path.islink(f"{INSTALL_TARGET}/client/etc/ogAdmClient.cfg"):
logger.warning(f"Link {INSTALL_TARGET}/client/etc/ogAdmClient.cfg exists.")
else:
try:
os.symlink(f"{INSTALL_TARGET}/client/etc/ogAdmClient-{DEFAULTDEV}.cfg", f"{INSTALL_TARGET}/client/etc/ogAdmClient.cfg")
logger.info (f"Symbolic link created: {INSTALL_TARGET}/client/etc/ogAdmClient-{DEFAULTDEV}.cfg -> {INSTALL_TARGET}/client/etc/ogAdmClient.cfg")
except OSError as e:
logger.error(f"Error creating symbolic link: {e}")
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():
global IPXE_DIR, INSTALL_OGBOOT_TARGET
repo_url = "https://github.com/ipxe/ipxe.git"
clone_dir = "/tmp/ogboot_ipxe"
# Clonar el repositorio desde Gitea
if os.path.exists(clone_dir):
logger.info(f"Eliminando el directorio {clone_dir} existente")
subprocess.call(["rm", "-rf", clone_dir])
logger.info(f"Clonando el repositorio {repo_url}")
if subprocess.call(["git", "-c", "http.sslVerify=false", "clone", repo_url, clone_dir]) == 0:
logger.info("Repositorio clonado correctamente.")
else:
logger.error(f"ERROR\tNo se pudo clonar el repositorio {repo_url}.")
exit(1)
# Ejecutar el comando make en el directorio src
logger.info(f"Ejecutando make en {IPXE_DIR}/src")
os.chdir(f"{IPXE_DIR}/src")
if subprocess.call(["make", "-s", "-j", "4"]) == 0:
logger.info(f"Directorio {IPXE_DIR}/src correctamente compilado.")
else:
logger.error(f"ERROR\tNo se pudo compilar el directorio {IPXE_DIR}/src.")
exit(1)
if not os.path.exists("/opt/opengnsys"):
os.symlink("/opt/ogboot/", "/opt/opengnsys")
logger.info("Symbolic link created successfully.")
if subprocess.call(["make", "-s", "bin/undionly.kpxe", f"EMBED={INSTALL_OGBOOT_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("Copiando undionly.kpxe con usuario ogboot:")
subprocess.call(["cp", "bin/undionly.kpxe", f"{INSTALL_OGBOOT_TARGET}/tftpboot"])
subprocess.call(["chown", "ogboot:ogboot", f"{INSTALL_OGBOOT_TARGET}/tftpboot"])
logger.info("Generando make de ipxe.efi:")
if subprocess.call(["make", "-s", "bin-x86_64-efi/ipxe.efi", f"EMBED={INSTALL_OGBOOT_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_OGBOOT_TARGET}/tftpboot"])
subprocess.call(["sudo", "chown", "-R", "tftp:ogboot", f"{INSTALL_OGBOOT_TARGET}/tftpboot/"])
subprocess.run(["sudo", "chmod", "-R", "775", f"{INSTALL_OGBOOT_TARGET}/tftpboot/"])
os.makedirs(f"{INSTALL_OGBOOT_TARGET}/tftpboot/ipxe_scripts/templates", exist_ok=True)
subprocess.call(["sudo", "chown", "-R", "tftp:ogboot", f"{INSTALL_OGBOOT_TARGET}/tftpboot/ipxe_scripts/templates"])
subprocess.call(["sudo", "chmod", "-R", "775", f"{INSTALL_OGBOOT_TARGET}/tftpboot/ipxe_scripts/templates"])
subprocess.call(["sudo", "cp", f"{WORKDIR}/ogboot/tftpboot/ipxe_scripts/templates/pxe_default", f"{INSTALL_OGBOOT_TARGET}/tftpboot/ipxe_scripts/templates"])
def get_ip_address(interface):
try:
result = subprocess.check_output(["ip", "addr", "show", interface]).decode()
for line in result.split('\n'):
if "inet " in line:
ip_address = line.strip().split()[1].split('/')[0]
return ip_address
except subprocess.CalledProcessError as e:
logger.error(f"Error get address IP: {e}")
return None
def generate_ipxe_script():
global DEFAULTDEV, INSTALL_OGBOOT_TARGET, WORKDIR
#ip_address_server = subprocess.check_output(["ifconfig", DEFAULTDEV]).decode().split("\n")[1].split()[1]
ip_address_server = get_ip_address(DEFAULTDEV)
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=0o775, 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 user_exists(user):
try:
result = subprocess.run(["sudo", "pdbedit", "-L", "-u", user], capture_output=True, text=True)
return user in result.stdout
except subprocess.CalledProcessError as e:
logger.error(f"Error checking if user exists: {e}")
return False
def smbConfigure():
global OPENGNSYS_CLIENT_PASSWD, OPENGNSYS_CLIENT_USER, PYTHON_VERSION
SAMBACFGDIR = "/etc/samba"
#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 = "smbd"
logger.info(f"Enabling {service} service.")
subprocess.run(["systemctl", "enable", f"{service}.service"])
logger.info(f"Restarting {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
# Ejecutar el comando smbpasswd para agregar el usuario de Samba
try:
if user_exists(OPENGNSYS_CLIENT_USER):
logger.info(f"{OPENGNSYS_CLIENT_USER} user exists. Changing password...")
subprocess.run(
["sudo", "smbpasswd", OPENGNSYS_CLIENT_USER],
input=f"{OPENGNSYS_CLIENT_PASSWD}\n{OPENGNSYS_CLIENT_PASSWD}\n",
text=True,
check=True
)
else:
logger.info(f"{OPENGNSYS_CLIENT_USER} user does not exist. Registering user...")
subprocess.run(
["sudo", "smbpasswd", "-a", OPENGNSYS_CLIENT_USER],
input=f"{OPENGNSYS_CLIENT_PASSWD}\n{OPENGNSYS_CLIENT_PASSWD}\n",
text=True,
check=True
)
logger.info("Add/Modify user: Operation completed successfully.")
except subprocess.CalledProcessError as e:
logger.error(f"Error adding/modifying user: {e}")
return 0
'''
try:
process_add = subprocess.run(
["sudo", "smbpasswd", "-a", OPENGNSYS_CLIENT_USER],
input=f"{OPENGNSYS_CLIENT_PASSWD}\n{OPENGNSYS_CLIENT_PASSWD}\n",
text=True,
check=True
)
logger.info(f"The password for the user {OPENGNSYS_CLIENT_USER} has been set correctly.")
except subprocess.CalledProcessError as e:
logger.error(f"Error setting password: {e}")
'''
def setup_nginx():
global DEFAULTDEV, WORKDIR
try:
# Obtener la IP del servidor
#ip_address_server = subprocess.check_output(["ifconfig", DEFAULTDEV]).decode().split("\n")[1].split()[1]
ip_address_server = get_ip_address(DEFAULTDEV)
php_version = get_php_fpm_version()
# 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)
nginx_content = nginx_content.replace("__PHPVERSION__", php_version)
# 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.")
logger.info("Samba service restarted successfully.")
# Modificar el archivo de configuración de nginx para ejecutarse como ogboot
nginx_conf_path = "/etc/nginx/nginx.conf"
with open(nginx_conf_path, 'r') as nginx_conf_file:
nginx_conf_content = nginx_conf_file.read()
nginx_conf_content = nginx_conf_content.replace("user www-data;", "user ogboot;")
with open(nginx_conf_path, 'w') as nginx_conf_file:
nginx_conf_file.write(nginx_conf_content)
logger.info("Nginx configuration file modified to run as ogboot.")
# Reiniciar el servicio de samba
subprocess.run(["sudo", "systemctl", "restart", "nginx.service"])
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 get_php_fpm_version():
try:
# Obtener la versión de PHP
php_version_output = subprocess.check_output(["php", "-v"]).decode()
# Extraer la versión principal y secundaria (por ejemplo, "7.4")
match = re.search(r"PHP (\d+\.\d+)", php_version_output)
if match:
php_version = match.group(1)
return php_version
else:
raise RuntimeError("No se pudo determinar la versión de PHP.")
except subprocess.CalledProcessError as e:
logger.error(f"Error al obtener la versión de PHP: {e}")
exit(1)
def modify_php_fpm_config():
php_version = get_php_fpm_version() # Establecemos la versión de PHP a 8.2
php_fpm_conf_path = f"/etc/php/{php_version}/fpm/pool.d/www.conf"
new_fpm_conf_path = f"/etc/php/{php_version}/fpm/pool.d/ogboot.conf"
socket_path = f"/run/php/php{php_version}-fpm-ogboot.sock"
try:
# Copiar www.conf a ogboot.conf
subprocess.run(["sudo", "cp", php_fpm_conf_path, new_fpm_conf_path], check=True)
logger.info(f"Archivo {php_fpm_conf_path} copiado a {new_fpm_conf_path}")
# Leer el archivo copiado ogboot.conf
with open(new_fpm_conf_path, 'r') as file:
config_lines = file.readlines()
# Modificar las líneas necesarias
with open(new_fpm_conf_path, 'w') as file:
for line in config_lines:
if line.startswith('[www]'):
file.write('[ogboot]\n') # Cambiar el nombre del pool
elif line.startswith('user ='):
file.write('user = ogboot\n')
elif line.startswith('group ='):
file.write('group = ogboot\n')
elif line.startswith('listen ='):
file.write(f'listen = {socket_path}\n') # Cambiar el nombre del socket
elif line.startswith('listen.owner ='):
file.write('listen.owner = ogboot\n')
elif line.startswith('listen.group ='):
file.write('listen.group = ogboot\n')
else:
file.write(line)
logger.info(f"Archivo {new_fpm_conf_path} modificado correctamente.")
# Reiniciar el servicio PHP-FPM
subprocess.run(["sudo", "systemctl", "restart", f"php{php_version}-fpm"], check=True)
logger.info("Servicio PHP-FPM reiniciado correctamente.")
# Verificar que el socket se ha creado
if os.path.exists(socket_path):
logger.info(f"Socket {socket_path} creado correctamente.")
else:
logger.error(f"El socket {socket_path} no se ha creado.")
exit(1)
except Exception as e:
logger.error(f"Ocurrió un error: {e}")
exit(1)
###############################################################################
###:::::::::::::::::::::::::::::::: MAIN :::::::::::::::::::::::::::::::::::###
###############################################################################
logger.info(f":::::::::::::::::::::::: Starting ogBoot installation ::::::::::::::::::::::::")
logger.info("environment variables")
logger.info(f"OGCORE_IP:{OGCORE_IP}")
logger.info(f"INSTALL_TARGET:{INSTALL_TARGET}")
logger.info(f"INSTALL_OGBOOT_TARGET:{INSTALL_OGBOOT_TARGET}")
logger.info(f"INSTALL_OPENGNSYS_TARGET:{INSTALL_OPENGNSYS_TARGET}")
logger.info(f"GIT_REPO:{GIT_REPO}")
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)
try:
logger.info("Verifying Python distribution and version.")
check_distribution()
check_python_version()
except Exception as e:
logger.error(f"Error verifying Python distribution or version: {e}")
exit(1)
try:
logger.info("Checking the operating system.")
autoConfigure()
except Exception as e:
logger.error(f"Error checking the operating system: {e}")
exit(1)
try:
logger.info("Installing necessary packages.")
Missing = get_missing_packages()
install_packages(Missing)
except Exception as e:
logger.error(f"Error installing necessary packages: {e}")
exit(1)
try:
logger.info("Obtaining the default network configuration.")
DEFAULTDEV = get_first_network_interface_with_traffic()
logger.info(f"Network interface default:[{DEFAULTDEV}]")
except Exception as e:
logger.error(f"Error obtaining network configuration: {e}")
exit(1)
try:
logger.info("Configuring package repositories.")
if REMOTE == 1:
downloadCode(GIT_REPO)
else:
if os.path.exists(f"{WORKDIR}/ogboot"):
os.remove(f"{WORKDIR}/ogboot")
logger.info(f"Existing symbolic link to ogBoot directory removed.")
if not os.path.exists(WORKDIR):
os.makedirs(WORKDIR, mode=0o775, exist_ok=True)
logger.info(f"{WORKDIR}/ogboot directory created")
logger.info(f"Creating a symbolic link to the code directory")
os.symlink(os.path.dirname(PROGRAM_DIR), f"{WORKDIR}/ogboot")
except Exception as e:
logger.error(f"Error configuring package repositories: {e}")
exit(1)
try:
add_sudoers_permissions()
except Exception as e:
logger.error(f"Error adding sudoers permissions: {e}")
exit(1)
try:
logger.info("Creating ogBoot project.")
og_core_create_user("ogboot")
og_core_create_user(OPENGNSYS_CLIENT_USER)
except Exception as e:
logger.error(f"Error creating ogBoot project or users: {e}")
exit(1)
try:
logger.info("Creating directories.")
og_boot_create_dirs()
except Exception as e:
logger.error(f"Error creating directories: {e}")
exit(1)
try:
logger.info("Copying installation files.")
og_boot_copy_files()
except Exception as e:
logger.error(f"Error copying installation files: {e}")
exit(1)
try:
logger.info("Installing Symfony.")
og_boot_symfony_install()
except Exception as e:
logger.error(f"Error installing Symfony: {e}")
exit(1)
try:
logger.info("Installing Composer.")
og_boot_composer_install()
except Exception as e:
logger.error(f"Error installing Composer: {e}")
exit(1)
try:
logger.info("Obteniendo UID y GID del usuario 'ogboot'.")
uid, gid = get_ogboot_uid_gid()
logger.info("Añadiendo entradas al archivo /etc/fstab.")
add_fstab_entries(uid, gid)
logger.info("Añadiendo el usuario 'ogboot' al grupo 'disk'.")
add_user_to_disk_group()
except Exception as e:
logger.error(f"Error durante la configuración: {e}")
exit(1)
try:
logger.info("Configuring tftpd-hpa service.")
tftpConfigure()
except Exception as e:
logger.error(f"Error configuring tftpd-hpa service: {e}")
exit(1)
try:
logger.info("Configuring IPXE services")
generate_ipxe_script()
except Exception as e:
logger.error(f"Error configuring IPXE services: {e}")
exit(1)
try:
logger.info("Setting up NFS system")
mount_NFS()
except Exception as e:
logger.error(f"Error setting up NFS system: {e}")
exit(1)
try:
logger.info("Compiling OpenGnsys services source code")
servicesCompilation()
except Exception as e:
logger.error(f"Error compiling OpenGnsys services: {e}")
exit(1)
try:
logger.info("Copy folder Interface between administration and cloning engine")
copyInterfaceAdm()
except Exception as e:
logger.error(f"Error copying Administration Interface: {e}")
exit(1)
try:
logger.info("Create the structure of the accesses to the server from the client (shared)")
copyClientFiles()
except Exception as e:
logger.error(f"Error creating client structure: {e}")
exit(1)
try:
logger.info("Setup nginx")
setup_nginx()
except Exception as e:
logger.error(f"Error setting up nginx: {e}")
exit(1)
try:
logger.info("Configure php fpm")
modify_php_fpm_config()
except Exception as e:
logger.error(f"Error configuring php fpm: {e}")
exit(1)
try:
logger.info("Configuring ogCore")
openGnsysConfigure()
except Exception as e:
logger.error(f"Error configuring ogCore: {e}")
exit(1)
try:
logger.info("Configuring Samba")
smbConfigure()
except Exception as e:
logger.error(f"Error configuring Samba: {e}")
exit(1)
logger.info(f"ogBoot installation finished.")
logging.shutdown()
console_handler.close()