#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c) 2020 Huawei Technologies Co.,Ltd.
#
# openGauss is licensed under Mulan PSL v2.
# You can use this software according to the terms
# and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
#          http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# ----------------------------------------------------------------------------
# Description  : gs_preinstall is a utility to create an installation
# environment for a cluster.
#############################################################################

import os
import pwd
import sys
import grp
import subprocess
from gspylib.common.CheckPythonVersion import check_python_version, \
    check_python_compiler_option, check_os_and_package_arch, check_package_name

package_path = os.path.dirname(os.path.realpath(__file__))
lib_path = os.path.join(package_path, "..", "lib")
clib_files = os.path.join(package_path, "gspylib/clib/*.so*")
if os.listdir(lib_path):
    check_os_and_package_arch()
    check_python_version()
    check_python_compiler_option()

from base_utils.os.file_util import FileUtil
from gspylib.common.GaussLog import GaussLog
if "--unused-third-party" in sys.argv:
    FileUtil.cleanDirectoryContent(lib_path)
    FileUtil.removeFile(clib_files)

    # use system pip dependecies
    import psutil
    import netifaces
    import cryptography
    import paramiko
else:
    check_os_and_package_arch()
    check_python_version()
    check_python_compiler_option()
    check_package_name()
    from gspylib.common.copy_python_lib import copy_lib
    copy_lib()

from gspylib.common.Common import DefaultValue
from gspylib.common.ErrorCode import ErrorCode
from gspylib.common.ParallelBaseOM import ParallelBaseOM
from gspylib.common.ParameterParsecheck import Parameter
from gspylib.common.DbClusterInfo import dbNodeInfo, \
    dbClusterInfo, compareObject
from impl.preinstall.OLAP.PreinstallImplOLAP import PreinstallImplOLAP
from gspylib.threads.SshTool import SshTool
from domain_utils.cluster_file.cluster_config_file import ClusterConfigFile
from domain_utils.cluster_file.cluster_dir import ClusterDir
from domain_utils.cluster_file.profile_file import ProfileFile
from domain_utils.cluster_file.version_info import VersionInfo
from domain_utils.cluster_os.cluster_user import ClusterUser
from base_utils.os.net_util import NetUtil
from base_utils.os.file_util import FileUtil
from domain_utils.domain_common.cluster_constants import ClusterConstants
from base_utils.os.user_util import UserUtil
from base_utils.template.xml_template import GenerateTemplate
from base_utils.os.hosts_util import HostsUtil


#############################################################################
# Global variables
#############################################################################
userNameFirtChar = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
# warning type
g_warningType = [5]

