install.py 18.5 KB
#!/usr/bin/env python3

import platform
import os, sys
import subprocess
import shutil
import fileinput


# By default, install the virtual environment AND the database
INSTALL_VENV = True
INSTALL_DB = True

VENV = "venv_py3_pyros"

SQL_DATABASE = "pyros"
SQL_DATABASE_TEST = "pyros_test"
SQL_USER = "pyros"
SQL_PSWD = "DjangoPyros"
MYSQL_EXE_PATH = ""

REQUIREMENTS = 'REQUIREMENTS.txt'
#REQUIREMENTS = 'REQUIREMENTS_36.txt'
#REQUIREMENTS = 'REQUIREMENTS_37.txt'
END_OF_LINE = '\n\n'
VENV_BIN = '/bin/'
WINDOWS = False
# --------------------------------------------
# --- Modified values for Windows
# --------------------------------------------
if (platform.system() == "Windows"):
    WINDOWS = True 
    REQUIREMENTS = 'REQUIREMENTS_WINDOWS.txt'
    #REQUIREMENTS = 'REQUIREMENTS_WINDOWS_36.txt'
    END_OF_LINE = "\r\n\r\n"
    VENV_BIN = '\\Scripts\\'
    #MYSQL_EXE_PATH = "C:/Program Files (x86)/MySQL/MySQL Server 5.0/bin/"
    #question = "Enter the path of the MySQL server if it is not the following name (" + MYSQL_EXE_PATH + "): "
    #res = input(question)
    #if res!="":
    #   MYSQL_EXE_PATH = res
VENV_PIP = VENV + VENV_BIN+'pip'
VENV_PYTHON = VENV + VENV_BIN+'python'


class Colors:
    if WINDOWS:
         ERROR = ''
         END = ''
         LOG_BLUE = ''
    else:
         ERROR = '\033[91m'
         END = '\033[0m'
         LOG_BLUE = '\033[94m'


# GLOBAL_PYTHON = 'python3'
GLOBAL_PYTHON = os.path.split(sys.executable)[-1]
print(Colors.LOG_BLUE + "Python executable is " + GLOBAL_PYTHON + Colors.END)
##if platform.dist()[0] == "centos": print("centos platform")





def replacePatternInFile(pattern, replace, file_path):
    try:
        with fileinput.FileInput(file_path, inplace=True, backup='.bak') as file:
            for line in file:
                print(line.replace(pattern, replace), end='')
    except:
        sys.stderr.write(Colors.ERROR + "ERROR !: replacement in file failed !" + Colors.END + "\r\n")
        return 1
    return 0


def install_dependency_ubuntu(command, mode):
    '''
    Install dependency then check the return code
    '''
    old = command
    if (mode == 'i'):
        command = 'apt-get install ' + command
    elif (mode == 'u'):
        command = 'apt-get update'
    elif (mode == 'a'):
        command = 'add-apt-repository ' + command
    process = subprocess.Popen(command, shell=True)
    process.wait()
    if process.returncode != 0:
        sys.stderr.write(Colors.ERROR + "ERROR !: installation of " + old + " failed !" + Colors.END + "\r\n")


def install_required_ubuntu():
    install_dependency_ubuntu("update", 'u')
    install_dependency_ubuntu("python-lxml", 'i')
    install_dependency_ubuntu("libxml2-dev", 'i')
    install_dependency_ubuntu("libxslt-dev", 'i')
    install_dependency_ubuntu("zlib1g-dev", 'i')
    install_dependency_ubuntu("update", 'u')
    install_dependency_ubuntu("rabbitmq-server", 'i')
    #install_dependency_ubuntu("libmysqlclient-dev", 'i')


def install_dependency_centos(command, mode):
    old = command
    if (mode == 'i'):
        command = 'yum -y install ' + command
    elif (mode == 'u'):
        command = 'yum update ' + command
    process = subprocess.Popen(command, shell=True)
    process.wait()
    if process.returncode != 0:
        sys.stderr.write(Colors.ERROR + "ERROR !: installation of " + old + " failed !" + Colors.END + "\r\n")


