Пример #1
0
 def backend_allocate(self, address_request):
     try:
         # allocate a specific IP
         if isinstance(address_request, ipam_req.SpecificAddressRequest):
             # This handles both specific and automatic address requests
             ip_address = str(address_request.address)
             # If this is the subnet gateway IP - no need to allocate it
             subnet = self.get_details()
             if str(subnet.gateway_ip) == ip_address:
                 LOG.info("Skip allocation of gateway-ip for pool %s",
                          self._nsx_pool_id)
                 return ip_address
         else:
             # Allocate any free IP
             ip_address = None
         response = self.nsxlib_ipam.allocate(self._nsx_pool_id,
                                              ip_addr=ip_address)
         ip_address = response['allocation_id']
     except nsx_lib_exc.ManagerError as e:
         LOG.error(
             "NSX IPAM failed to allocate ip %(ip)s of subnet "
             "%(id)s: %(e)s; code %(code)s", {
                 'e': e,
                 'ip': ip_address,
                 'id': self._subnet_id,
                 'code': e.error_code
             })
         if e.error_code == error.ERR_CODE_IPAM_POOL_EXHAUSTED:
             # No more IP addresses available on the pool
             raise ipam_exc.IpAddressGenerationFailure(
                 subnet_id=self._subnet_id)
         if e.error_code == error.ERR_CODE_IPAM_SPECIFIC_IP:
             # The NSX backend  does not support allocation of specific IPs
             # prior to version 2.0.
             msg = (_("NSX-V3 IPAM driver does not support allocation of a "
                      "specific ip %s for port") % ip_address)
             raise NotImplementedError(msg)
         if e.error_code == error.ERR_CODE_IPAM_IP_ALLOCATED:
             # This IP is already in use
             raise ipam_exc.IpAddressAlreadyAllocated(
                 ip=ip_address, subnet_id=self._subnet_id)
         if e.error_code == error.ERR_CODE_OBJECT_NOT_FOUND:
             msg = (_("NSX-V3 IPAM failed to allocate: pool %s was not "
                      "found") % self._nsx_pool_id)
             raise ipam_exc.IpamValueInvalid(message=msg)
         else:
             # another backend error
             raise ipam_exc.IPAllocationFailed()
     except Exception as e:
         LOG.error(
             "NSX IPAM failed to allocate ip %(ip)s of subnet "
             "%(id)s: %(e)s", {
                 'e': e,
                 'ip': ip_address,
                 'id': self._subnet_id
             })
         # handle unexpected failures
         raise ipam_exc.IPAllocationFailed()
     return ip_address
Пример #2
0
 def backend_allocate(self, address_request):
     try:
         # allocate a specific IP
         if isinstance(address_request, ipam_req.SpecificAddressRequest):
             # This handles both specific and automatic address requests
             ip_address = str(address_request.address)
             self._vcns.allocate_ipam_ip_from_pool(self._nsx_pool_id,
                                                   ip_addr=ip_address)
         else:
             # Allocate any free IP
             response = self._vcns.allocate_ipam_ip_from_pool(
                 self._nsx_pool_id)[1]
             # get the ip from the response
             root = et.fromstring(response)
             ip_address = root.find('ipAddress').text
     except vc_exc.VcnsApiException as e:
         # handle backend failures
         error_code = self._get_vcns_error_code(e)
         if error_code == constants.NSX_ERROR_IPAM_ALLOCATE_IP_USED:
             # This IP is already in use
             raise ipam_exc.IpAddressAlreadyAllocated(
                 ip=ip_address, subnet_id=self._subnet_id)
         if error_code == constants.NSX_ERROR_IPAM_ALLOCATE_ALL_USED:
             # No more IP addresses available on the pool
             raise ipam_exc.IpAddressGenerationFailure(
                 subnet_id=self._subnet_id)
         else:
             raise ipam_exc.IPAllocationFailed()
     return ip_address
Пример #3
0
    def delete_range(self, session, db_range):
        """Return count of deleted ranges

        :param session: database session
        :param db_range: IpamAvailabilityRange db object
        """
        try:
            return session.query(
                db_models.IpamAvailabilityRange).filter_by(
                allocation_pool_id=db_range.allocation_pool_id).filter_by(
                first_ip=db_range.first_ip).filter_by(
                last_ip=db_range.last_ip).delete()
        except orm_exc.ObjectDeletedError:
            raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed())
