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

from common import resourceParse
from common.constant import OM_MSG_OP_UPD_LST_SYS_PROGRESS, UPLOAD_PACKAGE_POLL_TIMES,UPLOAD_PACKAGE_POLL_INTERVAL
from common.constant import PATCH_TYPE, TLV_PACKAGE_TYPE
from common.baseFactory import log, finishProcess, threadUp
from service.func_utils import upload_timeout_callback
from cbb.frame.context import contextUtil
from cbb.frame.rest.restUtil import Tlv2Rest, REST_CAN_NOT_EXECUTE
from cbb.frame.rest import restData
from cbb.frame.base.baseUtil import config
from cbb.frame.base.baseUtil import UpgradeRes
from cbb.business.checkitems import UpdateInnerCheckVersion
from cbb.frame.base.funcUtils import time_out_decorator
from com.huawei.ism.tool.obase.utils import ManualConfigItemMgr
from utils import Products
from java.lang import Exception as JException

import traceback
import time
import os
import json
totalRemainTime = 34

def execute(dataDict):
    """
    功能说明：校验阵列状态是否支持升级，将补丁包上传到整列端，并获取阵列端处理结果（入口函数）
    """
    uploadErrMsg = ''
    context = dataDict.get("context")
    dev_type = contextUtil.getDevObj(dataDict).get("type", "")
    global upgrade_res
    upgrade_res = UpgradeRes(dev_type)
    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 check_upload_state(dataDict) == OM_MSG_OP_UPD_LST_SYS_PROGRESS.UPD_DLDING:
        return False, resource.get("upgrade.notsupport")

    try:
        threadUp(dataDict, "importpackage", totalRemainTime)

        # 更新检查项组件
        flag, err_msg = update_upgrade_component(dataDict)
        if not flag:
            return False, err_msg

        """上传补丁包"""
        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:
                params = []
                msgParam1 = (
                restData.Upgrade.UpdLstSysProgress.CMO_UPD_FLOW_ID, restData.Enum.UpgQueryFlowEnum.DISTRIBUTE_PACKAGE)
                params.append(msgParam1)
                recs = Tlv2Rest.execCmdJlist(rest, restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS, params)
                log.info(dataDict,'TLV cmd [%s] send[%s] receive[%s]'%(restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS,str(params),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.execCmdJlist(rest, restData.TlvCmd.OM_MSG_OP_UPD_LIST_DETAILINFO, params=[])
                    log.info(dataDict,'TLV cmd [%s] send[%s] receive[%s]'%(restData.TlvCmd.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)
                        itemState = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.ITEM_STATE)
                        nodeId = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.NODE_ID)
                        errorKey = Tlv2Rest.getRecordValue(rec, restData.Upgrade.UpdListDetaiInfo.ERROY_KEY)
                        if itemState == '3':
                            if not errorKey:
                                errMsg += "%s--%s--%s\n" % (
                                    nodeId,
                                    upgrade_res.get_res(nameKey), upgrade_res.get_res('failed'))
                                continue
                            details = get_error_info(dataDict, errorKey, nameKey)
                            errMsg += "%s--%s--%s\n" % (nodeId, upgrade_res.get_res(nameKey), details)

                    return (False, errMsg)
                try:
                    time.sleep(UPLOAD_PACKAGE_POLL_INTERVAL)
                except KeyboardInterrupt:
                    log.error(dataDict,"Interrupted" )
            except IsmException as e:
                if isinstance(e, IsmException) and str(e.getErrorId()) == str(REST_CAN_NOT_EXECUTE):
                    log.error(dataDict, "query upgrade process exception.[%s]" % str(e))
                    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(e))
                return (False, resource.get("upload.failed"))

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


def get_error_info(data_dict, error_key, name_key):
    details = ""
    resource = data_dict.get("resource")
    if error_key:
        try:
            error_key = json.loads(error_key)
        except Exception as e:
            log.error(data_dict, "get_error_info exception.{}".format(e))
            return resource.get("upload.errorkey.parse.failed")+"errorKey:{}".format(details)
    if isinstance(error_key, dict):
        errcode = error_key.get("errcode")
        params = error_key.get("params")
        log.info(data_dict, "errorcode=%s, params=%s" % (errcode, params))
        err_msg_format = upgrade_res.get_res(name_key + errcode)
        if params and len(params) > 0:
            if isinstance(params[0], list):
                # params是二维数组
                for param in params:
                    try:
                        err_msg = err_msg_format % tuple(param)
                    except Exception as e:
                        log.error(data_dict, "get_error_info exception.{}".format(e))
                        err_msg = err_msg_format + "\n" + str(param)
                    details += err_msg
            else:
                # params是一维数组
                try:
                    err_msg = err_msg_format % tuple(params)
                except Exception as e:
                    log.error(data_dict, "get_error_info exception.{}".format(e))
                    err_msg = err_msg_format + "\n" + str(params)
                details += err_msg
        else:
            details = err_msg_format
    return details


def update_upgrade_component(context):
    """
    更新升级组件
    :param context:
    :return:
    """
    dev = context.get('dev')
    dev_version = dev.getProductVersion()
    dev_ip = dev.getIp()
    # kunpeng设备不更新组件
    if Products.isKunpeng(dev_version):
        return True, ""
    # 数字版本低于6.0.0不更新升级组件，
    if Products.isDigitalVer(dev_version) and Products.compareVersion(dev_version, "6.0.0") < 0:
        return True, ""
    if ManualConfigItemMgr.getInstance().expected(
            dev_ip, "skip.upgrade.components", "yes"):
        return True, ""
    return UpdateInnerCheckVersion.excute(context)


@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 not 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

    pkgType = TLV_PACKAGE_TYPE.HOT_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.execCmdJlist(rest, restData.TlvCmd.OM_MSG_OP_GET_PACKAGE_UPLOADPATH, params)
    rec = recs[0]
    uploadPath = Tlv2Rest.getRecordValue(rec, restData.Upgrade.RecordParam.RECORD_RESULT)

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

    sftpIp = dev.getIp()
    # 上传升级包
    try:
        sftp.putFile(
            sftpIp, hotPatchTgzPkgName, uploadPath, dev.getLoginUser(), None)
    except (Exception, JException) as e:
        log.error(dataDict, "sftp exception.{}".format(e))
        return False, resource.get('upload.sftpup')
    log.info(dataDict, "pkg upload ip = " + str(sftpIp))

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


def check_upload_state(data_dict):
    """
    功能说明：查询阵列状态
    输入：工具框架上下文
    输出：int阵列端状态
    """
    rest = contextUtil.getRest(data_dict)
    params = []
    dev_type = str(data_dict.get("dev").getDeviceType())
    if dev_type == "OceanStor A800":
        msg_param1 = (restData.Upgrade.UpdLstSysProgress.CMO_UPD_FLOW_ID, restData.Enum.UpgQueryFlowEnum.UPGRADE_SYSTEM)
        params.append(msg_param1)
    recs = Tlv2Rest.execCmdJlist(rest, restData.TlvCmd.OM_MSG_OP_UPD_LST_SYS_PROGRESS, params)
    rec = recs[0]
    log.info(data_dict, '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(data_dict, "upload statusIndex is :" + str(rec))
    return status
