diff --git a/install/install.py b/install/install.py index 327cc34dc..a8de9aeb5 100644 --- a/install/install.py +++ b/install/install.py @@ -14,112 +14,42 @@ from os.path import * from stat import * import stat import secrets +import install_utils VERSION = '2.4' BUILD = 2 -char_set = {'small': 'abcdefghijklmnopqrstuvwxyz', 'nums': '0123456789', 'big': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'} +# Using shared char_set from install_utils +char_set = install_utils.char_set -def generate_pass(length=14): - chars = string.ascii_uppercase + string.ascii_lowercase + string.digits - size = length - return ''.join(random.choice(chars) for x in range(size)) +# Using shared function from install_utils +generate_pass = install_utils.generate_pass # There can not be peace without first a great suffering. -# distros +# distros - using from install_utils +centos = install_utils.centos +ubuntu = install_utils.ubuntu +cent8 = install_utils.cent8 +openeuler = install_utils.openeuler +cent9 = 4 # Not in install_utils yet +CloudLinux8 = 0 # Not in install_utils yet -centos = 0 -ubuntu = 1 -cent8 = 2 -cent9 = 4 -openeuler = 3 -CloudLinux8 = 0 - -def FetchCloudLinuxAlmaVersionVersion(): - if os.path.exists('/etc/os-release'): - data = open('/etc/os-release', 'r').read() - if (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('8.9') > -1 or data.find('Anatoly Levchenko') > -1 or data.find('VERSION="8.') > -1): - return 'cl-89' - elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('8.8') > -1 or data.find('Anatoly Filipchenko') > -1): - return 'cl-88' - elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('9.4') > -1 or data.find('VERSION="9.') > -1): - return 'cl-88' - elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('8.9') > -1 or data.find('Midnight Oncilla') > -1 or data.find('VERSION="8.') > -1): - return 'al-88' - elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('8.7') > -1 or data.find('Stone Smilodon') > -1): - return 'al-87' - elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('9.4') > -1 or data.find('9.3') > -1 or data.find('Shamrock Pampas') > -1 or data.find('Seafoam Ocelot') > -1 or data.find('VERSION="9.') > -1): - return 'al-93' - else: - return -1 +# Using shared function from install_utils +FetchCloudLinuxAlmaVersionVersion = install_utils.FetchCloudLinuxAlmaVersionVersion -def get_distro(): - distro = -1 - distro_file = "" - if exists("/etc/lsb-release"): - distro_file = "/etc/lsb-release" - with open(distro_file) as f: - for line in f: - if line == "DISTRIB_ID=Ubuntu\n": - distro = ubuntu - - elif exists("/etc/redhat-release"): - distro_file = "/etc/redhat-release" - distro = centos - - data = open('/etc/redhat-release', 'r').read() - - - if data.find('CentOS Linux release 8') > -1: - return cent8 - ## if almalinux 9 then pretty much same as cent8 - if data.find('AlmaLinux release 8') > -1 or data.find('AlmaLinux release 9') > -1: - return cent8 - if data.find('Rocky Linux release 8') > -1 or data.find('Rocky Linux 8') > -1 or data.find('rocky:8') > -1: - return cent8 - if data.find('CloudLinux 8') or data.find('cloudlinux 8'): - return cent8 - - else: - if exists("/etc/openEuler-release"): - distro_file = "/etc/openEuler-release" - distro = openeuler - - else: - logging.InstallLog.writeToFile("Can't find linux release file - fatal error") - preFlightsChecks.stdOut("Can't find linux release file - fatal error") - os._exit(os.EX_UNAVAILABLE) - - if distro == -1: - logging.InstallLog.writeToFile("Can't find distro name in " + distro_file + " - fatal error") - preFlightsChecks.stdOut("Can't find distro name in " + distro_file + " - fatal error") - os._exit(os.EX_UNAVAILABLE) - - return distro +# Using shared function from install_utils +get_distro = install_utils.get_distro def get_Ubuntu_release(): - release = -1 - if exists("/etc/lsb-release"): - distro_file = "/etc/lsb-release" - with open(distro_file) as f: - for line in f: - if line[:16] == "DISTRIB_RELEASE=": - release = float(line[16:]) - - if release == -1: - preFlightsChecks.stdOut("Can't find distro release name in " + distro_file + " - fatal error", 1, 1, - os.EX_UNAVAILABLE) - - else: - logging.InstallLog.writeToFile("Can't find linux release file - fatal error") - preFlightsChecks.stdOut("Can't find linux release file - fatal error") - os._exit(os.EX_UNAVAILABLE) - + release = install_utils.get_Ubuntu_release(use_print=False, exit_on_error=True) + if release == -1: + preFlightsChecks.stdOut("Can't find distro release name in /etc/lsb-release - fatal error", 1, 1, + os.EX_UNAVAILABLE) return release @@ -132,15 +62,7 @@ class preFlightsChecks: def install_package(self, package_name, options="", silent=False): """Unified package installation across distributions""" - if self.distro == ubuntu: - command = f"DEBIAN_FRONTEND=noninteractive apt-get -y install {package_name} {options}" - shell = True - elif self.distro == centos: - command = f"yum install -y {package_name} {options}" - shell = False - else: # cent8, openeuler - command = f"dnf install -y {package_name} {options}" - shell = False + command, shell = install_utils.get_package_install_command(self.distro, package_name, options) if not silent: return preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, shell) @@ -158,15 +80,7 @@ class preFlightsChecks: def remove_package(self, package_name, silent=False): """Unified package removal across distributions""" - if self.distro == ubuntu: - command = f"DEBIAN_FRONTEND=noninteractive apt-get -y remove {package_name}" - shell = True - elif self.distro == centos: - command = f"yum remove -y {package_name}" - shell = False - else: # cent8, openeuler - command = f"dnf remove -y {package_name}" - shell = False + command, shell = install_utils.get_package_remove_command(self.distro, package_name) if not silent: return preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, shell) @@ -371,18 +285,7 @@ class preFlightsChecks: @staticmethod def stdOut(message, log=0, do_exit=0, code=os.EX_OK): - print("\n\n") - print(("[" + time.strftime( - "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) - print(("[" + time.strftime("%m.%d.%Y_%H-%M-%S") + "] " + message + "\n")) - print(("[" + time.strftime( - "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) - - if log: - logging.InstallLog.writeToFile(message) - if do_exit: - logging.InstallLog.writeToFile(message) - sys.exit(code) + install_utils.stdOut(message, log, do_exit, code) def mountTemp(self): try: @@ -471,42 +374,15 @@ class preFlightsChecks: return 'pure-ftpd-mysql' return 'pure-ftpd' + # Using shared function from install_utils @staticmethod def resFailed(distro, res): - if distro == ubuntu and res != 0: - return True - elif distro == centos and res != 0: - return True - return False + return install_utils.resFailed(distro, res) + # Using shared function from install_utils @staticmethod def call(command, distro, bracket, message, log=0, do_exit=0, code=os.EX_OK, shell=False): - finalMessage = 'Running: %s' % (message) - preFlightsChecks.stdOut(finalMessage, log) - count = 0 - while True: - if shell == False: - res = subprocess.call(shlex.split(command)) - else: - res = subprocess.call(command, shell=True) - - if preFlightsChecks.resFailed(distro, res): - count = count + 1 - finalMessage = 'Running %s failed. Running again, try number %s' % (message, str(count)) - preFlightsChecks.stdOut(finalMessage) - if count == 3: - fatal_message = '' - if do_exit: - fatal_message = '. Fatal error, see /var/log/installLogs.txt for full details' - - preFlightsChecks.stdOut("[ERROR] We are not able to run " + message + ' return code: ' + str(res) + - fatal_message + ".", 1, do_exit, code) - return False - else: - preFlightsChecks.stdOut('Successfully ran: %s.' % (message), log) - break - - return True + return install_utils.call(command, distro, bracket, message, log, do_exit, code, shell) def checkIfSeLinuxDisabled(self): try: @@ -994,7 +870,7 @@ password="%s" ## Write secret phrase - rString = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(32)]) + rString = install_utils.generate_random_string(32) data = open('/usr/local/CyberCP/public/phpmyadmin/config.sample.inc.php', 'r').readlines() @@ -1653,7 +1529,7 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout'; ###################################################### Email setup ends! def reStartLiteSpeed(self): - command = '%sbin/lswsctrl restart' % (self.server_root_path) + command = install_utils.format_restart_litespeed_command(self.server_root_path) preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) def removeUfw(self): @@ -2793,8 +2669,6 @@ admin_password = "12345" """) writeToFile.close() - import randomPassword - content = """SetPassword('%s'); echo $oConfig->Save() ? 'Done' : 'Error'; -?>""" % (randomPassword.generate_pass()) +?>""" % (generate_pass()) writeToFile = open('/usr/local/CyberCP/public/snappymail.php', 'w') writeToFile.write(content) diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py index 38b9ef252..dc68b8d12 100644 --- a/install/installCyberPanel.py +++ b/install/installCyberPanel.py @@ -3,57 +3,26 @@ import subprocess import os from mysqlUtilities import mysqlUtilities import installLog as logging -import randomPassword import errno import MySQLdb as mariadb import install from os.path import exists import time +import install_utils -# distros -centos = 0 -ubuntu = 1 -cent8 = 2 -openeuler = 3 +# distros - using from install_utils +centos = install_utils.centos +ubuntu = install_utils.ubuntu +cent8 = install_utils.cent8 +openeuler = install_utils.openeuler def get_Ubuntu_release(): - release = -1 - if exists("/etc/lsb-release"): - distro_file = "/etc/lsb-release" - with open(distro_file) as f: - for line in f: - if line[:16] == "DISTRIB_RELEASE=": - release = float(line[16:]) - - if release == -1: - print("Can't find distro release name in " + distro_file + " - fatal error") - - else: - logging.InstallLog.writeToFile("Can't find linux release file - fatal error") - print("Can't find linux release file - fatal error") - os._exit(os.EX_UNAVAILABLE) - - return release + return install_utils.get_Ubuntu_release(use_print=True, exit_on_error=True) -def FetchCloudLinuxAlmaVersionVersion(): - if os.path.exists('/etc/os-release'): - data = open('/etc/os-release', 'r').read() - if (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('8.9') > -1 or data.find('Anatoly Levchenko') > -1 or data.find('VERSION="8.') > -1): - return 'cl-89' - elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('8.8') > -1 or data.find('Anatoly Filipchenko') > -1): - return 'cl-88' - elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('9.4') > -1 or data.find('VERSION="9.') > -1): - return 'cl-88' - elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('8.9') > -1 or data.find('Midnight Oncilla') > -1 or data.find('VERSION="8.') > -1): - return 'al-88' - elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('8.7') > -1 or data.find('Stone Smilodon') > -1): - return 'al-87' - elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('9.4') > -1 or data.find('9.3') > -1 or data.find('Shamrock Pampas') > -1 or data.find('Seafoam Ocelot') > -1 or data.find('VERSION="9.') > -1): - return 'al-93' - else: - return -1 +# Using shared function from install_utils +FetchCloudLinuxAlmaVersionVersion = install_utils.FetchCloudLinuxAlmaVersionVersion class InstallCyberPanel: mysql_Root_password = "" @@ -62,14 +31,14 @@ class InstallCyberPanel: def install_package(self, package_name, options=""): """Unified package installation across distributions""" + command, shell = install_utils.get_package_install_command(self.distro, package_name, options) + + # InstallCyberPanel always uses verbose mode (no silent option) if self.distro == ubuntu: - command = f"DEBIAN_FRONTEND=noninteractive apt-get -y install {package_name} {options}" - return install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - elif self.distro == centos: - command = f"yum install -y {package_name} {options}" - else: # cent8, openeuler - command = f"dnf install -y {package_name} {options}" - return install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, shell) + else: + # For non-Ubuntu, original code didn't pass shell parameter + return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) def manage_service(self, service_name, action="start"): """Unified service management""" @@ -81,7 +50,7 @@ class InstallCyberPanel: actual_service = service_map.get(service_name, service_name) command = f'systemctl {action} {actual_service}' - return install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) def modify_file_content(self, file_path, replacements): """Generic file content modification""" @@ -184,7 +153,7 @@ class InstallCyberPanel: @staticmethod def stdOut(message, log=0, exit=0, code=os.EX_OK): - install.preFlightsChecks.stdOut(message, log, exit, code) + install_utils.stdOut(message, log, exit, code) def installLiteSpeed(self): if self.ent == 0: @@ -194,13 +163,13 @@ class InstallCyberPanel: try: try: command = 'groupadd nobody' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) except: pass try: command = 'usermod -a -G nobody nobody' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) except: pass @@ -209,20 +178,20 @@ class InstallCyberPanel: else: command = 'wget https://www.litespeedtech.com/packages/6.0/lsws-6.2-ent-x86_64-linux.tar.gz' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) if InstallCyberPanel.ISARM(): command = 'tar zxf lsws-6.2-ent-aarch64-linux.tar.gz' else: command = 'tar zxf lsws-6.2-ent-x86_64-linux.tar.gz' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) if str.lower(self.serial) == 'trial': command = 'wget -q --output-document=lsws-6.2/trial.key http://license.litespeedtech.com/reseller/trial.key' if self.serial == '1111-2222-3333-4444': command = 'wget -q --output-document=/root/cyberpanel/install/lsws-6.2/trial.key http://license.litespeedtech.com/reseller/trial.key' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) else: writeSerial = open('lsws-6.2/serial.no', 'w') writeSerial.writelines(self.serial) @@ -234,13 +203,13 @@ class InstallCyberPanel: os.chdir('lsws-6.2') command = 'chmod +x install.sh' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = 'chmod +x functions.sh' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = './install.sh' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) os.chdir(self.cwd) confPath = '/usr/local/lsws/conf/' @@ -249,7 +218,7 @@ class InstallCyberPanel: shutil.copy('litespeed/httpd.conf', confPath) command = 'chown -R lsadm:lsadm ' + confPath - install.preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) except BaseException as msg: logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installLiteSpeed]") @@ -258,8 +227,8 @@ class InstallCyberPanel: return 1 def reStartLiteSpeed(self): - command = self.server_root_path + "bin/lswsctrl restart" - install.preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + command = install_utils.format_restart_litespeed_command(self.server_root_path) + install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) def fix_ols_configs(self): try: @@ -321,7 +290,7 @@ class InstallCyberPanel: elif self.distro == centos: # First install the group command = 'yum -y groupinstall lsphp-all' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) InstallCyberPanel.stdOut("LiteSpeed PHPs successfully installed!", 1) @@ -356,13 +325,13 @@ class InstallCyberPanel: if self.distro == ubuntu: command = 'DEBIAN_FRONTEND=noninteractive apt-get install software-properties-common apt-transport-https curl -y' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = "mkdir -p /etc/apt/keyrings" - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = "curl -o /etc/apt/keyrings/mariadb-keyring.pgp 'https://mariadb.org/mariadb_release_signing_key.pgp'" - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) RepoPath = '/etc/apt/sources.list.d/mariadb.sources' RepoContent = f""" # MariaDB 10.11 repository list - created 2023-12-11 07:53 UTC @@ -379,7 +348,7 @@ Signed-By: /etc/apt/keyrings/mariadb-keyring.pgp if get_Ubuntu_release() > 21.00: command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # WriteToFile = open(RepoPath, 'w') # WriteToFile.write(RepoContent) # WriteToFile.close() @@ -387,7 +356,7 @@ Signed-By: /etc/apt/keyrings/mariadb-keyring.pgp command = 'DEBIAN_FRONTEND=noninteractive apt-get update -y' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = "DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server -y" @@ -417,34 +386,34 @@ gpgcheck=1 if type == 'cl' and version >= 88: command = 'yum remove db-governor db-governor-mysql -y' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = 'yum install governor-mysql -y' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = '/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=mariadb106' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = '/usr/share/lve/dbgovernor/mysqlgovernor.py --install --yes' else: command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = 'yum remove mariadb* -y' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = 'sudo dnf -qy module disable mariadb' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = 'sudo dnf module reset mariadb -y' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = 'dnf install MariaDB-server MariaDB-client MariaDB-backup -y' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) ############## Start mariadb ###################### @@ -461,7 +430,7 @@ gpgcheck=1 command = 'mariadb -u root -e "' + passwordCMD + '"' - install.preFlightsChecks.call(command, self.distro, command, command, 0, 0, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 0, 0, os.EX_OSERR) def startMariaDB(self): @@ -519,24 +488,24 @@ gpgcheck=1 ] for filename, wget_cmd in packages: - install.preFlightsChecks.call(wget_cmd, self.distro, wget_cmd, wget_cmd, 1, 1, os.EX_OSERR) + install_utils.call(wget_cmd, self.distro, wget_cmd, wget_cmd, 1, 1, os.EX_OSERR) command = f'dpkg --install --force-confold {filename}' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) else: self.install_package('pure-ftpd') ####### Install pureftpd to system startup command = "systemctl enable " + install.preFlightsChecks.pureFTPDServiceName(self.distro) - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) ###### FTP Groups and user settings settings command = 'groupadd -g 2001 ftpgroup' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = 'useradd -u 2001 -s /bin/false -d /bin/null -c "pureftpd user" -g ftpgroup ftpuser' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) def startPureFTPD(self): ############## Start pureftpd ###################### @@ -559,7 +528,7 @@ gpgcheck=1 else: command = 'openssl req -x509 -nodes -days 7300 -newkey rsa:2048 -subj "/C=US/ST=Denial/L=Sprinal-ield/O=Dis/CN=www.example.com" -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) os.chdir(self.cwd) ftpdPath = "/etc/pure-ftpd" @@ -591,13 +560,13 @@ gpgcheck=1 if self.remotemysql == 'ON': command = "sed -i 's|localhost|%s|g' %s" % (self.mysqlhost, ftpConfPath) - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = "sed -i 's|3306|%s|g' %s" % (self.mysqlport, ftpConfPath) - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = "sed -i 's|MYSQLSocket /var/lib/mysql/mysql.sock||g' %s" % (ftpConfPath) - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) if self.distro == ubuntu: @@ -623,13 +592,13 @@ gpgcheck=1 subprocess.call(command, shell=True) command = 'ln -s /etc/pure-ftpd/conf/MySQLConfigFile /etc/pure-ftpd/auth/30mysql' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = 'ln -s /etc/pure-ftpd/conf/UnixAuthentication /etc/pure-ftpd/auth/65unix' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = 'systemctl restart pure-ftpd-mysql.service' - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) @@ -638,10 +607,10 @@ gpgcheck=1 ### change mysql md5 to crypt command = "sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' /etc/pure-ftpd/db/mysql.conf" - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = "systemctl restart pure-ftpd-mysql.service" - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) else: try: @@ -651,7 +620,7 @@ gpgcheck=1 if type == 'al' and version >= 90: command = "sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' /etc/pure-ftpd/pureftpd-mysql.conf" - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) except: pass @@ -728,10 +697,10 @@ gpgcheck=1 if self.remotemysql == 'ON': command = "sed -i 's|gmysql-host=localhost|gmysql-host=%s|g' %s" % (self.mysqlhost, dnsPath) - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) command = "sed -i 's|gmysql-port=3306|gmysql-port=%s|g' %s" % (self.mysqlport, dnsPath) - install.preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) InstallCyberPanel.stdOut("PowerDNS configured!", 1) @@ -750,8 +719,8 @@ gpgcheck=1 def Main(cwd, mysql, distro, ent, serial=None, port="8090", ftp=None, dns=None, publicip=None, remotemysql=None, mysqlhost=None, mysqldb=None, mysqluser=None, mysqlpassword=None, mysqlport=None): - InstallCyberPanel.mysqlPassword = randomPassword.generate_pass() - InstallCyberPanel.mysql_Root_password = randomPassword.generate_pass() + InstallCyberPanel.mysqlPassword = install_utils.generate_pass() + InstallCyberPanel.mysql_Root_password = install_utils.generate_pass() file_name = '/etc/cyberpanel/mysqlPassword' @@ -778,18 +747,18 @@ def Main(cwd, mysql, distro, ent, serial=None, port="8090", ftp=None, dns=None, try: command = 'chmod 640 %s' % (file_name) - install.preFlightsChecks.call(command, distro, '[chmod]', + install_utils.call(command, distro, '[chmod]', '', 1, 0, os.EX_OSERR) command = 'chown root:cyberpanel %s' % (file_name) - install.preFlightsChecks.call(command, distro, '[chmod]', + install_utils.call(command, distro, '[chmod]', '', 1, 0, os.EX_OSERR) except: pass if distro == centos: - InstallCyberPanel.mysqlPassword = randomPassword.generate_pass() + InstallCyberPanel.mysqlPassword = install_utils.generate_pass() else: InstallCyberPanel.mysqlPassword = InstallCyberPanel.mysql_Root_password diff --git a/install/install_utils.py b/install/install_utils.py new file mode 100644 index 000000000..80f3a58c3 --- /dev/null +++ b/install/install_utils.py @@ -0,0 +1,384 @@ +#!/usr/bin/env python +""" +Common utility functions for CyberPanel installation scripts. +This module contains shared functions used by both install.py and installCyberPanel.py +""" + +import os +import sys +import time +import logging +import subprocess +import shlex +import secrets +import string +from os.path import exists + + +def FetchCloudLinuxAlmaVersionVersion(): + """ + Detect CloudLinux or AlmaLinux version by parsing /etc/os-release + Returns: version string or -1 if not found + """ + if os.path.exists('/etc/os-release'): + data = open('/etc/os-release', 'r').read() + if (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('8.9') > -1 or data.find('Anatoly Levchenko') > -1 or data.find('VERSION="8.') > -1): + return 'cl-89' + elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('8.8') > -1 or data.find('Anatoly Filipchenko') > -1): + return 'cl-88' + elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and (data.find('9.4') > -1 or data.find('VERSION="9.') > -1): + return 'cl-88' + elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('8.9') > -1 or data.find('Midnight Oncilla') > -1 or data.find('VERSION="8.') > -1): + return 'al-88' + elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('8.7') > -1 or data.find('Stone Smilodon') > -1): + return 'al-87' + elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and (data.find('9.4') > -1 or data.find('9.3') > -1 or data.find('Shamrock Pampas') > -1 or data.find('Seafoam Ocelot') > -1 or data.find('VERSION="9.') > -1): + return 'al-93' + else: + return -1 + + +def get_Ubuntu_release(use_print=False, exit_on_error=True): + """ + Get Ubuntu release version from /etc/lsb-release + + Args: + use_print: If True, use print() for errors, otherwise use the provided output function + exit_on_error: If True, exit on error + + Returns: float release number or -1 if not found + """ + release = -1 + if exists("/etc/lsb-release"): + distro_file = "/etc/lsb-release" + with open(distro_file) as f: + for line in f: + if line[:16] == "DISTRIB_RELEASE=": + release = float(line[16:]) + + if release == -1: + error_msg = "Can't find distro release name in " + distro_file + " - fatal error" + if use_print: + print(error_msg) + else: + # This will be overridden by the calling module + return -1 + + else: + error_msg = "Can't find linux release file - fatal error" + if hasattr(logging, 'InstallLog'): + logging.InstallLog.writeToFile(error_msg) + if use_print: + print(error_msg) + if exit_on_error: + os._exit(os.EX_UNAVAILABLE) + + return release + + +# ANSI color codes +class Colors: + HEADER = '\033[95m' # Purple + INFO = '\033[94m' # Blue + SUCCESS = '\033[92m' # Green + WARNING = '\033[93m' # Yellow + ERROR = '\033[91m' # Red + ENDC = '\033[0m' # Reset + BOLD = '\033[1m' # Bold + UNDERLINE = '\033[4m' # Underline + + +def get_message_color(message): + """ + Determine the appropriate color based on message content + + Args: + message: The message to analyze + + Returns: + str: ANSI color code + """ + message_lower = message.lower() + + # Error messages + if any(word in message_lower for word in ['error', 'failed', 'fatal', 'critical', 'unable']): + return Colors.ERROR + + # Warning messages + elif any(word in message_lower for word in ['warning', 'warn', 'caution', 'alert']): + return Colors.WARNING + + # Success messages + elif any(word in message_lower for word in ['success', 'completed', 'installed', 'finished', 'done', 'enabled']): + return Colors.SUCCESS + + # Running/Processing messages + elif any(word in message_lower for word in ['running', 'installing', 'downloading', 'processing', 'starting', 'configuring']): + return Colors.INFO + + # Default color + else: + return Colors.HEADER + + +def stdOut(message, log=0, do_exit=0, code=os.EX_OK): + """ + Standard output function with timestamps, coloring, and logging + + Args: + message: Message to output + log: If 1, write to log file + do_exit: If 1, exit after outputting + code: Exit code to use if do_exit is 1 + """ + # Get appropriate color for the message + color = get_message_color(message) + + # Check if terminal supports color + try: + # Check if output is to a terminal + if not sys.stdout.isatty(): + color = '' + color_end = '' + else: + color_end = Colors.ENDC + except: + color = '' + color_end = '' + + # Format timestamps + timestamp = time.strftime("%m.%d.%Y_%H-%M-%S") + + print("\n\n") + print(f"{color}[{timestamp}] #########################################################################{color_end}\n") + print(f"{color}[{timestamp}] {message}{color_end}\n") + print(f"{color}[{timestamp}] #########################################################################{color_end}\n") + + if log and hasattr(logging, 'InstallLog'): + logging.InstallLog.writeToFile(message) + if do_exit: + if hasattr(logging, 'InstallLog'): + logging.InstallLog.writeToFile(message) + sys.exit(code) + + +def format_restart_litespeed_command(server_root_path): + """ + Format the LiteSpeed restart command + + Args: + server_root_path: Root path of the server installation + + Returns: Formatted command string + """ + return '%sbin/lswsctrl restart' % (server_root_path) + + +# Distribution constants +ubuntu = 0 +centos = 1 +cent8 = 2 +openeuler = 3 + + +def get_distro(): + """ + Detect Linux distribution + + Returns: Distribution constant (ubuntu, centos, cent8, or openeuler) + """ + distro = -1 + distro_file = "" + if exists("/etc/lsb-release"): + distro_file = "/etc/lsb-release" + with open(distro_file) as f: + for line in f: + if line == "DISTRIB_ID=Ubuntu\n": + distro = ubuntu + + elif exists("/etc/redhat-release"): + distro_file = "/etc/redhat-release" + distro = centos + + data = open('/etc/redhat-release', 'r').read() + + if data.find('CentOS Linux release 8') > -1: + return cent8 + ## if almalinux 9 then pretty much same as cent8 + if data.find('AlmaLinux release 8') > -1 or data.find('AlmaLinux release 9') > -1: + return cent8 + if data.find('Rocky Linux release 8') > -1 or data.find('Rocky Linux 8') > -1 or data.find('rocky:8') > -1: + return cent8 + if data.find('CloudLinux 8') or data.find('cloudlinux 8'): + return cent8 + + else: + if exists("/etc/openEuler-release"): + distro_file = "/etc/openEuler-release" + distro = openeuler + + else: + if hasattr(logging, 'InstallLog'): + logging.InstallLog.writeToFile("Can't find linux release file - fatal error") + print("Can't find linux release file - fatal error") + os._exit(os.EX_UNAVAILABLE) + + if distro == -1: + error_msg = "Can't find distro name in " + distro_file + " - fatal error" + if hasattr(logging, 'InstallLog'): + logging.InstallLog.writeToFile(error_msg) + print(error_msg) + os._exit(os.EX_UNAVAILABLE) + + return distro + + +def get_package_install_command(distro, package_name, options=""): + """ + Get the package installation command for a specific distribution + + Args: + distro: Distribution constant + package_name: Name of the package to install + options: Additional options for the package manager + + Returns: + tuple: (command, shell) where shell indicates if shell=True is needed + """ + if distro == ubuntu: + command = f"DEBIAN_FRONTEND=noninteractive apt-get -y install {package_name} {options}" + shell = True + elif distro == centos: + command = f"yum install -y {package_name} {options}" + shell = False + else: # cent8, openeuler + command = f"dnf install -y {package_name} {options}" + shell = False + + return command, shell + + +def get_package_remove_command(distro, package_name): + """ + Get the package removal command for a specific distribution + + Args: + distro: Distribution constant + package_name: Name of the package to remove + + Returns: + tuple: (command, shell) where shell indicates if shell=True is needed + """ + if distro == ubuntu: + command = f"DEBIAN_FRONTEND=noninteractive apt-get -y remove {package_name}" + shell = True + elif distro == centos: + command = f"yum remove -y {package_name}" + shell = False + else: # cent8, openeuler + command = f"dnf remove -y {package_name}" + shell = False + + return command, shell + + +def resFailed(distro, res): + """ + Check if a command execution result indicates failure + + Args: + distro: Distribution constant + res: Return code from subprocess + + Returns: + bool: True if failed, False if successful + """ + if distro == ubuntu and res != 0: + return True + elif distro == centos and res != 0: + return True + return False + + +def call(command, distro, bracket, message, log=0, do_exit=0, code=os.EX_OK, shell=False): + """ + Execute a shell command with retry logic and error handling + + Args: + command: Command to execute + distro: Distribution constant + bracket: Not used (kept for compatibility) + message: Description of the command for logging + log: If 1, write to log file + do_exit: If 1, exit on failure + code: Exit code to use if do_exit is 1 + shell: If True, execute through shell + + Returns: + bool: True if successful, False if failed + """ + finalMessage = 'Running: %s' % (message) + stdOut(finalMessage, log) + count = 0 + while True: + if shell == False: + res = subprocess.call(shlex.split(command)) + else: + res = subprocess.call(command, shell=True) + + if resFailed(distro, res): + count = count + 1 + finalMessage = 'Running %s failed. Running again, try number %s' % (message, str(count)) + stdOut(finalMessage) + if count == 3: + fatal_message = '' + if do_exit: + fatal_message = '. Fatal error, see /var/log/installLogs.txt for full details' + + stdOut("[ERROR] We are not able to run " + message + ' return code: ' + str(res) + + fatal_message + ".", 1, do_exit, code) + return False + else: + stdOut('Successfully ran: %s.' % (message), log) + break + + return True + + +# Character sets for password generation (kept for backward compatibility) +char_set = { + 'small': 'abcdefghijklmnopqrstuvwxyz', + 'nums': '0123456789', + 'big': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +} + + +def generate_pass(length=14): + """ + Generate a cryptographically secure random password + + Args: + length: Length of the password to generate (default 14) + + Returns: + str: Random password containing uppercase, lowercase letters and digits + """ + alphabet = string.ascii_letters + string.digits + return ''.join(secrets.choice(alphabet) for _ in range(length)) + + +def generate_random_string(length=32, include_special=False): + """ + Generate a random string with optional special characters + + Args: + length: Length of the string to generate + include_special: If True, include special characters + + Returns: + str: Random string + """ + alphabet = string.ascii_letters + string.digits + if include_special: + alphabet += string.punctuation + return ''.join(secrets.choice(alphabet) for _ in range(length))