# -*-coding:utf-8-*-
import collections
import math
import re
import traceback
from IPy import IP

from utils.common import log as logger
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.install_params_checkutils import InstallOSServerListCheckHook
from plugins.DistributedStorage.Deploy.params.install_params_checkutils import BusinessParametersCheckUtils
from plugins.DistributedStorage.Deploy.params.managestorfb import ManageStorFBParamCheckHook
from plugins.DistributedStorage.Deploy.params.StepCheck import StepCheckController
from plugins.DistributedStorage.Deploy.params.install_params_checkutils import TenantStorFBHCIParamsCheckUtils
from plugins.DistributedStorage.Deploy.params.install_params_checkutils import PoolCheck
from plugins.DistributedStorage.Deploy.params.install_params_checkutils import InstallOSParamCheckHook
from plugins.DistributedStorage.utils.common.DeployConstant import DeployConstant


NOT_EMPTY_CHECK_LIST = ['openstack_exe_host_dhcp_nic',
                        'fusionstorage_manage_range',
                        'fusionstorage_manage_netmask',
                        'fusionstorage_manage_gateway',
                        'fusionstorage_manage_vlan',
                        'fusionstorage_service_range',
                        'fusionstorage_service_netmask',
                        'fusionstorage_service_gateway',
                        'fusionstorage_service_vlan']
NOT_EMPTY_CHECK_LIST_REP = ['fusionstorage_rep_range1',
                            'fusionstorage_rep_gateway1',
                            'fusionstorage_rep_range2',
                            'fusionstorage_rep_gateway2',
                            'fusionstorage_rep_netmask1',
                            'fusionstorage_rep_netmask2']
NOT_EMPTY_CHECK_LIST_ARB = ['fusionstorage_arb_range',
                            'fusionstorage_arb_netmask',
                            'fusionstorage_arb_gateway']
NOT_EMPTY_CHECK_LIST_INNER = ['fusionstorage_inner_range',
                              'fusionstorage_inner_netmask',
                              'fusionstorage_inner_gateway',
                              'fusionstorage_inner_vlan']
IP_CHECK_LIST = ['fusionstorage_manage_gateway',
                 'fusionstorage_service_gateway']
IP_CHECK_LIST_REP = ['fusionstorage_rep_gateway1',
                     'fusionstorage_rep_gateway2']
IP_CHECK_LIST_ARB = ['fusionstorage_arb_gateway']
IP_CHECK_LIST_INNER = ['fusionstorage_inner_gateway']
IP_FORMATS_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']
IP_FORMATS_CHECK_LIST_REP = ['fusionstorage_rep_range1',
                             'fusionstorage_rep_gateway1',
                             'fusionstorage_rep_range2',
                             'fusionstorage_rep_gateway2',
                             'fusionstorage_rep_netmask1',
                             'fusionstorage_rep_netmask2']
IP_FORMATS_CHECK_LIST_ARB = ['fusionstorage_arb_range',
                             'fusionstorage_arb_netmask',
                             'fusionstorage_arb_gateway']
IP_FORMATS_CHECK_LIST_INNER = ['fusionstorage_inner_range',
                               'fusionstorage_inner_netmask',
                               'fusionstorage_inner_gateway',
                               'fusionstorage_inner_vlan']


