#!/usr/bin/python2.7
# coding:utf-8

import ipaddress
import re
import IPy
import traceback
import os
import sys
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 = {}

    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[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):
        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)

    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 = int(vlan_range[i])
                vlan_end = 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:
        """
        if not (key.endswith('start_addr') or key.endswith("start_addr_ipv6")) or key in self.output:
            return 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)
        try:
            if key_start in self.output or key_end in self.output:
                return True, None, None, None, None
            if IPy.IP(value_start).version() == 6 or IPy.IP(value_end).version() == 4:
                pass
            else:
                return True, None, None, None, None
        except Exception as e:
            logger.error('Error: {}'.format(e))
            return True, None, None, None, None
        return False, key_start, key_end, value_start, value_end

    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)

    @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 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:
                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]
            except Exception as e:
                logger.error('iterm: {}, Error:{}'.format(key_item, e))
                continue

        for net in IP_Reserved_Network.keys():
            value2 = IP_Reserved_Network[net]
            try:
                ipv6_ip_range = IPy.IP(value2).strNormal(3)
                key_start = net + '_start_addr'
                key_end = net + '_end_addr'
                params_temp[key_start] = ipv6_ip_range.split('-')[0]
                params_temp[key_end] = ipv6_ip_range.split('-')[1]
            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, key_end, key_start, start_addr, subnet, tunnelbearing=False):
        """
        检查当前ip结束地址参数是否正确
        """
        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[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))
