# -*- coding:utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import re
import types
import traceback
from platforms.project.ProjectUtils import get_project_condition_boolean
from utils.business.vm_util import can_vm_pinged_to
from utils.common import log as logger
from utils.common import param_check
from utils.common.error.fcd_error_code import get_code_msg
from plugins.DistributedStorage.scripts.utils.common.DeployConstant import DeployConstant
from plugins.DistributedStorage.params.network_utils import is_ip_in_section
from plugins.DistributedStorage.params.network_utils import GW_ERROR_CONTAIN

RETURNVALUE = (True, "")
REPLICATION_NODE = []
logger.init("FusionStorageBlockReplication")


def params_check(**kwargs):
    try:
        params_check_main(kwargs)
    except Exception as e:
        logger.error("FS800Rep params check is abnormal: {}".format(e))
        logger.error(traceback.format_exc())
        error_params = dict()
        error_params['params check'] = "The script is abnormal."
        return False, error_params
    return get_return_value()


def params_check_main(kwargs):
    params = kwargs.get("params")
    project_id = params["project_id"]
    param_list = params.get('param_list')
    # 先检查场景，在确定需要检查的项以及相对应的取参位置和key值, 然后遍历 检查参数，
    if get_project_condition_boolean(project_id, "!DRStorage_TFB_PD"):
        # 当只有是灾备存储FS的是才会有远端参数，校验远端参数
        flag, res = remote_and_quom_params_check(param_list, project_id)
        if not flag:
            update_return_value(res)
    # 追加独立部署 / 全新建独立部署 / 复制和存储融合部署计算独立全新建
    if get_project_condition_boolean(project_id, "DRStorage_TFB_Sep|((CSHAStorage_TFB|CSDRStorage_TFB)&!"
                                                 "DRStorage_TFB_Sep&!ExpansionAdCloudService&!TenantStorFBHCI&!"
                                                 "TenantStorFBHCI80)"):
        flag, res = local_params_check(param_list, project_id)
        if not flag:
            update_return_value(res)
        # 校验华为分布式块存储分离部署参数复制集群相关参数
        rep_cluster_info_check_in_fusionstorage_business_server_list_sheet(params, project_id)
    # 复制&存储&计算融合部署
    if get_project_condition_boolean(project_id, "(CSHAStorage_TFB|CSDRStorage_TFB)&"
                                                 "(TenantStorFBHCI|TenantStorFBHCI80)&!DRStorage_TFB_Sep"):
        flag, res = local_params_check(param_list, project_id)
        if not flag:
            update_return_value(res)
        # 1.1sheet中复制相关参数校验
        replicate_info = params.get("fs_server_list")
        logger.info("replicate_info = [%s]" % replicate_info)
        flag, res = replicate_check(replicate_info, project_id)
        if not flag:
            update_return_value(res)
    # 自动化装机时所需1.2参数校验
    auto_install_params_check(param_list, project_id)


def rep_cluster_info_check_in_fusionstorage_business_server_list_sheet(params, project_id):
    replicate_cluster_info = params.get("fusionstorage_business_server_list")
    if not replicate_cluster_info:
        logger.error('The sheet of fusionstorage_business_server_list parameter cannot be empty')
        update_return_value({"fusionstorage_business_server_list": get_code_msg('626023')})
    else:
        logger.info("start to check replicate_cluster_info in fusionstorage_business_server_list")
        flag, res = replicate_cluster_check(replicate_cluster_info, project_id)
        if not flag:
            update_return_value(res)
        deploy_component_params_check(project_id, replicate_cluster_info)


