# -*- coding: UTF-8 -*-
from com.huawei.ism.tlv.lang import UnsignedInt32
from com.huawei.ism.tlv.bean import Param
from com.huawei.ism.tlv.docoder import ParamType
from com.huawei.ism.tlv import TLVUtils
import os
import re
import shutil
from common import resourceParse
from common.constant import *
from common.cliUtils import *
from common.baseFactory import log
from common.baseFactory import deleteTempPatch
from cbb.business.checkitems import patch_ver_match_check
from cbb.frame.util.tar_util import decompress_tar_special_file


# windows路径的安全长度
PATCH_PATH_SAFE_LEN = 240


# *************************************************************#
# 函数名称: getHotPatchPKGVersionMult
# 功能说明: 解压热补丁包的配置文件，获取热补丁包版本号配套关系，返回多补丁压缩包中的子补丁路径
# 其 他   :  无
# *************************************************************#
def getPatchPKGSubPatchPath(dataDict, filePath):
    """
    功能说明：解析补丁包，判断是否为多补丁压缩包（是否存在 version.conf文件），如果则获取阵列数字版本号，若数字版本号与version.conf匹配则返回子补丁文件路径
   输入：上下文，多补丁压缩包路径。
    @return: (falg, Val)
        flag:
            True: 包括不需要处理的版本  +  空字符串， 或正确解析出阵列匹配的子补丁路径
            False: 补丁文件格式错误  +  错误消息资源ID
    """
    def parse_patch_file_config():
        # 解析补丁配置文件
        confFile = extractPath + os.sep + versionConf
        if len(confFile) > 240:
            return False, "HotPatchCheck.notpass.multpkgetPathTooLongErr"
        conFileFd = open(confFile, "r")
        confInfo = conFileFd.read()
        # 关闭文件句柄
        conFileFd.close()
        lineList = confInfo.splitlines()

        # 从配置文件中查找版本号是否与产品版本匹配
        for line in lineList:
            field = line.split()
            if len(field) < 2 or field[0] != "PatchConfig":
                continue
            # 获取版本配置信息
            pconfigArr = field[1].split(':')
            innerVersionTmp = str(pconfigArr[0]).strip()
            if innerVersionTmp == innerVersion:
                return True, str(pconfigArr[1]).strip()
        return False, "HotPatchCheck.notpass.multpkSysVerNotMatchErr"

    def check_dev_params():
        # 限定特定版本：当前只有 5800 V3R3C20SPC200 存在B047、B048双补丁
        typeEnum = ['5800 V3']
        productVersionEnum = ['V300R003C20SPC200']
        dev = dataDict.get('dev')
        deviceSN = str(dev.getDeviceSerialNumber())
        devType = str(dev.getDeviceType())
        productVersion = dev.getProductVersion()
        log.info(dataDict, 'devType=%s,productVersion=%s' % (devType, productVersion))
        if devType not in typeEnum or productVersion not in productVersionEnum:
            return True, deviceSN
        return False, deviceSN

    subPatchPath = ''
    packageValid = False
    versionConf = 'version.conf'
    context = dataDict.get("context")

    result, deviceSN = check_dev_params()
    if result:
        return True, ""

    #如果路径存在则删除，用于补丁路径修改路径
    deleteTempPatch(dataDict)

    extractPath = "%s_%s" % (filePath[0: filePath.index('.tgz')], deviceSN)
    context[('patch_dir_%s' % deviceSN)] = extractPath
    log.info(dataDict, 'extractPath=%s' % extractPath)
    if os.path.isdir(extractPath):
        shutil.rmtree(extractPath, ignore_errors=True)

    try:
        # 查看热补丁包是否有效
        # 补丁包无效，直接返回为空
        packageValid, _ = decompress_tar_special_file(filePath, extractPath, versionConf)
        if not packageValid:
            log.warn(dataDict, 'not find version.conf, its not a mult hotpatch')
            return True, ""
        packageValid = False
        log.info(dataDict, 'find %s' % versionConf)

        #获取产品数字版本号
        (retStatus, innerVersion, errMsg) = getDevSysVersionSupported(dataDict)
        if not retStatus:
            log.error(dataDict, errMsg)
            return (False, "HotPatchCheck.notpass.multpkgetDevSysVersionSupportedErr")
        log.info(dataDict, 'innerVersion=%s' % innerVersion)

        result, patchFileTmp = parse_patch_file_config()
        if not result:
            return False, patchFileTmp
        log.info(dataDict, 'find patchFileTmp=%s' % patchFileTmp)

        #解压补丁文件
        success, _ = decompress_tar_special_file(filePath, extractPath, patchFileTmp)
        if success:
            subPatchPath = extractPath + os.sep + patchFileTmp
            if len(subPatchPath) > PATCH_PATH_SAFE_LEN:
                return False, "HotPatchCheck.notpass.multpkgetPathTooLongErr"
            packageValid = True
        if subPatchPath == "":
            return (False, "HotPatchCheck.notpass.multpkSysVerNotMatchErr")
    except Exception, e:
        packageValid = False
        log.error(dataDict,'failed=%s' % str(e))
        return (False, "HotPatchCheck.notpass.multpkErr")
    finally:
        if not packageValid:
            log.info(dataDict, "delete extractPath file.")
            shutil.rmtree(extractPath, ignore_errors=True)

    log.info(dataDict, 'find subPatchPath=%s' % subPatchPath)
    return (True,subPatchPath)