def install_required_centos():
    install_dependency_centos("yum", 'u')
    install_dependency_centos("kernel", 'u')
    install_dependency_centos("", 'u')
    install_dependency_centos("libxml2", 'i')
    install_dependency_centos("libxslt libxslt-2", 'i')
    install_dependency_centos("libxslt-devel libxml2-devel", 'i')
    install_dependency_centos("rabbitmq-server", 'i')
    install_dependency_centos("mariadb-server", 'i')
    install_dependency_centos("mariadb", 'i')
    install_dependency_centos("mariadb-devel", 'i')

    process = subprocess.Popen("systemctl start mariadb.service", shell=True)
    process.wait()
    if process.returncode != 0:
        sys.stderr.write(Colors.ERROR + "ERROR !" + Colors.END + "\r\n")

    process = subprocess.Popen("systemctl enable mariadb.service", shell=True)
    process.wait()
    if process.returncode != 0:
        sys.stderr.write(Colors.ERROR + "ERROR !" + Colors.END + "\r\n")

    process = subprocess.Popen("mysql_secure_installation", shell=True)
    process.wait()
    if process.returncode != 0:
        sys.stderr.write(Colors.ERROR + "ERROR !" + Colors.END + "\r\n")


def install_required():
    # Checking if user is sudo then install the needed dependencies
    # Find the linux distribution and call the related function
    distribution = platform.dist()
    if not 'SUDO_UID' in os.environ.keys():
        sys.stderr.write("Super user rights are needed to install prerequisites\r\n")
        exit(1)
    if distribution[0] == "Ubuntu" or distribution[0] == "Debian":
        install_required_ubuntu()
    elif distribution[0] == "centos":
        install_required_centos()
    else:
        print("Requirements are made for Ubuntu, Debian and CentOS only")
        exit(1)


def venv_pip_install(package_name:str, options:str=''):
    os.system(VENV_PIP + ' install ' + options + ' ' + package_name)


