#  coding=UTF-8
#  Copyright (c) Huawei Technologies Co., Ltd. 2019-2019. All rights reserved.

from cbb.frame.cli import cliUtil
from cbb.frame.context import contextUtil
from cbb.frame.base import baseUtil
from cbb.frame.base import product
from cbb.frame.base.exception import UnCheckException

# 2800 V3/5300 V3/5500 V3/5600 V3/5500F V3/5600F V3/5800 V3/5800F V3/6800 V3/6800F V3/
# 18500 V3/18800 V3/18500F V3/18800F V3	V3R3C20SPC200及之后
DEVICE_TYPE_V3_LIST = ['2800 V3', '5300 V3', '5500 V3', '5600 V3',
                       '5500F V3', '5600F V3', '5800 V3', '5800F V3',
                       '6800 V3', '6800F V3', '18500 V3', '18800 V3',
                       '18500F V3', '18800F V3']

V3_VERSION = "V300R003C20SPC200"

# 2100 V3/2200 V3/2600 V3/2600V3 For Video/2600F V3 	V3R6C00SPC100及之后
DEVICE_TYPE_V3_ARM_LIST = ['2100 V3', '2200 V3', '2600 V3', '2600V3 For Video',
                           '2600F V3']

V3_ARM_VERSION = "V300R006C00SPC100"

# 2800 V5/5300 V5/5500 V5/5600 V5/5300F V5/5500F V5/5600F V5/5800 V5/5800F V5/6800 V5/6800F V5
# 18500 V5/18800 V5/18500F V5/18800F V5	V5R7C00SPC100及之后
# 5110 V5/5210 V5/5110F V5/5210F V5/5100K V5/5200K V5	V5R7C00SPC100及之后
DEVICE_TYPE_V5_LIST = ['2800 V5', '5300 V5', '5500 V5', '5600 V5', '5300F V5',
                       '5500F V5',
                       '5600F V5', '5800 V5', '5800F V5', '6800 V5', '6800F V5',
                       '18500 V5',
                       '18800 V5', '18500F V5', '18800F V5', '5110 V5',
                       '5210 V5',
                       '5110F V5', '5210F V5', '5100K V5', '5200K V5']

V5_VERSION = "V500R007C00SPC100"

RISK_VERSION_DICT = {"V500R007C10": "V500R007C10SPH030",
                     "V300R006C20": "V300R006C20SPH030",
                     "V500R007C30SPC100": "V500R007C30SPH127",
                     "V300R006C50SPC100": "V300R006C50SPH127",
                     "V500R007C50": "V500R007C50SPH007",
                     "V300R006C60": "V300R006C60SPH007",
                     "V500R007C61": "V500R007C61SPH018",
                     "V300R006C61": "V300R006C61SPH018"}


def execute(data_dict):
    """ 检查方法：
    步骤1、执行show controller general查询控制器数量；
    步骤2、执行show system general查询设备的型号及版本号；
    步骤3、小系统下执行free -m，查询空闲内存。
    步骤4、小系统下执行du -m /startup_disk/image/boot_osp查询系统分区大小
    检查标准：
    1、步骤1查到的控制器数量为1个，则继续检查，否则检查通过。
    2、当前版本为V5R7C10/V3R6C20版本且待安装的补丁版本低于SPH030，
    当前版本为V5R7C30SPC100/V3R6C50SPC100版本且待安装的补丁版本低于SPH127，
    当前版本为V5R7C50/V3R6C60版本且待安装的补丁版本低于SPH007，
    当前版本为V5R7C61/V3R6C61版本且待安装的补丁版本低于SPH018，
    或者为其他系统版本，则继续检查，否则检查通过；
    3、如果步骤3查出的空闲内存小于步骤4查出的分区大小，则检查不通过，否则检查通过。
    修复建议：
    当前空闲内存不够，请联系技术支持工程处理
    :param data_dict: 上下文信息
    :return: 检查结果
    """
    # 执行升级前检查
    return CheckSingleControllerFreeMemory(data_dict).execute()