def getDevSysVersionSupported(dataDict):
    """
    功能说明：查询当前阵列系统软件内部版本号
    输入：工具框架上下文
    返回：bool查询成功结果False/True，str 内部版本号(数字版本号)
    """
    errMsg = 'getDevSysVersionSupported faild'
    innerVersion = ''
    cli = dataDict["ssh"]
    cmd = "cat /OSM/conf/versions.conf"
    checkRet = excuteCmdInMinisystemMode(cli, cmd)
    if not checkRet[0]:
        return checkRet

    lineList = checkRet[1].splitlines()
    lsize = len(lineList)
    for i in range(0, lsize):
        line = lineList[i]
        if line.find("[GLOBAL]") >= 0:
            for j in range(i+1, lsize):
                line = lineList[j]
                if line.find("Version") >= 0:
                    innerVersion = line.split('=')[1]
                    innerVersion = str(innerVersion).strip()
                    return (True, innerVersion, "")
                if line.find('[') >= 0:
                    break
            break

    return (False,innerVersion, errMsg)

# *************************************************************#
# 函数名称: getHotPatchPKGVersion
# 功能说明: 解压热补丁包的配置文件，获取热补丁包版本号配套关系
# 其 他   :  无
# *************************************************************#  
def getPatchPKGVersion(filePath):
    """
    功能说明：解析补丁包，获取补丁包类型、热补丁版本、热补丁B版本、异构补丁名称和异构补丁版本
   输入：补丁包路径。
  输出：bool解析成功结果False/True，dict获取的补丁包信息(patchType、hotPatchPkgVersion、SysVersionBSupported、AslName、AslVersion)
    """
    packageVersion = {}
    packageValid = False
    
    extractPath = filePath[0: filePath.index('.tgz')]
    
    try:
        # 查看热补丁包是否有效,
        # 普通热补丁包：patch.conf文件在第一层压缩目录下
        # 补丁包无效，直接返回为空
        packageValid, _ = decompress_tar_special_file(filePath, extractPath, 'patch.conf')
        if not packageValid:
            return packageValid, "", "", ""
    
        #解析补丁配置文件
        confFile = extractPath + os.sep + 'patch.conf'
        conFileFd = open(confFile, "r")
        confInfo = conFileFd.read()
        #关闭文件句柄
        conFileFd.close()
        lineList = confInfo.splitlines()
        
        #获取配置文件中的热补丁版本号
        for line in lineList:
    
            field = line.split()
            if len(field) < 2:
                continue
            #获取版本配置信息
            if field[0] == "Version":
                packageVersion['hotPatchPkgVersion'] = field[1]
            elif field[0] == "PATCHTYPE":
                packageVersion['patchType'] = field[1]
            elif field[0] == "SysVersionBSupported":
                packageVersion['SysVersionBSupported'] = field[1]
            elif field[0] == 'Asl_Version':
                packageVersion['AslVersion'] = field[1]
            elif field[0] == 'Asl_Name':
                packageVersion['AslName'] = field[1]
            elif field[0] == 'SysVersionSupported':
                packageVersion['SysVersionSupported'] = field[1]
            else:
                continue
    except Exception:
        packageValid = False
    finally:
        shutil.rmtree(filePath[0:-4], ignore_errors=True)
    
    return (packageValid,packageVersion)

