if port_db['device_owner'] in [DEVICE_OWNER_ROUTER_INTF,
DEVICE_OWNER_ROUTER_GW,
DEVICE_OWNER_FLOATINGIP]:
- raise l3.L3PortInUse(port_id=port_id,
- device_owner=port_db['device_owner'])
+ # Raise port in use only if the port has IP addresses
+ # Otherwise it's a stale port that can be removed
+ fixed_ips = port_db['fixed_ips'].all()
+ if fixed_ips:
+ raise l3.L3PortInUse(port_id=port_id,
+ device_owner=port_db['device_owner'])
+ else:
+ LOG.debug(_("Port %(port_id)s has owner %(port_owner)s, but "
+ "no IP address, so it can be deleted"),
+ {'port_id': port_db['id'],
+ 'port_owner': port_db['device_owner']})
def disassociate_floatingips(self, context, port_id):
with context.session.begin(subtransactions=True):
r['router']['id'],
n['network']['id'], expected_code=exc.HTTPBadRequest.code)
+ def test_delete_unused_router_interface(self):
+ with self.network() as n:
+ with self.router() as r:
+ with self.subnet(network=n) as s:
+ res = self._create_port(self.fmt,
+ s['subnet']['network_id'])
+ p = self.deserialize(self.fmt, res)
+ self._router_interface_action('add',
+ r['router']['id'],
+ None,
+ p['port']['id'])
+ # The subnet here is deleted, and the port should have no IP
+ self._delete('ports', p['port']['id'])
+ # Verify the port has been deleted
+ self._show('ports', p['port']['id'],
+ expected_code=exc.HTTPNotFound.code)
+
def test_router_remove_interface_inuse_returns_409(self):
with self.router() as r:
with self.subnet() as s: