def assign_ip(self, address, handle_id, attributes, hostname=None): """ Assign the given address. Throws AlreadyAssignedError if the address is taken. :param address: IPAddress to assign. :param handle_id: allocation handle ID for this request. You can query this key using get_assignments_by_handle() or release all addresses with this handle_id using release_by_handle(). :param attributes: Contents of this dict will be stored with the assignment and can be queried using get_assignment_attributes(). Must be JSON serializable. :param hostname: (optional) the hostname to use for affinity if the block containing the IP address has no host affinity. Defaults to the hostname returned by get_hostname(). :return: None. """ assert isinstance(handle_id, str) or handle_id is None assert isinstance(address, IPAddress) if not hostname: hostname = get_hostname() block_cidr = get_block_cidr_for_address(address) for _ in xrange(RETRIES): try: block = self._read_block(block_cidr) except KeyError: _log.debug("Block %s doesn't exist.", block_cidr) pools = self.get_ip_pools(address.version, ipam=True) if any([address in pool for pool in pools]): _log.debug("Create and claim block %s.", block_cidr) try: self._claim_block_affinity(hostname, block_cidr) except HostAffinityClaimedError: _log.debug("Someone else claimed block %s before us.", block_cidr) continue # Block exists now, retry writing to it. _log.debug("Claimed block %s", block_cidr) continue else: raise ValueError("%s is not in any configured pool" % address) # Try to assign. Throws exception if already assigned -- let it. block.assign(address, handle_id, attributes) # If using a handle, increment by one IP if handle_id is not None: self._increment_handle(handle_id, block_cidr, 1) # Try to commit. try: self._compare_and_swap_block(block) return # Success! except CASError: _log.debug("CAS failed on block %s", block_cidr) if handle_id is not None: self._decrement_handle(handle_id, block_cidr, 1) raise RuntimeError("Hit max retries.")
def get_assignment_attributes(self, address): """ Return the attributes of a given address. :param address: IPAddress to query. :return: The attributes for the address as passed to auto_assign() or assign(). """ assert isinstance(address, IPAddress) block_cidr = get_block_cidr_for_address(address) try: block = self._read_block(block_cidr) except KeyError: _log.warning("Couldn't read block %s for requested address %s", block_cidr, address) raise AddressNotAssignedError("%s is not assigned." % address) else: _, attributes = block.get_attributes_for_ip(address) return attributes
def release_ips(self, addresses): """ Release the given addresses. :param addresses: Set of IPAddresses to release (ok to mix IPv4 and IPv6). :return: Set of addresses that were already unallocated. """ assert isinstance(addresses, (set, frozenset)) _log.info("Releasing addresses %s", [str(addr) for addr in addresses]) unallocated = set() # sort the addresses into blocks addrs_by_block = {} for address in addresses: block_cidr = get_block_cidr_for_address(address) addrs = addrs_by_block.setdefault(block_cidr, set()) addrs.add(address) # loop through blocks, CAS releasing. for block_cidr, addresses in addrs_by_block.iteritems(): unalloc_block = self._release_block(block_cidr, addresses) unallocated = unallocated.union(unalloc_block) return unallocated
def test_get_block_cidr(self, address, cidr): """ Test get_block_cidr_for_address """ block_id = get_block_cidr_for_address(address) assert_equal(block_id, cidr)
def assign_ip(self, address, handle_id, attributes, host=None): """ Assign the given address. Throws AlreadyAssignedError if the address is taken. :param address: IPAddress to assign. :param handle_id: allocation handle ID for this request. You can query this key using get_assignments_by_handle() or release all addresses with this handle_id using release_by_handle(). :param attributes: Contents of this dict will be stored with the assignment and can be queried using get_assignment_attributes(). Must be JSON serializable. :param host: (optional) The host ID to use for affinity in assigning IP addresses. Defaults to the hostname returned by get_hostname(). :return: None. """ assert isinstance(handle_id, str) or handle_id is None assert isinstance(address, IPAddress) if not host: host = get_hostname() block_cidr = get_block_cidr_for_address(address) for _ in xrange(RETRIES): try: block = self._read_block(block_cidr) except KeyError: _log.debug("Block %s doesn't exist.", block_cidr) pools = self.get_ip_pools(address.version, ipam=True) if any([address in pool for pool in pools]): _log.debug("Create and claim block %s.", block_cidr) try: self._claim_block_affinity(host, block_cidr) except HostAffinityClaimedError: _log.debug("Someone else claimed block %s before us.", block_cidr) continue # Block exists now, retry writing to it. _log.debug("Claimed block %s", block_cidr) continue else: raise PoolNotFound("%s is not in any configured pool" % address) # Try to assign. Throws exception if already assigned -- let it. block.assign(address, handle_id, attributes) # If using a handle, increment by one IP if handle_id is not None: self._increment_handle(handle_id, block_cidr, 1) # Try to commit. try: self._compare_and_swap_block(block) return # Success! except CASError: _log.debug("CAS failed on block %s", block_cidr) if handle_id is not None: self._decrement_handle(handle_id, block_cidr, 1) raise RuntimeError("Hit max retries.")