#!/usr/bin/env python3

#################################################################################
#####  ogBoot installer script                                           ########
#####  Autor: Antonio Emmanuel Guerrero Silva <aguerrero@qindel.com>     ########
#################################################################################

import platform, os, sys, subprocess, datetime, shutil, pwd, glob, zipfile, urllib.request, logging, distro

global PACKAGES, UBUNTU_OS_VERSION, PYTHON_VERSION, INSTALL_OGBOOT_TARGET, PROGRAM_DIR, OPENGNSYS_SERVER

# Reload variables from bashrc
subprocess.run(['source', '~/.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 = []
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(packages):
	faltantes = []
	for package in packages:
		if not is_installed(package):
			faltantes.append(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...")
	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", "--force-yes", "-y", package], check=True)
			logger.info(f"{package} installed correctly.")
			log.write(package + "\n")
	logger.info("All missing packages have been installed.")

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}")
	print(f'{OSDISTRIB}...............{OSDISTRIB}')
	print(f'{OSVERSION}...............{OSVERSION}')
	# Configuración según la distribución OSDISTRIB
	if OSDISTRIB == 'Ubuntu':
		# Código para Ubuntu
		if OSVERSION == '18.04':
			# Código para Ubuntu 18.04
			PACKAGES = ["apache2", "arp-scan", "automake", "build-essential", "btrfs-progs", "composer", "curl", "ctorrent", "debootstrap", "g++-multilib", "gawk", "gettext", "graphviz", "grub-efi-amd64-signed", "isc-dhcp-server", "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", "php-pdo", "php-pear", "php-xml", "php-zip", "procps", "coreutils", "rsync", "samba", "schroot", "shim-signed", "squashfs-tools", "subversion", "tftp-hpa", "tftpd-hpa", "udpcast", "unzip", "wakeonlan", "wget", "xinetd", "jq", "moreutils", "net-tools"]
			pass
		elif OSVERSION == '20.04':
			# Código para Ubuntu 20.04
			dfdf = ["htop"]
			pass
		elif OSVERSION == '22.04':
			# Código para Ubuntu 22.04
			print(f'ubu 22.04')
			PACKAGES = ["apache2", "arp-scan", "automake", "build-essential", "btrfs-progs", "composer", "curl", "ctorrent", "debootstrap", "g++-multilib", "gawk", "gettext", "graphviz", "grub-efi-amd64-signed", "kea-dhcp-server", "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", "php-pdo", "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"]
			pass
		else:
			logger.error("Ubuntu version not supported by ogBoot.")
			exit(1)
	elif OSDISTRIB == 'debian':
		# Código para Debian
		pass
	elif OSDISTRIB == 'linuxmint':
		# Código para Linux Mint
		pass
	elif OSDISTRIB == 'fedora':
		# Código para Fedora
		pass
	elif OSDISTRIB == 'centos':
		# Código para CentOS
		pass
	else:
		logger.error("Distribution not supported by ogBoot.")
		exit(1)

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:
			os.makedirs(INSTALL_OGBOOT_TARGET, mode=0o750, exist_ok=True)
			os.makedirs(os.path.join(INSTALL_OGBOOT_TARGET, "client"), mode=0o750, exist_ok=True)
			subprocess.run(["sudo", "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(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.")
	#Descargar oglive
	logger.info("Downloading oglive...")
	#Temporalmente se copia desde ~/oglive
	subprocess.run(["cp", "-r", f"{PROGRAM_DIR}/../../oglive/ogLive-5.11.0-r20210413", f"{TFTPCFGDIR}/"])
	subprocess.run(["cp", "-r", f"{PROGRAM_DIR}/../../tftpboot/ipxe_scripts", 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.link(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)
	subprocess.call(["ls", "/mnt/"])
	subprocess.call(["sudo", "cp", "-r", "/mnt/srv/artefactos/ipxe/", "/tmp"])
	os.chdir(f"{IPXE_DIR}/src")
	if subprocess.call(["sudo", "make", "-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 subprocess.call(["sudo", "make", "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)
	subprocess.call(["sudo", "cp", "bin/undionly.kpxe", f"{INSTALL_OPENGNSYS_TARGET}/tftpboot"])
	if subprocess.call(["sudo", "make", "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

###############################################################################
###:::::::::::::::::::::::::::::::: MAIN :::::::::::::::::::::::::::::::::::###
###############################################################################

logger.info(f"Starting installation of ogBoot.")
if os.geteuid() != 0:
	logger.error("This program must be run with root privileges..")
	exit(1)
print(f':INSTALL_OGBOOT_TARGET:::${INSTALL_OGBOOT_TARGET}')
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 (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.info(f"Does not exist {WORKDIR}/ogboot")
		if not os.path.exists(WORKDIR):
			os.makedirs(WORKDIR, mode=0o750, exist_ok=True)
		logger.error(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 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("Configuring IPXE services")
generate_ipxe_script()

logger.info("Configuring ogCore")
openGnsysConfigure()

logger.info("Setting up NFS system")
mount_NFS()

logger.info("Configuring Samba")
smbConfigure()

logger.info(f"ogBoot installation finished.")

logging.shutdown()
console_handler.close()
