NSX|P: Add validation on number of segment subnets
The NSX does not support multiple segment subnets of the same ip version This means that the plugin should prevent creation of a DHCP subnet and another overlay router-interface subnet on the same network Change-Id: I2898efe1ccbc7d06e6baeb2b30f76e3190801fa8
This commit is contained in:
parent
d1f41da8b5
commit
9cc5655489
@ -1177,6 +1177,42 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
|
def _validate_segment_subnets_num(self, context, net_id, subnet_data):
|
||||||
|
"""Validate no multiple segment subnets on the NSX
|
||||||
|
The NSX cannot support more than 1 segment subnet of the same ip
|
||||||
|
version. This include dhcp subnets and overlay router interfaces
|
||||||
|
"""
|
||||||
|
if ('enable_dhcp' not in subnet_data or
|
||||||
|
not subnet_data.get('enable_dhcp')):
|
||||||
|
# NO DHCP so no new segment subnet
|
||||||
|
return
|
||||||
|
|
||||||
|
ip_ver = subnet_data.get('ip_version', 4)
|
||||||
|
if ip_ver == 6:
|
||||||
|
# Since the plugin does not allow multiple ipv6 subnets,
|
||||||
|
# this can be ignored.
|
||||||
|
return
|
||||||
|
|
||||||
|
overlay_net = self._is_overlay_network(context, net_id)
|
||||||
|
if not overlay_net:
|
||||||
|
# Since the plugin allows only 1 DHCP subnet, if this is not an
|
||||||
|
# overlay network, no problem.
|
||||||
|
return
|
||||||
|
|
||||||
|
interface_ports = self._get_network_interface_ports(
|
||||||
|
context, net_id)
|
||||||
|
if interface_ports:
|
||||||
|
# Should have max 1 router interface per network
|
||||||
|
if_port = interface_ports[0]
|
||||||
|
if if_port['fixed_ips']:
|
||||||
|
if_subnet = interface_ports[0]['fixed_ips'][0]['subnet_id']
|
||||||
|
if subnet_data.get('id') != if_subnet:
|
||||||
|
msg = (_("Can not create a DHCP subnet on network %(net)s "
|
||||||
|
"as another %(ver)s subnet is attached to a "
|
||||||
|
"router") % {'net': net_id, 'ver': ip_ver})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
@nsx_plugin_common.api_replay_mode_wrapper
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
def create_subnet(self, context, subnet):
|
def create_subnet(self, context, subnet):
|
||||||
if not self.use_policy_dhcp:
|
if not self.use_policy_dhcp:
|
||||||
@ -1207,6 +1243,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
"subnet in network %s") % net_id)
|
"subnet in network %s") % net_id)
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
self._validate_segment_subnets_num(
|
||||||
|
context, net_id, subnet['subnet'])
|
||||||
|
|
||||||
# Create the neutron subnet.
|
# Create the neutron subnet.
|
||||||
# Any failure from here and on will require rollback.
|
# Any failure from here and on will require rollback.
|
||||||
@ -1302,6 +1340,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
|
self._validate_segment_subnets_num(
|
||||||
|
context, net_id, subnet_data)
|
||||||
|
|
||||||
updated_subnet = super(NsxPolicyPlugin, self).update_subnet(
|
updated_subnet = super(NsxPolicyPlugin, self).update_subnet(
|
||||||
context, subnet_id, subnet)
|
context, subnet_id, subnet)
|
||||||
self._extension_manager.process_update_subnet(
|
self._extension_manager.process_update_subnet(
|
||||||
@ -2512,6 +2553,38 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
cidr_prefix = int(subnet['cidr'].split('/')[1])
|
cidr_prefix = int(subnet['cidr'].split('/')[1])
|
||||||
return "%s/%s" % (subnet['gateway_ip'], cidr_prefix)
|
return "%s/%s" % (subnet['gateway_ip'], cidr_prefix)
|
||||||
|
|
||||||
|
def _validate_router_segment_subnets(self, context, network_id,
|
||||||
|
overlay_net, subnet):
|
||||||
|
"""Validate that adding an interface to a router will not cause
|
||||||
|
multiple segments subnets which is not allowed
|
||||||
|
"""
|
||||||
|
if not overlay_net:
|
||||||
|
# Only interfaces for overlay networks create segment subnets
|
||||||
|
return
|
||||||
|
|
||||||
|
if subnet.get('ip_version', 4) != 4:
|
||||||
|
# IPv6 is not relevant here since plugin allow only 1 ipv6 subnet
|
||||||
|
# per network
|
||||||
|
return
|
||||||
|
|
||||||
|
if subnet['enable_dhcp']:
|
||||||
|
# This subnet is with dhcp, so there cannot be any other with dhcp
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.use_policy_dhcp:
|
||||||
|
# Only policy DHCP creates segment subnets
|
||||||
|
return
|
||||||
|
|
||||||
|
# Look for another subnet with DHCP
|
||||||
|
network = self._get_network(context.elevated(), network_id)
|
||||||
|
for subnet in network.subnets:
|
||||||
|
if subnet.enable_dhcp and subnet.ip_version == 4:
|
||||||
|
msg = (_("Can not add router interface on network %(net)s "
|
||||||
|
"as another %(ver)s subnet has enabled DHCP") %
|
||||||
|
{'net': network_id, 'ver': subnet.ip_version})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
@nsx_plugin_common.api_replay_mode_wrapper
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
# NOTE: In dual stack case, neutron would create a separate interface
|
# NOTE: In dual stack case, neutron would create a separate interface
|
||||||
@ -2549,6 +2622,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
self._validate_gw_overlap_interfaces(context, gw_network_id,
|
self._validate_gw_overlap_interfaces(context, gw_network_id,
|
||||||
[network_id])
|
[network_id])
|
||||||
|
|
||||||
|
if subnet:
|
||||||
|
self._validate_router_segment_subnets(context, network_id,
|
||||||
|
overlay_net, subnet)
|
||||||
|
|
||||||
# Update the interface of the neutron router
|
# Update the interface of the neutron router
|
||||||
info = super(NsxPolicyPlugin, self).add_router_interface(
|
info = super(NsxPolicyPlugin, self).add_router_interface(
|
||||||
context, router_id, interface_info)
|
context, router_id, interface_info)
|
||||||
|
@ -2266,3 +2266,14 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
|
|||||||
def test_nat_rules_firewall_match_external(self):
|
def test_nat_rules_firewall_match_external(self):
|
||||||
self._test_nat_rules_firewall_match(
|
self._test_nat_rules_firewall_match(
|
||||||
False, pol_const.NAT_FIREWALL_MATCH_EXTERNAL)
|
False, pol_const.NAT_FIREWALL_MATCH_EXTERNAL)
|
||||||
|
|
||||||
|
def test_router_interface_with_dhcp_subnet(self):
|
||||||
|
with self.router() as r,\
|
||||||
|
self.network() as net,\
|
||||||
|
self.subnet(cidr='20.0.0.0/24', network=net),\
|
||||||
|
self.subnet(cidr='30.0.0.0/24', network=net,
|
||||||
|
enable_dhcp=False) as if_subnet:
|
||||||
|
self._router_interface_action(
|
||||||
|
'add', r['router']['id'],
|
||||||
|
if_subnet['subnet']['id'], None,
|
||||||
|
expected_code=exc.HTTPBadRequest.code)
|
||||||
|
@ -844,6 +844,36 @@ class NsxPolicyDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
ports[0]['network_id'])
|
ports[0]['network_id'])
|
||||||
self.assertEqual(False, ports[0]['port_security_enabled'])
|
self.assertEqual(False, ports[0]['port_security_enabled'])
|
||||||
|
|
||||||
|
def test_create_dhcp_subnet_with_rtr_if(self):
|
||||||
|
# Test that cannot create a DHCP subnet if a router interface exists
|
||||||
|
dummy_port = {'fixed_ips': [{'subnet_id': 'dummy'}]}
|
||||||
|
with mock.patch.object(self.plugin, 'get_ports',
|
||||||
|
return_value=[dummy_port]),\
|
||||||
|
self.network() as net:
|
||||||
|
subnet = self._make_subnet_data(
|
||||||
|
network_id=net['network']['id'], cidr='10.0.0.0/24',
|
||||||
|
tenant_id=net['network']['tenant_id'])
|
||||||
|
self.assertRaises(
|
||||||
|
n_exc.InvalidInput, self.plugin.create_subnet,
|
||||||
|
context.get_admin_context(), subnet)
|
||||||
|
|
||||||
|
def test_update_dhcp_subnet_with_rtr_if(self):
|
||||||
|
# Test that cannot enable a DHCP on a subnet if a router interface
|
||||||
|
# exists
|
||||||
|
dummy_port = {'fixed_ips': [{'subnet_id': 'dummy'}]}
|
||||||
|
with mock.patch.object(self.plugin, 'get_ports',
|
||||||
|
return_value=[dummy_port]),\
|
||||||
|
self.network() as net:
|
||||||
|
subnet = self._make_subnet_data(
|
||||||
|
network_id=net['network']['id'], cidr='10.0.0.0/24',
|
||||||
|
tenant_id=net['network']['tenant_id'], enable_dhcp=False)
|
||||||
|
neutron_subnet = self.plugin.create_subnet(
|
||||||
|
context.get_admin_context(), subnet)
|
||||||
|
self.assertRaises(
|
||||||
|
n_exc.InvalidInput, self.plugin.update_subnet,
|
||||||
|
context.get_admin_context(), neutron_subnet['id'],
|
||||||
|
{'subnet': {'enable_dhcp': True}})
|
||||||
|
|
||||||
|
|
||||||
class NsxPolicyMetadataTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
class NsxPolicyMetadataTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||||
"""Test native metadata config when using MP MDProxy"""
|
"""Test native metadata config when using MP MDProxy"""
|
||||||
|
Loading…
Reference in New Issue
Block a user