# -*- coding: UTF-8 -*-
import re
from com.huawei.ism.exception import IsmException

CLI_EXECUTE_CMD_SUCCESS = "Command executed successfully"
CLI_RET_END_FLAG = ':/>'
DEVELOPER_MODEL_FLAG = "developer:/>"
DIAGNOSE_MODEL_FLAG = ":/diagnose>"
RESULT_NOCHECK = "NOCHECK"
MINISYSTEM_MODEL_FLAG = "minisystem>"
BLACKLIST_DICT = [
{
    "key_word":"-bash:",
    "msg_en":"\nSystem is not in admin mode"
 },

{
    "key_word":"Storage:",
    "msg_en":"\nSystem is not in admin mode"
 },
           
{
    "key_word":"minisystem",
    "msg_en":"\nSystem is not in admin mode"
 },
                            
{
    "key_word":"upgrade:/>",
    "msg_en":"\nSystem is in upgrade Mode"
 },

{
    "key_word":"System is in safe mode",
    "msg_en":"\nSystem is in safe mode"
},

{
    "key_word":"System is not ready",
    "msg_en":"\nSystem is not ready"
},

{
    "key_word":"The system is powering off",
    "msg_en":"\nThe system is powering off"
 },

{
    "key_word":"The system failed to be started",
    "msg_en":"\nThe system failed to be started"
 },

{
    "key_word":"^",
    "msg_en":"\nCommand is not supported"
 },
                  
{
    "key_word":"system is busy",
    "msg_en":"\nSystem is busy"
 },
   
{
    "key_word":"Processing...",
    "msg_en":"\nSystem is processing"
 },
                  
{
    "key_word":"Socket connect failed",
    "msg_en":"\nSocket connect failed"
 },

{
    "key_word":"Failed to process the message",
    "msg_en":"\nFailed to process the message"
 },

{
    "key_word":"Operation timeout",
    "msg_en":"\nOperation timeout"
 },

{
    "key_word":"Receive message time out",
    "msg_en":"\nReceive message time out"
 },

{
    "key_word":"Error:",
    "msg_en":"\nSystem is abnormal"
 },
    
{
    "key_word":"Error :",
    "msg_en":"\nSystem is abnormal"
 },
]

def isInMinisystemMode(cliRet):
    '''
    @summary: 判断当前是否在minisystem模式下
    '''
    if re.search(MINISYSTEM_MODEL_FLAG, cliRet, re.IGNORECASE):
        return True
    return False

def excuteCmdInMinisystemModel(cli, cmd, lang):
    """
    @summary: 在minisystem模式下执行命令
    """
    cliRet = cli.execCmd(cmd)
    if "password:" in cliRet:
        return True, cliRet, ''

    if isInMinisystemMode(cliRet):
        return (True, cliRet, "")

    if isInDiagnoseMode(cliRet):
        cliRet = cli.execCmd("exit")
        if "(y/n)" in cliRet:
            cliRet = cli.execCmd("y")

    if isInDeveloperMode(cliRet):
        cliRet = cli.execCmd("minisystem")
        if isInMinisystemMode(cliRet):
            cliRet = cli.execCmd(cmd)
            return (True, cliRet, "")

        return (False, cliRet, "")

    flag, cliRet, errMsg = enterDeveloperMode(cli)
    if flag != True:
        return flag, cliRet, errMsg

    cliRet = cli.execCmd("minisystem")
    if not isInMinisystemMode(cliRet):
        return (False, cliRet, "")

    cliRet = cli.execCmd(cmd)
    return (True, cliRet, "")


def isInDeveloperMode(cliRet):
    '''
    @summary: 判断当前是否在developer模式下
    @param cliRet: cli回显
    @return: 
        True: 当前在developer模式下
        False: 当前不在developer模式下
    '''
    if re.search(DEVELOPER_MODEL_FLAG, cliRet, re.IGNORECASE):
        return True
    return False