def params_check(**kwargs):
    """插件参数校验接口函数
    :param kwargs:
    :return: 成功：True, ''， 失败：False，
              错误信息，格式：{key1:err_msg1, key2:err_msg2}
    """
    error_params = {}
    try:
        params = kwargs["params"]
        project_id = params["project_id"]
        cfg_flag = params.get("cfg_flag", '')
        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", '')

        # 1、分离部署参数检查
        check_separate_params(params, error_params)

        # 2、融合部署参数校验
        # 2.1 管理融合设备信息检查
        logger.info("ManageStorFBParamCheckHook: 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)
        # 2.2 业务融合设备信息检查
        logger.info("TenantStorFBHCI80: Start to check the parameters: fs_server_list")
        ret, detail = check_tenantstorfbhci_param(project_id, fs_server_list)
        logger.info('TenantStorFBHCI80 result: %s' % detail)
        if not ret:
            error_params.update(detail)

        # 3、基本参数检查
        ret, detail = ServerListParamCheckHook().check(params_list, project_id, cfg_flag)
        logger.info('param_list check status: %s, output: %s' % (ret, detail))
        if not ret:
            error_params.update(detail)
        if error_params:
            return False, error_params

        # 4、工步中涉及到的LLD表检查
        ret, detail = StepCheckController(project_id).get_condition(fs_server_list, fusion_storage_business_server_list)
        logger.info('status: %s, output: %s' % (ret, detail))
        if not ret:
            error_params.update(detail)
            return False, error_params
        return True, ""

    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


def check_separate_params(params, error_params):
    project_id = params["project_id"]
    params_list = params.get("param_list", '')
    fusion_storage_business_server_list = params.get("fusionstorage_business_server_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)
    if not fusion_storage_business_server_list and get_project_condition_boolean(project_id, 'TenantStorFB80'):
        error_params["fusionstorage_business_server_list"] = get_code_msg('626023')
    logger.info("Start to check the parameters: fusionstorage_business_server_list")
    hook_obj = FusionStorageParamCheckHook(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)


def check_tenantstorfbhci_param(project_id, input_params):
    # 业务存储融合部署场景设备信息sheet页相关参数校验
    err_msg_dict = {}
    if not get_project_condition_boolean(project_id, 'TenantStorFBHCI80'):
        return True, err_msg_dict
    pool_check = PoolCheck()
    # 元数据节点数量检测
    zk_node_num = 0
    zk_slot = ""
    kvm_dpdk_node_num = 0
    primary_capacity = 0
    cache_type = ""
    cache_capacity = 0
    for param_item in input_params:
        if "kvm" not in param_item["bmc_role"] and "dpdk" not in param_item["bmc_role"]:
            continue
        kvm_dpdk_node_num += 1
        if "1" == param_item["is_metadata_node"]:
            zk_node_num += 1
            # zk节点zk槽位值检测
            zk_slot, msg_dict = TenantStorFBHCIParamsCheckUtils.zk_slot_check(param_item, zk_slot, err_msg_dict)
            err_msg_dict.update(msg_dict)

        # 主存槽位的检测
        msg_dict = TenantStorFBHCIParamsCheckUtils.primary_slot_check(param_item, err_msg_dict)
        err_msg_dict.update(msg_dict)

        # 主存盘容量的检测
        primary_capacity, msg_dict = TenantStorFBHCIParamsCheckUtils.primary_capacity_check(
            param_item, primary_capacity, err_msg_dict)
        err_msg_dict.update(msg_dict)

        # 缓存类型的检测
        cache_type, msg_dict = TenantStorFBHCIParamsCheckUtils.cache_type_check(param_item, cache_type, err_msg_dict)
        err_msg_dict.update(msg_dict)

        #  缓存（卡、盘）总容量(GB)的检测
        cache_capacity, msg_dict = TenantStorFBHCIParamsCheckUtils.cache_capacity_check(
            param_item, cache_capacity, err_msg_dict)
        err_msg_dict.update(msg_dict)

        # 元数据槽位与主存槽位冲突检查
        msg_dict = TenantStorFBHCIParamsCheckUtils.zk_slot_and_primary_slot_conflict_check(param_item, err_msg_dict)
        err_msg_dict.update(msg_dict)
        # 记录磁盘数量
        disk_slot = param_item["primary_slot"]
        if disk_slot:
            disk_num = int(disk_slot.split('-')[1]) - int(disk_slot.split('-')[0]) + 1
            pool_check.record_disk_num(disk_num)
    logger.info("The number of metadata nodes is:%s" % str(zk_node_num))
    logger.info("The number of compute nodes is:%s" % str(kvm_dpdk_node_num))
    if zk_node_num not in [3, 5, 7]:
        err_msg_dict["tenantstorfbhci_is_metadata_node"] = get_code_msg('626214') % str(zk_node_num)
    if err_msg_dict:
        return False, err_msg_dict

    # 检查存储池中服务器上盘的数量是否满足要求
    check_result = pool_check.check_disk_num('BusinessStorage')
    if check_result:
        err_msg_dict.update(check_result)
        return False, err_msg_dict
    return True, err_msg_dict


class ServerListParamCheckHook(object):
    def __init__(self):
        self.params = dict()

    def check(self, input_params, project_id, cfg_flag):
        error_params = dict()
        if not input_params:
            return True, ''
        # 将input_params转换成dict
        self.params = dict([(x['key'], x['value']) for x in input_params])
        # 构造业务存储网络平面参数check_list
        not_empty_check_list = NOT_EMPTY_CHECK_LIST
        ip_check_list = IP_CHECK_LIST
        ip_formats_check_list = IP_FORMATS_CHECK_LIST
        # 复制节点和存储节点融合部署场景
        is_mixed = get_project_condition_boolean(project_id, "(CSHAStorage_TFB|CSDRStorage_TFB)&!DRStorage_TFB_Sep")
        if is_mixed:
            not_empty_check_list.extend(NOT_EMPTY_CHECK_LIST_REP)
            ip_check_list.extend(IP_CHECK_LIST_REP)
            ip_formats_check_list.extend(IP_FORMATS_CHECK_LIST_REP)
            if get_project_condition_boolean(project_id, "CSHAStorage_TFB"):
                not_empty_check_list.extend(NOT_EMPTY_CHECK_LIST_ARB)
                ip_check_list.extend(IP_CHECK_LIST_ARB)
                ip_formats_check_list.extend(IP_FORMATS_CHECK_LIST_ARB)
        # 前、后端独立组网场景
        backend_net_independent = get_project_condition_boolean(project_id, "TenantStorBackendNetSep")
        if backend_net_independent:
            not_empty_check_list.extend(NOT_EMPTY_CHECK_LIST_INNER)
            ip_check_list.extend(IP_CHECK_LIST_INNER)
            ip_formats_check_list.extend(IP_FORMATS_CHECK_LIST_INNER)

        # 自动装机场景下业务存储网络平面参数检查，复制融合部署还包括复制网络平面和仲裁网络平面
        logger.info('Start to check basic parameters')
        auto_install_flag = get_project_condition_boolean(project_id, "TenantStorFB80&!TenantStorFB_Heterogeneous")
        if not cfg_flag:
            storage_plane_input_check = InstallOSParamCheckHook(
                not_empty_check_list, ip_check_list, [], ip_formats_check_list)
            ret, detail = storage_plane_input_check.check(input_params, project_id)
            if not ret:
                error_params.update(detail)
            logger.info("basic parameters check result: status: {}, result: {}".format(ret, detail))

        # 自动装机场景下三层组网参数校验
        logger.info('Start to check three tier network')
        # 非自动装机跳过
        if auto_install_flag:
            msg_dict = self.check_three_tier_network(project_id)
            error_params.update(msg_dict)
            logger.info('Three tier network check is complete, result: {}'.format(msg_dict))

        # 计算节点存储网络平面参数检查，非必填。该平面未填写时，与管理网络共平面（management_storage_data_vlan_id）
        logger.info('Start to check business computer storage network')
        msg_dict = self.check_business_computer_storage_network(input_params)
        error_params.update(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)
            error_params.update(msg_dict)

        logger.info('Finish to check basic parameters')
        return (False, error_params) if error_params else (True, '')

    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)
        logger.info("External KMS info check is complete, result: {}".format(msg_dict))
        return msg_dict

    @staticmethod
    def check_external_kms_port_value(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_thress_tier_network_sub_get_computer_net(self):
        # 获取计算节点网段信息
        if self.params.get("service_storage_data_vlan_id"):
            compute_net_range = self.params.get("service_storage_data_range")
            compute_netmask = self.params.get("service_storage_data_netmask")
            compute_vlan = self.params.get("service_storage_data_vlan_id")
        else:
            compute_net_range = self.params.get("management_storage_data_range")
            compute_netmask = self.params.get("management_storage_data_netmask")
            compute_vlan = self.params.get("management_storage_data_vlan_id")
        compute_start_ip = compute_net_range.split('-')[0]
        return compute_start_ip, compute_netmask, compute_vlan

    def check_three_tier_network(self, project_id):
        network_error_dict = dict()
        # 获取存储节点网段信息
        storage_key_name = "fusionstorage_service_gateway"
        storage_vlan_key_name = "fusionstorage_service_vlan"
        storage_start_ip = self.params.get(storage_key_name)
        storage_netmask = self.params.get("fusionstorage_service_netmask")
        storage_vlan = self.params.get(storage_vlan_key_name)
        self.check_three_tier_network_empty(storage_start_ip,
                                            storage_key_name, network_error_dict)
        self.check_three_tier_network_empty(storage_netmask,
                                            "fusionstorage_service_netmask", network_error_dict)
        self.check_three_tier_network_empty(storage_vlan,
                                            storage_vlan_key_name, network_error_dict)
        if network_error_dict:
            return network_error_dict
        compute_start_ip, compute_netmask, compute_vlan = \
            self.check_thress_tier_network_sub_get_computer_net()
        if not compute_start_ip or not compute_netmask or not compute_vlan:
            return network_error_dict

        compute_network_info = IP(compute_start_ip).make_net(compute_netmask)
        storage_network_info = IP(storage_start_ip).make_net(storage_netmask)
        if get_project_condition_boolean(project_id, "TenantStor_ThreeTierNetwork")\
                and compute_network_info == storage_network_info:
            network_error_dict[storage_key_name] = get_code_msg('626204') % storage_key_name
        if get_project_condition_boolean(project_id, "TenantStor_ThreeTierNetwork")\
                and storage_vlan == compute_vlan:
            network_error_dict[storage_vlan_key_name] = get_code_msg('626204') % storage_vlan_key_name
        if not get_project_condition_boolean(project_id, "TenantStor_ThreeTierNetwork") \
                and compute_network_info != storage_network_info:
            network_error_dict[storage_key_name] = get_code_msg('626205') % storage_key_name
        if not get_project_condition_boolean(project_id, "TenantStor_ThreeTierNetwork")\
                and storage_vlan != compute_vlan:
            network_error_dict[storage_vlan_key_name] = get_code_msg('626205') % storage_vlan_key_name
        return network_error_dict

    @staticmethod
    def check_three_tier_network_empty(value, key, error_dict):
        if not value:
            error_dict[key] = get_code_msg('626260') % key

    @staticmethod
    def check_empty_value(value):
        if not value:
            return True
        elif value == '':
            return True
        else:
            return False

    @staticmethod
    def check_business_computer_storage_network(params_list):
        check_list = ['service_storage_data_range',
                      'service_storage_data_netmask',
                      'service_storage_data_gateway']
        storage_vlan = 'service_storage_data_vlan_id'
        msg_dict = dict()
        find_flag = False
        storage_vlan_value = None
        for param in params_list:
            if storage_vlan == param.get('key'):
                storage_vlan_value = param.get('value')
                find_flag = True
                break
        logger.info("Get value of parameter['service_storage_data_vlan_id']:%s" % storage_vlan_value)

        if find_flag and storage_vlan_value:
            for param in params_list:
                param_key = param.get('key')
                if param_key not in check_list:
                    continue
                param_value = param.get('value')
                if ServerListParamCheckHook.check_empty_value(param_value):
                    msg_dict[param_key] = get_code_msg('626271') % (param_key, param_value, param_key, param_value)
        logger.info("Finish to check business computer storage network. Detail:%s" % msg_dict)
        return msg_dict


class FusionStorageParamCheckHook(object):

    def __init__(self, project_id):
        self.ip_check_list = ["bmc_ip", "manageIp", "storageIp", "storageInnerIp"]
        self.check_list = [
            "equipment_model", "hostname", "bmc_ip", "bmc_name", "bmc_passwd",
            "creuser", "storage_pool_name_and_slot", "is_metadata_node", "cache_type",
            "storagepool_redundancy_policy", "primary_type"]
        self.backend_check_list = ["storageInnerIp", "storage_inner_plane"]
        if get_project_condition_boolean(project_id, "TenantStorBackendNetSep"):
            self.check_list.extend(self.backend_check_list)
        if get_project_condition_boolean(project_id, "!TenantStorFB_Heterogeneous"):
            self.check_list.extend(["management_plane", "storage_plane"])
        else:
            self.check_list.extend(["manageIp", "storageIp"])
        self.disk_type = DeployConstant.LLD_DISK_TYPE
        self.support_cache = ['ssd_card', 'ssd_disk', 'none']
        self.utils = BusinessParametersCheckUtils()
        self.node_num = collections.defaultdict(int)
        self.osd_node_list = list()
        self.pool_info_list = list()
        self.rack_dict = collections.defaultdict(set)
        self.storage_pool_name_set = set()
        self.zk_node_num = 0

    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

    @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] = ""

    def check(self, input_params, backend_pool_list, project_id='10'):
        """检测用户输入的parameter参数，检测取值格式， 范围等合法性检测

                :param input_params:“fusionstorage_business_server_list”: [
                {“bmc_ip”: “*.*.*.*”, “bmc_passwd”: “******”},...
                ],
                :param backend_pool_list:[
                "Type1_Business_Pool1,Type1_Business_Pool2",
                "Type2_Business_Pool1",...
                ]
                :return:
                """
        if not input_params:
            return True, ''
        self.handle_none_type_params(input_params)
        output = dict()
        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"

        # 对参数按照存储池名进行排序
        if input_params[0] and "storage_pool_name_and_slot" in input_params[0]:
            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')
        self.basic_params_check(fb_rack_ha, input_params, output, project_id)

        # 检查单个存储池的主存、缓存、冗余策略、EC参数
        storage_pool_name_list = list(self.storage_pool_name_set)
        logger.info('storage_pool_name_list: %s，osd number: %s' % (storage_pool_name_list, len(self.osd_node_list)))
        check_result = self.utils.storage_pool_params_check(
            storage_pool_name_list, self.osd_node_list, self.disk_type, self.support_cache)
        output.update(check_result)
        logger.info("check storage pool parameters complete, result: {}".format(check_result))

        # 校验EC策略下：同一资源池osd节点数量是否满足最低数量要求，机柜高可用场景机柜数量是否满足最低要求
        self.check_node_nums_when_ec_policy(fb_rack_ha, input_params, output, storage_pool_name_list)

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

        # 全闪场景，主存盘类型检查
        logger.info("start to check primary type in the all-flash scenario")
        check_result = self.utils.all_flash_primary_type_check(self.osd_node_list, output)
        output.update(check_result)

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

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

        if output:
            return False, output
        return True, output

    def basic_params_check(self, fb_rack_ha, input_params, output, project_id):
        """
        以下仅针对业务存储osd相关参数的检查，复制与业务的分开，不用考虑复制的
        """
        logger.info("Start to check the parameters: fusionstorage_business_server_list")
        zk_slot = ""
        zk_disk_type = None
        hostname = {"node_num": 0, "hostname_set": set()}
        server_list_check = InstallOSServerListCheckHook(self.check_list, self.ip_check_list, [])
        for param_item in input_params:
            # 排除复制与存储分离场景的rep节点
            if "deploy_component" in param_item and param_item.get("deploy_component") \
                    and "osd" not in param_item["deploy_component"]:
                continue
            self.osd_node_list.append(param_item)
            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槽位检查
            self.check_zk_params(output, param_item, zk_disk_type, zk_slot)

            # 主存槽位的检测
            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 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("check primary and zk slot conflict complete")

            # 系统帐户密码的检查
            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")
            self.node_num[storage_pool_name] += 1
            # 开启机柜高可用
            self.check_rack_ha_params(fb_rack_ha, output, param_item, storage_pool_name)
            self.storage_pool_name_set.add(storage_pool_name)

    def check_rack_ha_params(self, fb_rack_ha, output, param_item, storage_pool_name):
        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"
            if rack_id:
                self.rack_dict[storage_pool_name].add(rack_id)
            # 构造pool_info_list
            logger.info("Cabinet HA: query pool information")
            self.pool_info_list = self.utils.get_rack_ha_pool_info_list(
                param_item, storage_pool_name, self.storage_pool_name_set, self.pool_info_list)
            logger.info("Query result: {}".format(self.pool_info_list))

    def check_zk_params(self, output, param_item, zk_disk_type, zk_slot):
        if "1" == param_item.get("is_metadata_node"):
            self.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 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))

    def check_node_nums_when_ec_policy(self, rack_ha, input_params, output, storage_pool_list):
        """
        校验EC策略下：同一资源池osd节点数量是否满足最低数量要求，机柜高可用场景机柜数量是否满足最低要求
        """
        for pool in storage_pool_list:
            # 如果EC相关参数已经有误，跳过此项检查，避免脚本执行错误
            if "ec_data_fragments" in output or "storagepool_redundancy_policy" in output:
                break
            for param_item in input_params:
                if pool != param_item.get("storage_pool_name_and_slot"):
                    continue
                pool_policy = param_item.get("storagepool_redundancy_policy")
                if pool_policy.upper() != 'EC':
                    continue
                self.node_nums_check(output, param_item, rack_ha, pool)

    def node_nums_check(self, output, param_item, rack_ha, pool):
        ec_data_fragments = float(param_item.get("ec_data_fragments"))
        min_node_or_rack = int(math.ceil((ec_data_fragments + 2) / 2.0))
        if self.node_num[pool] < min_node_or_rack:
            output["ec_data_fragments"] = get_code_msg('626095') % (int(ec_data_fragments), min_node_or_rack)
        if rack_ha:
            logger.info("Cabinet Set:%s" % self.rack_dict[pool])
            rack_num = len(self.rack_dict[pool])
            if rack_num < min_node_or_rack:
                output["rack_id and ec_data_fragments"] = get_code_msg('626096') % (ec_data_fragments, min_node_or_rack)

    @staticmethod
    def _check_network_plane_port(project_id, node_info):
        err_results = dict()
        if not get_project_condition_boolean(project_id, '!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))
        return err_results

    @staticmethod
    def check_separate_pool_disk_num(pool_list, server_list, error_dict):
        result = dict()
        # 主存槽位不合规，跳过该项检查，避免脚本执行错误
        if "primary_slot" in error_dict:
            return result

        for pool in pool_list:
            pool_check = PoolCheck()
            for server in server_list:
                if server["storage_pool_name_and_slot"] != pool:
                    continue
                disk_slot = server["primary_slot"]
                if disk_slot:
                    disk_num = int(disk_slot.split('-')[1]) - int(disk_slot.split('-')[0]) + 1
                    pool_check.record_disk_num(disk_num)
            ret_msg = pool_check.check_disk_num(pool)
            if ret_msg:
                result.update(ret_msg)
        return result
