# -*-coding:utf-8-*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import math
import re
import traceback
from collections import namedtuple
from IPy import IP

from utils.common import log as logger
from utils.business.project_util import ProjectApi
from utils.common.param_check import check_param_ip
from utils.common.error.hcci_error_code import get_code_msg
from utils.business.project_condition_utils import get_project_condition_boolean
from plugins.DistributedStorage.Deploy.params.network_utils import NET_CONFLICT
from plugins.DistributedStorage.Expansion.params.managestorfb import ManageStorFBParamCheckHook
from plugins.DistributedStorage.Deploy.params.install_params_checkutils import InstallOSParamCheckHook
from plugins.DistributedStorage.Deploy.params.install_params_checkutils import InstallOSServerListCheckHook
from plugins.DistributedStorage.Deploy.params.install_params_checkutils import BusinessParametersCheckUtils
from plugins.DistributedStorage.Deploy.params.params_check import FusionStorageParamCheckHook


def params_check(**kwargs):
    """插件参数校验接口函数
    :param kwargs:
    :return: 成功：True, ''， 失败：False，
              错误信息，格式：{key1:err_msg1, key2:err_msg2}
    """
    is_exp_rep = get_project_condition_boolean(
        kwargs.get("params").get("project_id"),
        "(ExpansionServiceStorage&!TenantStorFB80|ExpansionAdCloudService)"
        "&(CSDRStorage_TFB|CSHAStorage_TFB)&DRStorage_TFB_Sep")
    if is_exp_rep:
        # 扩复制集群、扩可选云服务-CSHA/CSDR场景，参数校验在复制接口内进行
        return True, ""

    error_params = dict()
    try:
        main(error_params, kwargs)
    except Exception as e:
        logger.error("FS800Exp params check is abnormal：{}".format(e))
        logger.error(traceback.format_exc())
        error_params['params check'] = "The script is abnormal."
        return False, error_params
    if error_params:
        return False, error_params
    return True, ""


def main(error_params, kwargs):
    # LLD表参数获取
    params = kwargs.get("params")
    project_id = kwargs.get("params").get("project_id")
    params_list = params.get("param_list", '')
    fusion_storage_business_server_list = params.get("fusionstorage_business_server_list", '')
    fs_server_list = params.get("fs_server_list", '')
    compute_network_list = params.get("computing_node_network_planning_dict_list", '')
    # 业务存储参数校验
    logger.info("Start to query list of backend pool")
    business_storage_pool = BusinessParametersCheckUtils.get_backend_pool(project_id, params_list)
    logger.info('business_storage_pool: %s' % business_storage_pool)
    business_bool = get_project_condition_boolean(
        project_id,
        '(TenantStorFB80|TenantStorFBReuse80)&!(ExpansionComputeRes_KVMNode|ExpansionNetworkRes_NetworkNode)'
        '|ExpansionServiceStorage&(TenantStorNewNode|TenantStorNewPool)')
    if not fusion_storage_business_server_list and business_bool:
        error_params["fusionstorage_business_server_list"] = get_code_msg('626023')
    logger.info("Start to check the parameters: fusionstorage_business_server_list")
    hook_obj = ExpansionFusionStorageParamCheckHook(project_id)
    ret, detail = hook_obj.check(fusion_storage_business_server_list, business_storage_pool, project_id)
    logger.info('FusionStorageParamCheckHook status: %s, output: %s' % (ret, detail))
    if not ret:
        error_params.update(detail)
    # 管理融合部署参数校验
    logger.info("Start to check the parameters: fs_server_list")
    ret, detail = ManageStorFBParamCheckHook().check(project_id, fs_server_list)
    logger.info('ManageStorFBParamCheckHook status: %s, output: %s' % (ret, detail))
    if not ret:
        error_params.update(detail)
    # Basic_Parameters相应参数校验
    hook_obj = ServerListParamCheckHook()
    logger.info("Start to check the parameters: param_list")
    ret, detail = hook_obj.check(params_list, project_id, compute_network_list)
    logger.info('param_list check status: %s, output: %s' % (ret, detail))
    if not ret:
        error_params.update(detail)


