Merge "NSX-V3: Enhance subnet overlap checks"

This commit is contained in:
Zuul
2018-04-29 10:47:24 +00:00
committed by Gerrit Code Review
2 changed files with 75 additions and 28 deletions

View File

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

View File

@@ -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'}]