#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c): 2012-2017, Huawei Tech. Co., Ltd.
# Description  : DbClusterStatus.py is a utility to get cluster status information.
#############################################################################
try:
    import os
    import sys
    import time

    sys.path.append(sys.path[0] + "/../../")
    from gspylib.common.Common import DefaultValue, ClusterInstanceConfig
    from gspylib.common.DbClusterInfo import dbClusterInfo
    from gspylib.common.ErrorCode import ErrorCode
except ImportError as ie:
    sys.exit("[GAUSS-52200] : Unable to import module: %s." % str(ie))

###########################
# instance type. only for CN/DN
###########################
INSTANCE_TYPE_UNDEFINED = -1
# master
MASTER_INSTANCE = 0
# standby
STANDBY_INSTANCE = 1
# dummy standby
DUMMY_STANDBY_INSTANCE = 2

# Global parameter
g_clusterInfo = None
g_instanceInfo = None
g_clusterInfoInitialized = False
g_deletedCNId = []


class StatusReport():
    """
    classdocs
    """

    def __init__(self):
        """
        Constructor
        """
        self.nodeCount = 0
        self.cooNormal = 0
        self.cooAbnormal = 0
        self.gtmPrimary = 0
        self.gtmStandby = 0
        self.gtmAbnormal = 0
        self.gtmDown = 0
        self.dnPrimary = 0
        self.dnStandby = 0
        self.dnDummy = 0
        self.dnBuild = 0
        self.dnAbnormal = 0
        self.dnDown = 0
        self.fencedUDFNormal = 0
        self.fencedUDFAbnormal = 0


class DbInstanceStatus():
    """
    classdocs
    """

    def __init__(self, nodeId, instId=0):
        """
        Constructor
        """
        self.nodeId = nodeId
        self.instanceId = instId
        self.datadir = ""
        self.type = ""
        self.status = ""
        self.haStatus = ""
        self.connStatus = ""
        self.syncStatus = ""
        self.reason = ""

    def __str__(self):
        """
        """
        retStr = "nodeId=%s,instanceId=%s,datadir=%s,type=%s,status=%s," \
                 "haStatus=%s,connStatus=%s,syncStatus=%s,reason=%s" % \
                 (self.nodeId, self.instanceId, self.datadir, self.type, self.status,
                  self.haStatus, self.connStatus, self.syncStatus, self.reason)

        return retStr

    def isInstanceHealthy(self, is_dn_slave_ha=False):
        """
        function : Check if instance is healthy
        input : NA
        output : boolean
        """
        # check CN instance
        if (self.type == DbClusterStatus.INSTANCE_TYPE_COORDINATOR):
            if (self.status != DbClusterStatus.INSTANCE_STATUS_NORMAL and self.instanceId not in g_deletedCNId):
                return False

        # check gtm instance
        if (self.type == DbClusterStatus.INSTANCE_TYPE_GTM):
            if (self.status == DbClusterStatus.INSTANCE_STATUS_PRIMARY):
                return True
            elif (self.status == DbClusterStatus.INSTANCE_STATUS_STANDBY):
                if (self.connStatus != DbClusterStatus.CONN_STATUS_NORMAL):
                    return False
            else:
                return False

        # check DN instance
        if (self.type == DbClusterStatus.INSTANCE_TYPE_DATANODE):
            if (self.status == DbClusterStatus.INSTANCE_STATUS_PRIMARY):
                return True
            elif (self.status == DbClusterStatus.INSTANCE_STATUS_DUMMY):
                return True
            elif (self.status == DbClusterStatus.INSTANCE_STATUS_STANDBY):
                if is_dn_slave_ha and self.haStatus in (DbClusterStatus.HA_STATUS_NORMAL,
                                                        DbClusterStatus.HA_STATUS_BUILD,
                                                        DbClusterStatus.HA_STATUS_START):
                    return True
                if (self.haStatus != DbClusterStatus.HA_STATUS_NORMAL):
                    return False
            else:
                return False

        # check fenced udf instance
        if (self.type == DbClusterStatus.INSTANCE_TYPE_FENCED_UDF):
            if (self.status != DbClusterStatus.INSTANCE_STATUS_NORMAL):
                return False

        # check ETCD instance
        if (self.type == DbClusterStatus.INSTANCE_TYPE_ETCD):
            if (self.status != DbClusterStatus.INSTANCE_STATUS_STATElEADER and
                    self.status != DbClusterStatus.INSTANCE_STATUS_STATEfOLLOWER):
                return False

        # check CMS instance
        if (self.type == DbClusterStatus.INSTANCE_TYPE_CMSERVER):
            if (self.status == DbClusterStatus.INSTANCE_STATUS_PRIMARY or
                    self.status == DbClusterStatus.INSTANCE_STATUS_STANDBY):
                return True
            else:
                return False

        return True

    def isCNDeleted(self):
        """
        function : Check if CN instance state is Deleted
        input : NA
        output : boolean
        """
        # check CN instance
        if (self.type == DbClusterStatus.INSTANCE_TYPE_COORDINATOR and self.instanceId in g_deletedCNId):
            return True
        return False