class ExpansionFusionStorageParamCheckHook(FusionStorageParamCheckHook):
    def __init__(self, project_id):
        super(ExpansionFusionStorageParamCheckHook, self).__init__(project_id)
        self.exp_az_and_rep = get_project_condition_boolean(
            project_id, "(CSHAStorage_TFB|CSDRStorage_TFB)&ExpansionAZ_KVM")
        self.is_ipsan_exp_fs_and_rep = get_project_condition_boolean(
            project_id, "ExpansionServiceStorage&TenantStorFB80&(CSDRStorage_TFB|CSHAStorage_TFB)&DRStorage_TFB_Sep")
        self.is_exp_rep = self.exp_az_and_rep or self.is_ipsan_exp_fs_and_rep

    @staticmethod
    def handle_none_type_params(input_params):
        """
        将none type参数转化为空字符串("")适配hcci与hcsd传参差异
        """
        for item in input_params:
            for key, value in item.items():
                if value is None:
                    item[key] = ""

    @staticmethod
    def _check_network_plane_port(project_id, node_info):
        err_results = dict()
        if not get_project_condition_boolean(
                project_id, '(TenantStorFB80|TenantStorFBReuse80|ExpansionServiceStorage)&!TenantStorFB_Heterogeneous'):
            return err_results
        management_ports = node_info.get("management_plane").split(",")
        storage_frontend_ports = node_info.get("storage_plane").split(",")
        if len(management_ports) != 2 or management_ports[0] == management_ports[1]:
            err_results["management_plane"] = get_code_msg('626274') % ("management_plane", " ".join(management_ports))
        if len(storage_frontend_ports) != 2 or storage_frontend_ports[0] == storage_frontend_ports[1]:
            err_results["storage_plane"] = get_code_msg('626274') % ("storage_plane", " ".join(storage_frontend_ports))
        backend_net_independent = get_project_condition_boolean(project_id, "TenantStorBackendNetSep")
        if backend_net_independent:
            storage_backend_ports = node_info.get("storage_inner_plane")
            if storage_backend_ports:
                storage_backend_ports = storage_backend_ports.split(",")
            if len(storage_backend_ports) != 2 or storage_backend_ports[0] == storage_backend_ports[1]:
                err_results["storage_inner_plane"] = \
                    get_code_msg('626274') % ("storage_inner_plane", " ".join(storage_backend_ports))
            if storage_frontend_ports == storage_backend_ports:
                err_results["storage_plane and storage_inner_plane"] = \
                    get_code_msg('626275') % (" ".join(storage_frontend_ports), " ".join(storage_backend_ports))
        iscsi_net_boolean = get_project_condition_boolean(project_id, 'TenantStorFB_iSCSI')
        if iscsi_net_boolean:
            iscsi_ports = node_info.get('iscsi_business_plane')
            if iscsi_ports:
                iscsi_ports = iscsi_ports.split(',')
            if len(iscsi_ports) != 2 or iscsi_ports[0] == iscsi_ports[1]:
                err_results["iscsi_business_plane"] = \
                    get_code_msg('626274') % ("iscsi_business_plane", " ".join(iscsi_ports))
        return err_results

    def get_value(self, input_params):
        """获取用户输入的参数'business_storage_pool'的值
                       :param input_params:
                       :return:
        """
        value = ''
        if not input_params:
            return value

        for param_item in input_params:
            if param_item['key'] != "business_storage_pool":
                continue
            value = param_item['value']
        return value

    def check(self, input_params, backend_pool_list, project_id='10'):
        """
        检测用户输入的parameter参数，检测取值格式， 范围等合法性检测
        :param input_params:
        :param backend_pool_list:
        :param project_id:
        :return:
        """
        if not input_params:
            return True, ''
        self.handle_none_type_params(input_params)
        output = dict()
        # 池信息，包含存储池名称+机柜信息+服务器数量
        if not get_project_condition_boolean(project_id,
                                             'ExpansionServiceStorage&(TenantStorNewNode|DRStorage_TFB_Sep)'):
            # 检查business_storage_pool是否填写，值是否符合规则
            self.check_business_storage_pool_value(backend_pool_list, output)
        # 获取FusionStorage parameters里的存储池名
        # 对参数按照存储池名进行排序
        input_params = sorted(input_params, key=lambda input_param: input_param["storage_pool_name_and_slot"])
        # 获取场景，判断是否需要校验机柜信息
        fb_rack_ha = get_project_condition_boolean(project_id, 'TenantStorFB_RackHA')

        new_fusion_storage_flag = True
        if get_project_condition_boolean(
                project_id,
                'TenantStorFBReuse80|(TenantStorNewPool|TenantStorNewNode|DRStorage_TFB_Sep)&ExpansionServiceStorage'):
            new_fusion_storage_flag = False
        new_pool_storage_flag = get_project_condition_boolean(
            project_id,
            '(TenantStorFB80|TenantStorFBReuse80)&!(ExpansionComputeRes_KVMNode|ExpansionNetworkRes_NetworkNode)'
            '|ExpansionServiceStorage&TenantStorNewPool')
        data_info = self.check_input_params_for_node(
                *[input_params, output, new_fusion_storage_flag, project_id, fb_rack_ha])
        osd_node_list, storage_pool_name_set, zk_node_num, pool_info_list =\
            data_info[0], data_info[1], data_info[2], data_info[3]

        storage_pool_name_list = list(storage_pool_name_set)
        logger.info('storage_pool_name_list: {}, osd number: {}'.format(storage_pool_name_list, len(osd_node_list)))

        # 检查单个存储池的主存、缓存、冗余策略、EC参数
        logger.info("start to check redundancy policy")
        check_result = self.utils.storage_pool_params_check(
            storage_pool_name_list, osd_node_list, self.disk_type, self.support_cache)
        output.update(check_result)
        logger.info("check redundancy policy complete, result: {}".format(check_result))
        if new_fusion_storage_flag:
            # 全闪场景，主存盘类型检查
            logger.info("start to check primary type in the all-flash scenario")
            check_result = self.utils.all_flash_primary_type_check(osd_node_list, output)
            output.update(check_result)

            self.utils.check_zk_num(output, zk_node_num, osd_node_list)

        # 使用机柜创池场景下校验用户所填机柜信息是否满足后续要求
        logger.info("start to check rack status")
        check_result = self.utils.rack_status_and_node_number_check(fb_rack_ha, pool_info_list, input_params)
        output.update(check_result)
        logger.info("check rack status complete, result: {}".format(check_result))

        # 校验EC策略下：同一资源池osd节点数量是否满足最低数量要求
        logger.info("start to check osd node num")
        self.check_storage_pool_node_num(output, input_params, storage_pool_name_list, new_pool_storage_flag)

        # 检查同一资源池中服务器上盘的数量是否满足要求
        if new_pool_storage_flag:
            check_result = self.check_separate_pool_disk_num(storage_pool_name_list, input_params, output)
            output.update(check_result)

        # LLD1.2与LLD1.4里资源池名称和数量校验
        logger.info("start to check pool name Consistency")
        res, detail = self.utils.storage_pool_and_backend_pool_name_check(backend_pool_list, storage_pool_name_list)
        if not res:
            output.update(detail)
        logger.info("check pool name Consistency complete, result: {}".format(detail))

        return (False, output) if output else (True, output)

    def check_business_storage_pool_value(self, backend_pool_list, output):
        if not backend_pool_list:
            err_msg = "The value of params 'business_storage_pool' in LLD can not be empty"
            logger.error(err_msg)
            output["business_storage_pool"] = get_code_msg('626010') % "business_storage_pool"
        pool_name_list = []
        for business_storage_pool_name in backend_pool_list:
            pool_name_list += business_storage_pool_name.split(',')
        for pool_name in pool_name_list:
            if not re.match(r'^[0-9a-zA-Z_-]{1,64}$', pool_name):
                err_msg = "The value of params 'business_storage_pool' does not comply with the naming rule."
                logger.error(err_msg)
                output["business_storage_pool"] = get_code_msg('626383') % "business_storage_pool"

    def check_storage_pool_node_num(self, output, input_params, storage_pool_list, new_pool_storage_flag):
        """
        检查同一存储池osd数目是否满足要求
        :return:
        """
        if not new_pool_storage_flag:
            return
        for pool in storage_pool_list:
            # 如果EC相关参数已经有误，跳过此项检查，避免脚本执行错误
            min_node_or_rack = None
            if "ec_data_fragments" in output \
                    or "ec_verify_fragments" in output \
                    or "storagepool_redundancy_policy" in output:
                break
            node_num = 0
            for param_item in input_params:
                pool_policy = param_item.get("storagepool_redundancy_policy")
                if "deploy_component" in param_item and param_item.get("deploy_component")\
                        and "osd" not in param_item["deploy_component"] or \
                        pool != param_item.get("storage_pool_name_and_slot") or \
                        pool_policy.upper() != 'EC':
                    continue
                node_num += 1
                ec_data_fragments = float(param_item.get("ec_data_fragments"))
                ec_verify_fragments = float(param_item.get("ec_verify_fragments"))
                min_node = int(math.ceil(ec_data_fragments / ec_verify_fragments + 1))
                if ec_data_fragments == 6 and ec_verify_fragments == 4:
                    min_node = 10
            if not min_node:
                continue
            if node_num < min_node:
                output["ec_data_fragments"] = get_code_msg('626095') % (int(ec_data_fragments),
                                                                        int(ec_data_fragments),
                                                                        min_node)

    def check_input_params_for_node(self, *args):
        # 统计zk节点个数
        input_params, output, new_fusion_storage_flag, project_id, fb_rack_ha = args
        storage_pool_name_set = set()
        zk_node_num = 0
        zk_slot = ""
        zk_disk_type = None
        hostname = {"node_num": 0, "hostname_set": set()}
        osd_node_list = list()
        pool_info_list = list()
        server_list_check = InstallOSServerListCheckHook(self.check_list, self.ip_check_list, [])
        for param_item in input_params:
            if self.is_exp_rep and "osd" not in param_item["deploy_component"]:
                continue
            osd_node_list.append(param_item)
            # 必填参数是否填写检查
            # 检查出现在self.check_list内的参数是否填写，IP格式是否合法
            logger.info("start to check parameters those have to be written")
            ret, detail = server_list_check.check(param_item, project_id)
            if not ret:
                output.update(detail)
            logger.info("status: {}, result: {}".format(ret, detail))
            # 自动装机场景检查网络端口格式是否合法
            logger.info("Auto install: starting to check network port")
            check_result = self._check_network_plane_port(project_id, param_item)
            output.update(check_result)
            logger.info("network port check result: {}".format(check_result))

            # zk节点个数统计
            extra_params = [zk_node_num, zk_slot, zk_disk_type, new_fusion_storage_flag, param_item, output]
            zk_node_num, zk_slot, zk_disk_type = self.check_zk_num(*extra_params)

            # 主存槽位的检测
            logger.info("start to check primary slot")
            msg_dict = self.utils.primary_slot_check(param_item, output)
            output.update(msg_dict)
            logger.info("check primary slot complete, result: {}".format(msg_dict))

            # 系统帐户密码的检查
            logger.info("start to check system user")
            msg_dict = self.utils.check_system_user_info(project_id, param_item, output)
            output.update(msg_dict)
            logger.info("check system user complete, result: {}".format(msg_dict))

            # 存储节点的主机名不能相同
            logger.info("start to check hostname")
            msg_dict = self.utils.hostname_check(param_item, hostname, output)
            output.update(msg_dict)
            logger.info("check hostname complete, result: {}".format(msg_dict))

            # 未正确填写资源池名称，不做后续检查，避免脚本执行错误
            if "storage_pool_name_and_slot" in output:
                continue
            storage_pool_name = param_item.get("storage_pool_name_and_slot")

            # 校验对应场景下机柜信息是否填写正确
            if fb_rack_ha:
                # 机柜id检查
                rack_id = param_item.get("rack_id")
                if not re.match(r'^[\w]{1,256}$', rack_id):
                    output["rack_id"] = get_code_msg('626060') % "rack_id"
                # 构造pool_info_list
                self.utils.get_rack_ha_pool_info_list(
                    param_item, storage_pool_name, storage_pool_name_set, pool_info_list)
            storage_pool_name_set.add(storage_pool_name)
        data_tuple = namedtuple('data_tuple',
                                ['osd_node_list', 'storage_pool_name_set', 'zk_node_num', 'pool_info_list'])
        data_info = data_tuple(osd_node_list, storage_pool_name_set, zk_node_num, pool_info_list)
        return data_info

    def check_zk_num(self, *args):
        """zk节点个数统计检查"""
        zk_node_num, zk_slot, zk_disk_type, new_fusion_storage_flag, param_item, output = args
        if new_fusion_storage_flag and param_item.get("is_metadata_node") == "1":
            zk_node_num += 1
            logger.info("start to check zk slot")
            msg_dict, zk_slot = self.utils.zk_slot_check(param_item, zk_slot, output)
            output.update(msg_dict)
            logger.info("check zk slot complete, result: {}".format(msg_dict))
            logger.info("start to check primary and zk slot conflict")
            msg_dict = self.utils.zk_slot_and_primary_slot_conflict_check(param_item, output)
            output.update(msg_dict)
            logger.info("start to check zk disk type")
            msg_dict, zk_disk_type = self.utils.zk_disk_type_check(param_item, zk_disk_type, self.disk_type, output)
            output.update(msg_dict)
            logger.info("check zk disk type complete, result: {}".format(msg_dict))
        return zk_node_num, zk_slot, zk_disk_type


