def _check_subnet_not_used(context, subnet_id): try: kwargs = {'context': context, 'subnet_id': subnet_id} registry.notify( resources.SUBNET, events.BEFORE_DELETE, None, **kwargs) except exceptions.CallbackFailure as e: raise n_exc.SubnetInUse(subnet_id=subnet_id, reason=e)
def delete_subnet(self, context, id): # REVISIT(rkukura) The super(Ml2Plugin, self).delete_subnet() # function is not used because it auto-deletes ports from the # DB without invoking the derived class's delete_port(), # preventing mechanism drivers from being called. This # approach should be revisited when the API layer is reworked # during icehouse. LOG.debug(_("Deleting subnet %s"), id) session = context.session while True: with session.begin(subtransactions=True): subnet = self.get_subnet(context, id) # Get ports to auto-delete. allocated = (session.query(models_v2.IPAllocation). filter_by(subnet_id=id). join(models_v2.Port). filter_by(network_id=subnet['network_id']). with_lockmode('update').all()) LOG.debug(_("Ports to auto-delete: %s"), allocated) only_auto_del = all(not a.port_id or a.ports.device_owner in db_base_plugin_v2. AUTO_DELETE_PORT_OWNERS for a in allocated) if not only_auto_del: LOG.debug(_("Tenant-owned ports exist")) raise exc.SubnetInUse(subnet_id=id) if not allocated: mech_context = driver_context.SubnetContext(self, context, subnet) self.mechanism_manager.delete_subnet_precommit( mech_context) LOG.debug(_("Deleting subnet record")) record = self._get_subnet(context, id) session.delete(record) LOG.debug(_("Committing transaction")) break for a in allocated: try: self.delete_port(context, a.port_id) except Exception: LOG.exception(_("Exception auto-deleting port %s"), a.port_id) raise try: self.mechanism_manager.delete_subnet_postcommit(mech_context) except ml2_exc.MechanismDriverError: # TODO(apech) - One or more mechanism driver failed to # delete the subnet. Ideally we'd notify the caller of # the fact that an error occurred. LOG.error(_("mechanism_manager.delete_subnet_postcommit failed"))
def _subnet_check_ip_allocations_internal_router_ports( self, context, subnet_id): # Do not delete the subnet if IP allocations for internal # router ports still exist allocs = context.session.query(models_v2.IPAllocation).filter_by( subnet_id=subnet_id).join(models_v2.Port).filter( models_v2.Port.device_owner.in_( constants.ROUTER_INTERFACE_OWNERS)).first() if allocs: LOG.debug( "Subnet %s still has internal router ports, " "cannot delete", subnet_id) raise n_exc.SubnetInUse(subnet_id=id)
def delete_subnet(self, context, id): with context.session.begin(subtransactions=True): subnet = self._get_subnet(context, id) # Make sure the subnet isn't used by other resources _check_subnet_not_used(context, id) # Delete all network owned ports qry_network_ports = (context.session.query( models_v2.IPAllocation).filter_by(subnet_id=subnet['id']).join( models_v2.Port)) # Remove network owned ports, and delete IP allocations # for IPv6 addresses which were automatically generated # via SLAAC is_auto_addr_subnet = ipv6_utils.is_auto_address_subnet(subnet) if is_auto_addr_subnet: self._subnet_check_ip_allocations_internal_router_ports( context, id) else: qry_network_ports = (qry_network_ports.filter( models_v2.Port.device_owner.in_(AUTO_DELETE_PORT_OWNERS))) network_ports = qry_network_ports.all() if network_ports: for port in network_ports: context.session.delete(port) # Check if there are more IP allocations, unless # is_auto_address_subnet is True. In that case the check is # unnecessary. This additional check not only would be wasteful # for this class of subnet, but is also error-prone since when # the isolation level is set to READ COMMITTED allocations made # concurrently will be returned by this query if not is_auto_addr_subnet: alloc = self._subnet_check_ip_allocations(context, id) if alloc: LOG.info( _LI("Found port (%(port_id)s, %(ip)s) having IP " "allocation on subnet " "%(subnet)s, cannot delete"), { 'ip': alloc.ip_address, 'port_id': alloc.port_id, 'subnet': id }) raise n_exc.SubnetInUse(subnet_id=id) context.session.delete(subnet) # Delete related ipam subnet manually, # since there is no FK relationship self.ipam.delete_subnet(context, id)
def delete_subnet(self, context, id): LOG.debug(_("delete_subnet() called")) with context.session.begin(): subnet = self._get_subnet(context, id) # Check if ports are using this subnet allocated_qry = context.session.query(models_v2.IPAllocation) allocated_qry = allocated_qry.options(orm.joinedload('ports')) allocated = allocated_qry.filter_by(subnet_id=id) prefix = db_base_plugin_v2.AGENT_OWNER_PREFIX if not all(not a.port_id or a.ports.device_owner.startswith(prefix) for a in allocated): raise exc.SubnetInUse(subnet_id=id) context.session.close() kwargs = {const.SUBNET: subnet} self._invoke_device_plugins(self._func_name(), [context, id, kwargs]) return super(PluginV2, self).delete_subnet(context, id)
def _delete_subnet(context, subnet): if subnet.allocated_ips: raise exceptions.SubnetInUse(subnet_id=subnet["id"]) db_api.subnet_delete(context, subnet)
def delete_subnet(self, context, id): # REVISIT(rkukura) The super(Ml2Plugin, self).delete_subnet() # function is not used because it deallocates the subnet's addresses # from ports in the DB without invoking the derived class's # update_port(), preventing mechanism drivers from being called. # This approach should be revisited when the API layer is reworked # during icehouse. LOG.debug(_("Deleting subnet %s"), id) session = context.session while True: with session.begin(subtransactions=True): subnet = self.get_subnet(context, id) # Get ports to auto-deallocate allocated = (session.query(models_v2.IPAllocation).filter_by( subnet_id=id).join(models_v2.Port).filter_by( network_id=subnet['network_id']).with_lockmode( 'update').all()) LOG.debug(_("Ports to auto-deallocate: %s"), allocated) only_auto_del = all(not a.port_id or a.ports.device_owner in db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS for a in allocated) if not only_auto_del: LOG.debug(_("Tenant-owned ports exist")) raise exc.SubnetInUse(subnet_id=id) if not allocated: mech_context = driver_context.SubnetContext( self, context, subnet) self.mechanism_manager.delete_subnet_precommit( mech_context) LOG.debug(_("Deleting subnet record")) record = self._get_subnet(context, id) session.delete(record) LOG.debug(_("Committing transaction")) break for a in allocated: if a.port_id: # calling update_port() for each allocation to remove the # IP from the port and call the MechanismDrivers data = { 'port': { 'fixed_ips': [{ 'subnet_id': ip.subnet_id, 'ip_address': ip.ip_address } for ip in a.ports.fixed_ips if ip.subnet_id != id] } } try: self.update_port(context, a.port_id, data) except Exception: with excutils.save_and_reraise_exception(): LOG.exception( _("Exception deleting fixed_ip from " "port %s"), a.port_id) session.delete(a) try: self.mechanism_manager.delete_subnet_postcommit(mech_context) except ml2_exc.MechanismDriverError: # TODO(apech) - One or more mechanism driver failed to # delete the subnet. Ideally we'd notify the caller of # the fact that an error occurred. LOG.error(_("mechanism_manager.delete_subnet_postcommit failed"))
def delete_subnet(self, context, id): # REVISIT(rkukura) The super(Ml2Plugin, self).delete_subnet() # function is not used because it deallocates the subnet's addresses # from ports in the DB without invoking the derived class's # update_port(), preventing mechanism drivers from being called. # This approach should be revisited when the API layer is reworked # during icehouse. LOG.debug("Deleting subnet %s", id) session = context.session attempt = 0 while True: attempt += 1 LOG.info(i18n._LI("Attempt %(attempt)s to delete subnet %(subnet)s"), { 'attempt': attempt, 'subnet': id }) if attempt > 100: raise InfiniteLoopError() with session.begin(subtransactions=True): record = self._get_subnet(context, id) subnet = self._make_subnet_dict(record, None, context=context) qry_allocated = (session.query(models_v2.IPAllocation).filter_by( subnet_id=id).join(models_v2.Port)) is_auto_addr_subnet = ipv6_utils.is_auto_address_subnet(subnet) # Remove network owned ports, and delete IP allocations # for IPv6 addresses which were automatically generated # via SLAAC if is_auto_addr_subnet: self._subnet_check_ip_allocations_internal_router_ports( context, id) else: qry_allocated = (qry_allocated.filter( models_v2.Port.device_owner.in_( db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS))) allocated = qry_allocated.all() # Delete all the IPAllocation that can be auto-deleted if allocated: for x in allocated: session.delete(x) LOG.debug("Ports to auto-deallocate: %s", allocated) # Check if there are more IP allocations, unless # is_auto_address_subnet is True. In that case the check is # unnecessary. This additional check not only would be wasteful # for this class of subnet, but is also error-prone since when # the isolation level is set to READ COMMITTED allocations made # concurrently will be returned by this query if not is_auto_addr_subnet: alloc = self._subnet_check_ip_allocations(context, id) if alloc: user_alloc = self._subnet_get_user_allocation(context, id) if user_alloc: LOG.info( i18n._LI("Found port (%(port_id)s, %(ip)s) " "having IP allocation on subnet " "%(subnet)s, cannot delete"), { 'ip': user_alloc.ip_address, 'port_id': user_alloc.port_id, 'subnet': id }) raise exc.SubnetInUse(subnet_id=id) else: # allocation found and it was DHCP port # that appeared after autodelete ports were # removed - need to restart whole operation raise os_db_exception.RetryRequest( exc.SubnetInUse(subnet_id=id)) db_base_plugin_v2._check_subnet_not_used(context, id) # If allocated is None, then all the IPAllocation were # correctly deleted during the previous pass. if not allocated: network = self.get_network(context, subnet['network_id']) mech_context = driver_context.SubnetContext( self, context, subnet, network) self.mechanism_manager.delete_subnet_precommit(mech_context) LOG.debug("Deleting subnet record") session.delete(record) # The super(Ml2Plugin, self).delete_subnet() is not called, # so need to manually call delete_subnet for pluggable ipam self.ipam.delete_subnet(context, id) LOG.debug("Committing transaction") break for a in allocated: if a.port: # calling update_port() for each allocation to remove the # IP from the port and call the MechanismDrivers data = { attributes.PORT: { 'fixed_ips': [{ 'subnet_id': ip.subnet_id, 'ip_address': ip.ip_address } for ip in a.port.fixed_ips if ip.subnet_id != id] } } try: self.update_port(context, a.port_id, data) except exc.PortNotFound: LOG.debug("Port %s deleted concurrently", a.port_id) except Exception: with excutils.save_and_reraise_exception(): LOG.exception( i18n._LE("Exception deleting fixed_ip " "from port %s"), a.port_id) try: self.mechanism_manager.delete_subnet_postcommit(mech_context) except ml2_exc.MechanismDriverError: # TODO(apech) - One or more mechanism driver failed to # delete the subnet. Ideally we'd notify the caller of # the fact that an error occurred. LOG.error( i18n._LE("mechanism_manager.delete_subnet_postcommit failed"))
def check_subnet_in_use(self, context, subnet_id): query = context.session.query(Pool).filter_by(subnet_id=subnet_id) if query.count(): pool_id = query.one().id raise n_exc.SubnetInUse( reason=_LE("Subnet is used by loadbalancer pool %s") % pool_id)