def getHotPatchCurVersion(dataDict):
    """
    功能说明：查询当前阵列已安装的热补丁版本号
    输入：工具框架上下文
    返回：bool查询成功结果False/True，str热补丁版本号
    """
    tlvCon = dataDict["tlv"]
    msgParam0 = Param(0, ParamType.UNSIGN_INT, UnsignedInt32(TLV_PACKAGE_TYPE.HOT_PATCH_PKG))
    params = TLVUtils.paramList(msgParam0)
    retRec = tlvCon.invoke(TLV_CMD.OM_MSG_OP_LST_VER, params, CMD_DEFAULT_TIMEOUT)
    log.info(dataDict,'TLV cmd [%d] send[%s] receive[%s]'%(TLV_CMD.OM_MSG_OP_LST_VER,str(params),str(retRec)))
    curHotPatchVersion = retRec.getParamStrValue(4)
    if not curHotPatchVersion:
        log.error(dataDict,'Get curHotPatchVersion failed')
        return (True,'--')
    return (True,curHotPatchVersion)

def getAslPatchCurVersion(dataDict):
    """
    功能说明：查询当前阵列已安装的异构补丁版本号
    输入：工具框架上下文
    返回：bool查询成功结果False/True，str异构补丁版本号
    """
    curAslPatchVersions = ''
    tlvCon = dataDict["tlv"]
    msgParam0 = Param(0, ParamType.UNSIGN_INT, UnsignedInt32(TLV_PACKAGE_TYPE.ASL_PATCH_PKG))
    params = TLVUtils.paramList(msgParam0)
    recs = tlvCon.getBatch(TLV_CMD.OM_MSG_OP_LST_VER, params, CMD_DEFAULT_TIMEOUT)
    log.info(dataDict,'TLV cmd [%d] send[%s] receive[%s]'%(TLV_CMD.OM_MSG_OP_LST_VER,str(params),str(recs)))
    itemNum = recs.size()
    for index in range(0,itemNum):
        curAslPatchVersioninfo = recs.get(index)
        if index == 0:
            result = curAslPatchVersioninfo.getParamIntValue(0).intValue()
            log.info(dataDict,'getAslPatchCurVersion result:'+str(result))
            if result != 0:
                log.error(dataDict,'Get AslPatchCurVersion failed   result:'+ str(result))
                return (True,curAslPatchVersions)
        else:
            AslPatchName = curAslPatchVersioninfo.getParamStrValue(4)
            log.info(dataDict,'AslPatchName:'+AslPatchName)
            AslPatchVersion = curAslPatchVersioninfo.getParamStrValue(5)
            log.info(dataDict,'AslPatchVersion:'+AslPatchVersion)
            curAslPatchVersions += AslPatchName + ' ' + AslPatchVersion + '; '
    return (True,curAslPatchVersions)