def auto_install_params_check(param_list, project_id):
    if get_project_condition_boolean(project_id, "!TenantStorFB_Heterogeneous"):
        key_list = ['fusionstorage_rep_range1', 'fusionstorage_rep_netmask1',
                    'fusionstorage_rep_gateway1', 'fusionstorage_rep_range2',
                    'fusionstorage_rep_netmask2', 'fusionstorage_rep_gateway2']
        # CSHA灾备端需要仲裁相关参数校验
        if get_project_condition_boolean(project_id, "!DRStorage_TFB_PD&(CSHAStorage_TFB)"):
            key_list.append('fusionstorage_arb_range')
            key_list.append('fusionstorage_arb_netmask')
            key_list.append('fusionstorage_arb_gateway')
            key_list.append('fusionstorage_arb_vlan')
        ip_key_list = None
        flag, res = baseparamscheck(param_list, key_list, ip_key_list)
        if not flag:
            update_return_value(res)

        # 检查复制平面可用ip数量
        flag, res = check_rep_address(param_list)
        if not flag:
            update_return_value(res)

        # 检查复制平面网关与ip地址范围是否冲突
        flag, res = check_rep_gateway(param_list)
        if not flag:
            update_return_value(res)


def deploy_component_params_check(project_id, replicate_cluster_info):
    # 复制集群独立部署场景下1.4Sheet部署组件不能出现“osd,rep_ctl”
    err_data = dict()
    if get_project_condition_boolean(project_id, 'DRStorage_TFB_Sep'):
        for _index, item in enumerate(replicate_cluster_info):
            if "osd,rep_ctl" in item.get("deploy_component"):
                err_msg = 'The deploying components of LLD 1.4Sheet cannot be "osd,rep_ctl" when the ' \
                          'replication cluster is deployed independently'
                logger.info(err_msg)
                err_data['deploy_component'] = get_code_msg(627431)
                update_return_value(err_data)
                break
    else:
        osd_rep_ctl_num = 0
        for _index, item in enumerate(replicate_cluster_info):
            if "osd,rep_ctl" in item.get("deploy_component"):
                osd_rep_ctl_num += 1
        if osd_rep_ctl_num < 3:
            err_msg = 'The deploying components of LLD 1.4Sheet cannot be "osd,rep_ctl" when the replication ' \
                      'cluster is deployed independently'
            logger.info(err_msg)
            err_data['deploy_component'] = get_code_msg(627432)
            update_return_value(err_data)


def local_params_check(params, project_id):
    # 生产场景，本端及是生产端
    ret = True, None
    if get_project_condition_boolean(project_id, "DRStorage_TFB_PD"):
        key_list = ['produce_storage_fsm_ip', 'produce_storage_fsm_dsware_password',
                    'produce_storage_fsm_root_password']
        ip_key_list = ["produce_storage_fsm_ip"]
        # 全新建场景 生产端ip由Openstack分配，用户不必填写
        '''produce_storage_fsm_dsware_password与produce_storage_fsm_root_password参数全新建场景下也不需要校验，读取默认值'''
        if get_project_condition_boolean(project_id, "!ExpansionAdCloudService"):
            return ret
    # 灾备场景，本端及是灾备端
    else:
        key_list = ['disaster_tolerant_storage_fsm_ip', 'disaster_tolerant_storage_fsm_dsware_password',
                    'disaster_tolerant_storage_fsm_root_password']
        ip_key_list = ["disaster_tolerant_storage_fsm_ip"]
        # 全新建场景 灾备端ip由Openstack分配，用户不必填写
        '''produce_storage_fsm_dsware_password与produce_storage_fsm_root_password参数全新建场景下也不需要校验，读取默认值'''
        if get_project_condition_boolean(project_id, "!ExpansionAdCloudService"):
            return ret
    return baseparamscheck(params, key_list, ip_key_list)


