#!/usr/bin/python2.7
# coding:utf-8
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import collections
import ipaddress
import re
import traceback
import os
import sys
import IPy
from IPy import IP
import utils.common.log as log
import utils.common.log as logger
from utils.common.error.hcci_error_code import get_code_msg
from utils.Driver.CloudDC.OpenStack.Network.networkerror_constant import params_check_errornum

sys.path.append(os.path.abspath(__file__ + '/../../'))


# 需要使用到的正则表达式
IP_RE = ['^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$']
IPV6_RE = [
    '(([\da-fA-F]{1,4}:){6}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))|'
    '(::([\da-fA-F]{1,4}:){0,5}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))|'
    '(([\da-fA-F]{1,4}:):([\da-fA-F]{1,4}:){0,4}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))|'
    '(([\da-fA-F]{1,4}:){2}:([\da-fA-F]{1,4}:){0,3}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))|'
    '(([\da-fA-F]{1,4}:){3}:([\da-fA-F]{1,4}:){0,2}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))|'
    '(([\da-fA-F]{1,4}:){4}:([\da-fA-F]{1,4}:)?((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))|'
    '(([\da-fA-F]{1,4}:){5}:((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))|'
    '(([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4})|'
    '(:((:[\da-fA-F]{1,4}){1,7}|:))|'
    '([\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,6}|:))|'
    '(([\da-fA-F]{1,4}:){2}((:[\da-fA-F]{1,4}){1,5}|:))|'
    '(([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,4}|:))|'
    '(([\da-fA-F]{1,4}:){4}((:[\da-fA-F]{1,4}){1,3}|:))|'
    '(([\da-fA-F]{1,4}:){5}((:[\da-fA-F]{1,4}){1,2}|:))|'
    '(([\da-fA-F]{1,4}:){6}((:[\da-fA-F]{1,4})?|:))|'
    '(([\da-fA-F]{1,4}:){7}:)'
]
IPV6_MASK_RE = ['[a-fA-F0-9]{1,4}(:[a-fA-F0-9]{1,4}){7}|[a-fA-F0-9]{0,4}(:[a-fA-F0-9]{1,4}){0,7}::[a-fA-F0-9]{0,4}'
                '(:[a-fA-F0-9]{1,4}){0,7}/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]$)']
MASK_RE = ['^(254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.'
           '(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)$']
IP_MASK_RE = ['^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|'
              '25[0-5])/([0-9]|[1-2][0-9]|3[0-2])$']
IP_RANGE_RE = ['^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|'
               '25[0-5])~(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|'
               '2[0-4][0-9]|25[0-5])$']
QOS_RE = r"[1-9]-[1-9]-[1-9]"
BOOLEN = ['true|false']
COMMON_NAME = ['^[\w]+$']
PWD_CHECK = ["[。~!@#$%\^\+\*&\\\/\?\|:\.<>{}()';=\"\w]{6,16}"]