# *************************************************************#
# 函数名称: execute
# 功能说明: 解析热补丁包所在路径，返回字典：热补丁版本号+对应的文件名(入口函数)
# 其 他   :  无
# *************************************************************#
def execute(dataDict):
    """
          函数名称: execute
         功能说明: 解析热补丁包所在路径，返回字典：补丁包版本号+当前阵列补丁版本号(入口函数)
        其 他   :  无
    """
    lang = dataDict.get("lang")
    curPatchVersion = ''
    hotPatchPKGVersion = ''
    hotPatchTgzPkgName = dataDict['packagePath']
    deviceSN = str(dataDict.get('dev').getDeviceSerialNumber())
    checkDict = {}
    #解析resource文件并保存
    resource = resourceParse.execute(lang)
    dataDict["resource"] = resource
    errMsg = ''
    log.info(dataDict,"%s_checkPkg_hotPatchTgzPkgName:%s"%(deviceSN, hotPatchTgzPkgName))  
    if False == os.path.isfile(hotPatchTgzPkgName):
        log.error(dataDict,'patchPath is follow')
        log.error(dataDict, hotPatchTgzPkgName)
        errMsg = resource.get('upload.pkgPathAbnormality')
        return (False,errMsg,checkDict)

    #检查是否为多补丁压缩包，判断是否存在 version.conf 文件
    packagePathKey = "%s_packagePath" %deviceSN
    context = dataDict.get("context")
    context[packagePathKey] = ""    #初始化为空
    iRet = getPatchPKGSubPatchPath(dataDict, hotPatchTgzPkgName)
    if iRet[0]:
        if len(iRet[1]) > 1:
            hotPatchTgzPkgName = iRet[1]
            context[packagePathKey] = hotPatchTgzPkgName
    else:
        errMsgID = iRet[1]
        log.info(dataDict, "not a valid mul hotPatchPackge: %s" % errMsgID)
        errMsg = resource.get(errMsgID)
        return (False,errMsg,checkDict)
    log.info(dataDict, "hotPatchTgzPkgName = %s" % hotPatchTgzPkgName)

    #获取补丁包信息
    iRet = getPatchPKGVersion(hotPatchTgzPkgName)
    packageValid = iRet[0]
    if not packageValid:
        log.error(dataDict,'Patch package invalid')
        errMsg = resource.get('upload.pkgInvalid')
        return (False,errMsg,checkDict)
    
    packageVersion = iRet[1]
    pkgVersion = packageVersion.get('hotPatchPkgVersion')
    patchType = packageVersion.get('patchType')
    pkgSystemSpcVerComp = packageVersion.get('SysVersionBSupported')
    AslVersion = packageVersion.get('AslVersion')
    AslName = packageVersion.get('AslName')
    
    #判断补丁类型（热补丁、异构补丁）如果为空则为热补丁
    if not patchType:
        patchType = PATCH_TYPE.HOT_PATCH
        #we just need the version like this: VxxxRxxxCxx
        sysVersion = re.compile(r'^V\d{0,3}R\d{0,3}C\d{0,2}')
        if not pkgSystemSpcVerComp or not pkgVersion:
            errMsg = resource.get('upload.pkgAbnormality')
            return (False,errMsg,checkDict)
        tempResult = sysVersion.search(pkgSystemSpcVerComp)
        if tempResult is None:
            pkgSystemSpcVerComp = ""
        else:
            pkgSystemSpcVerComp = tempResult.group()
            
    elif patchType != PATCH_TYPE.ASL_PATCH:
        log.error(dataDict,u'获取补丁包类型异常   PATCH_TYPE:' +patchType)
        errMsg = resource.get('upload.pkgTypeAbnormality')
        return (False, errMsg,checkDict)

    # 检查-1补丁的VRC版本是否和阵列的VRC版本匹配
    flag, err_msg = patch_ver_match_check.execute(dataDict, packageVersion)
    if not flag:
        return False, err_msg, checkDict

    #查询当前阵列安装的补丁版本号
    if patchType == PATCH_TYPE.HOT_PATCH:
        ret = getHotPatchCurVersion(dataDict)
        if not ret[0]:
            log.error(dataDict,'gethotpatchversion failed')
        else:
            curPatchVersion = ret[1]
        hotPatchPKGVersion = pkgSystemSpcVerComp + pkgVersion
    else:
        if not AslName or not AslVersion:
           errMsg = resource.get('upload.pkgAbnormality')
           return (False,errMsg,checkDict) 
        ret = getAslPatchCurVersion(dataDict)
        if not ret[0]:
            log.error(dataDict,'getaslpatchversion failed')
        else:
            curPatchVersion = ret[1]
        hotPatchPKGVersion = AslName + ' ' + AslVersion
    #将补丁类型放入框架公共字典
    context["patchType_%s"%deviceSN] = patchType
    #填充返回字典      
    checkDict = {"hotPatchVersion": hotPatchPKGVersion,'curPatchVersion':curPatchVersion}
    #检查结果返回
    return (True, errMsg, checkDict)