def remote_and_quom_params_check(params, project_id):
    """远端参数和仲裁服务器参数校验"""
    key_list = ['storage_arbitration_manage_ip', 'storage_arbitration_root_password',
                'produce_storage_fsm_ip', 'produce_storage_fsm_dsware_password']
    ip_key_list = ['storage_arbitration_manage_ip', 'produce_storage_fsm_ip']

    # CSDR场景校验远端参数、复制存储融合&计算分离场景校验远端参数,其他场景灾备场景两者均校验
    if get_project_condition_boolean(project_id, "CSDRStorage_TFB|(!DRStorage_TFB_Sep&TenantStorFBHCI)"):
        key_list.remove("storage_arbitration_manage_ip")
        key_list.remove("storage_arbitration_root_password")
        ip_key_list.remove("storage_arbitration_manage_ip")
    return baseparamscheck(params, key_list, ip_key_list)


def baseparamscheck(params, key_list, ip_key_list):
    output = dict()
    for key in key_list:
        value = get_params_value_by_key(params, key)
        if not check_param_null(value):
            logger.error("parameter[%s] must be not empty." % key)
            output[key] = get_code_msg(627100) % key
            continue
        if not value:
            logger.error("parameter[%s] in not find." % key)
            output[key] = get_code_msg(627100) % key
            continue
        if ip_key_list:
            if key in ip_key_list:
                check_ip_return_msg(value, output, ping=False, is_fs_param=False)
    if output:
        return False, output
    return True, output


def check_ip_return_msg(item, output, ping=False, is_fs_param=True):
    """检查IP地址合法性与可达性"""
    code_msg_dict = {
        'user_input': [get_code_msg(627100) % item['key'], get_code_msg(627099) % (item['key'], item['value'])],
        'manageIp': [get_code_msg(627093), get_code_msg(627089) % item['value']],
        'storageIp': [get_code_msg(627092), ''],
        'replication_plane_ip': [get_code_msg(627091), ''],
        'arbitration_plane_ip': [get_code_msg(627090), ''],
        'bmc_ip': [get_code_msg(627101), get_code_msg(627102) % item['value']]
    }
    if not item['value']:
        logger.error("%s ip can not empty." % item['key'])
        if is_fs_param:
            output[item['key']] = code_msg_dict.get(item['key'])[0]
        else:
            output[item['key']] = code_msg_dict.get('user_input')[0]
        return
    if not param_check.check_param_ip(item['value']):
        logger.error('%s ip is invalid.detail: %s' % (item['key'], item['value']))
        if is_fs_param:
            output[item['key']] = code_msg_dict.get(item['key'])[0]
        else:
            output[item['key']] = code_msg_dict.get('user_input')[0]
        return
    if not ping:
        return
    if not can_vm_pinged_to(item['value']):
        logger.error('%s ip is reachable.' % item['value'])
        if is_fs_param:
            output[item['key']] = code_msg_dict.get(item['key'])[1]
        else:
            output[item['key']] = code_msg_dict.get('user_input')[1]
        return


def replicate_cluster_check(params, project_id):
    # 1.4参数检查
    if not params:
        return False, {"replicate_cluster_info": "replicate cluster info can not be empty."}
    output = dict()
    control_cluster_list = []
    service_cluster_list = []
    rep_zk_disk_type = {'rep_zk_type': set(), 'basic_zk': 0}
    hostname_info = {"node_num": 0, "hostname_set": set()}
    check_rep_params = {'params': params,
                        'project_id': project_id,
                        'rep_zk_disk_type': rep_zk_disk_type,
                        'hostname_info': hostname_info}
    # 不自动化装机时 参数检查
    if get_project_condition_boolean(project_id, "TenantStorFB_Heterogeneous"):
        check_rep_param_no_odk(check_rep_params, service_cluster_list, output, control_cluster_list)
        # 校验控制集群节点个数业务集群节点个数
        check_cluster(control_cluster_list, service_cluster_list, output)
        if rep_zk_disk_type.get('basic_zk') not in [0, len(control_cluster_list)]:
            logger.error('rep_meta_disk_plan_error: basic_zk num: {}'.format(rep_zk_disk_type.get('basic_zk')))
            output['rep_meta_disk_plan_error'] = get_code_msg(627454)
        if output:
            return False, output
        return True, ""
    # 自动化装机时 参数检查
    else:
        check_rep_param_with_odk(check_rep_params, service_cluster_list, control_cluster_list, output)
        # 校验控制集群节点个数业务集群节点个数
        check_cluster(control_cluster_list, service_cluster_list, output)
        if rep_zk_disk_type.get('basic_zk') not in [0, len(control_cluster_list)]:
            logger.error('rep_meta_disk_plan_error: basic_zk num:{}'.format(rep_zk_disk_type.get('basic_zk')))
            output['rep_meta_disk_plan_error'] = get_code_msg(627454)
        if output:
            return False, output
        return True, ""


