Exemplo n.º 1
0
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)
Exemplo n.º 2
0
    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"))
Exemplo n.º 3
0
 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)
Exemplo n.º 4
0
    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)
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
def _delete_subnet(context, subnet):
    if subnet.allocated_ips:
        raise exceptions.SubnetInUse(subnet_id=subnet["id"])
    db_api.subnet_delete(context, subnet)
Exemplo n.º 7
0
    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"))
Exemplo n.º 8
0
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"))
Exemplo n.º 9
0
 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)