class ServerListParamCheckHook(object):
    def __init__(self):
        self.params = dict()
        self.params_check_list = [
            'openstack_exe_host_dhcp_nic', 'time_zone', 'openstack_domain', 'openstack_expansion_az',
            'expansion_pod_cluster_alarm_password', 'expansion_pod_om_float_ip', 'expansion_new_fsm_float_ip',
            'expansion_az_cluster_alarm_password', 'expansion_az_om_float_ip',
            'reuse_fsm_admin_passwd', 'expansion_az_fsm_float_ip',
            'dc002_expansion_fsm_admin_passwd', 'dc002_expansion_fsm_float_ip',
            'dc001_expansion_fsm_admin_passwd', 'dc001_expansion_fsm_float_ip',
            'expansion_fs8_fsm_fsadmin_passwd',
            'dc001_expansion_fs8_fsm_fsadmin_passwd',
            'dc002_expansion_fs8_fsm_fsadmin_passwd',
            'expansion_fs8_fsm_root_passwd',
            'dc001_expansion_fs8_fsm_root_passwd',
            'dc002_expansion_fs8_fsm_root_passwd',
            'expansion_fsm_admin_passwd', 'expansion_fsm_float_ip', 'original_az_business_storagedata01_network',
            'original_pool_storagenode_bmc_info']
        self.ip_check_list = [
            'expansion_pod_om_float_ip', 'expansion_new_fsm_float_ip', 'expansion_az_om_float_ip',
            'expansion_az_fsm_float_ip', 'dc002_expansion_fsm_float_ip', 'dc001_expansion_fsm_float_ip',
            'expansion_fsm_float_ip']
        self.fusion_storage_check_list = [
            'fusionstorage_manage_range', 'fusionstorage_manage_netmask', 'fusionstorage_manage_gateway',
            'fusionstorage_manage_vlan', 'fusionstorage_service_range', 'fusionstorage_service_netmask',
            'fusionstorage_service_gateway', 'fusionstorage_service_vlan', 'fusionstorage_rep_range1',
            'fusionstorage_rep_gateway1', 'fusionstorage_rep_range2', 'fusionstorage_rep_gateway2',
            'fusionstorage_rep_netmask1', 'fusionstorage_rep_netmask2', 'fusionstorage_arb_range',
            'fusionstorage_arb_gateway', 'fusionstorage_arb_netmask', 'fusionstorage_arb_vlan',
            'fusionstorage_inner_range', 'fusionstorage_inner_netmask', 'fusionstorage_inner_gateway',
            'fusionstorage_inner_vlan', 'iscsi_service_range', 'iscsi_service_netmask', 'iscsi_service_gateway',
            'iscsi_service_vlan']
        self.fusion_storage_ip_check_list = [
            'fusionstorage_manage_gateway', 'fusionstorage_service_gateway', 'fusionstorage_rep_gateway1',
            'fusionstorage_rep_gateway2', 'fusionstorage_arb_gateway', 'fusionstorage_inner_gateway']
        self.params_check_list.extend(self.fusion_storage_check_list)
        self.ip_check_list.extend(self.fusion_storage_ip_check_list)

    @staticmethod
    def check_netmask_when_not_three_tier(compute_network_segment_list, network_error_dict, storage_key_name,
                                          storage_network_info):
        same_plane_flag = True
        for compute_network_info in compute_network_segment_list:
            if str(compute_network_info).strip() != str(storage_network_info).strip():
                same_plane_flag = False
                break
        if not same_plane_flag:
            logger.error("The storage plane of the storage node and the storage plane of "
                         "the compute node are in a network plane")
            network_error_dict[storage_key_name] = get_code_msg('626205') % storage_key_name

    @staticmethod
    def check_netmask_when_three_tier(compute_network_segment_list, network_error_dict, storage_key_name,
                                      storage_network_info):
        same_plane_flag = True
        for compute_network_info in compute_network_segment_list:
            if str(compute_network_info).strip() != str(storage_network_info).strip():
                same_plane_flag = False
                break
        if same_plane_flag:
            logger.error("The storage plane of the storage node and the storage plane of "
                         "the compute node are not in a network plane")
            network_error_dict[storage_key_name] = get_code_msg('626204') % storage_key_name

    @staticmethod
    def check_three_tier_vlan_id_conflict(compute_network_list, error_dic, storage_vlan, storage_vlan_key):
        check_ok_flag = False
        for compute_network_plan in compute_network_list:
            if compute_network_plan["network_plane"] == "storage_data0":
                compute_vlan = compute_network_plan["network_vlan_id"]
                if compute_vlan and storage_vlan and storage_vlan != compute_vlan:
                    check_ok_flag = True
                    break
        if not check_ok_flag:
            error_dic[storage_vlan_key] = get_code_msg('626204') % storage_vlan_key

    def check(self, input_params, project_id, compute_network_list):
        err_dict = dict()
        if not input_params:
            return True, ''
        # 将input_params转换成dict
        self.params = {x['key']: x['value'] for x in input_params}

        if ProjectApi().is_ipv6_project(project_id):
            ip_version = 6
        else:
            ip_version = 4

        # 基本参数表参数检查
        logger.info('Start to check basic parameters')
        basic_parameter_input_check = InstallOSParamCheckHook(
            self.params_check_list, self.ip_check_list, [], self.fusion_storage_check_list)
        ret, detail = basic_parameter_input_check.check(input_params, project_id)
        if not ret:
            err_dict.update(detail)
        logger.info("basic parameters check result: status: {}, result: {}".format(ret, detail))
        logger.info("Parameters of fusionStorage network plane have been checked")

        # 检查原AZ业务存储存储网络子网掩码
        for param_item in input_params:
            if param_item["key"] != "original_az_business_storagedata01_network":
                continue
            logger.info("start to check original az business network")
            msg_dict = self.check_original_az_business_network(param_item, ip_version)
            err_dict.update(msg_dict)
            logger.info("original az business network check result: {}".format(msg_dict))

        # 业务存储网络与计算网络组网检查
        logger.info('Start to check three tier network')
        logger.info('Not Automatic installation. Skip')
        auto_install_flag = get_project_condition_boolean(project_id, "!TenantStorFB_Heterogeneous")
        if auto_install_flag:
            msg_dict = self.check_threetier_network(project_id, err_dict, compute_network_list)
            err_dict.update(msg_dict)
            # 计算节点网络规划
            msg_dict = self.check_threetier_vlan(project_id, err_dict, compute_network_list)
            err_dict.update(msg_dict)
            logger.info('Three tier network check is complete, result: {}'.format(msg_dict))

        # iscsi网络与业务存储网络平面冲突检查
        if err_dict:
            return False, err_dict
        logger.info('Start to check iscsi network')
        msg_dict = self.iscsi_network_check(project_id)
        err_dict.update(msg_dict)
        logger.info('iscsi network check complete, result: {}'.format(msg_dict))

        # 启用加密且使用外置密管场景下，外置密管服务器参数校验
        external_kms_flag = get_project_condition_boolean(project_id, "FsExternalEncrypted")
        if external_kms_flag:
            logger.info("Start to check external KMS info.")
            msg_dict = self.check_external_kms_info(input_params, project_id)
            err_dict.update(msg_dict)
            logger.info("External KMS info check is complete, result: {}".format(msg_dict))

        logger.info('Finish to check basic parameters')
        if not err_dict:
            return True, ''
        return False, err_dict

    def check_external_kms_info(self, params_list, project_id):
        msg_dict = dict()
        # 构造参数check list
        not_empty_check_list = ['external_kms_ip',
                                'external_kms_port',
                                'external_kms_type']
        ip_check_list = ['external_kms_ip']
        external_kms_input_check = InstallOSParamCheckHook(not_empty_check_list, ip_check_list, [], [])
        # 校验参数值是否填写、IP是否合法
        ret, detail = external_kms_input_check.check(params_list, project_id)
        if not ret:
            msg_dict.update(detail)
        # 校验端口值是否合法
        check_result = self.check_external_kms_port_value(params_list)
        msg_dict.update(check_result)
        return msg_dict

    def check_external_kms_port_value(self, params_list):
        msg_dict = dict()
        for param in params_list:
            if param.get('key') != 'external_kms_port':
                continue
            if not param.get('value'):
                msg_dict[param.get('key')] = get_code_msg('626260') % param.get('key')
                break
            if not str(param.get('value')).isdigit():
                msg_dict[param.get('key')] = get_code_msg('626365') % (param.get('key'), param.get('value'))
                break
            if int(param.get('value')) not in range(1, 65536):
                msg_dict[param.get('key')] = get_code_msg('626365') % (param.get('key'), param.get('value'))
        return msg_dict

    def check_original_az_business_network(self, param_item, ip_version):
        param_key = "original_az_business_storagedata01_network"
        error_dict = {}
        # 非空flag
        no_empty_flag = True
        # 参数格式flag
        format_error_flag = False
        if not param_item['value']:
            no_empty_flag = False
            error_dict[param_key] = get_code_msg('626260') % param_key
        if no_empty_flag is True:
            param_value = param_item['value'].split(",")
            for value in param_value:
                sub_value = value.split("/")
                if len(sub_value) != 2:
                    format_error_flag = True
                    break
                if not check_param_ip(sub_value[0], ip_version=ip_version) or not str(sub_value[1]).isdigit():
                    format_error_flag = True
                    break

            if format_error_flag is True:
                error_dict[param_key] = get_code_msg('626301') % (param_key, param_item['value'])
        return error_dict

    def check_threetier_network(self, project_id, error_msg_dict, compute_network_list):
        network_error_dict = {}
        if not get_project_condition_boolean(project_id, "TenantStorFB80&(ExpansionAZ_KVM|ExpansionServiceStorage)|"
                                                         "TenantStorFBReuse80&ExpansionAZ_KVM"):
            return network_error_dict

        storage_key_name = "fusionstorage_service_gateway"
        if storage_key_name in error_msg_dict or not compute_network_list:
            return network_error_dict
        # 获取计算节点网段信息
        compute_network_segment_list = []
        for compute_network in compute_network_list:
            if compute_network["network_plane"] == "storage_data0":
                compute_network_segment_list.append(compute_network["network_segment_plan"])

        # 获取存储节点网段信息
        storage_start_ip = self.params.get(storage_key_name, None)
        storage_netmask = self.params.get("fusionstorage_service_netmask", None)
        if len(compute_network_segment_list) > 0 and storage_start_ip and storage_netmask:
            storage_network_info = IP(storage_start_ip).make_net(storage_netmask)
            logger.info("compute network:%s, storage network:%s" % (compute_network_segment_list, storage_network_info))
            if get_project_condition_boolean(project_id, "TenantStor_ThreeTierNetwork"):
                self.check_netmask_when_three_tier(
                    compute_network_segment_list, network_error_dict, storage_key_name, storage_network_info)
            else:
                self.check_netmask_when_not_three_tier(
                    compute_network_segment_list, network_error_dict, storage_key_name, storage_network_info)
        return network_error_dict

    def check_threetier_vlan(self, project_id, error_params, compute_network_list):
        storage_vlan_key = "fusionstorage_service_vlan"
        error_dic = {}
        if not get_project_condition_boolean(
                project_id, '(TenantStorFBReuse80|TenantStorFB80)&'
                            '(!(ExpansionComputeRes_KVMNode|ExpansionNetworkRes_NetworkNode))'):
            return error_dic
        if storage_vlan_key in error_params:
            return error_dic
        if not compute_network_list:
            return error_dic
        storage_vlan = self.params.get(storage_vlan_key, None)
        # 获取计算节点vlan信息
        if get_project_condition_boolean(project_id, "TenantStor_ThreeTierNetwork"):
            self.check_three_tier_vlan_id_conflict(compute_network_list, error_dic, storage_vlan, storage_vlan_key)
        else:
            data_tuple = namedtuple('data_tuple', ['compute_network_list',
                                                  'error_dic', 'error_params', 'storage_vlan', 'storage_vlan_key'])
            data_info = data_tuple(compute_network_list,
                                   error_dic, error_params, storage_vlan, storage_vlan_key)
            self.check_ip_range_conflict_when_not_three_tier(data_info)
        return error_dic

    def check_ip_range_conflict_when_not_three_tier(self, data_info):
        compute_network_list, error_dic, error_params, storage_vlan, storage_vlan_key =\
            data_info[0], data_info[1], data_info[2], data_info[3], data_info[4]
        for compute_network_plan in compute_network_list:
            if compute_network_plan["network_plane"] != "storage_data0":
                continue
            compute_vlan = compute_network_plan["network_vlan_id"]
            logger.info("Network vlan information, compute vlan:%s, storage vlan:%s" % (compute_vlan, storage_vlan))
            if storage_vlan != compute_vlan:
                error_dic[storage_vlan_key] = get_code_msg('626205') % storage_vlan_key
            # 获取计算节点网段信息
            compute_start_ip, compute_end_ip = compute_network_plan["start_addr"], compute_network_plan["end_addr"]
            if "fusionstorage_service_gateway" not in error_params and "fusionstorage_service_start_addr" not in \
                    error_params and compute_start_ip and compute_end_ip:
                storage_end_ip, storage_start_ip = self.get_storage_service_ip()
                start = max(IP(compute_start_ip).int(), IP(storage_start_ip).int())
                end = min(IP(compute_end_ip).int(), IP(storage_end_ip).int())
                if end - start >= 0:
                    range1 = "['%s':'%s', '%s':'%s']" % ("storage start ip", storage_start_ip,
                                                         "storage end ip", storage_end_ip)
                    range2 = "['%s':'%s', '%s':'%s']" % ("compute start ip", compute_start_ip,
                                                         "compute end ip", compute_end_ip)
                    err_msg = NET_CONFLICT % (range1, range2)
                    error_dic["fusionstorage_service_range"] = err_msg

    def get_storage_service_ip(self):
        service_range = self.params.get("fusionstorage_service_range")
        if service_range and service_range.find('-'):
            storage_start_ip = service_range.split('-')[0]
            storage_end_ip = service_range.split('-')[1]
        else:
            storage_start_ip = None
            storage_end_ip = None
        return storage_end_ip, storage_start_ip

    def iscsi_network_check(self, project_id):
        """
        检查iscsi网络平面与业务存储网络平面网段是否冲突
        """
        error_dict = {}
        if not get_project_condition_boolean(
                project_id, "!TenantStorFB_Heterogeneous&!TenantStorIPSAN&"
                            "!TenantStorFCSAN&TenantStorFB_iSCSI&!TenantStorLocalDisk"):
            return error_dict
        iscsi_range = self.params.get("iscsi_service_range")
        storage_service_range = self.params.get("fusionstorage_service_range")
        iscsi_start, iscsi_end = iscsi_range.split("-")
        storage_start, storage_end = storage_service_range.split("-")

        iscsi_start = IP(iscsi_start).int()
        iscsi_end = IP(iscsi_end).int()
        storage_start = IP(storage_start).int()
        storage_end = IP(storage_end).int()

        max_start = max(iscsi_start, storage_start)
        min_end = min(iscsi_end, storage_end)
        if max_start <= min_end:
            error_dict["iscsi_service_range"] = get_code_msg("378016") % (iscsi_range, storage_service_range)
        if iscsi_start == storage_start and iscsi_end == storage_end:
            error_dict["iscsi_service_range"] = get_code_msg("378016") % (iscsi_range, storage_service_range)
        return error_dict