def check_rep_param_no_odk(check_rep_params, service_cluster_list, output, control_cluster_list):
    params = check_rep_params.get('params')
    project_id = check_rep_params.get('project_id')
    rep_zk_disk_type = check_rep_params.get('rep_zk_disk_type')
    hostname_info = check_rep_params.get('hostname_info')
    for _index, item in enumerate(params):
        # 复制节点相关参数
        if "rep" in item.get("deploy_component"):
            service_cluster_list.append(item)
            if not item.get('bmc_ip'):
                logger.error("bmc_ip can not empty.")
                output['bmc_ip'] = get_code_msg(627101)
            bmc_ip = item.get("bmc_ip")
            if bmc_ip:
                bmc_ip_dict = {'key': 'bmc_ip', 'value': bmc_ip}
                check_ip_return_msg(bmc_ip_dict, output, ping=True)
            check_manage_storage_ip(item, output)
            # 复制网络平面ip校验
            check_replication_plane_ip(item, output)
            # 仲裁网络平面ip校验
            if get_project_condition_boolean(project_id, "CSHAStorage_TFB|CSHA"):
                arbitration_plane_ip = {'key': 'arbitration_plane_ip',
                                        'value': item.get("arbitration_plane_ip")}
                check_ip_return_msg(arbitration_plane_ip, output)
            if "rep_ctl" in item.get("deploy_component"):
                control_cluster_list.append(item)
                # 复制集群元数据信息校验
                check_rep_meta_info(item, output, project_id, rep_zk_disk=rep_zk_disk_type)
            # 检查主机名
            check_hostname(hostname_info, item, output)
            # 系统账户，密码校验
            check_system_account(item, output)


def check_manage_storage_ip(item, output):
    if not item.get('manageIp'):
        # 默认所有节点加入到业务集群
        key = "manageIp"
        logger.error("%s can not empty." % key)
        output[key] = get_code_msg(627093)
    manage_ip = item.get("manageIp").split("/")[0]
    if manage_ip:
        manage_ip_dict = {'key': 'manageIp', 'value': manage_ip}
        check_ip_return_msg(manage_ip_dict, output)
    if not item.get('storageIp'):
        key = "storageIp"
        logger.error("%s can not empty." % key)
        output[key] = get_code_msg(627092)
    storage_ip = item.get("storageIp").split("/")[0]
    if storage_ip:
        storage_ip_dict = {'key': 'storageIp', 'value': storage_ip}
        check_ip_return_msg(storage_ip_dict, output)


def check_replication_plane_ip(item, output):
    if not item.get('replication_plane_ip'):
        key = "replication_plane_ip"
        logger.error("%s can not empty." % key)
        output[key] = get_code_msg(627091)
    else:
        rep_ip_list = item.get("replication_plane_ip").split(',')
        if len(rep_ip_list) == 2:
            replication_plane_ip1 = {'key': 'replication_plane_ip',
                                     'value': item.get("replication_plane_ip").split(',')[0]}
            check_ip_return_msg(replication_plane_ip1, output)
            replication_plane_ip2 = {'key': 'replication_plane_ip',
                                     'value': item.get("replication_plane_ip").split(',')[1]}
            check_ip_return_msg(replication_plane_ip2, output)
        else:
            key = "replication_plane_ip"
            logger.error("%s is invalid." % key)
            output[key] = get_code_msg(627091)


