# -*- coding: UTF-8 -*-
from cbb.frame.ruleConfig import common
from cbb.frame.context import contextUtil
from cbb.frame.cli import cliUtil
from java.lang import Exception as JException
from checkitems.evaluate_pre_check import link_pre_check
from service.resourceService import ResourceXmlUtil
from com.huawei.ism.tool.obase.exception import ToolException

from cbb.frame.adapter.resourceService import ResrouceFactory

CTRL_A_PLAN = ["A", "C"]  # a平面控制器ID
CTRL_B_PLAN = ["B", "D"]  # b平面控制器ID


def execute(context):
    # 执行升级前检查
    flag, err_msg, cli_ret = link_pre_check(context)
    log = common.getLogger(contextUtil.getLogger(context), __file__)
    lang = context.get("lang")
    if flag:
        return flag, err_msg, cli_ret
    compatibility_check = HostCompatibilityCheck(context)
    try:
        flag = compatibility_check.execute()
    except Exception as e:
        log.logException(e)
        return (
            False,
            common.getMsg(lang, "query.result.abnormal"),
            compatibility_check.allCliRet,
        )
    return flag, compatibility_check.error_msg, compatibility_check.allCliRet


def HandleException(fn):
    def inner(self):
        retrytimes = 3
        while retrytimes > 0:
            try:
                fn(self)
                return True
            except Exception, e:
                self.logger.logInfo("script exception:%s" % unicode(e))
                return False
            except JException, e:
                self.logger.logInfo("java excption:%s" % unicode(e))
                retrytimes = retrytimes - 1
        return False

    return inner


# 主机兼容性检查类
class HostCompatibilityCheck:
    def __init__(self, dataDict):
        self.error_msg = ""
        self.dataDict = dataDict
        self.results = []
        self.allCliRet = ""
        self.hosts = []
        self.errHosts = []
        self.allHBAInfo = {}

    @HandleException
    def queryHosts(self):
        # /rest/[SN]/host
        # filter  "ISADD2HOSTGROUP"为true,"INITIATORNUM">0

        self.dataDict["ALL_RESOURCES"] = ResourceXmlUtil.parse()
        resrouceFactory = ResrouceFactory(self.dataDict)
        allHosts = resrouceFactory.executeInterface("getHost")
        self.allCliRet += ShareData.formatRestData("getHost", allHosts)
        self.logger.logInfo("host num=%s" % len(allHosts))
        if allHosts:
            for host in allHosts:
                initiatorNum = int(host.get("INITIATORNUM", 1))
                if host["ISADD2HOSTGROUP"] == "true" and initiatorNum > 0:
                    self.hosts.append(host)

    # 检查执行入口
    def execute(self):
        self.logger = common.getLogger(
            contextUtil.getLogger(self.dataDict), __file__
        )
        self.lang = contextUtil.getLang(self.dataDict)
        try:
            # 检查当前版本 支持V3R6C10及之后的版本，通过配置文件控制是否有该检查项
            # 主机查询失败，未检查
            ok = self.queryHosts()
            if not ok:
                self.error_msg = common.getMsg(
                    self.lang, "query.result.abnormal"
                )
                return False

            # 没有映射的主机，检查通过
            if len(self.hosts) == 0:
                self.logger.logInfo("no mapped host,the check is passed.")
                return True

            for host in self.hosts:
                instance = SingleHostCompatibilityCheck(self.dataDict, host)
                result = instance.execute()
                ret = dict(
                    hostId=host["ID"],
                    status=result.status,
                    message=result.message,
                    cliRet="\n".join(instance.allCliRet),
                )
                self.results.append(ret)
            return self.mergeResult()
        except (ToolException, Exception) as exception:
            self.logger.logException(exception)
            error_id = "login.clis.overrun.info" \
                if "PwdCLIOverrunException" in str(exception) else "query.result.abnormal"
            self.error_msg = common.getMsg(self.lang, error_id)
            return False
        finally:
            self.dataDict["ALL_RESOURCES"] = ""

    def mergeResult(self):
        allHostCliRet = map(lambda x: x["cliRet"], self.results)
        allHostCliRet = filter(lambda x: x is not None, allHostCliRet)
        self.allCliRet += "\n".join(allHostCliRet)

        # 将结果按状态分类
        resultDict = {}
        hostIdList = []
        for status in CheckStatus.getAllItems():
            resultDict[status] = []
        for result in self.results:
            resultDict[result["status"]].append(result)
            if result["status"] == CheckStatus.NOPASS:
                hostIdList.append(result["hostId"])

        # 设置状态
        cannotEvaluResultNum = map(
            lambda status: len(resultDict[status]),
            CheckStatus.getCannotEvaluItems(),
        )
        if len(resultDict[CheckStatus.NOPASS]) > 0:
            status = False
        elif len(resultDict[CheckStatus.NO_ONLINE_INITIATOR]) > 0:
            status = False
        elif reduce(lambda x, y: x + y, cannotEvaluResultNum) > 0:
            status = cliUtil.RESULT_NOCHECK
        else:
            status = True

        # 设置提示信息
        message = ""
        if not status:
            for key in CheckStatus.getAllNotPassItems():
                if len(resultDict[key]) > 0:
                    hostIds = map(lambda x: x["hostId"], resultDict[key])
                    hostIdStr = self.formatOutput(hostIds)
                    message += common.getMsg(
                        self.lang, "check.frontlink.result.%s" % key, hostIdStr
                    )
        self.error_msg = message
        return status

    # 将主机按每5个换行显示
    def formatOutput(self, results):
        ret = ""
        for i in range(len(results)):
            if i % 5 == 0 and i / 5 != 0:
                ret += "\n    "
            ret += results[i]
            ret += ", "
        ret = ret[:-2] if ret.endswith(", ") else ret
        return ret


