diff --git a/quantum/db/l3_db.py b/quantum/db/l3_db.py index dcf91becf5..f3fab12edd 100644 --- a/quantum/db/l3_db.py +++ b/quantum/db/l3_db.py @@ -182,6 +182,11 @@ class L3_NAT_db_mixin(l3.RouterPluginBase): # figure out if we need to delete existing port if gw_port and gw_port['network_id'] != network_id: + fip_count = self.get_floatingips_count(context.elevated(), + {'router_id': [router_id]}) + if fip_count: + raise l3.RouterExternalGatewayInUseByFloatingIp( + router_id=router_id, net_id=gw_port['network_id']) with context.session.begin(subtransactions=True): router.gw_port = None context.session.add(router) diff --git a/quantum/extensions/l3.py b/quantum/extensions/l3.py index 15542d31ae..3d23be5f21 100644 --- a/quantum/extensions/l3.py +++ b/quantum/extensions/l3.py @@ -64,6 +64,12 @@ class ExternalNetworkInUse(qexception.InUse): "non-external, since it has existing gateway ports") +class RouterExternalGatewayInUseByFloatingIp(qexception.InUse): + message = _("Gateway cannot be updated for router %(router_id), since a " + "gateway to external network %(net_id) is required by one or " + "more floating IPs.") + + def _validate_uuid_or_none(data, valid_values=None): if data is None: return None diff --git a/quantum/tests/unit/test_l3_plugin.py b/quantum/tests/unit/test_l3_plugin.py index fe0a0b6001..787d10e3ad 100644 --- a/quantum/tests/unit/test_l3_plugin.py +++ b/quantum/tests/unit/test_l3_plugin.py @@ -717,6 +717,21 @@ class L3NatDBTestCase(test_db_plugin.QuantumDbPluginV2TestCase): r['router']['id'], s2['subnet']['network_id']) + def test_router_update_gateway_with_existed_floatingip(self): + with self.subnet() as subnet: + self._set_net_external(subnet['subnet']['network_id']) + with self.floatingip_with_assoc() as fip: + self._add_external_gateway_to_router( + fip['floatingip']['router_id'], + subnet['subnet']['network_id'], + expected_code=exc.HTTPConflict.code) + + def test_router_update_gateway_to_empty_with_existed_floatingip(self): + with self.floatingip_with_assoc() as fip: + self._remove_external_gateway_from_router( + fip['floatingip']['router_id'], None, + expected_code=exc.HTTPConflict.code) + def test_router_add_gateway_invalid_network(self): with self.router() as r: self._add_external_gateway_to_router( @@ -950,9 +965,6 @@ class L3NatDBTestCase(test_db_plugin.QuantumDbPluginV2TestCase): port_id=p['port']['id']) self.assertEqual(res.status_int, exc.HTTPCreated.code) floatingip = self.deserialize(fmt, res) - self._remove_external_gateway_from_router( - r['router']['id'], - public_sub['subnet']['network_id']) self._delete('routers', r['router']['id'], expected_code=exc.HTTPConflict.code) # Cleanup