D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
imh-python
/
lib
/
python2.7
/
site-packages
/
cmu_ded
/
Libs
/
Filename :
EasyApacheTransfer.py
back
Copy
import re import os import json import yaml import shutil import string import subprocess import common class EasyApacheTransfer(common.Migration): """ EA4 Transfer profile, specifically for ea3 to ea4. This is done to make the function a little bit more readable. This should help with updating everything as well. """ # Translation between EA versions. # Deprecated or modules we want to ignore are blank. # Or by default, not on apache24. Such as AuthzHost. # ldap is causing issues with ea4 and ea-apr. Ignoring for now. apache_translation = { "cpanel::easy::modsec": "ea-apache24-mod_security2", "cpanel::easy::modbandwidth": "ea-apache24-mod_bwlimited", "cpanel::easy::modruid2": "ea-apache24-mod_ruid2", "mpmworker": "", "mpmprefork": "", "mpmitk": "", "mpmevent": "", "alias": "ea-apache24-mod_vhost_alias", "vhostalias": "ea-apache24-mod_vhost_alias", "authdigest": "ea-apache24-mod_auth_digest", "authldap": "", "authdbm": "ea-apache24-mod_authn_dbm", "authdb": "ea-apache24-mod_authn_dbd", "authanon": "ea-apache24-mod_authn_anon", "authnzldap": "", "authnldap": "", "authndbm": "ea-apache24-mod_authn_dbm", "authndb": "ea-apache24-mod_authn_dbd", "authnanon": "ea-apache24-mod_authn_anon", "authzdbm": "ea-apache24-mod_authn_dbm", "authzowner": "ea-apache24-mod_authz_owner", "davfs": "ea-apache24-mod_dav_fs", "davlock": "ea-apache24-mod_dav_lock", "fastcgi": "ea-apache24-mod_proxy_fcgi", "filecache": "ea-apache24-mod_file_cache", "diskcache": "ea-apache24-mod_cache_disk", "extfilter": "ea-apache24-mod_ext_filter", "proxy": "ea-apache24-mod_proxy_html", "logforensic": "ea-apache24-mod_log_forensic", "memcache": "ea-apache24-mod_socache_memcache", "mimemagic": "ea-apache24-mod_mime_magic", "uniqueid": "ea-apache24-mod_unique_id", "ldap": "", "mpmperchild": "", "mime": "", "userdir": "", "autoindex": "", "authzhost": "", "authndefault": "", "authnalias": "", "access": "", "actions": "", } def __init__(self, logger=None): """Init, simply passing over to parent class to setup logger.""" super(EasyApacheTransfer, self).__init__(logger) # Core variables. self.skel = {} self.loaded_modules = {} self.php_binaries_to_transfer = {} self.is_ioncube = False self.is_ea4 = False self.errors = [] # main functions. Used after entry point. def main(self): """ Main function that runs everything. Needs load to be ran and dump ran afterwards. """ self.debug("Entering main.") self.check_ea4() if not self.is_ea4: self.setup_ea3() return self.convert() else: return self.ea4_move_php_local_inis() def vzf_conf_main(self): """ Main function specifically for vz failover. Needs rootdir1 and rootdir2 set first. """ self.debug("Entering vzf_conf_main.") self.check_ea4() self.load() self.main() self.dump_file(self.rootdir2 + "/etc/cpanel/ea4/profiles/custom/transfered_profile.json") # Loading a dumping files. def load(self, file_location=None): """ Loads the configuration file specified in file_location. :return: True or false depending on if the file has been loaded. """ self.debug("Entering load") if file_location: result = self.remote_cmd_exec("cat %s" % file_location) if "No such file or directory" in result: self.errors.append("%s does not exist to load." % file_location) self.warning("%s does not exist to load." % file_location) return False if file_location.split('.')[-1] == 'json': self.ea4_load_json(result) else: self.ea3_load_yaml(result) else: if self.is_ea4: file = self.remote_cmd_exec("/usr/local/bin/ea_current_to_profile") result = self.remote_cmd_exec("cat %s" % file) if "No such file or directory" in result: self.errors.append("Unable to load the EA4 json file, the file does not exist") self.critical("Unable to load the EA4 json file, the file does not exist.") return False self.ea4_load_json(result) else: result = self.remote_cmd_exec("cat %s" % '/var/cpanel/easy/apache/profile/_main.yaml') if "No such file or directory" in result: self.errors.append("Unable to load the EA4 json file, the file does not exist.") self.critical("Unable to load the EA4 json file, the file does not exist.") return False self.ea3_load_yaml(result) return True def ea3_load_yaml(self, file_contents): """ loads yaml string. :return: True if yaml is loaded. """ self.debug("Entering ea3_load_yaml") self.loaded_modules = yaml.safe_load(file_contents) return True def ea4_load_json(self, file_contents): """ Load json string for ea4. :return: Returns true if json is loaded. """ self.debug("Entering ea4_load_json") self.loaded_modules = json.loads(file_contents) return True def dump(self): """ Dumps a dictionary out that is setup for JSON """ self.debug("Entering dump") return json.dumps(self.skel, separators=(',\n', ' : '), sort_keys=True, indent=4) def dump_file(self, file_location=None): """ Dumps the JSON into a file. """ self.debug("Entering dump_file") if file_location: open(file_location, 'a').write(self.dump()) else: open('/etc/cpanel/ea4/profiles/custom/transfered_profile.json', 'a').write(self.dump()) return True def check_ea4(self): """ Checks rootdir1 + path to is_ea4. :return: If its there return false, if it is return true. """ self.debug("Entering check_ea4") result = self.remote_cmd_exec('stat /etc/cpanel/ea4/is_ea4') if not result or 'No such file or directory' in result: self.is_ea4 = False else: self.is_ea4 = True return self.is_ea4 def setup_ea3(self): """ Setting up skel before conversion. Should run convert after this. :return: Modifies skel. """ self.skel["version"] = "1.0" self.skel["tags"] = ["Apache2.4"] self.skel["desc"] = "EA3 to EA4 Profile from cmu_ded" # Default packages.l self.skel["pkgs"] = ["ea-apache24", "ea-apr", "ea-apr-util", "ea-apache24-mod_ssl"] self.skel["name"] = "Transfered Profile" def convert(self): """ Runs all the ea3 related stuff for converting the profile. :return: True or false. """ return self.ea3_grab_php_binaries() and \ self.build_ea4_skel_profile() and \ self.append_ea4_php_mods() and \ self.ea3_modify_htaccess_files() and \ self.ea3_move_php_ini_files() # Grabbing information for the setup. def ea3_grab_php_binaries(self): """ Grabbing the PHP binaries on the server and the extensions installed on them. :return: Dictionary of each version and a list of each extension under that. """ self.debug("Entering ea3_grab_php_binaries.") if self.is_ea4: return False veid = None return_dict = {} typical_php_locations = ['/usr/local/bin/php', '/opt/php52/bin/php', '/opt/php53/bin/php', '/opt/php54/bin/php', '/opt/php55/bin/php', '/opt/php56/bin/php', '/opt/php70/bin/php', '/opt/php71/bin/php'] # Need to be flexible here. If rootdir1 is set, we need to vzctl exec, otherwise we need to use a shell. # ( For cmu_ded ) if self.rootdir1: veid = self.rootdir1.split("/")[3] self.debug("VEID1 is: " + veid) for binary in typical_php_locations: # Validating the path exists. ( Fix ) result = self.remote_cmd_exec('stat %s' % binary) if 'No such file or directory' in result: self.warning("%s does not exist. Skipping" % binary) continue # Grabbing the version information. version_string = self.remote_cmd_exec('%s -v' % binary) self.debug(version_string) split = version_string.split()[1].split(".") current_version = "php" + split[0] + split[1] self.debug(split) if int(split[0]) <= 5 and int(split[1]) < 4: self.warning("PHP Version is too low, skipping") continue # Grabbing the module information now. modules_string = self.remote_cmd_exec('%s -m' % binary) final_module_string = [] for line in modules_string.replace('\r', '').split('\n'): if ' ' not in line and line: final_module_string.append(line) self.debug(final_module_string) return_dict[current_version] = final_module_string self.php_binaries_to_transfer = return_dict self.debug(return_dict) return return_dict # Functions that build the new profile. def build_ea4_skel_profile(self): """ Building the skeleton for the ea4 profile. Goes through the YAML file that is loaded. and gets the info that we need. It also checks for empty responses for blacklisted modules. Nothing is added in that case. :return: Modifies skel during exec. Returns True if everything is ran without issue. """ self.debug("Entering into build_ea4_skel_profile") # Getting all the PHP versions for the tag. for i in self.php_binaries_to_transfer.keys(): self.skel["tags"].append("PHP" + i) for cpanel_mod in self.loaded_modules.keys(): for translation in self.apache_translation.keys(): if cpanel_mod.lower() == translation.lower() and self.apache_translation[translation]: self.debug("Adding %s to skel" % self.apache_translation[translation]) self.skel['pkgs'].append(self.apache_translation[translation]) for apache_mod in self.loaded_modules['Apache']['optmods'].keys(): for translation in self.apache_translation.keys(): if apache_mod.lower() == translation.lower() and self.apache_translation[translation]: self.debug("Adding %s to skel" % self.apache_translation[translation]) self.skel['pkgs'].append(self.apache_translation[translation]) for specific_mods in self.loaded_modules.keys(): if 'ioncube' in specific_mods.lower(): self.debug("is_ioncube is set to true.") self.is_ioncube = True return True def append_ea4_php_mods(self): """ This needs the skeleton to be built first. Run build_ea4_skel_profile first! This then uses the grabbed list of php and its modules installed. It then adds anything required. Then it goes through and adds when is listed in php_binaries_to_transfer. From there is checks for duplicates and for any ignored items. If everything is kosher, we add it to the skeleton. :return: Modifies skel during exec. """ self.debug("Entering append_ea4_php_mods") php_mods_to_ignore = ["pdo_", " ", "tidy", "debuginfo"] php_mods_required = ["-php-common", "-build"] # You should not need to edit below this! for php_vers in self.php_binaries_to_transfer.keys(): mod = "ea-" + php_vers.replace(".", "") # Making sure mod is not in skel already. if mod in self.skel['pkgs']: continue for required_item in php_mods_required: php_mod = mod + required_item if php_mod not in self.skel['pkgs']: self.skel['pkgs'].append(php_mod) self.debug("+++ Adding %s" % php_mod) if self.is_ioncube: ioncube_mod = mod + "-php-ioncube" if ioncube_mod not in self.skel['pkgs']: self.skel['pkgs'].append(ioncube_mod) self.skel['pkgs'].append(ioncube_mod + '5') self.debug("+++ Adding %s" % ioncube_mod) for item in self.php_binaries_to_transfer[php_vers]: # If the item is not empty or doesn't start with a letter if len(item) != 0 and item[0] in string.ascii_letters: php_mod = mod + "-php-" + item.strip().lower() to_ignore = False if php_mod in self.skel['pkgs']: continue # If item is in the ignore list. for ignore in php_mods_to_ignore: if ignore in php_mod: to_ignore = True # Checking if we need to ignore or in pkgs already. if php_mod not in self.skel['pkgs'] and not to_ignore: self.skel['pkgs'].append(php_mod) self.debug("[*] Adding %s" % php_mod) return True # Post move functions. def ea3_modify_htaccess_files(self): """ Modifies the .htaccess file so that errors don't rise up. :return: Modifies the .htaccess files. """ self.debug("Entering ea3_modify_htaccess_files") htacces_to_modify = {} result = self.local_cmd_exec('/bin/find /home/ -name .htaccess') for line in result.split("\n"): file = '\ '.join(line.split(" ")) # Getting file permissions. self.debug("Attempting to modify %s" % file) if os.path.exists(file): stat_data = os.stat(file) user = stat_data.st_uid group = stat_data.st_gid # Doing it this way due to spaces possibly being in the folder name. htacces_to_modify[file] = [user, group] for htaccess in htacces_to_modify.keys(): version = "" if os.path.exists(htaccess): with open(htaccess, 'r') as file: for line in file.readlines(): if "AddHandler application" in line: version = line.split()[1][-2:] try: if float(version[0] + "." + version[1]) <= 5.4: version = "54" except ValueError: version = "54" file.close() common.file_replace_line(htaccess, "AddHandler application", "") common.file_replace_line(htaccess, "MAGICK_THREAD_LIMIT", "") if version: with open(htaccess, 'w') as file: self.info("[*] Converting %s to use the correct call for PHP" % htaccess) file.write("AddType application/x-httpd-ea-php" + version + " .php .phtml") file.close() # Setting the permissions since file_replace_line is making a new file. os.chown(htaccess, htacces_to_modify[htaccess][0], htacces_to_modify[htaccess][1]) return True def ea3_move_php_ini_files(self): """ Moves php.ini files to php.ini.old :return: """ self.debug("Entering ea3_move_php_ini_files") php_ini_to_disable = [] result = self.local_cmd_exec('/bin/find /home/ -name php.ini') for line in result.split('\n'): php_ini_to_disable.append('\ '.join(line.split(" "))) for php_ini in php_ini_to_disable: if php_ini: self.info("Moving %s to disable" % php_ini) self.local_cmd_exec("mv %s %s" % (php_ini, php_ini + '.old')) return True def ea4_move_php_local_inis(self): """ Moving files in /opt/cpanel/ea-php*/root/etc/php.d/local.ini This helps managed hosting in the event ea4 is moved. :return: True if everything is good. """ self.debug("Entering ea4_move_php_local_inis") result = self.remote_cmd_exec('/bin/find /opt/cpanel -name local.ini') # Checking for an empty string. if not result: return True for ini in result.split("\n"): self.move_file(ini) return True def install_profile(self): """ Installs the profile after it has been transfered. :return: """ self.debug("Entering install_profile") result = self.local_cmd_exec( "/usr/local/bin/ea_install_profile /etc/cpanel/ea4/profiles/custom/transfered_profile.json" ) # Catching Perl Exception. if "::Exception::" in result: self.errors.append("Exception was found in install profile for Easy Apache.") self.error("Exception was found in install profile for Easy Apache") return False if "failed" in result: self.errors.append("Easy Apache install profile failed.") self.error("Easy Apache isntall profile failed.") return False return True def __str__(self): return "\n".join(self.errors) def __repr__(self): return "<EasyApacheTransfer: errors=%r : skel=%r>" % (self.errors, self.skel)