def isInDiagnoseMode(cliRet):
    '''
    @summary: 判断当前是否在diagnose模式下
    @param cliRet: cli回显
    @return: 
        True: 当前在diagnose模式下
        False: 当前不在diagnose模式下
    '''
    if re.search(DIAGNOSE_MODEL_FLAG, cliRet, re.IGNORECASE):
        return True
    return False

def checkLineInBlackList(cliLine):
    '''
    @summary: 判断CLI语句是否在黑名单中
    @param cliLine: cli语句行
    @return: 
        True: cli语句行在黑名单中
        False: cli语句行不在黑名单中
    '''
    errMsg = ""
    
    for dictItems in BLACKLIST_DICT:
        if dictItems.get("key_word") in cliLine:
            errMsg = dictItems.get("msg_en")
            return (True, errMsg)
    return (False, errMsg)


def isInternalError(cliRet):
    '''
    @summary: 判断回显结果是否包含内部错误信息
    @param cliRet: cli回显
    @return: 
        True: cli回显包含内部错误信息
        False: cli回显不包含内部错误信息
    '''
    lowerCliRet = cliRet.lower()
    if "internal error" in lowerCliRet:
        return True
    else:
        return False


def isNoneLicense(cliRet):
    '''
    @summary: 判断回显结果里面是否包含需要license
    @param cliRet: cli回显
    @return: 
        True: cli回显包含需要license
        False: cli回显不包含需要license
    '''
    cliRetList = cliRet.splitlines()
    for line in cliRetList:
        lowerLine = line.lower()
        if "license" in lowerLine and ("error" in lowerLine or "suggestion" in lowerLine):
            return True
    
    return False


def isExecuteCmdSuccess(cliRet):
    '''
    @summary: 判断回显是否为Command executed successfully
    @param cliRet: cli回显
    @return: 
        True: cli回显中包含Command executed successfully
        False: cli回显中不包含Command executed successfully
    '''
    if re.search(CLI_EXECUTE_CMD_SUCCESS, cliRet, re.IGNORECASE):
        return True
    return False


def isDetailInfoInexistence(cliRet):
    '''
    @summary: 判断回显结果是否没有具体明细信息
    @param cliRet: cli回显
    @return: 
        True: cli回显没有具体明细信息
        False: cli回显有具体明细信息
    '''
    if isExecuteCmdSuccess(cliRet) or isNoneLicense(cliRet):
        return True
    else:
        return False

def enterDeveloperMode(cli):
    '''
    @summary: 进入developer模式
    @param cli: cli对象
    @param lang: 语言lang
    @return: (falg, cliRet, errMsg)
        flag:
            True: 进入developer模式成功
            False: 进入developer模式失败
        cliRet: cli回显
        errMsg: 进入developer模式失败时的错误消息
    '''
    cmd = "change user_mode current_mode user_mode=developer"
    checkRet = excuteCmdInCliMode(cli, cmd, True)
    if checkRet[0] != True: 
        return checkRet
    cliRet = checkRet[1]
    
    if isInDeveloperMode(cliRet):
        return (True, cliRet, "")
    else:
        errMsg = "has.not.cli.privilege"
        return (RESULT_NOCHECK, cliRet, errMsg)
    
def enterDiagnoseMode(cli):
    '''
    @summary: 进入diagnose模式
    @param cli: cli对象
    @param lang: 语言lang
    @return: (falg, cliRet, errMsg)
        flag:
            True: 进入diagnose模式成功
            False: 进入diagnose模式失败
        cliRet: cli回显
        errMsg: 进入diagnose模式失败时的错误消息
    '''
    ret = enterDeveloperMode(cli)
    if not ret[0]:
        return ret
    
    cmd = "debug"
    checkRet = excuteCmdInCliMode(cli, cmd, True)
    if checkRet[0] != True: 
        return checkRet
    cliRet = checkRet[1]
    if isInDiagnoseMode(cliRet):
        return (True, cliRet, "")
    else:
        errMsg = "has.not.cli.privilege"
        return (RESULT_NOCHECK, cliRet, errMsg)
    
