diff --git a/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/__init__.py b/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/__init__.py index 7bcee5f89c..bb98a89055 100644 --- a/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/__init__.py +++ b/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/__init__.py @@ -35,8 +35,6 @@ LROUTER_TYPES = [LROUTERPORT_UPLINK, LROUTERPORT_DOWNLINK, LROUTERPORT_LINK] -ROUTER_INTF_PORT_NAME = "Tier1-RouterDownLinkPort" - def get_edge_cluster(edge_cluster_uuid): resource = "edge-clusters/%s" % edge_cluster_uuid @@ -243,23 +241,6 @@ def get_logical_router_port_by_ls_id(logical_switch_id): operation=err_msg) -def create_logical_router_port_by_ls_id(logical_router_id, - ls_id, - logical_switch_port_id, - resource_type, - address_groups): - try: - port = get_logical_router_port_by_ls_id(ls_id) - except nsx_exc.ResourceNotFound: - return create_logical_router_port(logical_router_id, - ROUTER_INTF_PORT_NAME, - resource_type, - logical_switch_port_id, - address_groups) - else: - return update_logical_router_port(port['id'], subnets=address_groups) - - def create_logical_router_port(logical_router_id, display_name, resource_type, diff --git a/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/router.py b/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/router.py index 667484d833..8529ac364f 100644 --- a/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/router.py +++ b/vmware_nsx/neutron/plugins/vmware/nsxlib/v3/router.py @@ -34,6 +34,7 @@ MIN_EDGE_NODE_NUM = 1 TIER0_ROUTER_LINK_PORT_NAME = "TIER0-RouterLinkPort" TIER1_ROUTER_LINK_PORT_NAME = "TIER1-RouterLinkPort" +ROUTER_INTF_PORT_NAME = "Tier1-RouterDownLinkPort" def validate_tier0(tier0_groups_dict, tier0_uuid): @@ -126,3 +127,20 @@ def add_gw_snat_rule(logical_router_id, gw_ip): def update_router_edge_cluster(nsx_router_id, edge_cluster_uuid): return nsxlib.update_logical_router(nsx_router_id, edge_cluster_id=edge_cluster_uuid) + + +def create_logical_router_intf_port_by_ls_id(logical_router_id, + ls_id, + logical_switch_port_id, + address_groups): + try: + port = nsxlib.get_logical_router_port_by_ls_id(ls_id) + except nsx_exc.ResourceNotFound: + return nsxlib.create_logical_router_port(logical_router_id, + ROUTER_INTF_PORT_NAME, + nsxlib.LROUTERPORT_DOWNLINK, + logical_switch_port_id, + address_groups) + else: + return nsxlib.update_logical_router_port( + port['id'], subnets=address_groups) diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v3_plugin.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v3_plugin.py index 30c7f8883a..0249c7f033 100644 --- a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v3_plugin.py +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v3_plugin.py @@ -797,26 +797,54 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2, address_groups.append(address_group) return (ports, address_groups) + def _validate_multiple_subnets_diff_routers(self, context, network_id): + port_filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF], + 'network_id': [network_id]} + intf_ports = self.get_ports(context.elevated(), filters=port_filters) + router_ids = [port['device_id'] for port in intf_ports] + router_id_set = set(router_ids) + if len(router_id_set) >= 2: + err_msg = _("Subnets on network %s cannot be attached to " + "different routers") % network_id + raise n_exc.InvalidInput(error_message=err_msg) + def add_router_interface(self, context, router_id, interface_info): - # TODO(berlin): disallow multiple subnets attached to different routers + info = super(NsxV3Plugin, self).add_router_interface( context, router_id, interface_info) - subnet = self.get_subnet(context, info['subnet_ids'][0]) - port = self.get_port(context, info['port_id']) - network_id = subnet['network_id'] - nsx_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id( - context.session, port['id']) + try: + subnet = self.get_subnet(context, info['subnet_ids'][0]) + port = self.get_port(context, info['port_id']) + network_id = subnet['network_id'] + # disallow multiple subnets belong to same network being attached + # to different routers + self._validate_multiple_subnets_diff_routers(context, network_id) + nsx_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id( + context.session, port['id']) - nsx_router_id = nsx_db.get_nsx_router_id(context.session, - router_id) - _ports, address_groups = self._get_ports_and_address_groups( - context, router_id, network_id) - nsxlib.create_logical_router_port_by_ls_id( - logical_router_id=nsx_router_id, - ls_id=nsx_net_id, - logical_switch_port_id=nsx_port_id, - resource_type="LogicalRouterDownLinkPort", - address_groups=address_groups) + nsx_router_id = nsx_db.get_nsx_router_id(context.session, + router_id) + _ports, address_groups = self._get_ports_and_address_groups( + context, router_id, network_id) + routerlib.create_logical_router_intf_port_by_ls_id( + logical_router_id=nsx_router_id, + ls_id=nsx_net_id, + logical_switch_port_id=nsx_port_id, + address_groups=address_groups) + + router_db = self._get_router(context, router_id) + if router_db.gw_port and not router_db.enable_snat: + # TODO(berlin): Announce the subnet on tier0 if enable_snat + # is False + pass + except n_exc.InvalidInput: + with excutils.save_and_reraise_exception(): + super(NsxV3Plugin, self).remove_router_interface( + context, router_id, interface_info) + except nsx_exc.ManagerError: + with excutils.save_and_reraise_exception(): + self.remove_router_interface( + context, router_id, interface_info) return info def remove_router_interface(self, context, router_id, interface_info): @@ -850,6 +878,12 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2, raise l3.RouterInterfaceNotFoundForSubnet(router_id=router_id, subnet_id=subnet_id) try: + # TODO(berlin): Revocate announce the subnet on tier0 if + # enable_snat is False + router_db = self._get_router(context, router_id) + if router_db.gw_port and not router_db.enable_snat: + pass + nsx_net_id, _nsx_port_id = nsx_db.get_nsx_switch_and_port_id( context.session, port_id) subnet = self.get_subnet(context, subnet_id) diff --git a/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v3_plugin.py b/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v3_plugin.py index b2793e89b3..ace2e70f71 100644 --- a/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v3_plugin.py +++ b/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v3_plugin.py @@ -18,6 +18,7 @@ from oslo_config import cfg import six from neutron.api.v2 import attributes +from neutron.common import exceptions as n_exc from neutron import context from neutron.extensions import external_net from neutron.extensions import extraroute @@ -188,11 +189,11 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxPluginV3TestCase): ext_mgr = ext_mgr or TestL3ExtensionManager() super(L3NatTest, self).setUp( plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) - plugin_instance = manager.NeutronManager.get_plugin() + self.plugin_instance = manager.NeutronManager.get_plugin() self._plugin_name = "%s.%s" % ( - plugin_instance.__module__, - plugin_instance.__class__.__name__) - self._plugin_class = plugin_instance.__class__ + self.plugin_instance.__module__, + self.plugin_instance.__class__.__name__) + self._plugin_class = self.plugin_instance.__class__ nsxlib.create_logical_port = self.v3_mock.create_logical_port nsxlib.create_logical_router = self.v3_mock.create_logical_router nsxlib.update_logical_router = self.v3_mock.update_logical_router @@ -251,6 +252,33 @@ class TestL3NatTestCase(L3NatTest, def test_floatingip_with_invalid_create_port(self): self._test_floatingip_with_invalid_create_port(self._plugin_name) + def test_routes_update_for_multiple_routers(self): + self.skipTest('not supported') + + def test_floatingip_multi_external_one_internal(self): + self.skipTest('not supported') + + def test_multiple_subnets_on_different_routers(self): + with self.network() as network: + with self.subnet(network=network) as s1,\ + self.subnet(network=network, + cidr='11.0.0.0/24') as s2,\ + self.router() as r1,\ + self.router() as r2: + self._router_interface_action('add', r1['router']['id'], + s1['subnet']['id'], None) + self.assertRaises(n_exc.InvalidInput, + self.plugin_instance.add_router_interface, + context.get_admin_context(), + r2['router']['id'], + {'subnet_id': s2['subnet']['id']}) + self._router_interface_action('remove', r1['router']['id'], + s1['subnet']['id'], None) + self._router_interface_action('add', r2['router']['id'], + s2['subnet']['id'], None) + self._router_interface_action('remove', r2['router']['id'], + s2['subnet']['id'], None) + class ExtGwModeTestCase(L3NatTest, test_ext_gw_mode.ExtGwModeIntTestCase):