def check_rep_param_with_odk(check_rep_params, service_cluster_list, control_cluster_list, output):
    params = check_rep_params.get('params')
    project_id = check_rep_params.get('project_id')
    rep_zk_disk_type = check_rep_params.get('rep_zk_disk_type')
    hostname_info = check_rep_params.get('hostname_info')
    for _index, item in enumerate(params):
        if "rep" in item.get("deploy_component"):
            service_cluster_list.append(item)
            if "rep_ctl" in item.get("deploy_component"):
                control_cluster_list.append(item)
            check_hostname(hostname_info, item, output)

            check_bmc_params(item, output)
            # 管理平面、存储平面、复制平面的校验
            check_network_plane(item, output)
            check_rep_net_plane(item, output)
            # 复制集群元数据信息校验
            if "rep_ctl" in item.get("deploy_component"):
                check_rep_meta_info(item, output, project_id, rep_zk_disk=rep_zk_disk_type)
            # 系统账户，密码校验
            check_system_account(item, output)


def check_bmc_params(item, output):
    # bmc_ip校验
    if not item.get('bmc_ip'):
        logger.error("bmc_ip can not empty.")
        output['bmc_ip'] = get_code_msg(627101)
    bmc_ip = {'key': 'bmc_ip', 'value': item.get("bmc_ip")}
    check_ip_return_msg(bmc_ip, output, ping=False)
    # BMC用户名
    if not item.get("bmc_name"):
        key = "bmc_name"
        logger.error("bmc_name can not be empty.")
        output[key] = get_code_msg(627113)
    if not item.get("bmc_passwd"):
        key = "bmc_passwd"
        logger.error("bmc_passwd can not be empty.")
        output[key] = get_code_msg(627113)


def check_hostname(hostname_info, item, output):
    msg_dict = dict()
    if not item.get("hostname"):
        key = "hostname"
        logger.error("hostname can not be empty.")
        msg_dict[key] = get_code_msg(627112)
    else:
        pattern = re.compile(r'^(?!-|\.)[A-Za-z0-9-\.]{1,64}(?!-|\.)$')
        m = pattern.match(item.get("hostname"))
        if not m:
            key = "hostname"
            logger.error("hostname is invalid.")
            msg_dict[key] = get_code_msg(627112)
    if msg_dict:
        output.update(msg_dict)
        return
    # 存储节点的主机名不能相同
    hostname_info["node_num"] += 1
    hostname_info["hostname_set"].add(item.get('hostname'))
    if hostname_info["node_num"] != len(hostname_info["hostname_set"]):
        output["hostname"] = get_code_msg('627449') % hostname_info["hostname_set"]


def replicate_check(params, project_id):
    """
    复制&存储&计算融合 1.1设备信息复制相关参数校验
    :param params:
    :param project_id:
    :return:
    """
    if not params:
        return False, {"replicate_cluster_info": "replicate cluster info can not be empty."}
    output = dict()
    control_cluster_list = []
    service_cluster_list = []
    for _index, item in enumerate(params):
        # 复制节点相关参数
        if "rep_ctl" in item.get("ref_component"):
            control_cluster_list.append(item)
            check_rep_meta_info(item, output, project_id, is_fs_param=False)
        if "rep" in item.get("ref_component"):
            service_cluster_list.append(item)
    # 校验控制集群节点个数业务集群节点个数
    check_cluster(control_cluster_list, service_cluster_list, output, is_fs_param=False)
    if output:
        return False, output
    return True, ""


def get_params_value_by_key(params, key):
    value = [tmpDict for tmpDict in params if tmpDict['key'] == key]
    value = value[0] if value else ""
    return value


def modify_return_value(key, err_msg):
    global RETURNVALUE
    tmp_dict = {key: err_msg}
    if not isinstance(RETURNVALUE[1], types.DictType):
        RETURNVALUE = (False, tmp_dict)
    else:
        RETURNVALUE[1].update(tmp_dict)