def developerMode2CliMode(cli):
    '''
    @summary: developer模式下退回到cli模式
    @param cli: cli对象
    '''
    cliRet = cli.execCmd("exit")
    if "y/n" in cliRet:
        cli.execCmd("n")
        
    return None

def diagnoseMode2CliMode(cli):
    '''
    @summary: developer模式下退回到cli模式
    @param cli: cli对象
    '''
    cliRet = cli.execCmd("exit")
    if isInDeveloperMode(cliRet):
        cliRet = cli.execCmd("exit")
        
    if "y/n" in cliRet:
        cli.execCmd("n")
        
    return None

def excuteCmdInCliMode(cli, cmd, isHasLog):
    '''
    @summary: 获取cli模式下执行命令后的回显
    @param cli: cli对象
    @param cmd: 待执行命令
    @param isHasLog: 是否需要以有log的方式执行cli命令下发
    @param lang: 语言lang
    @return: (falg, cliRet, errMsg)
        flag:
            True: 执行命令正常
            False: 执行命令不正常
        cliRet: cli回显
        errMsg: 错误消息
    '''
    errMsg = ""
    cliRet = ""
    try:
        if isHasLog:
            cliRet = cli.execCmd(cmd)
        else:
            cliRet = cli.execCmdNoLog(cmd)
            
        #判断是否在黑名单中，在黑名单中则重连一次CLI
        lineList = cliRet.splitlines()
        for line in lineList:
            checkRet = checkLineInBlackList(line)
            if checkRet[0]:
                errMsg = "cli exit!"
                try:
                    cli.close()
                    cli.reConnect()
                    if isHasLog:
                        cliRet = cli.execCmd(cmd)
                    else:
                        cliRet = cli.execCmdNoLog(cmd)
                    break
                except:
                    errMsg = "Excute cmd except!"
                    return (False, cliRet, errMsg)                
    except:
        errMsg = "reconnect cli!"
        try:
            cli.close()
            cli.reConnect()
            if isHasLog:
                cliRet = cli.execCmd(cmd)
            else:
                cliRet = cli.execCmdNoLog(cmd)
        except:
            errMsg = "Excute cmd except!"
            return (False, cliRet, errMsg)
    
    if cliRet is None or len(cliRet) == 0:
        errMsg = "Cli result is empty!"
        return (False, cliRet, errMsg)
    
    if isDetailInfoInexistence(cliRet):
        errMsg = "Command executed successfully or need license!"
        return (True, cliRet, errMsg)
    
    if isInternalError(cliRet):
        errMsg = "Internal error!"
        return (True, cliRet, errMsg)

    #判断是否在黑名单中
    lineList = cliRet.splitlines()
    for line in lineList:
        checkRet = checkLineInBlackList(line)
        if checkRet[0]:
            errMsg = "System status is abnormal!"
            return (False, cliRet, errMsg)  
    
    return (True, cliRet, errMsg)


def excuteCmdInDeveloperMode(cli, cmd, isHasLog):
    '''
    @summary: 获取developer模式下执行命令后的回显
    @param cli: cli对象
    @param cmd: 待执行命令
    @param isHasLog: 是否需要以有log的方式执行cli命令下发
    @param lang: 语言lang
    @return: (falg, ret, errMsg)
        flag:
            True: 执行命令正常
            False: 执行命令不正常
        ret: cli回显
        errMsg: 错误消息
    '''
    try:
        #兼容部分命令视图切换，先尝试在普通用户模式下执行，再尝试在研发模式下执行
        checkRet = excuteCmdInCliMode(cli, cmd, isHasLog)
        if checkRet[0] == True:
            return checkRet
        
        enterDeveloperCheckRet = enterDeveloperMode(cli)
        if enterDeveloperCheckRet[0] != True:
            return enterDeveloperCheckRet
    
        checkRet = excuteCmdInCliMode(cli, cmd, isHasLog)
        cliRet = checkRet[1]
        
        if isInDeveloperMode(cliRet):
            developerMode2CliMode(cli)
        return checkRet
    except IsmException:
        #出现ISM异常时，由框架上层处理
        raise