class Preinstall(ParallelBaseOM):
    def __init__(self):
        ParallelBaseOM.__init__(self)
        self.password = ""
        self.envParams = []
        self.warningType = "5"
        self.rootUser = ""
        self.rootPasswd = ""
        self.create_user_ssh_trust = True
        self.clusterToolPath = ""
        self.needFixOwnerPaths = []
        self.preMode = False
        self.skipOSSet = False
        self.skipHostnameSet = False
        self.passwordsec = ""
        self.corePath = ""
        self.is_new_root_path = False
        self.ips = ""
        self.root_ssh_agent_flag = False
        self.root_delete_flag = False
        self.user_ssh_agent_flag = False
        self.enable_dss = ""
        self.dss_vg_info = ""
        self.dss_vgname = ""
        self.enable_perf_config = False
        self.skip_cgroup_set = False
        self.cluster_core_path = ""
        self.om_version_cfg = None  # structure like {'pkg_prefix': '', 'pgversion': '', 'commit': '', 'mode': ''}
        self.og_version_cfg = None
        self.enable_hugebin = False

    def usage(self):
        """
gs_preinstall is a utility to create an installation environment for a cluster.

Usage:
    gs_preinstall -? | --help
    gs_preinstall -V | --version
    gs_preinstall -U USER -G GROUP -X XMLFILE --alarm-type=ALARMTYPE [--alarm] [--alarm-server-addr=HOSTADDR]
      [-L]  [--skip-os-set] [--env-var="ENVVAR" [...]]
      [--sep-env-file=ENVFILE] [--skip-hostname-set] [-l LOGFILE]
      [--non-interactive] [--delete-root-trust] [--unused-third-party]

General options:
    -U                                 Cluster user.
    -G                                 Group of the cluster user.
    -X                                 Path of the XML configuration file.
        --alarm-type=ALARMTYPE         Default value is 5, the PublicCloudScene format is used.
        --alarm-server-addr=HOSTADDR   IP address of the warning server.
    -L                                 Only perform preinstallation on local
                                       nodes.
        --skip-os-set                  Whether to skip OS parameter setting.
                                       (The default value is set.)
        --env-var="ENVVAR"             OS user environment variables.
        --sep-env-file=ENVFILE       Path of the MPP environment file.
        --skip-hostname-set            Whether to skip hostname setting.
                                       (The default value is set.)
        --skip-cgroup-set               Whether to skip cgroup setting.
        --one-stop-install             Interactive one stop installation
                                        (The default value is unset.)
    -l                                 Path of log file.
    -?, --help                         Show help information for this
                                       utility, and exit the command line mode.
    -V, --version                      Show version information.
        --non-interactive              Pre-execution of non-secure mode.
                                     If it is not specified, you can choose
                                     whether create the SSH trust for root
                                     user or cluster user.
                                     If it is specified, you must ensure the
                                     SSH trust for root user and cluster
                                     user have been created.
        --delete-root-trust            Whether to delete root trust.
                                       (The default value is not deleted)
        --unused-third-party           Whether to use om's third-party.
                                       (The default value is used)
        --enable-perf-config           Use gs_perfconfig to tune os configuration
                                       after pre installation.
        """
        print(self.usage.__doc__)

    # get parameter from command
    def parseCommandLine(self):
        """
        function: Parse command line and save to global variable
        input: NA
        output: NA
        """
        # init the ParaObj
        ParaObj = Parameter()
        ParaDict = ParaObj.ParameterCommandLine("preinstall")
        # parameter -h or -?
        if (ParaDict.__contains__("helpFlag")):
            self.usage()
            sys.exit(0)

        # check whether one-stop install  one-stop-install
        if (ParaDict.__contains__("one_stop_install")):
            xml_template = GenerateTemplate()
            xml_template.run()
            self.xmlFile = xml_template.target_xml

        # check no root user paramters
        self.check_no_root_paramter(ParaDict)
        # Resolves command line arguments
        # parameter -U
        if (ParaDict.__contains__("user")):
            self.user = ParaDict.get("user")
            DefaultValue.checkPathVaild(self.user)
        # parameter -G
        if (ParaDict.__contains__("group")):
            self.group = ParaDict.get("group")
        # parameter -X
        if (ParaDict.__contains__("confFile") and not ParaDict.__contains__("one_stop_install")):
            self.xmlFile = ParaDict.get("confFile")
        # parameter -L
        if (ParaDict.__contains__("localMode")):
            self.localMode = ParaDict.get("localMode")
        # parameter -l
        if (ParaDict.__contains__("logFile")):
            self.logFile = ParaDict.get("logFile")
        # parameter --env-var
        if (ParaDict.__contains__("envparams")):
            self.envParams = ParaDict.get("envparams")
        # parameter --sep-env-file
        if (ParaDict.__contains__("mpprcFile")):
            self.mpprcFile = ParaDict.get("mpprcFile")
            DefaultValue.checkPathVaild(self.mpprcFile)
        # parameter --alarm-type
        if (ParaDict.__contains__("warningType")):
            self.warningType = ParaDict.get("warningType")
        # parameter --alarm-server-addr
        if (ParaDict.__contains__("warningserverip")):
            self.warningserverip = ParaDict.get("warningserverip")
        # parameter --skip-hostname-set
        if (ParaDict.__contains__("skipHostnameSet")):
            self.skipHostnameSet = ParaDict.get("skipHostnameSet")
        # parameter --skip-cgroup-set
        if (ParaDict.__contains__("skip_cgroup_set")):
            self.skip_cgroup_set = ParaDict.get("skip_cgroup_set")
        # parameter --skip-os-set
        if (ParaDict.__contains__("skipOSSet")):
            self.skipOSSet = ParaDict.get("skipOSSet")
        # parameter --non-interactive
        if (ParaDict.__contains__("preMode")):
            self.preMode = ParaDict.get("preMode")
        # parameter --delete-root-trust
        if (ParaDict.__contains__("root_delete_flag")):
            self.root_delete_flag = ParaDict.get("root_delete_flag")
        # parameter --enable-perf-config
        if (ParaDict.__contains__("enable_perf_config")):
            self.enable_perf_config = True

    def check_no_root_paramter(self, para_dict):
        """
        function: Check no root user paramter
        input: NA
        output: NA
        """
        if not self.current_user_root:
            if (para_dict.__contains__("user") and (para_dict.get("user") != self.user)):
                GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50324"])
            if (para_dict.__contains__("group") and (para_dict.get("group") != self.group)):
                GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50324"])
            if not self.skipOSSet:
                GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50028"] % 'os')
            if not self.skip_cgroup_set:
                GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50028"] % 'cgroup')

    def checkUserParameter(self):
        """
        """
        if (self.user == ""):
            GaussLog.exitWithError(
                ErrorCode.GAUSS_500["GAUSS_50001"] % 'U' + ".")
        elif (":" in self.user):
            GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50004"] % 'U')

        # check if user exists
        cmd = "cat /etc/passwd|grep -v nologin|grep -v halt|" \
              "grep -v shutdown|awk -F: '{ print $1 }'|" \
              " grep '^%s$' 2>/dev/null" % self.user
        status = subprocess.getstatusoutput(cmd)[0]
        if status == 0:
            if pwd.getpwnam(self.user).pw_uid == 0:
                # user exists and uid is 0, exit.
                GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50302"])

        # check the local user and the localmode,
        # if user not exist exit with error
        if (self.localMode):
            try:
                DefaultValue.getUserId(self.user)
            except Exception as e:
                GaussLog.exitWithError(str(e))


    def checkUserAndGroup(self):
        """
        """
        if (self.localMode):
            usergroup = grp.getgrgid(pwd.getpwnam(self.user).pw_gid).gr_name
            if (self.group != usergroup):
                GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50305"]
                                       + "User:Group[%s:%s]"
                                       % (self.user, self.group))

    def check_local_node_info(self):
        """
        check local node info
        """
        hostName = NetUtil.GetHostIpOrName()
        g_nodeInfo = self.clusterInfo.getDbNodeByName(hostName)
        if (g_nodeInfo is None):
            GaussLog.exitWithError(ErrorCode.GAUSS_516["GAUSS_51620"] % "local" +
                                " It is not a host name %s." % hostName)

    def check_config_content(self, g_nodeInfo):
        UserUtil.check_path_owner(ClusterConfigFile.getOneClusterConfigItem("gaussdbAppPath", self.xmlFile))
        UserUtil.check_path_owner(ClusterConfigFile.getOneClusterConfigItem("gaussdbToolPath", self.xmlFile))
        UserUtil.check_path_owner(ClusterConfigFile.getOneClusterConfigItem("tmpMppdbPath", self.xmlFile))
        UserUtil.check_path_owner(ClusterConfigFile.getOneClusterConfigItem("gaussdbLogPath", self.xmlFile))
        UserUtil.check_path_owner(ClusterConfigFile.getOneClusterConfigItem("corePath", self.xmlFile))

        # check cm
        UserUtil.check_path_owner(g_nodeInfo.cmDataDir)
        for cmaInst in g_nodeInfo.cmagents:
            UserUtil.check_path_owner(cmaInst.datadir)
        for cmsInst in g_nodeInfo.cmservers:
            UserUtil.check_path_owner(cmsInst.datadir)

        # check dn
        for dnInst in g_nodeInfo.datanodes:
            UserUtil.check_path_owner(dnInst.datadir)
            if (len(dnInst.ssdDir) != 0):
                UserUtil.check_path_owner(dnInst.ssdDir)
        # check dn xlog
        for dnInst in g_nodeInfo.datanodes:
            if dnInst.xlogdir != '':
                UserUtil.check_path_owner(dnInst.xlogdir)


    def checkEnvValueParameter(self):
        """
        """
        for param in self.envParams:
            # check environmental variables vaild
            illegal = ["|", ";", "&", "$", ">", "<", "`", "\\", "!", "\n"]
            if any(ill_char in param for ill_char in illegal):
                GaussLog.exitWithError(
                    ErrorCode.GAUSS_500["GAUSS_50004"] % "-env-var" +
                    " There are illegal characters in the parameter.")

    def checkLogFile(self):
        """
        """
        if (self.logFile == ""):
            self.logFile = self.getPreOMLogPath(
                ClusterConstants.PREINSTALL_LOG_FILE, self.xmlFile)
        if (not os.path.isabs(self.logFile)):
            GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50213"]
                                   % self.logFile)
        UserUtil.check_path_owner(self.logFile)

    def checkWarningType(self):
        """
        """
        if self.warningType == "":
            raise Exception(ErrorCode.GAUSS_500["GAUSS_50001"] %'-alarm-type' + ".")
        if not self.warningType.isdigit():
            raise Exception(ErrorCode.GAUSS_500["GAUSS_50003"] %('-alarm-type', "a number"))
        self.warningType = int(self.warningType)
        if self.warningType not in g_warningType:
            raise Exception(ErrorCode.GAUSS_500["GAUSS_50004"] % "-alarm-type")

    def checkMpprcFile(self):
        """
        """
        if (self.mpprcFile == ""):
            return

        if (not os.path.isabs(self.mpprcFile)):
            GaussLog.exitWithError(ErrorCode.GAUSS_512["GAUSS_51206"]
                                   % self.mpprcFile)

        # check mpprc file path
        mpprcFilePath = os.path.normpath(self.mpprcFile)
        if os.path.islink(mpprcFilePath):
            GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_51251"] % mpprcFilePath)
        if (mpprcFilePath == "/home/%s" % self.user):
            GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50004"] % \
                                   '-sep-env-file' + " The file [%s] can not"
                                                     " be a reserved home "
                                                     "directory."
                                   % self.mpprcFile)
        if (os.path.isdir(self.mpprcFile)):
            GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50004"] % \
                                   '-sep-env-file' + " The file [%s] can not "
                                                     "be a directory."
                                   % self.mpprcFile)

        ProfileFile.checkMpprcFileChange(self.mpprcFile, "", self.mpprcFile)
        (checkstatus, checkoutput) = ProfileFile.check_env_file(self.mpprcFile)
        if (not checkstatus):
            if (self.mpprcFile != ""):
                envfile = self.mpprcFile + " and /etc/profile"
            else:
                envfile = "/etc/profile and ~/.bashrc"
            GaussLog.exitWithError(ErrorCode.GAUSS_518["GAUSS_51808"] % \
                                   checkoutput + "Please check %s." % envfile)
    
    def delete_host_ip(self):
        """
        function: delete host_ip env
        input: NA
        output: NA
        """
        if self.current_user_root:
            cmd = "sed -i '/^export[ ]*HOST_IP=/d' /etc/profile"
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50205"]
                                    % ClusterConstants.ETC_PROFILE + "The cmd is %s" % cmd)

        if "HOST_IP" in os.environ.keys():
            os.environ.pop("HOST_IP")

    def checkParameter(self):
        """
        function: Check parameter from command line
        input: NA
        output: NA
        """
        if self.current_user_root:
            # check -G parameter
            ClusterUser.checkGroupParameter(self.user, self.group)

        # remove HOST_IP info with /etc/profile and environ
        self.delete_host_ip()
        # check config file
        ClusterConfigFile.checkConfigFile(self.xmlFile)
        # check user info
        self.checkUserParameter()
        # check user group match
        self.checkUserAndGroup()
        # init cluster info
        self.initClusterInfo()
        # check local node info
        self.check_local_node_info()
        # check env-val
        self.checkEnvValueParameter()
        # check alarm type
        self.checkWarningType()
        # check mpprc file
        self.checkMpprcFile()
        # check log file
        self.checkLogFile()

    # set LD_LIBRARY_PATH add local lib
    def setLibPath(self):
        package_path = os.path.dirname(os.path.realpath(__file__))
        ld_path = package_path + "/gspylib/clib"
        rerun = True

        if 'LD_LIBRARY_PATH' not in os.environ:
            os.environ['LD_LIBRARY_PATH'] = ld_path
        elif not os.environ.get('LD_LIBRARY_PATH').startswith(ld_path):
            os.environ['LD_LIBRARY_PATH'] = \
                ld_path + ":" + os.environ['LD_LIBRARY_PATH']
        else:
            rerun = False

        if rerun:
            try:
                os.execve(os.path.realpath(__file__), sys.argv, os.environ)
            except Exception as e:
                GaussLog.exitWithError(str(e))

    # decompress version.cfg from Server, and read it
    def readVersioncfg(self):
        def _parse_version_cfg(_cfg):
            _cmd = f'cat {_cfg}'
            _status, _output = subprocess.getstatusoutput(_cmd)
            if _status != 0:
                GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50217"] %
                                        "version.cfg" + "The cmd is %s. " % _cmd +
                                        "The output is %s." % _output)
            _lines = _output.splitlines()
            _res = {
                'pkg_prefix': _lines[0].replace("OM", "Server"),
                'pgversion': _lines[1],
                'commit': _lines[2],
                'mode': 'release' if len(_lines) < 4 else _lines[3]
            }
            return _res

        root = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')
        cfg = os.path.join(root, 'version.cfg')

        # read version.cfg of om package
        self.om_version_cfg = _parse_version_cfg(cfg)

        # upack and read version.cfg of openGauss-server package
        # the existing om version.cfg will be overwritten
        cmd = 'cd {} && tar -xpf {}*.tar.bz2 ./version.cfg'.format(root, self.om_version_cfg['pkg_prefix'])
        status, output = subprocess.getstatusoutput(cmd)
        if status != 0:
            cmd = 'cd {} && tar -xpf `ls openGauss-Server*.tar.bz2 | tail -1` ./version.cfg'.format(root)
            status, output = subprocess.getstatusoutput(cmd)
        if status != 0:
            GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50217"] % "version.cfg" +
                                   "The cmd is %s. " % cmd +
                                   "The output is %s." % output)
        self.og_version_cfg = _parse_version_cfg(cfg)

    def unpackSomeToolsNeeded(self):
        pkg_prefix = self.om_version_cfg['pkg_prefix']

        # target tools
        bin_files = ['./bin/encrypt']
        ogai_files = ['./lib/libonnxruntime.so.1', './lib/libtokenizers.so']
        dss_files = []
        memcheck_files = []
        if self.clusterInfo.enable_dss == 'on':
            dss_files = ['./bin/perctrl', './bin/dsscmd', './lib/libdssapi.so', './bin/dss_clear.sh']
        if self.og_version_cfg['mode'] == 'memcheck':
            memcheck_files = ['./lib/libasan.so', './lib/libasan.so.6', './lib/libasan.so.6.0.0']

        # target path
        root = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')
        clib = os.path.join(root, "script/gspylib/clib")
        clib_dss = os.path.realpath(os.path.join(f'{clib}', f"dss_app_{self.og_version_cfg['commit']}"))
        memcheck_root_lib = ''
        if self.clusterInfo.enable_dss == 'on' and \
            self.og_version_cfg['mode'] == 'memcheck' and \
            not os.access('/usr/lib64/libasan.so.6', os.F_OK):
            memcheck_root_lib = '/usr/lib64'

        # unpack and move tools into target path
        cmd = 'cd {} && '.format(root)
        cmd += 'tar -xpf {}*.tar.bz2  {} && '.format(pkg_prefix, ' '.join(bin_files + dss_files + memcheck_files + ogai_files))
        if dss_files:
            cmd += 'mkdir -p {0} -m u=rwx && '.format(clib_dss)
            cmd += 'mv {} {} && '.format(' '.join(dss_files), clib_dss)
        if memcheck_root_lib != '':
            cmd += 'cp ./lib/libasan.so.6 {} &&'.format(memcheck_root_lib)
        cmd += '\mv {} {} && '.format(' '.join(bin_files + memcheck_files + ogai_files), clib)
        cmd += 'cd {} && rm -rf bin'.format(root)
        status, output = subprocess.getstatusoutput(cmd)
        if status != 0:
            GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50217"] %
                                   "version.cfg" + "The cmd is %s. " % cmd +
                                   "The output is %s." % output)
            
    def check_hugebin_support(self):
        """
        Check if hugebin can be used
        """
        if os.getuid() != 0:
            self.logger.warn("EnableHugeBin is on, but current user is not root, "
                            "so install without hugebin support.", "warning")
            return False
        
        pagesize = os.sysconf(os.sysconf_names.get('SC_PAGESIZE', os.sysconf_names.get('SC_PAGE_SIZE')))
        if pagesize != 4096:
            self.logger.warn("EnableHugeBin is on, but system page size is %d bytes, "
                            "not 4KB, so install without hugebin support." % pagesize, "warning")
            return False

        sysversion = subprocess.getoutput("uname -r")
        major_version = int(sysversion.split('.')[0])
        minor_version = int(sysversion.split('.')[1])
        if major_version < 5 or (major_version == 5 and minor_version < 10):
            self.logger.warn("EnableHugeBin is on, but current kernel version is %s, "
                            "lower than 5.10, so install without hugebin support." % sysversion, "warning")
            return False
        
        return True
        
    # init global variables
    def initGlobals(self):
        """
        function: init global parameters
        input: NA
        output: NA
        """
        # init the log file
        self.initLogger("gs_preinstall")

        
        current_path = os.path.dirname(os.path.realpath(__file__))
        package_dir = os.path.normpath(os.path.join(current_path, "../"))
        self.write_host_file(package_dir)

        # get the clusterToolPath
        self.clusterToolPath = ClusterDir.getPreClusterToolPath(self.xmlFile)
        temp_nodes = ClusterConfigFile.getOneClusterConfigItem("nodeNames", self.xmlFile)
        if len(temp_nodes.split(',')) < 2:
            self.isSingle = True
        os.environ[ClusterConstants.TOOL_PATH_ENV] = self.clusterToolPath
        self.cluster_core_path = self.clusterInfo.readClustercorePath(self.xmlFile)

        self.logger.log("Parsing the configuration file.", "addStep")
        
        try:
            # parse the configuration file
            self.sshTool = SshTool(self.clusterInfo.getClusterNodeNames(),
                                   self.logFile,
                                   DefaultValue.TIMEOUT_PSSH_PREINSTALL)

        except Exception as e:
            self.logger.logExit(str(e))

        # check the local hostname
        if NetUtil.GetHostIpOrName() not in \
                self.clusterInfo.getClusterNodeNames():
            self.logger.logExit(ErrorCode.GAUSS_516["GAUSS_51619"]
                                % NetUtil.GetHostIpOrName())
        
        enablehugebin = ClusterConfigFile.getOneClusterConfigItem("enableHugeBin", self.xmlFile)
        if enablehugebin == "on" and self.check_hugebin_support():
            self.enable_hugebin = True
            self.logger.log("Preinstall database with enableHugeBin is on", "constant")
            

        self.logger.log("Successfully parsed the configuration file.",
                        "constant")

    def write_host_file(self, package_dir):
        hosts_file = os.path.normpath(os.path.join(package_dir, "hosts"))
        if os.path.exists(hosts_file):
            FileUtil.removeFile(hosts_file)
        FileUtil.createFile(hosts_file)
        
        # hosts contents
        contents = {}
        hostname_list = self.clusterInfo.getClusterNodeNames()
        for hostname in hostname_list:
            node = self.clusterInfo.getDbNodeByName(hostname)
            ip = node.sshIps[0]
            contents[ip] = hostname

        HostsUtil.write_hosts_file(hosts_file, contents)
        FileUtil.changeMode(DefaultValue.FILE_MODE, hosts_file)

    # check expect for cm/create trust
    def check_expect(self):
        """
        function: check expect
        input: NA
        output: NA
        """
        temp_nodes = ClusterConfigFile.getOneClusterConfigItem("nodeNames", self.xmlFile)
        if len(temp_nodes.split(',')) > 1:
            cmd = "echo exit|expect"
            (status, _) = subprocess.getstatusoutput(cmd)
            if status != 0:
                GaussLog.exitWithError(ErrorCode.GAUSS_514["GAUSS_51405"] % "expect")

    def getPreOMLogPath(self, logName, xml):
        """
        function: get the OM log path
        input: logName, xml
        output: fullLogPath
        """
        try:
            fullLogPath = ""
            # get the log path
            configedLogPath = ClusterConfigFile.getOneClusterConfigItem("gaussdbLogPath",
                                                           xml)
            DefaultValue.checkPathVaild(configedLogPath)
            # check gaussdbLogPath is not null
            if configedLogPath == "":
                fullLogPath = "%s/%s/om/%s" % (
                ClusterConstants.GAUSSDB_DIR, self.user, logName)
            else:
                fullLogPath = "%s/%s/om/%s" % (
                os.path.normpath(configedLogPath), self.user, logName)
            UserUtil.check_path_owner(fullLogPath)
            return fullLogPath
        except Exception as e:
            GaussLog.exitWithError(str(e))



    def change_lib_path(self):
        """
        if gs_preinstall current path is /root/gauss_om/username,
         so change its lib path
        :return:
        """
        gsom_path = os.path.realpath(
                    os.path.join(os.path.realpath(__file__), "../../../"))
        package_path = os.path.dirname(os.path.realpath(__file__))
        lib_path = os.path.join(package_path, "..", "lib")
        sys.path.insert(0, lib_path)
        if gsom_path == DefaultValue.ROOT_SCRIPTS_PATH:
            self.is_new_root_path = True
            
    def checkIfNest(self):
        """
        function: check syncNode
        """
        clusterInfo = dbClusterInfo()
        clusterInfo.initFromXml(self.xmlFile)
        dbNodes = clusterInfo.dbNodes
        # get the dss_home path
        dss_home_path = clusterInfo.dss_home
     
        for dbinfo in dbNodes:
            if dbinfo is None:
                break
            datanodes = dbinfo.datanodes
            
            # check if subdir_path starts with parentdir_path
            for datainfo in datanodes:
                parentdir_path = os.path.normpath(datainfo.datadir)
                subdir_path = os.path.normpath(dss_home_path)
                if not parentdir_path.endswith(os.path.sep):
                    parentdir_path += os.path.sep
                if not subdir_path.endswith(os.path.sep):
                    subdir_path += os.path.sep
                    
                if subdir_path.startswith(parentdir_path):
                    GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50240"] % dss_home_path)

    def clear_hist_time_format(self):
        if self.current_user_root:
            cmd = "sed -i '/HISTTIMEFORMAT=/d' /etc/profile"
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                GaussLog.exitWithError("Clear HISTTIMEFORMAT from /etc/profile "
                                    "failed.\nError: %s\nThe cmd is: %s\n" %
                                    (output,cmd))
    def check_current_user(self):
        user_info = UserUtil.getUserInfo()
        if user_info.get("uid") == 0:
            self.current_user_root = True
        else:
            self.current_user_root = False
            self.user = user_info.get("name")
            self.group = user_info.get("g_name")
            self.skipOSSet = True
            self.skip_cgroup_set = True

    def check_upgrade_finish(self):
        """
        function: check upgrade finish
        input: NA
        output: NA
        """
        gp_host = ClusterConfigFile.getOneClusterConfigItem("GPHOST", self.xmlFile)
        if not os.path.exists(gp_host):
            return
        upgrade_dir = os.path.dirname(os.path.join(gp_host, "binary_upgrade"))
        if os.path.exists(gp_host):
            GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50240"] % gp_host)


