PATH:
usr
/
share
/
lve
/
dbgovernor
/
modules
/
Editing: cpanel.py
# coding:utf-8 # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved # # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENSE.TXT # """ This module contains class for managing governor on cPanel server """ import os import re import shutil import sys import textwrap import urllib.request, urllib.error, urllib.parse import hashlib from utilities import exec_command_out, grep, add_line, \ service, remove_lines, write_file, replace_lines, touch, \ is_package_installed, remove_packages, exec_command, \ parse_rpm_name, service_symlink, bcolors, get_cl_num, is_ubuntu from .base import InstallManager class cPanelManager(InstallManager): """ Installation manager for cPanel """ def update_user_map_file(self): """ Update user mapping file for cPanel """ try: self._script_subprocess("dbgovernor_map") except RuntimeError as e: self.warning(str(e)) def warning(self, orig_msg): """ Warn user in case of errors during dbgovernor_map process and exit with exitcode 1. If CpuserNotInMap Exception occurred, advice user to run rebuild_dbmap script Otherwise, just show original exception :param orig_msg: original exception text """ if 'CpuserNotInMap' in orig_msg: username = self.retrieve_username(orig_msg) print(bcolors.fail('cPanel user `{u}` does not exist in the database map.'.format(u=username))) print(bcolors.info('Try to perform the following command: /scripts/rebuild_dbmap {u}\n' 'and then run /usr/share/lve/dbgovernor/mysqlgovernor.py --dbupdate again.'.format(u=username))) print(bcolors.warning('If this does not help, please, ' 'contact cPanel support with the original error:\n{m}'.format(u=username, m=orig_msg.split('\n')[1]))) else: print(bcolors.fail(orig_msg)) sys.exit(1) @staticmethod def retrieve_username(msg): """ Try to get corrupted user name from the exception text (e.g. CpuserNotInMap Exception) :param msg: original exception text :return: username if retrieved, None otherwise """ try: return re.findall(r'cPanel user “(.+)” does not exist in the database map.', msg)[0] except IndexError: return None def install_mysql_beta_testing_hooks(self): """ Specific hooks """ self.set_fs_suid_dumpable() self._script("cpanel-install-hooks") def update_mysql_hooks(self): """ Update mysql hooks """ self._script("cpanel-delete-hooks") self._script("cpanel-install-hooks") def _delete(self, installed_packages): """ Remove installed packages """ # through mysql --version cmd current_version = self._check_mysql_version() if os.path.exists("/etc/chkserv.d/db_governor"): os.remove("/etc/chkserv.d/db_governor") self._script("chek_mysql_rpms_local", "-d") self._script("cpanel-delete-hooks") if os.path.exists("/etc/mysqlupdisable"): os.remove("/etc/mysqlupdisable") if os.path.exists("/var/cpanel/rpm.versions.d/cloudlinux.versions"): os.remove("/var/cpanel/rpm.versions.d/cloudlinux.versions") if os.path.exists("/etc/cpupdate.conf.governor"): if os.path.exists("/etc/cpupdate.conf"): os.remove("/etc/cpupdate.conf") os.rename("/etc/cpupdate.conf.governor", "/etc/cpupdate.conf") self._mysqlservice("stop") # delete installed packages and restore native remove_packages(installed_packages) self.restore_mysql_packages(current_version) # remove governor package exec_command_out("rpm -e governor-mysql") exec_command_out("/scripts/upcp --force") def restore_mysql_packages(self, current_version): """ Install legacy packages after --delete procedure """ # According to July 2022 CPanel Letter, they will not be supporting MariaDB 10.7, 10.8, or 10.9 # since they will now be reaching end-of-life sometime in 2023. # So we will need to add this to unsupported logic MYSQLG-730 if current_version['full'] == 'mariadb104': print('{} is unsupported by cPanel, mariadb105 will be installed instead'.format( current_version['full'])) current_version = { 'short': '10.5', 'mysql_type': 'mariadb', 'full': 'mariadb105' } print('Restoring known packages for {}'.format(current_version['full'])) targets = { 'mysql55': 'MySQL55', 'mysql56': 'MySQL56', 'mariadb100': 'MariaDB100', 'mariadb101': 'MariaDB101', 'mariadb102': 'MariaDB102', 'mariadb103': 'MariaDB103', 'mariadb104': 'MariaDB104', 'mariadb105': 'MariaDB105', 'mariadb106': 'MariaDB106', 'mariadb1011': 'MariaDB1011', 'mariadb1104': 'MariaDB1104', } old = 'MySQL50,MySQL51,' # old unsupported targets not_managed = ('mysql57', 'mysql80', 'mysql84') # latest mysql not managed by cPanel # clear rpm management for all known targets for t in targets.values(): exec_command('/usr/local/cpanel/scripts/update_local_rpm_versions --del target_settings.%(target)s' % {'target': t}) # disable mysql targets for upcp not to fix them! # for k in filter(lambda x: 'mariadb' not in x and x != current_version['full'], targets.keys()): # exec_command('/usr/local/cpanel/scripts/update_local_rpm_versions --edit target_settings.%(target)s uninstalled' % {'target': targets[k]}) # update mysql version in cPanel's configuration file exec_command(self._rel("scripts/set_cpanel_mysql_version.pm %s" % current_version['short'])) if current_version['mysql_type'] == 'mariadb': # add repo, yum install mariadb pkgs self.install_mariadb(current_version['full']) elif current_version['full'] in not_managed: # add repo, yum install mysql57 or mysql80 self.install_mysql_community(current_version['full']) # create mysql alias for mysqld service self.mysql_service_symlink() else: # enable current mysql target to rpm management t = targets.get(current_version['full']) if not t: raise RuntimeError('unknown target for RPM management: {}'.format(current_version['full'])) exec_command('/usr/local/cpanel/scripts/update_local_rpm_versions --edit target_settings.%(target)s installed' % {'target': t}) # fix legacy RPMs (works for mysql55 and mysql56 only) if os.path.exists("/scripts/check_cpanel_rpms"): exec_command_out( "/scripts/check_cpanel_rpms --fix --targets={}".format( old + ','.join(targets.values()))) # clear RPM management targets for cPanel higher than 72 version, # because setting it to `installed` causes MySQL/MariaDB Upgrade interface errors if self.get_panel_version() > 72: exec_command('/usr/local/cpanel/scripts/update_local_rpm_versions --del target_settings.%(target)s' % {'target': t}) def install_mariadb(self, version): """ Install official MariaDB """ pkgs = ('MariaDB-server', 'MariaDB-client', 'MariaDB-shared', 'MariaDB-devel', 'MariaDB-compat',) # do not try to install MariaDB-compat on CL9 if get_cl_num() >= 9: pkgs = [pkg for pkg in pkgs if pkg != 'MariaDB-compat'] num = version.split('mariadb')[-1] cpanel_alter_repo = f'MariaDB{num}' # try to find preinstalled cPanel own repo for given version if not self.install_from_existing_repo(cpanel_alter_repo, pkgs): # prepare repo data print('Preparing official MariaDB repository...') dot_version = f'{num[:2]}.{num[2:]}' repo_data = f""" [mariadb] name = MariaDB baseurl = https://archive.mariadb.org/yum/{dot_version}/rhel/$releasever/$basearch gpgkey = https://archive.mariadb.org/PublicKey https://yum.mariadb.org/RPM-GPG-KEY-MariaDB module_hotfixes = 1 enabled = 1 gpgcheck = 1 """ with open('/etc/yum.repos.d/MariaDB.repo', 'w') as repo_file: repo_file.write(textwrap.dedent(repo_data)) # install MariaDB packages print('Installing packages') exec_command( "yum install -y --disableexcludes=all --disablerepo=cl-mysql* --disablerepo=mysqclient* {pkgs}".format( pkgs=' '.join(pkgs))) def install_mysql_community(self, version): """ Install official MySQL 5.7, MySQL 8.0 or MySQL 8.4, not managed by cPanel """ pkgs = ('mysql-community-server', 'mysql-community-client', 'mysql-community-common', 'mysql-community-libs') if get_cl_num() >= 8: cpanel_alter_repo = f'{version.capitalize()}-community' else: cpanel_alter_repo = f'{version}-community' # try to find preinstalled cPanel own repo for given version if not self.install_from_existing_repo(cpanel_alter_repo, pkgs): # prepare mysql-community repo if not exec_command(f"rpm -qa | grep {version}-community", silent=True): self.download_and_install_mysql_repo(version) # select MySQL version print('Selected version %s' % version) m = re.match(r"^mysql(\d)(\d)$", version) # currently only 'mysqlNN' are passed here assert m is not None ver_maj, ver_min = m.groups() exec_command('yum-config-manager --disable mysql*-community') # Before 2023, the format was "[mysql80-community]". # After that, dotted version format and "...-lts-..." part can appear in repo names, # example: "[mysql-8.4-lts-community]". # "yum-config-manager" fails on missing wildcard matches, so using "silent". exec_command(f"yum-config-manager --enable {version}-community", silent=True) exec_command(f"yum-config-manager --enable mysql-{ver_maj}.{ver_min}-*community", silent=True) # install mysql-community packages print(f'Installing packages from {version}-community') exec_command( "yum install -y --disableexcludes=all --disablerepo=cl-mysql* --disablerepo=mysqclient* {pkgs}".format( pkgs=' '.join(pkgs))) @staticmethod def install_from_existing_repo(reponame, packages): """ Try to install from existing repository and return result Args: reponame: repository name packages: list pf packages to install Returns: True in case of success, False otherwise """ # try to find preinstalled cPanel own repo for given version if exec_command( f"yum repolist -y --enablerepo=* | grep {reponame} -c", True, True) != "0": print(f'Installing packages from {reponame}') exec_command( "yum install -y --disableexcludes=all --disablerepo=cl-mysql* --disablerepo=mysqclient* --enablerepo={repo} {pkgs}".format( repo=reponame, pkgs=' '.join(packages))) return True return False def download_and_install_mysql_repo(self, version): """ Download mysql[57|80|84]-community-release repository and install it locally. Can be called only for "version" in "not_managed" set. """ # download repo file url = f"https://repo.mysql.com/{version}-community-release-el{self.cl_version}.rpm" repo_file = os.path.join(self.SOURCE, 'mysql-community-release.rpm') if version == 'mysql57': repo_md5 = { 6: 'afe0706ac68155bf91ade1c55058fd78', 7: 'c070b754ce2de9f714ab4db4736c7e05' } elif version == 'mysql80': repo_md5 = { 6: 'ec5978dc1c9b79ef570e3bc08ba3d531', # Indeed, now '9424ad10a2e9a4bfab5cee8e8239a965' for version=="mysql80". Was other version meant? 7: '42048ccae58835e40e37b68a3f8b91fb', 8: 'e436076fe27a60ea88024cc3db4b1bb0', 9: '4fa11545b76db63df0efe852e28c4d6b' } elif version == 'mysql84': repo_md5 = { 7: 'a3541dff58744c344d8930518b87c289', 8: '7e3700fd23a3f8a28c4141a3a2e00b01', 9: '15a20fea9018662224f354cb78b392e7' } else: assert False # version must be in "not_managed" set if self.cl_version not in repo_md5: print(f"There is no community {version} release for CL{self.cl_version}") sys.exit(1) opener = urllib.request.build_opener() opener.addheaders = [('User-agent', 'Mozilla/5.0')] print('Downloading %s' % url) try: rpm = opener.open(url).read() with open(repo_file, 'wb') as f: f.write(rpm) except urllib.error.HTTPError as err: print('Failed to download MySQL repository file: {e}'.format(e=err)) sys.exit(1) if hashlib.md5(open(repo_file, 'rb').read()).hexdigest() != repo_md5[self.cl_version]: print('Failed to download MySQL repository file. File is corrupted!') sys.exit(1) # install repo exec_command_out('yum localinstall -y --disableexcludes=all {}'.format(repo_file)) def mysql_service_symlink(self): """ Create mysql alias for mysqld service """ service_symlink('mysqld', 'mysql') # delete version cache (for web-interface correct version detection) try: os.unlink('/var/cpanel/mysql_server_version_cache') os.unlink(f'{self.my_cnf_datadir}/mysql_upgrade_info') except Exception: pass def _after_install_new_packages(self): """ cPanel triggers after install new packages to system """ # cpanel script for restart mysql service exec_command_out("/scripts/restartsrv_mysql") print("db_governor checking: ") if is_package_installed("governor-mysql"): exec_command_out("chkconfig --level 35 db_governor on") service("restart", "db_governor") print("OK") else: print("FAILED") # print "The installation of MySQL for db_governor completed" if os.path.exists("/usr/local/cpanel/cpanel"): if os.path.exists( "/usr/local/cpanel/scripts/update_local_rpm_versions"): shutil.copy2(self._rel("utils/cloudlinux.versions"), "/var/cpanel/rpm.versions.d/cloudlinux.versions") else: if not os.path.exists("/etc/cpupdate.conf.governor"): self._get_mysqlup() touch("/etc/mysqlupdisable") self._script("cpanel-install-hooks") if os.path.exists("/usr/local/cpanel/cpanel") and \ os.path.exists( "/usr/local/cpanel/scripts/update_local_rpm_versions"): if os.path.exists("/etc/mysqlupdisable"): os.unlink("/etc/mysqlupdisable") remove_lines("/etc/cpupdate.conf", "MYSQLUP=never") if os.path.exists("/etc/chkserv.d") and os.path.exists( self._rel("utils/db_governor")): shutil.copy2(self._rel("utils/db_governor"), "/etc/chkserv.d/db_governor") # call parent after_install InstallManager._after_install_new_packages(self) def _after_install_rollback(self): """ Rollback after install triggers """ # if os.path.exists("/etc/mysqlupdisable"): # os.remove("/etc/mysqlupdisable") # if os.path.exists("/var/cpanel/rpm.versions.d/cloudlinux.versions"): # os.remove("/var/cpanel/rpm.versions.d/cloudlinux.versions") # if os.path.exists("/etc/cpupdate.conf.governor"): # if os.path.exists("/etc/cpupdate.conf"): # os.remove("/etc/cpupdate.conf") # os.rename("/etc/cpupdate.conf.governor", "/etc/cpupdate.conf") # exec_command_out(SOURCE+"cpanel/cpanel-delete-hooks") # exec_command_out("/scripts/upcp --force") # if os.path.exists("/scripts/check_cpanel_rpms"): # exec_command_out("/scripts/check_cpanel_rpms --fix --targets=MySQL50,MySQL51,MySQL55,MySQL56,MariaDB") ############################# ############################# ############################# # if os.path.exists("/var/cpanel/rpm.versions.d/cloudlinux.versions"): # os.unlink("/var/cpanel/rpm.versions.d/cloudlinux.versions") # exec_command_out(SOURCE+"cpanel/cpanel-delete-hooks") # remove_lines("/etc/cpupdate.conf", "MYSQLUP=never") # if os.path.exists("/etc/cpupdate.conf.governor"): # os.unlink("/etc/cpupdate.conf.governor") # if os.path.exists("/etc/mysqlupdisable"): # os.unlink("/etc/mysqlupdisable") def _before_delete(self): """ Disable mysql service monitoring """ self.enable_mysql_monitor(False) def _after_delete(self): """ Enable mysql service monitoring """ # call parent first InstallManager._after_delete(self) self.enable_mysql_monitor() def _before_install(self): """ Disable mysql service monitoring """ self.enable_mysql_monitor(False) def _after_install(self): """ Enable mysql service monitoring """ # call parent first InstallManager._after_install(self) self.enable_mysql_monitor() @staticmethod def _get_mysqlup(): """ ? Set value for panel update MYSQLUP option """ if os.path.exists("/etc/cpupdate.conf"): shutil.copy2("/etc/cpupdate.conf", "/etc/cpupdate.conf.governor") is_mysqlup = grep("/etc/cpupdate.conf", "MYSQLUP") if is_mysqlup: if not grep(is_mysqlup, "never$", True): replace_lines("/etc/cpupdate.conf", "".join(is_mysqlup), "MYSQLUP=never") else: add_line("/etc/cpupdate.conf", "\nMYSQLUP=never\n") else: write_file("/etc/cpupdate.conf.governor", "") write_file("/etc/cpupdate.conf", "MYSQLUP=never\n") def _detect_version_if_auto(self): """ Detect version of MySQL if mysql.type is auto """ if os.path.exists(self._rel("scripts/detect-cpanel-mysql-version.pm")): mysqlname_array = exec_command( self._rel("scripts/detect-cpanel-mysql-version.pm")) mysqlname = "" if len(mysqlname_array) > 0: mysqlname = mysqlname_array[0] if "mysql" in mysqlname or "mariadb" in mysqlname: return mysqlname.strip() return "" def _custom_rpm_locator(self, package_name): """ Where we should download installed MySQL package from. For the special argument value "package_name"=="+" returning "yes" means that this _custom_rpm_locator() implementation is fully functional, and returning any other value means that it's not supported. """ if package_name == "+": # supported? return "yes" result = parse_rpm_name(package_name) if len(result) == 4: return exec_command(self._rel( "scripts/cpanel-mysql-url-detect.pm %s %s-%s" % ( result[0], result[1], result[2])), True) return "" def make_additional_panel_related_check(self): """ Specific cPanel check :return: """ if os.path.exists("/usr/local/cpanel/cpanel"): if os.path.exists( "/usr/local/cpanel/scripts/update_local_rpm_versions") and \ os.path.exists( "/var/cpanel/rpm.versions.d/cloudlinux.versions") and \ os.path.exists( self._rel("utils/cloudlinux.versions")): shutil.copy2(self._rel("utils/cloudlinux.versions"), "/var/cpanel/rpm.versions.d/cloudlinux.versions") return def unsupported_db_version(self, force=False): """ Skip an installation if not supported db version has been set MariaDB 10.5 is supported starting from cPanel v.98 """ # According to July 2022 CPanel Letter, they will not be supporting MariaDB 10.7, 10.8, or 10.9 # since they will now be reaching end-of-life sometime in 2023. # So we will need to add this to unsupported logic MYSQLG-730 # # We also still don't support MariaDB 10.7, 10.8 and 10.9 and possibly won't do it. # So they are absent in mysql version list, and we have not check for them #UNSUPPORTED_MARIADB_VERSIONS = ['mariadb107', 'mariadb108', 'mariadb109'] UNSUPPORTED_MARIADB_VERSIONS = [ ] super().unsupported_db_version(force) version = InstallManager._get_result_mysql_version(self) if version in UNSUPPORTED_MARIADB_VERSIONS: print(bcolors.fail(f"{version} is unsupported version for cPanel")) sys.exit(1) if version in ('mariadb104',) or ( version == 'mariadb105' and self.get_panel_version() < 98): print(bcolors.fail(f"{version} is unsupported version for cPanel")) if not force: sys.exit(1) @staticmethod def enable_mysql_monitor(enable=True): """ Enable or disable mysql monitoring :param enable: if True - enable monitor if False - disable monitor """ exec_command_out( "whmapi1 configureservice service=mysql enabled=1 monitored={}".format(int(enable))) @staticmethod def get_panel_version(): """ Retrieve cPanel current version from /usr/local/cpanel/version file :return: major version value """ with open('/usr/local/cpanel/version', 'r') as content: full_version = content.read().strip() return int(full_version.split('.')[1]) @staticmethod def prepare_statement_for_ubuntu(): """For cPanel preparing system for governor. 1. Skip if not ubuntu 2. Remove /etc/apt/sources.list.d/mysql.list 3. Remove unneeded packages """ if not is_ubuntu(): return mysql_list = '/etc/apt/sources.list.d/mysql.list' try: os.remove(mysql_list) except FileNotFoundError: print(f'{mysql_list} not exists. Already deleted') exec_command_out('apt-get update -o Dpkg::Options::=--force-confnew') exec_command_out('apt remove mysql-community-server -y') exec_command_out('apt remove mysql-client -y') exec_command_out('apt remove libmysqlclient21 libmysqlclient-dev mysql-community* -y') if is_package_installed('mysql-client'): exec_command_out('dpkg -P mysql-client')
SAVE
CANCEL