def excuteCmdInDiagnoseMode(cli, cmd, isHasLog):
    '''
    @summary: 获取diagnose模式下执行命令后的回显
    @param cli: cli对象
    @param cmd: 待执行命令
    @param isHasLog: 是否需要以有log的方式执行cli命令下发
    @param lang: 语言lang
    @return: (falg, ret, errMsg)
        flag:
            True: 执行命令正常
            False: 执行命令不正常
        ret: cli回显
        errMsg: 错误消息
    '''
    try:
        enterDeveloperCheckRet = enterDiagnoseMode(cli)
        if enterDeveloperCheckRet[0] != True:
            return enterDeveloperCheckRet
    
        checkRet = excuteCmdInCliMode(cli, cmd, isHasLog)
        cliRet = checkRet[1]
        
        if isInDeveloperMode(cliRet):
            developerMode2CliMode(cli)
        if isInDiagnoseMode(cliRet):
            diagnoseMode2CliMode(cli)
        return checkRet
    except IsmException:
        #出现ISM异常时，由框架上层处理
        raise
    
def getCliTable2DictList(cliRet):
    '''
    @summary: 对表格形式cli回显统一处理
    @param cliRet: cli回显
    @return: 将表格形式cli回显处理为以表头为key，以项值为键的字典集合,处理不正常时，返回空集合
    '''
    try:
        #获取表头----
        headline = ""
        i = 0
        cliRetList = cliRet.encode("utf8").splitlines()
        for line in cliRetList:
            #表头以任意空格开头和结尾，中间含空格和-
            reg_headline = re.compile("^\s*-+(\s+-+)*\s*$") 
            match_headline = reg_headline.search(line)
            if match_headline:
                headline = match_headline.group()
                break
            i += 1
        if headline == "" or i == 0 or i >= len(cliRetList) - 1:
            return []
        
        #获取标题
        title = cliRetList[i - 1]
        
        #获取字段值
        field_words = cliRetList[(i + 1):]
        
        #分割线以任意空格开关和结尾，中间含1个或多个-
        reg_split = re.compile("\s*-+\s*")
        tuple_idxs = []
        start_pos = 0
        end_pos = 0
        while (start_pos <= len(headline)):
            match = reg_split.search(headline, start_pos)
            if match:
                end_pos = match.end()
                tuple_idxs.append((start_pos, end_pos))
                start_pos = end_pos
            else:
                break
            
        keys = []
        for item in tuple_idxs:
            key = title[item[0]:item[1]].strip()
            #字段名重复时,自动添加后缀：_{自增长标记}
            if keys.count(key):
                key += "_" + str(str(keys).count(key + "_") + 1)
            keys.append(key.decode("utf8"))
        
        dictList = []
        for line in field_words:
            if len(line) != len(headline):
                break
            vals = []
            for item in tuple_idxs:
                vals.append(line[item[0]:item[1]].strip().decode("utf8"))
            dictList.append(dict(zip(keys, vals)))
            
        return dictList
    except:
        return []
    