def install_venv(venv:str):

    # --------------------------------------------
    # --- Be aware not to create virtual environment in case of user root
    # --------------------------------------------
    if 'SUDO_UID' in os.environ.keys():
        answer = input(
            "You are about to install your virtualenv only for root, this is discouraged, are you sure ? (Y/N) If you are not sure, relaunch the script without super user privileges\n")
        while (answer != 'Y' and answer != 'y' and answer != 'n' and answer != 'N'):
            answer = input(
                "You are about to install your virtualenv only for root, this is discouraged, are you sure ? (Y/N) \n")
        if (answer not in ['y', 'Y']):
            exit(1)

    # --------------------------------------------
    # --- Create the (private) venv directory to put in files for virtual environment
    # --------------------------------------------
    if (os.path.basename(os.getcwd()) != "venv"):
        if not(os.path.isdir("../venv")):
            print(Colors.LOG_BLUE + "-----------------------------Creating \'venv\' directory-----------------------------" + Colors.END)
            os.mkdir("../venv")

    
    # --------------------------------------------
    # --- Deleting if already exist then creating the venv
    # --------------------------------------------
    #print(Colors.LOG_BLUE + "-----------------------------cd venv-----------------------------" + Colors.END)
    os.chdir("../venv/")
    while True:
        try:
            if (os.path.isdir(venv)):
                print(Colors.LOG_BLUE + "-----------------------------Deleting existing venv-----------------------------" + Colors.END)
                shutil.rmtree(venv)
            break
        # Exception on Windows WinError 145 : Cannot remove folder because files in folder not yet removed...
        except Exception as e:
            #print(e)
            continue

    # --------------------------------------------
    # --- Reinstall the virtual environment (from ../venv/)
    # --------------------------------------------
    
    print(Colors.LOG_BLUE + "-----------------------------Creating venv " + venv + "-----------------------------"+END_OF_LINE + Colors.END)
    os.system(GLOBAL_PYTHON+" -m venv " + venv)
        
    print(Colors.LOG_BLUE + "-----------------------------Upgrade pip, wheel, and setuptools" + "-----------------------------"+END_OF_LINE + Colors.END)
    # Upgrade pip
    os.system(VENV_PYTHON + ' -m pip install --upgrade pip')
    '''
    if (platform.system() == "Windows"):    
        os.system(venv + '\Scripts\python -m pip install --upgrade pip')
    else: # Linux
        os.system(venv + '/bin/python -m pip install --upgrade pip')
    '''
    
    # Pip upgrade wheel and setuptools
    venv_pip_install('wheel', '--upgrade')
    #os.system(VENV_PIP+' install --upgrade wheel')
    venv_pip_install('setuptools', '--upgrade')
    #os.system(VENV_PIP+' install --upgrade setuptools')
    
    # Pip install required packages from REQUIREMENTS file
    print()
    print(Colors.LOG_BLUE + "-----------------------------Installing python packages via pip-----------------------------" + Colors.END)
    venv_pip_install('../install/'+REQUIREMENTS, '-r')
    #os.system(VENV_PIP+' install -r ../install' + os.sep + REQUIREMENTS)
    
    #print(Colors.LOG_BLUE + "-----------------------------cd ../install-----------------------------" + Colors.END)
    os.chdir("../install")

    if WINDOWS:
        ## moving voeventparse in site-packages directory
        try:
            site_packages = "..\\venv\\"+VENV+"\\Lib\\site-packages\\"
            if (not os.path.isdir(site_packages + "voevent_parse-0.9.5.dist-info") and
            not os.path.isdir(site_packages + "voeventparse")):
                print(Colors.LOG_BLUE + "\r\n\r\n-----------------------------Copying the voevent library in Lib/site-packages-----------------------------" + Colors.END)
                cmdline = "xcopy /i /y windows\\voeventparse " + site_packages + "voeventparse"
                process = subprocess.Popen(cmdline)
                process.wait()
                if (process.returncode != 0): raise Exception
                process = subprocess.Popen("xcopy /i /y windows\\voevent_parse-0.9.5.dist-info " + site_packages + "voevent_parse-0.9.5.dist-info")
                process.wait()
                if (process.returncode != 0): raise Exception
                print(Colors.LOG_BLUE + "\r\n-----------------------------library successfully copied-----------------------------" + Colors.END)
        except Exception as e:
            print(Colors.ERROR + "ERROR while Copying the voevent library in Lib/site-packages" + Colors.END) ; #, file=stderr)
            return False    
    return 0
    