def update_return_value(check_item):
    global RETURNVALUE
    if not isinstance(RETURNVALUE[1], dict):
        RETURNVALUE = (False, check_item)
    else:
        RETURNVALUE[1].update(check_item)


def get_return_value():
    global RETURNVALUE
    return RETURNVALUE


def check_param_null(params):
    if params:
        if params["value"]:
            return True
    return False


def check_language(param_name):
    if param_name == "zh_CN" or param_name == "en_US":
        return True
    else:
        return False


def check_param_relay_network(param_name):
    pattern = r"^(?:(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:1[0-9][0-9]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}" \
              "(?:(?:2[0-5][0-5])|(?:25[0-5])|(?:1[0-9][0-9])|(?:[1-9][0-9])|(?:[0-9]))\/([1-2][0-9]|3[0-2]|[0-9])$"
    match_obj = re.match(pattern, param_name, re.I)
    if match_obj:
        return True
    else:
        return False


def check_rep_meta_info(item, output, project_id, is_fs_param=True, rep_zk_disk=None):
    # 复制集群元数据信息不为空校验
    if is_fs_param:
        if not item.get("replication_cluster_meta_info"):
            key = "replication_cluster_meta_info"
            logger.error("replication_cluster_meta_info can not be empty.")
            output[key] = get_code_msg(627107)
        if not item.get("replication_cluster_meta_type") and is_fs_param:
            output["replication_cluster_meta_type"] = get_code_msg(627445)
    else:
        if not item.get("replication_cluster_meta_info"):
            key = "replication_cluster_meta_info"
            logger.error("replication_cluster_meta_info can not be empty.")
            output[key] = get_code_msg(627098)
    if output.get('replication_cluster_meta_type') or output.get('replication_cluster_meta_info'):
        return

    # 复制独立部署时，不需要检查是否与主存有重复
    if get_project_condition_boolean(project_id, "DRStorage_TFB_Sep"):
        return
    # 新建场景时复制集群元数据信息正确性校验
    if get_project_condition_boolean(project_id, "!ExpansionAdCloudService&TenantStorFB80"):
        rep_cluster_meta = int(item.get("replication_cluster_meta_info"))
        # 主存槽位
        primary_slot_min = int(item.get("primary_slot").split("-")[0])
        primary_slot_max = int(item.get("primary_slot").split("-")[1])
        if primary_slot_min <= rep_cluster_meta <= primary_slot_max:
            err_msg = "The replication_cluster_meta_info in sheet of LLD  conflicts with primary_slot"
            logger.error(err_msg)
            if is_fs_param:
                output["replication_cluster_meta_info"] = get_code_msg('627110')
            else:
                output["replication_cluster_meta_info"] = get_code_msg('627097')
        # 复制元数据盘类型检查
        check_rep_zk_disk(item, output, rep_zk_disk)


def check_rep_net_plane(item, output):
    """复制平面 2.1,2.2"""
    if not item.get('replication_plane'):
        logger.error("replication_plane can not be empty.")
        output["replication_plane"] = get_code_msg(627087)
        return
    if not check_slot(item.get('replication_plane'), is_double=True):
        output["replication_plane"] = get_code_msg(627087)
        return


def check_slot(string, is_double=False):
    pattern1 = re.compile("^\d{1,2}\.\d,\d{1,2}\.\d$")
    pattern2 = re.compile("^\d{1,2}\.\d$")
    if is_double:
        if pattern1.findall(string):
            return True
    else:
        if pattern2.findall(string):
            return True
    return False


