Merge "NSX-V3: Enhance subnet overlap checks"
This commit is contained in:
@@ -1674,38 +1674,51 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
"network %s", network_id)
|
"network %s", network_id)
|
||||||
|
|
||||||
def _validate_address_space(self, context, subnet):
|
def _validate_address_space(self, context, subnet):
|
||||||
cidr = subnet.get('cidr')
|
# Only working for IPv4 at the moment
|
||||||
if (not validators.is_attr_set(cidr) or
|
if (subnet['ip_version'] != 4):
|
||||||
netaddr.IPNetwork(cidr).version != 4):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# get the subnet IPs
|
||||||
|
if ('allocation_pools' in subnet and
|
||||||
|
validators.is_attr_set(subnet['allocation_pools'])):
|
||||||
|
# use the pools instead of the cidr
|
||||||
|
subnet_networks = [
|
||||||
|
netaddr.IPRange(pool.get('start'), pool.get('end'))
|
||||||
|
for pool in subnet.get('allocation_pools')]
|
||||||
|
else:
|
||||||
|
cidr = subnet.get('cidr')
|
||||||
|
if not validators.is_attr_set(cidr):
|
||||||
|
return
|
||||||
|
subnet_networks = [netaddr.IPNetwork(subnet['cidr'])]
|
||||||
|
|
||||||
# Check if subnet overlaps with shared address space.
|
# Check if subnet overlaps with shared address space.
|
||||||
# This is checked on the backend when attaching subnet to a router.
|
# This is checked on the backend when attaching subnet to a router.
|
||||||
if netaddr.IPSet([cidr]) & netaddr.IPSet(['100.64.0.0/10']):
|
shared_ips = '100.64.0.0/10'
|
||||||
msg = _("Subnet overlaps with shared address space 100.64.0.0/10")
|
for subnet_net in subnet_networks:
|
||||||
LOG.error(msg)
|
if netaddr.IPSet(subnet_net) & netaddr.IPSet([shared_ips]):
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
msg = _("Subnet overlaps with shared address space "
|
||||||
|
"%s") % shared_ips
|
||||||
|
LOG.error(msg)
|
||||||
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
# Ensure that the NSX uplink does not lie on the same subnet as
|
# Ensure that the NSX uplink does not lie on the same subnet as
|
||||||
# the external subnet
|
# the external subnet
|
||||||
filters = {'id': [subnet['network_id']],
|
filters = {'id': [subnet['network_id']],
|
||||||
'router:external': [True]}
|
'router:external': [True]}
|
||||||
nets = self.get_networks(context, filters=filters)
|
external_nets = self.get_networks(context, filters=filters)
|
||||||
for net in nets:
|
tier0_routers = [ext_net[pnet.PHYSICAL_NETWORK]
|
||||||
tier0 = net.get(pnet.PHYSICAL_NETWORK)
|
for ext_net in external_nets
|
||||||
if tier0:
|
if ext_net.get(pnet.PHYSICAL_NETWORK)]
|
||||||
ports = self.nsxlib.logical_router_port.get_by_router_id(tier0)
|
for tier0_rtr in set(tier0_routers):
|
||||||
for port in ports:
|
tier0_ips = self.nsxlib.logical_router_port.get_tier0_uplink_ips(
|
||||||
if (port.get('resource_type') ==
|
tier0_rtr)
|
||||||
'LogicalRouterUpLinkPort'):
|
for ip_address in tier0_ips:
|
||||||
for subnet in port.get('subnets', []):
|
for subnet_network in subnet_networks:
|
||||||
for ip_address in subnet.get('ip_addresses'):
|
if (netaddr.IPAddress(ip_address) in subnet_network):
|
||||||
if (netaddr.IPAddress(ip_address) in
|
msg = _("External subnet cannot overlap with T0 "
|
||||||
netaddr.IPNetwork(cidr)):
|
"router address %s!") % ip_address
|
||||||
msg = _("External subnet cannot "
|
LOG.error(msg)
|
||||||
"overlap with T0 router "
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
"address!")
|
|
||||||
LOG.error(msg)
|
|
||||||
raise n_exc.InvalidInput(
|
|
||||||
error_message=msg)
|
|
||||||
|
|
||||||
def _create_bulk_with_callback(self, resource, context, request_items,
|
def _create_bulk_with_callback(self, resource, context, request_items,
|
||||||
post_create_func=None, rollback_func=None):
|
post_create_func=None, rollback_func=None):
|
||||||
@@ -1803,7 +1816,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
return self._create_bulk('subnet', context, subnets)
|
return self._create_bulk('subnet', context, subnets)
|
||||||
|
|
||||||
def create_subnet(self, context, subnet):
|
def create_subnet(self, context, subnet):
|
||||||
self._validate_address_space(context, subnet['subnet'])
|
|
||||||
# TODO(berlin): public external subnet announcement
|
# TODO(berlin): public external subnet announcement
|
||||||
if (cfg.CONF.nsx_v3.native_dhcp_metadata and
|
if (cfg.CONF.nsx_v3.native_dhcp_metadata and
|
||||||
subnet['subnet'].get('enable_dhcp', False)):
|
subnet['subnet'].get('enable_dhcp', False)):
|
||||||
@@ -1820,6 +1832,17 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
if self._has_no_dhcp_enabled_subnet(context, network):
|
if self._has_no_dhcp_enabled_subnet(context, network):
|
||||||
created_subnet = super(
|
created_subnet = super(
|
||||||
NsxV3Plugin, self).create_subnet(context, subnet)
|
NsxV3Plugin, self).create_subnet(context, subnet)
|
||||||
|
try:
|
||||||
|
# This can be called only after the super create
|
||||||
|
# since we need the subnet pool to be translated
|
||||||
|
# to allocation pools
|
||||||
|
self._validate_address_space(
|
||||||
|
context, created_subnet)
|
||||||
|
except n_exc.InvalidInput:
|
||||||
|
# revert the subnet creation
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
super(NsxV3Plugin, self).delete_subnet(
|
||||||
|
context, created_subnet['id'])
|
||||||
self._extension_manager.process_create_subnet(context,
|
self._extension_manager.process_create_subnet(context,
|
||||||
subnet['subnet'], created_subnet)
|
subnet['subnet'], created_subnet)
|
||||||
dhcp_relay = self.get_network_az_by_net_id(
|
dhcp_relay = self.get_network_az_by_net_id(
|
||||||
@@ -1842,6 +1865,16 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
else:
|
else:
|
||||||
created_subnet = super(NsxV3Plugin, self).create_subnet(
|
created_subnet = super(NsxV3Plugin, self).create_subnet(
|
||||||
context, subnet)
|
context, subnet)
|
||||||
|
try:
|
||||||
|
# This can be called only after the super create
|
||||||
|
# since we need the subnet pool to be translated
|
||||||
|
# to allocation pools
|
||||||
|
self._validate_address_space(context, created_subnet)
|
||||||
|
except n_exc.InvalidInput:
|
||||||
|
# revert the subnet creation
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
super(NsxV3Plugin, self).delete_subnet(
|
||||||
|
context, created_subnet['id'])
|
||||||
return created_subnet
|
return created_subnet
|
||||||
|
|
||||||
def delete_subnet(self, context, subnet_id):
|
def delete_subnet(self, context, subnet_id):
|
||||||
|
|||||||
@@ -696,7 +696,14 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
|||||||
def test_create_subnet_with_shared_address_space(self):
|
def test_create_subnet_with_shared_address_space(self):
|
||||||
with self.network() as network:
|
with self.network() as network:
|
||||||
data = {'subnet': {'network_id': network['network']['id'],
|
data = {'subnet': {'network_id': network['network']['id'],
|
||||||
'cidr': '100.64.0.0/16'}}
|
'cidr': '100.64.0.0/16',
|
||||||
|
'name': 'sub1',
|
||||||
|
'enable_dhcp': False,
|
||||||
|
'dns_nameservers': None,
|
||||||
|
'allocation_pools': None,
|
||||||
|
'tenant_id': 'tenant_one',
|
||||||
|
'host_routes': None,
|
||||||
|
'ip_version': 4}}
|
||||||
self.assertRaises(n_exc.InvalidInput,
|
self.assertRaises(n_exc.InvalidInput,
|
||||||
self.plugin.create_subnet,
|
self.plugin.create_subnet,
|
||||||
context.get_admin_context(), data)
|
context.get_admin_context(), data)
|
||||||
@@ -714,7 +721,14 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
|||||||
def test_create_subnet_with_conflicting_t0_address(self):
|
def test_create_subnet_with_conflicting_t0_address(self):
|
||||||
network = self._create_external_network()
|
network = self._create_external_network()
|
||||||
data = {'subnet': {'network_id': network['network']['id'],
|
data = {'subnet': {'network_id': network['network']['id'],
|
||||||
'cidr': '172.20.1.0/24'}}
|
'cidr': '172.20.1.0/24',
|
||||||
|
'name': 'sub1',
|
||||||
|
'enable_dhcp': False,
|
||||||
|
'dns_nameservers': None,
|
||||||
|
'allocation_pools': None,
|
||||||
|
'tenant_id': 'tenant_one',
|
||||||
|
'host_routes': None,
|
||||||
|
'ip_version': 4}}
|
||||||
ports = [{'subnets': [{'ip_addresses': [u'172.20.1.60'],
|
ports = [{'subnets': [{'ip_addresses': [u'172.20.1.60'],
|
||||||
'prefix_length': 24}],
|
'prefix_length': 24}],
|
||||||
'resource_type': 'LogicalRouterUpLinkPort'}]
|
'resource_type': 'LogicalRouterUpLinkPort'}]
|
||||||
|
|||||||
Reference in New Issue
Block a user