#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c): 2012-2017, Huawei Tech. Co., Ltd.
# Description  : DbClusterInfo.py is a utility to get cluster information
#############################################################################
try:
    import binascii
    import os
    import subprocess
    import struct
    import time
    import sys
    import pwd
    import socket
    import copy
    import stat
    from collections import Counter

    sys.path.append(os.path.split(os.path.realpath(__file__))[0] + "/../../../")
    from gspylib.common.ErrorCode import ErrorCode
    from gspylib.common.VersionInfo import VersionInfo
    from gspylib.os.gsnetwork import g_network
    from gspylib.common.cluster_topology.node_info import dbNodeInfo
    from gspylib.common.cluster_topology.instance_info import instanceInfo
    from gspylib.common.cluster_topology.parse_xml import ParseXml
    from gspylib.common.cluster_topology import const
except ImportError as ie:
    sys.exit("[GAUSS-52200] : Unable to import module: %s." % str(ie))
g_networkType = 0
xmlRootNode = None
parse_xml = ParseXml()


class BaseDbClusterInfo():
    """
    Cluster info
    """

    def __init__(self, checkSctpPort=False):
        """
        Constructor
        """
        self.name = ""
        self.appPath = ""
        self.logPath = ""
        self.xmlFile = ""
        self.dbNodes = []
        self.newNodes = []
        self.vwNodes = []
        self.cmsFloatIp = ""
        self.__newInstanceId = [const.BASE_ID_CMSERVER,
                                const.BASE_ID_GTM,
                                const.BASE_ID_ETCD,
                                const.BASE_ID_COORDINATOR,
                                const.BASE_ID_DATANODE,
                                const.BASE_ID_CMAGENT,
                                const.BASE_ID_VW_DN]
        self.__newNodeId = [const.BASE_ID_NODE,
                            const.BASE_ID_VW_NODE]
        self.__newDummyStandbyId = const.BASE_ID_DUMMYDATANODE
        self.__newMirrorId = 0
        self.clusterRings = []
        self.clusterType = const.CLUSTER_TYPE_MASTER_STANDBY
        self.is_htap_cluster = False
        self.checkSctpPort = checkSctpPort
        self.clusterName = ""
        self.toolPath = ""
        self.tmpPath = ""

        # add azName
        self.azName = ""
        # cluster properties
        self.replicationCount = 0
        self.quorumMode = ""
        self.gtmcount = 0
        self.etcdcount = 0
        self.cmscount = 0
        self.__newGroupId = 0
        self.config_version = 0

    def __str__(self):
        """
        function : Construct a printable string representation of a dbClusterInfo
        input : NA
        output : String
        """
        retStr = "ClusterName=%s,AppPath=%s,LogPath=%s,ClusterType=%s" % \
                 (self.name, self.appPath, self.logPath, self.clusterType)

        for dbNode in self.dbNodes:
            retStr = "%s\n%s" % (retStr, str(dbNode))

        return retStr

    @staticmethod
    def readClusterTmpMppdbPath(user, xmlFile):
        """
        function : Read temporary mppdb path from xml file
        input : String,String
        output : String
        """
        parse_xml.setDefaultXmlFile(xmlFile)
        # read temporary mppdb path from xml file
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(parse_xml.initParserXMLFile(xmlFile),
                                                                   "tmpMppdbPath", "cluster")
        if retStatus != 0:
            return "/tmp/%s_mppdb" % user

        tmppath = os.path.normpath(retValue)
        parse_xml.checkPathVaild(tmppath)
        return tmppath

    @staticmethod
    def readClusterLogPath(xmlFile):
        """
        function : Read log path from xml file
        input : String
        output : NA
        """
        parse_xml.setDefaultXmlFile(xmlFile)
        # read log path from xml file
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(parse_xml.initParserXMLFile(xmlFile),
                                                                   "gaussdbLogPath", "cluster")
        if retStatus == 0:
            return os.path.normpath(retValue)
        elif retStatus == 2:
            return "/var/log/gaussdb"
        else:
            raise Exception(ErrorCode.GAUSS_500["GAUSS_51200"] % "gaussdbLogPath" + " Error: \n%s" % retValue)

    def initFromStaticConfig(self, user, static_config_file="", isLCCluster=False):
        """
        function : Init cluster from static configuration file
        input : String,String
        output : NA
        """
        # check Os user
        self.__checkOsUser(user)
        # get static_config_file
        if static_config_file == "":
            staticConfigFile = self.__getStaticConfigFilePath(user)
        else:
            staticConfigFile = static_config_file
        # read static_config_file
        self.__readStaticConfigFile(staticConfigFile, user, isLCCluster)
        # check static_config_file
        if not isLCCluster:
            self.__checkClusterConfig()

    def getClusterVersion(self, staticConfigFile):
        """
        function : get cluster version information from static configuration file
        input : String
        output : version
        """
        fp = None
        try:
            fp = open(staticConfigFile, "rb")
            info = fp.read(28)
            cluster_info = struct.unpack("=IIIqiI", info)
        except Exception as e:
            if fp:
                fp.close()
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] + " Error: \n%s." % str(e))

        return cluster_info[2]

    def isMiniaturizedDeployment(self, cluster_version):
        """
        function: judge whether is the miniaturized deployment
        input : Int
        output : bool value
        """
        if 101 <= cluster_version <= 200:
            return True
        return False

    def isSinglePrimaryMultiStandbyDeployment(self, cluster_version):
        """
        judge whether is the single primary multi standby deployment
        """
        if 201 <= cluster_version <= 300:
            return True
        return False

    def __checkOsUser(self, user):
        """
        function : Check os user
        input : String
        output : NA
        """
        try:
            user = pwd.getpwnam(user).pw_gid
        except Exception as e:
            raise Exception(ErrorCode.GAUSS_503["GAUSS_50300"] % user + " Error: \n%s." % str(e))

    def __getStaticConfigFilePath(self, user):
        """
        function : get the path of static configuration file.
        input : String
        output : String
        """
        gaussHome = self.__getEnvironmentParameterValue("GAUSSHOME", user)
        if gaussHome == "":
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] %
                            ("installation path of designated user [%s]" % user))

        staticConfigFile = "%s/bin/cluster_static_config" % gaussHome
        staticConfigBak = "%s/bin/cluster_static_config_bak" % gaussHome
        if os.path.exists(staticConfigFile):
            return staticConfigFile
        elif os.path.exists(staticConfigBak):
            return staticConfigBak
        else:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] %
                            ("static configuration file [%s] of designated user [%s]" % (staticConfigFile, user)))

    def __getEnvironmentParameterValue(self, environmentParameterName, user):
        """
        function :Get the environment parameter.
        !!!!Do not call this function in preinstall.py script.
        because we determine if we are using env separate version by the value of MPPDB_ENV_SEPARATE_PATH
        input : String,String
        output : String
        """
        # get mpprc file
        mpprcFile = os.getenv('MPPDB_ENV_SEPARATE_PATH')
        if mpprcFile is not None and mpprcFile:
            mpprcFile = mpprcFile.replace("\\", "\\\\").replace('"', '\\"\\"')
            parse_xml.checkPathVaild(mpprcFile)
            userProfile = mpprcFile
        else:
            userProfile = "~/.bashrc"
        # build shell command
        if os.getuid() == 0:
            cmd = "su - %s -c 'source %s;echo $%s' 2>/dev/null" % (user, userProfile, environmentParameterName)
        else:
            cmd = "source %s;echo $%s 2>/dev/null" % (userProfile, environmentParameterName)
        (status, output) = subprocess.getstatusoutput(cmd)
        if status != 0:
            raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s" % output)
        return output.split("\n")[0]

    def __checkClusterVersion(self, version):
        """
        function : check the cluster static config file
        input : String
        output : NA
        """
        # primary, standy, dummystandby
        if version <= 100:
            self.clusterType = const.CLUSTER_TYPE_MASTER_STANDBY
            if version not in [const.BIN_CONFIG_VERSION,
                               const.BIN_CONFIG_VERSION_IPV6]:
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                ("cluster static config version[%s]" % version,
                                 "the new version[%s]" % const.BIN_CONFIG_VERSION))
        # single cluster
        elif 101 <= version <= 200:
            self.clusterType = const.CLUSTER_TYPE_SINGLE
            if version not in [const.BIN_CONFIG_VERSION_SINGLE,
                               const.BIN_CONFIG_VERSION_SINGLE_IPV6]:
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                ("cluster static config version[%s]" % version,
                                 "the new version[%s]" % const.BIN_CONFIG_VERSION_SINGLE))
        # single primary multi standby
        elif 201 <= version <= 300:
            self.clusterType = const.CLUSTER_TYPE_SINGLE_PRIMARY_MULTI_STANDBY
            if version not in [const.BIN_CONFIG_VERSION_SINGLE_PRIMARY_MULTI_STANDBY,
                               const.BIN_CONFIG_VERSION_SINGLE_PRIMARY_MULTI_STANDBY_IPV6]:
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                ("cluster static config version[%s]" % version,
                                 "the new version[%s]" % const.BIN_CONFIG_VERSION_SINGLE_PRIMARY_MULTI_STANDBY))
        elif 301 <= version <= 400:
            # single inst
            self.clusterType = const.CLUSTER_TYPE_SINGLE_INST
            if version in [const.BIN_CONFIG_VERSION_HTAP_INST,
                           const.BIN_CONFIG_VERSION_HTAP_INST_IPV6]:
                # htap cluster
                self.is_htap_cluster = True
        elif 501 <= version <= 600:
            # master standby multi az
            self.clusterType = const.CLUSTER_TYPE_MASTER_STANDBY_MULTI_AZ
            if version not in [const.BIN_CONFIG_VERSION_MASTER_STANDBY_MULTI_AZ,
                               const.BIN_CONFIG_VERSION_MASTER_STANDBY_MULTI_AZ_IPV6]:
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                ("cluster static config version[%s]" % version,
                                 "the new version[%s]" % const.BIN_CONFIG_VERSION_MASTER_STANDBY_MULTI_AZ))
        self.config_version = version

    def __readStaticConfigFile(self, staticConfigFile, user, isLCCluster=False):
        """
        function : read cluster information from static configuration file
        input : String,String
        output : NA
        """
        fp = None
        try:
            # get env parameter
            self.name = self.__getEnvironmentParameterValue("GS_CLUSTER_NAME", user)
            self.appPath = self.__getEnvironmentParameterValue("GAUSSHOME", user)
            logPathWithUser = self.__getEnvironmentParameterValue("GAUSSLOG", user)

            if self.name == "":
                raise Exception(ErrorCode.GAUSS_503["GAUSS_50300"] %
                                ("cluster name of designated user [%s]" % user))
            if self.appPath == "":
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] %
                                ("installation path of designated user [%s]" % user))
            if logPathWithUser == "":
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] %
                                ("log path of designated user [%s]" % user))

            splitMark = "/%s" % user
            # set log path without user
            # find the path from right to left
            self.logPath = logPathWithUser[0:(logPathWithUser.rfind(splitMark))]
            try:
                # read static_config_file
                fp = open(staticConfigFile, "rb")
                info = fp.read(28)
                (_, _, version, _, nodeNum, _) = struct.unpack("=IIIqiI", info)
            except Exception as e:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] % staticConfigFile + " Error:\n" + str(e))

            self.__checkClusterVersion(version)

            self.dbNodes = []
            try:
                for _ in range(nodeNum):
                    offset = (fp.tell() // const.PAGE_SIZE + 1) * const.PAGE_SIZE
                    fp.seek(offset)
                    dbNode = self.__unPackNodeInfo(fp, isLCCluster)
                    self.dbNodes.append(dbNode)
                fp.close()

                # set sctpPort/controlPort/servicePort
                self.__setNewPortForDnInst()
                self.__setNewPortForCnInst()
            except Exception as e:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                staticConfigFile + " Error:\nThe content is not correct." + str(e))
        except Exception as e:
            if fp:
                fp.close()
            raise Exception(str(e))

    def __unPackNodeInfo(self, fp, isLCCluster=False):
        """
        function : unpack a node config info
        input : file
        output : Object
        """
        info = fp.read(72)
        (_, nodeId, nodeName) = struct.unpack("=II64s", info)
        nodeName = nodeName.decode().strip('\x00')
        dbNode = dbNodeInfo(nodeId, nodeName)
        if not isLCCluster and \
                (self.isSinglePrimaryMultiStandbyCluster() or
                 self.isMasterStandbyMultiAZCluster() or
                 (self.isSingleInstCluster() and not self.isHtapCluster())):
            info = fp.read(68)
            (azName, azPriority) = struct.unpack("=64sI", info)
            dbNode.azName = azName.decode().strip('\x00')
            dbNode.azPriority = azPriority
        # get backIps
        self.__unPackIps(fp, dbNode.backIps)
        # get sshIps
        self.__unPackIps(fp, dbNode.sshIps)
        if not isLCCluster:
            # get cm_server information
            self.__unPackCmsInfo(fp, dbNode)
            # get cm_agent information
            self.__unpackAgentInfo(fp, dbNode)
            # get gtm information
            self.__unpackGtmInfo(fp, dbNode)
            info = fp.read(404)
            # get cn information
            self.__unpackCooInfo(fp, dbNode)
        # get dn information
        self.__unpackDataNode(fp, dbNode)
        if not isLCCluster:
            # get etcd information
            self.__unpackEtcdInfo(fp, dbNode)
            info = fp.read(8)
        # set dn azName for OLAP
        if self.isSinglePrimaryMultiStandbyCluster() or \
                self.isMasterStandbyMultiAZCluster() or \
                (self.isSingleInstCluster() and not self.isHtapCluster()):
            for inst in dbNode.datanodes:
                inst.azName = dbNode.azName
        return dbNode

    def __unpackEtcdInfo(self, fp, dbNode):
        """
        function : unpack the info of etcd
        input : file,Object
        output : NA
        """
        etcdInst = instanceInfo()
        etcdInst.instanceRole = const.INSTANCE_ROLE_ETCD
        etcdInst.hostname = dbNode.name
        etcdInst.instanceType = const.INSTANCE_TYPE_UNDEFINED
        info = fp.read(1100)
        (etcdNum, etcdInst.instanceId, etcdInst.mirrorId, _, etcdInst.datadir) = struct.unpack("=IIi64s1024s", info)
        etcdInst.datadir = etcdInst.datadir.decode().strip('\x00')
        self.__unPackIps(fp, etcdInst.listenIps)
        info = fp.read(4)
        (etcdInst.port,) = struct.unpack("=I", info)
        self.__unPackIps(fp, etcdInst.haIps)
        info = fp.read(4)
        (etcdInst.haPort,) = struct.unpack("=I", info)
        if etcdNum == 1:
            dbNode.etcdNum = 1
            dbNode.etcds.append(etcdInst)
        else:
            dbNode.etcdNum = 0
            dbNode.etcds = []

    def __unPackIps(self, fp, ips, support_multi_listen_ip=False):
        """
        function : Unpack the info of ips
        input : file,[]
        output : NA
        """
        info = fp.read(4)
        (n,) = struct.unpack("=i", info)
        for _ in range(int(n)):
            info = fp.read(128)
            (currentIp,) = struct.unpack("=128s", info)
            currentIp = currentIp.decode().strip('\x00')
            ips.append(str(currentIp.strip()))
        if support_multi_listen_ip and self.config_version in const.IPV6_VERSION_LIST:
            max_ip_num = const.NEW_MAX_IP_NUM
        else:
            max_ip_num = const.MAX_IP_NUM
        info = fp.read(128 * (max_ip_num - n))

    def __unPackCmsInfo(self, fp, dbNode):
        """
        function : Unpack the info of CMserver
        input : file Object
        output : NA
        """
        cmsInst = instanceInfo()
        cmsInst.instanceRole = const.INSTANCE_ROLE_CMSERVER
        cmsInst.hostname = dbNode.name
        info = fp.read(1164)
        (cmsInst.instanceId, cmsInst.mirrorId, dbNode.cmDataDir, cmsInst.level, self.cmsFloatIp) = \
            struct.unpack("=II1024sI128s", info)
        dbNode.cmDataDir = dbNode.cmDataDir.decode().strip('\x00')
        self.cmsFloatIp = self.cmsFloatIp.decode().strip('\x00')
        cmsInst.datadir = "%s/cm_server" % dbNode.cmDataDir
        self.__unPackIps(fp, cmsInst.listenIps)
        info = fp.read(4)
        (cmsInst.port,) = struct.unpack("=I", info)
        self.__unPackIps(fp, cmsInst.haIps)
        info = fp.read(8)
        (cmsInst.haPort, cmsInst.instanceType) = struct.unpack("=II", info)
        if cmsInst.instanceType == const.MASTER_INSTANCE:
            dbNode.cmsNum = 1
        elif cmsInst.instanceType == const.STANDBY_INSTANCE:
            dbNode.cmsNum = 0
        else:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51204"] % ("CMServer", cmsInst.instanceType))
        info = fp.read(4 + 128 * const.MAX_IP_NUM + 4)

        if cmsInst.instanceId:
            dbNode.cmservers.append(cmsInst)
            self.cmscount += 1
        else:
            dbNode.cmservers = []

    def __unpackAgentInfo(self, fp, dbNode):
        """
        function : Unpack the info of agent. It should be called after __unPackCmsInfo, because dbNode.cmDataDir
                   get value in __unPackCmsInfo
        input : file Object
        output : NA
        """
        cmaInst = instanceInfo()
        cmaInst.instanceRole = const.INSTANCE_ROLE_CMAGENT
        cmaInst.hostname = dbNode.name
        cmaInst.instanceType = const.INSTANCE_TYPE_UNDEFINED
        info = fp.read(8)
        (cmaInst.instanceId, cmaInst.mirrorId) = struct.unpack("=Ii", info)
        self.__unPackIps(fp, cmaInst.listenIps)
        cmaInst.datadir = "%s/cm_agent" % dbNode.cmDataDir
        dbNode.cmagents.append(cmaInst)

    def __unpackGtmInfo(self, fp, dbNode):
        """
        function : Unpack the info of gtm
        input : file Object
        output : NA
        """
        gtmInst = instanceInfo()
        gtmInst.instanceRole = const.INSTANCE_ROLE_GTM
        gtmInst.hostname = dbNode.name
        info = fp.read(1036)
        (gtmInst.instanceId, gtmInst.mirrorId, gtmNum, gtmInst.datadir) = struct.unpack("=III1024s", info)
        gtmInst.datadir = gtmInst.datadir.decode().strip('\x00')
        self.__unPackIps(fp, gtmInst.listenIps)
        info = fp.read(8)
        (gtmInst.port, gtmInst.instanceType) = struct.unpack("=II", info)
        if gtmInst.instanceType == const.MASTER_INSTANCE:
            dbNode.gtmNum = 1
        elif gtmInst.instanceType == const.STANDBY_INSTANCE:
            dbNode.gtmNum = 0
        else:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51204"] % ("GTM", gtmInst.instanceType))
        self.__unPackIps(fp, gtmInst.haIps)
        info = fp.read(4)
        (gtmInst.haPort,) = struct.unpack("=I", info)
        info = fp.read(1024 + 4 + 128 * const.MAX_IP_NUM + 4)

        if gtmNum == 1:
            dbNode.gtms.append(gtmInst)
            self.gtmcount += 1
        else:
            dbNode.gtms = []

    def __unpackCooInfo(self, fp, dbNode):
        """
        function : Unpack the info of coordinator
        input : file Object
        output : NA
        """
        cooInst = instanceInfo()
        cooInst.instanceRole = const.INSTANCE_ROLE_COODINATOR
        cooInst.hostname = dbNode.name
        cooInst.instanceType = const.INSTANCE_TYPE_UNDEFINED
        info = fp.read(2060)
        (cooInst.instanceId, cooInst.mirrorId, cooNum, cooInst.datadir, cooInst.ssdDir) = \
            struct.unpack("=IiI1024s1024s", info)
        cooInst.datadir = cooInst.datadir.decode().strip('\x00')
        cooInst.ssdDir = cooInst.ssdDir.decode().strip('\x00')
        self.__unPackIps(fp, cooInst.listenIps, support_multi_listen_ip=True)
        info = fp.read(8)
        (cooInst.port, cooInst.haPort) = struct.unpack("=II", info)
        if cooNum == 1:
            dbNode.cooNum = 1
            dbNode.coordinators.append(cooInst)
        else:
            dbNode.cooNum = 0
            dbNode.coordinators = []

    def __unpackDataNode(self, fp, dbNode):
        """
        function : Unpack the info of datanode
        input : file Object
        output : NA
        """
        info = fp.read(4)
        (dataNodeNums,) = struct.unpack("=I", info)
        dbNode.dataNum = 0
        dbNode.nodeType = const.NODE_TYPE_REPL

        dbNode.datanodes = []
        for _ in range(dataNodeNums):
            dnInst = instanceInfo()
            dnInst.instanceRole = const.INSTANCE_ROLE_DATANODE
            dnInst.hostname = dbNode.name
            info = fp.read(2056)
            (dnInst.instanceId, dnInst.mirrorId, dnInst.datadir, dnInst.ssdDir) = struct.unpack("=II1024s1024s", info)
            if dnInst.mirrorId == const.MIRROR_ID_VW_DN:
                dbNode.nodeType = const.NODE_TYPE_VW
                if dbNode.name not in self.vwNodes:
                    self.vwNodes.append(dbNode.name)

            dnInst.datadir = dnInst.datadir.decode().strip('\x00')
            dnInst.ssdDir = dnInst.ssdDir.decode().strip('\x00')
            self.__unPackIps(fp, dnInst.listenIps, support_multi_listen_ip=True)
            info = fp.read(8)
            (dnInst.port, dnInst.instanceType) = struct.unpack("=II", info)
            if dnInst.instanceType == const.MASTER_INSTANCE:
                dbNode.dataNum += 1
            elif dnInst.instanceType == const.STANDBY_INSTANCE or dnInst.instanceType == const.DUMMY_STANDBY_INSTANCE:
                pass
            else:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51204"] % ("DN", dnInst.instanceType))
            self.__unPackIps(fp, dnInst.haIps)
            info = fp.read(4)
            (dnInst.haPort,) = struct.unpack("=I", info)
            if (self.clusterType == const.CLUSTER_TYPE_SINGLE_PRIMARY_MULTI_STANDBY or
                    self.clusterType == const.CLUSTER_TYPE_SINGLE_INST):
                maxStandbyCount = const.MIRROR_COUNT_REPLICATION_MAX - 1
            else:
                maxStandbyCount = const.MIRROR_COUNT_DATA - 1
            info = fp.read((1024 + 4 + 128 * const.MAX_IP_NUM + 4 + 4) * maxStandbyCount)
            dbNode.datanodes.append(dnInst)

    def initFromStaticConfigWithoutUser(self, staticConfigFile):
        """
        function : Init cluster from static config with out user
        input : file Object
        output : NA
        """
        fp = None
        try:
            # read cluster info from static config file
            fp = open(staticConfigFile, "rb")
            info = fp.read(28)
            (_, _, version, _, nodeNum, _) = struct.unpack("=IIIqiI", info)

            self.__checkClusterVersion(version)

            self.dbNodes = []
            for _ in range(nodeNum):
                offset = (fp.tell() // const.PAGE_SIZE + 1) * const.PAGE_SIZE
                fp.seek(offset)
                dbNode = self.__unPackNodeInfo(fp)
                self.dbNodes.append(dbNode)

            # set sctpPort/controlPort/servicePort
            self.__setNewPortForDnInst()
            self.__setNewPortForCnInst()
        except Exception as e:
            if fp:
                fp.close()
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51203"] % "cluster" + " Error: \n%s" % str(e))
        self.__checkClusterConfig()

    def __appendInstanceId(self, static_config_file):
        """
        function : instance id append to the old cluster.
        input : file Object
        output : NA
        """
        try:
            # init oldClusterInfo
            oldClusterInfo = BaseDbClusterInfo()
            oldClusterInfo.initFromStaticConfigWithoutUser(static_config_file)
            # get max CN/CMA/master-standby DN/dummy DN instanceId of old cluster.
            # CMS/GTM/ETCD instanceId and nodeId will not be changed.
            (maxCNInstanceId, maxCMAInstanceId, maxMasterDNInstanceId, maxDummyDNInstanceId,
             maxMirrorId) = self.getCurrentlyInstanceId(oldClusterInfo)

            mirrorIdDict = {}
            for newdbNode in self.dbNodes:
                if len(newdbNode.coordinators) > 0:
                    # refresh CN instanceId here
                    newdbNode.coordinators[0].instanceId = maxCNInstanceId
                    maxCNInstanceId += 1

                if len(newdbNode.cmagents) > 0:
                    # refresh CMA instanceId here
                    newdbNode.cmagents[0].instanceId = maxCMAInstanceId
                    maxCMAInstanceId += 1

                for dnInst in newdbNode.datanodes:
                    if dnInst.instanceType == const.MASTER_INSTANCE:
                        masterInst = dnInst
                        # refresh master instanceId here
                        dnInst.instanceId = maxMasterDNInstanceId
                        maxMasterDNInstanceId = self.get_next_dn_instance_id(maxMasterDNInstanceId, True)

                        # get related standby and dummy-standby instances
                        for dbNode in self.dbNodes:
                            for inst in dbNode.datanodes:
                                if (inst.mirrorId == dnInst.mirrorId and
                                        inst.instanceType == const.STANDBY_INSTANCE):
                                    standbyInst = inst
                                    # refresh related standby instanceId here
                                    inst.instanceId = maxMasterDNInstanceId
                                    maxMasterDNInstanceId = self.get_next_dn_instance_id(maxMasterDNInstanceId, True)

                                elif (inst.mirrorId == dnInst.mirrorId and
                                      inst.instanceType == const.DUMMY_STANDBY_INSTANCE):
                                    dummyInst = inst
                                    # refresh related dummy-standby instanceId here
                                    inst.instanceId = maxDummyDNInstanceId
                                    maxDummyDNInstanceId = self.get_next_dn_instance_id(maxDummyDNInstanceId, False)

                        # refresh mirrorId here,Must refresh it at last.
                        mirrorIdDict[maxMirrorId] = [masterInst, standbyInst, dummyInst]
                        maxMirrorId += 1

            for mirrorId in mirrorIdDict.keys():
                mirrorIdDict[mirrorId][0].mirrorId = mirrorId
                mirrorIdDict[mirrorId][1].mirrorId = mirrorId
                mirrorIdDict[mirrorId][2].mirrorId = mirrorId
        except Exception as e:
            raise Exception(str(e))

    def getCurrentlyInstanceId(self, oldClusterInfo):
        """
        function : get max CN/CMA/master-standby DN/dummy DN instanceId of old cluster
        input : file Object
        output : NA
        """
        maxCNInstanceId = 0
        maxCMAInstanceId = 0
        maxMasterDNInstanceId = 0
        maxDummyDNInstanceId = 0
        # new DN mirrorId shoud be refreshed.
        # CN mirrorId is const -1, so no need to refresh.
        # CMA mirrorId is const-3, so no need to refresh.
        # ETCD mirrorId is const -5, so no need to refresh.
        # CMS and GTM of new cluster will not simultaneous exist with old cluster,
        # so no need to refresh.
        maxMirrorId = 0
        for olddbNode in oldClusterInfo.dbNodes:
            for oldcnInst in olddbNode.coordinators:
                if oldcnInst.instanceId > maxCNInstanceId:
                    maxCNInstanceId = oldcnInst.instanceId
            for oldcmaInst in olddbNode.cmagents:
                if oldcmaInst.instanceId > maxCMAInstanceId:
                    maxCMAInstanceId = oldcmaInst.instanceId
            for olddnInst in olddbNode.datanodes:
                if (olddnInst.instanceType == const.MASTER_INSTANCE and
                        olddnInst.instanceId > maxMasterDNInstanceId):
                    maxMasterDNInstanceId = olddnInst.instanceId
                elif (olddnInst.instanceType == const.DUMMY_STANDBY_INSTANCE and
                      olddnInst.instanceId > maxDummyDNInstanceId):
                    maxDummyDNInstanceId = olddnInst.instanceId
                if olddnInst.mirrorId > maxMirrorId:
                    maxMirrorId = olddnInst.mirrorId
            for oldcmsInst in olddbNode.cmservers:
                if oldcmsInst.mirrorId > maxMirrorId:
                    maxMirrorId = oldcmsInst.mirrorId
            for oldetcdInst in olddbNode.etcds:
                if oldetcdInst.mirrorId > maxMirrorId:
                    maxMirrorId = oldetcdInst.mirrorId

        maxCNInstanceId += 1
        maxCMAInstanceId += 1
        # The next maxMasterDNInstanceId must be maxMasterDNInstanceId + 2. (master -> standby -> master)
        maxStandbyDNInstanceId = self.get_next_dn_instance_id(maxMasterDNInstanceId, True)
        maxMasterDNInstanceId = self.get_next_dn_instance_id(maxStandbyDNInstanceId, True)

        maxDummyDNInstanceId = self.get_next_dn_instance_id(maxDummyDNInstanceId, False)
        maxMirrorId += 1
        return maxCNInstanceId, maxCMAInstanceId, maxMasterDNInstanceId, maxDummyDNInstanceId, maxMirrorId

    def flushNodeId(self, oldNodesList, newNodesList, clusterNodesList=None, nodename=""):
        """
        function : Refresh nodeid
        input : oldNodesList:                :The cluster nodes list from static_config_file
                newNodesList:                :The cluster nodes list from new oldes
                clusterNodesList:            :The cluster nodes list from xml
                nodename:                    :The node name will be deleted when do deleteCN
        output : NA
        """
        if clusterNodesList is None:
            clusterNodesList = []

        oldNodesList_MW = [n for n in oldNodesList if not n.isComputeOnlyNode()]
        oldNodesList_VW = [n for n in oldNodesList if n.isComputeOnlyNode()]

        newNodesList_MW = [n for n in newNodesList if not n.isComputeOnlyNode()]
        newNodesList_VW = [n for n in newNodesList if n.isComputeOnlyNode()]

        self.flushNodeIdInner(oldNodesList_MW, newNodesList_MW, clusterNodesList, nodename)
        if oldNodesList_VW and newNodesList_VW:
            self.flushNodeIdInner(oldNodesList_VW, newNodesList_VW, clusterNodesList, nodename)

    def flushNodeIdInner(self, oldNodesList, newNodesList, clusterNodesList, nodename=""):
        """
        """
        oldNodesNum = len(oldNodesList)
        newNodesNum = len(newNodesList)

        # get all nodeId list
        oldNodeIdList = []
        for oldNode in oldNodesList:
            oldNodeIdList.append(oldNode.id)

        # get the max node id number
        oldNodeIdList.sort()
        newNodeId = oldNodeIdList[-1] + 1

        # main function to refresh node id
        if newNodesNum >= oldNodesNum:
            # refresh node id one by one for oldNodesList
            ind = 0
            for oldNode in oldNodesList:
                newNodesList[ind].id = oldNode.id
                ind += 1

            # set new node id to newNodeId
            for i in range(ind, newNodesNum):
                newNodesList[i].id = newNodeId
                newNodeId += 1
        else:
            # only deleteCN & shrink newNodesNum < oldNodesNum
            # delete CN out of Cluster
            if len(clusterNodesList) > 0 and nodename != '':
                # set node id
                ind = 0
                for oldNode in oldNodesList:
                    if ind >= newNodesNum:
                        break
                    if oldNode.name == nodename:
                        continue
                    newNodesList[ind].id = oldNode.id
                    ind += 1
            # shrink, nothing to do

    def setInstId(self, instList, nodeIdInstIdDict, newNodeId, newInstId):
        """
        instList                  instance list
        nodeIdInstIdDict          node id and instance id dict
        newNodeId                 new node id
        newInstId                 new instance id
        """
        for inst in instList:
            if newNodeId in nodeIdInstIdDict.keys():
                inst.instanceId = nodeIdInstIdDict[newNodeId]
            # the New agent instance
            else:
                inst.instanceId = newInstId
                newInstId += 1
        return newInstId

    def getNodeIdInstanceIdDict(self, oldNodesList, newNodesList, instType):
        """
        """
        nodeIdInstanceIdDict = {}
        # get the node id and cmagent/cmserver/gtm/etcd/cn instance id dict
        for oldNode in oldNodesList:
            if instType == "cmagent":
                for cmaInst in oldNode.cmagents:
                    nodeIdInstanceIdDict[oldNode.id] = cmaInst.instanceId
            elif instType == "cmserver":
                for cmsInst in oldNode.cmservers:
                    nodeIdInstanceIdDict[oldNode.id] = cmsInst.instanceId
            elif instType == "gtm":
                for gtmInst in oldNode.gtms:
                    nodeIdInstanceIdDict[oldNode.id] = gtmInst.instanceId
            elif instType == "etcd":
                for etcdInst in oldNode.etcds:
                    nodeIdInstanceIdDict[oldNode.id] = etcdInst.instanceId
            elif instType == "cn":
                for cnInst in oldNode.coordinators:
                    # warm-standby:the number of nodes is same,refrush by id
                    # addcn out cluster:refrush by id or nodename
                    # addcn in cluster:refrush by id or nodename
                    # deletecn out cluster:refrush by nodename
                    # deletecn in cluster:refrush by id or nodename
                    # expand:refrush by id or nodename
                    # shink in tail:refrush by id or nodename
                    # shink in mid:refrush by nodename
                    if len(oldNodesList) == len(newNodesList):
                        nodeIdInstanceIdDict[oldNode.id] = cnInst.instanceId
                    else:
                        nodeIdInstanceIdDict[oldNode.name] = cnInst.instanceId

        return nodeIdInstanceIdDict

    def refreshInstIdByInstType(self, oldNodesList, newNodesList, instType="cmagent"):
        """
        """
        nodeIdInstanceIdDict = self.getNodeIdInstanceIdDict(oldNodesList, newNodesList, instType)
        # sort instance id lists and set newInstId = the max ID num + 1
        instIDList = list(nodeIdInstanceIdDict.values())
        instIDList.sort()
        if len(instIDList) > 0:
            newInstId = instIDList[-1] + 1
        else:
            newInstId = 1

        # refresh instance id by oldClusterInfo
        for newNode in newNodesList:
            if instType == "cmagent":
                newInstId = self.setInstId(newNode.cmagents, nodeIdInstanceIdDict, newNode.id, newInstId)
            elif instType == "cmserver":
                newInstId = self.setInstId(newNode.cmservers, nodeIdInstanceIdDict, newNode.id, newInstId)
            elif instType == "gtm":
                newInstId = self.setInstId(newNode.gtms, nodeIdInstanceIdDict, newNode.id, newInstId)
            elif instType == "etcd":
                newInstId = self.setInstId(newNode.etcds, nodeIdInstanceIdDict, newNode.id, newInstId)
            elif instType == "cn":
                if len(oldNodesList) == len(newNodesList):
                    newInstId = self.setInstId(newNode.coordinators, nodeIdInstanceIdDict, newNode.id, newInstId)
                else:
                    newInstId = self.setInstId(newNode.coordinators, nodeIdInstanceIdDict, newNode.name, newInstId)

    def flushCNInstanceId(self, oldNodesList, newNodesList):
        """
        function : Refresh CN instance id
        input : oldNodesList:                :The cluster nodes list from static_config_file
                newNodesList:                :The cluster nodes list from new oldes
        output : NA
        """
        self.refreshInstIdByInstType(oldNodesList, newNodesList, "cn")

    def getMaxStandbyAndDummyDNInstanceId(self, oldNodesList):
        """
        function : get max standby and dummy DN instanceId of old cluster.
        input : oldNodesList:                :The cluster nodes list from static_config_file
        output : NA
        """
        # get max standby and dummy DN instanceId of old cluster.
        maxStandbyDNInstanceId = 0
        maxDummyDNInstanceId = 0
        for oldNode in oldNodesList:
            for olddnInst in oldNode.datanodes:
                if olddnInst.instanceType == const.STANDBY_INSTANCE and olddnInst.instanceId > maxStandbyDNInstanceId:
                    maxStandbyDNInstanceId = olddnInst.instanceId
                elif olddnInst.instanceType == const.DUMMY_STANDBY_INSTANCE \
                        and olddnInst.instanceId > maxDummyDNInstanceId:
                    maxDummyDNInstanceId = olddnInst.instanceId
        return maxStandbyDNInstanceId, maxDummyDNInstanceId

    def getMaxVWDNInstanceId(self, oldNodesList):
        masterDNInstanceIdList = []
        for oldNode in oldNodesList:
            for inst in oldNode.datanodes:
                if inst.instanceType == const.MASTER_INSTANCE:
                    masterDNInstanceIdList.append(inst.instanceId)

        return max(masterDNInstanceIdList) + 1

    def get_next_dn_instance_id(self, instId, isMasterStandbyDn=True):
        """
        """
        if isMasterStandbyDn:
            if const.OLD_LAST_PRIMARYSTANDBY_BASEID_NUM <= instId < const.NEW_FIRST_PRIMARYSTANDBY_BASEID_NUM:
                instId = const.NEW_FIRST_PRIMARYSTANDBY_BASEID_NUM
        else:
            if const.OLD_LAST_DUMMYNODE_BASEID_NUM <= instId < const.NEW_FIRST_DUMMYNODE_BASEID_NUM:
                instId = const.NEW_FIRST_DUMMYNODE_BASEID_NUM
        instId += 1
        return instId

    def get_old_node(self, oldNodesList, newNodesList, isVW):
        """
        get all old node id list
        :param isVW:
        :param oldNodesList:
        :param newNodesList:
        :return:maxLen, minLen, maxMasterDNInstanceId, maxDummyDNInstanceId
        """
        oldNodeIdList = []
        for oldNode in oldNodesList:
            oldNodeIdList.append(oldNode.id)

        # get max standby and dummy DN instanceId of old cluster.
        if isVW:
            maxMasterDNInstanceId = self.getMaxVWDNInstanceId(oldNodesList)
            maxDummyDNInstanceId = 0
        else:
            (maxStandbyDNInstanceId, maxDummyDNInstanceId) = self.getMaxStandbyAndDummyDNInstanceId(oldNodesList)
            # set next primary/standby and dummy DN instanceId
            maxMasterDNInstanceId = self.get_next_dn_instance_id(maxStandbyDNInstanceId, True)
            maxDummyDNInstanceId = self.get_next_dn_instance_id(maxDummyDNInstanceId, False)

        # refresh DN instance id of new nodes by oldNodesList and maxMasterDNInstanceId/maxDummyDNInstanceId
        oldLen = len(oldNodesList)
        newLen = len(newNodesList)
        if oldLen > newLen:
            maxLen = oldLen
            minLen = newLen
        else:
            maxLen = newLen
            minLen = oldLen
        return maxLen, minLen, maxMasterDNInstanceId, maxDummyDNInstanceId

    def flushDNInstanceId(self, oldNodesList, newNodesList):
        """
        function : Refresh DN instance id. When refresh dn id, the node id has been refreshed.
        input : oldNodesList:                :The cluster nodes list from static_config_file
                newNodesList:                :The cluster nodes list from new oldes
        output : NA
        """
        oldNodesList_MW = [n for n in oldNodesList if not n.isComputeOnlyNode()]
        oldNodesList_VW = [n for n in oldNodesList if n.isComputeOnlyNode()]

        newNodesList_MW = [n for n in newNodesList if not n.isComputeOnlyNode()]
        newNodesList_VW = [n for n in newNodesList if n.isComputeOnlyNode()]

        self.flushDNInstanceIdInner(oldNodesList_MW, newNodesList_MW, False)
        if oldNodesList_VW and newNodesList_VW:
            self.flushDNInstanceIdInner(oldNodesList_VW, newNodesList_VW, True)

    def flushDNInstanceIdInner(self, oldNodesList, newNodesList, isVW):
        """
        """
        # get all old node id list
        maxLen, minLen, maxMasterDNInstanceId, maxDummyDNInstanceId = \
            self.get_old_node(oldNodesList, newNodesList, isVW)
        # refresh DN id one by one by old node
        i = 0
        for newNode in newNodesList[0:minLen]:
            # refresh DN instanceId if DN numbers not equal. Only for move DN instance
            if len(oldNodesList[i].datanodes) != len(newNode.datanodes):
                break
            else:
                # refresh DN instanceId one by one (primary/standby/dummy in cluster_static_config )
                # isMasterStandbyCluster and isSinglePrimaryMultiStandbyCluster are same
                instid = 0
                for dnInst in newNode.datanodes:
                    dnInst.instanceId = oldNodesList[i].datanodes[instid].instanceId
                    instid += 1
                i += 1

        # refresh the new node DN id
        for newNode in newNodesList[minLen:maxLen]:
            for dnInst in newNode.datanodes:
                if dnInst.instanceType == const.MASTER_INSTANCE:
                    standbyInsts, dummyStandbyInsts = self.getMasterDNPeerInstance(dnInst, isVW)

                    # refresh master instanceId here
                    dnInst.instanceId = maxMasterDNInstanceId
                    maxMasterDNInstanceId = self.get_next_dn_instance_id(maxMasterDNInstanceId, True)

                    # refresh standby/dummy instanceId here. Only do it under new dbnodes list
                    for tmpNode in newNodesList[minLen:maxLen]:
                        for tmpdnInst in tmpNode.datanodes:
                            if tmpdnInst.instanceType == const.STANDBY_INSTANCE:
                                for standbyInst in standbyInsts:
                                    if tmpdnInst.instanceId == standbyInst.instanceId:
                                        # refresh standby instanceId here
                                        tmpdnInst.instanceId = maxMasterDNInstanceId
                                        maxMasterDNInstanceId = \
                                            self.get_next_dn_instance_id(maxMasterDNInstanceId, True)
                            elif tmpdnInst.instanceType == const.DUMMY_STANDBY_INSTANCE:
                                for dummyStandbyInst in dummyStandbyInsts:
                                    if tmpdnInst.instanceId == dummyStandbyInst.instanceId:
                                        # refresh standby instanceId here
                                        tmpdnInst.instanceId = maxDummyDNInstanceId
                                        maxDummyDNInstanceId = self.get_next_dn_instance_id(maxDummyDNInstanceId, False)

    def getMasterDNPeerInstance(self, dnInst, isVW):
        # get standby/dummy instances
        # isMasterStandbyCluster only one standbyInst and one dummyStandbyinst
        # isSinglePrimaryMultiStandbyCluster at least two standbyInst and no dummyStandbyinst
        if isVW:
            standbyInsts = []
            dummyStandbyInsts = []
        else:
            standbyInsts = []
            dummyStandbyInsts = []
            peerInsts = self.getPeerInstance(dnInst)
            for inst in peerInsts:
                if inst.instanceType == const.STANDBY_INSTANCE:
                    standbyInsts.append(inst)
                elif inst.instanceType == const.DUMMY_STANDBY_INSTANCE:
                    dummyStandbyInsts.append(inst)

        return standbyInsts, dummyStandbyInsts

    def flushClusterIdInfo(self, static_config_file, clusterNodesList=None, nodename=""):
        """
        function : Refresh nodeID, CN/DN instance id For AP. When fresh CN/DN, do node id first
        input : static_config_file:              :cluster_static_config file
                clusterNodesList:                :The cluster nodes list from xml
                nodename:                        :The node name will be deleted when do deleteCN
        output : NA
        """
        if clusterNodesList is None:
            clusterNodesList = []
        try:
            # init oldClusterInfo
            oldClusterInfo = BaseDbClusterInfo()
            oldClusterInfo.initFromStaticConfigWithoutUser(static_config_file)
            if len(clusterNodesList) > 0:
                newDbNodes = clusterNodesList
            else:
                newDbNodes = self.dbNodes

            #######################################################
            # refresh node id
            #######################################################
            self.flushNodeId(oldClusterInfo.dbNodes, newDbNodes, clusterNodesList, nodename)

            #######################################################
            # refresh cmagent/cmserver/gtm/etcd instance id
            #######################################################
            self.refreshInstIdByInstType(oldClusterInfo.dbNodes, newDbNodes, "cmagent")

            #######################################################
            # refresh coordinator instance id
            #######################################################
            self.flushCNInstanceId(oldClusterInfo.dbNodes, newDbNodes)

            #######################################################
            # refresh datanode instance id
            #######################################################
            if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
                self.flushDNInstanceId(oldClusterInfo.dbNodes, newDbNodes)

        except Exception as e:
            raise Exception(str(e))

    def initFromXml(self, xmlFile, static_config_file="", mode="inherit"):
        """
        function : Init cluster from xml config file
        input : file Object for OLAP
                dbClusterInfo instance
                inherit: instance id inherit from the old cluster.
                append: instance id append to the old cluster.
        output : NA
        """
        if not os.path.exists(xmlFile):
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] % "XML configuration file")

        self.xmlFile = xmlFile

        # Set the environment variable, then the readcluster command can read from it.
        os.putenv(const.ENV_CLUSTERCONFIG, xmlFile)
        # parse xml file
        global xmlRootNode
        try:
            xmlRootNode = parse_xml.initParserXMLFile(xmlFile)
        except Exception as e:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51234"] % xmlFile + " Error:\n%s" % str(e))

        self.__readClusterGlobalInfo()
        self.__readClusterNodeInfo()
        if self.isSinglePrimaryMultiStandbyCluster():
            self.__checkAZNamesWithDNReplication()
        if self.isMasterStandbyMultiAZCluster():
            self.__checkAZInfoForMasterStandbyMultiAZ()
        if self.isSingleInstCluster() and not self.isHtapCluster():
            self.__checkAZForSingleInst()
        IpPort = self.__checkInstancePortandIP()
        if self.isMasterStandbyCluster() or \
                self.isSinglePrimaryMultiStandbyCluster() or \
                self.isMasterStandbyMultiAZCluster():
            self.__readExpandNodeInfo()
            self.__readClusterRingsInfo()
        self.__checkClusterConfig()
        self.config_version = self.get_cluster_version()
        if ((self.isMasterStandbyCluster() or
             self.isSinglePrimaryMultiStandbyCluster() or
             self.isMasterStandbyMultiAZCluster()) and
                os.path.isfile(static_config_file)):
            if mode == "inherit":
                self.flushClusterIdInfo(static_config_file)
            elif mode == "append" and (self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster()):
                # used for resize only. resize old and new clusters can not contain VW.
                self.__appendInstanceId(static_config_file)

        return IpPort

    def getClusterNodeNames(self):
        """
        function : Get the cluster's node names.
        input : NA
        output : NA
        """
        return [dbNode.name for dbNode in self.dbNodes]

    def getClusterDNNodeNames(self):
        """
        function : Get the cluster's DN node names.
        input : NA
        output : NA
        """
        hostPerNodeList = []
        for dbNode in self.dbNodes:
            # loop per node
            for dnInst in dbNode.datanodes:
                if dnInst.instanceType == const.MASTER_INSTANCE and dnInst.hostname not in hostPerNodeList:
                    hostPerNodeList.append(dnInst.hostname)
        return hostPerNodeList

    def getClusterNewNodeNames(self):
        """
        function : Get the cluster's node names.
        input : NA
        output : NA
        """
        return [dbNode.name for dbNode in self.newNodes]

    def getClusterDirectorys(self, hostName="", ignore=True):
        """
        function : Get cluster all directorys
        input : NA
        output : List
        """
        clusterDirs = {}
        clusterDirs["appPath"] = [self.appPath]
        if ignore:
            clusterDirs["logPath"] = [self.logPath]
        # get cluster all directorys
        for dbNode in self.dbNodes:
            nodeName = dbNode.name
            if hostName != "":
                if hostName != nodeName:
                    continue
            nodeDirs = []
            # including cm_server, cm_agent, cn, dn, gtm, etcd, ssd
            nodeDirs.append(dbNode.cmDataDir)
            for dbInst in dbNode.cmservers:
                nodeDirs.append(dbInst.datadir)
            for dbInst in dbNode.cmagents:
                nodeDirs.append(dbInst.datadir)
            for dbInst in dbNode.gtms:
                nodeDirs.append(dbInst.datadir)
            for dbInst in dbNode.coordinators:
                nodeDirs.append(dbInst.datadir)
                if len(dbInst.ssdDir) != 0:
                    nodeDirs.append(dbInst.ssdDir)
            for dbInst in dbNode.datanodes:
                nodeDirs.append(dbInst.datadir)
                if len(dbInst.ssdDir) != 0:
                    nodeDirs.append(dbInst.ssdDir)
            for dbInst in dbNode.etcds:
                nodeDirs.append(dbInst.datadir)
            clusterDirs[nodeName] = nodeDirs
        return clusterDirs

    def getDbNodeByName(self, name):
        """
        function : Get node by name.
        input : nodename
        output : []
        """
        for dbNode in self.dbNodes:
            if dbNode.name == name:
                return dbNode

        return None

    def getDbNodeByID(self, inputid):
        """
        function : Get node by id.
        input : nodename
        output : []
        """
        for dbNode in self.dbNodes:
            if dbNode.id == inputid:
                return dbNode

        return None

    def getMirrorInstance(self, mirrorId):
        """
        function : Get primary instance and standby instance.
        input : String
        output : []
        """
        instances = []

        for dbNode in self.dbNodes:
            for inst in dbNode.cmservers:
                if inst.mirrorId == mirrorId:
                    instances.append(inst)

            for inst in dbNode.gtms:
                if inst.mirrorId == mirrorId:
                    instances.append(inst)

            for inst in dbNode.coordinators:
                if inst.mirrorId == mirrorId:
                    instances.append(inst)

            for inst in dbNode.datanodes:
                if inst.mirrorId == mirrorId:
                    instances.append(inst)

        return instances

    def getPeerInstance(self, dbInst):
        """
        function : Get peer instance of specified instance.
        input : dbInst
        output : []
        """
        instances = []
        for dbNode in self.dbNodes:
            if dbInst.instanceRole == const.INSTANCE_ROLE_CMSERVER:
                for inst in dbNode.cmservers:
                    if (inst.mirrorId == dbInst.mirrorId and inst.instanceId != dbInst.instanceId):
                        instances.append(inst)
            elif dbInst.instanceRole == const.INSTANCE_ROLE_GTM:
                for inst in dbNode.gtms:
                    if (inst.mirrorId == dbInst.mirrorId and inst.instanceId != dbInst.instanceId):
                        instances.append(inst)
            elif dbInst.instanceRole == const.INSTANCE_ROLE_COODINATOR:
                for inst in dbNode.coordinators:
                    if (inst.mirrorId == dbInst.mirrorId and inst.instanceId != dbInst.instanceId):
                        instances.append(inst)
            elif dbInst.instanceRole == const.INSTANCE_ROLE_DATANODE:
                for inst in dbNode.datanodes:
                    if dbInst.mirrorId != const.MIRROR_ID_VW_DN and \
                            inst.mirrorId == dbInst.mirrorId and \
                            inst.instanceId != dbInst.instanceId:
                        instances.append(inst)
        return instances

    def getAllCNInsts(self):
        """
        function : get CN instance list on all nodes.
        input : NA
        output : list
        """
        insts = []
        for dbNode in self.dbNodes:
            insts.extend(dbNode.coordinators)
        return insts

    def getAllDNInsts(self):
        """
        function : get DN instance list on all nodes.
        input:NA
        output:list
        """
        insts = []
        for dbNode in self.dbNodes:
            for dnInst in dbNode.datanodes:
                if dnInst.instanceType == const.MASTER_INSTANCE:
                    insts.append(dnInst)
                    peerInsts = self.getPeerInstance(dnInst)
                    for inst in peerInsts:
                        if inst.instanceType == const.STANDBY_INSTANCE:
                            insts.append(inst)
                    for inst in peerInsts:
                        if inst.instanceType == const.DUMMY_STANDBY_INSTANCE:
                            insts.append(inst)
        return insts

    def getClusterBackIps(self):
        """
        function : Get cluster back IP.
        input : NA
        output : []
        """
        backIps = []
        backIpNum = []
        # get backIp number
        for dbNode in self.dbNodes:
            backIpNum.append(len(dbNode.backIps))
        if max(backIpNum) != min(backIpNum):
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51227"] % "backIps")
        for num in range(backIpNum[0]):
            ips = []
            for dbNode in self.dbNodes:
                ips.append(dbNode.backIps[num])
            backIps.extend(ips)
        return backIps

    def getClusterSshIps(self):
        """
        function : Get cluster ssh IP.
        input : NA
        output : []
        """
        sshIps = []
        sshIpNum = []
        # get sshIp number
        for dbNode in self.dbNodes:
            sshIpNum.append(len(dbNode.sshIps))
        if max(sshIpNum) != min(sshIpNum):
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51227"] % "sshIps")
        for num in range(sshIpNum[0]):
            ips = []
            for dbNode in self.dbNodes:
                ips.append(dbNode.sshIps[num])
            sshIps.append(ips)
        return sshIps

    def get_az_info(self):
        az_info = {}
        for dbNode in self.dbNodes:
            if dbNode.azName in az_info:
                if dbNode.azPriority not in az_info[dbNode.azName]:
                    az_info[dbNode.azName].append(dbNode.azPriority)
            else:
                az_info[dbNode.azName] = [dbNode.azPriority]

        return az_info

    def getazNames(self):
        """
        """
        azNames = []
        for dbNode in self.dbNodes:
            if dbNode.azName not in azNames:
                azNames.append(dbNode.azName)
        return azNames

    def getNodeNameByBackIp(self, backIp):
        """
        function : Get Nodename by backip.
        input : String
        output : String
        """
        nodeName = ""
        for dbNode in self.dbNodes:
            if backIp in dbNode.backIps:
                nodeName = dbNode.name
                break
        return nodeName

    def __get_node_ips_and_ports(self, inst, node_ips, node_ports):
        """
        function: get db node ip and port
        input: inst
        output: node_ip list, node_port list
        """
        node_ips.extend(inst.listenIps)
        node_ips.extend(inst.haIps)
        node_ports.append(inst.port)
        node_ports.append(inst.haPort)

    def __checkInstancePortandIP(self):
        """
        function : Check instance Port and IP.
        input : NA
        output : NA
        """
        nodeipport = {}
        for dbNode in self.dbNodes:
            node_ips = []
            node_ports = []
            cmsListenIPs = []
            ipCheckMap = {}
            backIP1 = dbNode.backIps[0]
            node_ips.extend(dbNode.backIps)
            node_ips.extend(dbNode.sshIps)
            # get node ip and node port from cmserver
            for cmsInst in dbNode.cmservers:
                self.__get_node_ips_and_ports(cmsInst, node_ips, node_ports)
                cmsListenIPs = cmsInst.listenIps
                ipCheckMap["cmServerListenIp1"] = cmsInst.listenIps[0]
                ipCheckMap["cmServerHaIp1"] = cmsInst.haIps[0]
            # get node ip and node port from gtm
            for gtmInst in dbNode.gtms:
                self.__get_node_ips_and_ports(gtmInst, node_ips, node_ports)
            # get node ip and node port from cn
            for cooInst in dbNode.coordinators:
                self.__get_node_ips_and_ports(cooInst, node_ips, node_ports)
            # get node ip and node port from dn
            for dnInst in dbNode.datanodes:
                self.__get_node_ips_and_ports(dnInst, node_ips, node_ports)
                if self.checkSctpPort:
                    node_ports.append(dnInst.port + dbNode.getDnNum(dnInst.instanceType) * 2)
            # get node ip and node port from etcd
            for etcdInst in dbNode.etcds:
                self.__get_node_ips_and_ports(etcdInst, node_ips, node_ports)
                ipCheckMap["etcdListenIp1"] = etcdInst.listenIps[0]
                ipCheckMap["etcdHaIp1"] = etcdInst.haIps[0]
                if len(etcdInst.listenIps) > 1:
                    etcdListenIp2 = etcdInst.listenIps[1]
                    if etcdListenIp2 != backIP1:
                        raise Exception(ErrorCode.GAUSS_512["GAUSS_51220"] % ("%s with etcdListenIp2" % etcdListenIp2) +
                                        " Error: \nThe IP address must be the same as the backIP1 %s." % backIP1)

            # CMS IP must be consistent with CMA IP
            cmaListenIPs = dbNode.cmagents[0].listenIps
            if cmsListenIPs and cmsListenIPs != cmaListenIPs:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51220"] % ("%s with cm_server" % cmsListenIPs) +
                                " Error: \nThe IP address must be the same as the cm_agent %s." % cmaListenIPs)
            if g_networkType == 1:
                # Check
                ipCheckMap["cmAgentConnectIp1"] = cmaListenIPs[0]
                if len(set(ipCheckMap.values())) != 1:
                    errMsg = " Error: \nThe following IPs must be consistent:"
                    for ipConfigItem in ipCheckMap.keys():
                        errMsg = "%s\n%s: %s" % (errMsg, ipConfigItem, ipCheckMap.get(ipConfigItem))
                    raise Exception(ErrorCode.GAUSS_512["GAUSS_51220"] % "with cm and etcd" + errMsg)
            # create a dictionary
            nodeipport[dbNode.name] = [node_ips, node_ports]
            # delete redundant records
            self.__Deduplication(node_ports)
            self.__Deduplication(node_ips)
            # check port and ip
            self.__checkPortandIP(node_ips, node_ports, dbNode.name)
        return nodeipport

    def __Deduplication(self, currentlist):
        """
        function : Delete the deduplication.
        input : []
        output : NA
        """
        currentlist.sort()
        for i in range(len(currentlist) - 2, -1, -1):
            if currentlist.count(currentlist[i]) > 1:
                del currentlist[i]

    def __checkPortandIP(self, ips, ports, name):
        """
        function : Check  port and IP.
        input : String,int,string
        output : NA
        """
        for port in ports:
            if not self.__isPortValid(port):
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51233"] % (port, name) + " Please check it.")

        for ip in ips:
            if not self.__isIpValid(ip):
                raise Exception(ErrorCode.GAUSS_506["GAUSS_50603"] +
                                "The IP address is: %s." % ip + " Please check it.")

    def __readClusterGlobalInfo(self):
        """
        Read cluster info from xml config's <CLUSTER> tag except nodeNames,
        clusterRings and sqlExpandNames info
        :return: NA
        """
        global g_networkType
        # Read cluster type
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "clusterType", "cluster")
        if retStatus == 0:
            self.clusterType = retValue.strip()
            if (self.clusterType not in (const.CLUSTER_TYPE_SINGLE,
                                         const.CLUSTER_TYPE_MASTER_STANDBY,
                                         const.CLUSTER_TYPE_MASTER_STANDBY_MULTI_AZ,
                                         const.CLUSTER_TYPE_SINGLE_PRIMARY_MULTI_STANDBY,
                                         const.CLUSTER_TYPE_SINGLE_INST)):
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                "cluster type" + " Error: \n%s" % self.clusterType)

        # Read cluster name
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "clusterName", "cluster")
        if retStatus != 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] % "cluster name" + " Error: \n%s" % retValue)
        self.name = retValue.strip()
        parse_xml.checkPathVaild(self.name)

        # Read application install path
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "gaussdbAppPath", "cluster")
        if retStatus != 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            "application installation path" + " Error: \n%s" % retValue)
        self.appPath = os.path.normpath(retValue)
        parse_xml.checkPathVaild(self.appPath)

        # Read application log path
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "gaussdbLogPath", "cluster")
        if retStatus == 0:
            self.logPath = os.path.normpath(retValue)
            parse_xml.checkPathVaild(self.logPath)
        elif retStatus == 2:
            self.logPath = ""
        else:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            "application log path" + " Error: \n%s" % retValue)
        if self.logPath == "":
            self.logPath = "/var/log/gaussdb"
        if not os.path.isabs(self.logPath):
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50213"] %
                            ("%s log path(%s)" % (VersionInfo.PRODUCT_NAME, self.logPath)))

        # Read network type
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "networkType", "cluster")
        if retStatus == 0:
            if retValue.isdigit() and int(retValue) in [0, 1]:
                g_networkType = int(retValue)
            else:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                "cluster network type" + " Error: \nThe parameter value must be 0 or 1.")
        elif retStatus == 2:
            g_networkType = 0
        else:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            "cluster network type" + " Error: \n%s" % retValue)

    def __getAllHostnamesFromDEVICELIST(self):
        """
        function : Read all host name from <DEVICELIST>
        input : Na
        output : str
        """
        DeviceArray = xmlRootNode.findall('DEVICELIST')[0]
        DeviceNodeList = DeviceArray.findall('DEVICE')
        allNodeName = []
        for dev in DeviceNodeList:
            paramList = dev.findall('PARAM')
            for param in paramList:
                thisname = param.attrib['name']
                if thisname == 'name':
                    value = param.attrib['value']
                    allNodeName.append(value)
        return allNodeName

    def __checkClusterNodeInfo(self, nodeNames_tmp):
        """
        function: check cluster Before getting node information.
        input : nodeNames_tmp
        output: nodenames
        """
        nodeNames = []
        for nodename in nodeNames_tmp:
            nodeNames.append(nodename.strip())
        if len(nodeNames) == 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            "cluster configuration" + " There is no node in cluster configuration file.")

        if len(nodeNames) != len(list(set(nodeNames))):
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            "cluster configuration" + " There contains repeated node in cluster configuration file.")

        if self.isSingleCluster() and len(nodeNames) != 1:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            "cluster configuration" + " There is no node in cluster configuration file.")

        # Check node names
        nodeNameList = self.__getAllHostnamesFromDEVICELIST()
        if len(nodeNameList) != len(nodeNames):
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                            " The number of nodeNames and DEVICE are not same.")
        for nodeName in nodeNames:
            if nodeName not in nodeNameList:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                                " Can not found DEVICE for [%s]." % nodeName)
        return nodeNames

    def __readClusterNodeInfo(self):
        """
        function : Read cluster node info.
        input : NA
        output : NA
        """
        # read cluster node info.
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "nodeNames", "cluster")
        if retStatus != 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] % "node names"
                            + " Error: \n%s" % retValue)

        nodeNames_tmp = retValue.split(",")
        nodeNames = self.__checkClusterNodeInfo(nodeNames_tmp)
        # Get basic info of node: name, ip and master instance number etc.
        self.dbNodes = []
        for name in nodeNames:
            dbNode = dbNodeInfo(name=name)
            self.__readNodeBasicInfo(dbNode, nodeNames)
            self.dbNodes.append(dbNode)

        # Get cm server info
        for dbNode in self.dbNodes:
            self.__readCmsConfig(dbNode)

        if not self.isSingleInstCluster():
            # Get gtm info
            for dbNode in self.dbNodes:
                self.__readGtmConfig(dbNode)

            # Get coordinator info
            for dbNode in self.dbNodes:
                self.__readCooConfig(dbNode)

        # Get datanode info
        for dbNode in self.dbNodes:
            self.__readDataNodeConfig(dbNode)

        # Get cm agent info
        for dbNode in self.dbNodes:
            self.__readCmaConfig(dbNode)

        # get ETCD info
        for dbNode in self.dbNodes:
            self.__readEtcdConfig(dbNode)

        # set dn port for OLAP
        if self.isSinglePrimaryMultiStandbyCluster() or self.isSingleInstCluster():
            # set AZname
            self.__setAZNameForMultiAZ()
            # set DN port
            self.__setDnPortForSinglePrimaryMultiStandby()
            # set cmserver port
            self.__setCMSPortForSinglePrimaryMultiStandby()
            # set gtm port
            self.__setGTMPortForSinglePrimaryMultiStandby()

        if self.isMasterStandbyMultiAZCluster():
            self.__setAZNameForMultiAZ()

        # set sctpPort/controlPort/servicePort
        self.__setNewPortForDnInst()
        self.__setNewPortForCnInst()

    def __setAZNameForMultiAZ(self):
        """
        function : set AZ Name
        input : []
        output : NA
        """
        for node in self.dbNodes:
            for inst in node.datanodes + node.coordinators + node.cmservers + node.gtms + node.etcds:
                inst.azName = node.azName

    def __setDnPortForSinglePrimaryMultiStandby(self):
        """
        function : set the standy dn port.
        input : []
        output : NA
        """
        for dbNode in self.dbNodes:
            # flush DN instance port
            i = 0
            for dbInst in dbNode.datanodes:
                if dbInst.instanceType == const.MASTER_INSTANCE:
                    dbInst.port = dbNode.masterBasePorts[const.INSTANCE_ROLE_DATANODE] + \
                                  i * const.PORT_STEP_SIZE
                    dbInst.haPort = dbInst.port + 1
                    peerInsts = self.getPeerInstance(dbInst)
                    for j in range(len(peerInsts)):
                        peerInsts[j].port = dbInst.port
                        peerInsts[j].haPort = peerInsts[j].port + 1
                    i += 1

    def __setCMSPortForSinglePrimaryMultiStandby(self):
        """
        function : flush CMSERVER instance port
        input : []
        output : NA
        """
        for dbNode in self.dbNodes:
            # flush CMSERVER instance port
            i = 0
            for dbInst in dbNode.cmservers:
                if dbInst.instanceType == const.MASTER_INSTANCE:
                    cmsbaseport = dbNode.masterBasePorts[const.INSTANCE_ROLE_CMSERVER]
                    dbInst.port = cmsbaseport + i * const.PORT_STEP_SIZE
                    dbInst.haPort = dbInst.port + 1
                    peerInsts = self.getPeerInstance(dbInst)
                    for j in range(len(peerInsts)):
                        peerInsts[j].port = cmsbaseport
                        peerInsts[j].haPort = peerInsts[j].port + 1
                    i += 1

    def __setGTMPortForSinglePrimaryMultiStandby(self):
        """
        function : flush GTM instance port
        input : []
        output : NA
        """
        for dbNode in self.dbNodes:
            # flush GTM instance port
            i = 0
            for dbInst in dbNode.gtms:
                if dbInst.instanceType == const.MASTER_INSTANCE:
                    gtmbaseport = dbNode.masterBasePorts[const.INSTANCE_ROLE_GTM]
                    dbInst.port = gtmbaseport + i * const.PORT_STEP_SIZE
                    dbInst.haPort = dbInst.port + 1
                    peerInsts = self.getPeerInstance(dbInst)
                    for j in range(len(peerInsts)):
                        peerInsts[j].port = gtmbaseport
                        peerInsts[j].haPort = peerInsts[j].port + 1
                    i += 1

    def __setDnNewPortForSinglePrimaryMultiStandby(self):
        """
        function : set the dn port.
        input : []
        output : NA
        """
        for dbNode in self.dbNodes:
            # flush DN instance port
            i = 0
            for dbInst in dbNode.datanodes:
                if dbInst.instanceType == const.MASTER_INSTANCE:
                    dbNode = self.getDbNodeByName(dbInst.hostname)
                    if dbNode is None:
                        raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                        ("DN configuration on host [%s]" % dbInst.hostname))
                    dbInst.sctpPort = dbInst.port + 2
                    dbInst.controlPort = dbInst.port + 3
                    dbInst.servicePort = dbInst.port + 4

                    peerInsts = self.getPeerInstance(dbInst)
                    for j in range(len(peerInsts)):
                        peerDbNode = self.getDbNodeByName(peerInsts[j].hostname)
                        if peerDbNode is None:
                            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                            ("DN configuration on host [%s]" % peerInsts[j].hostname))
                        peerInsts[j].sctpPort = peerInsts[j].port + 2
                        peerInsts[j].controlPort = peerInsts[j].port + 3
                        peerInsts[j].servicePort = peerInsts[j].port + 4
                    i += 1

    def __setDnNewPortForSingleCluster(self):
        """
        function : set single cluster dn port.
        input : []
        output : NA
        """
        # there is only master DN instance on single cluster
        for dbNode in self.dbNodes:
            for dbInst in dbNode.datanodes:
                # get master instance number
                masterDbNode = self.getDbNodeByName(dbInst.hostname)
                if masterDbNode is None:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] % (
                            "DN configuration on host [%s]" % dbInst.hostname))
                masterDataNum = masterDbNode.getDnNum(dbInst.instanceType)
                # assign the port
                dbInst.sctpPort = dbInst.port + masterDataNum * 2
                dbInst.controlPort = dbInst.port + masterDataNum * 2 + 1
                dbInst.servicePort = dbInst.port + masterDataNum * 4

    def __setDnNewPortForMasterStandbyCluster(self):
        """
        function : set master/standby/dummy cluster dn port.
        input : NA
        output : NA
        """
        for dbNode in self.dbNodes:
            masterDataNum = dbNode.getDnNum(const.MASTER_INSTANCE)
            standbyDataNum = dbNode.getDnNum(const.STANDBY_INSTANCE)
            dummyDataNum = dbNode.getDnNum(const.DUMMY_STANDBY_INSTANCE)
            for dbInst in dbNode.datanodes:
                # get the instance type num
                dataNum = 1
                if dbInst.instanceType == const.MASTER_INSTANCE:
                    dataNum = masterDataNum
                elif dbInst.instanceType == const.STANDBY_INSTANCE:
                    dataNum = standbyDataNum
                elif dbInst.instanceType == const.DUMMY_STANDBY_INSTANCE:
                    dataNum = dummyDataNum
                # assign the port
                dbInst.sctpPort = dbInst.port + dataNum * 2
                dbInst.controlPort = dbInst.port + dataNum * 2 + 1
                dbInst.servicePort = dbInst.port + dataNum * 4

    def __setNewPortForDnInst(self):
        """
        function : set DN new port.
        input : NA
        output : NA
        """
        # primary, standy, dummystandby
        # single cluster
        # single primary multi standy
        if (self.isSinglePrimaryMultiStandbyCluster()
                or self.isSingleInstCluster()):
            self.__setDnNewPortForSinglePrimaryMultiStandby()
        elif self.isSingleCluster():
            self.__setDnNewPortForSingleCluster()
        elif self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
            self.__setDnNewPortForMasterStandbyCluster()

    def __setNewPortForCnInst(self):
        """
        function : set cn new port.
        input : NA
        output : NA
        """
        # primary, standy, dummystandby
        # single cluster
        # single primary multi standy
        for dbNode in self.dbNodes:
            for cnInst in dbNode.coordinators:
                cnInst.sctpPort = cnInst.port + 2
                cnInst.controlPort = cnInst.port + 3
                cnInst.servicePort = 0

    def __readEtcdConfig(self, dbNode):
        """
        function : Read ETCD config info on node.
        input : []
        output : NA
        """
        etcdClientIp = None
        etcdHAIp = None

        if dbNode.etcdNum > 0:
            if dbNode.isComputeOnlyNode():
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                                "Can not contain ETCD in %s." % dbNode.name)

            self.etcdcount += self.__readNodeIntValue(dbNode.name, "dbNode", True, 0)
            etcdClientIp = self.__readInstanceIps(dbNode.name, "etcdListenIp", 1)
            etcdHAIp = self.__readInstanceIps(dbNode.name, "etcdHaIp", 1)

        for i in range(dbNode.etcdNum):
            key = "etcdDir%d" % (i + 1)
            etcdDir = self.__readNodeStrValue(dbNode.name, key)
            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_ETCD)
            dbNode.appendInstance(instId, const.MIRROR_ID_ETCD,
                                  const.INSTANCE_ROLE_ETCD,
                                  const.INSTANCE_TYPE_UNDEFINED,
                                  etcdClientIp[i], etcdHAIp[i], etcdDir,
                                  clusterType=self.clusterType)
            i += 1

    def __readExpandNodeInfo(self):
        """
        function : Read expand node info.
        input : NA
        output : NA
        """
        # read expand node info.
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "sqlExpandNames", "cluster")
        if retStatus != 0 or retValue.strip() == "":
            return
        nodeNames = [nodeName.strip() for nodeName in retValue.split(",")]
        if len(nodeNames) == 0:
            return

        for nodeName in nodeNames:
            dbNode = self.getDbNodeByName(nodeName)
            if dbNode is not None:
                self.newNodes.append(dbNode)
            else:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                "expand nodes configuration" +
                                " There is no node [%s] in cluster configuration file." % nodeName)

    def __readClusterRingsInfo(self):
        """
        function : Read cluster rings info.
        input : NA
        output : NA
        """
        # read cluster rings info.
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, "clusterRings", "cluster")
        if retStatus != 0 or retValue.strip() == "":
            return
        rings = retValue.split(";")
        if len(rings) == 0:
            return
        for ring in rings:
            ring_tmp = []
            for ring_one in ring.strip().split(","):
                ring_tmp.append(ring_one.strip())
            self.clusterRings.append(ring_tmp)

    def __get_base_port(self, db_node):
        """
        function: get cms, gtm, etcd, cn, dn base port
        input: db node
        output:
        """
        if db_node.cmsNum > 0:
            db_node.masterBasePorts[const.INSTANCE_ROLE_CMSERVER] = \
                self.__readNodeIntValue(db_node.name, "cmServerPortBase", True, const.MASTER_BASEPORT_CMS)
            if self.isSinglePrimaryMultiStandbyCluster() or self.isSingleInstCluster():
                db_node.standbyBasePorts[const.INSTANCE_ROLE_CMSERVER] = \
                    db_node.masterBasePorts[const.INSTANCE_ROLE_CMSERVER]

        if db_node.gtmNum > 0:
            db_node.masterBasePorts[const.INSTANCE_ROLE_GTM] = \
                self.__readNodeIntValue(db_node.name, "gtmPortBase", True, const.MASTER_BASEPORT_GTM)
            if self.isSinglePrimaryMultiStandbyCluster():
                db_node.standbyBasePorts[const.INSTANCE_ROLE_GTM] = db_node.masterBasePorts[const.INSTANCE_ROLE_GTM]

        if db_node.etcdNum > 0:
            db_node.masterBasePorts[const.INSTANCE_ROLE_ETCD] = \
                self.__readNodeIntValue(db_node.name, "etcdListenPort", True, const.MASTER_BASEPORT_ETCD)
            db_node.standbyBasePorts[const.INSTANCE_ROLE_ETCD] = \
                self.__readNodeIntValue(db_node.name, "etcdHaPort", True, const.STANDBY_BASEPORT_ETCD)

        if db_node.cooNum > 0:
            db_node.masterBasePorts[const.INSTANCE_ROLE_COODINATOR] = \
                self.__readNodeIntValue(db_node.name, "cooPortBase", True,
                                        const.MASTER_BASEPORT_COO)

        if db_node.dataNum > 0:
            db_node.masterBasePorts[const.INSTANCE_ROLE_DATANODE] = \
                self.__readNodeIntValue(db_node.name, "dataPortBase", True,
                                        const.MASTER_BASEPORT_DATA)
            if self.isSinglePrimaryMultiStandbyCluster() or self.isSingleInstCluster():
                db_node.standbyBasePorts[const.INSTANCE_ROLE_DATANODE] = \
                    db_node.masterBasePorts[const.INSTANCE_ROLE_DATANODE]

    def __readNodeBasicInfo(self, dbNode, nodenames):
        """
        function : Read basic info of specified node.
        input : []
        output : NA
        """
        # get backIp
        dbNode.backIps = self.__readNodeIps(dbNode.name, "backIp")
        if len(dbNode.backIps) == 0:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51207"] % dbNode.name)
        # get sshIp
        dbNode.sshIps = self.__readNodeIps(dbNode.name, "sshIp")
        if len(dbNode.sshIps) == 0:
            dbNode.sshIps = dbNode.backIps[:]
        # get virtualIp
        dbNode.virtualIp = self.__readVirtualIp(dbNode.name, "virtualIp")

        # Get cm_server number
        dbNode.cmsNum = self.__readNodeIntValue(dbNode.name, "cmsNum", True, 0)
        # Get gtm number
        dbNode.gtmNum = self.__readNodeIntValue(dbNode.name, "gtmNum", True, 0)
        # Get etcd number
        dbNode.etcdNum = self.__readNodeIntValue(dbNode.name, "etcdNum", True, 0)
        # Get cn number
        dbNode.cooNum = self.__readNodeIntValue(dbNode.name, "cooNum", True, 0)
        # Get dn number
        dbNode.dataNum = self.__readNodeIntValue(dbNode.name, "dataNum", True, 0)

        # check num and cmdir
        instDict = {"cms": dbNode.cmsNum,
                    "gtm": dbNode.gtmNum,
                    "cn": dbNode.cooNum,
                    "dn": dbNode.dataNum,
                    "etcd": dbNode.etcdNum
                    }
        self.__checkNodeBasicInfo(instDict, dbNode, nodenames)

        # Get base port
        self.__get_base_port(dbNode)
        if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
            dbNode.standbyBasePorts[const.INSTANCE_ROLE_CMSERVER] = \
                self.__readNodeIntValue(dbNode.name, "cmServerPortStandby", True, const.STANDBY_BASEPORT_CMS)
            dbNode.standbyBasePorts[const.INSTANCE_ROLE_GTM] = \
                self.__readNodeIntValue(dbNode.name, "gtmPortStandby", True, const.STANDBY_BASEPORT_GTM)
            dbNode.standbyBasePorts[const.INSTANCE_ROLE_DATANODE] = \
                self.__readNodeIntValue(dbNode.name, "dataPortStandby", True, const.STANDBY_BASEPORT_DATA)
            dbNode.dummyStandbyBasePort = \
                self.__readNodeIntValue(dbNode.name, "dataPortDummyStandby", True, const.DUMMY_STANDBY_BASEPORT_DATA)

        if self.isSinglePrimaryMultiStandbyCluster() or \
                self.isSingleInstCluster() or \
                self.isMasterStandbyMultiAZCluster():
            try:
                # Get az name
                dbNode.azName = self.__readNodeStrValue(dbNode.name, "azName")
                # check azName
                if dbNode.azName == "":
                    raise Exception(ErrorCode.GAUSS_512["GAUSS_51212"] % "azName")
                if (self.isSinglePrimaryMultiStandbyCluster() or self.isSingleInstCluster()) \
                        and dbNode.azName not in const.AZNMAE_LIST:
                    raise Exception("Please check [%s] azName value, azName must be in [AZ1, AZ2, AZ3]" % dbNode.name)

                # Get az Priority
                dbNode.azPriority = self.__readNodeIntValue(dbNode.name, "azPriority", True, 0)
                if dbNode.azPriority < const.AZPRIORITY_MIN or dbNode.azPriority > const.AZPRIORITY_MAX:
                    raise Exception(ErrorCode.GAUSS_532["GAUSS_53206"] % "azPriority")
            except Exception as e:
                # htap cluster is no azname
                if self.isSingleInstCluster():
                    self.is_htap_cluster = True
                else:
                    raise Exception(str(e))

        dbNode.nodeType = const.NODE_TYPE_REPL
        if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
            node_type = self.__readNodeStrValue(dbNode.name, "nodeType", True, "")
            if node_type == "readonly":
                dbNode.nodeType = const.NODE_TYPE_VW
                self.vwNodes.append(dbNode.name)

        dbNode.id = self.__assignNewNodeId(dbNode.nodeType)

    def __checkNodeBasicInfo(self, instDict, dbNode, nodenames):
        """
        function:check instance number
        input:dict
        output:na
        """
        for name in instDict.keys():
            if instDict[name] < 0:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51208"] % (name, instDict[name]))

        # read cm directory for server and agent
        if (self.isSingleCluster() or
                self.isMasterStandbyCluster() or
                self.isMasterStandbyMultiAZCluster() or
                self.isSinglePrimaryMultiStandbyCluster() or
                self.isSingleInstCluster()):
            dbNode.cmDataDir = self.__readNodeStrValue(dbNode.name, "cmDir")
            for nodename in nodenames:
                if dbNode.cmDataDir.replace(" ", "").find("," + nodename.replace(" ", "") + ",") >= 0:
                    raise Exception(ErrorCode.GAUSS_512["GAUSS_51235"] % dbNode.cmDataDir +
                                    " The cmDir only need one path while you "
                                    "configure it with primary and standby cmDir, "
                                    "please modify it and try again. "
                                    "You can examine the install guide for more information to configure xml file.")

    def __getCmsCountFromWhichConfiguredNode(self, masterNode):
        """
        function : get the count of cmservers if current node configured cmserver
        input : masterNode
        output : cmsCount
        """
        cmsList = self.__readNodeStrValue(masterNode.name, "cmServerRelation", True, "").split(",")
        if len(cmsList) == 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            ("CMServer configuration on host [%s]" % str(masterNode.name)) +
                            " The information of %s is wrong." % "cmServerRelation")
        cmsCount = len(cmsList)
        return cmsCount

    def __getGtmsCountFromWhichConfiguredNode(self, masterNode):
        """
        function : get the count of cmservers if current node configured cmserver
        input : masterNode
        output : cmsCount
        """
        gtmList = self.__readNodeStrValue(masterNode.name, "gtmRelation", True, "").split(",")
        if len(gtmList) == 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            ("GTM configuration on host [%s]" % str(masterNode.name)) +
                            " The information of %s is wrong." % "gtmRelation")
        gtmCount = len(gtmList)
        return gtmCount

    def __readCmsConfig(self, masterNode):
        """
        function : Read cm server config on node.
        input : []
        output : NA
        """
        if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
            self.__readCmsConfigForMasterStandby(masterNode)
        elif self.isSingleCluster():
            self.__readCmsConfigForSingle(masterNode)
        elif self.isSinglePrimaryMultiStandbyCluster() or self.isSingleInstCluster():
            self.__readCmsConfigForMutilAZ(masterNode)

    def __readCmsConfigForMasterStandby(self, masterNode):
        """
        """
        cmsListenIps = None
        cmsHaIps = None
        if (masterNode.cmsNum > 0):
            if masterNode.isComputeOnlyNode():
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                                "Can not contain cmServer in %s." % masterNode.name)

            self.cmscount = self.__getCmsCountFromWhichConfiguredNode(masterNode)
            cmsListenIps = self.__readInstanceIps(masterNode.name, "cmServerListenIp",
                                                  masterNode.cmsNum * const.MIRROR_COUNT_CMS)
            cmsHaIps = self.__readInstanceIps(masterNode.name, "cmServerHaIp",
                                              masterNode.cmsNum * const.MIRROR_COUNT_CMS)

        for i in range(masterNode.cmsNum):
            level = self.__readNodeIntValue(masterNode.name, "cmServerlevel")
            hostNames = []
            hostNames_tmp = self.__readNodeStrValue(masterNode.name, "cmServerRelation").split(",")
            for hostname in hostNames_tmp:
                hostNames.append(hostname.strip())
            if len(hostNames) != const.MIRROR_COUNT_CMS:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("CMServer configuration on host [%s]" % masterNode.name) +
                                " The information of cmServerRelation is wrong.")

            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_CMSERVER)
            mirrorId = self.__assignNewMirrorId()
            instIndex = i * const.MIRROR_COUNT_CMS
            masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_CMSERVER, const.MASTER_INSTANCE,
                                      cmsListenIps[instIndex], cmsHaIps[instIndex], "", "", level,
                                      clusterType=self.clusterType)

            for j in range(1, const.MIRROR_COUNT_CMS):
                dbNode = self.getDbNodeByName(hostNames[j])
                if dbNode is None:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                    ("CMServer configuration on host [%s]" % masterNode.name) +
                                    " There is no host named %s." % hostNames[j])
                instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_CMSERVER)
                instIndex += 1
                dbNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_CMSERVER, const.STANDBY_INSTANCE,
                                      cmsListenIps[instIndex], cmsHaIps[instIndex], "", "", level,
                                      clusterType=self.clusterType)

    def __readCmsConfigForSingle(self, masterNode):
        """
        """
        cmsListenIps = None
        if masterNode.cmsNum > 0:
            self.cmscount = self.__getCmsCountFromWhichConfiguredNode(masterNode)
            cmsListenIps = self.__readInstanceIps(masterNode.name, "cmServerListenIp", masterNode.cmsNum * 1)

        for i in range(masterNode.cmsNum):
            level = self.__readNodeIntValue(masterNode.name, "cmServerlevel")
            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_CMSERVER)
            mirrorId = self.__assignNewMirrorId()
            instIndex = i * 1
            masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_CMSERVER, const.MASTER_INSTANCE,
                                      cmsListenIps[instIndex], [], "", "", level, clusterType=self.clusterType)

    def __readCmsConfigForMutilAZ(self, masterNode):
        """
        """
        cmsListenIps = None
        cmsHaIps = None
        if masterNode.cmsNum > 0:
            self.cmscount = self.__getCmsCountFromWhichConfiguredNode(masterNode)
            cmsListenIps = self.__readInstanceIps(masterNode.name, "cmServerListenIp", self.cmscount)
            cmsHaIps = self.__readInstanceIps(masterNode.name, "cmServerHaIp", self.cmscount)

        for i in range(masterNode.cmsNum):
            level = self.__readNodeIntValue(masterNode.name, "cmServerlevel")
            hostNames = []
            hostNames_tmp = self.__readNodeStrValue(masterNode.name, "cmServerRelation").split(",")
            for hostname in hostNames_tmp:
                hostNames.append(hostname.strip())

            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_CMSERVER)
            mirrorId = self.__assignNewMirrorId()
            instIndex = i * self.cmscount
            masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_CMSERVER, const.MASTER_INSTANCE,
                                      cmsListenIps[instIndex], cmsHaIps[instIndex], "", "", level,
                                      clusterType=self.clusterType)

            for j in range(1, self.cmscount):
                dbNode = self.getDbNodeByName(hostNames[j])
                if dbNode is None:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                    ("CMServer configuration on host [%s]" % masterNode.name) +
                                    " There is no host named %s." % hostNames[j])
                instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_CMSERVER)
                instIndex += 1
                dbNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_CMSERVER, const.STANDBY_INSTANCE,
                                      cmsListenIps[instIndex], cmsHaIps[instIndex], "", "", level,
                                      clusterType=self.clusterType)

    def __readGtmConfig(self, masterNode):
        """
        function : Read gtm config on node.
        input : []
        output : NA
        """
        if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
            self.__readGtmConfigForMasterStandby(masterNode)
        elif self.isSinglePrimaryMultiStandbyCluster():
            self.__readGtmConfigForSingleMasterMultiStandby(masterNode)
        elif self.isSingleCluster():
            self.__readGtmConfigForSingle(masterNode)

    def __readGtmConfigForMasterStandby(self, masterNode):
        """
        """
        gtmListenIps = None
        gtmHaIps = None
        if masterNode.gtmNum > 0:
            if masterNode.isComputeOnlyNode():
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                                "Can not contain GTM in %s." % masterNode.name)

            self.gtmcount = self.__getGtmsCountFromWhichConfiguredNode(masterNode)
            gtmListenIps = self.__readInstanceIps(masterNode.name, "gtmListenIp",
                                                  masterNode.gtmNum * const.MIRROR_COUNT_GTM)
            gtmHaIps = self.__readInstanceIps(masterNode.name, "gtmHaIp", masterNode.gtmNum * const.MIRROR_COUNT_GTM)

        for i in range(masterNode.gtmNum):
            hostNames = []
            hostNames_tmp = self.__readNodeStrValue(masterNode.name, "gtmRelation").split(",")
            for hostname in hostNames_tmp:
                hostNames.append(hostname.strip())
            if len(hostNames) != const.MIRROR_COUNT_GTM:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("GTM configuration on host [%s]" % masterNode.name) +
                                " The information of [gtmRelation] is wrong.")
            gtmInfoList = []
            key = "gtmDir%d" % (i + 1)
            gtmInfoList_tmp = self.__readNodeStrValue(masterNode.name, key).split(",")
            for gtmInfo in gtmInfoList_tmp:
                gtmInfoList.append(gtmInfo.strip())
            if len(gtmInfoList) != 2 * const.MIRROR_COUNT_GTM - 1:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("GTM configuration on host [%s]" % masterNode.name) +
                                " The information of [%s] is wrong." % key)

            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_GTM)
            mirrorId = self.__assignNewMirrorId()
            instIndex = i * const.MIRROR_COUNT_GTM
            masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_GTM,
                                      const.MASTER_INSTANCE, gtmListenIps[instIndex],
                                      gtmHaIps[instIndex], gtmInfoList[0], clusterType=self.clusterType)

            for j in range(1, const.MIRROR_COUNT_GTM):
                dbNode = self.getDbNodeByName(hostNames[j])
                if dbNode is None:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                    ("GTM configuration on host [%s]" % masterNode.name) +
                                    " There is no host named %s." % hostNames[j])
                instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_GTM)
                instIndex += 1
                dbNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_GTM,
                                      const.STANDBY_INSTANCE, gtmListenIps[instIndex],
                                      gtmHaIps[instIndex], gtmInfoList[2 * j], clusterType=self.clusterType)

    def __readGtmConfigForSingleMasterMultiStandby(self, masterNode):
        """
        """
        gtmListenIps = None
        gtmHaIps = None
        if masterNode.gtmNum > 0:
            self.gtmcount = self.__getGtmsCountFromWhichConfiguredNode(masterNode)
            gtmListenIps = self.__readInstanceIps(masterNode.name, "gtmListenIp", self.gtmcount)
            gtmHaIps = self.__readInstanceIps(masterNode.name, "gtmHaIp", self.gtmcount)

        for i in range(masterNode.gtmNum):
            hostNames = []
            hostNames_tmp = self.__readNodeStrValue(masterNode.name, "gtmRelation").split(",")
            self.gtmcount = len(hostNames_tmp)
            for hostname in hostNames_tmp:
                hostNames.append(hostname.strip())
            if len(hostNames) != self.gtmcount:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("GTM configuration on host [%s]" % masterNode.name) +
                                " The information of [gtmRelation] is wrong.")
            gtmInfoList = []
            key = "gtmDir%d" % (i + 1)
            gtmInfoList_tmp = self.__readNodeStrValue(masterNode.name, key).split(",")
            for gtmInfo in gtmInfoList_tmp:
                gtmInfoList.append(gtmInfo.strip())
            if len(gtmInfoList) != 2 * self.gtmcount - 1:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("GTM configuration on host [%s]" % masterNode.name) +
                                " The information of [%s] is wrong." % key)

            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_GTM)
            mirrorId = self.__assignNewMirrorId()
            instIndex = i * self.gtmcount
            masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_GTM,
                                      const.MASTER_INSTANCE, gtmListenIps[instIndex],
                                      gtmHaIps[instIndex], gtmInfoList[0], clusterType=self.clusterType)

            for j in range(1, self.gtmcount):
                dbNode = self.getDbNodeByName(hostNames[j])
                if dbNode is None:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                    ("GTM configuration on host [%s]" % masterNode.name) +
                                    " There is no host named %s." % hostNames[j])
                instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_GTM)
                instIndex += 1
                dbNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_GTM,
                                      const.STANDBY_INSTANCE, gtmListenIps[instIndex],
                                      gtmHaIps[instIndex], gtmInfoList[2 * j], clusterType=self.clusterType)

    def __readGtmConfigForSingle(self, masterNode):
        """
        """
        gtmListenIps = None
        if masterNode.gtmNum > 0:
            gtmListenIps = self.__readInstanceIps(masterNode.name, "gtmListenIp", masterNode.gtmNum * 1)
            self.gtmcount = self.__getGtmsCountFromWhichConfiguredNode(masterNode)
        for i in range(masterNode.gtmNum):
            gtmInfoList = []
            key = "gtmDir%d" % (i + 1)
            gtmInfoList_tmp = self.__readNodeStrValue(masterNode.name, key).split(",")
            for gtmInfo in gtmInfoList_tmp:
                gtmInfoList.append(gtmInfo.strip())
            if len(gtmInfoList) != 2 * 1 - 1:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("GTM configuration on host [%s]" % masterNode.name) +
                                " The information of [%s] is wrong." % key)

            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_GTM)
            mirrorId = self.__assignNewMirrorId()
            instIndex = i * 1
            masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_GTM,
                                      const.MASTER_INSTANCE, gtmListenIps[instIndex], [],
                                      gtmInfoList[0], clusterType=self.clusterType)

    def __readCooConfig(self, dbNode):
        """
        function : Read Coordinator config on node.
        input : []
        output : NA
        """
        cooListenIps = None
        if dbNode.cooNum > 0:
            if dbNode.isComputeOnlyNode():
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                                "Can not contain CN in %s." % dbNode.name)

            cooListenIps = self.__readInstanceIps(dbNode.name, "cooListenIp", 1)

        for i in range(dbNode.cooNum):
            cooDir = []
            key = "cooDir%d" % (i + 1)
            cooDir_tmp = self.__readNodeStrValue(dbNode.name, key).split(",")
            for cnDir in cooDir_tmp:
                cooDir.append(cnDir.strip())
            if len(cooDir) != 1:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] % "CN instance path")
            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_COODINATOR)
            # ssd doesn't supply ,so set ssddir value to empty
            dbNode.appendInstance(instId, const.MIRROR_ID_COO,
                                  const.INSTANCE_ROLE_COODINATOR, const.INSTANCE_TYPE_UNDEFINED,
                                  cooListenIps[i], None, cooDir[0], "", clusterType=self.clusterType)
            i += 1

    def __getDataNodeCount(self, masterNode):
        """
        function : get the count of data nodes
        input : masterNode
        output : dataNodeCount
        """
        dataNodeList = self.__readNodeStrValue(masterNode.name, "dataNode1", True, "").split(",")
        dnListLen = len(dataNodeList)
        dataNodeCount = (dnListLen + 1) // 2
        return dataNodeCount

    def __readDataNodeConfig(self, masterNode):
        """
        function : Read datanode config on node.
        input : []
        output : NA
        """
        if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
            self.__readDataNodeConfigForMasterStandby(masterNode)
        elif self.isSingleCluster():
            self.__readDataNodeConfigForSingle(masterNode)
        elif (self.isSinglePrimaryMultiStandbyCluster() or
              self.isSingleInstCluster()):
            self.__readDataNodeConfigForMutilAZ(masterNode)

    def __CheckDataNodeConfigForMasterStandby(self, masterNode, dnInfoLists, ssdInfoList, dnListenIps, dnHaIps):
        """
        function: check datanode config for master tandby cluster
                  before read datanode config.
        input : masterNode, dnInfoLists,
               ssdInfoList, dnListenIps, dnHaIps
        output: NA
        """
        totalDnInstanceNum = 0
        mirror_count = 1 if masterNode.isComputeOnlyNode() else const.MIRROR_COUNT_DATA
        for i in range(masterNode.dataNum):
            dnInfoList = []
            key = "dataNode%d" % (i + 1)
            dnInfoList_tmp = self.__readNodeStrValue(masterNode.name, key).split(",")
            for dnInfo in dnInfoList_tmp:
                dnInfoList.append(dnInfo.strip())
            dnInfoListLen = len(dnInfoList)
            if dnInfoListLen != 2 * mirror_count - 1:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("DN configuration on host [%s]" % masterNode.name) +
                                " The information of [%s] is wrong." % key)
            totalDnInstanceNum += (dnInfoListLen + 1) // 2
            dnInfoLists[i].extend(dnInfoList)

            # ssd doesn't supply ,so set ssddir value to empty
            ssddirList = []
            ssdInfoList[i].extend(ssddirList)

        # check ip num
        if dnListenIps is not None and len(dnListenIps[0]) != 0:
            colNum = len(dnListenIps[0])
            rowNum = len(dnListenIps)
            for col in range(colNum):
                ipNum = 0
                for row in range(rowNum):
                    if dnListenIps[row][col] != "":
                        ipNum += 1
                    else:
                        break
                if ipNum != totalDnInstanceNum:
                    raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                    ("IP number of dataListenIp", "instance number"))

        if dnHaIps is not None and len(dnHaIps[0]) != 0:
            colNum = len(dnHaIps[0])
            rowNum = len(dnHaIps)
            for col in range(colNum):
                ipNum = 0
                for row in range(rowNum):
                    if dnHaIps[row][col] != "":
                        ipNum += 1
                    else:
                        break
                if ipNum != totalDnInstanceNum:
                    raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                    ("IP number of dataHaIps", "instance number"))

    def __readDataNodeConfigForMasterStandby(self, masterNode):
        """
        """
        dnListenIps = dnHaIps = None
        if masterNode.dataNum > 0:
            ip_count = masterNode.dataNum if masterNode.isComputeOnlyNode() \
                else masterNode.dataNum * const.MIRROR_COUNT_DATA
            dnListenIps = self.__readInstanceIps(masterNode.name, "dataListenIp",
                                                 ip_count, True)
            dnHaIps = self.__readInstanceIps(masterNode.name, "dataHaIp",
                                             ip_count, True)

        dnInfoLists = [[] for _ in range(masterNode.dataNum)]
        ssdInfoList = [[] for _ in range(masterNode.dataNum)]
        self.__CheckDataNodeConfigForMasterStandby(masterNode, dnInfoLists, ssdInfoList, dnListenIps, dnHaIps)

        instIndex = 0
        for i in range(masterNode.dataNum):
            dnInfoList = dnInfoLists[i]
            mirrorId = const.MIRROR_ID_VW_DN if masterNode.isComputeOnlyNode() \
                else self.__assignNewMirrorId()
            ssddirList = None
            if len(ssdInfoList[i]) > 1:
                ssddirList = ssdInfoList[i]
            # master datanode
            inst_role = const.INSTANCE_ROLE_VW_DN if masterNode.isComputeOnlyNode() \
                else const.INSTANCE_ROLE_DATANODE
            instId = self.__assignNewInstanceId(inst_role)
            # ssd doesn't supply ,this branch will not arrive when len(ssdInfoList[i])  is 0
            if len(ssdInfoList[i]) > 1:
                masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_DATANODE, const.MASTER_INSTANCE,
                                          dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[0], ssddirList[0],
                                          clusterType=self.clusterType)
            else:
                masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_DATANODE, const.MASTER_INSTANCE,
                                          dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[0],
                                          clusterType=self.clusterType)

            instIndex += 1

            if masterNode.isComputeOnlyNode():
                continue

            # standby datanode
            dbNode = self.getDbNodeByName(dnInfoList[1])
            if dbNode is None:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("DN configuration on host [%s]" % masterNode.name) +
                                " There is no host named %s." % dnInfoList[1])
            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_DATANODE)
            # ssd doesn't supply ,this branch will not arrive when len(ssdInfoList[i])  is 0
            if len(ssdInfoList[i]) > 1:
                dbNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_DATANODE, const.STANDBY_INSTANCE,
                                      dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[2], ssddirList[1],
                                      clusterType=self.clusterType)
            else:
                dbNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_DATANODE, const.STANDBY_INSTANCE,
                                      dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[2],
                                      clusterType=self.clusterType)
            instIndex += 1

            # dummy standby datanode
            dbNode = self.getDbNodeByName(dnInfoList[3])
            if dbNode is None:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("DN configuration on host [%s]" % masterNode.name) +
                                " There is no host named %s." % dnInfoList[3])
            instId = self.__assignNewDummyInstanceId()
            dbNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_DATANODE, const.DUMMY_STANDBY_INSTANCE,
                                  dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[4],
                                  clusterType=self.clusterType)
            instIndex += 1

    def __readDataNodeConfigForSingle(self, masterNode):
        """
        """
        dnListenIps = None
        if masterNode.dataNum > 0:
            dnListenIps = self.__readInstanceIps(masterNode.name, "dataListenIp", masterNode.dataNum * 1, True)

        dnInfoLists = [[] for _ in range(masterNode.dataNum)]
        ssdInfoList = [[] for _ in range(masterNode.dataNum)]
        totalDnInstanceNum = 0
        for i in range(masterNode.dataNum):
            dnInfoList = []
            key = "dataNode%d" % (i + 1)
            dnInfoList_tmp = self.__readNodeStrValue(masterNode.name, key).split(",")
            for dnInfo in dnInfoList_tmp:
                dnInfoList.append(dnInfo.strip())
            dnInfoListLen = len(dnInfoList)
            if dnInfoListLen != 2 * 1 - 1:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("DN configuration on host [%s]" % masterNode.name) +
                                " The information of [%s] is wrong." % key)
            totalDnInstanceNum += (dnInfoListLen + 1) // 2
            dnInfoLists[i].extend(dnInfoList)

            # ssd doesn't supply ,so set ssddir value to empty
            ssddirList = []
            ssdInfoList[i].extend(ssddirList)

        # check ip num
        if dnListenIps is not None and len(dnListenIps[0]) != 0:
            colNum = len(dnListenIps[0])
            rowNum = len(dnListenIps)
            for col in range(colNum):
                ipNum = 0
                for row in range(rowNum):
                    if dnListenIps[row][col] != "":
                        ipNum += 1
                    else:
                        break
                if ipNum != totalDnInstanceNum:
                    raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                    ("IP number of dataListenIp", "instance number"))

        instIndex = 0
        for i in range(masterNode.dataNum):
            dnInfoList = dnInfoLists[i]
            mirrorId = self.__assignNewMirrorId()
            ssddirList = None
            if len(ssdInfoList[i]) > 1:
                ssddirList = ssdInfoList[i]
            # master datanode
            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_DATANODE)
            # ssd doesn't supply ,this branch will not arrive when len(ssdInfoList[i])  is 0
            if len(ssdInfoList[i]) > 1:
                masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_DATANODE, const.MASTER_INSTANCE,
                                          dnListenIps[instIndex], [], dnInfoList[0], ssddirList[0],
                                          clusterType=self.clusterType)
            else:
                masterNode.appendInstance(instId, mirrorId, const.INSTANCE_ROLE_DATANODE, const.MASTER_INSTANCE,
                                          dnListenIps[instIndex], [], dnInfoList[0], clusterType=self.clusterType)

            instIndex += 1

    def __checkDataNodeConfigForMutilAZ(
            self, masterNode, dnInfoLists, ssdInfoList,
            mirror_count_data, dnListenIps, dnHaIps):
        """
        function: check datanode config for master tandby cluster
                  before read datanode config.
        input : masterNode, dnInfoLists,ssdInfoList,
               mirror_count_data, dnListenIps, dnHaIps
        output:NA
        """
        totalDnInstanceNum = 0
        for i in range(masterNode.dataNum):
            dnInfoList = []
            key = "dataNode%d" % (i + 1)
            dnInfoList_tmp = self.__readNodeStrValue(
                masterNode.name, key).split(",")
            for dnInfo in dnInfoList_tmp:
                dnInfoList.append(dnInfo.strip())
            dnInfoListLen = len(dnInfoList)
            if dnInfoListLen != 2 * mirror_count_data - 1:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("DN configuration on host [%s]" % masterNode.name) +
                                " The information of [%s] is wrong." % key)
            totalDnInstanceNum += (dnInfoListLen + 1) // 2
            dnInfoLists[i].extend(dnInfoList)

            # ssd doesn't supply ,so set ssddir value to empty
            ssddirList = []
            ssdInfoList[i].extend(ssddirList)

        # check ip num
        if dnListenIps is not None and len(dnListenIps[0]) != 0:
            colNum = len(dnListenIps[0])
            rowNum = len(dnListenIps)
            for col in range(colNum):
                ipNum = 0
                for row in range(rowNum):
                    if dnListenIps[row][col] != "":
                        ipNum += 1
                    else:
                        break
                if ipNum != totalDnInstanceNum:
                    raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                    ("IP number of dataListenIp", "instance number"))

        if dnHaIps is not None and len(dnHaIps[0]) != 0:
            colNum = len(dnHaIps[0])
            rowNum = len(dnHaIps)
            for col in range(colNum):
                ipNum = 0
                for row in range(rowNum):
                    if dnHaIps[row][col] != "":
                        ipNum += 1
                    else:
                        break
                if ipNum != totalDnInstanceNum:
                    raise Exception(ErrorCode.GAUSS_516["GAUSS_51637"] %
                                    ("IP number of dataHaIps", "instance number"))

    def __readDataNodeConfigForMutilAZ(self, masterNode):
        """
        """
        dnListenIps = None
        dnHaIps = None
        mirror_count_data = self.__getDataNodeCount(masterNode)
        if masterNode.dataNum > 0:
            dnListenIps = self.__readInstanceIps(
                masterNode.name, "dataListenIp",
                masterNode.dataNum * mirror_count_data, True)
            dnHaIps = self.__readInstanceIps(
                masterNode.name, "dataHaIp",
                masterNode.dataNum * mirror_count_data, True)

        dnInfoLists = [[] for _ in range(masterNode.dataNum)]
        ssdInfoList = [[] for _ in range(masterNode.dataNum)]

        self.__checkDataNodeConfigForMutilAZ(
            masterNode, dnInfoLists, ssdInfoList,
            mirror_count_data, dnListenIps, dnHaIps)

        instIndex = 0
        for i in range(masterNode.dataNum):
            dnInfoList = dnInfoLists[i]
            groupId = self.__assignNewGroupId()
            ssddirList = None
            if len(ssdInfoList[i]) > 1:
                ssddirList = ssdInfoList[i]
            # master datanode
            instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_DATANODE)
            # ssd doesn't supply ,this branch will not arrive when len(ssdInfoList[i])  is 0
            if len(ssdInfoList[i]) > 1:
                masterNode.appendInstance(instId, groupId, const.INSTANCE_ROLE_DATANODE, const.MASTER_INSTANCE,
                                          dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[0], ssddirList[0],
                                          clusterType=self.clusterType)
            else:
                masterNode.appendInstance(instId, groupId, const.INSTANCE_ROLE_DATANODE, const.MASTER_INSTANCE,
                                          dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[0],
                                          clusterType=self.clusterType)

            instIndex += 1

            for nodeLen in range((len(dnInfoList) + 1) // 2 - 1):
                dbNode = self.getDbNodeByName(dnInfoList[nodeLen * 2 + 1])
                if dbNode is None:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] % (
                            "DN configuration on host [%s]" % str(masterNode.name)) +
                                    " There is no host named %s." % dnInfoList[nodeLen * 2 + 1])
                instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_DATANODE)

                # ssd doesn't supply ,this branch will not arrive when len(ssdInfoList[i])  is 0
                if len(ssdInfoList[i]) > 1:
                    dbNode.appendInstance(instId, groupId, const.INSTANCE_ROLE_DATANODE, const.STANDBY_INSTANCE,
                                          dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[nodeLen * 2 + 2],
                                          ssddirList[nodeLen * 2 + 1], clusterType=self.clusterType)
                else:
                    dbNode.appendInstance(instId, groupId, const.INSTANCE_ROLE_DATANODE, const.STANDBY_INSTANCE,
                                          dnListenIps[instIndex], dnHaIps[instIndex], dnInfoList[nodeLen * 2 + 2],
                                          clusterType=self.clusterType)
                instIndex += 1

    def __readCmaConfig(self, dbNode):
        """
        function : Read cm agent config on node.
        input : []
        output : NA
        """
        agentIps = self.__readInstanceIps(dbNode.name, "cmAgentConnectIp", 1)
        instId = self.__assignNewInstanceId(const.INSTANCE_ROLE_CMAGENT)
        dbNode.appendInstance(instId, const.MIRROR_ID_AGENT, const.INSTANCE_ROLE_CMAGENT,
                              const.INSTANCE_TYPE_UNDEFINED, agentIps[0],
                              None, "", clusterType=self.clusterType)

    def __assignNewInstanceId(self, instRole):
        """
        function : Assign a new id for instance.
        input : String
        output : NA
        """
        newId = self.__newInstanceId[instRole]
        if const.INSTANCE_ROLE_DATANODE == instRole:
            if newId == const.OLD_LAST_PRIMARYSTANDBY_BASEID_NUM:
                self.__newInstanceId[instRole] = self.__newInstanceId[instRole] + 1 + \
                                                 (const.NEW_FIRST_PRIMARYSTANDBY_BASEID_NUM -
                                                  const.OLD_LAST_PRIMARYSTANDBY_BASEID_NUM)
            else:
                self.__newInstanceId[instRole] += 1
        else:
            self.__newInstanceId[instRole] += 1
        return newId

    def __assignNewNodeId(self, nodeType):
        """
        function : Assign a new id for node.
        input : String
        output : NA
        """
        newId = self.__newNodeId[nodeType]
        self.__newNodeId[nodeType] += 1
        return newId

    def __assignNewDummyInstanceId(self):
        """
        function : Assign a new dummy standby instance id.
        input : NA
        output : NA
        """
        if self.__newDummyStandbyId == const.OLD_LAST_DUMMYNODE_BASEID_NUM:
            self.__newDummyStandbyId = self.__newDummyStandbyId + 1 + \
                                       (const.NEW_FIRST_DUMMYNODE_BASEID_NUM -
                                        const.OLD_LAST_DUMMYNODE_BASEID_NUM)
        else:
            self.__newDummyStandbyId += 1
        return self.__newDummyStandbyId

    def __assignNewMirrorId(self):
        """
        function : Assign a new mirror id.
        input : NA
        output : NA
        """
        self.__newMirrorId += 1

        return self.__newMirrorId

    def __assignNewGroupId(self):
        """"""
        self.__newGroupId += 1
        return self.__newGroupId

    def __readNodeIps(self, nodeName, prefix):
        """
        function : Read ip for node, such as backIp1, sshIp1 etc..
        input : String,String
        output : NA
        """
        ipList = []
        n = 1

        if prefix == "cooListenIp":
            n = const.NEW_MAX_IP_NUM
        elif prefix == "etcdListenIp":
            n = 2
        elif prefix == "dataListenIp" and self.is_htap_cluster:
            n = const.NEW_MAX_IP_NUM

        for i in range(1, const.CONFIG_IP_NUM + n):
            key = "%s%d" % (prefix, i)
            value = self.__readNodeStrValue(nodeName, key, True, "")
            value = value.strip()
            if value == "":
                break
            value = g_network.formatIP(value)
            ipList.append(value)

        return ipList

    def __readVirtualIp(self, nodeName, prefix):
        """
        function : Read  virtual ip only for node.
        input : String,String
        output : NA
        """
        ipList = []
        value = self.__readNodeStrValue(nodeName, prefix, True, "")
        if value != "":
            for ip in value.split(","):
                ip = g_network.formatIP(ip.strip())
                if ip not in ipList:
                    ipList.append(ip)
        return ipList

    def __isIpValid(self, ip):
        """
        function : check if the input ip address is valid
        input : String
        output : boolean
        """
        return g_network.isIpValid(ip)

    def __isPortValid(self, port):
        """
        function :Judge if the port is valid
        input : int
        output : boolean
        """
        if port < 0 or port > 65535:
            return False
        elif 0 <= port <= 1023:
            return False
        else:
            return True

    def __readInstanceIps(self, nodeName, prefix, InstCount, isDataNode=False):
        """
        function :Read instance ips
        input : String,String,int
        output : NA
        """
        multiIpList = self.__readNodeIps(nodeName, prefix)

        mutilIpCount = len(multiIpList)
        if mutilIpCount == 0:
            return [[] for _ in range(InstCount)]

        instanceIpList = [["" for _ in range(mutilIpCount)] for _ in range(InstCount)]
        for i in range(mutilIpCount):
            ipList = []
            ipList_tmp = multiIpList[i].split(",")
            for ip in ipList_tmp:
                ipList.append(g_network.formatIP(ip.strip()))
            ipNum = len(ipList)
            if ipNum != InstCount:
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                                ("[%s] of node [%s]" % (prefix, nodeName)) + " The count of IP is wrong.")
            for j in range(ipNum):
                instanceIpList[j][i] = ipList[j]

        return instanceIpList

    def __readNodeIntValue(self, nodeName, key, nullable=False, defValue=0):
        """
        function :Read integer value of specified node
        input : String,int
        output : NA
        """
        value = defValue

        strValue = self.__readNodeStrValue(nodeName, key, nullable, "")
        if strValue != "":
            value = int(strValue)

        return value

    def __readNodeStrValue(self, nodeName, key, nullable=False, defValue=""):
        """
        function : Read string of specified node
        input : String,int
        output : defValue
        """
        (retStatus, retValue) = parse_xml.readOneClusterConfigItem(xmlRootNode, key, "node", nodeName)
        if retStatus == 0:
            return str(retValue).strip()
        elif retStatus == 2 and nullable:
            return defValue
        else:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] %
                            ("[%s] of node [%s]" % (key, nodeName)) +
                            " Return status: %d. value: %s. Check whether the dataNum is correct first." %
                            (retStatus, retValue))

    def __checkVirtualIp(self, clusterVirtualIp, dbNode):
        """
        function : Check virtual ip
        input : String,int
        output : NA
        """
        allIps = dbNode.virtualIp[:]
        allIps.extend(dbNode.backIps)
        tempIps = []
        for ip in allIps:
            if not self.__isIpValid(ip):
                raise Exception(ErrorCode.GAUSS_506["GAUSS_50603"] +
                                "The IP address is: %s" % ip + " Please check it.")
            if ip in tempIps:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51220"] %
                                ip + " Virtual IP(s) cannot be same as back IP(s).")
            tempIps.append(ip)

        for ip in allIps:
            if ip in clusterVirtualIp:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51224"] % ip)
        clusterVirtualIp.extend(allIps)

        for dnInstance in dbNode.datanodes:
            for dnIp in dnInstance.listenIps:
                if (dnIp not in allIps):
                    raise Exception(ErrorCode.GAUSS_512["GAUSS_51229"] %
                                    (dnIp, dbNode.name) + "Please check it.")

    def checkDbNodes(self):
        """
        """
        if len(self.dbNodes) > const.MIRROR_COUNT_NODE_MAX:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("nodes", "be less than or equal to %s" % const.MIRROR_COUNT_NODE_MAX) + " Please set it.")

    def checkCmsNumForMasterStandby(self, cmsNum):
        """
        """
        if cmsNum != 2:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("CMServer", "be equal to 2") + " Please set it.")

    def checkGtmNumForMasterStandby(self, gtmNum):
        """
        """
        if gtmNum != 2:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("GTM", "be equal to 2") + " Please set it.")

    def checkCooNumForMasterStandby(self, cooNum):
        """
        """
        if cooNum <= 0 or cooNum > const.MIRROR_COUNT_CN_MAX:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("CN", "be greater than 0 and less than or equal to %s" %
                             const.MIRROR_COUNT_CN_MAX) + " Please set it.")

    def checkDataNumForMasterStandby(self, dataNum):
        """
        """
        if dataNum <= 0 or dataNum > const.MIRROR_COUNT_DN_MAX:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("DN", "be greater than 0 and less than or equal to %s" %
                             const.MIRROR_COUNT_DN_MAX) + " Please set it.")

    def checkEtcdNumForMasterStandby(self, etcdNum):
        """
        """
        if etcdNum > 0:
            if etcdNum < const.MIRROR_COUNT_ETCD_MIN or etcdNum > const.MIRROR_COUNT_ETCD_MAX:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                                ("ETCD", "be greater than 2 and less than 8") + " Please set it.")

    ######################################################
    def checkCmsNumForSinglePrimaryMultiStandby(self, cmsNum):
        """
        """
        if not self.isHtapCluster():
            if cmsNum < const.MIRROR_COUNT_REPLICATION_MIN or cmsNum > const.MIRROR_COUNT_REPLICATION_MAX:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                                ("CMServer", "be greater than 1 and less than 9") + " Please set it.")
        else:
            self.checkCmsNumForSingle(cmsNum)

    def checkGtmNumForSinglePrimaryMultiStandby(self, gtmNum):
        """
        """
        if gtmNum < const.MIRROR_COUNT_REPLICATION_MIN or gtmNum > const.MIRROR_COUNT_REPLICATION_MAX:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("GTM", "be greater than 1 and less than 9") + " Please set it.")

    def checkEtcdNumForSinglePrimaryMultiStandby(self, etcdNum):
        """
        """
        if etcdNum < const.MIRROR_COUNT_ETCD_MIN or etcdNum > const.MIRROR_COUNT_ETCD_MAX:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("ETCD", "be greater than 2 and less than 8") + " Please set it.")

    def checkCmsNumForSingle(self, cmsNum):
        """
        """
        if cmsNum != 1:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] % ("CMServer", "be equal to 1") + " Please set it.")

    def checkGtmNumForSingle(self, gtmNum):
        """
        """
        if gtmNum != 1:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] % ("GTM", "be equal to 1") + " Please set it.")

    def checkCooNumForSingle(self, cooNum):
        """
        """
        if cooNum != 1:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] % ("CN", "be equal to 1") + " Please set it.")

    def checkDataNumForSingle(self, dataNum):
        """
        """
        if dataNum < 2 or dataNum > 6:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("DN", "be greater than 1 and less than 7") + " Please set it.")

    def checkEtcdNumForSingle(self, etcdNum):
        """
        """
        if etcdNum != 0:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] % ("ETCD", "be equal to 0") + " Please set it.")

    ######################################################
    def checkGtmNumForSingleInst(self, gtmNum):
        if gtmNum != 0:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("GTM", "be equal to 0") + " Please set it.")

    def checkCooNumForSingleInst(self, cooNum):
        if cooNum != 0:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("CN", "be equal to 0") + " Please set it.")

    def checkDataNumForSingleInst(self, dataNum):
        if not self.isHtapCluster():
            if dataNum <= 0 or dataNum > const.MIRROR_COUNT_DN_MAX:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                                ("DN", "be greater than 0 and less than or equal to %s" %
                                 const.MIRROR_COUNT_DN_MAX) + " Please set it.")

            for dbNode in self.dbNodes:
                if len(dbNode.datanodes) > 1:
                    raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                                    ("DN", "be less than or equal to 1 on each node"))
        else:
            if dataNum != 1:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                                ("DN", "be equal to 1") + " Please set it.")

    def checkEtcdNumForSingleInst(self, etcdNum):
        """
        """
        if not self.isHtapCluster():
            if etcdNum != 0 and (etcdNum < const.MIRROR_COUNT_ETCD_MIN or etcdNum > const.MIRROR_COUNT_ETCD_MAX):
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                                ("ETCD", "be greater than 2 and less than 8") + " Please set it.")
        else:
            self.checkEtcdNumForSingle(etcdNum)

    ######################################################
    def checkNewNodes(self):
        """
        """
        if len(self.dbNodes) - len(self.newNodes) <= 1:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51231"] + " Please check the cluster configuration file.")
        for dbNode in self.newNodes:
            if len(dbNode.cmservers) > 0 \
                    or len(dbNode.gtms) > 0 \
                    or len(dbNode.etcds) > 0 \
                    or len(dbNode.coordinators) > 0:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51215"] % dbNode.name +
                                " Please check the cluster configuration file.")
            if len(dbNode.datanodes) == 0:
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51216"] % dbNode.name +
                                " Please check the cluster configuration file.")

        vw_node_info = [dbNode.isComputeOnlyNode() for dbNode in self.newNodes]
        if vw_node_info and len(Counter(vw_node_info)) != 1:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                            "Can not expand vw node and non vw node at the same time.")

    def __checkClusterConfig(self):
        """
        function : Check the count of instance
        input : NA
        output : NA
        """
        if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
            self.__checkClusterConfigForMasterStandby()
        elif self.isSinglePrimaryMultiStandbyCluster():
            self.__checkClusterConfigForSinglePrimaryMultiStandby()
        elif self.isSingleCluster():
            self.__checkClusterConfigForSingle()
        elif self.isSingleInstCluster():
            self.__checkClusterConfigForSingleInst()

    def __checkClusterConfigForMasterStandby(self):
        """
        """
        cmsNum = 0
        gtmNum = 0
        cooNum = 0
        dataNum = 0
        etcdNum = 0

        # init instance number
        for dbNode in self.dbNodes:
            cmsNum += len(dbNode.cmservers)
            gtmNum += len(dbNode.gtms)
            cooNum += len(dbNode.coordinators)
            dataNum += dbNode.dataNum
            etcdNum += len(dbNode.etcds)

        # check dbNodes
        self.checkDbNodes()
        # check cmsNum
        self.checkCmsNumForMasterStandby(cmsNum)
        # check gtmNum
        self.checkGtmNumForMasterStandby(gtmNum)
        # check cooNum
        self.checkCooNumForMasterStandby(cooNum)
        # check dataNum
        self.checkDataNumForMasterStandby(dataNum)
        # check etcdNum
        self.checkEtcdNumForMasterStandby(etcdNum)
        # check CM path
        self.checkCmPath()

        for dbNode in self.dbNodes:
            dbInstList = []
            portList = []
            # get instance
            dbInstList.extend(dbNode.cmservers)
            dbInstList.extend(dbNode.coordinators)
            dbInstList.extend(dbNode.datanodes)
            dbInstList.extend(dbNode.gtms)
            dbInstList.extend(dbNode.etcds)

            for dbInst in dbInstList:
                portList.append(dbInst.port)
                portList.append(dbInst.haPort)
                if self.checkSctpPort and dbInst.instanceRole == const.INSTANCE_ROLE_DATANODE:
                    portList.append(dbInst.sctpPort)
                    portList.append(dbInst.controlPort)
                    portList.append(dbInst.servicePort)
            for port in portList:
                if (portList.count(port) > 1):
                    raise Exception(
                        ErrorCode.GAUSS_512["GAUSS_51213"] % port + " Please check it.")

        # check new nodes
        self.checkNewNodes()

    def __checkAZForSingleInst(self):
        """
        function : check az names and dn replication
        input : NA
        output : NA
        """
        # AZ list
        FirstAZ = [const.azName1]

        # Get dn standys num
        # The number of standbys for each DN instance must be the same
        peerNum = 0
        for dbNode in self.dbNodes:
            for inst in dbNode.datanodes:
                if inst.instanceType == const.MASTER_INSTANCE:
                    peerInsts = self.getPeerInstance(inst)
                    if peerNum == 0:
                        peerNum = len(peerInsts)
                    elif peerNum != len(peerInsts):
                        raise Exception(ErrorCode.GAUSS_532["GAUSS_53200"])

        if peerNum < 1 or peerNum > 3:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("DN standbys", "be greater than 0 and less than 4") + " Please set it.")

        # Get AZ names in cluster
        azNames = self.getazNames()
        azNames.sort()
        if azNames not in [FirstAZ]:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"] + " Only single AZ is supported now.")

        # Check az names and dn replication
        # When the number of standbys is less than 3, the AZ name must be "AZ1"
        # When the number of standbys is equal 3, the AZ name must be "AZ1" or "AZ1,AZ2"(in the future)

        # no ETCD when the peerNum == 1.
        # 3 ETCDs when the peerNum >= 2.
        az1_etcd = 0
        for dbNode in self.dbNodes:
            if dbNode.azName == const.azName1:
                az1_etcd += len(dbNode.etcds)

        if peerNum == 1 and az1_etcd != 0:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53203"] % "one-primary-one-standby must be 0")

        if peerNum > 1 and az1_etcd != 3:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53203"] % "one-primary-multi-standby must be 3")

        # the standby instance number of cmserver and datanode must be same.
        cms_num = 0
        for dbNode in self.dbNodes:
            cms_num += len(dbNode.cmservers)

        if peerNum == 1 and cms_num != 2:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51236"] +
                            "the standby instance number of cmserver and datanode must be same.")

    def __checkAZInfoForMasterStandbyMultiAZ(self):
        az_info = self.get_az_info()
        # must 3 AZ
        if len(az_info) != 3:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"] +
                            " Only 3 AZ is supported in %s, XML AZ Names:%s." %
                            (const.CLUSTER_TYPE_MASTER_STANDBY_MULTI_AZ, az_info))

        # each az must has same priority.
        az_with_multi_priority = [az for az in az_info if len(az_info[az]) != 1]
        if az_with_multi_priority:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"] +
                            " AZ (%s) has multi priority (%s)." %
                            (az_with_multi_priority, [az_info[az] for az in az_with_multi_priority]))

        cms_list, etcd_list, gtm_list, cn_list, dn_list, = [], [], [], [], []
        for dbNode in self.dbNodes:
            cms_list.extend(dbNode.cmservers)
            etcd_list.extend(dbNode.etcds)
            gtm_list.extend(dbNode.gtms)
            cn_list.extend(dbNode.coordinators)
            for inst in dbNode.datanodes:
                if inst.instanceType == const.MASTER_INSTANCE:
                    peer_inst = self.getPeerInstance(inst)
                    dn_list.extend([[inst] + peer_inst])

        # ETCD is unsupported
        if etcd_list:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"] +
                            " ETCD is unsupported in %s." % const.CLUSTER_TYPE_MASTER_STANDBY_MULTI_AZ)

        # cms must be in different AZ
        if not (len(cms_list) == 2 and cms_list[0].azName != cms_list[1].azName):
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"] +
                            " cm_server must be setup master/standby and in different AZ.")
        # gtm must be in different AZ
        if not (len(gtm_list) == 2 and gtm_list[0].azName != gtm_list[1].azName):
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"] +
                            " GTM must be setup master/standby and in different AZ.")
        # dn must be in different AZ
        dn_with_same_az = [dn[0] for dn in dn_list if
                           not (len(dn) == 3 and len(Counter([i.azName for i in dn])) == 3)]
        if dn_with_same_az:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"] +
                            " DN (%s) must be setup master/standby/dummy and in different AZ." %
                            [(dn.hostname, dn.datadir) for dn in dn_with_same_az])

    def __checkAZNamesWithDNReplication(self):
        """
        function : check az names and dn replication
        input : NA
        output : NA
        """
        # AZ list
        FirstAZ = [const.azName1]
        SecondAZ = [const.azName1, const.azName2]
        ThirdAZ = [const.azName1, const.azName2, const.azName3]
        # Get dn standbys num
        peerNum = 0
        for dbNode in self.dbNodes:
            for inst in dbNode.datanodes:
                if inst.instanceType == const.MASTER_INSTANCE:
                    peerInsts = self.getPeerInstance(inst)
                    # The number of standbys for each DN instance must be the same
                    if peerNum == 0:
                        peerNum = len(peerInsts)
                    elif peerNum != len(peerInsts):
                        raise Exception(ErrorCode.GAUSS_532["GAUSS_53200"])

        # Get AZ names in cluster
        azNames = self.getazNames()
        azNames.sort()
        if azNames not in [FirstAZ, SecondAZ, ThirdAZ]:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53202"])
        if peerNum < 1 or peerNum > 7:
            raise Exception(ErrorCode.GAUSS_512["GAUSS_51230"] %
                            ("DN standbys", "be greater than 1 and less than 8") + " Please set it.")
        # Check az names and dn replication
        # When the number of standbys is less than 3, the AZ name must be "AZ1"
        # When the number of standbys is equal 3, the AZ name must be "AZ1,AZ2" or "AZ1,AZ2,AZ3"
        # When the number of standbys is equal 4, the AZ name must be "AZ1,AZ2,AZ3"
        # When the number of standbys is greater than 1 and less than 8, the AZ name must be "AZ1,AZ2,AZ3"
        if azNames != FirstAZ and peerNum <= 2:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53201"])
        elif azNames == FirstAZ and peerNum == 3:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53201"])
        elif azNames != ThirdAZ and peerNum == 4:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53201"])
        elif azNames != ThirdAZ and 7 >= peerNum > 4:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53201"])

        # Check AZ replication
        self.__checkAzInfoForSinglePrimaryMultiStandby()

    def __checkAzInfoForSinglePrimaryMultiStandby(self):
        """
        1.Check if AZ info with etcd number is set correctly.
        2. Check if the azPriority value is set correctly.
        return: NA
        """
        az1_etcd = 0
        az2_etcd = 0
        az3_etcd = 0
        az2Priority_max = 0
        az1PriorityLst = []
        az2PriorityLst = []
        az3PriorityLst = []
        syncAz = False
        thirdPartAZ = False

        for dbNode in self.dbNodes:
            if dbNode.azName == const.azName1:
                az1_etcd += len(dbNode.etcds)
                az1PriorityLst.append(dbNode.azPriority)
            if dbNode.azName == const.azName2:
                syncAz = True
                az2_etcd += len(dbNode.etcds)
                az2PriorityLst.append(dbNode.azPriority)

            if dbNode.azName == const.azName3:
                thirdPartAZ = True
                az3_etcd += len(dbNode.etcds)
                az3PriorityLst.append(dbNode.azPriority)

        # In a primary multi-standby cluster, AZ1 has a higher priority than AZ2 and AZ2 has a higher priority than AZ3.
        az1Priority_max = max(az1PriorityLst)

        # Each AZ requires at least one or more ETCDs.
        if az1_etcd != 3 and not syncAz and not thirdPartAZ:
            raise Exception(ErrorCode.GAUSS_532["GAUSS_53203"] % "AZ1 must be 3")
        if syncAz:
            if az1_etcd < 2 or az2_etcd < 1:
                raise Exception(ErrorCode.GAUSS_532["GAUSS_53203"] %
                                "AZ1 must be greater than 1 and the number of ETCD in AZ2 must be greater than 0")
            # check az2 priority
            az2Priority_max = max(az2PriorityLst)
            az2Priority_min = min(az2PriorityLst)
            if az1Priority_max >= az2Priority_min:
                raise Exception(ErrorCode.GAUSS_532["GAUSS_53205"] % ("AZ1", "AZ2"))
        if thirdPartAZ:
            if az1_etcd < 2 or az2_etcd < 2 or az3_etcd < 1:
                raise Exception(ErrorCode.GAUSS_532["GAUSS_53203"] %
                                "AZ1 and AZ2 must be greater than 1 "
                                "and the number of ETCD in AZ3 must be greater than 0")
            # check az3 priority
            az3Priority_min = min(az3PriorityLst)
            if az2Priority_max >= az3Priority_min:
                raise Exception(ErrorCode.GAUSS_532["GAUSS_53205"] % ("AZ2", "AZ3"))

    def __checkClusterConfigForSinglePrimaryMultiStandby(self):
        """
        function : check cluster config XML file
        input : NA
        output : NA
        """
        cmsNum = 0
        gtmNum = 0
        cooNum = 0
        dataNum = 0
        etcdNum = 0

        # init instance number
        for dbNode in self.dbNodes:
            cmsNum += len(dbNode.cmservers)
            gtmNum += len(dbNode.gtms)
            cooNum += len(dbNode.coordinators)
            dataNum += dbNode.dataNum
            etcdNum += len(dbNode.etcds)

        # check dbNodes
        self.checkDbNodes()
        # check cmsNum
        self.checkCmsNumForSinglePrimaryMultiStandby(cmsNum)
        # check gtmNum
        self.checkGtmNumForSinglePrimaryMultiStandby(gtmNum)
        # check cooNum
        self.checkCooNumForMasterStandby(cooNum)
        # check dataNum
        self.checkDataNumForMasterStandby(dataNum)
        # check etcdNum
        self.checkEtcdNumForSinglePrimaryMultiStandby(etcdNum)
        # check CM path
        self.checkCmPath()

        for dbNode in self.dbNodes:
            dbInstList = []
            portList = []
            # get instance
            dbInstList.extend(dbNode.cmservers)
            dbInstList.extend(dbNode.coordinators)
            dbInstList.extend(dbNode.datanodes)
            dbInstList.extend(dbNode.gtms)
            dbInstList.extend(dbNode.etcds)

            for dbInst in dbInstList:
                portList.append(dbInst.port)
                # check CN port
                if dbInst.instanceRole == const.INSTANCE_ROLE_COODINATOR:
                    # check comm_sctp_port
                    portList.append(dbInst.sctpPort)
                    # check comm_control_port
                    portList.append(dbInst.controlPort)
                # dn and etcd has ha port
                if dbInst.instanceRole == const.INSTANCE_ROLE_ETCD:
                    portList.append(dbInst.haPort)
                if dbInst.instanceRole == const.INSTANCE_ROLE_DATANODE:
                    portList.append(dbInst.haPort)
                    # check comm_sctp_port
                    portList.append(dbInst.sctpPort)
                    # check comm_control_port
                    portList.append(dbInst.controlPort)
                    # check servicePort
                    portList.append(dbInst.servicePort)
            for port in portList:
                if portList.count(port) > 1:
                    raise Exception(
                        ErrorCode.GAUSS_512["GAUSS_51213"] % port + " Please check it.")
        # check new nodes
        self.checkNewNodes()

    def __checkClusterConfigForSingleInst(self):
        """
        function : check cluster config XML file
        input : NA
        output : NA
        """
        cmsNum = 0
        gtmNum = 0
        cooNum = 0
        dataNum = 0
        etcdNum = 0

        # init instance number
        for dbNode in self.dbNodes:
            cmsNum += len(dbNode.cmservers)
            gtmNum += len(dbNode.gtms)
            cooNum += len(dbNode.coordinators)
            dataNum += dbNode.dataNum
            etcdNum += len(dbNode.etcds)

        # check dbNodes
        self.checkDbNodes()
        # check cmsNum
        self.checkCmsNumForSinglePrimaryMultiStandby(cmsNum)
        # check gtmNum
        self.checkGtmNumForSingleInst(gtmNum)
        # check cooNum
        self.checkCooNumForSingleInst(cooNum)
        # check dataNum
        self.checkDataNumForSingleInst(dataNum)
        # check etcdNum
        self.checkEtcdNumForSingleInst(etcdNum)
        # check CM path
        self.checkCmPath()

        for dbNode in self.dbNodes:
            dbInstList = []
            portList = []
            # get instance
            dbInstList.extend(dbNode.cmservers)
            dbInstList.extend(dbNode.datanodes)
            dbInstList.extend(dbNode.etcds)

            for dbInst in dbInstList:
                portList.append(dbInst.port)
                # dn and etcd has ha port
                if dbInst.instanceRole == const.INSTANCE_ROLE_ETCD:
                    portList.append(dbInst.haPort)
                if dbInst.instanceRole == const.INSTANCE_ROLE_DATANODE:
                    portList.append(dbInst.haPort)
                    # check comm_sctp_port
                    portList.append(dbInst.sctpPort)
                    # check comm_control_port
                    portList.append(dbInst.controlPort)
                    # check servicePort
                    portList.append(dbInst.servicePort)
            for port in portList:
                if portList.count(port) > 1:
                    raise Exception(
                        ErrorCode.GAUSS_512["GAUSS_51213"] % port + " Please check it.")
        # check new nodes
        if not self.isHtapCluster():
            self.checkNewNodes()

    def __checkClusterConfigForSingle(self):
        """
        """
        cmsNum = 0
        gtmNum = 0
        cooNum = 0
        dataNum = 0
        clusterVirtualIp = []
        etcdNum = 0

        # init instance number
        for dbNode in self.dbNodes:
            cmsNum += len(dbNode.cmservers)
            gtmNum += len(dbNode.gtms)
            cooNum += len(dbNode.coordinators)
            dataNum += dbNode.dataNum
            etcdNum += len(dbNode.etcds)

        # check dbNodes
        self.checkDbNodes()
        # check cmsNum
        self.checkCmsNumForSingle(cmsNum)
        # check gtmNum
        self.checkGtmNumForSingle(gtmNum)
        # check cooNum
        self.checkCooNumForSingle(cooNum)
        # check dataNum
        self.checkDataNumForSingle(dataNum)
        # check etcdNum
        self.checkEtcdNumForSingle(etcdNum)
        # check CM path
        self.checkCmPath()

        for dbNode in self.dbNodes:
            if dbNode.virtualIp:
                self.__checkVirtualIp(clusterVirtualIp, dbNode)
            dbInstList = []
            portList = []
            # get instance
            dbInstList.extend(dbNode.cmservers)
            dbInstList.extend(dbNode.coordinators)
            dbInstList.extend(dbNode.datanodes)
            dbInstList.extend(dbNode.gtms)
            dbInstList.extend(dbNode.etcds)

            for dbInst in dbInstList:
                portList.append(dbInst.port)
                portList.append(dbInst.haPort)
                if self.checkSctpPort and dbInst.instanceRole == const.INSTANCE_ROLE_DATANODE:
                    portList.append(dbInst.sctpPort)
                    portList.append(dbInst.controlPort)
                    portList.append(dbInst.servicePort)
            for port in portList:
                if portList.count(port) > 1:
                    raise Exception(
                        ErrorCode.GAUSS_512["GAUSS_51213"] % port + " Please check it.")

    def checkCmPath(self):
        """
        function : check cm_server and cm_agent path is valid or not
        input : NA
        output : NA
        """
        for dbNode in self.dbNodes:
            # cm_server
            for cmsInst in dbNode.cmservers:
                parse_xml.checkPathVaild(cmsInst.datadir)

            # cm_agent
            for cmaInst in dbNode.cmagents:
                parse_xml.checkPathVaild(cmaInst.datadir)

    def __getDNPeerInstance(self, dbInst):
        """
        function : Get DN peer instance of specified instance when write static configuration file.
        input : []
        output : []
        """
        instances = []
        instIdLst = []

        for dbNode in self.dbNodes:
            for inst in dbNode.datanodes:
                if inst.mirrorId != const.MIRROR_ID_VW_DN and \
                        inst.mirrorId == dbInst.mirrorId and \
                        inst.instanceId != dbInst.instanceId:
                    instances.append(inst)
                    instIdLst.append(inst.instanceId)

        # In a primary multi-standby cluster,
        # since the CM update system table depends on the DN read/write sequence in the static configuration file,
        # we must sort the DN's standby list by instanceId.
        if ((self.isSinglePrimaryMultiStandbyCluster() or
             self.isSingleInstCluster()) and
                dbInst.instanceType == const.MASTER_INSTANCE):
            instIdLst.sort()
            instanceLst = []
            for instId in instIdLst:
                for inst in instances:
                    if inst.instanceId == instId:
                        instanceLst.append(inst)
            return instanceLst
        else:
            return instances

    def get_cluster_version(self):
        """
        """
        if self.is_multi_ip_listening():
            if self.isSingleCluster():
                version = const.BIN_CONFIG_VERSION_SINGLE_IPV6
            elif self.isSinglePrimaryMultiStandbyCluster():
                version = const.BIN_CONFIG_VERSION_SINGLE_PRIMARY_MULTI_STANDBY_IPV6
            elif self.isSingleInstCluster():
                if self.isHtapCluster():
                    version = const.BIN_CONFIG_VERSION_HTAP_INST_IPV6
                else:
                    version = const.BIN_CONFIG_VERSION_SINGLE_INST_IPV6
            elif self.isMasterStandbyMultiAZCluster():
                version = const.BIN_CONFIG_VERSION_MASTER_STANDBY_MULTI_AZ_IPV6
            else:
                version = const.BIN_CONFIG_VERSION_IPV6
        else:
            if self.isSingleCluster():
                version = const.BIN_CONFIG_VERSION_SINGLE
            elif self.isSinglePrimaryMultiStandbyCluster():
                version = const.BIN_CONFIG_VERSION_SINGLE_PRIMARY_MULTI_STANDBY
            elif self.isSingleInstCluster():
                if self.isHtapCluster():
                    version = const.BIN_CONFIG_VERSION_HTAP_INST
                else:
                    version = const.BIN_CONFIG_VERSION_SINGLE_INST
            elif self.isMasterStandbyMultiAZCluster():
                version = const.BIN_CONFIG_VERSION_MASTER_STANDBY_MULTI_AZ
            else:
                version = const.BIN_CONFIG_VERSION

        return version

    def saveToStaticConfig(self, filePath, localNodeId, dbNodes=None):
        """
        function : Save cluster info into to static config
        input : String,int
        output : NA
        BIN_CONFIG_VERSION_IPV6: 3
        BIN_CONFIG_VERSION_SINGLE_IPV6: 102
        BIN_CONFIG_VERSION_SINGLE_PRIMARY_MULTI_STANDBY_IPV6: 202
        BIN_CONFIG_VERSION_SINGLE_INST_IPV6: 303
        BIN_CONFIG_VERSION_HTAP_INST_IPV6: 304
        BIN_CONFIG_VERSION_MASTER_STANDBY_MULTI_AZ_IPV6: 502
        """
        try:
            if dbNodes is None:
                dbNodes = self.dbNodes
            flags = os.O_WRONLY | os.O_CREAT
            modes = stat.S_IWUSR | stat.S_IRUSR
            fp = os.fdopen(os.open(filePath, flags, modes), "wb")
            # len
            info = struct.pack("I", 28)
            # version
            if self.config_version == 0:
                self.config_version = self.get_cluster_version()
            info += struct.pack("I", self.config_version)
            # time
            info += struct.pack("q", int(time.time()))
            # node count
            info += struct.pack("I", len(dbNodes))
            # local node
            info += struct.pack("I", localNodeId)

            crc = binascii.crc32(info)
            info = struct.pack("I", crc) + info
            fp.write(info)

            for dbNode in dbNodes:
                offset = (fp.tell() // const.PAGE_SIZE + 1) * const.PAGE_SIZE
                fp.seek(offset)

                info = self.__packNodeInfo(dbNode)
                fp.write(info)
            endBytes = const.PAGE_SIZE - fp.tell() % const.PAGE_SIZE
            if endBytes != const.PAGE_SIZE:
                info = struct.pack("%dx" % endBytes)
                fp.write(info)
            fp.flush()
            fp.close()
            subprocess.getstatusoutput("chmod 750 %s" % filePath)
        except Exception as e:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50205"] %
                            "static configuration file" + " Error: \n%s" % str(e))

    def saveToStaticConfigForLC(self, filePath, localNodeId, dbNodes):
        """
        function : Save cluster info into static config file of logic cluster
        input : String,int
        output : NA
        """
        try:
            flags = os.O_WRONLY | os.O_CREAT
            modes = stat.S_IWUSR | stat.S_IRUSR
            fp = os.fdopen(os.open(filePath, flags, modes), "wb")
            # len
            info = struct.pack("I", 28)
            # version
            if self.config_version == 0:
                self.config_version = self.get_cluster_version()
            info += struct.pack("I", self.config_version)
            # time
            info += struct.pack("q", int(time.time()))
            # node count
            info += struct.pack("I", len(dbNodes))
            # local node
            info += struct.pack("I", localNodeId)

            crc = binascii.crc32(info)
            info = struct.pack("I", crc) + info
            fp.write(info)

            for dbNode in dbNodes:
                offset = (fp.tell() // const.PAGE_SIZE + 1) * const.PAGE_SIZE
                fp.seek(offset)

                info = self.__packNodeInfoForLC(dbNode)
                fp.write(info)
            endBytes = const.PAGE_SIZE - fp.tell() % const.PAGE_SIZE
            if endBytes != const.PAGE_SIZE:
                info = struct.pack("%dx" % endBytes)
                fp.write(info)
            fp.flush()
            fp.close()
            subprocess.getstatusoutput("chmod 750 %s" % filePath)
        except Exception as e:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50205"] %
                            "static configuration file" + " Error: \n%s" % str(e))

    def __packNodeInfo(self, dbNode):
        """
        function : Pack the info of node
        input : []
        output : String
        """
        # node id
        info = struct.pack("I", dbNode.id)
        # node name
        info += struct.pack("64s", dbNode.name.encode("utf-8"))
        # az info
        if self.isSinglePrimaryMultiStandbyCluster() or \
                self.isMasterStandbyMultiAZCluster() or \
                (self.isSingleInstCluster() and not self.isHtapCluster()):
            info += struct.pack("64s", dbNode.azName.encode("utf-8"))
            info += struct.pack("I", dbNode.azPriority)
        # backIp
        info += self.__packIps(dbNode.backIps)
        # sshIp
        info += self.__packIps(dbNode.sshIps)
        # cm_server
        info += self.__packCmsInfo(dbNode)
        # cm_agent
        info += self.__packAgentInfo(dbNode)
        # gtm
        info += self.__packGtmInfo(dbNode)
        # cancel save gtmProxy info,need a placeholder
        info += self.__packGtmProxyInfo(dbNode)
        # cn
        info += self.__packCooInfo(dbNode)
        # dn
        info += self.__packDataNode(dbNode)
        # etcd
        info += self.__packEtcdInfo(dbNode)
        # cancel save sctp begin/end port,need a placeholder
        info += struct.pack("I", 0)
        info += struct.pack("I", 0)
        crc = binascii.crc32(info)

        return struct.pack("I", crc) + info

    def __packNodeInfoForLC(self, dbNode):
        """
        function : Pack the info of node for the logic cluster
        input : []
        output : String
        """
        # node id
        info = struct.pack("I", dbNode.id)
        # node name
        info += struct.pack("64s", dbNode.name.encode("utf-8"))
        # backIp
        info += self.__packIps(dbNode.backIps)
        # sshIp
        info += self.__packIps(dbNode.sshIps)
        # dn
        info += self.__packDataNode(dbNode)
        # cancel save sctp begin/end port,need a placeholder
        info += struct.pack("I", 0)
        info += struct.pack("I", 0)
        crc = binascii.crc32(info)

        return struct.pack("I", crc) + info

    def __packEtcdInfo(self, dbNode):
        """
        function : Pack the info of etcd
        input : []
        output : String
        """
        n = len(dbNode.etcds)

        info = "".encode()
        if n == 0:
            # etcd count
            info += struct.pack("I", 0)
            # etcd id
            info += struct.pack("I", 0)
            # etcd mirror id
            info += struct.pack("i", 0)
            # etcd name
            info += struct.pack("64x")
            # datadir
            info += struct.pack("1024x")
            # listen ip
            info += self.__packIps([])
            # listn port
            info += struct.pack("I", 0)
            # ha ip
            info += self.__packIps([])
            # ha port
            info += struct.pack("I", 0)
        elif n == 1:
            etcdInst = dbNode.etcds[0]
            # etcd count
            info += struct.pack("I", 1)
            # etcd id
            info += struct.pack("I", etcdInst.instanceId)
            # etcd mirror id
            info += struct.pack("i", etcdInst.mirrorId)
            # etcd name
            info += struct.pack("64s", "etcd_%d".encode("utf-8") % etcdInst.instanceId)
            # datadir
            info += struct.pack("1024s", etcdInst.datadir.encode("utf-8"))
            # listen ip
            info += self.__packIps(etcdInst.listenIps)
            # listn port
            info += struct.pack("I", etcdInst.port)
            # ha ip
            info += self.__packIps(etcdInst.haIps)
            # ha port
            info += struct.pack("I", etcdInst.haPort)

        return info

    def __packCmsInfo(self, dbNode):
        """
        function : Pack the info of cm server
        input : []
        output : String
        """
        n = len(dbNode.cmservers)

        info = "".encode()
        if n == 0:
            # cm server id
            info += struct.pack("I", 0)
            # cm_server mirror id
            info += struct.pack("I", 0)
            # datadir
            info += struct.pack("1024s", dbNode.cmDataDir.encode("utf-8"))
            # cm server level
            info += struct.pack("I", 0)
            # float ip
            info += struct.pack("128x")
            # listen ip
            info += self.__packIps([])
            # listen port
            info += struct.pack("I", 0)
            # local ha ip
            info += self.__packIps([])
            # local ha port
            info += struct.pack("I", 0)
            # is primary
            info += struct.pack("I", 0)
            # peer ha ip
            info += self.__packIps([])
            # peer ha port
            info += struct.pack("I", 0)
        elif n == 1:
            cmsInst = dbNode.cmservers[0]
            # cm server id
            info += struct.pack("I", cmsInst.instanceId)
            # cm_server mirror id
            info += struct.pack("I", cmsInst.mirrorId)
            # datadir
            info += struct.pack("1024s", dbNode.cmDataDir.encode("utf-8"))
            # cm server level
            info += struct.pack("I", cmsInst.level)
            info += struct.pack("128s", self.cmsFloatIp.encode("utf-8"))
            # listen ip
            info += self.__packIps(cmsInst.listenIps)
            # listen port
            info += struct.pack("I", cmsInst.port)
            # local ha ip
            info += self.__packIps(cmsInst.haIps)
            # local ha port
            info += struct.pack("I", cmsInst.haPort)
            # instance type
            info += struct.pack("I", cmsInst.instanceType)
            if self.isMasterStandbyCluster() or \
                    self.isMasterStandbyMultiAZCluster() or \
                    (self.isSingleInstCluster() and not self.isHtapCluster()):
                instances = self.getPeerInstance(cmsInst)
                peerInst = instances[0]
                # peer ha ip
                info += self.__packIps(peerInst.haIps)
                # peer ha port
                info += struct.pack("I", peerInst.haPort)
            else:
                # peer ha ip
                info += self.__packIps([])
                # peer ha port
                info += struct.pack("I", 0)

        return info

    def __packAgentInfo(self, dbNode):
        """
        function : Pack the info of agent
        input : []
        output : String
        """
        n = len(dbNode.cmagents)

        info = "".encode()
        if n == 1:
            cmaInst = dbNode.cmagents[0]
            # Agent id
            info += struct.pack("I", cmaInst.instanceId)
            # Agent mirror id
            info += struct.pack("i", cmaInst.mirrorId)
            # agent ips
            info += self.__packIps(cmaInst.listenIps)

        return info

    def __packGtmInfo(self, dbNode):
        """
        function : Pack the info of gtm
        input : []
        output : String
        """
        n = len(dbNode.gtms)

        info = "".encode()
        if n == 0:
            # gtm id
            info += struct.pack("I", 0)
            # gtm mirror id
            info += struct.pack("I", 0)
            # gtm count
            info += struct.pack("I", 0)
            # datadir
            info += struct.pack("1024x")
            # listen ip
            info += self.__packIps([])
            # listn port
            info += struct.pack("I", 0)
            #  instance type
            info += struct.pack("I", 0)
            # loacl ha ip
            info += self.__packIps([])
            # local ha port
            info += struct.pack("I", 0)
            # peer gtm datadir
            info += struct.pack("1024x")
            # peer ha ip
            info += self.__packIps([])
            # peer ha port
            info += struct.pack("I", 0)
        elif n == 1:
            gtmInst = dbNode.gtms[0]
            # gtm id
            info += struct.pack("I", gtmInst.instanceId)
            # gtm mirror id
            info += struct.pack("I", gtmInst.mirrorId)
            # gtm count
            info += struct.pack("I", 1)
            # datadir
            info += struct.pack("1024s", gtmInst.datadir.encode("utf-8"))
            # listen ip
            info += self.__packIps(gtmInst.listenIps)
            # listn port
            info += struct.pack("I", gtmInst.port)
            #  instance type
            info += struct.pack("I", gtmInst.instanceType)
            # loacl ha ip
            info += self.__packIps(gtmInst.haIps)
            # local ha port
            info += struct.pack("I", gtmInst.haPort)
            if self.isMasterStandbyCluster() or self.isMasterStandbyMultiAZCluster():
                instances = self.getPeerInstance(gtmInst)
                peerInst = instances[0]
                # peer gtm datadir
                info += struct.pack("1024s", peerInst.datadir.encode("utf-8"))
                # peer ha ip
                info += self.__packIps(peerInst.haIps)
                # peer ha port
                info += struct.pack("I", peerInst.haPort)
            else:
                # peer gtm datadir
                info += struct.pack("1024x")
                # peer ha ip
                info += self.__packIps([])
                # peer ha port
                info += struct.pack("I", 0)

        return info

    def __packGtmProxyInfo(self, dbNode):
        """
        function : Pack the info of gtm proxy
        input : []
        output : String
        """
        info = "".encode()
        info += struct.pack("I", 0)
        info += struct.pack("I", 0)
        info += struct.pack("I", 0)
        info += self.__packIps([])
        info += struct.pack("I", 0)
        return info

    def __packCooInfo(self, dbNode):
        """
        function : Pack the info of coordinator
        input : []
        output : String
        """
        n = len(dbNode.coordinators)

        info = "".encode()
        if n == 0:
            # coordinator id
            info += struct.pack("I", 0)
            # coordinator mirror id
            info += struct.pack("i", 0)
            # coordinator count
            info += struct.pack("I", 0)
            # datadir
            info += struct.pack("1024x")
            # ssdDir
            info += struct.pack("1024x")
            # listen ip
            info += self.__packIps([], support_multi_listen_ip=True)
            # listen port
            info += struct.pack("I", 0)
            # ha port
            info += struct.pack("I", 0)
        elif n == 1:
            cooInst = dbNode.coordinators[0]
            # coordinator id
            info += struct.pack("I", cooInst.instanceId)
            # coordinator mirror id
            info += struct.pack("i", cooInst.mirrorId)
            # coordinator count
            info += struct.pack("I", 1)
            # datadir
            info += struct.pack("1024s", cooInst.datadir.encode("utf-8"))
            # ssdDir
            info += struct.pack("1024s", cooInst.ssdDir.encode("utf-8"))
            # listen ip
            info += self.__packIps(cooInst.listenIps, support_multi_listen_ip=True)
            # listn port
            info += struct.pack("I", cooInst.port)
            # ha port
            info += struct.pack("I", cooInst.haPort)

        return info

    def __packDataNode(self, dbNode):
        """
        function : Pack the info of datanode
        input : []
        output : String
        """

        info = struct.pack("I", len(dbNode.datanodes))
        for dnInst in dbNode.datanodes:
            # datanode id
            info += struct.pack("I", dnInst.instanceId)
            # datanode id
            info += struct.pack("I", dnInst.mirrorId)
            # datadir
            info += struct.pack("1024s", dnInst.datadir.encode("utf-8"))
            # ssdDir
            info += struct.pack("1024s", dnInst.ssdDir.encode("utf-8"))
            # listen ip
            info += self.__packIps(dnInst.listenIps, support_multi_listen_ip=True)
            # port
            info += struct.pack("I", dnInst.port)
            # instance type
            info += struct.pack("I", dnInst.instanceType)
            # loacl ha ip
            info += self.__packIps(dnInst.haIps)
            # local ha port
            info += struct.pack("I", dnInst.haPort)

            if self.isSinglePrimaryMultiStandbyCluster() or self.isSingleInstCluster():
                maxStandbyCount = const.MIRROR_COUNT_REPLICATION_MAX - 1
            else:
                maxStandbyCount = const.MIRROR_COUNT_DATA - 1

            instances = self.__getDNPeerInstance(dnInst)
            n = len(instances)
            for i in range(n):
                peerInst = instances[i]
                # peer1 datadir
                info += struct.pack("1024s", peerInst.datadir.encode("utf-8"))
                # peer1 ha ip
                info += self.__packIps(peerInst.haIps)
                # peer1 ha port
                info += struct.pack("I", peerInst.haPort)
                # instance type
                info += struct.pack("I", peerInst.instanceType)
            for i in range(n, maxStandbyCount):
                # peer1 datadir
                info += struct.pack("1024x")
                # peer1 ha ip
                info += self.__packIps([])
                # peer1 ha port
                info += struct.pack("I", 0)
                # instance type
                info += struct.pack("I", 0)

        return info

    def __packIps(self, ips, support_multi_listen_ip=False):
        """
        function : Pack the info of ips
        input : []
        output : String
        """
        n = len(ips)

        info = struct.pack("I", n)
        for i in range(n):
            info += struct.pack("128s", g_network.formatIP(ips[i]).encode("utf-8"))
        if support_multi_listen_ip and self.config_version in const.IPV6_VERSION_LIST:
            max_ip_num = const.NEW_MAX_IP_NUM
        else:
            max_ip_num = const.MAX_IP_NUM
        for _ in range(n, max_ip_num):
            info += struct.pack("128x")

        return info

    def listToCSV(self, obj):
        """
        convert a list (like IPs) to comma-sep string for XML
        """
        return ','.join(map(str, obj))

    def __writeWithIndent(self, fp, line, indent):
        """
        write the XML content with indentation
        """
        fp.write('%s%s\n' % (' ' * indent * 2, line))

    def generateXMLFromStaticConfigFile(self, user, static_config_file, xmlFilePath):
        """
        function : Generate cluster installation XML from static configuration file
        input : String,String,String
        output : Cluster installation XML file
        """
        indent = 0

        # get cluster version
        cluster_version = self.getClusterVersion(static_config_file)

        # Write XML header
        # file permission added to make it with 600
        fp = os.fdopen(os.open(xmlFilePath, os.O_WRONLY | os.O_CREAT, 0o600), "w")
        self.__writeWithIndent(fp, '<?xml version="1.0" encoding="UTF-8"?>', indent)

        # Get cluster info from ClusterStatic
        self.initFromStaticConfig(user, static_config_file)

        # Cluster header
        indent += 1
        self.__writeWithIndent(fp, '<ROOT>', indent)
        self.__writeWithIndent(fp, '<CLUSTER>', indent)
        indent += 1
        self.__writeWithIndent(fp, '<PARAM name="clusterName" value="%s" />' % self.name, indent)

        nodeList = self.getClusterNodeNames()
        node_names_list = []
        for item in nodeList:
            node_names_list.append(str(item) + ",")
        node_names = "".join(node_names_list)
        nodeNames = node_names[:-1]
        self.__writeWithIndent(fp, '<PARAM name="nodeNames" value="%s" />' % nodeNames, indent)
        self.__writeWithIndent(fp, '<PARAM name="gaussdbAppPath" value="%s" />' % self.appPath, indent)
        self.__writeWithIndent(fp, '<PARAM name="gaussdbLogPath" value="%s" />' % self.logPath, indent)
        self.__writeWithIndent(fp, '<PARAM name="tmpMppdbPath" value="%s" />' % self.tmpPath, indent)
        self.__writeWithIndent(fp, '<PARAM name="gaussdbToolPath" value="%s" />' % self.toolPath, indent)
        if self.isMiniaturizedDeployment(cluster_version):
            self.__writeWithIndent(fp, '<PARAM name="clusterType" value="single" />', indent)
        elif self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
            self.__writeWithIndent(fp, '<PARAM name="clusterType" value="single-primary-multi-standby" />', indent)
        indent -= 1
        self.__writeWithIndent(fp, '</CLUSTER>', indent)
        self.__writeWithIndent(fp, '<DEVICELIST>', indent)

        # <DEVICELIST>
        ctr = 1000001
        # For each node
        for local_dbn in self.dbNodes:
            # Device beginning
            self.__writeWithIndent(fp, '<DEVICE sn="%s">' % (str(ctr)), indent)

            indent += 1
            self.__writeWithIndent(fp, '<PARAM name="name" value="%s" />' % local_dbn.name, indent)
            if self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
                self.__writeWithIndent(fp, '<PARAM name="azName" value="%s" />' % local_dbn.azName, indent)
                self.__writeWithIndent(fp, '<PARAM name="azPriority" value="%s" />' % local_dbn.azPriority, indent)
            self.__writeWithIndent(fp, '<PARAM name="backIp1" value="%s" />' %
                                   (self.listToCSV(local_dbn.backIps)), indent)
            self.__writeWithIndent(fp, '<PARAM name="sshIp1" value="%s" />' %
                                   (self.listToCSV(local_dbn.sshIps)), indent)
            if not self.isMiniaturizedDeployment(cluster_version):
                self.__writeWithIndent(fp, '<PARAM name="virtualIp" value="%s" />' %
                                       (self.listToCSV(local_dbn.virtualIp)), indent)
                # ETCD beginning
                if local_dbn.etcdNum > 0:
                    # Common part
                    self.__writeWithIndent(fp, '<!-- etcd-->', indent)
                    self.__writeWithIndent(fp, '<PARAM name="etcdNum" value="%d" />' % local_dbn.etcdNum, indent)
                    self.__writeWithIndent(fp, '<PARAM name="etcdListenPort" value="%d" />' %
                                           local_dbn.etcds[0].port,
                                           indent)
                    self.__writeWithIndent(fp, '<PARAM name="etcdHaPort" value="%d" />' %
                                           local_dbn.etcds[0].haPort, indent)

                    # Repeated part
                    i = 1
                    for etcdInst in local_dbn.etcds:
                        self.__writeWithIndent(fp, '<PARAM name="etcdDir%d" value="%s" />' %
                                               (i, etcdInst.datadir), indent)
                        self.__writeWithIndent(fp, '<PARAM name="etcdListenIp%d" value="%s" />' %
                                               (i, self.listToCSV(etcdInst.listenIps)), indent)
                        self.__writeWithIndent(fp, '<PARAM name="etcdHaIp%d" value="%s" />' %
                                               (i, self.listToCSV(etcdInst.haIps)), indent)
                        i += 1
                # ETCD ending

            # generate XML for CM GTM CN
            self.generateXMLForCMGTMCN(local_dbn, fp, indent, cluster_version)

            # dn beginning
            if local_dbn.dataNum > 0:
                # Find master DN
                dnList = [dn for dn in local_dbn.datanodes if
                          dn.instanceRole == const.INSTANCE_ROLE_DATANODE and dn.instanceType == const.MASTER_INSTANCE]
                if len(dnList) == 0:
                    # No master DN found in this node, so skip...
                    continue

                # generate XML for DN common part1
                self.generateXMLForDNPart1(dnList, cluster_version, local_dbn, fp, indent)

                # generate XML for DN common part2 part3
                self.generateXMLForDNPart23(dnList, cluster_version, indent, fp)
            # dn ending

            # Device ending
            indent -= 1
            self.__writeWithIndent(fp, '</DEVICE>', indent)
            ctr += 1
        self.__writeWithIndent(fp, '</DEVICELIST>', indent)
        self.__writeWithIndent(fp, '</ROOT>', indent)
        fp.close()

    def generate_xml_for_cm(self, local_dbn, fp, indent, cluster_version):
        """
        function: generate XML for CM
        input: local_dbn, fp, indent, cluster_version
        output: NA
        """
        try:
            cmsInst = local_dbn.cmservers[0]
            self.__writeWithIndent(fp, '<!--  cm (configuration manager)-->', indent)
            self.__writeWithIndent(fp, '<PARAM name="cmsNum" value="%d" />' % local_dbn.cmsNum, indent)
            self.__writeWithIndent(fp, '<PARAM name="cmServerPortBase" value="%d" />' % cmsInst.port, indent)
            self.__writeWithIndent(fp, '<PARAM name="cmServerlevel" value="%d" />' % cmsInst.level, indent)
            self.__writeWithIndent(fp, '<PARAM name="cmDir" value="%s" />' % local_dbn.cmDataDir, indent)
            if not self.isMiniaturizedDeployment(cluster_version):
                peerInst_listenIps = ''
                peerInst_haIps = ''
                peerInst_hostname = ''
                for peerInst in self.getPeerInstance(cmsInst):
                    peerInst_listenIps = "%s%s," % (peerInst_listenIps, peerInst.listenIps[0])
                    peerInst_haIps = "%s%s," % (peerInst_haIps, peerInst.haIps[0])
                    peerInst_port = peerInst.port
                    peerInst_hostname = "%s%s," % (peerInst_hostname, peerInst.hostname)

                self.__writeWithIndent(fp, '<PARAM name="cmServerPortStandby" value="%d" />' %
                                       peerInst_port, indent)
                self.__writeWithIndent(fp, '<PARAM name="cmServerListenIp1" value="%s,%s" />' %
                                       (cmsInst.listenIps[0], peerInst_listenIps[:-1]), indent)
                self.__writeWithIndent(fp, '<PARAM name="cmServerHaIp1" value="%s,%s" />' %
                                       (cmsInst.haIps[0], peerInst_haIps[:-1]), indent)
                self.__writeWithIndent(fp, '<PARAM name="cmServerHaPort" value="%d" />' %
                                       cmsInst.haPort, indent)
                self.__writeWithIndent(fp, '<PARAM name="cmServerRelation" value="%s,%s" />' %
                                       (cmsInst.hostname, peerInst_hostname[:-1]), indent)
            else:
                self.__writeWithIndent(fp, '<PARAM name="cmServerListenIp1" value="%s" />' %
                                       (cmsInst.listenIps[0]), indent)
        except IndexError:
            # No CM in this instance - make blank entry...
            self.__writeWithIndent(fp, '<!--  cm (configuration manager)-->', indent)
            self.__writeWithIndent(fp, '<PARAM name="cmsNum" value="0" />', indent)
            self.__writeWithIndent(fp, '<PARAM name="cmServerPortBase" value="%d" />' %
                                   const.MASTER_BASEPORT_CMS, indent)
            self.__writeWithIndent(fp, '<PARAM name="cmServerListenIp1" value="" />', indent)
            self.__writeWithIndent(fp, '<PARAM name="cmServerlevel" value="1" />', indent)
            self.__writeWithIndent(fp, '<PARAM name="cmDir" value="%s" />' % local_dbn.cmDataDir, indent)
            if not self.isMiniaturizedDeployment(cluster_version):
                self.__writeWithIndent(fp, '<PARAM name="cmServerPortStandby" value="%d" />' %
                                       const.STANDBY_BASEPORT_CMS, indent)
                self.__writeWithIndent(fp, '<PARAM name="cmServerHaIp1" value="" />', indent)
                self.__writeWithIndent(fp, '<PARAM name="cmServerHaPort" value="" />', indent)
                self.__writeWithIndent(fp, '<PARAM name="cmServerRelation" value="%s,%s" />' %
                                       (local_dbn.name, local_dbn.name), indent)

    def generate_xml_for_gtm(self, local_dbn, fp, indent, cluster_version):
        """
        function: generate XML for gtm
        input: local_dbn, fp, indent, cluster_version
        output: NA
        """
        try:
            gtmInst = local_dbn.gtms[0]
            self.__writeWithIndent(fp, '<!--  gtm (global transaction manager)-->', indent)
            self.__writeWithIndent(fp, '<PARAM name="gtmNum" value="%d" />' % local_dbn.gtmNum, indent)
            self.__writeWithIndent(fp, '<PARAM name="gtmPortBase" value="%d" />' % gtmInst.port, indent)
            # No GTM in this instance - make blank entry...
            if not self.isMiniaturizedDeployment(cluster_version):
                peerInst_listenIps = ''
                peerInst_haIps = ''
                peerInst_hostname = ''
                peerInst_hostname_datadir = ''
                for peerInst in self.getPeerInstance(gtmInst):
                    peerInst_listenIps = "%s%s," % (peerInst_listenIps, peerInst.listenIps[0])
                    peerInst_haIps = "%s%s," % (peerInst_haIps, peerInst.haIps[0])
                    peerInst_port = peerInst.port
                    peerInst_hostname = "%s%s," % (peerInst_hostname, peerInst.hostname)
                    peerInst_hostname_datadir = "%s%s,%s," % (peerInst_hostname_datadir,
                                                              peerInst.hostname,
                                                              peerInst.datadir)
                if not self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
                    self.__writeWithIndent(fp, '<PARAM name="gtmPortStandby" value="%d" />' %
                                           peerInst_port, indent)
                    self.__writeWithIndent(fp, '<PARAM name="gtmHaPort" value="%d" />' % gtmInst.haPort, indent)
                self.__writeWithIndent(fp, '<PARAM name="gtmListenIp1" value="%s,%s" />' %
                                       (gtmInst.listenIps[0], peerInst_listenIps[:-1]), indent)
                self.__writeWithIndent(fp, '<PARAM name="gtmHaIp1" value="%s,%s" />' %
                                       (gtmInst.haIps[0], peerInst_haIps[:-1]), indent)
                self.__writeWithIndent(fp, '<PARAM name="gtmDir1" value="%s,%s" />' %
                                       (gtmInst.datadir, peerInst_hostname_datadir[:-1]), indent)
                self.__writeWithIndent(fp, '<PARAM name="gtmRelation" value="%s,%s" />' %
                                       (gtmInst.hostname, peerInst_hostname[:-1]), indent)
            else:
                self.__writeWithIndent(fp, '<PARAM name="gtmListenIp1" value="%s" />' %
                                       (gtmInst.listenIps[0]), indent)
                self.__writeWithIndent(fp, '<PARAM name="gtmDir1" value="%s" />' %
                                       gtmInst.datadir, indent)
        except IndexError:
            self.__writeWithIndent(fp, '<!--  gtm (global transaction manager)-->', indent)
            self.__writeWithIndent(fp, '<PARAM name="gtmNum" value="0" />', indent)
            self.__writeWithIndent(fp, '<PARAM name="gtmPortBase" value="%d" />' % const.MASTER_BASEPORT_GTM, indent)
            self.__writeWithIndent(fp, '<PARAM name="gtmListenIp1" value="" />', indent)
            self.__writeWithIndent(fp, '<PARAM name="gtmDir1" value="" />', indent)
            if not self.isMiniaturizedDeployment(cluster_version):
                if not self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
                    self.__writeWithIndent(fp, '<PARAM name="gtmPortStandby" value="%d" />' %
                                           const.STANDBY_BASEPORT_GTM, indent)
                    self.__writeWithIndent(fp, '<PARAM name="gtmHaPort" value="" />', indent)
                self.__writeWithIndent(fp, '<PARAM name="gtmHaIp1" value="" />', indent)
                self.__writeWithIndent(fp, '<PARAM name="gtmRelation" value="%s,%s" />' %
                                       (local_dbn.name, local_dbn.name), indent)

    def generate_xml_for_cn(self, local_dbn, fp, indent, cluster_version):
        """
        function: generate XML for cn
        input: local_dbn, fp, indent, cluster_version
        output: NA
        """
        if local_dbn.cooNum > 0:
            for cooInst in local_dbn.coordinators:
                self.__writeWithIndent(fp, '<!--  cn (co-ordinator)-->', indent)
                self.__writeWithIndent(fp, '<PARAM name="cooNum" value="%d" />' % local_dbn.cooNum, indent)
                self.__writeWithIndent(fp, '<PARAM name="cooPortBase" value="%d" />' % cooInst.port, indent)
                self.__writeWithIndent(fp, '<PARAM name="cooListenIp1" value="%s" />' %
                                       (self.listToCSV(cooInst.listenIps)), indent)
                self.__writeWithIndent(fp, '<PARAM name="cooDir1" value="%s" />' % cooInst.datadir, indent)
                if not self.isMiniaturizedDeployment(cluster_version):
                    self.__writeWithIndent(fp, '<PARAM name="ssdCNDir1" value="" />', indent)

    def generateXMLForCMGTMCN(self, local_dbn, fp, indent, cluster_version):
        """
        function: generate XML for CM GTM CN
        input: NA
        output: NA
        """
        self.generate_xml_for_cm(local_dbn, fp, indent, cluster_version)
        self.generate_xml_for_gtm(local_dbn, fp, indent, cluster_version)
        self.generate_xml_for_cn(local_dbn, fp, indent, cluster_version)

    def generateXMLForDNPart1(self, dnList, cluster_version, local_dbn, fp, indent):
        """
        function: generate XML for DN common part1
        input: NA
        output: NA
        """
        # Find min MasterDN port value
        dnPort = dnList[0].port
        for dn in dnList:
            if dnPort > dn.port:
                dnPort = dn.port

        if not self.isMiniaturizedDeployment(cluster_version) and \
                not self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
            # Find min StandbyDN port and IP value - need to optimize
            snList = [sn for sn in local_dbn.datanodes if
                      sn.instanceRole == const.INSTANCE_ROLE_DATANODE and sn.instanceType == const.STANDBY_INSTANCE]
            snPort = snList[0].port
            for sn in snList:
                if snPort > sn.port:
                    snPort = sn.port

            # Find min MasterDN port value - need to optimize
            dsnList = [dsn for dsn in local_dbn.datanodes if
                       dsn.instanceRole == const.INSTANCE_ROLE_DATANODE
                       and dsn.instanceType == const.DUMMY_STANDBY_INSTANCE]
            dsnPort = dsnList[0].port
            for dsn in dsnList:
                if dsnPort > dsn.port:
                    dsnPort = dsn.port

        if self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
            # Find min StandbyDN port and IP value - need to optimize
            snList = [sn for sn in local_dbn.datanodes if
                      sn.instanceRole == const.INSTANCE_ROLE_DATANODE and sn.instanceType == const.STANDBY_INSTANCE]
            snPort = snList[0].port
            for sn in snList:
                if snPort > sn.port:
                    snPort = sn.port
        # DN common part (1/3)
        self.__writeWithIndent(fp, '<!--  dn (data-node)-->', indent)
        self.__writeWithIndent(fp, '<PARAM name="dataNum" value="%d" />' % local_dbn.dataNum, indent)
        self.__writeWithIndent(fp, '<PARAM name="dataPortBase" value="%d" />' % dnPort, indent)
        if not self.isMiniaturizedDeployment(cluster_version) and \
                not self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
            self.__writeWithIndent(fp, '<PARAM name="dataPortStandby" value="%s" />' % (snPort), indent)
            self.__writeWithIndent(fp, '<PARAM name="dataPortDummyStandby" value="%s" />' % (dsnPort), indent)

    def generateXMLForDNPart23(self, dnList, cluster_version, indent, fp):
        """
        function: generate XML for DN common part2 part3
        input: NA
        output: NA
        """
        i = 1
        dnInst = None
        snListenIP = snHaIP = snHostNm = snDir = sn_HostNm_Dir = ''
        for dnInst in dnList:
            if not self.isMiniaturizedDeployment(cluster_version):
                # Find SNs
                instances = self.getPeerInstance(dnInst)
                snList = [sn for sn in instances if
                          sn.instanceRole == const.INSTANCE_ROLE_DATANODE and sn.instanceType == const.STANDBY_INSTANCE]
                if len(snList) == 0:
                    # Will it ever come here - can be removed???
                    print("<<DEBUG>> No SN found for DN(%s)" % dnInst.name)
                else:
                    for sn in snList:
                        snListenIP = "%s%s," % (snListenIP, sn.listenIps[0])
                        snHostNm = "%s%s," % (snHostNm, sn.hostname)
                        snDir = "%s%s," % (snDir, sn.datadir)
                        sn_HostNm_Dir = "%s%s%s," % (sn_HostNm_Dir, sn.hostname, sn.datadir)
                        snHaIP = "%s%s," % (snHaIP, sn.haIps[0])

            # Once only per Host, the ListenIP entry needs to be written. Part (2/3)
            if i == 1:
                self.write_data_listen_ip(cluster_version, fp, dnInst, snListenIP, snHaIP, indent)
            # Find DSNs
            if not self.isMiniaturizedDeployment(cluster_version) and \
                    not self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
                instances = self.getPeerInstance(dnInst)
                dsnList = [dsn for dsn in instances if
                           dsn.instanceRole == const.INSTANCE_ROLE_DATANODE
                           and dsn.instanceType == const.DUMMY_STANDBY_INSTANCE]
                if len(dsnList) == 0:
                    # Will it ever come here - can be removed???
                    print("<<DEBUG>> No DSN found for DN(%s)" % dnInst.name)
                    dsnHostNm = dsnDir = ''
                else:
                    dsnHostNm = dsnList[0].hostname
                    dsnDir = dsnList[0].datadir
            # DN repeated part (3/3)
            if self.isMiniaturizedDeployment(cluster_version):
                self.__writeWithIndent(fp, '<PARAM name="dataNode%d" value="%s" />' % (i, dnInst.datadir), indent)
            elif self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
                self.__writeWithIndent(fp, '<PARAM name="dataNode%d" value="%s,%s" />' %
                                       (i, dnInst.datadir, sn_HostNm_Dir[:-1]), indent)
            else:
                self.__writeWithIndent(fp, '<PARAM name="dataNode%d" value="%s,%s,%s,%s,%s" />' %
                                       (i, dnInst.datadir, snHostNm[:-1], snDir[:-1], dsnHostNm, dsnDir), indent)
                self.__writeWithIndent(fp, '<PARAM name="ssdDNDir%d" value="" />' % i, indent)
            i += 1
        if not self.isMiniaturizedDeployment(cluster_version):
            self.__writeWithIndent(fp, '<PARAM name="cmAgentConnectIp" value="%s" />' % (dnInst.listenIps[0]), indent)

    def write_data_listen_ip(self, cluster_version, fp, dnInst, snListenIP, snHaIP, indent):
        """
        """
        if self.isMiniaturizedDeployment(cluster_version):
            self.__writeWithIndent(fp, '<PARAM name="dataListenIp" value="%s,%s" />' %
                                   (dnInst.listenIps[0], dnInst.listenIps[0]), indent)
        elif self.isSinglePrimaryMultiStandbyDeployment(cluster_version):
            self.__writeWithIndent(fp, '<PARAM name="dataListenIp1" value="%s,%s" />' %
                                   (dnInst.listenIps[0], snListenIP[:-1]), indent)
            self.__writeWithIndent(fp, '<PARAM name="dataHaIp1" value="%s,%s" />' %
                                   (dnInst.listenIps[0], snHaIP[:-1]), indent)
        else:
            self.__writeWithIndent(fp, '<PARAM name="dataListenIp" value="%s,%s" />' %
                                   (dnInst.listenIps[0], snListenIP[:-1]), indent)

    def getLocalNode(self):
        """
        function: get local node
        input: NA
        output: NA
        """
        hostname = socket.gethostname()
        for node in self.dbNodes:
            if node.name == hostname:
                return node
        return []

    def __getInstsInNode(self, nodeName):
        """
        function: get instance in specified node
        input: node name
        output: instances list
        """
        for node in self.dbNodes:
            if node.name == nodeName:
                insts = node.etcds + node.cmservers + node.datanodes + node.coordinators
                return insts
        return []

    def __getAllInsts(self):
        """
        function: get all instances
        input: NA
        output: all instances list
        """
        insts = []
        for node in self.dbNodes:
            insts += node.etcds + node.cmservers + node.datanodes + node.coordinators
        return insts

    def getInstances(self, nodeName=""):
        """
        function: get instances in the cluster, if nodeName is specified, return
                  the instances in the ndoe
        input: node name
        output: all instances
        """
        if nodeName:
            insts = self.__getInstsInNode(nodeName)
        else:
            insts = self.__getAllInsts()
        return insts

    def isSinglePrimaryMultiStandbyCluster(self):
        return self.clusterType == const.CLUSTER_TYPE_SINGLE_PRIMARY_MULTI_STANDBY

    def isMasterStandbyCluster(self):
        return self.clusterType == const.CLUSTER_TYPE_MASTER_STANDBY

    def is_multi_ip_listening(self):
        listen_ip_num = []
        for dbNode in self.dbNodes:
            for inst in dbNode.coordinators + dbNode.datanodes:
                listen_ip_num.append(len(inst.listenIps))

        return max(listen_ip_num) > const.MAX_IP_NUM

    def isSingleCluster(self):
        return self.clusterType == const.CLUSTER_TYPE_SINGLE

    def isSingleInstCluster(self):
        return self.clusterType == const.CLUSTER_TYPE_SINGLE_INST

    def isHtapCluster(self):
        return self.is_htap_cluster

    def isMasterStandbyMultiAZCluster(self):
        return self.clusterType == const.CLUSTER_TYPE_MASTER_STANDBY_MULTI_AZ

    def mergeClusterInfo(self, oldClusterInfo, newClusterInfo):
        """
        function: get etcd address
        input: NA
        output: etcd address
        """
        # should not modify newClusterInfo, so deepcopy
        tmpClusterInfo = copy.deepcopy(newClusterInfo)

        # name/clusterName are different between old and new cluster.
        # clusterType/appPath/logPath/toolPath/tmpPath are same between old and new cluster.
        self.name = tmpClusterInfo.name
        self.clusterName = tmpClusterInfo.clusterName
        self.clusterType = tmpClusterInfo.clusterType
        self.appPath = tmpClusterInfo.appPath
        self.logPath = tmpClusterInfo.logPath
        self.toolPath = tmpClusterInfo.toolPath
        self.tmpPath = tmpClusterInfo.tmpPath
        self.config_version = tmpClusterInfo.config_version

        # get max nodeId of old cluster.
        maxNodeId = max([int(oldNode.id) for oldNode in oldClusterInfo.dbNodes])
        maxNodeId += 1

        for dbNode in tmpClusterInfo.dbNodes:
            # CMS/GTM/ETCD will be dropped in merged cluster.
            dbNode.cmservers = []
            dbNode.gtms = []
            dbNode.etcds = []

            # nodeId will append to old cluster.
            dbNode.id = maxNodeId
            maxNodeId += 1

        self.dbNodes = oldClusterInfo.dbNodes + tmpClusterInfo.dbNodes
        self.newNodes = tmpClusterInfo.dbNodes

    def _get_dn_hostname_per_node(self):
        dn_hostname_per_node_list = []
        for dbNode in self.dbNodes:
            dn_hostname_per_node = []
            for dn_inst in dbNode.datanodes:
                if dn_inst.instanceType == const.MASTER_INSTANCE:
                    if dn_inst.hostname not in dn_hostname_per_node:
                        dn_hostname_per_node.append(dn_inst.hostname)
                    instances = self.getPeerInstance(dn_inst)
                    for inst in instances:
                        if inst.hostname not in dn_hostname_per_node:
                            dn_hostname_per_node.append(inst.hostname)
            dn_hostname_per_node_list.append(dn_hostname_per_node)
        return dn_hostname_per_node_list

    def get_cluster_rings(self):
        dn_hostname_per_node_list = self._get_dn_hostname_per_node()
        # Loop the hostname list on each node where the master and slave of the DN instance.
        for i in range(len(dn_hostname_per_node_list)):
            # Loop the list after the i-th list
            for host_list in dn_hostname_per_node_list[i + 1:len(dn_hostname_per_node_list)]:
                in_same_ring = False
                # Loop the elements of each host_list
                for host_name in host_list:
                    # If one host on the i-th node, each host of the list are joined in dn_hostname_per_node_list[i]
                    if host_name in dn_hostname_per_node_list[i]:
                        in_same_ring = True
                        for one_host in host_list:
                            if one_host not in dn_hostname_per_node_list[i]:
                                dn_hostname_per_node_list[i].append(one_host)
                        break
                if in_same_ring:
                    dn_hostname_per_node_list.remove(host_list)
        return dn_hostname_per_node_list

    def get_dn_num_in_one_node(self):
        """
        """
        data_node_num = []
        for db_node in self.dbNodes:
            data_node_num.append(db_node.dataNum)
        max_data_node_num = max(data_node_num)

        return max_data_node_num

    def get_total_data_node_dum(self):
        """
        """
        data_count = 0
        for db_node in self.dbNodes:
            data_count += db_node.dataNum

        return data_count
