from ee.cli.plugins.stack import EEStackController from ee.core.fileutils import EEFileUtils from ee.core.mysql import * from ee.core.shellexec import * from ee.core.variables import EEVariables from ee.cli.plugins.sitedb import * from ee.core.aptget import EEAptGet from ee.core.logging import Log import subprocess from subprocess import CalledProcessError import os import random import string import sys import getpass import glob import re class SiteError(Exception): """Custom Exception Occured when setting up site""" def __init__(self, message): self.message = message def __str__(self): return repr(self.message) def check_domain_exists(self, domain): if getSiteInfo(self, domain): return True else: return False def setupdomain(self, data): ee_domain_name = data['site_name'] ee_site_webroot = data['webroot'] # Check if nginx configuration already exists if os.path.isfile('/etc/nginx/sites-available/{0}'.format(ee_domain_name)): raise SiteError("nginx configuration already exists for site") Log.info(self, "Setting up NGINX configuration \t", end='') # write nginx config for file try: ee_site_nginx_conf = open('/etc/nginx/sites-available/{0}' .format(ee_domain_name), encoding='utf-8', mode='w') self.app.render((data), 'virtualconf.mustache', out=ee_site_nginx_conf) ee_site_nginx_conf.close() except IOError as e: Log.debug(self, "{0}".format(e)) raise SiteError("create nginx configuration failed for site") except Exception as e: Log.debug(self, "{0}".format(e)) raise SiteError("create nginx configuration failed for site") finally: # Check nginx -t and return status over it try: Log.debug(self, "Checking generated nginx conf, please wait ...") FNULL = open('/dev/null', 'w') ret = subprocess.check_call(["nginx", "-t"], stdout=FNULL, stderr=subprocess.STDOUT) Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") except CalledProcessError as e: Log.debug(self, "{0}".format(str(e))) Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") raise SiteError("created nginx configuration failed for site." " check with `nginx -t`") # create symbolic link for EEFileUtils.create_symlink(self, ['/etc/nginx/sites-available/{0}' .format(ee_domain_name), '/etc/nginx/sites-enabled/{0}' .format(ee_domain_name)]) # Creating htdocs & logs directory Log.info(self, "Setting up webroot \t\t", end='') try: if not os.path.exists('{0}/htdocs'.format(ee_site_webroot)): os.makedirs('{0}/htdocs'.format(ee_site_webroot)) if not os.path.exists('{0}/logs'.format(ee_site_webroot)): os.makedirs('{0}/logs'.format(ee_site_webroot)) EEFileUtils.create_symlink(self, ['/var/log/nginx/{0}.access.log' .format(ee_domain_name), '{0}/logs/access.log' .format(ee_site_webroot)]) EEFileUtils.create_symlink(self, ['/var/log/nginx/{0}.error.log' .format(ee_domain_name), '{0}/logs/error.log' .format(ee_site_webroot)]) except Exception as e: Log.debug(self, "{0}".format(e)) raise SiteError("setup webroot failed for site") finally: # TODO Check if directories are setup if (os.path.exists('{0}/htdocs'.format(ee_site_webroot)) and os.path.exists('{0}/logs'.format(ee_site_webroot))): Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") else: Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") raise SiteError("setup webroot failed for site") def setupdatabase(self, data): ee_domain_name = data['site_name'] ee_random = (''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase + string.digits, 15))) ee_replace_dot = ee_domain_name.replace('.', '_') prompt_dbname = self.app.config.get('mysql', 'db-name') prompt_dbuser = self.app.config.get('mysql', 'db-user') ee_mysql_grant_host = self.app.config.get('mysql', 'grant-host') ee_db_name = '' ee_db_username = '' ee_db_password = '' if prompt_dbname == 'True' or prompt_dbname == 'true': try: ee_db_name = input('Enter the MySQL database name [{0}]: ' .format(ee_replace_dot)) except EOFError as e: Log.debug(self, "{0}".format(e)) raise SiteError("Unable to input database name") if not ee_db_name: ee_db_name = ee_replace_dot if prompt_dbuser == 'True' or prompt_dbuser == 'true': try: ee_db_username = input('Enter the MySQL database user name [{0}]: ' .format(ee_replace_dot)) ee_db_password = getpass.getpass(prompt='Enter the MySQL database' ' password [{0}]: ' .format(ee_random)) except EOFError as e: Log.debug(self, "{0}".format(e)) raise SiteError("Unable to input database credentials") if not ee_db_username: ee_db_username = ee_replace_dot if not ee_db_password: ee_db_password = ee_random if len(ee_db_username) > 16: Log.debug(self, 'Autofix MySQL username (ERROR 1470 (HY000)),' ' please wait') ee_db_username = (ee_db_name[0:6] + generate_random()) # create MySQL database Log.info(self, "Setting up database\t\t", end='') Log.debug(self, "Creating database {0}".format(ee_db_name)) if EEMysql.check_db_exists(self, ee_db_name): Log.debug(self, "Database already exists, Updating DB_NAME .. ") ee_db_name = (ee_db_name[0:6] + generate_random()) ee_db_username = (ee_db_name[0:6] + generate_random()) try: EEMysql.execute(self, "create database `{0}`" .format(ee_db_name)) except StatementExcecutionError as e: Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") raise SiteError("create database execution failed") # Create MySQL User Log.debug(self, "Creating user {0}".format(ee_db_username)) Log.debug(self, "create user `{0}`@`{1}` identified by ''" .format(ee_db_username, ee_mysql_grant_host)) try: EEMysql.execute(self, "create user `{0}`@`{1}` identified by '{2}'" .format(ee_db_username, ee_mysql_grant_host, ee_db_password), log=False) except StatementExcecutionError as e: Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") raise SiteError("creating user failed for database") # Grant permission Log.debug(self, "Setting up user privileges") try: EEMysql.execute(self, "grant all privileges on `{0}`.* to `{1}`@`{2}`" .format(ee_db_name, ee_db_username, ee_mysql_grant_host)) except StatementExcecutionError as e: Log.info(self, "[" + Log.ENDC + Log.FAIL + "Failed" + Log.OKBLUE + "]") SiteError("grant privileges to user failed for database ") Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") data['ee_db_name'] = ee_db_name data['ee_db_user'] = ee_db_username data['ee_db_pass'] = ee_db_password data['ee_db_host'] = EEVariables.ee_mysql_host return(data) def setupwordpress(self, data): ee_domain_name = data['site_name'] ee_site_webroot = data['webroot'] prompt_wpprefix = self.app.config.get('wordpress', 'prefix') ee_wp_user = self.app.config.get('wordpress', 'user') ee_wp_pass = self.app.config.get('wordpress', 'password') ee_wp_email = self.app.config.get('wordpress', 'email') # Random characters ee_random = (''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase + string.digits, 15))) ee_wp_prefix = '' # ee_wp_user = '' # ee_wp_pass = '' Log.info(self, "Downloading Wordpress \t\t", end='') EEFileUtils.chdir(self, '{0}/htdocs/'.format(ee_site_webroot)) try: EEShellExec.cmd_exec(self, "wp --allow-root core" " download") except CommandExecutionError as e: Log.info(self, "[" + Log.ENDC + Log.FAIL + "Fail" + Log.OKBLUE + "]") raise SiteError(self, "download wordpress core failed") Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") if not (data['ee_db_name'] and data['ee_db_user'] and data['ee_db_pass']): data = setupdatabase(self, data) if prompt_wpprefix == 'True' or prompt_wpprefix == 'true': try: ee_wp_prefix = input('Enter the WordPress table prefix [wp_]: ') while not re.match('^[A-Za-z0-9_]*$', ee_wp_prefix): Log.warn(self, "table prefix can only " "contain numbers, letters, and underscores") ee_wp_prefix = input('Enter the WordPress table prefix [wp_]: ' ) except EOFError as e: Log.debug(self, "{0}".format(e)) raise SiteError("input table prefix failed") if not ee_wp_prefix: ee_wp_prefix = 'wp_' # Modify wp-config.php & move outside the webroot EEFileUtils.chdir(self, '{0}/htdocs/'.format(ee_site_webroot)) Log.debug(self, "Setting up wp-config file") if not data['multisite']: Log.debug(self, "Generating wp-config for WordPress Single site") Log.debug(self, "bash -c \"php /usr/bin/wp --allow-root " + "core config " + "--dbname=\'{0}\' --dbprefix=\'{1}\' --dbuser=\'{2}\' " "--dbhost=\'{3}\' " .format(data['ee_db_name'], ee_wp_prefix, data['ee_db_user'], data['ee_db_host']) + "--dbpass= " "--extra-php< {1}/{0}.sql" .format(ee_db_name, backup_path)) except CommandExecutionError as e: Log.info(self, "[" + Log.ENDC + "Fail" + Log.OKBLUE + "]") raise SiteError("mysqldump failed to backup database") Log.info(self, "[" + Log.ENDC + "Done" + Log.OKBLUE + "]") # move wp-config.php/ee-config.php to backup if data['currsitetype'] in ['mysql']: EEFileUtils.mvfile(self, configfiles[0], backup_path) else: EEFileUtils.copyfile(self, configfiles[0], backup_path) def site_package_check(self, stype): apt_packages = [] packages = [] stack = EEStackController() stack.app = self.app if stype in ['html', 'php', 'mysql', 'wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting apt_packages variable for Nginx") if not EEAptGet.is_installed(self, 'nginx-common'): apt_packages = apt_packages + EEVariables.ee_nginx if stype in ['php', 'mysql', 'wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting apt_packages variable for PHP") if not EEAptGet.is_installed(self, 'php5-fpm'): apt_packages = apt_packages + EEVariables.ee_php if stype in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting apt_packages variable for MySQL") if not EEShellExec.cmd_exec(self, "mysqladmin ping"): apt_packages = apt_packages + EEVariables.ee_mysql if stype in ['php', 'mysql', 'wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting apt_packages variable for Postfix") if not EEAptGet.is_installed(self, 'postfix'): apt_packages = apt_packages + EEVariables.ee_postfix if stype in ['wp', 'wpsubdir', 'wpsubdomain']: Log.debug(self, "Setting packages variable for WP-CLI") if not EEShellExec.cmd_exec(self, "which wp"): packages = packages + [["https://github.com/wp-cli/wp-cli/" "releases/download/v{0}/" "wp-cli-{0}.phar" .format(EEVariables.ee_wp_cli), "/usr/bin/wp", "WP-CLI"]] return(stack.install(apt_packages=apt_packages, packages=packages, disp_msg=False)) def updatewpuserpassword(self, ee_domain, ee_site_webroot): ee_wp_user = '' ee_wp_pass = '' EEFileUtils.chdir(self, '{0}/htdocs/'.format(ee_site_webroot)) # Check if ee_domain is wordpress install try: is_wp = EEShellExec.cmd_exec(self, "wp --allow-root core" " version") except CommandExecutionError as e: raise SiteError("is wordpress site? check command failed ") # Exit if ee_domain is not wordpress install if not is_wp: Log.error(self, "{0} does not seem to be a WordPress site" .format(ee_domain)) try: ee_wp_user = input("Provide WordPress user name [admin]: ") except Exception as e: Log.debug(self, "{0}".format(e)) Log.error(self, "\nCould not update password") if ee_wp_user == "?": Log.info(self, "Fetching WordPress user list") try: EEShellExec.cmd_exec(self, "wp --allow-root user list " "--fields=user_login | grep -v user_login") except CommandExecutionError as e: raise SiteError("fetch wp userlist command failed") if not ee_wp_user: ee_wp_user = 'admin' try: is_user_exist = EEShellExec.cmd_exec(self, "wp --allow-root user list " "--fields=user_login | grep {0}$ " .format(ee_wp_user)) except CommandExecutionError as e: raise SiteError("if wp user exists check command failed") if is_user_exist: try: ee_wp_pass = getpass.getpass(prompt="Provide password for " "{0} user: " .format(ee_wp_user)) except Exception as e: Log.debug(self, "{0}".format(e)) raise SiteError("failed to get input password ") if len(ee_wp_pass) > 8: try: EEShellExec.cmd_exec(self, "wp --allow-root user update {0}" " --user_pass={1}" .format(ee_wp_user, ee_wp_pass)) except CommandExecutionError as e: raise SiteError("wp user password update command failed") Log.info(self, "Password updated successfully") else: Log.error(self, "Password Unchanged. Hint : Your password must be " "8 characters long") else: Log.error(self, "Invalid WordPress user {0} for {1}." .format(ee_wp_user, ee_domain)) def display_cache_settings(self, data): if data['wpsc']: if data['multisite']: Log.info(self, "Configure WPSC:" "\t\thttp://{0}/wp-admin/network/settings.php?" "page=wpsupercache" .format(data['site_name'])) else: Log.info(self, "Configure WPSC:" "\t\thttp://{0}/wp-admin/options-general.php?" "page=wpsupercache" .format(data['site_name'])) if data['wpfc']: if data['multisite']: Log.info(self, "Configure nginx-helper:" "\thttp://{0}/wp-admin/network/settings.php?" "page=nginx".format(data['site_name'])) else: Log.info(self, "Configure nginx-helper:" "\thttp://{0}/wp-admin/options-general.php?" "page=nginx".format(data['site_name'])) if data['wpfc'] or data['w3tc']: if data['multisite']: Log.info(self, "Configure W3TC:" "\t\thttp://{0}/wp-admin/network/admin.php?" "page=w3tc_general".format(data['site_name'])) else: Log.info(self, "Configure W3TC:" "\t\thttp://{0}/wp-admin/admin.php?" "page=w3tc_general".format(data['site_name'])) if data['wpfc']: Log.info(self, "Page Cache:\t\tDisable") elif data['w3tc']: Log.info(self, "Page Cache:\t\tDisk Enhanced") Log.info(self, "Database Cache:\t\tMemcached") Log.info(self, "Object Cache:\t\tMemcached") Log.info(self, "Browser Cache:\t\tDisable") def logwatch(self, logfiles): import zlib import base64 import time from ee.core import logwatch def callback(filename, lines): for line in lines: if line.find(':::') == -1: print(line) else: data = line.split(':::') try: print(data[0], data[1], zlib.decompress(base64.decodestring(data[2]))) except Exception as e: Log.info(time.time(), 'caught exception rendering a new log line in %s' % filename) l = logwatch.LogWatcher(logfiles, callback) l.loop() def detSitePar(opts): """ Takes dictionary of parsed arguments 1.returns sitetype and cachetype 2. raises RuntimeError when wrong combination is used like "--wp --wpsubdir" or "--html --wp" """ sitetype, cachetype = '', '' typelist = list() cachelist = list() for key, val in opts.items(): if val and key in ['html', 'php', 'mysql', 'wp', 'wpsubdir', 'wpsubdomain']: typelist.append(key) elif val and key in ['wpfc', 'wpsc', 'w3tc']: cachelist.append(key) if len(typelist) > 1 or len(cachelist) > 1: raise RuntimeError("could not determine site and cache type") else: if not typelist and not cachelist: sitetype = 'html' cachetype = 'basic' elif (not typelist) and cachelist: sitetype = 'wp' cachetype = cachelist[0] elif typelist and (not cachelist): sitetype = typelist[0] cachetype = 'basic' else: sitetype = typelist[0] cachetype = cachelist[0] return (sitetype, cachetype) def generate_random(): ee_random10 = (''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase + string.digits, 10))) return ee_random10