def remove_mpp_env():
    mpprc = os.environ.get('MPPDB_ENV_SEPARATE_PATH')
    if mpprc:
        os.environ.pop('MPPDB_ENV_SEPARATE_PATH')

if __name__ == '__main__':
    """
    main function
    """
    try:
        # if not remove mpprc env, it will affect preinstall init
        remove_mpp_env()
        # Objectize class
        preinstall = Preinstall()
         # check if user is root
        preinstall.check_current_user()
        # remove HISTTIMEFORMAT with /etc/profile for root
        preinstall.clear_hist_time_format()
        # set LD_LIBRARY_PATH
        preinstall.setLibPath()
        # parse cmd lines
        preinstall.parseCommandLine()
        # check parameters
        preinstall.checkParameter()
        # check if the path between dss and datanode is nested
        preinstall.checkIfNest()
        # check if the upgrade is complete
        preinstall.check_upgrade_finish()
        # check expect
        preinstall.check_expect()
        # init global variables
        preinstall.initGlobals()
        # read version.cfg
        preinstall.readVersioncfg()
        # unpack some tools needed
        preinstall.unpackSomeToolsNeeded()

        preinstall.change_lib_path()
        impl = PreinstallImplOLAP(preinstall)
        # Perform the whole extand process
        impl.run()
    except Exception as e:
        GaussLog.exitWithError(str(e))