Пример #4
0
    def update_range(self, session, db_range, first_ip=None, last_ip=None):
        """Updates db_range to have new first_ip and last_ip.

        :param session: database session
        :param db_range: IpamAvailabilityRange db object
        :param first_ip: first ip address in range
        :param last_ip: last ip address in range
        :return: count of updated rows
        """
        opts = {}
        if first_ip:
            opts['first_ip'] = str(first_ip)
        if last_ip:
            opts['last_ip'] = str(last_ip)
        if not opts:
            raise ipam_exc.IpamAvailabilityRangeNoChanges()
        try:
            return session.query(
                db_models.IpamAvailabilityRange).filter_by(
                allocation_pool_id=db_range.allocation_pool_id).filter_by(
                first_ip=db_range.first_ip).filter_by(
                last_ip=db_range.last_ip).update(opts)
        except orm_exc.ObjectDeletedError:
            raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed())
Пример #5
0
    def _allocate_specific_ip(self,
                              session,
                              ip_address,
                              allocation_pool_id=None,
                              auto_generated=False):
        """Remove an IP address from subnet's availability ranges.

        This method is supposed to be called from within a database
        transaction, otherwise atomicity and integrity might not be
        enforced and the operation might result in incosistent availability
        ranges for the subnet.

        :param session: database session
        :param ip_address: ip address to mark as allocated
        :param allocation_pool_id: identifier of the allocation pool from
             which the ip address has been extracted. If not specified this
             routine will scan all allocation pools.
        :param auto_generated: indicates whether ip was auto generated
        :returns: list of IP ranges as instances of IPAvailabilityRange
        """
        # Return immediately for EUI-64 addresses. For this
        # class of subnets availability ranges do not apply
        if ipv6_utils.is_eui64_address(ip_address):
            return

        LOG.debug(
            "Removing %(ip_address)s from availability ranges for "
            "subnet id:%(subnet_id)s", {
                'ip_address': ip_address,
                'subnet_id': self.subnet_manager.neutron_id
            })
        # Netaddr's IPRange and IPSet objects work very well even with very
        # large subnets, including IPv6 ones.
        final_ranges = []
        ip_in_pools = False
        if allocation_pool_id:
            av_ranges = self.subnet_manager.list_ranges_by_allocation_pool(
                session, allocation_pool_id)
        else:
            av_ranges = self.subnet_manager.list_ranges_by_subnet_id(session)
        for db_range in av_ranges:
            initial_ip_set = netaddr.IPSet(
                netaddr.IPRange(db_range['first_ip'], db_range['last_ip']))
            final_ip_set = initial_ip_set - netaddr.IPSet([ip_address])
            if not final_ip_set:
                ip_in_pools = True
                # Range exhausted - bye bye
                if not self.subnet_manager.delete_range(session, db_range):
                    raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed())
                continue
            if initial_ip_set == final_ip_set:
                # IP address does not fall within the current range, move
                # to the next one
                final_ranges.append(db_range)
                continue
            ip_in_pools = True
            for new_range in final_ip_set.iter_ipranges():
                # store new range in database
                # use netaddr.IPAddress format() method which is equivalent
                # to str(...) but also enables us to use different
                # representation formats (if needed) for IPv6.
                first_ip = netaddr.IPAddress(new_range.first)
                last_ip = netaddr.IPAddress(new_range.last)
                if (db_range['first_ip'] == first_ip.format()
                        or db_range['last_ip'] == last_ip.format()):
                    rows = self.subnet_manager.update_range(session,
                                                            db_range,
                                                            first_ip=first_ip,
                                                            last_ip=last_ip)
                    if not rows:
                        raise db_exc.RetryRequest(
                            ipam_exc.IPAllocationFailed())
                    LOG.debug("Adjusted availability range for pool %s",
                              db_range['allocation_pool_id'])
                    final_ranges.append(db_range)
                else:
                    new_ip_range = self.subnet_manager.create_range(
                        session, db_range['allocation_pool_id'],
                        first_ip.format(), last_ip.format())
                    LOG.debug("Created availability range for pool %s",
                              new_ip_range['allocation_pool_id'])
                    final_ranges.append(new_ip_range)

        # If ip is autogenerated it should be present in allocation pools,
        # so retry if it is not there
        if auto_generated and not ip_in_pools:
            raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed())
        # Most callers might ignore this return value, which is however
        # useful for testing purposes
        LOG.debug(
            "Availability ranges for subnet id %(subnet_id)s "
            "modified: %(new_ranges)s", {
                'subnet_id':
                self.subnet_manager.neutron_id,
                'new_ranges':
                ", ".join([
                    "[%s; %s]" % (r['first_ip'], r['last_ip'])
                    for r in final_ranges
                ])
            })
        return final_ranges