QOS_BANDWIDTH_RE_VALUE_ERROR = get_code_msg(params_check_errornum.NetParamError061)
IP_ERRO_MSG = get_code_msg(params_check_errornum.NetParamError001)
MASK_ERRO_MSG = get_code_msg(params_check_errornum.NetParamError002)
VLAN_ERRO_MSG = get_code_msg(params_check_errornum.NetParamError003)
MAC_POOL_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError004)
VLAN_ERROR_CONFLICT = get_code_msg(params_check_errornum.NetParamError005)
MAC_ERROR_NOCONTAIN = get_code_msg(params_check_errornum.NetParamError006)
EXTERNALNET_ERROR_NOCONTAIN = get_code_msg(params_check_errornum.NetParamError007)
GW_ERROR_CONTAIN = get_code_msg(params_check_errornum.NetParamError009)
MAC_ERROR_CONFLICT = get_code_msg(params_check_errornum.NetParamError014)
GW_ERROR_NOCONTAIN = get_code_msg(params_check_errornum.NetParamError015)
NET_CONFLICT = get_code_msg(params_check_errornum.NetParamError016)
MAC_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError018)
IP_MASK_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError019)
INTEGER_PARSE_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError021)
DIGIT_RANGE_VALUE_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError022)
DIGIT_START_ZERO_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError023)
VALUE_CONTAIN_CN_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError024)
VLAN_RANGE_FORMAT_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError025)
VLAN_IP_POOL_NUM__ERROR_MSG = get_code_msg(params_check_errornum.NetParamError026)
END_ADDR_MSG = get_code_msg(params_check_errornum.NetParamError028)
IP_POOL_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError029)
START_ADDR_MSG = get_code_msg(params_check_errornum.NetParamError030)
SUBNET_MASK_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError031)
IP_START_END_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError032)
HOST_IP_MAPPING_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError033)
HM_IP_MAPPINGS_NUM_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError034)
HM_IP_FORMAT_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError035)
HM_IP_FIP_CIDR_IP_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError036)
HM_IP_FIP_CIDR_MASK_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError037)
HM_IP_MASK_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError038)
HM_IP_LBAAS_GATEWAY_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError039)
VLAN_RANGE3_ERRO_MSG = get_code_msg(params_check_errornum.NetParamError040)
IP_RANGE_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError043)
VLAN_FORMAT_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError044)
MUL_GW_ERROR_CONTAIN = get_code_msg(params_check_errornum.NetParamError046)
PARA_RE_VALUE_ERROR = get_code_msg(params_check_errornum.NetParamError049)
VLAN_RANGE4_ERRO_MSG = get_code_msg(params_check_errornum.NetParamError057)
VLAN_VALUE1_ERRO_MSG = get_code_msg(params_check_errornum.NetParamError058)
CPU_NUM_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError059)
EXTERNALNET_ERROR2_NOCONTAIN = get_code_msg(params_check_errornum.NetParamError060)
BOOLEN_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError064)
COMMON_NAME_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError065)
PWD_CHECK_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError066)
DCGW_PARAM_EXIST_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError067)
PARAMS_REQUIRED = get_code_msg(params_check_errornum.NetParamError068)
IPV6_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError077)
IPV6_MASK_ERRO_MSG = get_code_msg(params_check_errornum.NetParamError078)
IPV6_IP_MASK_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError107)
START_ADDR_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError119)
END_ADDR_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError120)
GW_NET_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError121)
GW_BROADCAST_ERROR_MSG = get_code_msg(params_check_errornum.NetParamError122)
RESERVED_NETWORK_MSG = get_code_msg(params_check_errornum.NetParamError123)

# neutron服务涉及的参数, 非必须参数必须加{'required': false}, 默认为必须
OPENSTACK_PARAMS_DICT = {
    'openstack_externalapi_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_externalapi_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_externalapi_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'openstack_externalapi_gateway': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_externalapi_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'openstack_externalom_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_externalom_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_externalom_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'openstack_externalom_gateway': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_externalom_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'openstack_storage0_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_storage0_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_storage0_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'openstack_storage0_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'openstack_storage1_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_storage1_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_storage1_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'openstack_storage1_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'openstack_storage2_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG, 'required': False},
    'openstack_storage2_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG, 'required': False},
    'openstack_storage2_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG, 'required': False},
    'openstack_storage2_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG, 'required': False},
    'openstack_storage3_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG, 'required': False},
    'openstack_storage3_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG, 'required': False},
    'openstack_storage3_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG, 'required': False},
    'openstack_storage3_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG, 'required': False},
    'openstack_tunnelbearing_mask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'openstack_tunnelbearing_startip': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_tunnelbearing_endip': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_tunnelbearing_gateway': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'openstack_tunnelbearing_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'openstack_internalbase_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    # 对应级联层external_relay_network
    'external_relay_network': {'type': 're', 'range': IP_MASK_RE, 'message': IP_MASK_ERROR_MSG},
    # 对应级联层external_relay_network vlan
    'external_relay_network_tag': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},

    'public_service_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'public_service_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'public_service_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'public_service_gateway': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'public_service_vlanid': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'dmz_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'dmz_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'dmz_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'dmz_gateway': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'dmz_vlanid': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'dmz_tenant_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'dmz_tenant_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'dmz_tenant_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'dmz_tenant_gateway': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'dmz_tenant_vlanid': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG},
    'dmz_tenant_start_addr_ipv6': {'type': 're', 'range': IPV6_RE, 'message': IPV6_ERROR_MSG},
    'dmz_tenant_end_addr_ipv6': {'type': 're', 'range': IPV6_RE, 'message': IPV6_ERROR_MSG},
    'dmz_tenant_netmask_ipv6': {'type': 'int', 'range': [1, 128], 'message': IPV6_MASK_ERRO_MSG},
    'dmz_tenant_gateway_ipv6': {'type': 're', 'range': IPV6_RE, 'message': IPV6_ERROR_MSG},
    'tenant_network_ipv6': {'type': 're', 'range': IPV6_MASK_RE, 'message': IPV6_IP_MASK_ERROR_MSG},
    'heartbeat_start_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'heartbeat_end_addr': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'heartbeat_netmask': {'type': 're', 'range': MASK_RE, 'message': MASK_ERRO_MSG},
    'heartbeat_gateway': {'type': 're', 'range': IP_RE, 'message': IP_ERRO_MSG},
    'heartbeat_vlanid': {'type': 'int', 'range': [1, 4094], 'message': VLAN_ERRO_MSG}
}