class CheckSingleControllerFreeMemory:
    def __init__(self, data_dict):
        self.data_dict = data_dict
        self.cli = contextUtil.getCli(data_dict)
        self.logger = contextUtil.getLogger(data_dict)
        self.lang = contextUtil.getLang(data_dict)
        self.dev = self.data_dict.get("dev")
        self.dev_type = str(self.dev.getDeviceType())
        self.product_version = self.dev.getProductVersion()
        self.origin_info = []

    def execute(self):
        try:
            self.logger.info(
                "Check the free memory of a single controller start.")
            self.logger.info("dev_type:{} product_version:{}".format(
                self.dev_type, self.product_version))
            self.origin_info.append("dev_type:{} product_version:{}".format(
                self.dev_type, self.product_version))
            target_patch_version = self.data_dict.get("pkgVersion")
            self.logger.info("target_patch_version:{}".format(target_patch_version))
            # 检查设备型号和版本信息。
            if not self.is_involved_version():
                return True, "", self.get_origin_info()
            # 步骤1查到的控制器数量为1个，则继续检查，否则检查通过。
            if self.get_controllers_num() != 1:
                return True, "", self.get_origin_info()
            # 2、当前版本为V5R7C10/V3R6C20版本且待安装的补丁版本低于SPH030，
            # 或者为其他系统版本，则继续检查，否则检查通过；
            if self.is_install_patch(target_patch_version):
                return True, "", self.get_origin_info()
            # 3、如果步骤3查出的空闲内存小于步骤4查出的分区大小，则检查不通过，否则检查通过。
            free_mem = self.get_free_memory()
            partition_size = self.get_system_partition_size()
            self.logger.info("free_mem:{} partition_size:{}".format(free_mem,
                                                                    partition_size))
            if free_mem < partition_size:
                err_msg = baseUtil.getPyResource(self.lang,
                                                 "check.single.controller.free.memory.not.pass")
                return False, err_msg, self.get_origin_info()
            return True, "", self.get_origin_info()

        except UnCheckException as e:
            self.logger.error("Excute ndmp service check exception:"
                              "{}".format(str(e)))
            return "NOCHECK", \
                   baseUtil.getPyResource(self.lang, "query.result.abnormal"), \
                   self.get_origin_info()

    def get_origin_info(self):
        return "\n".join(self.origin_info)

    def is_involved_version(self):

        if "Dorado" in self.dev_type:
            return False
        if product.isKunpeng(self.product_version):
            return False
        if self.dev_type in DEVICE_TYPE_V3_LIST and self.product_version >= V3_VERSION:
            return True
        if self.dev_type in DEVICE_TYPE_V3_ARM_LIST and self.product_version >= V3_ARM_VERSION:
            return True
        if self.dev_type in DEVICE_TYPE_V5_LIST and self.product_version >= V5_VERSION:
            return True
        return False

    def get_controllers_num(self):
        """
        执行show controller general查询控制器数量；
        :return: 控制器数量
        """
        self.logger.info("get_controllers_num start.")
        cmd = "show controller general"
        flag, cli_ret, err_msg = cliUtil.excuteCmdInCliMode(self.cli, cmd, True,
                                                            self.lang)
        self.origin_info.append(cli_ret)
        if flag is not True:
            self.logger.error("get contrller failed")
            raise UnCheckException(err_msg)
        result_list = cliUtil.getVerticalCliRet(cli_ret)
        if not result_list:
            return 0
        return len(result_list)

    def is_install_patch(self, target_patch_version):
        """
        2、当前版本为V5R7C10/V3R6C20版本且待安装的补丁版本低于SPH030，
        当前版本为V5R7C30SPC100/V3R6C50SPC100版本且待安装的补丁版本低于SPH127，
        当前版本为V5R7C50/V3R6C60版本且待安装的补丁版本低于SPH007，
        当前版本为V5R7C61/V3R6C61版本且待安装的补丁版本低于SPH018，
        或者为其他系统版本，则继续检查，否则检查通过；
        :return: 版本的检查结果。
        """

        flag, software_version, patch_version, cli_ret = cliUtil.get_system_version_with_ret(
            self.cli, self.lang)
        self.origin_info.append(cli_ret)
        if flag is not True:
            self.logger.error("get_system_version_with_ret failed")
            raise UnCheckException("get_system_version_with_ret failed.")
        if software_version in RISK_VERSION_DICT and target_patch_version >= RISK_VERSION_DICT.get(software_version):
            return True
        return False

    def get_free_memory(self):
        """
        步骤3、小系统下执行free -m，查询空闲内存。
        :return:  空闲内存大小M
        """
        self.logger.info("get_free_memory start.")
        flag, free_mem, err_msg, cli_ret = cliUtil.get_system_free_memory_with_ret(
            self.data_dict)
        self.origin_info.append(cli_ret)
        if flag is not True:
            self.logger.info("get_free_memory failed.")
            raise UnCheckException(err_msg)
        return int(free_mem)

    def get_system_partition_size(self):
        """
        小系统下执行du -m /startup_disk/image/boot_osp查询系统分区大小
        :return: partition_size系统分区的大小。
        """
        cmd = "du -m /startup_disk/image/boot_osp"
        partition_size = 0
        flag, cli_ret, err_msg = cliUtil.excuteCmdInMinisystemModel(self.cli,
                                                                    cmd,
                                                                    self.lang)
        self.origin_info.append(cli_ret)
        if flag is not True:
            self.logger.error("get_system_partition_size failed")
            raise UnCheckException(err_msg)

        partition_info = cliUtil.get_system_partition(cli_ret)
        partition_size = int(partition_info.get("/startup_disk/image/boot_osp"))
        return partition_size
