diff --git a/quantum/db/l3_db.py b/quantum/db/l3_db.py index bbf2b34f7c..19ae37d107 100644 --- a/quantum/db/l3_db.py +++ b/quantum/db/l3_db.py @@ -351,6 +351,16 @@ class L3_NAT_db_mixin(l3.RouterPluginBase): return {'port_id': port['id'], 'subnet_id': port['fixed_ips'][0]['subnet_id']} + def _confirm_router_interface_not_in_use(self, context, router_id, + subnet_id): + subnet_db = self._get_subnet(context, subnet_id) + subnet_cidr = netaddr.IPNetwork(subnet_db['cidr']) + fip_qry = context.session.query(FloatingIP) + for fip_db in fip_qry.filter_by(router_id=router_id): + if netaddr.IPAddress(fip_db['fixed_ip_address']) in subnet_cidr: + raise l3.RouterInterfaceInUseByFloatingIP( + router_id=router_id, subnet_id=subnet_id) + def remove_router_interface(self, context, router_id, interface_info): # make sure router exists router = self._get_router(context, router_id) @@ -382,9 +392,15 @@ class L3_NAT_db_mixin(l3.RouterPluginBase): if port_db['device_id'] != router_id: raise w_exc.HTTPConflict("port_id %s not used by router" % port_db['id']) + self._confirm_router_interface_not_in_use( + context, router_id, + port_db['fixed_ips'][0]['subnet_id']) self.delete_port(context, port_db['id'], l3_port_check=False) elif 'subnet_id' in interface_info: subnet_id = interface_info['subnet_id'] + self._confirm_router_interface_not_in_use(context, router_id, + subnet_id) + subnet = self._get_subnet(context, subnet_id) found = False diff --git a/quantum/extensions/l3.py b/quantum/extensions/l3.py index 3d23be5f21..7d6f3a67c1 100644 --- a/quantum/extensions/l3.py +++ b/quantum/extensions/l3.py @@ -37,6 +37,12 @@ class RouterInUse(qexception.InUse): message = _("Router %(router_id)s still has active ports") +class RouterInterfaceInUseByFloatingIP(qexception.InUse): + message = _("Router interface for subnet %(subnet_id)s on router " + "%(router_id)s cannot be deleted, as it is required " + "by one or more floating IPs.") + + class FloatingIPNotFound(qexception.NotFound): message = _("Floating IP %(floatingip_id)s could not be found") diff --git a/quantum/tests/unit/test_l3_plugin.py b/quantum/tests/unit/test_l3_plugin.py index 787d10e3ad..5f252c3cf6 100644 --- a/quantum/tests/unit/test_l3_plugin.py +++ b/quantum/tests/unit/test_l3_plugin.py @@ -1099,6 +1099,33 @@ class L3NatDBTestCase(test_db_plugin.QuantumDbPluginV2TestCase): uuidutils.generate_uuid(), 'iamnotnanip') self.assertEqual(res.status_int, 400) + def test_floatingip_delete_router_intf_with_subnet_id_returns_409(self): + found = False + with self.floatingip_with_assoc() as fip: + for p in self._list('ports')['ports']: + if p['device_owner'] == 'network:router_interface': + subnet_id = p['fixed_ips'][0]['subnet_id'] + router_id = p['device_id'] + self._router_interface_action( + 'remove', router_id, subnet_id, None, + expected_code=exc.HTTPConflict.code) + found = True + break + self.assertTrue(found) + + def test_floatingip_delete_router_intf_with_port_id_returns_409(self): + found = False + with self.floatingip_with_assoc() as fip: + for p in self._list('ports')['ports']: + if p['device_owner'] == 'network:router_interface': + router_id = p['device_id'] + self._router_interface_action( + 'remove', router_id, None, p['id'], + expected_code=exc.HTTPConflict.code) + found = True + break + self.assertTrue(found) + def test_list_nets_external(self): with self.network() as n1: self._set_net_external(n1['network']['id'])