class DbNodeStatus():
    """
    classdocs
    """

    def __init__(self, nodeId):
        """
        Constructor
        """
        self.id = nodeId
        self.name = ""
        self.version = ""
        self.coordinators = []
        self.gtms = []
        self.datanodes = []

        self.cmservers = []
        self.primaryDNs = []
        self.standbyDNs = []
        self.dummies = []
        self.fencedUDFs = []
        self.etcds = []

    def __str__(self):
        """
        """
        inst = ["NodeId=%s,HostName=%s" % (self.id, self.name)]
        for cmsInst in self.cmservers:
            inst.append("\n%s" % str(cmsInst))
        for gtmInst in self.gtms:
            inst.append("\n%s" % str(gtmInst))
        for cooInst in self.coordinators:
            inst.append("\n%s" % str(cooInst))
        for dataInst in self.datanodes:
            inst.append("\n%s" % str(dataInst))
        for dataInst in self.etcds:
            inst.append("\n%s" % str(dataInst))
        for udfInst in self.fencedUDFs:
            inst.append("\n%s" % str(udfInst))

        ret_str = "".join(inst)
        return ret_str

    def isNodeHealthy(self, is_dn_slave_ha=False):
        """
        function : Check if node is healthy
        input : NA
        output : boolean
        """
        # get CN, DN and gtm instance
        instances = self.coordinators + self.gtms + self.datanodes
        # check if node is healthy
        for inst in instances:
            if (not inst.isInstanceHealthy(is_dn_slave_ha)):
                return False

        return True

    def getNodeStatusReport(self):
        """
        function :  Get the status report of node
        input : NA
        output : report
        """
        # init class StatusReport
        report = StatusReport()
        for inst in self.coordinators:
            if (inst.status == DbClusterStatus.INSTANCE_STATUS_NORMAL):
                report.cooNormal += 1
            else:
                report.cooAbnormal += 1

        for inst in self.gtms:
            if (inst.status == DbClusterStatus.INSTANCE_STATUS_PRIMARY):
                report.gtmPrimary += 1
            elif (inst.status == DbClusterStatus.INSTANCE_STATUS_STANDBY):
                if (inst.connStatus == DbClusterStatus.CONN_STATUS_NORMAL):
                    report.gtmStandby += 1
                else:
                    report.gtmAbnormal += 1
            elif (inst.status == DbClusterStatus.INSTANCE_STATUS_DOWN):
                report.gtmDown += 1
            else:
                report.gtmAbnormal += 1

        for inst in self.datanodes:
            if (inst.status == DbClusterStatus.INSTANCE_STATUS_PRIMARY):
                report.dnPrimary += 1
            elif (inst.status == DbClusterStatus.INSTANCE_STATUS_STANDBY):
                if (inst.haStatus == DbClusterStatus.HA_STATUS_NORMAL):
                    report.dnStandby += 1
                elif (inst.haStatus == DbClusterStatus.HA_STATUS_BUILD):
                    report.dnBuild += 1
                else:
                    report.dnAbnormal += 1
            elif (inst.status == DbClusterStatus.INSTANCE_STATUS_DOWN):
                report.dnDown += 1
            elif (inst.status == DbClusterStatus.INSTANCE_STATUS_DUMMY):
                report.dnDummy += 1
            else:
                report.dnAbnormal += 1

        # check fenced UDF instance
        for inst in self.fencedUDFs:
            if (inst.status == DbClusterStatus.INSTANCE_STATUS_NORMAL):
                report.fencedUDFNormal += 1
            else:
                report.fencedUDFAbnormal += 1

        return report

    def outputNodeStatus(self, stdout, user, showDetail=False):
        """
        function : output the status of node
        input : stdout, user
        output : NA
        """
        global g_clusterInfo
        global g_instanceInfo
        global g_clusterInfoInitialized
        if not g_clusterInfoInitialized:
            DefaultValue.checkUser(user)
            g_clusterInfo = dbClusterInfo()
            g_clusterInfo.initFromStaticConfig(user)
            g_clusterInfoInitialized = True
        dbNode = g_clusterInfo.getDbNodeByName(self.name)
        # print node information
        print("%-20s: %d" % ("node", self.id), file=stdout)
        print("%-20s: %s" % ("node_name", self.name), file=stdout)
        if self.isNodeHealthy():
            print("%-20s: %s\n" % ("node_state", DbClusterStatus.OM_NODE_STATUS_NORMAL), file=stdout)
        else:
            print("%-20s: %s\n" % ("node_state", DbClusterStatus.OM_NODE_STATUS_ABNORMAL), file=stdout)

        if not showDetail:
            return

        # coordinator status
        for inst in self.coordinators:
            # get the instance info
            g_instanceInfo = None
            for instInfo in dbNode.coordinators:
                if instInfo.instanceId == inst.instanceId:
                    g_instanceInfo = instInfo
                    break
            if g_instanceInfo is None:
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51620"] % "CN")
            # construct the instance name
            instName = "cn_%s" % g_instanceInfo.instanceId
            # print CN instance information
            self.print_cn_and_gtm_instance_info(inst, stdout, instName, g_instanceInfo)
        for inst in self.gtms:
            # get the instance info
            g_instanceInfo = None
            for instInfo in dbNode.gtms:
                if instInfo.instanceId == inst.instanceId:
                    g_instanceInfo = instInfo
                    break
            if g_instanceInfo is None:
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51620"] % "GTM")
            # construct the instance name
            instName = "gtm_%s" % g_instanceInfo.instanceId
            # print gtm instance information
            DbNodeStatus.print_cn_and_gtm_instance_info(inst, stdout, instName, g_instanceInfo, inst_type="gtm")
        self.outputCnDnStatus(dbNode, stdout)

    @staticmethod
    def print_cn_and_gtm_instance_info(inst, stdout, inst_name, instance_info, inst_type="cn"):
        """
        function: print cn and gtm instance information
        input: inst, stdout, inst_name, instance_info, inst_type
        output: NA
        """
        if inst_type == "cn":
            print("Coordinator", file=stdout)
        if inst_type == "gtm":
            print("GTM", file=stdout)
        print("%-20s: %d" % ("    node", inst.nodeId), file=stdout)
        print("%-20s: %s" % ("    instance_name", inst_name), file=stdout)
        print("%-20s: %s" % ("    listen_IP", instance_info.listenIps), file=stdout)
        print("%-20s: %d" % ("    port", instance_info.port), file=stdout)
        print("%-20s: %s" % ("    data_path", inst.datadir), file=stdout)
        print("%-20s: %s" % ("    instance_state", inst.status), file=stdout)
        if inst_type == "gtm":
            print("%-20s: %s" % ("    conn_state", inst.connStatus), file=stdout)
            print("%-20s: %s" % ("    reason", inst.reason), file=stdout)
        print("", file=stdout)

    def outputCnDnStatus(self, dbNode, stdout):
        """
        """
        i = 1
        global g_instanceInfo
        for inst in self.datanodes:
            # get the instance info
            g_instanceInfo = None
            for instInfo in dbNode.datanodes:
                if instInfo.instanceId == inst.instanceId:
                    g_instanceInfo = instInfo
                    break
            if (g_instanceInfo is None):
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51620"] % "DN")
            # construct the instance name
            peerInsts = g_clusterInfo.getPeerInstance(g_instanceInfo)
            if (g_clusterInfo.isMasterStandbyCluster() or g_clusterInfo.isMasterStandbyMultiAZCluster()) \
                    and len(peerInsts) != 2 and len(peerInsts) != 1:
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51603"] % g_instanceInfo.datadir)

            if g_clusterInfo.isSinglePrimaryMultiStandbyCluster() or g_clusterInfo.isSingleInstCluster():
                instName = ClusterInstanceConfig.setReplConninfoForSinglePrimaryMultiStandbyCluster(
                    g_instanceInfo, peerInsts)[1]
            elif g_clusterInfo.isMasterStandbyCluster() or g_clusterInfo.isMasterStandbyMultiAZCluster():
                instName = ClusterInstanceConfig.get_pgxc_node_name(g_instanceInfo, peerInsts)
            else:
                instName = "dn_%d" % inst.instanceId

            # print DN instance information
            print("Datanode%d" % i, file=stdout)
            print("%-20s: %d" % ("    node", inst.nodeId), file=stdout)
            print("%-20s: %s" % ("    instance_name", instName), file=stdout)
            print("%-20s: %s" % ("    listen_IP", g_instanceInfo.listenIps), file=stdout)
            print("%-20s: %s" % ("    HA_IP", g_instanceInfo.haIps), file=stdout)
            print("%-20s: %d" % ("    port", g_instanceInfo.port), file=stdout)
            print("%-20s: %s" % ("    data_path", inst.datadir), file=stdout)
            print("%-20s: %s" % ("    instance_state", inst.status), file=stdout)
            print("%-20s: %s" % ("    HA_state", inst.haStatus), file=stdout)
            print("%-20s: %s" % ("    reason", inst.reason), file=stdout)
            print("", file=stdout)

            i += 1
        # print fenced UDF status
        for inst in self.fencedUDFs:
            print("Fenced UDF", file=stdout)
            print("%-20s: %d" % ("    node", inst.nodeId), file=stdout)
            print("%-20s: %s" % ("    listen_IP", dbNode.backIps[0]), file=stdout)
            print("%-20s: %s" % ("    instance_state", inst.status), file=stdout)


