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

    sys.path.append(os.path.split(os.path.realpath(__file__))[0] + "/../../../")
    from gspylib.common.ErrorCode import ErrorCode
    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))
parse_xml = ParseXml()


class dbNodeInfo():
    """
    Instance info on a node
    """

    def __init__(self, nodeId=0, name=""):
        """
        Constructor
        """
        # node id
        self.id = nodeId
        # node name
        self.name = name
        self.backIps = []
        self.virtualIp = []
        self.sshIps = []
        # instance number
        self.cmsNum = 0
        self.cooNum = 0
        self.dataNum = 0
        self.gtmNum = 0
        self.etcdNum = 0
        # cm_servers instance
        self.cmservers = []
        # cn instance
        self.coordinators = []
        # dn instance
        self.datanodes = []
        # gtm instance
        self.gtms = []
        # cm_agent instance
        self.cmagents = []
        # etcd instance
        self.etcds = []
        # cm_server/cm_agent data directory
        self.cmDataDir = ""
        self.dummyStandbyBasePort = 0
        self.masterBasePorts = [const.MASTER_BASEPORT_CMS, const.MASTER_BASEPORT_GTM, const.MASTER_BASEPORT_COO,
                                const.MASTER_BASEPORT_DATA, const.MASTER_BASEPORT_ETCD, const.MASTER_BASEPORT_CMAGENT]
        self.standbyBasePorts = [const.STANDBY_BASEPORT_CMS, const.STANDBY_BASEPORT_GTM, const.STANDBY_BASEPORT_COO,
                                 const.STANDBY_BASEPORT_DATA, const.STANDBY_BASEPORT_ETCD,
                                 const.STANDBY_BASEPORT_CMAGENT]
        # azName
        self.azName = ""
        self.azPriority = 1
        self.nodeType = ""

    def __cmp__(self, target):
        """
        Type compare
        """
        if type(self) != type(target):
            return 1
        if not isinstance(target, dbNodeInfo):
            return 1
        if not hasattr(target, "id"):
            return 1
        else:
            return self.id - target.id

    def __str__(self):
        """
        function : Construct a printable string representation of a dbNodeInfo
        input : NA
        output : String
        """
        ret_str_list = ["HostName=%s,backIps=%s,azName=%s,azPriority=%s" %
                        (self.name, self.backIps, self.azName, self.azPriority)]
        # cm_server instance information
        for cmsInst in self.cmservers:
            ret_str_list.append("\n%s" % str(cmsInst))
        # cm_agent instance information
        for cmaInst in self.cmagents:
            ret_str_list.append("\n%s" % str(cmaInst))
        # gtm instance information
        for gtmInst in self.gtms:
            ret_str_list.append("\n%s" % str(gtmInst))
        # cn instance information
        for cooInst in self.coordinators:
            ret_str_list.append("\n%s" % str(cooInst))
        # dn instance information
        for dataInst in self.datanodes:
            ret_str_list.append("\n%s" % str(dataInst))
        # etcd instance information
        for dataInst in self.etcds:
            ret_str_list.append("\n%s" % str(dataInst))
        ret_str = "".join(ret_str_list)

        return ret_str

    def getDnNum(self, dntype):
        """
        function: get dn num
        input: dntype
        output:dn num
        """
        count = 0
        for dnInst in self.datanodes:
            if dnInst.instanceType == dntype:
                count += 1
        return count

    def appendInstance(self, instId, mirrorId, instRole, instanceType, listenIps=None,
                       haIps=None, datadir="", ssddir="", level=1, clusterType=const.CLUSTER_TYPE_MASTER_STANDBY):
        """
        function : Classify the instance of cmserver/gtm
        input : int,int,String,String
        output : NA
        """
        if not self.__checkDataDir(datadir, instRole):
            raise Exception(ErrorCode.GAUSS_516["GAUSS_51638"] %
                            self.name + " Data directory[%s] is conflicting." % datadir)

        dbInst = instanceInfo(instId, mirrorId)
        dbInst.hostname = self.name
        dbInst.datadir = os.path.realpath(datadir)
        dbInst.instanceType = instanceType
        dbInst.instanceRole = instRole
        if listenIps is not None:
            if len(listenIps) == 0:
                dbInst.listenIps = self.backIps[:]
            else:
                dbInst.listenIps = listenIps[:]

        if haIps is not None:
            if len(haIps) == 0:
                dbInst.haIps = self.backIps[:]
            else:
                dbInst.haIps = haIps[:]
        # cm_server
        if instRole == const.INSTANCE_ROLE_CMSERVER:
            dbInst.datadir = os.path.join(self.cmDataDir, "cm_server")
            dbInst.port = self.__assignNewInstancePort(self.cmservers, instRole, instanceType)
            dbInst.level = level
            dbInst.haPort = dbInst.port + 1
            self.cmservers.append(dbInst)
        # gtm
        elif instRole == const.INSTANCE_ROLE_GTM:
            dbInst.port = self.__assignNewInstancePort(self.gtms, instRole, instanceType)
            dbInst.haPort = dbInst.port + 1
            self.gtms.append(dbInst)
        # cn
        elif instRole == const.INSTANCE_ROLE_COODINATOR:
            dbInst.port = self.__assignNewInstancePort(self.coordinators, instRole, instanceType)
            dbInst.haPort = dbInst.port + 1
            dbInst.ssdDir = ssddir
            self.coordinators.append(dbInst)
        # dn
        elif instRole == const.INSTANCE_ROLE_DATANODE:
            dbInst.port = self.__assignNewInstancePort(self.datanodes, instRole, instanceType)
            dbInst.haPort = dbInst.port + 1
            dbInst.ssdDir = ssddir
            self.datanodes.append(dbInst)
        # cm_agent
        elif instRole == const.INSTANCE_ROLE_CMAGENT:
            dbInst.datadir = os.path.join(self.cmDataDir, "cm_agent")
            self.cmagents.append(dbInst)
        # etcd
        elif instRole == const.INSTANCE_ROLE_ETCD:
            dbInst.port = self.__assignNewInstancePort(self.etcds, instRole, instanceType)
            dbInst.haPort = self.__assignNewInstancePort(self.etcds, instRole, const.STANDBY_INSTANCE)
            self.etcds.append(dbInst)

    def __checkDataDir(self, datadir, instRole):
        """
        function : Check whether the instance path is the same as with the parameter of datadir
        input : String,String
        output : boolean
        """
        if datadir == "":
            return instRole == const.INSTANCE_ROLE_CMSERVER or instRole == const.INSTANCE_ROLE_CMAGENT
        parse_xml.checkPathVaild(datadir)
        # cm_server
        for cmsInst in self.cmservers:
            if cmsInst.datadir == datadir:
                return False
        # cn
        for cooInst in self.coordinators:
            if cooInst.datadir == datadir:
                return False
        # dn
        for dataInst in self.datanodes:
            if dataInst.datadir == datadir:
                return False
        # gtm
        for gtmInst in self.gtms:
            if gtmInst.datadir == datadir:
                return False
        # etcd
        for etcd in self.etcds:
            if etcd.datadir == datadir:
                return False
        # cm_agent
        for cmaInst in self.cmagents:
            if cmaInst.datadir == datadir:
                return False

        return True

    def __assignNewInstancePort(self, instList, instRole, instanceType):
        """
        function : Assign a new port for the instance
        input : [],String ,String
        output : int
        """
        port = 0
        # master instance
        if instanceType == const.MASTER_INSTANCE:
            port = self.masterBasePorts[instRole]
        # standby instance
        elif instanceType == const.STANDBY_INSTANCE:
            port = self.standbyBasePorts[instRole]
        # dn dummy standby instance
        elif instanceType == const.DUMMY_STANDBY_INSTANCE:
            port = self.dummyStandbyBasePort
        # cn and cm_agent instance
        elif instanceType == const.INSTANCE_TYPE_UNDEFINED:
            port = self.masterBasePorts[instRole]
            return port
        for inst in instList:
            if inst.instanceType == instanceType:
                port += 2
        return port

    def isComputeOnlyNode(self):
        return self.nodeType == const.NODE_TYPE_VW
