# -*- coding: UTF-8 -*-
from com.huawei.ism.tlv.lang import UnsignedInt32
from com.huawei.ism.exception import IsmException

from cbb.business.checkitems import patch_limit_maintenance_port
from cbb.frame.base.funcUtils import time_out_decorator
from common import resourceParse
from common.constant import *
from common.baseFactory import log, finishProcess, threadUp
from frame.context import contextUtil
from frame.rest.restUtil import Tlv2Rest, REST_CAN_NOT_EXECUTE
from frame.rest import restData
from frame.base import config
from service.patch_multi_service import PatchMultiPackagesManager
from service.func_utils import upload_timeout_callback
from cbb.business.operate.checkitems import check_non_array_ip
import traceback
import time
import os

totalRemainTime = 34


def execute(dataDict):
    """
    功能说明：校验阵列状态是否支持升级，将补丁包上传到整列端，并获取阵列端处理结果（入口函数）
    """
    # 判断工具连接的IP是否是阵列IP
    flag, err_msg = check_non_array_ip.execute(dataDict)
    log.info(dataDict, "flag={},err_msg={}".format(flag, err_msg))
    if not flag:
        return (False, err_msg)

    # 检查是否维护口登录
    ip_check_ret = patch_limit_maintenance_port.execute(dataDict)
    if not ip_check_ret[0]:
        return ip_check_ret

    uploadErrMsg = ''
    context = dataDict.get("context")
    deviceSN = str(dataDict.get('dev').getDeviceSerialNumber())
    hotPatchTgzPkgName = dataDict['packagePath']
    packagePathKey = "%s_packagePath" % deviceSN
    if packagePathKey in context and context[packagePathKey] != "":
        hotPatchTgzPkgName = context[packagePathKey]

    log.info(dataDict, "%s_hotPatchTgzPkgName:%s" % (deviceSN, hotPatchTgzPkgName))

    lang = dataDict['lang']
    rest = contextUtil.getRest(dataDict)
    """解析resource文件并保存"""
    resource = resourceParse.execute(lang)
    dataDict["resource"] = resource
    if checkUploadState(dataDict) == OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_DLDING:
        return False, resource.get("upgrade.notsupport")

    manager = PatchMultiPackagesManager(dataDict, hotPatchTgzPkgName)
    try:
        if manager.is_multi_patch_package():
            if not manager.is_valid_multi_package():
                log.error(dataDict, 'Patch multi package invalid')
                return False, resource.get('upload.pkgInvalid')
            hotPatchTgzPkgName = manager.get_first_patch_package()
            log.info(dataDict, "multi patch packages")
        if not manager.is_forbid_progress():
            threadUp(dataDict, "importpackage", totalRemainTime)
        """上传补丁包"""
        errMsg = ''
        processRet = upload(dataDict, hotPatchTgzPkgName)
        if not processRet[0]:
            uploadErrMsg = processRet[1]
            return (False, uploadErrMsg)
        try:
            time.sleep(5)
        except KeyboardInterrupt:
            log.error(dataDict, "Interrupted")
        status = None
        """轮询获取阵列端处理状态"""
        DEVEICE_DLD_QUERY_TIME = 0  # 轮询计数器
        while (OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_DLD_SUCCESS != status):
            DEVEICE_DLD_QUERY_TIME = DEVEICE_DLD_QUERY_TIME + 1
            if DEVEICE_DLD_QUERY_TIME > UPLOAD_PACKAGE_POLL_TIMES:
                return (False, resource.get("upload.failed"))
            # 查询处理状态
            try:
                recs = Tlv2Rest.execCmd(rest, restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS, params=[])
                log.info(dataDict,
                         'TLV cmd [%s] send[%s] receive[%s]' %
                         (TLV_CMD.OM_MSG_OP_UPD_LST_SYS_PROGRESS, str([]), str(recs)))
                status = Tlv2Rest.getRecordValue(recs[0],
                                                 restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_TASK_STATUS)
                if status == OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_DLD_FAIL:
                    # 查询处理详细信息
                    recs = Tlv2Rest.execCmd(rest, restData.TlvCmd.OM_MSG_OP_UPD_LIST_DETAILINFO, params=[])
                    log.info(dataDict,
                             'TLV cmd [%s] send[%s] receive[%s]' %
                             (TLV_CMD.OM_MSG_OP_UPD_LIST_DETAILINFO, str([]), str(recs)))
                    # 解析处理详细信息，生成返回信息
                    itemNum = recs.size()
                    for index in range(0, itemNum):
                        rec = recs.get(index)
                        nameKey = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.NAME_KEY)
                        errorKey = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.ERROY_KEY)
                        nodeId = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.NODE_ID)
                        errMsg += "%s--%s--%s\n" % (nodeId, resource.get(nameKey), resource.get(errorKey))
                    return (False, errMsg)
                try:
                    time.sleep(UPLOAD_PACKAGE_POLL_INTERVAL)
                except KeyboardInterrupt:
                    log.error(dataDict, "Interrupted")
            except IsmException as exe:
                if isinstance(exe, IsmException) and str(exe.getErrorId()) == str(REST_CAN_NOT_EXECUTE):
                    log.error(dataDict, "query upgrade process exception.[%s]" % str(exe))
                    try:
                        try:
                            time.sleep(UPLOAD_PACKAGE_POLL_INTERVAL)
                        except KeyboardInterrupt:
                            log.error(dataDict, "Interrupted")
                        rest = contextUtil.getRest(dataDict)
                    except:
                        log.error(dataDict, "creat rest connection exception.")
                    continue
                log.error(dataDict, "notify upgrade package upload exception.[%s]" % str(exe))
                return (False, resource.get("upload.failed"))

    except IsmException as exe:
        log.error(dataDict, "notify upgrade package upload over error!" + str(traceback.format_exc()) + str(exe))
        if str(exe.getErrorId()) == "1077949032":
            return (False, resource.get("exist.background.task"))
        if str(exe.getErrorId()) == "1077949058":
            return (False, resource.get("user.does.not.hava.permission"))
        return (False, resource.get("upload.failed"))
    finally:
        if not manager.is_forbid_progress():
            finishProcess(dataDict, "importpackage")
        manager.del_child_directory()
    return (True, "")