# 单个主机兼容性检查类
class SingleHostCompatibilityCheck:
    def __init__(self, dataDict, host):
        self.dataDict = dataDict
        self.hostId = host["ID"]
        self.host = host
        self.lang = contextUtil.getLang(dataDict)
        self.logger = common.getLogger(
            contextUtil.getLogger(self.dataDict), __file__
        )
        self.allCliRet = [
            "\n=========the host(%s) origin info start=========" % self.hostId
        ]

    def execute(self):
        try:
            context = dict(
                host=self.host,
                dataDict=self.dataDict,
                allCliRet=self.allCliRet,
            )
            flow = LunNumCheck(self.hostId)
            while not isinstance(flow, FlowEnd):
                self.logger.logInfo("flow start, step=%s" % flow.__class__)
                flow.setContext(context)
                ok = flow.queryInfo()
                if not ok:
                    self.logger.logInfo("quey info failed.")
                    flow = FlowEnd(CheckStatus.QUERY_INFO_ERROR)
                    break
                flow = flow.next()
                self.logger.logInfo("flow next, step=%s" % flow.__class__)
            return flow
        except Exception, e:
            self.logger.logException(e)
            self.logger.logInfo("single host compatibility:%s" % unicode(e))
            return FlowEnd(CheckStatus.QUERY_INFO_ERROR)
        finally:
            self.allCliRet.append(
                "\n=========the host(%s) origin info end========="
                % self.hostId
            )


class FlowEnd:
    def __init__(self, status, message="", data=None):
        self.status = status
        self.message = message
        self.data = data


class CheckStatus:
    PASS = "pass"
    NOPASS = "nopass"
    QUERY_INFO_ERROR = "queryInfoError"
    NO_ONLINE_INITIATOR = "noOnlineInitiator"
    NOSUPPORT_IBLINK = "noSupportIbLink"

    @staticmethod
    def getAllItems():
        return [
            "pass",
            "nopass",
            "queryInfoError",
            "noOnlineInitiator",
            "noSupportIbLink",
        ]

    @staticmethod
    def getAllNotPassItems():
        return [
            "nopass",
            "queryInfoError",
            "noOnlineInitiator",
            "noSupportIbLink",
        ]

    @staticmethod
    def getCannotEvaluItems():
        return ["queryInfoError", "noSupportIbLink"]


class ShareData:
    def __init__(self):
        self.context = {}
        self.dataDict = {}

    def setContext(self, context):
        self.context = context
        self.host = context["host"]
        self.dataDict = context["dataDict"]
        self.logger = common.getLogger(
            contextUtil.getLogger(self.dataDict), __file__
        )
        self.lang = contextUtil.getLang(self.dataDict)
        self.resrouceFactory = ResrouceFactory(self.dataDict)

    def appendCliRet(self, cliRet):
        self.context["allCliRet"].append(cliRet)

    def appendRestRet(self, uri, datas):
        dataStr = self.formatRestData(uri, datas)
        self.context["allCliRet"].append(dataStr)

    @staticmethod
    def formatRestData(uri, datas):
        dataStr = ""
        if datas:
            if not isinstance(datas, list):
                return "\nRest Uri:%s\nResponse:[%s]" % (uri, str(datas))
            for data in datas:
                dataStr += str(data)
                dataStr += "\n"
        return "\nRest Uri:%s\nResponse:[%s]" % (uri, dataStr)


class InitiatorCheck(ShareData):
    def __init__(self, hostId):
        ShareData.__init__(self)
        self.hostId = hostId
        self.initators = []
        self.noRunningInitators = []

    @HandleException
    def queryInfo(self):
        """查询在线状态的启动器"""
        fcInitiators = self.resrouceFactory.executeInterface(
            "getFcInitiator", self.hostId
        )
        self.initators.extend(fcInitiators)
        self.appendRestRet("getFcInitiator", fcInitiators)

        iscsiInitiatorsRet = self.resrouceFactory.executeInterface(
            "getIscsiInitiator", self.hostId
        )
        self.initators.extend(iscsiInitiatorsRet)
        self.appendRestRet("getIscsiInitiator", iscsiInitiatorsRet)

        ibInitiatorsRet = self.resrouceFactory.executeInterface(
            "getIbInitiator", self.hostId
        )
        self.initators.extend(ibInitiatorsRet)
        self.appendRestRet("getIbInitiator", ibInitiatorsRet)
        return True

    def next(self):
        # 无启动器，检查通过
        if self.initators is None or len(self.initators) == 0:
            return FlowEnd(CheckStatus.PASS)
        else:
            self._filterRunningInitators()
            if len(self.noRunningInitators) > 0:
                return FlowEnd(CheckStatus.NO_ONLINE_INITIATOR)
            return HostLinkCheck(self.hostId, self.initators)

    def _filterRunningInitators(self):
        for initator in self.initators:
            if int(initator.get("RUNNINGSTATUS")) != 27:
                self.noRunningInitators.append(initator)
        return