def check_network_plane(item, output):
    """检查管理面存储面 填的网口信息是否正确
        单网卡  应该是 管理面  5.1 存储 5.2
        双网卡  应该是 管理面 5.1,1.1， 存储面 5.2,1.2
    """
    plane_code_dict = {
        'management_plane': get_code_msg(627114),
        'storage_plane': get_code_msg(627088)
    }
    is_value = True
    for plane_name in plane_code_dict.keys():
        if not item.get(plane_name):
            logger.error("%s can not be empty." % plane_name)
            output[plane_name] = plane_code_dict.get(plane_name)
            is_value = False
            continue
        if not check_slot(item.get(plane_name)) and not check_slot(item.get(plane_name), is_double=True):
            output[plane_name] = plane_code_dict.get(plane_name)
            is_value = False
    if not is_value:
        return
    mgr_nic_list = item.get("management_plane").split(",")
    stg_nic_list = item.get("storage_plane").split(",")
    if len(mgr_nic_list) != len(stg_nic_list):
        output['management_plane'] = get_code_msg(627094)
        return


def check_system_account(item, output):
    key = "system_account_password"
    if not item["creuser"]:
        err_msg = "The System_account_password in sheet of LLD can not be empty"
        logger.error(err_msg)
        output[key] = get_code_msg('627103') % "system_account_password"
    else:
        # 判断是否为导出密码
        logger.info("Judge whether the password is export")
        ignore_regrex = r"^\*{8}$"
        match_regrex = ""
        if type(item["creuser"]) is list:
            match_regrex = re.findall(ignore_regrex, item["creuser"][0])
        else:
            match_regrex = re.findall(ignore_regrex, item["creuser"])
        if match_regrex:
            return
        system_account_password_list = item["creuser"].split(",")
        list_length = 4
        # 系统帐户密码不是两组帐户时出错
        if len(system_account_password_list) != list_length:
            logger.info("The number of system_account_password in sheet of LLD is:%s" % len(
                system_account_password_list))
            err_msg = "The number of system_account_password in sheet of LLD is wrong"
            logger.error(err_msg)
            output[key] = get_code_msg('627104') % "system_account_password"
        else:
            # 系统帐户密码的第一个必须是root帐户
            user = system_account_password_list[0]
            if user != 'root':
                logger.info("The first user of system_account_password in sheet of LLD is:%s" % user)
                err_msg = "The first user of system_account_password in sheet of LLD must be root"
                logger.error(err_msg)
                output[key] = get_code_msg('627105') % "system_account_password"

            # 普通用户白名单校验
            com_user = system_account_password_list[2]
            product_account_white_list = DeployConstant.ACCOUNT_WHITE_LIST
            if com_user in product_account_white_list:
                logger.error("The system common user[%s] cannot use the product built-in account[%s]."
                             % (com_user, product_account_white_list))
                output[key] = get_code_msg('627119') % ("system_account_password", product_account_white_list)


def check_cluster(control_cluster_list, service_cluster_list, output, is_fs_param=True):
    global REPLICATION_NODE
    # 校验控制集群节点个数
    if len(control_cluster_list) not in [3, 5, 7]:
        logger.error("Control cluster length is invalid.")
        if is_fs_param:
            output["controlClusterNum"] = get_code_msg(627106)
        else:
            output["controlClusterNum"] = get_code_msg(627095)
    # 校验业务集群节点个数
    if len(service_cluster_list) > 64:
        logger.error("Service cluster length is invalid.")
        if is_fs_param:
            output["ClusterNum"] = get_code_msg(627109)
        else:
            output["ClusterNum"] = get_code_msg(627096)
    REPLICATION_NODE = service_cluster_list

    return REPLICATION_NODE


def check_rep_gateway(params):
    """
    检查复制平面网关与ip地址范围是否冲突
    :param params:
    :return:
    """
    output = dict()
    error_dict1 = check_rep_gateway_and_range_collision(params)
    error_dict2 = check_rep_gateway_and_range_collision(params, 2)
    output.update(error_dict1)
    output.update(error_dict2)
    if output:
        return False, output
    return True, output