@time_out_decorator(time_out=1800,
                    timeout_callback_func=upload_timeout_callback)
def upload(dataDict, hotPatchTgzPkgName):
    """
    功能说明：查询阵列段补丁包路径，sftp上传补丁包到阵列端，通知阵列端分发补丁包
    输入：工具框架上下文，补丁包路径
    输出：bool执行结果，str错误信息
    """
    dev = dataDict["dev"]
    sftp = dataDict['sftp']

    rest = contextUtil.getRest(dataDict)
    resource = dataDict["resource"]
    context = dataDict['context']
    deviceSN = str(dataDict.get('dev').getDeviceSerialNumber())
    patchType = context["patchType_%s" % deviceSN]

    if False == os.path.isfile(hotPatchTgzPkgName):
        log.error(dataDict, 'patchPath is follow')
        log.error(dataDict, hotPatchTgzPkgName)
        errMsg = resource.get('upload.pkgPathAbnormality')
        return (False, errMsg)
    # 校验补丁包大小是否安全
    patchSize = os.stat(hotPatchTgzPkgName).st_size

    if patchType == PATCH_TYPE.HOT_PATCH:
        pkgType = TLV_PACKAGE_TYPE.HOT_PATCH_PKG
    elif patchType == PATCH_TYPE.ASL_PATCH:
        pkgType = TLV_PACKAGE_TYPE.ASL_PATCH_PKG

    params = []
    param0 = (restData.Upgrade.GetPackageUploadPath.CMO_PACKAGE_TYPE, UnsignedInt32(pkgType))
    param2 = (restData.Upgrade.GetPackageUploadPath.CMO_PACKAGE_SIZE, UnsignedInt32(patchSize))
    params.extend([param0, param2])

    log.info(dataDict, 'Execute cmd [%s] [%s]' % (restData.TlvCmd.OM_MSG_OP_GET_PACKAGE_UPLOADPATH, str(params)))
    # 获取补丁包上传路径
    recs = Tlv2Rest.execCmd(rest, restData.TlvCmd.OM_MSG_OP_GET_PACKAGE_UPLOADPATH, params)
    rec = recs[0]
    uploadPath = Tlv2Rest.getRecordValue(rec, restData.Upgrade.RecordParam.RECORD_RESULT)

    # 检查到V5R7C10/V3R6C20已经安装SPH006- SPH009补丁，等待70s，等进程起来
    try:
        dev = dataDict.get('dev')
        patchVersion = str(dev.getHotPatchVersion())
        log.info(dataDict, "Current HotPatchVersion:{}".format(patchVersion))
        checkVersionDict = ["V300R006C20SPH006", "V300R006C20SPH007", "V300R006C20SPH008", "V300R006C20SPH009",
                            "V500R007C10SPH006", "V500R007C10SPH007", "V500R007C10SPH008", "V500R007C10SPH009"]
        if patchVersion in checkVersionDict:
            log.info(dataDict, 'sleep 70s, wait upgrade process start.')
            time.sleep(70)
    except:
        log.error(dataDict, "wait process start exception.")

    # 高端设备上传包需要使用阵列内部IP
    productType = dev.getDeviceType()
    log.info(dataDict, "The device tpye is %s" % productType)

    sftpIp = None
    if str(productType) not in config.HIGH_END_DEVS:
        sftpIp = dev.getIp()
        # 上传升级包
        try:
            sftp.putFile(sftpIp, hotPatchTgzPkgName, uploadPath, dev.getLoginUser(), None)
        except:
            return (False, resource.get('upload.sftpup'))
        log.info(dataDict, "pkg upload ip = " + str(sftpIp))
    else:
        innerIpList = dev.getIpList()
        log.info(dataDict, "The innerIp list is %s" % str(innerIpList))
        for innerIp in innerIpList:
            # 上传升级包
            try:
                sftp = contextUtil.initSftpConnection(dataDict, innerIp)
                sftp.putFile(hotPatchTgzPkgName, uploadPath, None)
                sftpIp = innerIp
                log.info(dataDict, "pkg upload ip = %s" % str(sftpIp))
                break
            except:
                log.error(dataDict, "Exception:%s" % str(traceback.format_exc()))
                log.info(dataDict, "The innerIp: %s Sftp is error." % str(innerIp))
                rest = contextUtil.getRest(dataDict)

    if sftpIp is None:
        return (False, resource.get('upload.sftpup'))

    # 通知其他控制器同步升级包
    devPkgPath = sftpIp + ":" + uploadPath
    pkgPathParam0 = (restData.Upgrade.NotifyPackagePath.CMO_NOTIFY_PACKAGE_PATH, str(devPkgPath))
    params = [pkgPathParam0]
    recs = Tlv2Rest.execCmd(rest, restData.TlvCmd.OM_MSG_OP_NOTIFY_PACKAGE_PATH, params)
    # 处理正常返回
    return (True, "")


def checkUploadState(dataDict):
    """
        功能说明：查询阵列状态
       输入：工具框架上下文
      输出：int阵列端状态
    """
    rest = contextUtil.getRest(dataDict)
    params = []
    recs = Tlv2Rest.execCmd(rest, restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS, params)
    rec = recs[0]
    log.info(dataDict,
             'TLV cmd [%s] send[%s] receive[%s]' %
             (restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS, str(params), str(rec)))
    status = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdLstSysProgress.CMO_UPD_SYS_STEP_TASK_STATUS)
    log.info(dataDict, "upload statusIndex is :" + str(rec))
    return status