class LunNumCheck(ShareData):
    def __init__(self, hostId):
        ShareData.__init__(self)
        self.hostId = hostId
        self.lunNum = 0

    @HandleException
    def queryInfo(self):
        lunNumRet = self.resrouceFactory.executeInterface(
            "getHostLunCount", self.hostId
        )
        self.lunNum = int(lunNumRet.get("COUNT", 0))
        self.appendRestRet("getHostLunCount", lunNumRet)
        return True

    def next(self):
        if self.lunNum == 0:
            return FlowEnd(CheckStatus.PASS)
        else:
            return InitiatorCheck(self.hostId)


class HostLinkCheck(ShareData):
    def __init__(self, hostId, initiators):
        ShareData.__init__(self)
        self.hostId = hostId
        self.hostLinks = None
        self.hostLinksDict = {}
        self.initiators = initiators

    @HandleException
    def queryInfo(self):
        datas = self.resrouceFactory.executeInterface(
            "getHostLink", self.hostId
        )
        self.appendRestRet("getHostLink", datas)
        if datas and len(datas) != 0:
            self.hostLinks = datas
        return True

    def _checkRules(self, ctrlIdDict):
        ctrlIds = []
        for engine, ctrlList in ctrlIdDict.items():
            ctrlIds.extend(ctrlList)
        alist = list(set(ctrlIds).intersection(set(CTRL_A_PLAN)))
        blist = list(set(ctrlIds).intersection(set(CTRL_B_PLAN)))
        if len(alist) == 0 or len(blist) == 0:
            return False
        return True

    def next(self):
        """
        获取每条信息的CTRL_ID字段，获取到的信息格式为，XA、XB、XC、XD（X为引擎编号，可为0~7）。
        所有信息都获取完后，以引擎号进行分组，分别判断是否同时包含A、B，或者A、D，或者C、B，
        或者C、D，如果所有引擎链路均满足上述规则，说明主机到阵列路径冗余，否则不冗余。
        """
        # 检查是否存在IB启动器，而不存在IB启动器的链路，提示未检查，请手动检查。

        if not self.checkIBLink():
            self.logger.logInfo("exist ib initiator but can not get ib link.")
            return FlowEnd(CheckStatus.NOSUPPORT_IBLINK)

        if self.hostLinks is None or len(self.hostLinks) == 0:
            return FlowEnd(CheckStatus.NOPASS)
        for hostLink in self.hostLinks:
            # 只判断在线的链路
            if int(hostLink["RUNNINGSTATUS"]) != 27:
                continue
            initiatorType = hostLink.get("INITIATOR_TYPE")
            ctrlId = hostLink.get("CTRL_ID")
            parentId = hostLink.get("PARENTID")
            hostLinkList = self.hostLinksDict.get(initiatorType, [])
            if hostLinkList == []:
                self.hostLinksDict[initiatorType] = hostLinkList
            hostLinkList.append(ctrlId)
        self.logger.logInfo(
            "HostLinkCheck parse host link,hostLinksDict:%s"
            % str(self.hostLinksDict)
        )

        ctrlIdDict = {}
        for typeKey, ctrlIdList in self.hostLinksDict.items():
            ctrlIdList = list(set(ctrlIdList))
            for ctrlId in ctrlIdList:
                engine = ctrlId[0]
                ctrl = ctrlId[1]
                idList = ctrlIdDict.get(engine, [])
                if len(idList) == 0:
                    ctrlIdDict[engine] = idList
                idList.append(ctrl)
            ok = self._checkRules(ctrlIdDict)
            if not ok:
                self.logger.logInfo(
                    "Host(%s) have not redundant link." % str(self.hostId)
                )
                return FlowEnd(CheckStatus.NOPASS)
        self.logger.logInfo("Host(%s) have redundant link." % str(self.hostId))
        return FlowEnd(CheckStatus.PASS)

    def checkIBLink(self):
        existIbInitiator = False
        existIbLink = False
        for oneInitiator in self.initiators:
            if str(oneInitiator.get("TYPE")) == "16499":
                existIbInitiator = True
        if self.hostLinks:
            for oneLink in self.hostLinks:
                if str(oneLink.get("INITIATOR_TYPE")) == "16499":
                    existIbLink = True
        if existIbInitiator and not existIbLink:
            return False
        return True