def check_rep_gateway_and_range_collision(params, rep_index=1):
    """
    检查复制平面网关与ip地址范围是否冲突
    :param params:
    :param rep_index:1 or 2.表示两个不同的复制平面
    :return:
    """
    out_put = dict()
    rep_range_key = "fusionstorage_rep_range%s" % rep_index
    rep_gateway_key = "fusionstorage_rep_gateway%s" % rep_index
    rep_range = get_params_value_by_key(params, rep_range_key)
    rep_gateway = get_params_value_by_key(params, rep_gateway_key).get("value")
    section = get_rep_ip_section(rep_range)
    if not section or not rep_gateway:
        return out_put
    if is_ip_in_section(rep_gateway, section):
        out_put[rep_gateway_key] = GW_ERROR_CONTAIN % (rep_gateway, section)
    return out_put


def get_rep_ip_section(rep_range):
    """
    获取复制网络平面ip返回
    :param rep_range:
    :return:
    """
    ret = None
    addr_start, addr_end = get_rep_address(rep_range)
    if addr_start and addr_end:
        ret = addr_start + '-' + addr_end
        return ret
    return ret


def check_rep_address(params):
    """
    检查复制平面可用ip数量
    :param params:
    :return:
    """
    output = dict()
    rep_range1 = get_params_value_by_key(params, "fusionstorage_rep_range1")
    address1_start, address1_end = get_rep_address(rep_range1)
    rep_range2 = get_params_value_by_key(params, "fusionstorage_rep_range2")
    address2_start, address2_end = get_rep_address(rep_range2)
    check_rep_address_validity(address1_start, address1_end, output, 1)
    check_rep_address_validity(address2_start, address2_end, output, 2)
    if output:
        return False, output
    return True, output


def get_rep_address(rep_range):
    if rep_range:
        address_start = rep_range.get('value').split('-')[0]
        address_end = rep_range.get('value').split('-')[1]
    else:
        address_start = None
        address_end = None
    return address_start, address_end


def check_rep_address_validity(address_start, address_end, output, index):
    if address_start and address_end and param_check.check_param_ip(address_start) \
            and param_check.check_param_ip(address_end):
        value_start = address_start.split(".")[-1]
        value_end = address_end.split(".")[-1]
        value = int(value_end) - int(value_start) + 1
        if len(REPLICATION_NODE) > value:
            logger.error("the ip number of fusionstorage_rep_addr[%s]  is invalid.detail: %s-%s"
                         % (index, value_start, value_end))
            key = "fusionstorage_rep_range1" if index == 1 else "fusionstorage_rep_range2"
            output[key] = get_code_msg(627115) % (value, len(REPLICATION_NODE))


def check_rep_zk_disk(item, output, rep_zk_disk):
    if output.get("replication_cluster_meta_type"):
        return
    rep_meta_type = item.get("replication_cluster_meta_type")
    if rep_meta_type not in DeployConstant.REP_METADATA_SUPPORT_DISK_TYPE:
        logger.error("replication zk disk type:{}".format(rep_meta_type))
        output["replication_cluster_meta_type"] = get_code_msg(627446) % DeployConstant.REP_METADATA_SUPPORT_DISK_TYPE
        return
    if item.get("zk_slot") == item.get("replication_cluster_meta_info"):
        rep_zk_disk['basic_zk'] += 1
        if item.get("zk_type") != rep_meta_type:
            logger.error('The type of the replication metadata disk {} is different from the basic '
                         'metadata disk[{}]'.format(item.get("replication_cluster_meta_type"), item.get("zk_type")))
            output["replication_cluster_meta_type"] = get_code_msg(627448) % (rep_meta_type, item.get("zk_type"))
    rep_zk_disk['rep_zk_type'].add(rep_meta_type)
    logger.info("rep zk disk: {}".format(rep_zk_disk))
    if len(rep_zk_disk['rep_zk_type']) > 1:
        output["replication_cluster_meta_type"] = get_code_msg(627446) % DeployConstant.REP_METADATA_SUPPORT_DISK_TYPE
