diff --git a/vmware_nsx/common/exceptions.py b/vmware_nsx/common/exceptions.py index 9b22f36a50..2e8664a427 100644 --- a/vmware_nsx/common/exceptions.py +++ b/vmware_nsx/common/exceptions.py @@ -181,3 +181,9 @@ class NsxBgpSpeakerUnableToAddGatewayNetwork(n_exc.BadRequest): message = _("Unable to add gateway network %(network_id)s to BGP speaker " "%(bgp_speaker_id)s, network may be associated with one BGP " "speaker at most.") + + +class NsxRouterInterfaceDoesNotMatchAddressScope(n_exc.BadRequest): + message = _("Unable to update no-NAT router %(router_id)s, " + "only subnets allocated from address-scope " + "%(address_scope_id)s can be connected.") diff --git a/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py index 91ae5d6144..bf4100ee5e 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py @@ -233,8 +233,8 @@ class RouterDistributedDriver(router_driver.RouterBaseDriver): def _validate_multiple_subnets_routers(self, context, router_id, interface_info): _nsxv_plugin = self.plugin - net_id = _nsxv_plugin._get_interface_info_net_id(context, - interface_info) + net_id, subnet_id = _nsxv_plugin._get_interface_info(context, + interface_info) port_filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF], 'network_id': [net_id]} diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index 65071e97a5..0f94d9009e 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -65,6 +65,7 @@ from neutron.db import portsecurity_db from neutron.db import quota_db # noqa from neutron.db import securitygroups_db from neutron.db import vlantransparent_db +from neutron.extensions import address_scope as ext_address_scope from neutron.extensions import allowedaddresspairs as addr_pair from neutron.extensions import availability_zone as az_ext from neutron.extensions import external_net as ext_net_extn @@ -2969,7 +2970,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, def update_router(self, context, router_id, router): # Validate that the gateway information is relevant - self._extract_external_gw(context, router, is_extract=False) + gw_info = self._extract_external_gw(context, router, is_extract=False) # Toggling the distributed flag is not supported if 'distributed' in router['router']: r = self.get_router(context, router_id) @@ -3017,6 +3018,15 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, # but remove the router-size that was already updated router['router'].pop(ROUTER_SIZE, None) + if (validators.is_attr_set(gw_info) and + not gw_info.get('enable_snat', cfg.CONF.enable_snat_by_default)): + router_ports = self._get_router_interfaces(context, router_id) + for port in router_ports: + for fip in port['fixed_ips']: + self._validate_address_scope_for_router_interface( + context, router_id, + gw_info['network_id'], fip['subnet_id']) + router_driver = self._find_router_driver(context, router_id) return router_driver.update_router(context, router_id, router) @@ -3266,6 +3276,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, 'network_id': [network_id]} return self.get_ports(context, filters=port_filters) + def _get_router_interfaces(self, context, router_id): + port_filters = {'device_id': [router_id], + 'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]} + return self.get_ports(context, filters=port_filters) + def _get_address_groups(self, context, router_id, network_id): address_groups = [] ports = self._get_router_interface_ports_by_network( @@ -3380,31 +3395,50 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, else: edge_utils.update_internal_interface(*update_args) - def _get_interface_info_net_id(self, context, interface_info): + def _get_interface_info(self, context, interface_info): is_port, is_sub = self._validate_interface_info(interface_info) if is_port: - net_id = self.get_port( - context, interface_info['port_id'])['network_id'] + port = self._check_router_port(context, + interface_info['port_id'], '') + subnet_id = port['fixed_ips'][0]['subnet_id'] + net_id = port['network_id'] elif is_sub: + subnet_id = interface_info['subnet_id'] net_id = self.get_subnet( - context, interface_info['subnet_id'])['network_id'] - return net_id + context, subnet_id)['network_id'] + return net_id, subnet_id - def _is_external_interface_info(self, context, interface_info): - net_id = self._get_interface_info_net_id(context, interface_info) - network = self.get_network(context, net_id) - if (network.get(ext_net_extn.EXTERNAL)): - return True - return False + def _validate_address_scope_for_router_interface(self, context, router_id, + gw_network_id, subnet_id): + network = self.get_network(context, gw_network_id) + subnet = self.get_subnet(context, subnet_id) + address_scope = network.get(ext_address_scope.IPV4_ADDRESS_SCOPE) + if not address_scope: + return + if not subnet['subnetpool_id']: + raise nsx_exc.NsxRouterInterfaceDoesNotMatchAddressScope( + router_id=router_id, address_scope_id=address_scope) + subnetpool = self.get_subnetpool(context, subnet['subnetpool_id']) + if subnetpool.get('address_scope_id', '') != address_scope: + raise nsx_exc.NsxRouterInterfaceDoesNotMatchAddressScope( + router_id=router_id, address_scope_id=address_scope) def add_router_interface(self, context, router_id, interface_info): + router = self.get_router(context, router_id) + net_id, subnet_id = self._get_interface_info(context, interface_info) + network = self.get_network(context.elevated(), net_id) # Do not support external subnet/port as a router interface - if self._is_external_interface_info(context.elevated(), - interface_info): - msg = (_('cannot add an external subnet/port as a router ' - 'interface')) + if network.get(ext_net_extn.EXTERNAL): + msg = _("cannot add an external subnet/port as a router interface") raise n_exc.InvalidInput(error_message=msg) + snat_disabled = (router[l3.EXTERNAL_GW_INFO] and + not router[l3.EXTERNAL_GW_INFO]['enable_snat']) + if snat_disabled and subnet_id: + gw_network_id = router[l3.EXTERNAL_GW_INFO]['network_id'] + self._validate_address_scope_for_router_interface( + context, router_id, gw_network_id, subnet_id) + router_driver = self._find_router_driver(context, router_id) try: return router_driver.add_router_interface(