def getVerticalCliRet(cliRet):
    '''
    @summary: 按逐行字典的方式获取垂直表格形式的cli回显集合
    @param cliRet: cli回显
    @return: 将表格形式cli回显处理为以表头为key，以项值为键的字典集合,处理不正常时，返回空集合
    '''
    cliRetList = cliRet.encode("utf8").splitlines()
    lineDict = {}
    for line in cliRetList:
        if CLI_RET_END_FLAG in line:
            break
        
        if re.search("^-+\r*\n*$", line):
            lineDict.clear()
            
        fields = line.split(" : ")
        if len(fields) < 2:
            continue
        
        key = fields[0].strip().decode("utf8")
        value = ":".join(fields[1:len(fields)]).strip().decode("utf8")
        
        if lineDict.has_key(key):
            key += "_" + str(str(lineDict.keys()).count(key + "_") + 1)
        lineDict.setdefault(key, value)
    
    
    return lineDict
def getControllerIdList(cli):
    '''
    @summary: 获取设备所有控制器ID
    @param cli: cli对象
    @param lang: 语言lang
    @return: (falg, ret, errMsg)
        flag:
            True: 获取成功
            False: 获取失败
        ret: 
            flag为True时，设备所有控制器ID列表
            flag为False时，cli回显
        errMsg: 错误消息
    '''
    cmd = "show controller general|filterColumn include columnList=Controller"
    checkRet = excuteCmdInCliMode(cli, cmd, True)
    if checkRet[0] != True: 
        return checkRet
    
    cliRet = checkRet[1]
    cliRetLinesList = cliRet.splitlines()
    controlList = []
    
    for line in cliRetLinesList:
        fields = line.split(":")
        if len(fields) < 2:
            continue
        
        fieldName = fields[0].strip()
        fieldValue = fields[1].strip()
        
        if fieldName == "Controller":
            ctrlId = fieldValue
            controlList.append(ctrlId)
            
    if len(controlList) == 0:
        errMsg = "cannot.get.controller.info"
        return (False, cliRet, errMsg)
    
    return (True, controlList, "")

def getHorizontalCliRet(cliRet):
    '''
    @summary: 按逐行字典的方式获取水平表格形式的cli回显集合
    @param cliRet: cli回显
    @return: 将表格形式cli回显处理为以表头为key，以项值为键的字典集合,处理不正常时，返回空集合
    '''
    try:
        headline = ""
        i = 0
        cliRetList = cliRet.encode("utf8").splitlines()
        for line in cliRetList:
            reg_headline = re.compile("^\s*-+(\s+-+)*\s*$") 
            match_headline = reg_headline.search(line)
            if match_headline:
                headline = match_headline.group()
                break
            i += 1
        if headline == "" or i == 0 or i >= len(cliRetList) - 1:
            return []
        
        title = cliRetList[i - 1]
        field_words = cliRetList[(i + 1):]
        reg_split = re.compile("\s*-+\s*")
        tuple_idxs = []
        start_pos = 0
        end_pos = 0
        
        while (start_pos <= len(headline)):
            match = reg_split.search(headline, start_pos)
            if match:
                end_pos = match.end()
                tuple_idxs.append((start_pos, end_pos))
                start_pos = end_pos
            else:
                break
            
        keys = []
        for item in tuple_idxs:
            key = title[item[0]:item[1]].strip()
            if keys.count(key):
                key += "_" + str(str(keys).count(key + "_") + 1)
            keys.append(key.decode("utf8"))
        
        requiredLineLen = tuple_idxs[-1][0]
        dictList = []
        for line in field_words:
            if CLI_RET_END_FLAG in line:
                break
            
            #标题换行的场景
            if re.search("^-+(\s+-+)*\s*$", line):
                continue
            
            if len(line.strip()) == 0:
                continue
            
            if len(line) <= requiredLineLen:
                continue
            
            vals = []
            for item in tuple_idxs:
                vals.append(line[item[0]:item[1]].strip().decode("utf8"))
            dictList.append(dict(zip(keys, vals)))
            
        return dictList
    except:
        return []
    