IP_PARAM_TRANSFORMED = [
    "internal_base_network"
]

IP_Reserved_Network = {
    "ip_reserved_net1": "FF00::/8",
    "ip_reserved_net2": "FE80::/10",
    "ip_reserved_net3": "::FFFF:0:0/96",
    "ip_reserved_net4": "::/96"
}


class OpenStackParamChecker(object):

    def __init__(self):
        self.output = {}
        self.params = {}
        self.vlans = {}

    @classmethod
    def get_ip_range(cls, key_item, params_temp, value):
        ipv6_ip_range = IPy.IP(value).strNormal(3)
        key_start = key_item + '_start_addr'
        key_end = key_item + '_end_addr'
        params_temp[key_start] = ipv6_ip_range.split('-')[0]
        params_temp[key_end] = ipv6_ip_range.split('-')[1]

    @staticmethod
    def get_key_start_end_for_check_ip_range_conflict(key, key_start, key_end):
        if key[0:key.find("_start_addr")] in IP_PARAM_TRANSFORMED:
            key_start = key[0:key.find("_start_addr")]
            key_end = key[0:key.find("_start_addr")]
        return key_start, key_end

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

        :param input_params:
        :return:
        """

        # 将input_params转换成dict
        self.params = dict([(x['key'], x['value']) for x in input_params])
        # 根据OPENSTACK_PARAMS_DICT做基本校验。因为传入参数不确定，可能是级联层、
        # 被级联层、网络节点、公共服务的自由组合，所以遍历输入参数，而不是遍历
        # OPENSTACK_PARAMS_DICT
        for param_item in input_params:
            key = param_item['key']
            value = param_item['value']
            if key not in OPENSTACK_PARAMS_DICT:
                continue
            # 校验参数是否为空
            dic_condition = OPENSTACK_PARAMS_DICT.get(key)
            if not value:
                if dic_condition.get('required', True):
                    self.output[key] = PARAMS_REQUIRED % key
                continue
            if not self.check_switch_case_handle(key, value, dic_condition):
                continue

        # 针对性的做高级校验
        self.check_storage_data2()
        self.check_subnet_ippool_gateway()
        self.check_tunnelbearing_subnet_ippool_gateway()
        self.check_vlan_tags()
        self.check_qos_bandwidth()
        self.analyse_ipv6_mask()
        self.check_ip_range_conflict()

        if self.output:
            log.error("output: %s" % self.output)
            return False, self.output
        log.info("output: %s" % self.output)
        return True, self.output

    def check_switch_case_handle(self, key, value, dic_condition):
        no_func_name = None
        switch_case = {
            "int": self.check_int_type_params,
            "re": self.check_re_type_params,
            "vlan_range": self.check_vlan_range_type_params
        }
        check_type = dic_condition['type']
        check_func_name = switch_case.get(check_type)
        if check_func_name:
            return check_func_name(key, value, dic_condition)
        return no_func_name

    def check_int_type_params(self, key, value, dic_condition):
        """
        int类型参数检查
        :param key: 参数key
        :param value: 参数值
        :param dic_condition:参数字典
        :return: bool :True检查成功，False检查失败跳过该参数检查继续下一项参数检查
        """
        lst_range = dic_condition['range']
        try:
            int_value = int(value)
        except Exception:
            log.error("parse value failed. key=%s, value=%s, "
                      "raise exception:%s."
                      % (key, value, traceback.format_exc()))
            err_msg = INTEGER_PARSE_ERROR_MSG % (key, value)
            self.output[key] = err_msg
            return False
        int_min = lst_range[0]
        int_max = lst_range[1]
        if int_value < int_min or int_value > int_max:
            log.error("The value filed. "
                      "key=%s, value=%s, range=%s."
                      % (key, value, lst_range))
            err_msg = DIGIT_RANGE_VALUE_ERROR_MSG % (key, value, lst_range)
            self.output[key] = err_msg

        if int_value > 0 and value.startswith('0'):
            err_msg = DIGIT_START_ZERO_ERROR_MSG % (key, value)
            self.output[key] = err_msg
        return True

    def check_re_type_params(self, key, value, dic_condition):
        """
        检测字正则表达式的类型
        :param key: 参数key
        :param value: 参数值
        :param dic_condition:参数字典
        :return: bool :True检查成功，False检查失败跳过该参数检查继续下一项参数检查
        """
        if ('message' in
            dic_condition) and \
                dic_condition['message']:
            err_msg = dic_condition['message']
        else:
            err_msg = PARA_RE_VALUE_ERROR \
                      % (key, value)
        for pattern in dic_condition['range']:
            if re.match(pattern, str(value)) is not None:
                break
        else:
            log.error(
                "parse value failed. key=%s, value=%s." % (key, value))
            self.output[key] = err_msg
        return True

    def check_vlan_range_type_params(self, key, value, dic_condition):
        """
        检测vlan范围，格式为2000:2255
        :param key: 参数key
        :param value: 参数值
        :param dic_condition:参数字典
        :return: bool :True检查成功，False检查失败跳过该参数检查继续下一项参数检查
        """
        lst_range = dic_condition['range']
        for char in value:
            if ord(char) not in range(128):
                # check is not ascii return false
                self.output[key] = VALUE_CONTAIN_CN_ERROR_MSG
                return False

        regex = r'\d{1,4}'
        vlan_range = re.findall(regex, self.params[key])
        if len(vlan_range) % 2 == 1:
            self.output[key] = VLAN_RANGE4_ERRO_MSG
            return False

        for i in range(0, len(vlan_range), 2):
            try:
                vlan_start, vlan_end = int(vlan_range[i]), int(vlan_range[i + 1])
            except Exception:
                logger.error("parse value failed. key=%s, value=%s, "
                             "raise exception:%s."
                             % (key, value, traceback.format_exc()))
                err_msg = VLAN_RANGE_FORMAT_ERROR_MSG % (key, value)
                self.output[key] = err_msg
                continue
            int_min = lst_range[0]
            int_max = lst_range[1]
            if vlan_start < int_min or vlan_start > int_max \
                    or vlan_end < int_min or vlan_end > int_max:
                err_msg = DIGIT_RANGE_VALUE_ERROR_MSG % (key, value, lst_range)
                logger.error(
                    "parse value failed. key=%s, value=%s." % (key, value))
                self.output[key] = err_msg
            if vlan_start > vlan_end:
                logger.error("The value filed. "
                             "value=%s, range=%s."
                             % (value, lst_range))
                err_msg = VLAN_FORMAT_ERROR_MSG % (key, value)
                self.output[key] = err_msg

            if int(vlan_range[i]) > 0 and vlan_range[i].startswith('0'):
                err_msg = DIGIT_START_ZERO_ERROR_MSG % value
                self.output[key] = err_msg
        return True

    def check_storage_data2(self):
        key = 'openstack_storage2_name'
        storage2_name = self.params.get(key)
        if not storage2_name:
            # cinder param check would check this case.
            return

        storage2_keys = [
            'openstack_storage2_start_addr',
            'openstack_storage2_end_addr',
            'openstack_storage2_netmask',
            'openstack_storage2_gateway',
            'openstack_storage2_tag'
        ]
        if storage2_name == 'storage_data2':
            for storage2_key in storage2_keys:
                if storage2_key in self.params and not self.params.get(storage2_key):
                    self.output[storage2_key] = PARAMS_REQUIRED % storage2_key

    def get_ip_range_value_handle(self, key):
        """
        根据key值获取ip范围起始值和结束值
        :param key: ip地址范围key值
        :return:
        """
        ret = collections.namedtuple("ret", ["flag", "key_start1", "key_end1", "value_start1", "value_end1"])
        if not (key.endswith('start_addr') or key.endswith("start_addr_ipv6")) or key in self.output:
            return ret(True, None, None, None, None)
        key_start = key.replace('start_addr', 'start_addr')
        key_end = key.replace('start_addr', 'end_addr')
        value_start = self.params.get(key_start)
        value_end = self.params.get(key_end)
        condition_1 = key_start in self.output or key_end in self.output
        try:
            condition_2 = not(IPy.IP(value_start).version() == 6 or IPy.IP(value_end).version() == 4)
        except Exception as e:
            logger.error('Error: {}'.format(e))
            return ret(True, None, None, None, None)
        ret_true = ret(True, None, None, None, None)
        ret_false = ret(False, key_start, key_end, value_start, value_end)
        return ret_true if condition_1 or condition_2 else ret_false

    def check_ip_range_conflict(self):
        """检查lld中的每一个IP池是否与其他的IP池存在冲突"""
        # 思路： 把IP池冲突问题转换成区间重叠问题。
        for key1 in self.params:
            continue_flag, key_start1, key_end1, value_start1, value_end1 = \
                self.get_ip_range_value_handle(key1)
            if continue_flag:
                continue
            for key2 in self.params:
                continue_flag, key_start2, key_end2, value_start2, value_end2 = \
                    self.get_ip_range_value_handle(key2)
                if key_start1 == key_start2 or continue_flag:
                    continue
                start = max(IPy.IP(value_start1).int(), IPy.IP(value_start2).int())
                end = min(IPy.IP(value_end1).int(), IPy.IP(value_end2).int())
                if end - start < 0:
                    continue
                key_start2, key_end2 = self.get_key_start_end_for_check_ip_range_conflict(
                    key2, key_start2, key_end2)
                key_start1, key_end1 = self.get_key_start_end_for_check_ip_range_conflict(
                    key1, key_start1, key_end1)
                range1 = "['%s':'%s', '%s':'%s']" \
                         % (key_start1, value_start1, key_end1, value_end1)
                range2 = "['%s':'%s', '%s':'%s']" \
                         % (key_start2, value_start2, key_end2, value_end2)
                args_list = key1, key2, key_start1, key_start2, range1, range2
                self.err_msg_for_check_ip_range_conflict(*args_list)

    def err_msg_for_check_ip_range_conflict(self, *args):
        key1, key2, key_start1, key_start2, range1, range2 = args
        err_msg = NET_CONFLICT % (range1, range2)
        err_msg2 = RESERVED_NETWORK_MSG % range1
        err_msg3 = RESERVED_NETWORK_MSG % range2

        if key1[0:key1.find("_start_addr")] in IP_Reserved_Network:
            self.output[key_start2] = err_msg3
            return
        if key2[0:key2.find("_start_addr")] in IP_Reserved_Network:
            self.output[key_start1] = err_msg2
            return
        if key2[0:key2.find("_start_addr")] in IP_PARAM_TRANSFORMED:
            self.output[key2[0:key2.find("_start_addr")]] = err_msg
        else:
            self.output[key2] = err_msg

    def check_qos_bandwidth(self):
        qos_bandwidth = 'network_plane_bandwidth'
        if not self.params.get(qos_bandwidth):
            return
        qos = self.params.get(qos_bandwidth)
        if not re.match(QOS_RE, str(qos)):
            self.output[qos_bandwidth] = QOS_BANDWIDTH_RE_VALUE_ERROR % (qos)
            return
        total = 0
        for bandwidth in self.params.get(qos_bandwidth).split('-'):
            total += int(bandwidth)
        if total != 10:
            self.output[qos_bandwidth] = QOS_BANDWIDTH_RE_VALUE_ERROR % (total)

    def analyse_ipv6_mask(self):
        params_temp = {}
        for key_item in self.params:
            nework_ip_list = ['tenant_network_ipv6', 'openstack_internal_base_network']
            if key_item not in nework_ip_list or key_item in self.output:
                continue
            value = self.params.get(key_item)
            try:
                self.get_ip_range(key_item, params_temp, value)
            except Exception as e:
                logger.error('iterm: {}, Error:{}'.format(key_item, e))
                continue

        for net in IP_Reserved_Network.keys():
            value2 = IP_Reserved_Network.get(net)
            try:
                self.get_ip_range(net, params_temp, value2)
            except Exception as e:
                logger.error('net: {}, Error:{}'.format(net, e))
                continue
        self.params = dict(list(self.params.items()) + list(params_temp.items()))

    def check_subnet_ippool_gateway(self):
        """检查网关、IP池(起始和终止)是否在子网网段内"""
        for key in OPENSTACK_PARAMS_DICT:
            if not self.params.get(key) or key in self.output:
                continue
            if key.endswith('start_addr'):
                start_addr = self.params[key]
                # 保留key_start，以应对方案变化
                key_start = key.replace('start_addr', 'start_addr')
                key_end = key.replace('start_addr', 'end_addr')
                key_gw = key.replace('start_addr', 'gateway')
                key_mask = key.replace('start_addr', 'netmask')
                key_subnet = key.replace('start_addr', 'subnet')
                # 生成的subnet
                if not self.params.get(key_mask) or key_mask in self.output:
                    continue
                subnet = get_subnet(start_addr, self.params.get(key_mask))
                # 将subnet写入参数self.params，供其他方法使用
                self.params[key_subnet] = subnet

                # start_addr和netmask生成subnet，无需校验，保留校验以应对方案变化
                self.check_subnet_ippool_gateway_for_ip_start(key_start, subnet)
                self.check_subnet_ippool_gateway_for_ip_end(key_end, key_start, start_addr, subnet)
                self.check_subnet_ippool_gateway_for_gw(key_gw, subnet)
                self.check_subnet_ippool_gateway_for_mask(key_mask, subnet)

    def check_subnet_ippool_gateway_for_ip_start(self, key_start, subnet, tunnelbearing=False):
        """
        检查当前key参数ip地址起始参数是否正确
        """
        if self.params.get(key_start) and key_start not in self.output:
            start_addr = self.params[key_start]
            if not (is_ip_in_net(start_addr, subnet)):
                self.output[key_start] = START_ADDR_MSG % subnet
            elif not tunnelbearing and is_ip_net_ip(start_addr, subnet):
                self.output[key_start] = START_ADDR_ERROR_MSG % start_addr

    def check_subnet_ippool_gateway_for_ip_end(self, *args, tunnelbearing=False):
        """
        检查当前ip结束地址参数是否正确
        """
        key_end, key_start, start_addr, subnet = args
        if self.params.get(key_end) and key_end not in self.output:
            end_addr = self.params[key_end]
            if not (is_ip_in_net(end_addr, subnet)):
                self.output[key_end] = END_ADDR_MSG % subnet
            elif not tunnelbearing and is_ip_broadcast_ip(end_addr, subnet):
                self.output[key_end] = END_ADDR_ERROR_MSG % end_addr
            else:
                if ipaddress.ip_address(start_addr) > ipaddress.ip_address(end_addr):
                    self.output[key_end] = IP_START_END_ERROR_MSG % (key_start, key_end)

    def check_subnet_ippool_gateway_for_gw(self, key_gw, subnet, **kwargs):
        """
        检查网关参数是否正确
        """
        tunnelbearing = kwargs.get("tunnelbearing")
        key_end = kwargs.get("key_end")
        key_start = kwargs.get("key_start")
        start_addr = kwargs.get("start_addr")
        if self.params.get(key_gw) and key_gw not in self.output:
            gateway = self.params[key_gw]
            if not (is_ip_in_net(gateway, subnet)):
                self.output[key_gw] = GW_ERROR_NOCONTAIN % subnet
            elif not tunnelbearing and is_ip_net_ip(gateway, subnet):
                self.output[key_gw] = GW_NET_ERROR_MSG % gateway
            elif not tunnelbearing and is_ip_broadcast_ip(gateway, subnet):
                self.output[key_gw] = GW_BROADCAST_ERROR_MSG % gateway
            if tunnelbearing and self.params.get(key_end) and \
                    not {key_gw, key_end, key_start} & set(self.output.keys()):
                end_addr = self.params[key_end]
                section = start_addr + '-' + end_addr
                if is_ip_in_section(gateway, section):
                    self.output[key_gw] = GW_ERROR_CONTAIN % (gateway, section)

    def check_subnet_ippool_gateway_for_mask(self, key_mask, subnet):
        """
        检查子网与网络掩码是否匹配
        """
        if self.params.get(key_mask) and key_mask not in self.output:
            netmask = self.params[key_mask]
            if not does_net_match_mask(subnet, netmask):
                self.output[key_mask] = \
                    SUBNET_MASK_ERROR_MSG % (subnet, netmask)

    def check_tunnelbearing_subnet_ippool_gateway(self):
        """检查网关、IP池(起始和终止)是否在子网网段内"""
        nets = ['openstack_tunnelbearing_startip']
        for key in nets:
            if not self.params.get(key) or key in self.output:
                continue
            start_addr = self.params[key]
            # 保留key_start，以应对方案变化
            key_start = key.replace('startip', 'startip')
            key_end = key.replace('startip', 'endip')
            key_gw = key.replace('startip', 'gateway')
            key_mask = key.replace('startip', 'mask')
            key_subnet = key.replace('startip', 'subnet')
            # 生成的subnet
            if not self.params.get(key_mask) or key_mask in self.output:
                continue
            subnet = get_subnet(start_addr, self.params.get(key_mask))
            # 将subnet写入参数self.params，供其他方法使用
            self.params[key_subnet] = subnet

            # start_addr和netmask生成subnet，无需校验，保留校验以应对方案变化
            self.check_subnet_ippool_gateway_for_ip_start(key_start, subnet, tunnelbearing=True)
            self.check_subnet_ippool_gateway_for_ip_end(key_end, key_start, start_addr, subnet, tunnelbearing=True)
            extra_params = {
                "tunnelbearing": True,
                "key_end": key_end,
                "key_start": key_start,
                "start_addr": start_addr
            }
            self.check_subnet_ippool_gateway_for_gw(key_gw, subnet, **extra_params)
            self.check_subnet_ippool_gateway_for_mask(key_mask, subnet)

    def check_vlan_tags(self):
        """检查网络 vlan tag"""
        for key in OPENSTACK_PARAMS_DICT:
            if not self.params.get(key) or key in self.output:
                continue
            if key.endswith('_tag') or key.endswith('_vlanid'):
                vlanid = self.params[key]
                if vlanid in self.vlans:
                    self.output[key] = VLAN_ERROR_CONFLICT % (self.vlans.get(vlanid))
                else:
                    self.vlans[vlanid] = key


def is_ip_in_net(ip, net):
    """
    判断IP是否在子网内, 不支持IPV6.
    :param ip:
    :param net:
    :return:
    """
    # socket.inet_pton is used to convert an IP address into its representation
    #  as a binary string (a 32-bit/4-byte string for IPv4 and a
    #  128-bit/16-byte string for IPv6). As an example, "192.168.1.0" is
    #  converted to "\xc0\xa8\x01\x00", whose bytes represent the numbers
    #  192 (0xc0), 168 (0xa8), 1 (0x01) and 0 (0x00) in hexadecimal notation
    #  respectively.

    return ip in IP(net)


def is_ip_net_ip(ip, net):
    """
    判断地址是不是网络地址
    :param ip:
    :param net:
    :return:
    """
    network = IPy.IP(net)
    net_ip = str(network.net())
    return ip == net_ip


def is_ip_broadcast_ip(ip, net):
    """
    判读地址是不是广播地址
    :param ip:
    :param net:
    :return:
    """
    network = IPy.IP(net)
    broadcast_ip = str(network.broadcast())
    return ip == broadcast_ip


def does_net_match_mask(net, mask):
    """
    检查子网网段和掩码是否匹配, 不支持IPV6。
    :param net:
    :param mask: 地址型mask，如255.255.255.0
    :return:
    """
    try:
        mask_int = int(mask)
    except Exception:
        return str(IP(net).netmask()) == mask

    masknum = net.split('/')[1]
    try:
        masknum = int(masknum)
    except Exception:
        return False
    return masknum == mask_int


def is_net_in_net(net1, net2):
    """
    检查一个网段是否包含于另一个网段，不支持IPV6。
    :param net1:
    :param net2:
    :return:
    """
    return IP(net1).overlaps(net2) or IP(net2).overlaps(net1)


def is_ip_in_section(ip, section):
    """
    判断一个ip是否在一个网络区间
    ip=192.168.3.28
    network=192.168.3.1-192.168.10.6
    :param ip:
    :param section:
    :return:
    """
    return IP(ip) > IP(section.split("-")[0]) and IP(ip) < IP(section.split("-")[1])


def is_mac_in_mac(a, b):
    """
    :param a 'fa:16:3e:00:00:00':
    :param b 'fa:16:3e:01:00:00':
    :return:
    """

    if a[:9] != b[:9]:
        return False

    la = a[9:].split(':')
    lb = b[9:].split(':')
    for x in range(3):
        if la[x] != lb[x] and la[x] != '00':
            return False
    return True


def get_subnet(ip, mask):
    return str(IP(ip).make_net(mask))