def install_database(venv):

    print(Colors.LOG_BLUE + END_OF_LINE+"-----------------------------Launching mysql to create database and create and grant user pyros-----------------------------" + Colors.END)
  
    # --------------------------------------------
    # --- Determine the MySQL version
    # --------------------------------------------
    output = subprocess.check_output("mysql --version", shell=True)
    # output is something like: "mysql  Ver 15.1 Distrib 10.0.20-MariaDB, for Linux (x86_64) using  EditLine wrapper"
    tmp = (str(output).split()[4]).split('.')
    sql_version = float(tmp[0]+'.'+tmp[1])
    print(Colors.LOG_BLUE + "MySQL version is " + str(sql_version) + Colors.END)

    # --------------------------------------------
    # --- Prepare the SQL query to create and initialize the pyros database if needed
    # --------------------------------------------
    if sql_version<5.5:
        #sql_query = "drop database "+SQL_DATABASE+" ; CREATE DATABASE "+SQL_DATABASE+"; drop database "+SQL_DATABASE_TEST+" ; CREATE DATABASE "+SQL_DATABASE_TEST+"; CREATE USER "+SQL_USER+" ; GRANT USAGE ON *.* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"' WITH GRANT OPTION; DROP USER '"+SQL_USER+"'@'localhost'; GRANT ALL ON "+SQL_DATABASE+".* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"'; GRANT ALL PRIVILEGES ON "+SQL_DATABASE+".* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"' WITH GRANT OPTION;  GRANT ALL PRIVILEGES ON "+SQL_DATABASE_TEST+".* TO "+SQL_USER+"@localhost IDENTIFIED BY '"+SQL_PSWD+"' WITH GRANT OPTION;"
        sql_query = ""
        sql_query += "drop database "+SQL_DATABASE+" ; CREATE DATABASE "+SQL_DATABASE+"; "
        sql_query += "drop database "+SQL_DATABASE_TEST+" ; CREATE DATABASE "+SQL_DATABASE_TEST+"; "
        sql_query += "CREATE USER "+SQL_USER+" ; GRANT USAGE ON *.* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"' WITH GRANT OPTION; "
        sql_query += "DROP USER '"+SQL_USER+"'@'localhost'; "
        sql_query += "GRANT ALL ON "+SQL_DATABASE+".* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"'; "
        sql_query += "GRANT ALL PRIVILEGES ON "+SQL_DATABASE+".* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"' WITH GRANT OPTION; "
        sql_query += "GRANT ALL PRIVILEGES ON "+SQL_DATABASE_TEST+".* TO "+SQL_USER+"@localhost IDENTIFIED BY '"+SQL_PSWD+"' WITH GRANT OPTION; "
    else:
        sql_query = "CREATE DATABASE IF NOT EXISTS " + SQL_DATABASE +"; " +\
            "CREATE DATABASE IF NOT EXISTS "+SQL_DATABASE_TEST+"; " +\
            "CREATE USER IF NOT EXISTS "+SQL_USER+"; " +\
            "GRANT USAGE ON *.* TO '"+SQL_USER+"'; " +\
            "DROP USER '"+SQL_USER+"'; " +\
            "GRANT ALL ON "+SQL_DATABASE+".* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"'; " +\
            "GRANT ALL ON "+SQL_DATABASE_TEST+".* TO '"+SQL_USER+"'@'localhost'; " +\
            "GRANT ALL PRIVILEGES ON "+SQL_DATABASE_TEST+".* TO '"+SQL_USER+"'@'localhost'; " +\
            "GRANT ALL ON "+SQL_DATABASE_TEST+".* TO '"+SQL_USER+"'@'localhost' IDENTIFIED BY '"+SQL_PSWD+"' ;"
        # NEWER MYSQL:
        # OLDER MYSQL: Try this instead for OLDER mysql (works on CentOS 6.4 and Centos 7.5 with mysql 5.5):
        #req = "drop database pyros; CREATE DATABASE pyros; drop database pyros_test ; CREATE DATABASE pyros_test; drop user 'pyros'@'localhost' ; CREATE USER pyros; GRANT USAGE ON *.* TO 'pyros'; DROP USER 'pyros'; GRANT ALL ON pyros.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros'; GRANT ALL ON test_pyros.* TO 'pyros'@'localhost'; GRANT ALL PRIVILEGES ON test_pyros_test.* TO 'pyros'@'localhost'; GRANT ALL ON pyros_test.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros'"
        #req = "drop database pyros ; CREATE DATABASE pyros; drop database pyros_test ; CREATE DATABASE pyros_test; DROP USER 'pyros'@'localhost' ; GRANT USAGE ON *.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros' WITH GRANT OPTION; DROP USER 'pyros'@'localhost'; GRANT ALL ON pyros.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros'; GRANT ALL PRIVILEGES ON pyros.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros' WITH GRANT OPTION;  GRANT ALL PRIVILEGES ON pyros_test.* TO pyros@localhost IDENTIFIED BY 'DjangoPyros' WITH GRANT OPTION;"
        # (EP) ok for CENTOS 7 I suppose (but not for CentOS 6):
        #req_centos = "CREATE DATABASE IF NOT EXISTS pyros; CREATE DATABASE IF NOT EXISTS pyros_test; GRANT USAGE ON *.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros' WITH GRANT OPTION; DROP USER 'pyros'@'localhost'; GRANT ALL ON pyros.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros'; GRANT ALL PRIVILEGES ON pyros.* TO 'pyros'@'localhost' IDENTIFIED BY 'DjangoPyros' WITH GRANT OPTION;  GRANT ALL PRIVILEGES ON pyros_test.* TO pyros@localhost IDENTIFIED BY 'DjangoPyros' WITH GRANT OPTION;"
    
    # --- Prepare the SQL query to create and initialize the pyros database if needed
    #if platform.dist()[0] == "centos":
    #    req = sql_query
    mysql_call_root = '"' + MYSQL_EXE_PATH + 'mysql" -u root -p'
    mysql_call_pyros = "\"" + MYSQL_EXE_PATH+ "mysql\" -u "+SQL_USER+" -p"
    
    # --------------------------------------------
    # --- Creating database and creating and granting user pyros
    # --------------------------------------------
    user_ros_is_created = True
    if sql_version<5.5:
       print(Colors.LOG_BLUE +"------------------ Check if the user pyros exists in MYSQL (type the pyros password) -----------------------------" + Colors.END)
       # --- We are testing if user pyros already exists in the database
       process = subprocess.Popen("echo quit |" + mysql_call_pyros, shell=True)
       process.wait()
       if (process.returncode == 0):
           user_ros_is_created = False
    if user_ros_is_created:
       # --- The user pyros must be created in the database
       print(Colors.LOG_BLUE +"-----------------------------Please enter your MYSQL root password-----------------------------" + Colors.END)
       #process = subprocess.Popen("echo \"" + sql_query + "\" |"+ mysql_call_root, shell=True)
       sql_cmd = 'echo "' + sql_query + '" | '+ mysql_call_root
       print("Executing sql cmd: ", sql_cmd)
       process = subprocess.Popen(sql_cmd, shell=True)
       process.wait()
    if (process.returncode != 0):
        sys.stderr.write(Colors.ERROR + "ERROR !: db configuration failed !" + Colors.END + "\r\n")
        return -1
    print(Colors.LOG_BLUE + END_OF_LINE+"-----------------------------Database created and user pyros successfully created and granted-----------------------------" + Colors.END)

    # --------------------------------------------
    # --- Replacing pattern in settings.py to use mysql
    # --------------------------------------------
    print(Colors.LOG_BLUE + "-----------------------------setting MYSQL = True in settings-----------------------------" + Colors.END)
    replacePatternInFile("MYSQL = False", "MYSQL = True", os.path.normpath("../src/core/pyros_django/pyros/settings.py"))
    
    #print(Colors.LOG_BLUE + "\r\n-----------------------------cd ..-----------------------------" + Colors.END)
    os.chdir("..")

    # --------------------------------------------
    # ---  Executing migrations
    # --------------------------------------------
    
    print(Colors.LOG_BLUE + "\r\n\r\n-----------------------------Migrate : executing pyros.py init_database-----------------------------" + Colors.END)
    #TODO: from venv !!!
    try:
        #os.system(GLOBAL_PYTHON+" pyros.py init_database")
        os.system(GLOBAL_PYTHON+" pyros.py initdb")
        '''
        process = subprocess.Popen(GLOBAL_PYTHON + " pyros.py init_database"  , shell=True)
        process.wait()
        '''
    except Exception as e:
        print("Exception ", e)
        print(Colors.ERROR + "Error while initializing database :" + Colors.END)
        return -1

    print(Colors.LOG_BLUE + "\r\n\r\n-----------------------------Install successfull !-----------------------------" + Colors.END)
    return 0


def _help():
    print(
        "Welcome in the installation script of the pyros venv.\t\nPlease launch it from the install directory of pyros.\n\tIf you're on Ubuntu Debian or CentOS:\n\tlaunch it with sudo and <--prerequisites> or <-p> to install the prerequisites.\n\t-->sudo ./test_install.py -p\n\n\tFor the python packages launch it from the install directory of pyros without sudo and without parameter\n\t-->./test_install.py")





if __name__ == '__main__':
    if (len(sys.argv) > 1):
        if sys.argv[1] == "--prerequisites" or sys.argv[1] == "-p":
            install_required()
        else:
            _help()
    elif len(sys.argv) == 1:
        if INSTALL_VENV: install_venv(VENV)
        if INSTALL_DB: install_database(VENV)

    else:
        _help()