def getHorizontalNostandardCliRet(cliRet):
    '''
    @summary: 按逐行字典的方式获取水平表格形式的cli回显集合,此方法用来解析CLI回显不对齐的情况
    @param cliRet: cli回显
    @return: 将表格形式cli回显处理为以表头为key，以项值为键的字典集合,处理不正常时，返回空集合
    @Tips: V3R1版本开始，CLI回显以10行作为一组对齐，存在CLI不对齐的情况，通过split("  ")两个空格解析。
    '''
    try:
        headline = ""
        i = 0
        cliRetList = cliRet.encode("utf8").splitlines()
        for line in cliRetList:
            reg_headline = re.compile("^\s*-+(\s+-+)*\s*$") 
            match_headline = reg_headline.search(line)
            if match_headline:
                headline = match_headline.group()
                break
            i += 1
        if headline == "" or i == 0 or i >= len(cliRetList) - 1:
            return []
        title = cliRetList[i - 1]
        field_words = cliRetList[(i + 1):]
        reg_split = re.compile("\s*-+\s*")
        tuple_idxs = []
        start_pos = 0
        end_pos = 0
        while (start_pos <= len(headline)):
            match = reg_split.search(headline, start_pos)
            if match:
                end_pos = match.end()
                tuple_idxs.append((start_pos, end_pos))
                start_pos = end_pos
            else:
                break
        keys = []
        for item in tuple_idxs:
            key = title[item[0]:item[1]].strip()
            if keys.count(key):
                key += "_" + str(str(keys).count(key + "_") + 1)
            keys.append(key.decode("utf8"))
        lenkeys = len(keys)
        dictList = []
        for line in field_words:
            if line.find(":/>") >= 0:
                break
            if re.search("^-+(\s+-+)*\s*$", line):
                continue
            if len(line.strip()) == 0:
                continue
            vals = []
            valueList = line.strip().split("  ")
            valueSpaceList =[]
            for value in valueList:
                if value != "":
                    vals.append(value.strip().decode("utf8"))
                    valueSpaceList.append(value)
            lenvalue = len(valueSpaceList)
            if lenvalue == lenkeys:
                dictList.append(dict(zip(keys, vals)))
        return dictList
    except:
        return []
def enterPreciseMode(cli):
    '''
    @summary: 进入precise模式
    @param cli: cli对象
    @return: (flag, cliRet, errMsg)
        flag:
            True: 进入precise模式成功
            False: 进入precise模式失败
        cliRet: cli回显
        errMsg: 进入precise模式失败时的错误消息
    '''
    cmd = "change cli capacity_mode=precise"
    checkRet = excuteCmdInCliMode(cli, cmd, True)
    if checkRet[0] != True: 
        return checkRet
    cliRet = checkRet[1]
    
    return (True, cliRet, "")

def preciseMode2CliMode(cli):
    '''
    @summary: precise模式下退回到cli模式
    @param cli: cli对象
    '''
    cmd = "change cli capacity_mode=automatic"
    checkRet = excuteCmdInCliMode(cli, cmd, True)
    if checkRet[0] != True: 
        return checkRet
    cliRet = checkRet[1]
    
    return (True, cliRet, "")

def excuteCmdInPreciseMode(cli, cmd, isHasLog):
    '''
    @summary: 获取precise模式下执行命令后的回显
    @param cli: cli对象
    @param cmd: 待执行命令
    @param isHasLog: 是否需要以有log的方式执行cli命令下发
    @return: (falg, ret, errMsg)
        flag:
            True: 执行命令正常
            False: 执行命令不正常
        ret: cli回显
        errMsg: 错误消息
    '''
    try:
        enterPreciseCheckRet = enterPreciseMode(cli)
        if enterPreciseCheckRet[0] != True:
            return enterPreciseCheckRet
        
        checkRet = excuteCmdInCliMode(cli, cmd, isHasLog)
        backToCliCheckRet = preciseMode2CliMode(cli)
        if backToCliCheckRet[0] != True:
            return backToCliCheckRet
        return checkRet
    except IsmException:
        #出现ISM异常时，由框架上层处理
        raise
    