mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-04-05 20:38:30 +00:00
CLI consolidation: - Delete 13 legacy CLI scripts from includes/cli/ (ondemand, proxy, queue, record, scanner, signals, startup, thumbnail, tools, update, watchdog, plex_item, watch_item) - Convert status and tools entry points to thin proxies that delegate to console.php - Update all shell_exec() calls across admin controllers, views, and API layer to use console.php command syntax instead of direct CLI file paths - Update src/service to launch daemons via console.php (signals, watchdog, queue, cache_handler, startup) - Update Python src/update script to call console.php update instead of includes/cli/update.php - Update test_installer to use console.php startup Streaming deduplication: - Extract StreamAuthMiddleware — common response headers and token decryption shared by live/vod/timeshift - Extract ShutdownHandler — unified shutdown logic replacing 3 duplicate function shutdown() blocks - Refactor live.php, vod.php, timeshift.php to use new middleware classes - Add streaming micro-router to www/stream/index.php as fallback entry point Routing fixes: - Fix admin index.php redirect to use relative path (supports access code prefixes) - Add access code root redirect in public/index.php to prevent broken CSS/JS asset resolution - Fix init.php for CLI compatibility: guard $_SERVER access, define PHP_ERRORS safely Migrations: - 001_update_crontab_filenames.sql — strip .php suffix from crontab filenames - Fix cache.php view query to match new filename format (cache_engine instead of cache_engine.php)
1850 lines
85 KiB
Python
1850 lines
85 KiB
Python
#!/usr/bin/python3
|
||
import subprocess, os, random, sys, shutil, socket, time, io, json, urllib.request, tarfile, zipfile
|
||
|
||
if sys.version_info.major != 3:
|
||
print("Please run with python3.")
|
||
sys.exit(1)
|
||
|
||
rPath = os.path.dirname(os.path.realpath(__file__))
|
||
|
||
# Distribuciones soportadas
|
||
SUPPORTED_DISTROS = {
|
||
'ubuntu': ['20.04', '22.04', '24.04'],
|
||
'debian': ['11', '12', '13'],
|
||
'rocky': ['8', '9'],
|
||
'almalinux': ['8', '9'],
|
||
'centos': ['7', '8'],
|
||
'rhel': ['8', '9']
|
||
}
|
||
|
||
PACKAGES = {
|
||
"debian": [
|
||
"iproute2", "net-tools", "dirmngr", "gpg-agent",
|
||
"software-properties-common", "libmaxminddb0", "libmaxminddb-dev",
|
||
"mmdb-bin", "libcurl4", "libgeoip-dev", "libxslt1-dev", "libonig-dev",
|
||
"e2fsprogs", "wget", "mariadb-server", "mariadb-client", "sysstat",
|
||
"alsa-utils", "v4l-utils", "certbot", "iptables-persistent",
|
||
"libjpeg-dev", "libpng-dev", "libharfbuzz-dev", "libfribidi-dev",
|
||
"libogg0", "xz-utils", "zip", "unzip", "libssh2-1",
|
||
"php-ssh2", "cron", "git", "curl"
|
||
],
|
||
|
||
"debian13": [
|
||
"iproute2", "net-tools", "dirmngr", "gpg-agent",
|
||
"software-properties-common", "libmaxminddb0", "libmaxminddb-dev",
|
||
"mmdb-bin", "libcurl4", "wget", "unzip", "zip",
|
||
"xz-utils", "cron", "git", "sysstat", "perl", "gawk", "socat",
|
||
"libxml2-dev", "libxslt1-dev", "libonig5", "libonig-dev",
|
||
"zlib1g-dev", "libssl-dev", "pkg-config", "autoconf", "automake",
|
||
"alsa-utils", "v4l-utils", "e2fsprogs",
|
||
"certbot", "iptables-persistent",
|
||
"libssh2-1", "libssh2-1-dev",
|
||
"mariadb-server", "mariadb-client", "mariadb-common",
|
||
"libjpeg-dev", "libpng-dev", "libharfbuzz-dev", "libfribidi-dev",
|
||
"libgeoip1", "geoip-bin"
|
||
],
|
||
"ubuntu20": [
|
||
"iproute2", "net-tools", "dirmngr", "gpg-agent",
|
||
"software-properties-common", "wget", "curl", "unzip", "zip",
|
||
"xz-utils", "cron", "git", "sysstat", "ca-certificates",
|
||
"libmaxminddb0", "mmdb-bin", "libcurl4-openssl-dev", "libxml2-dev",
|
||
"libxslt1-dev", "libonig5", "libonig-dev", "libjpeg-dev",
|
||
"libpng-dev", "zlib1g-dev", "alsa-utils", "v4l-utils", "e2fsprogs",
|
||
"iptables-persistent", "certbot", "python3-certbot",
|
||
"libssh2-1", "libssh2-1-dev", "mariadb-server", "mariadb-client", "mariadb-common"
|
||
],
|
||
"ubuntu22": [
|
||
"iproute2", "net-tools", "dirmngr", "gpg-agent",
|
||
"software-properties-common", "libmaxminddb0", "libmaxminddb-dev",
|
||
"mmdb-bin", "libcurl4", "libgeoip-dev", "libxslt1-dev", "libonig-dev",
|
||
"e2fsprogs", "wget", "curl", "unzip", "zip",
|
||
"xz-utils", "cron", "git", "sysstat", "ca-certificates",
|
||
"libxml2-dev", "libonig5", "zlib1g-dev",
|
||
"mariadb-server", "mariadb-client", "mariadb-common",
|
||
"alsa-utils", "v4l-utils",
|
||
"certbot", "python3-certbot", "iptables-persistent",
|
||
"libjpeg-dev", "libpng-dev", "libharfbuzz-dev", "libfribidi-dev",
|
||
"libogg0", "libssh2-1", "libssh2-1-dev", "php-ssh2"
|
||
],
|
||
"ubuntu24": [
|
||
"iproute2", "net-tools", "dirmngr", "gpg-agent",
|
||
"software-properties-common", "libmaxminddb0", "libmaxminddb-dev",
|
||
"mmdb-bin", "libcurl4t64", "wget", "unzip", "zip",
|
||
"xz-utils", "cron", "git", "sysstat", "perl", "gawk", "socat",
|
||
"libxml2-dev", "libxslt1-dev", "libonig5", "libonig-dev",
|
||
"zlib1g-dev", "libssl-dev", "pkg-config", "autoconf", "automake",
|
||
"alsa-utils", "v4l-utils", "e2fsprogs",
|
||
"certbot", "python3-certbot", "ufw",
|
||
"libssh2-1t64", "libssh2-1-dev",
|
||
"mariadb-server", "mariadb-client", "mariadb-common",
|
||
"libjpeg-dev", "libpng16-dev", "libharfbuzz-dev", "libfribidi-dev",
|
||
"libgeoip1t64", "geoip-bin"
|
||
],
|
||
"debian11": [
|
||
"iproute2", "net-tools", "dirmngr", "gpg-agent",
|
||
"software-properties-common", "libmaxminddb0", "libmaxminddb-dev",
|
||
"mmdb-bin", "libcurl4", "libgeoip-dev", "libxslt1-dev", "libonig-dev",
|
||
"e2fsprogs", "wget", "curl", "unzip", "zip",
|
||
"xz-utils", "cron", "git", "sysstat",
|
||
"mariadb-server", "mariadb-client", "mariadb-common",
|
||
"alsa-utils", "v4l-utils",
|
||
"certbot", "iptables-persistent",
|
||
"libjpeg-dev", "libpng-dev", "libharfbuzz-dev", "libfribidi-dev",
|
||
"libogg0", "libssh2-1", "libssh2-1-dev"
|
||
],
|
||
"redhat": [
|
||
"epel-release", "wget", "mariadb-server", "mariadb", "sysstat",
|
||
"alsa-utils", "v4l-utils", "libmaxminddb", "libmaxminddb-devel",
|
||
"libcurl-devel", "geoip-devel", "libxslt-devel", "oniguruma-devel",
|
||
"e2fsprogs", "libjpeg-turbo-devel", "libpng-devel", "harfbuzz-devel",
|
||
"fribidi-devel", "libogg", "xz", "zip", "unzip", "libssh2-devel",
|
||
"cronie", "certbot", "iptables-services", "GeoIP-update", "git", "curl"
|
||
]
|
||
}
|
||
|
||
rRemove = ["mysql-server"]
|
||
rMySQLCnfTemplate = """# XC_VM\n[client]\nport = 3306\n\n[mysqld_safe]\nnice = 0\n\n[mysqld]\nuser = mysql\nport = 3306\nbasedir = /usr\ndatadir = /var/lib/mysql\ntmpdir = /tmp\nlc-messages-dir = /usr/share/mysql\nskip-external-locking\nskip-name-resolve\nbind-address = *\n\n# MyISAM\nkey_buffer_size = {{KEY_BUFFER}}M\nmyisam_sort_buffer_size = 4M\nmyisam-recover-options = BACKUP\nmax_length_for_sort_data = 4096\n\n# Connections\nmax_connections = {{MAX_CONNECTIONS}}\nback_log = {{BACK_LOG}}\nmax_connect_errors = 1000\n\n# Packet and cache\nmax_allowed_packet = 16M\nopen_files_limit = 2048\ninnodb_open_files = 1024\ntable_open_cache = 1024\ntable_definition_cache = 1024\n\n# Temp tables\ntmp_table_size = {{TMP_TABLE_SIZE}}M\nmax_heap_table_size = {{TMP_TABLE_SIZE}}M\n\n# InnoDB\ninnodb_buffer_pool_size = {{BUFFER_POOL_SIZE}}\ninnodb_buffer_pool_instances = {{BUFFER_POOL_INSTANCES}}\ninnodb_read_io_threads = 4\ninnodb_write_io_threads = 4\ninnodb_flush_log_at_trx_commit = 1\ninnodb_flush_method = O_DIRECT\ninnodb_file_per_table = 1\ninnodb_io_capacity = 1000\ninnodb_table_locks = 1\ninnodb_lock_wait_timeout = 30\n\n# Logging\nexpire_logs_days = 7\nmax_binlog_size = 64M\n\n# Query cache – disabled\nquery_cache_limit = 0\nquery_cache_size = 0\nquery_cache_type = 0\n\nperformance_schema = 0\n\nsql_mode = "NO_ENGINE_SUBSTITUTION"\n\n[mariadb]\nthread_cache_size = {{THREAD_CACHE}}\nthread_handling = pool-of-threads\nthread_pool_size = 4\nthread_pool_idle_timeout = 20\nthread_pool_max_threads = 256\n\n[mysqldump]\nquick\nquote-names\nmax_allowed_packet = 16M\n\n[mysql]\n\n[isamchk]\nkey_buffer_size = 8M"""
|
||
rConfig = '; XC_VM Configuration\n; -----------------\n; To change your username or password, modify BOTH\n; below and XC_VM will read and re-encrypt them.\n\n[XC_VM]\nhostname = "127.0.0.1"\ndatabase = "xc_vm"\nport = 3306\nserver_id = 1\n\n[Encrypted]\nusername = "%s"\npassword = "%s"'
|
||
rRedisConfig = 'bind *\nprotected-mode yes\nport 6379\ntcp-backlog 511\ntimeout 0\ntcp-keepalive 300\ndaemonize yes\nsupervised no\npidfile /home/xc_vm/bin/redis/redis-server.pid\nloglevel warning\nlogfile /home/xc_vm/bin/redis/redis-server.log\ndatabases 1\nalways-show-logo yes\nstop-writes-on-bgsave-error no\nrdbcompression no\nrdbchecksum no\ndbfilename dump.rdb\ndir /home/xc_vm/bin/redis/\nslave-serve-stale-data yes\nslave-read-only yes\nrepl-diskless-sync no\nrepl-diskless-sync-delay 5\nrepl-disable-tcp-nodelay no\nslave-priority 100\nrequirepass #PASSWORD#\nmaxclients 655350\nlazyfree-lazy-eviction no\nlazyfree-lazy-expire no\nlazyfree-lazy-server-delay no\nslave-lazy-flush no\nappendonly no\nappendfilename "appendonly.aof"\nappendfsync everysec\nno-appendfsync-on-rewrite no\nauto-aof-rewrite-percentage 100\nauto-aof-rewrite-min-size 64mb\naof-load-truncated yes\naof-use-rdb-preamble no\nlua-time-limit 5000\nslowlog-log-slower-than 10000\nslowlog-max-len 128\nlatency-monitor-threshold 0\nnotify-keyspace-events ""\nhash-max-ziplist-entries 512\nhash-max-ziplist-value 64\nlist-max-ziplist-size -2\nlist-compress-depth 0\nset-max-intset-entries 512\nzset-max-ziplist-entries 128\nzset-max-ziplist-value 64\nhll-sparse-max-bytes 3000\nactiverehashing yes\nclient-output-buffer-limit normal 0 0 0\nclient-output-buffer-limit slave 256mb 64mb 60\nclient-output-buffer-limit pubsub 32mb 8mb 60\nhz 10\naof-rewrite-incremental-fsync yes\nsave 60 1000\nserver-threads 4\nserver-thread-affinity true'
|
||
rSysCtl = "# XC_VM\n\nnet.ipv4.tcp_congestion_control = bbr\nnet.core.default_qdisc = fq\nnet.ipv4.tcp_rmem = 8192 87380 134217728\nnet.ipv4.udp_rmem_min = 16384\nnet.core.rmem_default = 262144\nnet.core.rmem_max = 268435456\nnet.ipv4.tcp_wmem = 8192 65536 134217728\nnet.ipv4.udp_wmem_min = 16384\nnet.core.wmem_default = 262144\nnet.core.wmem_max = 268435456\nnet.core.somaxconn = 1000000\nnet.core.netdev_max_backlog = 250000\nnet.core.optmem_max = 65535\nnet.ipv4.tcp_max_tw_buckets = 1440000\nnet.ipv4.tcp_max_orphans = 16384\nnet.ipv4.ip_local_port_range = 2000 65000\nnet.ipv4.tcp_no_metrics_save = 1\nnet.ipv4.tcp_slow_start_after_idle = 0\nnet.ipv4.tcp_fin_timeout = 15\nnet.ipv4.tcp_keepalive_time = 300\nnet.ipv4.tcp_keepalive_probes = 5\nnet.ipv4.tcp_keepalive_intvl = 15\nfs.file-max=20970800\nfs.nr_open=20970800\nfs.aio-max-nr=20970800\nnet.ipv4.tcp_timestamps = 1\nnet.ipv4.tcp_window_scaling = 1\nnet.ipv4.tcp_mtu_probing = 1\nnet.ipv4.route.flush = 1\nnet.ipv6.route.flush = 1"
|
||
rSystemd = "[Unit]\nSourcePath=/home/xc_vm/service\nDescription=XC_VM Service\nAfter=network.target\nStartLimitIntervalSec=0\n\n[Service]\nType=simple\nUser=root\nRestart=always\nRestartSec=1\nLimitNOFILE=655350\nExecStart=/bin/sh /home/xc_vm/service start\nExecRestart=/bin/sh /home/xc_vm/service restart\nExecStop=/bin/sh /home/xc_vm/service stop\n\n[Install]\nWantedBy=multi-user.target"
|
||
rChoice = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ"
|
||
rConfigPath = "/home/xc_vm/config/config.ini"
|
||
|
||
class col:
|
||
HEADER = "\033[95m"
|
||
OKBLUE = "\033[94m"
|
||
OKGREEN = "\033[92m"
|
||
WARNING = "\033[93m"
|
||
FAIL = "\033[91m"
|
||
ENDC = "\033[0m"
|
||
BOLD = "\033[1m"
|
||
UNDERLINE = "\033[4m"
|
||
|
||
# NUEVO: Función para verificar si se ejecuta como root
|
||
def check_root():
|
||
"""Check if script is running as root"""
|
||
if os.geteuid() != 0:
|
||
printc("This script must be run as root.", col.FAIL)
|
||
printc("Please use: su -c 'python3 your_script_name.py'", col.OKBLUE)
|
||
sys.exit(1)
|
||
|
||
# NUEVO: Función para instalar prerequisitos
|
||
def install_prerequisites(dist_info):
|
||
"""Install prerequisites like sudo and curl if they are missing"""
|
||
if dist_info['family'] == 'debian':
|
||
printc("Checking for prerequisites (sudo, curl)...", col.OKBLUE)
|
||
|
||
# Check if sudo exists
|
||
ret, _, _ = run_command("which sudo", capture_output=True)
|
||
sudo_missing = ret != 0
|
||
|
||
# Check if curl exists
|
||
ret, _, _ = run_command("which curl", capture_output=True)
|
||
curl_missing = ret != 0
|
||
|
||
if sudo_missing or curl_missing:
|
||
printc("Installing missing prerequisites...", col.WARNING)
|
||
printc("Updating package lists...", col.OKBLUE)
|
||
run_command("apt-get update -y")
|
||
|
||
packages_to_install = []
|
||
if sudo_missing:
|
||
packages_to_install.append("sudo")
|
||
if curl_missing:
|
||
packages_to_install.append("curl")
|
||
|
||
if packages_to_install:
|
||
packages_str = " ".join(packages_to_install)
|
||
printc(f"Installing {packages_str}...", col.OKBLUE)
|
||
run_command(f"apt-get install -y {packages_str}")
|
||
printc("Prerequisites installed successfully.", col.OKGREEN)
|
||
else:
|
||
printc("Prerequisites (sudo, curl) are already installed.", col.OKGREEN)
|
||
|
||
elif dist_info['family'] == 'redhat':
|
||
printc("Checking for prerequisites (sudo, curl, wget)...", col.OKBLUE)
|
||
|
||
ret, _, _ = run_command("which sudo", capture_output=True)
|
||
sudo_missing = ret != 0
|
||
|
||
ret, _, _ = run_command("which curl", capture_output=True)
|
||
curl_missing = ret != 0
|
||
|
||
ret, _, _ = run_command("which wget", capture_output=True)
|
||
wget_missing = ret != 0
|
||
|
||
if sudo_missing or curl_missing or wget_missing:
|
||
printc("Installing missing prerequisites...", col.WARNING)
|
||
packages_to_install = []
|
||
if sudo_missing:
|
||
packages_to_install.append("sudo")
|
||
if curl_missing:
|
||
packages_to_install.append("curl")
|
||
if wget_missing:
|
||
packages_to_install.append("wget")
|
||
|
||
if packages_to_install:
|
||
packages_str = " ".join(packages_to_install)
|
||
printc(f"Installing {packages_str}...", col.OKBLUE)
|
||
run_command(f"yum install -y {packages_str} || dnf install -y {packages_str}")
|
||
printc("Prerequisites installed successfully.", col.OKGREEN)
|
||
else:
|
||
printc("Prerequisites are already installed.", col.OKGREEN)
|
||
|
||
# Función genérica para instalar binarios específicos de distribución
|
||
def install_distribution_binaries(dist_id, version):
|
||
"""Download and install distribution-specific binaries for XC_VM"""
|
||
printc(f"Installing {dist_id} {version} specific binaries...", col.OKBLUE)
|
||
|
||
# Determinar el nombre del parche basado en la distribución y versión
|
||
if dist_id == 'ubuntu':
|
||
if version.startswith('20'):
|
||
patch_name = "ubuntu20_xc_vm.tar.gz"
|
||
display_name = "Ubuntu 20"
|
||
elif version.startswith('22'):
|
||
patch_name = "ubuntu22_xc_vm.tar.gz"
|
||
display_name = "Ubuntu 22"
|
||
elif version.startswith('24'):
|
||
patch_name = "ubuntu24_xc_vm.tar.gz"
|
||
display_name = "Ubuntu 24"
|
||
else:
|
||
printc(f"Ubuntu version {version} not supported for patches", col.WARNING)
|
||
return False
|
||
elif dist_id == 'debian':
|
||
if version.startswith('11'):
|
||
patch_name = "debian11_xc_vm.tar.gz"
|
||
display_name = "Debian 11"
|
||
elif version.startswith('12'):
|
||
patch_name = "debian12_xc_vm.tar.gz"
|
||
display_name = "Debian 12"
|
||
elif version.startswith('13'):
|
||
patch_name = "debian13_xc_vm.tar.gz"
|
||
display_name = "Debian 13"
|
||
else:
|
||
printc(f"Debian version {version} not supported for patches", col.WARNING)
|
||
return False
|
||
elif dist_id in ['rocky', 'almalinux', 'rhel', 'centos']:
|
||
major = version.split('.')[0]
|
||
if major in ['8', '9']:
|
||
patch_name = f"rhel{major}_xc_vm.tar.gz"
|
||
display_name = f"RHEL/Rocky/Alma {major}"
|
||
else:
|
||
printc(f"{dist_id} version {version} not supported for patches", col.WARNING)
|
||
return False
|
||
else:
|
||
printc(f"Distribution {dist_id} not supported for patches", col.WARNING)
|
||
return False
|
||
|
||
# Configurar rutas
|
||
local_patch_file = os.path.join(rPath, patch_name)
|
||
remote_url = f"http://gamba.dynns.com:5000/apk/xc_vm/{patch_name}"
|
||
temp_tar_file = f"/tmp/{patch_name}"
|
||
temp_extract_dir = f"/tmp/{patch_name.replace('.tar.gz', '_extract')}"
|
||
|
||
try:
|
||
# 1. Verificar archivo local primero, luego descargar si no se encuentra
|
||
if os.path.exists(local_patch_file) and os.path.getsize(local_patch_file) > 0:
|
||
printc(f"Usando archivo local de parche {display_name}: {local_patch_file}", col.OKGREEN)
|
||
temp_tar_file = local_patch_file
|
||
else:
|
||
printc(f"Archivo local no encontrado. Descargando binarios {display_name} desde {remote_url}...", col.OKBLUE)
|
||
urllib.request.urlretrieve(remote_url, temp_tar_file)
|
||
|
||
if not (os.path.exists(temp_tar_file) and os.path.getsize(temp_tar_file) > 0):
|
||
printc(f"No se pudieron obtener los binarios de {display_name}", col.FAIL)
|
||
return False
|
||
|
||
printc(f"Binarios de {display_name} obtenidos exitosamente", col.OKGREEN)
|
||
|
||
# 2. Limpiar directorio de extracción anterior si existe
|
||
if os.path.exists(temp_extract_dir):
|
||
shutil.rmtree(temp_extract_dir)
|
||
|
||
# 3. Extraer a directorio temporal
|
||
printc(f"Extrayendo binarios de {display_name} a ubicación temporal...", col.OKBLUE)
|
||
with tarfile.open(temp_tar_file, 'r:gz') as tar:
|
||
tar.extractall(path=temp_extract_dir)
|
||
|
||
# Detectar automáticamente la estructura del directorio
|
||
distro_dirname = f"{dist_id}{version.split('.')[0]}"
|
||
|
||
# Posibles rutas donde pueden estar los binarios
|
||
possible_paths = [
|
||
os.path.join(temp_extract_dir, distro_dirname, "bin"), # debian11/bin/
|
||
os.path.join(temp_extract_dir, "bin"), # bin/ directamente
|
||
os.path.join(temp_extract_dir, distro_dirname), # debian11/ (si bin está adentro)
|
||
temp_extract_dir # raíz del extracto
|
||
]
|
||
|
||
source_bin_dir = None
|
||
for path in possible_paths:
|
||
if os.path.exists(path):
|
||
# Verificar si contiene archivos binarios o directorios típicos
|
||
contents = os.listdir(path)
|
||
has_binaries = any(item in contents for item in ['php', 'nginx', 'nginx_rtmp', 'bin'])
|
||
|
||
if has_binaries or path.endswith('/bin'):
|
||
source_bin_dir = path
|
||
printc(f"Estructura encontrada: {source_bin_dir}", col.OKGREEN)
|
||
break
|
||
|
||
# Si no se encuentra en las rutas esperadas, buscar recursivamente
|
||
if not source_bin_dir:
|
||
printc("Buscando estructura de directorios recursivamente...", col.OKBLUE)
|
||
for root, dirs, files in os.walk(temp_extract_dir):
|
||
# Buscar directorios que contengan binarios típicos
|
||
if any(item in dirs for item in ['php', 'nginx', 'nginx_rtmp', 'bin']):
|
||
source_bin_dir = root
|
||
printc(f"Estructura encontrada recursivamente: {source_bin_dir}", col.OKGREEN)
|
||
break
|
||
|
||
target_bin_dir = "/home/xc_vm/bin"
|
||
|
||
if not source_bin_dir:
|
||
printc(f"Error: No se pudo encontrar la estructura de binarios en el parche.", col.FAIL)
|
||
printc(f"Contenido de {temp_extract_dir}: {os.listdir(temp_extract_dir)}", col.WARNING)
|
||
# Mostrar estructura completa para debug
|
||
printc("Estructura completa del directorio extraído:", col.WARNING)
|
||
for root, dirs, files in os.walk(temp_extract_dir):
|
||
level = root.replace(temp_extract_dir, '').count(os.sep)
|
||
indent = ' ' * 2 * level
|
||
printc(f"{indent}{os.path.basename(root)}/", col.WARNING)
|
||
subindent = ' ' * 2 * (level + 1)
|
||
for file in files[:10]: # Limitar a 10 archivos para no saturar
|
||
printc(f"{subindent}{file}", col.WARNING)
|
||
if len(files) > 10:
|
||
printc(f"{subindent}... y {len(files) - 10} archivos más", col.WARNING)
|
||
return False
|
||
|
||
# 4. Reemplazar archivos específicos, no todo el directorio
|
||
printc(f"Reemplazando binarios específicos con versiones de {display_name}...", col.OKBLUE)
|
||
|
||
# Recorrer recursivamente los archivos en el directorio fuente
|
||
for root, dirs, files in os.walk(source_bin_dir):
|
||
# Calcular la ruta relativa desde el directorio fuente
|
||
rel_path = os.path.relpath(root, source_bin_dir)
|
||
|
||
# Crear directorios correspondientes en el destino si no existen
|
||
if rel_path != ".":
|
||
target_dir = os.path.join(target_bin_dir, rel_path)
|
||
os.makedirs(target_dir, exist_ok=True)
|
||
|
||
# Copiar archivos
|
||
for file in files:
|
||
source_file = os.path.join(root, file)
|
||
if rel_path == ".":
|
||
target_file = os.path.join(target_bin_dir, file)
|
||
else:
|
||
target_file = os.path.join(target_bin_dir, rel_path, file)
|
||
|
||
# Crear directorios si es necesario
|
||
os.makedirs(os.path.dirname(target_file), exist_ok=True)
|
||
|
||
# Copiar archivo (sobrescribiendo si existe)
|
||
shutil.copy2(source_file, target_file)
|
||
printc(f"Actualizado: {target_file}", col.OKBLUE)
|
||
|
||
printc(f"Binarios de {display_name} actualizados exitosamente", col.OKGREEN)
|
||
|
||
# 5. Establecer permisos apropiados en ejecutables clave
|
||
printc(f"Estableciendo permisos para binarios de {display_name}...", col.OKBLUE)
|
||
executables_to_chmod = [
|
||
"/home/xc_vm/bin/php/bin/php",
|
||
"/home/xc_vm/bin/php/sbin/php-fpm",
|
||
"/home/xc_vm/bin/nginx/sbin/nginx",
|
||
"/home/xc_vm/bin/nginx_rtmp/sbin/nginx_rtmp"
|
||
]
|
||
for exe_path in executables_to_chmod:
|
||
if os.path.exists(exe_path):
|
||
run_command(f"chmod +x {exe_path}")
|
||
|
||
# 6. Limpiar (solo archivos temporales, no parche local)
|
||
printc("Limpiando archivos temporales...", col.OKBLUE)
|
||
if temp_tar_file != local_patch_file: # Solo remover si no es el parche local
|
||
os.remove(temp_tar_file)
|
||
if os.path.exists(temp_extract_dir):
|
||
shutil.rmtree(temp_extract_dir)
|
||
|
||
printc(f"Instalación de binarios específicos de {display_name} completada", col.OKGREEN)
|
||
return True
|
||
|
||
except Exception as e:
|
||
printc(f"Error instalando binarios de {display_name}: {e}", col.FAIL)
|
||
# Limpiar en error (solo archivos temporales, no parche local)
|
||
if temp_tar_file != local_patch_file and os.path.exists(temp_tar_file):
|
||
os.remove(temp_tar_file)
|
||
if os.path.exists(temp_extract_dir):
|
||
shutil.rmtree(temp_extract_dir)
|
||
return False
|
||
|
||
# Funciones específicas para cada versión (mantenidas para compatibilidad)
|
||
def install_debian12_binaries():
|
||
"""Wrapper function for Debian 12"""
|
||
return install_distribution_binaries('debian', '12')
|
||
|
||
def install_ubuntu20_binaries():
|
||
"""Wrapper function for Ubuntu 20"""
|
||
return install_distribution_binaries('ubuntu', '20')
|
||
|
||
def detect_distribution():
|
||
"""Detect Linux distribution and version without external modules"""
|
||
dist_id = "unknown"
|
||
version = "unknown"
|
||
family = "unknown"
|
||
|
||
# Try /etc/os-release first (standard method)
|
||
if os.path.exists("/etc/os-release"):
|
||
try:
|
||
with open("/etc/os-release", "r") as f:
|
||
lines = f.readlines()
|
||
for line in lines:
|
||
line = line.strip()
|
||
if line.startswith("ID="):
|
||
dist_id = line.split("=")[1].strip().strip('"')
|
||
elif line.startswith("VERSION_ID="):
|
||
version = line.split("=")[1].strip().strip('"')
|
||
except:
|
||
pass
|
||
|
||
# Try older methods
|
||
if dist_id == "unknown":
|
||
if os.path.exists("/etc/redhat-release"):
|
||
dist_id = "centos"
|
||
try:
|
||
with open("/etc/redhat-release", "r") as f:
|
||
content = f.read().lower()
|
||
if "rocky" in content:
|
||
dist_id = "rocky"
|
||
elif "alma" in content:
|
||
dist_id = "almalinux"
|
||
elif "rhel" in content:
|
||
dist_id = "rhel"
|
||
elif "fedora" in content:
|
||
dist_id = "fedora"
|
||
except:
|
||
pass
|
||
elif os.path.exists("/etc/debian_version"):
|
||
dist_id = "debian"
|
||
try:
|
||
with open("/etc/debian_version", "r") as f:
|
||
version = f.read().strip()
|
||
except:
|
||
pass
|
||
elif os.path.exists("/etc/lsb-release"):
|
||
try:
|
||
with open("/etc/lsb-release", "r") as f:
|
||
lines = f.readlines()
|
||
for line in lines:
|
||
if line.startswith("DISTRIB_ID="):
|
||
dist_id = line.split("=")[1].strip().lower().strip('"')
|
||
elif line.startswith("DISTRIB_RELEASE="):
|
||
version = line.split("=")[1].strip().strip('"')
|
||
except:
|
||
pass
|
||
|
||
# Determine family
|
||
if dist_id in ['centos', 'rhel', 'rocky', 'almalinux', 'fedora']:
|
||
family = 'redhat'
|
||
elif dist_id in ['ubuntu', 'debian']:
|
||
family = 'debian'
|
||
else:
|
||
family = dist_id
|
||
|
||
return {
|
||
'id': dist_id,
|
||
'family': family,
|
||
'version': version,
|
||
'full_version': version
|
||
}
|
||
|
||
def check_supported_distro(dist_info):
|
||
"""Check if distribution is supported"""
|
||
dist_id = dist_info['id']
|
||
version = dist_info['version']
|
||
|
||
if dist_id in SUPPORTED_DISTROS:
|
||
if version in SUPPORTED_DISTROS[dist_id]:
|
||
return True
|
||
else:
|
||
# Check if any supported version starts with the same major version
|
||
for supported_version in SUPPORTED_DISTROS[dist_id]:
|
||
if supported_version.startswith(version.split('.')[0]):
|
||
return True
|
||
|
||
# If not in list but is a known family, we'll try anyway
|
||
if dist_info['family'] in ['debian', 'redhat']:
|
||
return True
|
||
|
||
return False
|
||
|
||
def run_command(cmd, shell=True, capture_output=False):
|
||
"""Run shell command with error handling"""
|
||
try:
|
||
if capture_output:
|
||
result = subprocess.run(cmd, shell=shell, capture_output=True, text=True)
|
||
return result.returncode, result.stdout, result.stderr
|
||
else:
|
||
result = subprocess.run(cmd, shell=shell)
|
||
return result.returncode, None, None
|
||
except Exception as e:
|
||
return 1, None, str(e)
|
||
|
||
def is_valid_zip(file_path):
|
||
"""Check if a file is a valid ZIP archive"""
|
||
try:
|
||
with zipfile.ZipFile(file_path, 'r') as zip_ref:
|
||
# Try to list contents
|
||
zip_ref.namelist()
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
def is_valid_tar(file_path):
|
||
"""Check if a file is a valid TAR archive"""
|
||
try:
|
||
with tarfile.open(file_path, 'r:*') as tar_ref:
|
||
# Try to list contents
|
||
tar_ref.getmembers()
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
def download_xc_vm():
|
||
"""Download XC_VM from GitHub releases - SIGUIENDO LA MISMA LÓGICA DEL BASH"""
|
||
printc("Checking for XC_VM installation files...", col.OKBLUE)
|
||
|
||
# Check if valid files already exist (igual que antes)
|
||
if os.path.exists("./xc_vm.tar.gz") and is_valid_tar("./xc_vm.tar.gz"):
|
||
printc("Valid xc_vm.tar.gz found locally", col.OKGREEN)
|
||
return True
|
||
|
||
if os.path.exists("./XC_VM.zip") and is_valid_zip("./XC_VM.zip"):
|
||
printc("Valid XC_VM.zip found locally", col.OKGREEN)
|
||
return True
|
||
|
||
printc("Not found. Trying to download from GitHub...", col.OKBLUE)
|
||
|
||
try:
|
||
# 1. Obtener la última versión de la API de GitHub (IGUAL QUE BASH)
|
||
printc("Getting latest version from GitHub API...", col.OKBLUE)
|
||
api_url = "https://api.github.com/repos/Vateron-Media/XC_VM/releases/latest"
|
||
req = urllib.request.Request(api_url)
|
||
req.add_header('User-Agent', 'XC_VM-Installer/1.0')
|
||
|
||
with urllib.request.urlopen(req, timeout=10) as response:
|
||
data = json.loads(response.read().decode())
|
||
latest_version = data['tag_name']
|
||
printc(f"Latest version: {latest_version}", col.OKGREEN)
|
||
|
||
# 2. Descargar XC_VM.zip directamente (IGUAL QUE BASH: wget "https://github.com/Vateron-Media/XC_VM/releases/download/${latest_version}/XC_VM.zip")
|
||
download_url = f"https://github.com/Vateron-Media/XC_VM/releases/download/{latest_version}/XC_VM.zip"
|
||
printc(f"Downloading: {download_url}", col.OKBLUE)
|
||
|
||
# Usar urllib para descargar
|
||
urllib.request.urlretrieve(download_url, "XC_VM.zip")
|
||
|
||
# Verificar la descarga
|
||
if os.path.exists("XC_VM.zip") and os.path.getsize("XC_VM.zip") > 0:
|
||
file_size = os.path.getsize("XC_VM.zip")
|
||
printc(f"Download successful: XC_VM.zip ({file_size} bytes)", col.OKGREEN)
|
||
|
||
# Validar que sea un ZIP válido
|
||
if is_valid_zip("XC_VM.zip"):
|
||
printc("ZIP archive validated successfully", col.OKGREEN)
|
||
return True
|
||
else:
|
||
printc("Downloaded file is not a valid ZIP archive", col.WARNING)
|
||
os.remove("XC_VM.zip")
|
||
return False
|
||
else:
|
||
printc("Download failed or file is empty", col.FAIL)
|
||
return False
|
||
|
||
except Exception as e:
|
||
printc(f"Download error: {e}", col.FAIL)
|
||
|
||
# Intentar métodos alternativos si falla
|
||
printc("Trying alternative download methods...", col.WARNING)
|
||
|
||
# Método alternativo 1: Descarga directa del latest
|
||
try:
|
||
alt_url = "https://github.com/Vateron-Media/XC_VM/releases/latest/download/XC_VM.zip"
|
||
printc(f"Trying alternative: {alt_url}", col.OKBLUE)
|
||
urllib.request.urlretrieve(alt_url, "XC_VM.zip")
|
||
|
||
if os.path.exists("XC_VM.zip") and is_valid_zip("XC_VM.zip"):
|
||
printc("Alternative download successful", col.OKGREEN)
|
||
return True
|
||
except:
|
||
pass
|
||
|
||
# Método alternativo 2: Versión específica 1.2.10
|
||
try:
|
||
specific_url = "https://github.com/Vateron-Media/XC_VM/releases/download/1.2.10/XC_VM.zip"
|
||
printc(f"Trying specific version: {specific_url}", col.OKBLUE)
|
||
urllib.request.urlretrieve(specific_url, "XC_VM.zip")
|
||
|
||
if os.path.exists("XC_VM.zip") and is_valid_zip("XC_VM.zip"):
|
||
printc("Specific version download successful", col.OKGREEN)
|
||
return True
|
||
except:
|
||
pass
|
||
|
||
return False
|
||
|
||
def install_mariadb_repo(dist_info):
|
||
"""Install MariaDB repository based on distribution"""
|
||
dist_id = dist_info['id']
|
||
version = dist_info['version']
|
||
family = dist_info['family']
|
||
|
||
printc(f"Configuring MariaDB repository for {dist_id} {version}", col.OKBLUE)
|
||
|
||
if family == 'debian':
|
||
# Debian/Ubuntu
|
||
if dist_id == 'ubuntu':
|
||
# Try to get codename from /etc/os-release
|
||
codename = "jammy" # Default for Ubuntu 22.04
|
||
try:
|
||
with open("/etc/os-release", "r") as f:
|
||
for line in f:
|
||
if line.startswith("UBUNTU_CODENAME="):
|
||
codename = line.split("=")[1].strip().strip('"')
|
||
break
|
||
elif line.startswith("VERSION_CODENAME="):
|
||
codename = line.split("=")[1].strip().strip('"')
|
||
break
|
||
except:
|
||
# Fallback based on version
|
||
if version.startswith("20"):
|
||
codename = "focal"
|
||
elif version.startswith("22"):
|
||
codename = "jammy"
|
||
elif version.startswith("24"):
|
||
codename = "noble"
|
||
|
||
printc(f"Using Ubuntu codename: {codename}", col.OKGREEN)
|
||
|
||
# Install prerequisites
|
||
run_command("apt-get install -y apt-transport-https curl gnupg software-properties-common")
|
||
|
||
# Special handling for Ubuntu 20.04 LTS (EOL)
|
||
if dist_id == 'ubuntu' and version.startswith("20"):
|
||
printc("Ubuntu 20.04 LTS detected, using system MariaDB packages...", col.WARNING)
|
||
printc("MariaDB 11.4 is not compatible with Ubuntu 20.04 (libc6 incompatibility)", col.OKBLUE)
|
||
printc("Using Ubuntu 20.04 default MariaDB packages for compatibility", col.OKGREEN)
|
||
|
||
# For Ubuntu 20.04, we'll use the system MariaDB packages
|
||
# Ubuntu 20.04 default repositories have MariaDB 10.3 which is compatible
|
||
try:
|
||
# Remove any existing MariaDB repository files
|
||
if os.path.exists("/etc/apt/sources.list.d/mariadb.list"):
|
||
os.remove("/etc/apt/sources.list.d/mariadb.list")
|
||
printc("Removed incompatible MariaDB repository", col.OKBLUE)
|
||
|
||
if os.path.exists("/usr/share/keyrings/mariadb.gpg"):
|
||
os.remove("/usr/share/keyrings/mariadb.gpg")
|
||
|
||
printc("Will use Ubuntu 20.04 default MariaDB packages (10.3.x)", col.OKGREEN)
|
||
# Skip external repository setup for Ubuntu 20.04
|
||
|
||
except Exception as e:
|
||
printc(f"Error cleaning up MariaDB repositories: {e}", col.WARNING)
|
||
printc("Continuing with system packages...", col.WARNING)
|
||
|
||
else:
|
||
# For other versions, use the official script
|
||
printc("Adding MariaDB repository using official setup script...", col.OKBLUE)
|
||
run_command("curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version='mariadb-11.4'")
|
||
|
||
# CORREGIDO: Añadir MaxMind repository solo para Ubuntu (con manejo de errores)
|
||
if dist_id == 'ubuntu':
|
||
printc("Adding MaxMind repository for GeoIP...", col.OKBLUE)
|
||
try:
|
||
run_command("add-apt-repository -y ppa:maxmind/ppa")
|
||
printc("MaxMind repository added successfully", col.OKGREEN)
|
||
except Exception as e:
|
||
printc(f"MaxMind PPA failed to add: {e}", col.WARNING)
|
||
printc("Continuing without MaxMind PPA (not critical for functionality)", col.OKBLUE)
|
||
else:
|
||
printc("Skipping MaxMind PPA (not available for Debian)", col.OKBLUE)
|
||
|
||
# Update package list
|
||
printc("Updating package list...", col.OKBLUE)
|
||
run_command("apt-get update")
|
||
|
||
elif family == 'redhat':
|
||
# RedHat based distributions
|
||
run_command("yum install -y curl")
|
||
|
||
# Install MariaDB repository using official script
|
||
printc("Adding MariaDB repository...", col.OKBLUE)
|
||
run_command("curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version='mariadb-11.4'")
|
||
|
||
else:
|
||
printc(f"Unsupported distribution: {dist_id}", col.WARNING)
|
||
return False
|
||
|
||
return True
|
||
|
||
def secure_mariadb_installation(root_password, dist_info=None):
|
||
"""Secure MariaDB installation with root password (from bash script)"""
|
||
printc("Securing MariaDB installation", col.OKBLUE)
|
||
|
||
# Check if MariaDB is running
|
||
ret, out, err = run_command("systemctl is-active mariadb", capture_output=True)
|
||
|
||
if ret != 0:
|
||
printc("Starting MariaDB service", col.OKBLUE)
|
||
run_command("systemctl start mariadb")
|
||
time.sleep(5)
|
||
|
||
# Get MariaDB version to determine syntax
|
||
printc("Checking MariaDB version...", col.OKBLUE)
|
||
version_cmd = "mariadb --version 2>/dev/null | head -n 1 || mysql --version 2>/dev/null | head -n 1"
|
||
ret, out, err = run_command(version_cmd, capture_output=True)
|
||
|
||
mariadb_version = out.strip() if out else ""
|
||
is_mariadb_103 = "10.3" in mariadb_version or "5.7" in mariadb_version or "5.6" in mariadb_version
|
||
|
||
if is_mariadb_103:
|
||
printc("MariaDB 10.3 detected, using legacy password syntax", col.OKBLUE)
|
||
else:
|
||
printc("MariaDB 11.x detected, using modern password syntax", col.OKBLUE)
|
||
|
||
# Determine authentication plugin
|
||
printc("Checking MariaDB authentication plugin...", col.OKBLUE)
|
||
auth_cmd = "mariadb -u root -e \"SELECT plugin FROM mysql.user WHERE User='root' AND Host='localhost';\" 2>/dev/null | tail -n +2"
|
||
ret, out, err = run_command(auth_cmd, capture_output=True)
|
||
|
||
auth_plugin = out.strip() if out else ""
|
||
|
||
if ret == 0 and auth_plugin == "unix_socket":
|
||
printc("Using unix_socket authentication, converting to password...", col.OKBLUE)
|
||
# Convert from unix_socket to password authentication with version-specific syntax
|
||
if is_mariadb_103:
|
||
# MariaDB 10.3 and older syntax
|
||
sql_commands = [
|
||
"FLUSH PRIVILEGES;",
|
||
f"SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{root_password}');",
|
||
"DELETE FROM mysql.user WHERE User='';",
|
||
"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');",
|
||
"DROP DATABASE IF EXISTS test;",
|
||
"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\\\_%';",
|
||
"FLUSH PRIVILEGES;"
|
||
]
|
||
else:
|
||
# MariaDB 10.4+ and MariaDB 11.x syntax
|
||
sql_commands = [
|
||
"FLUSH PRIVILEGES;",
|
||
f"ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('{root_password}');",
|
||
"DELETE FROM mysql.user WHERE User='';",
|
||
"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');",
|
||
"DROP DATABASE IF EXISTS test;",
|
||
"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\\\_%';",
|
||
"FLUSH PRIVILEGES;"
|
||
]
|
||
|
||
for sql in sql_commands:
|
||
if is_mariadb_103 and "SET PASSWORD" in sql:
|
||
# Special handling for SET PASSWORD command
|
||
run_command(f"mariadb -u root -e \"{sql}\"", shell=True)
|
||
else:
|
||
run_command(f"mariadb -u root -e \"{sql}\"")
|
||
|
||
printc("MariaDB secured with password authentication", col.OKGREEN)
|
||
else:
|
||
printc("Setting MariaDB root password...", col.OKBLUE)
|
||
# Try to set password with version-specific syntax
|
||
if is_mariadb_103:
|
||
# MariaDB 10.3 syntax
|
||
set_cmd = f"mariadb -u root -e \"SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{root_password}');\" 2>/dev/null || true"
|
||
else:
|
||
# MariaDB 11.x syntax
|
||
set_cmd = f"mariadb -u root -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '{root_password}';\" 2>/dev/null || true"
|
||
run_command(set_cmd)
|
||
|
||
# Create custom security configuration (like 99-custom.cnf from bash script)
|
||
printc("Creating custom MariaDB security configuration...", col.OKBLUE)
|
||
custom_conf = """[mysqld]
|
||
bind-address = 0.0.0.0
|
||
skip-name-resolve
|
||
local-infile = 0
|
||
symbolic-links = 0
|
||
slow_query_log = 1
|
||
slow_query_log_file = /var/log/mysql/mariadb-slow.log
|
||
long_query_time = 2
|
||
log_error = /var/log/mysql/error.log"""
|
||
|
||
conf_dir = "/etc/mysql/mariadb.conf.d/"
|
||
if not os.path.exists(conf_dir):
|
||
conf_dir = "/etc/my.cnf.d/"
|
||
if not os.path.exists(conf_dir):
|
||
os.makedirs(conf_dir, exist_ok=True)
|
||
|
||
custom_path = os.path.join(conf_dir, "99-custom.cnf")
|
||
with open(custom_path, "w") as f:
|
||
f.write(custom_conf)
|
||
|
||
# Create log directory and set permissions
|
||
run_command("mkdir -p /var/log/mysql && chown mysql:mysql /var/log/mysql")
|
||
|
||
# Restart MariaDB
|
||
run_command("systemctl restart mariadb")
|
||
time.sleep(3)
|
||
|
||
# NO crear /root/mariadb_root_password.txt, solo usar /root/credentials.txt
|
||
printc("MariaDB security hardening completed", col.OKGREEN)
|
||
return root_password
|
||
|
||
def get_system_ram_mb():
|
||
"""Get total system RAM in MB"""
|
||
try:
|
||
with open('/proc/meminfo', 'r') as f:
|
||
for line in f:
|
||
if line.startswith('MemTotal:'):
|
||
mem_kb = int(line.split()[1])
|
||
return mem_kb // 1024 # Convert to MB
|
||
except:
|
||
pass
|
||
return 1024 # Value by default if unable to determine
|
||
|
||
def generate_mysql_config(total_ram_mb):
|
||
"""Generate MySQL configuration based on total RAM (from bash script logic)"""
|
||
|
||
# Calculate based on RAM (similar to bash script)
|
||
buffer_pool_mb = int(total_ram_mb * 0.25)
|
||
|
||
if total_ram_mb < 512:
|
||
buffer_pool_mb = 64
|
||
max_connections = 30
|
||
elif total_ram_mb < 1024:
|
||
if buffer_pool_mb > 128:
|
||
buffer_pool_mb = 128
|
||
max_connections = 50
|
||
elif total_ram_mb < 2048:
|
||
if buffer_pool_mb > 256:
|
||
buffer_pool_mb = 256
|
||
max_connections = 80
|
||
elif total_ram_mb < 4096:
|
||
if buffer_pool_mb > 512:
|
||
buffer_pool_mb = 512
|
||
max_connections = 120
|
||
elif total_ram_mb < 8192:
|
||
if buffer_pool_mb > 1024:
|
||
buffer_pool_mb = 1024
|
||
max_connections = 150
|
||
else:
|
||
if buffer_pool_mb > 2048:
|
||
buffer_pool_mb = 2048
|
||
max_connections = 200
|
||
|
||
# Format buffer pool size
|
||
if buffer_pool_mb >= 1024:
|
||
buffer_pool_size = f"{buffer_pool_mb // 1024}G"
|
||
else:
|
||
buffer_pool_size = f"{buffer_pool_mb}M"
|
||
|
||
# Calculate other values
|
||
key_buffer = min(buffer_pool_mb // 8, 32)
|
||
tmp_table_size = min(buffer_pool_mb // 4, 64)
|
||
back_log = min(max_connections // 2, 256)
|
||
thread_cache = min(max_connections // 4, 64)
|
||
buffer_pool_instances = "1" if buffer_pool_mb < 1024 else "2"
|
||
|
||
# Generate config from template
|
||
config = rMySQLCnfTemplate
|
||
config = config.replace("{{KEY_BUFFER}}", str(key_buffer))
|
||
config = config.replace("{{MAX_CONNECTIONS}}", str(max_connections))
|
||
config = config.replace("{{BACK_LOG}}", str(back_log))
|
||
config = config.replace("{{TMP_TABLE_SIZE}}", str(tmp_table_size))
|
||
config = config.replace("{{BUFFER_POOL_SIZE}}", buffer_pool_size)
|
||
config = config.replace("{{BUFFER_POOL_INSTANCES}}", buffer_pool_instances)
|
||
config = config.replace("{{THREAD_CACHE}}", str(thread_cache))
|
||
|
||
printc(f"RAM detected: {total_ram_mb}MB", col.OKGREEN)
|
||
printc(f"Buffer pool configured: {buffer_pool_size} ({buffer_pool_mb}MB)", col.OKGREEN)
|
||
printc(f"Max connections: {max_connections}", col.OKGREEN)
|
||
|
||
return config
|
||
|
||
def generate_random_password(length=32):
|
||
"""Generate random password (similar to bash script)"""
|
||
chars = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ'
|
||
return ''.join(random.choice(chars) for _ in range(length))
|
||
|
||
def generate_root_password():
|
||
"""Generate secure root password of 20 characters"""
|
||
chars = 'A-Za-z0-9!@#%^*()_+-=,.<>?'
|
||
try:
|
||
# Try using /dev/urandom
|
||
cmd = f"cat /dev/urandom | tr -dc '{chars}' | head -c 20"
|
||
ret, out, err = run_command(cmd, capture_output=True)
|
||
if ret == 0 and out:
|
||
return out.strip()
|
||
except:
|
||
pass
|
||
|
||
# Fallback to Python random
|
||
import string
|
||
chars = string.ascii_letters + string.digits + '!@#%^*()_+-=,.<>?'
|
||
return ''.join(random.choice(chars) for _ in range(20))
|
||
|
||
def getIP():
|
||
try:
|
||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
s.connect(("8.8.8.8", 80))
|
||
ip = s.getsockname()[0]
|
||
s.close()
|
||
return ip
|
||
except:
|
||
# Fallback method
|
||
try:
|
||
hostname = socket.gethostname()
|
||
ip = socket.gethostbyname(hostname)
|
||
if ip and ip != "127.0.0.1":
|
||
return ip
|
||
except:
|
||
pass
|
||
return "127.0.0.1"
|
||
|
||
def printc(rText, rColour=col.OKBLUE, rPadding=0):
|
||
rLeft = int(30 - (len(rText) / 2))
|
||
rRight = 60 - rLeft - len(rText)
|
||
print(
|
||
"%s |--------------------------------------------------------------| %s"
|
||
% (rColour, col.ENDC)
|
||
)
|
||
for i in range(rPadding):
|
||
print(
|
||
"%s | | %s"
|
||
% (rColour, col.ENDC)
|
||
)
|
||
print("%s | %s%s%s | %s" % (rColour, " " * rLeft, rText, " " * rRight, col.ENDC))
|
||
for i in range(rPadding):
|
||
print(
|
||
"%s | | %s"
|
||
% (rColour, col.ENDC)
|
||
)
|
||
print(
|
||
"%s |--------------------------------------------------------------| %s"
|
||
% (rColour, col.ENDC)
|
||
)
|
||
print(" ")
|
||
|
||
def extract_archive(archive_path):
|
||
"""Extract archive with proper validation and error handling"""
|
||
printc(f"Extracting {archive_path}...", col.OKBLUE)
|
||
|
||
if archive_path.endswith('.tar.gz') or archive_path.endswith('.tgz'):
|
||
try:
|
||
with tarfile.open(archive_path, 'r:gz') as tar:
|
||
# Get member list for debugging
|
||
members = tar.getmembers()
|
||
printc(f"Archive contains {len(members)} files/directories", col.OKGREEN)
|
||
|
||
# Extract all files
|
||
tar.extractall(path="/home/xc_vm/")
|
||
printc("Extraction successful", col.OKGREEN)
|
||
return True
|
||
except Exception as e:
|
||
printc(f"Failed to extract tar.gz: {e}", col.FAIL)
|
||
return False
|
||
|
||
elif archive_path.endswith('.zip'):
|
||
try:
|
||
with zipfile.ZipFile(archive_path, 'r') as zip_ref:
|
||
# Get file list for debugging
|
||
file_list = zip_ref.namelist()
|
||
printc(f"Archive contains {len(file_list)} files", col.OKGREEN)
|
||
|
||
# Extract all files
|
||
zip_ref.extractall(path="/home/xc_vm/")
|
||
printc("Extraction successful", col.OKGREEN)
|
||
|
||
# Check if zip contains nested tar.gz
|
||
for file in file_list:
|
||
if file.endswith('xc_vm.tar.gz') or file.endswith('.tar.gz'):
|
||
nested_path = os.path.join("/home/xc_vm", file)
|
||
if os.path.exists(nested_path):
|
||
printc(f"Found nested archive: {file}, extracting...", col.OKBLUE)
|
||
return extract_archive(nested_path)
|
||
return True
|
||
except Exception as e:
|
||
printc(f"Failed to extract zip: {e}", col.FAIL)
|
||
return False
|
||
|
||
else:
|
||
printc(f"Unsupported archive format: {archive_path}", col.FAIL)
|
||
return False
|
||
|
||
def fix_ssh2_library_issue():
|
||
"""Fix SSH2 library issue by creating proper symlinks"""
|
||
printc("Configuring SSH2 libraries for PHP", col.OKBLUE)
|
||
|
||
# List of possible libssh2.so.1 locations
|
||
libssh2_paths = [
|
||
"/usr/lib/x86_64-linux-gnu/libssh2.so.1",
|
||
"/usr/lib/x86_64-linux-gnu/libssh2.so.1.0.1",
|
||
"/usr/lib/x86_64-linux-gnu/libssh2.so",
|
||
"/usr/lib64/libssh2.so.1",
|
||
"/usr/lib/libssh2.so.1",
|
||
"/usr/local/lib/libssh2.so.1"
|
||
]
|
||
|
||
found_lib = None
|
||
for lib_path in libssh2_paths:
|
||
if os.path.exists(lib_path):
|
||
found_lib = lib_path
|
||
printc(f"Found SSH2 library: {lib_path}", col.OKGREEN)
|
||
break
|
||
|
||
if found_lib:
|
||
# Create symlinks in common locations
|
||
symlink_targets = [
|
||
"/usr/lib/libssh2.so.1",
|
||
"/usr/local/lib/libssh2.so.1",
|
||
"/lib/libssh2.so.1"
|
||
]
|
||
|
||
for target in symlink_targets:
|
||
if not os.path.exists(target):
|
||
try:
|
||
run_command(f"ln -sf {found_lib} {target}")
|
||
printc(f"Created symlink: {found_lib} -> {target}", col.OKGREEN)
|
||
except:
|
||
printc(f"Failed to create symlink for {target}", col.WARNING)
|
||
|
||
# Also check if we need to symlink in PHP extensions directory
|
||
php_ext_dir = "/home/xc_vm/bin/php/lib/php/extensions/"
|
||
if os.path.exists(php_ext_dir):
|
||
# Find the actual extensions directory
|
||
for dirpath, dirnames, filenames in os.walk(php_ext_dir):
|
||
if "ssh2.so" in filenames:
|
||
ssh2_so_path = os.path.join(dirpath, "ssh2.so")
|
||
printc(f"Found PHP ssh2.so at: {ssh2_so_path}", col.OKGREEN)
|
||
break
|
||
else:
|
||
printc("Warning: libssh2.so.1 not found. SSH2 may not work properly.", col.WARNING)
|
||
|
||
# Try to find any libssh2 version
|
||
try:
|
||
ret, out, err = run_command("find /usr -name 'libssh2.so*' 2>/dev/null", capture_output=True)
|
||
if ret == 0 and out:
|
||
libs = out.strip().split('\n')
|
||
if libs:
|
||
found_lib = libs[0]
|
||
printc(f"Found alternative SSH2 library: {found_lib}", col.OKGREEN)
|
||
|
||
# Create symlink
|
||
run_command(f"ln -sf {found_lib} /usr/lib/libssh2.so.1")
|
||
printc(f"Created symlink: {found_lib} -> /usr/lib/libssh2.so.1", col.OKGREEN)
|
||
except:
|
||
pass
|
||
|
||
# NO deshabilitar la extensión ssh2 - solo crear enlaces
|
||
|
||
if __name__ == "__main__":
|
||
##################################################
|
||
# START #
|
||
##################################################
|
||
|
||
printc("XC_VM Multi-Distribution Installer", col.OKGREEN, 2)
|
||
|
||
# NUEVO: Verificar si se ejecuta como root
|
||
check_root()
|
||
|
||
# Detect distribution
|
||
dist_info = detect_distribution()
|
||
printc(f"Detected: {dist_info['id']} {dist_info['version']} ({dist_info['family']} family)", col.OKGREEN)
|
||
|
||
# NUEVO: Instalar prerequisitos antes de continuar
|
||
install_prerequisites(dist_info)
|
||
|
||
if not check_supported_distro(dist_info):
|
||
printc(f"Warning: {dist_info['id']} {dist_info['version']} is not officially supported", col.WARNING)
|
||
response = input("Continue anyway? (Y/N): ").strip().upper()
|
||
if response != 'Y':
|
||
sys.exit(1)
|
||
printc("Continuing with installation...", col.WARNING)
|
||
|
||
# Try to download XC_VM if not present
|
||
has_valid_archive = False
|
||
archive_path = None
|
||
|
||
# Check for existing valid archives
|
||
if os.path.exists("./xc_vm.tar.gz") and is_valid_tar("./xc_vm.tar.gz"):
|
||
has_valid_archive = True
|
||
archive_path = "./xc_vm.tar.gz"
|
||
printc("Found valid xc_vm.tar.gz", col.OKGREEN)
|
||
elif os.path.exists("./XC_VM.zip") and is_valid_zip("./XC_VM.zip"):
|
||
has_valid_archive = True
|
||
archive_path = "./XC_VM.zip"
|
||
printc("Found valid XC_VM.zip", col.OKGREEN)
|
||
|
||
# Download if needed
|
||
if not has_valid_archive:
|
||
if download_xc_vm():
|
||
# Check what was downloaded
|
||
if os.path.exists("./xc_vm.tar.gz") and is_valid_tar("./xc_vm.tar.gz"):
|
||
has_valid_archive = True
|
||
archive_path = "./xc_vm.tar.gz"
|
||
elif os.path.exists("./XC_VM.zip") and is_valid_zip("./XC_VM.zip"):
|
||
has_valid_archive = True
|
||
archive_path = "./XC_VM.zip"
|
||
|
||
if not has_valid_archive or not archive_path:
|
||
printc("XC_VM package not found or invalid. Please download manually.", col.FAIL)
|
||
printc("You can download from: https://github.com/Vateron-Media/XC_VM/releases", col.OKBLUE)
|
||
sys.exit(1)
|
||
|
||
rHost = "127.0.0.1"
|
||
rServerID = 1
|
||
rUsername = generate_random_password(32) # Username de 32 caracteres
|
||
rPassword = generate_random_password(32) # Password de 32 caracteres
|
||
rDatabase = "xc_vm"
|
||
rPort = 3306
|
||
|
||
# Ask for MariaDB root password (or generate)
|
||
printc("MariaDB Root Password Configuration", col.OKBLUE)
|
||
print("For security, you should set a strong root password for MariaDB.")
|
||
print("Leave empty to generate a random password.")
|
||
|
||
root_password = input("MariaDB root password (or press Enter to generate): ").strip()
|
||
if not root_password:
|
||
root_password = generate_root_password() # Ahora genera de 20 caracteres
|
||
printc(f"Generated root password: {root_password}", col.OKGREEN)
|
||
else:
|
||
# Si el usuario proporciona una contraseña, asegurar que tenga al menos 20 caracteres
|
||
if len(root_password) < 20:
|
||
printc(f"Warning: Provided password is only {len(root_password)} characters. For security, consider using at least 20 characters.", col.WARNING)
|
||
response = input("Continue with provided password? (Y/N): ").strip().upper()
|
||
if response != 'Y':
|
||
root_password = generate_root_password()
|
||
printc(f"Using generated root password: {root_password}", col.OKGREEN)
|
||
else:
|
||
printc("Using provided root password", col.OKGREEN)
|
||
|
||
if os.path.exists("/home/xc_vm/"):
|
||
printc("XC_VM Directory Exists!", col.WARNING)
|
||
while True:
|
||
rAnswer = input("Continue and overwrite? (Y / N) : ").strip().upper()
|
||
if rAnswer in ["Y", "N"]:
|
||
break
|
||
if rAnswer == "N":
|
||
sys.exit(1)
|
||
|
||
##################################################
|
||
# SYSTEM PREPARATION #
|
||
##################################################
|
||
|
||
printc("Preparing System", col.OKBLUE)
|
||
|
||
if dist_info['family'] == 'debian':
|
||
# Debian/Ubuntu
|
||
printc("Cleaning package locks", col.OKBLUE)
|
||
for rFile in [
|
||
"/var/lib/dpkg/lock-frontend",
|
||
"/var/cache/apt/archives/lock",
|
||
"/var/lib/dpkg/lock",
|
||
"/var/lib/apt/lists/lock",
|
||
]:
|
||
if os.path.exists(rFile):
|
||
try:
|
||
os.remove(rFile)
|
||
except:
|
||
pass
|
||
|
||
printc("Updating system", col.OKBLUE)
|
||
run_command("apt-get update -y")
|
||
|
||
# Install MariaDB repository
|
||
if not install_mariadb_repo(dist_info):
|
||
printc("Using system MariaDB repository", col.WARNING)
|
||
|
||
# Stop conflicting services (like bash script)
|
||
printc("Stopping conflicting services (Apache/System Nginx)...", col.OKBLUE)
|
||
run_command("systemctl stop apache2 nginx 2>/dev/null || true")
|
||
run_command("systemctl disable apache2 nginx 2>/dev/null || true")
|
||
|
||
# Remove conflicting packages
|
||
for rPackage in rRemove:
|
||
printc(f"Removing {rPackage}", col.OKBLUE)
|
||
run_command(f"apt-get remove {rPackage} -y")
|
||
|
||
# Install packages in groups (like bash script)
|
||
printc("Installing system packages", col.OKBLUE)
|
||
|
||
# Select appropriate package list based on distribution
|
||
printc(f"DEBUG: Distribution detected: {dist_info['id']} version: {dist_info['version']}", col.OKBLUE)
|
||
|
||
if dist_info['id'] == 'ubuntu' and dist_info['version'].startswith("20"):
|
||
printc("Using Ubuntu 20.04 compatible package installation", col.OKBLUE)
|
||
printc("Skipping OpenSSL 3 (incompatible with Ubuntu 20.04)", col.WARNING)
|
||
|
||
# Fix broken packages first
|
||
printc("Fixing any broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
run_command("apt-get autoremove -y || true")
|
||
run_command("apt-get autoclean || true")
|
||
|
||
# Install all packages from PACKAGES['ubuntu20']
|
||
ubuntu20_packages = PACKAGES.get('ubuntu20', [])
|
||
if ubuntu20_packages:
|
||
packages_str = " ".join(ubuntu20_packages)
|
||
printc(f"Installing Ubuntu 20.04 packages ({len(ubuntu20_packages)} packages)...", col.OKBLUE)
|
||
run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Install OpenSSL 3 compatible library for PHP binaries (Ubuntu 20.04 specific)
|
||
printc("Installing OpenSSL 3 compatibility library for PHP binaries...", col.OKBLUE)
|
||
try:
|
||
# Download and install libssl3 from Ubuntu 22.04 repository (compatible)
|
||
run_command(
|
||
"wget -qO /tmp/libssl3_ubuntu20.deb \"https://packages.debian.org/trixie/amd64/libssl3/download\" "
|
||
"|| wget -qO /tmp/libssl3_ubuntu20.deb \"http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl3_3.0.2-0ubuntu1_amd64.deb\""
|
||
)
|
||
|
||
# Try to install with --force-depends if needed
|
||
run_command("dpkg --force-depends -i /tmp/libssl3_ubuntu20.deb 2>/dev/null || dpkg -i /tmp/libssl3_ubuntu20.deb 2>/dev/null || true")
|
||
|
||
# Alternative method if library not present
|
||
if not os.path.exists("/usr/lib/x86_64-linux-gnu/libssl.so.3"):
|
||
printc("Trying alternative OpenSSL 3 installation method...", col.OKBLUE)
|
||
run_command("find /usr -name 'libssl.so*' 2>/dev/null | head -5")
|
||
run_command("ls -la /usr/lib/x86_64-linux-gnu/libssl* 2>/dev/null || ls -la /usr/lib/libssl* 2>/dev/null || true")
|
||
|
||
run_command("rm -f /tmp/libssl3_ubuntu20.deb")
|
||
printc("OpenSSL 3 library installation completed", col.OKGREEN)
|
||
|
||
except Exception as e:
|
||
printc(f"OpenSSL 3 installation failed: {e}", col.WARNING)
|
||
printc("PHP binaries may not work without libssl.so.3", col.WARNING)
|
||
printc("You may need to manually install compatible OpenSSL 3 library", col.WARNING)
|
||
|
||
elif dist_info['id'] == 'ubuntu' and (dist_info['version'].startswith("24") or dist_info['version'].startswith("24.04")):
|
||
printc("Using Ubuntu 24.04 compatible package installation (t64 transition)", col.OKBLUE)
|
||
|
||
# Fix broken packages first (especially important for Ubuntu 24.04)
|
||
printc("Fixing broken packages from t64 transition...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
run_command("apt-get autoremove -y || true")
|
||
run_command("apt-get autoclean || true")
|
||
|
||
printc("Ubuntu 24.04 already has OpenSSL 3.13+ - no additional installation needed", col.OKGREEN)
|
||
|
||
# Install all packages from PACKAGES['ubuntu24']
|
||
ubuntu24_packages = PACKAGES.get('ubuntu24', [])
|
||
if ubuntu24_packages:
|
||
packages_str = " ".join(ubuntu24_packages)
|
||
printc(f"Installing Ubuntu 24.04 packages ({len(ubuntu24_packages)} packages)...", col.OKBLUE)
|
||
run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Fix broken packages again after installation
|
||
printc("Final fix for any remaining broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
|
||
elif dist_info['id'] == 'debian' and dist_info['version'].startswith("13"):
|
||
printc("Using Debian 13 compatible package installation", col.OKBLUE)
|
||
|
||
# Fix broken packages first
|
||
printc("Fixing any broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
run_command("apt-get autoremove -y || true")
|
||
run_command("apt-get autoclean || true")
|
||
|
||
printc("Debian 13 already has OpenSSL 3.x - no additional installation needed", col.OKGREEN)
|
||
|
||
# Install packages from PACKAGES['debian']
|
||
debian_packages = PACKAGES.get('debian13', [])
|
||
if debian_packages:
|
||
# Optionally install in batches of 10 to avoid long command lines
|
||
batch_size = 10
|
||
for i in range(0, len(debian_packages), batch_size):
|
||
batch = debian_packages[i:i + batch_size]
|
||
packages_str = " ".join(batch)
|
||
printc(f"Installing package group {i // batch_size + 1} ({len(batch)} packages)...", col.OKBLUE)
|
||
run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Fix broken packages again after installation
|
||
printc("Final fix for any remaining broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
|
||
elif dist_info['id'] == 'ubuntu' and dist_info['version'].startswith("22"):
|
||
printc("Using Ubuntu 22.04 compatible package installation", col.OKBLUE)
|
||
|
||
# Fix broken packages first
|
||
printc("Fixing any broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
run_command("apt-get autoremove -y || true")
|
||
run_command("apt-get autoclean || true")
|
||
|
||
# Ubuntu 22.04 already has OpenSSL 3.0, no special handling needed
|
||
printc("Ubuntu 22.04 has OpenSSL 3.0 - compatible with PHP binaries", col.OKGREEN)
|
||
|
||
# Install all packages from PACKAGES['ubuntu22']
|
||
ubuntu22_packages = PACKAGES.get('ubuntu22', [])
|
||
if ubuntu22_packages:
|
||
packages_str = " ".join(ubuntu22_packages)
|
||
printc(f"Installing Ubuntu 22.04 packages ({len(ubuntu22_packages)} packages)...", col.OKBLUE)
|
||
run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Fix broken packages again after installation
|
||
printc("Final fix for any remaining broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
|
||
elif dist_info['id'] == 'debian' and dist_info['version'].startswith("11"):
|
||
printc("Using Debian 11 compatible package installation", col.OKBLUE)
|
||
|
||
# Fix broken packages first
|
||
printc("Fixing any broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
run_command("apt-get autoremove -y || true")
|
||
run_command("apt-get autoclean || true")
|
||
|
||
# Debian 11 may need OpenSSL 3 for PHP binaries
|
||
printc("Installing OpenSSL 3 compatibility library for PHP binaries...", col.OKBLUE)
|
||
try:
|
||
run_command(
|
||
"wget -qO /tmp/libssl3_debian11.deb "
|
||
"\"http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl3_3.0.2-0ubuntu1_amd64.deb\""
|
||
)
|
||
run_command("dpkg --force-depends -i /tmp/libssl3_debian11.deb 2>/dev/null || dpkg -i /tmp/libssl3_debian11.deb 2>/dev/null || true")
|
||
run_command("rm -f /tmp/libssl3_debian11.deb")
|
||
printc("OpenSSL 3 library installation completed", col.OKGREEN)
|
||
except Exception as e:
|
||
printc(f"OpenSSL 3 installation warning: {e}", col.WARNING)
|
||
|
||
# Install packages from PACKAGES['debian11']
|
||
debian11_packages = PACKAGES.get('debian11', [])
|
||
if debian11_packages:
|
||
packages_str = " ".join(debian11_packages)
|
||
printc(f"Installing Debian 11 packages ({len(debian11_packages)} packages)...", col.OKBLUE)
|
||
run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Fix broken packages again after installation
|
||
printc("Final fix for any remaining broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
|
||
elif dist_info['id'] == 'debian' and dist_info['version'].startswith("12"):
|
||
printc("Using Debian 12 compatible package installation", col.OKBLUE)
|
||
|
||
# Fix broken packages first
|
||
printc("Fixing any broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
run_command("apt-get autoremove -y || true")
|
||
run_command("apt-get autoclean || true")
|
||
|
||
printc("Debian 12 already has OpenSSL 3.x - no additional installation needed", col.OKGREEN)
|
||
|
||
# Install packages from PACKAGES['debian'] (generic debian list works for 12)
|
||
debian_packages = PACKAGES.get('debian', [])
|
||
if debian_packages:
|
||
packages_str = " ".join(debian_packages)
|
||
printc(f"Installing Debian 12 packages ({len(debian_packages)} packages)...", col.OKBLUE)
|
||
run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Fix broken packages again after installation
|
||
printc("Final fix for any remaining broken packages...", col.OKBLUE)
|
||
run_command("apt --fix-broken install -y || true")
|
||
|
||
else:
|
||
# Fallback для других версий Debian/Ubuntu
|
||
debian_packages = PACKAGES.get('debian', [])
|
||
printc(f"Using standard Debian/Ubuntu package list for {dist_info['id']} {dist_info['version']}", col.OKBLUE)
|
||
|
||
# Install packages
|
||
if debian_packages:
|
||
packages_str = " ".join(debian_packages)
|
||
printc(f"Installing Debian packages ({len(debian_packages)} packages)...", col.OKBLUE)
|
||
run_command(f"DEBIAN_FRONTEND=noninteractive apt-get -yq install {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Install SSH2 libraries specifically for unknown distributions
|
||
printc("Installing SSH2 libraries...", col.OKBLUE)
|
||
run_command("apt-get install -y libssh2-1 libssh2-1-dev || apt-get install -y libssh2-1 libssh2-1t64 || true")
|
||
|
||
# Fix SSH2 library issue
|
||
printc("Configuring SSH2 libraries", col.OKBLUE)
|
||
fix_ssh2_library_issue()
|
||
|
||
elif dist_info['family'] == 'redhat':
|
||
# RedHat based distributions
|
||
printc("Configuring repositories", col.OKBLUE)
|
||
|
||
# Install EPEL
|
||
run_command("yum install -y epel-release")
|
||
|
||
# Install MariaDB repository
|
||
if not install_mariadb_repo(dist_info):
|
||
printc("Using system MariaDB repository", col.WARNING)
|
||
|
||
printc("Updating system", col.OKBLUE)
|
||
run_command("yum update -y")
|
||
|
||
# Install system packages from PACKAGES['redhat']
|
||
redhat_packages = PACKAGES.get('redhat', [])
|
||
if redhat_packages:
|
||
packages_str = " ".join(redhat_packages)
|
||
printc(f"Installing RedHat packages ({len(redhat_packages)} packages)...", col.OKBLUE)
|
||
run_command(f"yum install -y {packages_str} || echo 'Some packages may not be available'")
|
||
|
||
# Ensure libssh2 libraries are installed
|
||
printc("Verifying SSH2 libraries for RedHat...", col.OKBLUE)
|
||
run_command("yum install -y libssh2 libssh2-devel || true")
|
||
|
||
# Enable services
|
||
run_command("systemctl enable mariadb")
|
||
run_command("systemctl enable crond")
|
||
|
||
# Fix SSH2 library issue
|
||
printc("Configuring SSH2 libraries", col.OKBLUE)
|
||
fix_ssh2_library_issue()
|
||
else:
|
||
printc(f"Unsupported distribution family: {dist_info['family']}", col.FAIL)
|
||
sys.exit(1)
|
||
|
||
# Create user if doesn't exist (like bash script)
|
||
printc("Creating/verifying xc_vm user", col.OKBLUE)
|
||
try:
|
||
ret, out, err = run_command("getent passwd xc_vm", capture_output=True)
|
||
if ret == 0:
|
||
printc("User xc_vm already exists", col.OKGREEN)
|
||
else:
|
||
raise Exception("User not found")
|
||
except:
|
||
printc("Creating user xc_vm", col.OKBLUE)
|
||
if dist_info['family'] == 'debian':
|
||
run_command("adduser --system --shell /bin/false --no-create-home --home /nonexistent --group --disabled-login xc_vm")
|
||
else: # redhat
|
||
run_command("groupadd -r xc_vm")
|
||
run_command("useradd -r -g xc_vm -s /bin/false -M -d /nonexistent xc_vm")
|
||
|
||
if not os.path.exists("/home/xc_vm"):
|
||
os.makedirs("/home/xc_vm", exist_ok=True)
|
||
run_command("chown xc_vm:xc_vm /home/xc_vm")
|
||
|
||
##################################################
|
||
# INSTALL XC_VM #
|
||
##################################################
|
||
|
||
printc("Installing XC_VM", col.OKBLUE)
|
||
|
||
# Extract the archive
|
||
if not extract_archive(archive_path):
|
||
printc("Failed to extract archive! Exiting", col.FAIL)
|
||
sys.exit(1)
|
||
|
||
# Verify extraction
|
||
if not os.path.exists("/home/xc_vm/status"):
|
||
printc("Extraction failed: /home/xc_vm/status not found", col.FAIL)
|
||
sys.exit(1)
|
||
else:
|
||
printc("XC_VM extracted successfully", col.OKGREEN)
|
||
|
||
# Instalar binarios específicos para distribuciones soportadas
|
||
dist_id = dist_info['id']
|
||
version = dist_info['version']
|
||
|
||
# Verificar si la distribución tiene parches disponibles
|
||
if dist_id in ['ubuntu', 'debian']:
|
||
# Ubuntu: 20, 22, 24
|
||
if dist_id == 'ubuntu' and any(version.startswith(v) for v in ['20', '22', '24']):
|
||
if not install_distribution_binaries(dist_id, version):
|
||
printc(f"Warning: Failed to install {dist_id} {version} specific binaries", col.WARNING)
|
||
|
||
# Debian: 11, 12, 13
|
||
elif dist_id == 'debian' and any(version.startswith(v) for v in ['11', '12', '13']):
|
||
if not install_distribution_binaries(dist_id, version):
|
||
printc(f"Warning: Failed to install {dist_id} {version} specific binaries", col.WARNING)
|
||
|
||
else:
|
||
printc(f"No specific patches available for {dist_id} {version}, using default binaries", col.OKBLUE)
|
||
|
||
elif dist_id in ['rocky', 'almalinux', 'rhel', 'centos']:
|
||
major = version.split('.')[0]
|
||
if major in ['8', '9']:
|
||
if not install_distribution_binaries(dist_id, version):
|
||
printc(f"Warning: Failed to install {dist_id} {version} specific binaries", col.WARNING)
|
||
else:
|
||
printc(f"No specific patches available for {dist_id} {version}, using default binaries", col.OKBLUE)
|
||
else:
|
||
printc(f"No patches available for {dist_id} {version}, using default binaries", col.OKBLUE)
|
||
|
||
##################################################
|
||
# MariaDB CONFIGURATION #
|
||
##################################################
|
||
|
||
printc("Configuring MariaDB", col.OKBLUE)
|
||
|
||
# Secure MariaDB installation (using bash script logic)
|
||
secure_mariadb_installation(root_password, dist_info)
|
||
|
||
# Get total system RAM and generate config
|
||
total_ram_mb = get_system_ram_mb()
|
||
rMySQLCnf = generate_mysql_config(total_ram_mb)
|
||
|
||
# Write MySQL configuration (like bash script)
|
||
printc("Writing MySQL performance configuration", col.OKBLUE)
|
||
if dist_info['family'] == 'debian':
|
||
mysql_conf_path = "/etc/mysql/mariadb.conf.d/50-server.cnf"
|
||
else:
|
||
mysql_conf_path = "/etc/my.cnf.d/server.cnf"
|
||
|
||
# Ensure directory exists
|
||
os.makedirs(os.path.dirname(mysql_conf_path), exist_ok=True)
|
||
|
||
with io.open(mysql_conf_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(rMySQLCnf)
|
||
|
||
# Restart MariaDB
|
||
run_command("systemctl restart mariadb")
|
||
time.sleep(5)
|
||
|
||
# Connect to MariaDB and configure databases
|
||
printc("Setting up databases and users", col.OKBLUE)
|
||
|
||
# Create databases
|
||
run_command(f'mariadb -u root -p"{root_password}" -e "CREATE DATABASE IF NOT EXISTS xc_vm; CREATE DATABASE IF NOT EXISTS xc_vm_migrate;"')
|
||
|
||
# Import database schema
|
||
printc("Importing database schema", col.OKBLUE)
|
||
db_schema_path = "/home/xc_vm/bin/install/database.sql"
|
||
if os.path.exists(db_schema_path):
|
||
run_command(f'mariadb -u root -p"{root_password}" xc_vm < "{db_schema_path}"')
|
||
else:
|
||
printc(f"Database schema not found at {db_schema_path}", col.WARNING)
|
||
|
||
# Create XC_VM user with all privileges (like bash script)
|
||
printc("Creating database user", col.OKBLUE)
|
||
|
||
# Localhost grants
|
||
commands_localhost = [
|
||
f"CREATE USER IF NOT EXISTS '{rUsername}'@'localhost' IDENTIFIED BY '{rPassword}';",
|
||
f"GRANT ALL PRIVILEGES ON xc_vm.* TO '{rUsername}'@'localhost';",
|
||
f"GRANT ALL PRIVILEGES ON xc_vm_migrate.* TO '{rUsername}'@'localhost';",
|
||
f"GRANT ALL PRIVILEGES ON mysql.* TO '{rUsername}'@'localhost';",
|
||
f"GRANT GRANT OPTION ON xc_vm.* TO '{rUsername}'@'localhost';"
|
||
]
|
||
|
||
# 127.0.0.1 grants (REQUIRED for startup.php - like bash script)
|
||
commands_127 = [
|
||
f"CREATE USER IF NOT EXISTS '{rUsername}'@'127.0.0.1' IDENTIFIED BY '{rPassword}';",
|
||
f"GRANT ALL PRIVILEGES ON xc_vm.* TO '{rUsername}'@'127.0.0.1';",
|
||
f"GRANT ALL PRIVILEGES ON xc_vm_migrate.* TO '{rUsername}'@'127.0.0.1';",
|
||
f"GRANT ALL PRIVILEGES ON mysql.* TO '{rUsername}'@'127.0.0.1';",
|
||
f"GRANT GRANT OPTION ON xc_vm.* TO '{rUsername}'@'127.0.0.1';",
|
||
"FLUSH PRIVILEGES;"
|
||
]
|
||
|
||
all_commands = commands_localhost + commands_127
|
||
|
||
for cmd in all_commands:
|
||
run_command(f'mariadb -u root -p"{root_password}" -e "{cmd}"')
|
||
|
||
# Write XC_VM configuration
|
||
printc("Writing XC_VM configuration", col.OKBLUE)
|
||
os.makedirs(os.path.dirname(rConfigPath), exist_ok=True)
|
||
rConfigData = rConfig % (rUsername, rPassword)
|
||
with io.open(rConfigPath, "w", encoding="utf-8") as rFile:
|
||
rFile.write(rConfigData)
|
||
|
||
printc("MariaDB configuration completed", col.OKGREEN)
|
||
|
||
##################################################
|
||
# SYSTEM CONFIGURATION #
|
||
##################################################
|
||
|
||
printc("Configuring System", col.OKBLUE)
|
||
|
||
# Configure tmpfs mounts (like bash script)
|
||
if not os.path.exists("/etc/fstab"):
|
||
printc("/etc/fstab not found", col.WARNING)
|
||
else:
|
||
try:
|
||
with open("/etc/fstab", "r") as f:
|
||
fstab_content = f.read()
|
||
|
||
if "/home/xc_vm/" not in fstab_content:
|
||
printc("Adding tmpfs mounts to /etc/fstab", col.OKBLUE)
|
||
# Create directories first
|
||
run_command("mkdir -p /home/xc_vm/content/streams")
|
||
run_command("mkdir -p /home/xc_vm/tmp")
|
||
|
||
with io.open("/etc/fstab", "a", encoding="utf-8") as rFile:
|
||
rFile.write("\ntmpfs /home/xc_vm/content/streams tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=90% 0 0\ntmpfs /home/xc_vm/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=6G 0 0")
|
||
|
||
# Mount immediately
|
||
run_command("mount -a")
|
||
printc("Reloading systemd to recognize fstab changes", col.OKBLUE)
|
||
run_command("systemctl daemon-reload")
|
||
except Exception as e:
|
||
printc(f"Error updating /etc/fstab: {e}", col.WARNING)
|
||
|
||
# Remove any restrictive sudoers rules (like bash script)
|
||
sudoers_file = "/etc/sudoers.d/xc_vm"
|
||
if os.path.exists(sudoers_file):
|
||
run_command(f"rm -f {sudoers_file}")
|
||
|
||
# Configure HTTP/HTTPS ports (interactive like bash script)
|
||
printc("Port Configuration", col.OKBLUE)
|
||
print("If you want to change the ports, enter new values, or leave empty to use the default ports")
|
||
|
||
while True:
|
||
http_port = input("HTTP port (default 80): ").strip()
|
||
if not http_port:
|
||
http_port = "80"
|
||
break
|
||
if http_port.isdigit() and 1 <= int(http_port) <= 65535:
|
||
break
|
||
printc("Error: port must be a number between 1 and 65535", col.FAIL)
|
||
|
||
while True:
|
||
https_port = input("HTTPS port (default 443): ").strip()
|
||
if not https_port:
|
||
https_port = "443"
|
||
break
|
||
if https_port.isdigit() and 1 <= int(https_port) <= 65535:
|
||
break
|
||
printc("Error: port must be a number between 1 and 65535", col.FAIL)
|
||
|
||
# Write HTTP ports configuration
|
||
http_conf_path = "/home/xc_vm/bin/nginx/conf/ports/http.conf"
|
||
os.makedirs(os.path.dirname(http_conf_path), exist_ok=True)
|
||
with io.open(http_conf_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(f"listen {http_port};")
|
||
|
||
# Write HTTPS ports configuration
|
||
https_conf_path = "/home/xc_vm/bin/nginx/conf/ports/https.conf"
|
||
os.makedirs(os.path.dirname(https_conf_path), exist_ok=True)
|
||
with io.open(https_conf_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(f"listen {https_port} ssl;")
|
||
|
||
printc(f"Ports configured: HTTP - {http_port}, HTTPS - {https_port}", col.OKGREEN)
|
||
|
||
# Configure sysctl (like bash script)
|
||
printc("Configuring kernel parameters (sysctl)", col.OKBLUE)
|
||
with io.open("/etc/sysctl.conf", "w", encoding="utf-8") as rFile:
|
||
rFile.write(rSysCtl)
|
||
run_command("sysctl -p > /dev/null 2>&1")
|
||
|
||
# Configure systemd limits (like bash script)
|
||
printc("Configuring systemd file limits", col.OKBLUE)
|
||
systemd_conf = "/etc/systemd/system.conf"
|
||
if os.path.exists(systemd_conf):
|
||
with open(systemd_conf, "r") as f:
|
||
systemd_content = f.read()
|
||
|
||
if "DefaultLimitNOFILE=655350" not in systemd_content:
|
||
with open(systemd_conf, "a") as f:
|
||
f.write("\nDefaultLimitNOFILE=655350\n")
|
||
|
||
user_conf = "/etc/systemd/user.conf"
|
||
if os.path.exists(user_conf):
|
||
with open(user_conf, "r") as f:
|
||
user_content = f.read()
|
||
|
||
if "DefaultLimitNOFILE=655350" not in user_content:
|
||
with open(user_conf, "a") as f:
|
||
f.write("\nDefaultLimitNOFILE=655350\n")
|
||
|
||
# Ask for systemd service (like bash script)
|
||
printc("Systemd Service Configuration", col.OKBLUE)
|
||
while True:
|
||
enable_systemd = input("Do you want to configure and enable XC_VM as a systemd service? (Y/N): ").strip().upper()
|
||
if enable_systemd in ["Y", "N"]:
|
||
break
|
||
|
||
if enable_systemd == "Y":
|
||
printc("Configuring systemd service", col.OKBLUE)
|
||
service_path = "/etc/systemd/system/xc_vm.service"
|
||
with io.open(service_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(rSystemd)
|
||
|
||
run_command("chmod +x /etc/systemd/system/xc_vm.service")
|
||
run_command("systemctl daemon-reload")
|
||
run_command("systemctl enable xc_vm")
|
||
run_command("systemctl start xc_vm")
|
||
printc("Systemd service configured and started", col.OKGREEN)
|
||
|
||
# Configure Redis
|
||
printc("Configuring Redis", col.OKBLUE)
|
||
redis_conf_path = "/home/xc_vm/bin/redis/redis.conf"
|
||
os.makedirs(os.path.dirname(redis_conf_path), exist_ok=True)
|
||
if not os.path.exists(redis_conf_path):
|
||
with io.open(redis_conf_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(rRedisConfig)
|
||
printc("Redis configuration created", col.OKGREEN)
|
||
else:
|
||
printc("Redis configuration already exists", col.OKGREEN)
|
||
|
||
##################################################
|
||
# ACCESS CODE #
|
||
##################################################
|
||
|
||
printc("Generating access code", col.OKBLUE)
|
||
rCodeDir = "/home/xc_vm/bin/nginx/conf/codes/"
|
||
|
||
# CORREGIDO: Asegurar que el directorio de códigos exista
|
||
os.makedirs(rCodeDir, exist_ok=True)
|
||
|
||
admin_code = None
|
||
|
||
if os.path.exists(rCodeDir):
|
||
for filename in os.listdir(rCodeDir):
|
||
if filename.endswith(".conf"):
|
||
filepath = os.path.join(rCodeDir, filename)
|
||
if filename.split(".")[0] == "setup":
|
||
os.remove(filepath)
|
||
else:
|
||
try:
|
||
with open(filepath, "r") as f:
|
||
content = f.read()
|
||
if "/home/xc_vm/admin" in content:
|
||
admin_code = filename.split(".")[0]
|
||
break
|
||
except:
|
||
pass
|
||
|
||
if not admin_code:
|
||
admin_code = generate_random_password(8)
|
||
printc(f"Generated access code: {admin_code}", col.OKGREEN)
|
||
|
||
# Insert into database
|
||
insert_cmd = f'mariadb -u root -p"{root_password}" -e "USE xc_vm; INSERT INTO access_codes(code, type, enabled, groups) VALUES(\'{admin_code}\', 0, 1, \'[1]\');"'
|
||
run_command(insert_cmd)
|
||
|
||
# Create nginx configuration
|
||
template_path = os.path.join(rCodeDir, "template")
|
||
if os.path.exists(template_path):
|
||
with open(template_path, "r") as f:
|
||
template_content = f.read()
|
||
|
||
# Replace placeholders (like bash script)
|
||
template_content = template_content.replace("#WHITELIST#", "")
|
||
template_content = template_content.replace("#TYPE#", "admin")
|
||
template_content = template_content.replace("#CODE#", admin_code)
|
||
template_content = template_content.replace("#BURST#", "500")
|
||
|
||
code_conf_path = os.path.join(rCodeDir, f"{admin_code}.conf")
|
||
with io.open(code_conf_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(template_content)
|
||
printc(f"Access code configuration created: {admin_code}.conf", col.OKGREEN)
|
||
else:
|
||
printc("Template file not found, creating basic configuration", col.WARNING)
|
||
# Fallback configuration (like bash script)
|
||
fallback_config = f"location /{admin_code} {{ include /home/xc_vm/bin/nginx/conf/proxy.conf; proxy_pass http://127.0.0.1:8080/admin; }}"
|
||
code_conf_path = os.path.join(rCodeDir, f"{admin_code}.conf")
|
||
with io.open(code_conf_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(fallback_config)
|
||
else:
|
||
printc(f"Using existing access code: {admin_code}", col.OKGREEN)
|
||
|
||
##################################################
|
||
# FINAL CONFIGURATION #
|
||
##################################################
|
||
|
||
printc("Finalizing installation", col.OKBLUE)
|
||
|
||
# Set permissions (like bash script)
|
||
run_command("chown -R xc_vm:xc_vm /home/xc_vm")
|
||
|
||
# Set executable permissions on key files
|
||
run_command("chmod +x /home/xc_vm/service")
|
||
run_command("chmod +x /home/xc_vm/status")
|
||
run_command("chmod +x /home/xc_vm/bin/nginx/sbin/nginx")
|
||
|
||
# Check for RTMP nginx
|
||
nginx_rtmp_path = "/home/xc_vm/bin/nginx_rtmp/sbin/nginx_rtmp"
|
||
if os.path.exists(nginx_rtmp_path):
|
||
run_command(f"chmod +x {nginx_rtmp_path}")
|
||
|
||
# Set capabilities for binding to privileged ports (like bash script)
|
||
nginx_bin = "/home/xc_vm/bin/nginx/sbin/nginx"
|
||
if os.path.exists(nginx_bin):
|
||
run_command(f"setcap 'cap_net_bind_service=+ep' {nginx_bin} 2>/dev/null || true")
|
||
|
||
if os.path.exists(nginx_rtmp_path):
|
||
run_command(f"setcap 'cap_net_bind_service=+ep' {nginx_rtmp_path} 2>/dev/null || true")
|
||
|
||
# Save credentials in the format you requested
|
||
printc("Saving credentials", col.OKBLUE)
|
||
|
||
# Save to /root/credentials.txt
|
||
with io.open("/root/credentials.txt", "w", encoding="utf-8") as rFile:
|
||
rFile.write("MariaDB Root \n")
|
||
rFile.write(f"Username: root\n")
|
||
rFile.write(f"Password: {root_password}\n\n")
|
||
rFile.write(f"XC_VM Username: {rUsername}\n")
|
||
rFile.write(f"XC_VM Password: {rPassword}\n")
|
||
rFile.write(f"Database: {rDatabase}\n")
|
||
|
||
# Also save to installer directory (like original install script)
|
||
local_creds_path = os.path.join(rPath, "credentials.txt")
|
||
with io.open(local_creds_path, "w", encoding="utf-8") as rFile:
|
||
rFile.write(f"MariaDB Root Password: {root_password}\n")
|
||
rFile.write(f"MariaDB Username: {rUsername}\n")
|
||
rFile.write(f"MariaDB Password: {rPassword}\n")
|
||
rFile.write(f"Database: {rDatabase}\n")
|
||
rFile.write(f"Admin Access Code: {admin_code}\n")
|
||
|
||
# Remove the old mariadb_root_password.txt file if it exists
|
||
if os.path.exists("/root/mariadb_root_password.txt"):
|
||
os.remove("/root/mariadb_root_password.txt")
|
||
|
||
printc("Credentials saved to /root/credentials.txt", col.OKGREEN)
|
||
printc(f"Credentials also saved to {local_creds_path}", col.OKGREEN)
|
||
|
||
# Restart service if systemd was enabled
|
||
if enable_systemd == "Y":
|
||
run_command("systemctl restart xc_vm")
|
||
|
||
# Mount tmpfs filesystems (like original install script)
|
||
run_command("mount -a >/dev/null 2>&1 || true")
|
||
|
||
# Reload systemd daemon
|
||
run_command("systemctl daemon-reload")
|
||
|
||
# Post-install startup (like bash script)
|
||
printc("Starting XC_VM processes...", col.OKBLUE)
|
||
time.sleep(10)
|
||
|
||
# Run status command
|
||
if os.path.exists("/home/xc_vm/status"):
|
||
run_command("/home/xc_vm/status 1")
|
||
|
||
# Set config permissions
|
||
run_command("chown -R xc_vm:xc_vm /home/xc_vm/config/")
|
||
|
||
# Run startup command via console.php
|
||
startup_cmd = "/home/xc_vm/console.php"
|
||
if os.path.exists(startup_cmd):
|
||
run_command(f"/home/xc_vm/bin/php/bin/php {startup_cmd} startup >/dev/null 2>&1")
|
||
|
||
time.sleep(3)
|
||
|
||
# Final restart
|
||
if enable_systemd == "Y":
|
||
run_command("systemctl restart xc_vm")
|
||
|
||
# Get server IP
|
||
server_ip = getIP()
|
||
|
||
##################################################
|
||
# FINISHED - SHOW SUMMARY #
|
||
##################################################
|
||
|
||
printc("=" * 60, col.OKGREEN)
|
||
printc("INSTALLATION COMPLETED SUCCESSFULLY!", col.OKGREEN, 1)
|
||
|
||
printc(f"Distribution: {dist_info['id']} {dist_info['version']}", col.OKGREEN)
|
||
printc(f"Continue Setup: http://{server_ip}:{http_port}/{admin_code}", col.OKBLUE)
|
||
printc(f"Total RAM: {total_ram_mb}MB", col.OKGREEN)
|
||
|
||
printc("Credentials have been saved to:", col.OKBLUE)
|
||
printc("/root/credentials.txt", col.OKGREEN)
|
||
printc(f"{local_creds_path}", col.OKGREEN)
|
||
|
||
printc("IMPORTANT: Move the credentials file to a secure location!", col.WARNING)
|
||
|
||
printc("=" * 60, col.OKGREEN)
|