class DbClusterStatus():
    """
    classdocs
    """

    OM_STATUS_FILE = "gs_om_status.dat"
    OM_STATUS_KEEPTIME = 1800
    ###################################################################
    # OM status
    ###################################################################
    OM_STATUS_NORMAL = "Normal"
    OM_STATUS_ABNORMAL = "Abnormal"
    OM_STATUS_STARTING = "Starting"
    OM_STATUS_UPGRADE = "Upgrade"
    OM_STATUS_DILATATION = "Dilatation"
    OM_STATUS_REPLACE = "Replace"
    OM_STATUS_REDISTIRBUTE = "Redistributing"

    ###################################################################
    # node status
    ###################################################################
    OM_NODE_STATUS_NORMAL = "Normal"
    OM_NODE_STATUS_ABNORMAL = "Abnormal"

    ###################################################################
    # cluster status
    ###################################################################
    CLUSTER_STATUS_NORMAL = "Normal"
    CLUSTER_STATUS_STARTING = "Starting"
    CLUSTER_STATUS_ABNORMAL = "Abnormal"
    CLUSTER_STATUS_PENDING = "Pending"
    CLUSTER_STATUS_DEGRADED = "Degraded"
    CLUSTER_STATUS_MAP = {
        "Normal": "Normal",
        "Redistributing": "Redistributing",
        "Repair": "Abnormal",
        "Starting": "Starting",
        "Degraded": "Degraded",
        "Unknown": "Abnormal"
    }

    ###################################################################
    # instance role
    ###################################################################
    INSTANCE_TYPE_GTM = "GTM"
    INSTANCE_TYPE_DATANODE = "Datanode"
    INSTANCE_TYPE_COORDINATOR = "Coordinator"
    INSTANCE_TYPE_CMSERVER = "CMServer"
    INSTANCE_TYPE_FENCED_UDF = "Fenced UDF"
    INSTANCE_TYPE_ETCD = "ETCD"

    ###################################################################
    # instance status
    ###################################################################
    INSTANCE_STATUS_NORMAL = "Normal"
    INSTANCE_STATUS_PRIMARY = "Primary"
    INSTANCE_STATUS_STANDBY = "Standby"
    INSTANCE_STATUS_ABNORMAL = "Abnormal"
    INSTANCE_STATUS_DOWN = "Down"
    INSTANCE_STATUS_DUMMY = "Secondary"
    INSTANCE_STATUS_DELETED = "Deleted"
    INSTANCE_STATUS_STATElEADER = "StateLeader"
    INSTANCE_STATUS_STATEfOLLOWER = "StateFollower"
    INSTANCE_STATUS_MAP = {
        # When instance run stand-alone,it's 'Normal'
        "Normal": "Primary",
        "Unnormal": "Abnormal",
        "Primary": "Primary",
        "Standby": "Standby",
        "Secondary": "Secondary",
        "Pending": "Abnormal",
        "Down": "Down",
        "Unknown": "Abnormal"
    }

    ###################################################################
    # ha status
    ###################################################################
    HA_STATUS_NORMAL = "Normal"
    HA_STATUS_BUILD = "Building"
    HA_STATUS_START = "Starting"
    HA_STATUS_ABNORMAL = "Abnormal"
    HA_STATUS_MAP = {
        "Normal": "Normal",
        "Building": "Building",
        "Need repair": "Abnormal",
        "Starting": "Starting",
        "Demoting": "Demoting",
        "Promoting": "Promoting",
        "Waiting": "Abnormal",
        "Unknown": "Abnormal",
        "Catchup": "Normal"
    }

    ###################################################################
    # connection status
    ###################################################################
    CONN_STATUS_NORMAL = "Normal"
    CONN_STATUS_ABNORMAL = "Abnormal"
    CONN_STATUS_MAP = {
        "Connection ok": "Normal",
        "Connection bad": "Abnormal",
        "Connection started": "Abnormal",
        "Connection made": "Abnormal",
        "Connection awaiting response": "Abnormal",
        "Connection authentication ok": "Abnormal",
        "Connection prepare SSL": "Abnormal",
        "Connection needed": "Abnormal",
        "Unknown": "Abnormal"
    }

    ###################################################################
    # data status
    ###################################################################
    DATA_STATUS_SYNC = "Sync"
    DATA_STATUS_ASYNC = "Async"
    DATA_STATUS_Unknown = "Unknown"
    DATA_STATUS_MAP = {
        "Async": "Async",
        "Sync": "Sync",
        "Most available": "Standby Down",
        "Potential": "Potential",
        "Unknown": "Unknown"
    }

    def __init__(self):
        """
        Constructor
        """
        self.dbNodes = []
        self.clusterStatus = ""
        self.redistributing = ""
        self.clusterStatusDetail = ""
        self.__curNode = None
        self.__curInstance = None
        self.balanced = ""

    def __str__(self):
        """
        """
        status_list = ["clusterStatus=%s,redistributing=%s,clusterStatusDetail=%s,balanced=%s" %
                       (self.clusterStatus, self.redistributing, self.clusterStatusDetail, self.balanced)]

        for dbNode in self.dbNodes:
            status_list.append("\n%s" % str(dbNode))

        ret_str = "".join(status_list)
        return ret_str

    @staticmethod
    def getOmStatus():
        """
        function : Get om status from file
        input : String
        output : NA
        """
        # check status file
        statFile = os.path.join(DefaultValue.getTmpDirFromEnv(), DbClusterStatus.OM_STATUS_FILE)
        if (not os.path.isfile(statFile)):
            return DbClusterStatus.OM_STATUS_NORMAL
        # get om status from file
        status = DbClusterStatus.OM_STATUS_NORMAL
        try:
            modifiedTime = os.stat(statFile).st_mtime
            deltaTime = time.time() - modifiedTime
            if deltaTime <= DbClusterStatus.OM_STATUS_KEEPTIME:
                status = DefaultValue.readFileLine(statFile)
        except Exception as ex:
            raise Exception(str(ex))

        return status

    def getDbNodeStatusById(self, nodeId):
        """
        function : Get node status by node id
        input : nodeId
        output : dbNode
        """
        for dbNode in self.dbNodes:
            if (dbNode.id == nodeId):
                return dbNode

        return None

    def getInstanceStatusById(self, instId):
        """
        function : Get instance by its id
        input : instId
        output : dbInst
        """
        for dbNode in self.dbNodes:
            # get CN, DN and gtm instance
            instances = dbNode.coordinators + dbNode.gtms + dbNode.datanodes
            # get instance by its id
            for dbInst in instances:
                if (dbInst.instanceId == instId):
                    return dbInst

        return None

    def isAllHealthy(self, cluster_normal_status=None):
        """
        function : Check if cluster is healthy
        input : cluster_normal_status
        output : boolean
        """
        if (cluster_normal_status is None):
            cluster_normal_status = [DbClusterStatus.CLUSTER_STATUS_NORMAL]

        if (self.clusterStatus not in cluster_normal_status):
            return False

        for dbNode in self.dbNodes:
            if (not dbNode.isNodeHealthy()):
                return False

        return True

    def getClusterStatusReport(self):
        """
        function : Get the health report of cluster
        input : NA
        output : clusterRep
        """
        clusterRep = StatusReport()
        for dbNode in self.dbNodes:
            nodeRep = dbNode.getNodeStatusReport()
            clusterRep.nodeCount += 1
            clusterRep.cooNormal += nodeRep.cooNormal
            clusterRep.cooAbnormal += nodeRep.cooAbnormal
            clusterRep.gtmPrimary += nodeRep.gtmPrimary
            clusterRep.gtmStandby += nodeRep.gtmStandby
            clusterRep.gtmAbnormal += nodeRep.gtmAbnormal
            clusterRep.gtmDown += nodeRep.gtmDown
            clusterRep.dnPrimary += nodeRep.dnPrimary
            clusterRep.dnStandby += nodeRep.dnStandby
            clusterRep.dnDummy += nodeRep.dnDummy
            clusterRep.dnBuild += nodeRep.dnBuild
            clusterRep.dnAbnormal += nodeRep.dnAbnormal
            clusterRep.dnDown += nodeRep.dnDown
            clusterRep.fencedUDFNormal += nodeRep.fencedUDFNormal
            clusterRep.fencedUDFAbnormal += nodeRep.fencedUDFAbnormal

        return clusterRep

    def getClusterStauts(self):
        """
        function : Get the status of cluster for Healthcheck
        input : user
        output : statusInfo
        """
        clusterStat = DbClusterStatus.getOmStatus()
        redistributing_state = self.redistributing
        balanced_state = self.balanced
        if (clusterStat == DbClusterStatus.OM_STATUS_NORMAL):
            clusterStat = self.clusterStatus
        statusInfo = "        %s: %s\n" % ("cluster_state".ljust(22), clusterStat)
        statusInfo += "        %s: %s\n" % ("redistributing".ljust(22), redistributing_state)
        statusInfo += "        %s: %s\n" % ("balanced".ljust(22), balanced_state)
        for dbNode in self.dbNodes:
            if (dbNode.isNodeHealthy()):
                statusInfo += "        %s: %s \n" % (dbNode.name.ljust(22), DbClusterStatus.OM_NODE_STATUS_NORMAL)
            else:
                statusInfo += "        %s: %s \n" % (dbNode.name.ljust(22), DbClusterStatus.OM_NODE_STATUS_ABNORMAL)
        return statusInfo

    def initFromFile(self, filePath, isExpandScene=False):
        """
        function : Init from status file
        input : filePath
        output : NA
        """
        # check file path
        if (not os.path.exists(filePath)):
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"] % "status file" + " Path: %s." % filePath)
        if (not os.path.isfile(filePath)):
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50210"] % "status file" + " Path: %s." % filePath)

        try:
            with open(filePath, "r") as fp:
                for line in fp.readlines():
                    line = line.strip()
                    if (line == ""):
                        continue

                    strList = line.split(":")
                    if (len(strList) != 2):
                        continue

                    self.__fillField(strList[0].strip(), strList[1].strip(), isExpandScene)
        except Exception as e:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50204"] % "status file" + " Error: \n%s" % str(e))

    def __fillField(self, field, value, isExpandScene):
        """
        function : Fill field
        input : field, value
        output : NA
        """
        if (field == "cluster_state"):
            status = DbClusterStatus.CLUSTER_STATUS_MAP.get(value)
            self.clusterStatus = DbClusterStatus.CLUSTER_STATUS_ABNORMAL if status is None else status
            self.clusterStatusDetail = value
        elif (field == "redistributing"):
            self.redistributing = value
        elif (field == "balanced"):
            self.balanced = value
        elif (field == "node"):
            if (not value.isdigit()):
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51633"] % "node id")
            newId = int(value)
            if (self.__curNode is None or self.__curNode.id != newId):
                self.__curNode = DbNodeStatus(newId)
                self.dbNodes.append(self.__curNode)
        elif (field == "node_name"):
            self.__curNode.name = value
        elif (field == "instance_id"):
            if (not value.isdigit()):
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51633"] % "instance id")
            self.__curInstance = DbInstanceStatus(self.__curNode.id, int(value))
        elif (field == "data_path"):
            self.__curInstance.datadir = value
        elif (field == "type"):
            self.__fillFieldByType(value)
        elif (field == "instance_state"):
            status = DbClusterStatus.INSTANCE_STATUS_MAP.get(value)
            self.__curInstance.status = DbClusterStatus.INSTANCE_STATUS_ABNORMAL if status is None else status
        elif (field == "state"):
            self.__curInstance.status = self.__getFieldState(value, isExpandScene)
        else:
            self.__fillFiledHAInfo(field, value)

    def __fillFiledHAInfo(self, field, value):
        """
        """
        if (field == "HA_state"):
            haStatus = DbClusterStatus.HA_STATUS_MAP.get(value)
            self.__curInstance.haStatus = DbClusterStatus.HA_STATUS_ABNORMAL if haStatus is None else haStatus
        elif (field == "con_state"):
            connStatus = DbClusterStatus.CONN_STATUS_MAP.get(value)
            self.__curInstance.connStatus = DbClusterStatus.CONN_STATUS_ABNORMAL if connStatus is None else connStatus
        elif (field == "static_connections"):
            connStatus = DbClusterStatus.CONN_STATUS_MAP.get(value)
            self.__curInstance.connStatus = DbClusterStatus.CONN_STATUS_ABNORMAL if connStatus is None else connStatus
        elif (field == "sync_state"):
            dataStatus = DbClusterStatus.DATA_STATUS_MAP.get(value)
            self.__curInstance.syncStatus = DbClusterStatus.DATA_STATUS_Unknown if dataStatus is None else dataStatus
        elif (field == "reason"):
            self.__curInstance.reason = value

    def __fillFieldByType(self, value):
        """
        """
        if (value == DbClusterStatus.INSTANCE_TYPE_FENCED_UDF):
            self.__curInstance = DbInstanceStatus(self.__curNode.id)
            self.__curNode.fencedUDFs.append(self.__curInstance)
        self.__curInstance.type = value
        if (value == DbClusterStatus.INSTANCE_TYPE_GTM):
            self.__curNode.gtms.append(self.__curInstance)
        elif (value == DbClusterStatus.INSTANCE_TYPE_DATANODE):
            self.__curNode.datanodes.append(self.__curInstance)
        elif (value == DbClusterStatus.INSTANCE_TYPE_COORDINATOR):
            self.__curNode.coordinators.append(self.__curInstance)
        elif (value == DbClusterStatus.INSTANCE_TYPE_CMSERVER):
            self.__curNode.cmservers.append(self.__curInstance)
        elif (value == DbClusterStatus.INSTANCE_TYPE_ETCD):
            self.__curNode.etcds.append(self.__curInstance)

    def __getFieldState(self, value, isExpandScene):
        """
        function : Get field [state]
        input : value, isExpandScene
        output : status
        """
        if (value == DbClusterStatus.INSTANCE_STATUS_NORMAL or
                value == DbClusterStatus.INSTANCE_STATUS_STATElEADER or
                value == DbClusterStatus.INSTANCE_STATUS_STATEfOLLOWER):
            return value
        elif (value == DbClusterStatus.INSTANCE_STATUS_DELETED):
            global g_deletedCNId
            g_deletedCNId.append(self.__curInstance.instanceId)
            return DbClusterStatus.INSTANCE_STATUS_ABNORMAL
        else:
            if (isExpandScene and self.__curInstance.type == DbClusterStatus.INSTANCE_TYPE_COORDINATOR):
                self.clusterStatus = DbClusterStatus.CLUSTER_STATUS_ABNORMAL
            return DbClusterStatus.INSTANCE_STATUS_ABNORMAL

    def parse_cluster_status(self):
        """
        :param clusterStatus:
        :return:
        """

        def parse_list(input_list):
            if not input_list:
                return []
            if isinstance(input_list[0], DbNodeStatus):
                return parse_db_list(input_list)
            elif isinstance(input_list[0], DbInstanceStatus):
                return parse_inst_list(input_list)
            else:
                return input_list

        def parse_inst_list(inst_list):
            return [i.__dict__ for i in inst_list]

        def parse_db_list(db_list):
            new_db_list = [i.__dict__ for i in db_list]
            for db_item in new_db_list:
                for db_key in db_item:
                    if isinstance(db_item[db_key], list):
                        new_inst_list = parse_list(db_item[db_key])
                        db_item[db_key] = new_inst_list

            return new_db_list

        cluster_dict = self.__dict__
        for cluster_item in cluster_dict:
            if isinstance(cluster_dict[cluster_item], list):
                new_list = parse_list(cluster_dict[cluster_item])
                cluster_dict[cluster_item] = new_list

        if '_DbClusterStatus__curNode' in cluster_dict and \
                cluster_dict['_DbClusterStatus__curNode']:
            cluster_dict['_DbClusterStatus__curNode'] = \
                cluster_dict['_DbClusterStatus__curNode'].__dict__

        if '_DbClusterStatus__curInstance' in cluster_dict and \
                cluster_dict['_DbClusterStatus__curInstance']:
            cluster_dict['_DbClusterStatus__curInstance'] = \
                cluster_dict['_DbClusterStatus__curInstance'].__dict__

        cluster_dict['deletedCN'] = g_deletedCNId

        return cluster_dict
