def _stubs(self, network, subnet, address, lock=False): self.ipam = quark.ipam.QuarkIpamANY() with self.context.session.begin(): next_ip = subnet.pop("next_auto_assign_ip", 0) net_mod = db_api.network_create(self.context, **network) subnet["network"] = net_mod sub_mod = db_api.subnet_create(self.context, **subnet) address["network_id"] = net_mod["id"] address["subnet_id"] = sub_mod["id"] ip = db_api.ip_address_create(self.context, **address) address.pop("address") ip = db_api.ip_address_update(self.context, ip, **address) # NOTE(asadoughi): update after cidr constructor has been invoked db_api.subnet_update(self.context, sub_mod, next_auto_assign_ip=next_ip) if lock: db_api.lock_holder_create(self.context, ip, name="testlock", type="ip_address") yield net_mod
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None): version = version or [4, 6] elevated = context.elevated() # We never want to take the chance of an infinite loop here. Instead, # we'll clean up multiple bad IPs if we find them (assuming something # is really wrong) for times in xrange(3): with context.session.begin(subtransactions=True): address = db_api.ip_address_find( elevated, network_id=net_id, reuse_after=reuse_after, deallocated=True, scope=db_api.ONE, ip_address=ip_address, lock_mode=True, version=version, order_by="address") if address: #NOTE(mdietz): We should always be in the CIDR but we've # also said that before :-/ if address.get("subnet"): cidr = netaddr.IPNetwork(address["subnet"]["cidr"]) addr = netaddr.IPAddress(int(address["address"]), version=int(cidr.version)) if addr in cidr: updated_address = db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None, allocated_at=timeutils.utcnow()) return [updated_address] else: # Make sure we never find it again context.session.delete(address) continue break return []
def allocate_ip_address(self, context, net_id, port_id, reuse_after, version=None, ip_address=None): elevated = context.elevated() if ip_address: ip_address = netaddr.IPAddress(ip_address) address = db_api.ip_address_find( elevated, network_id=net_id, reuse_after=reuse_after, deallocated=True, scope=db_api.ONE, ip_address=ip_address) if address: return db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None) subnet = self._choose_available_subnet( elevated, net_id, ip_address=ip_address, version=version) # Creating this IP for the first time next_ip = None if ip_address: next_ip = ip_address else: address = True while address: next_ip_int = int(subnet["next_auto_assign_ip"]) next_ip = netaddr.IPAddress(next_ip_int) if subnet["ip_version"] == 4: next_ip = next_ip.ipv4() subnet["next_auto_assign_ip"] = next_ip_int + 1 address = db_api.ip_address_find( elevated, network_id=net_id, ip_address=next_ip, tenant_id=elevated.tenant_id, scope=db_api.ONE) # TODO(mdietz): this is a hack until we have IP policies ip_int = int(next_ip) first_ip = netaddr.IPAddress(int(subnet["first_ip"])) last_ip = netaddr.IPAddress(int(subnet["last_ip"])) if subnet["ip_version"] == 4: first_ip = first_ip.ipv4() last_ip = last_ip.ipv4() first_ip = int(first_ip) last_ip = int(last_ip) diff = ip_int - first_ip if diff < 2: next_ip = netaddr.IPAddress(ip_int + (2 - diff)) if ip_int == last_ip: raise exceptions.IpAddressGenerationFailure(net_id=net_id) if next_ip not in netaddr.IPNetwork(subnet["cidr"]): raise exceptions.IpAddressGenerationFailure(net_id=net_id) address = db_api.ip_address_create( elevated, address=next_ip, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id) return address
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None): version = version or [4, 6] elevated = context.elevated() # We never want to take the chance of an infinite loop here. Instead, # we'll clean up multiple bad IPs if we find them (assuming something # is really wrong) #TODO(mdietz & mpath): Perhaps remove, select for update might quash for times in xrange(3): with context.session.begin(subtransactions=True): sub_ids = [] if subnets: sub_ids = subnets else: if segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "scope": db_api.ONE, "ip_address": ip_address, "lock_mode": True, "version": version, "order_by": "address"} if sub_ids: ip_kwargs["subnet_id"] = sub_ids address = db_api.ip_address_find(elevated, **ip_kwargs) if address: #NOTE(mdietz): We should always be in the CIDR but we've # also said that before :-/ if address.get("subnet"): cidr = netaddr.IPNetwork(address["subnet"]["cidr"]) addr = netaddr.IPAddress(int(address["address"]), version=int(cidr.version)) if addr in cidr: updated_address = db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None, allocated_at=timeutils.utcnow()) return [updated_address] else: # Make sure we never find it again context.session.delete(address) continue break return []
def _stubs(self, network, subnet, address): self.ipam = quark.ipam.QuarkIpamANY() with self.context.session.begin(): next_ip = subnet.pop("next_auto_assign_ip", 0) net_mod = db_api.network_create(self.context, **network) subnet["network"] = net_mod sub_mod = db_api.subnet_create(self.context, **subnet) address["network_id"] = net_mod["id"] address["subnet_id"] = sub_mod["id"] ip = db_api.ip_address_create(self.context, **address) address.pop("address") db_api.ip_address_update(self.context, ip, **address) # NOTE(asadoughi): update after cidr constructor has been invoked db_api.subnet_update(self.context, sub_mod, next_auto_assign_ip=next_ip) yield net_mod
def _allocate_from_v6_subnet(self, context, net_id, subnet, port_id, ip_address=None, **kwargs): """This attempts to allocate v6 addresses as per RFC2462 and RFC3041. To accomodate this, we effectively treat all v6 assignment as a first time allocation utilizing the MAC address of the VIF. Because we recycle MACs, we will eventually attempt to recreate a previously generated v6 address. Instead of failing, we've opted to handle reallocating that address in this method. This should provide a performance boost over attempting to check each and every subnet in the existing reallocate logic, as we'd have to iterate over each and every subnet returned """ if not (ip_address is None and "mac_address" in kwargs and kwargs["mac_address"]): return self._allocate_from_subnet(context, net_id, subnet, ip_address, **kwargs) else: ip_policy_cidrs = models.IPPolicy.get_ip_policy_cidrs(subnet) for tries, ip_address in enumerate( generate_v6(kwargs["mac_address"]["address"], port_id, subnet["cidr"])): if tries > CONF.QUARK.v6_allocation_attempts - 1: raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_address = netaddr.IPAddress(ip_address) # NOTE(mdietz): treating the IPSet as a boolean caused netaddr # to attempt to enumerate the entire set! if (ip_policy_cidrs is not None and ip_address in ip_policy_cidrs): continue with context.session.begin(): address = db_api.ip_address_find( context, network_id=net_id, ip_address=ip_address, used_by_tenant_id=context.tenant_id, scope=db_api.ONE, lock_mode=True) if address: return db_api.ip_address_update( context, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow()) with context.session.begin(): return db_api.ip_address_create( context, address=ip_address, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id)
def allocate_ip_address(self, context, net_id, port_id, reuse_after, version=None, ip_address=None): elevated = context.elevated() if ip_address: ip_address = netaddr.IPAddress(ip_address) address = db_api.ip_address_find( elevated, network_id=net_id, reuse_after=reuse_after, deallocated=True, scope=db_api.ONE, ip_address=ip_address) if address: return db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None) subnet = self._choose_available_subnet( elevated, net_id, ip_address=ip_address, version=version) ip_policy_rules = self.get_ip_policy_rule_set(subnet) # Creating this IP for the first time next_ip = None if ip_address: next_ip = ip_address address = db_api.ip_address_find( elevated, network_id=net_id, ip_address=next_ip, tenant_id=elevated.tenant_id, scope=db_api.ONE) if address: raise exceptions.IpAddressGenerationFailure(net_id=net_id) else: address = True while address: next_ip_int = int(subnet["next_auto_assign_ip"]) next_ip = netaddr.IPAddress(next_ip_int) if subnet["ip_version"] == 4: next_ip = next_ip.ipv4() subnet["next_auto_assign_ip"] = next_ip_int + 1 if ip_policy_rules and next_ip in ip_policy_rules: continue address = db_api.ip_address_find( elevated, network_id=net_id, ip_address=next_ip, tenant_id=elevated.tenant_id, scope=db_api.ONE) address = db_api.ip_address_create( elevated, address=next_ip, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id) address["deallocated"] = 0 return address
def allocate_ip_address(self, context, net_id, port_id, reuse_after, version=None, ip_address=None): elevated = context.elevated() if ip_address: ip_address = netaddr.IPAddress(ip_address) address = db_api.ip_address_find(elevated, network_id=net_id, reuse_after=reuse_after, deallocated=True, scope=db_api.ONE, ip_address=ip_address) if address: return db_api.ip_address_update(elevated, address, deallocated=False, deallocated_at=None) subnet = self._choose_available_subnet(elevated, net_id, ip_address=ip_address, version=version) ip_policy_rules = self.get_ip_policy_rule_set(subnet) # Creating this IP for the first time next_ip = None if ip_address: next_ip = ip_address address = db_api.ip_address_find(elevated, network_id=net_id, ip_address=next_ip, tenant_id=elevated.tenant_id, scope=db_api.ONE) if address: raise exceptions.IpAddressGenerationFailure(net_id=net_id) else: address = True while address: next_ip_int = int(subnet["next_auto_assign_ip"]) next_ip = netaddr.IPAddress(next_ip_int) if subnet["ip_version"] == 4: next_ip = next_ip.ipv4() subnet["next_auto_assign_ip"] = next_ip_int + 1 if ip_policy_rules and next_ip in ip_policy_rules: continue address = db_api.ip_address_find(elevated, network_id=net_id, ip_address=next_ip, tenant_id=elevated.tenant_id, scope=db_api.ONE) address = db_api.ip_address_create(elevated, address=next_ip, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id) address["deallocated"] = 0 return address
def _allocate_from_v6_subnet(self, context, net_id, subnet, port_id, reuse_after, ip_address=None, **kwargs): """This attempts to allocate v6 addresses as per RFC2462 and RFC3041. To accomodate this, we effectively treat all v6 assignment as a first time allocation utilizing the MAC address of the VIF. Because we recycle MACs, we will eventually attempt to recreate a previously generated v6 address. Instead of failing, we've opted to handle reallocating that address in this method. This should provide a performance boost over attempting to check each and every subnet in the existing reallocate logic, as we'd have to iterate over each and every subnet returned """ if not (ip_address is None and "mac_address" in kwargs and kwargs["mac_address"]): return self._allocate_from_subnet(context, net_id, subnet, reuse_after, ip_address, **kwargs) else: ip_policy_cidrs = models.IPPolicy.get_ip_policy_cidrs(subnet) for tries, ip_address in enumerate( generate_v6(kwargs["mac_address"]["address"], port_id, subnet["cidr"])): if tries > CONF.QUARK.v6_allocation_attempts - 1: raise exceptions.IpAddressGenerationFailure(net_id=net_id) ip_address = netaddr.IPAddress(ip_address) # NOTE(mdietz): treating the IPSet as a boolean caused netaddr # to attempt to enumerate the entire set! if (ip_policy_cidrs is not None and ip_address in ip_policy_cidrs): continue # TODO(mdietz): replace this with a compare-and-swap loop with context.session.begin(): address = db_api.ip_address_find(context, network_id=net_id, ip_address=ip_address, scope=db_api.ONE, reuse_after=reuse_after, deallocated=True, subnet_id=subnet["id"], lock_mode=True) if address: return db_api.ip_address_update( context, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow()) # This triggers when the IP is allocated to another tenant, # either because we missed it due to our filters above, or # in an extremely unlikely race between the find and here. try: with context.session.begin(): return db_api.ip_address_create( context, address=ip_address, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id) except db_exception.DBDuplicateEntry: LOG.debug( "Duplicate entry found when inserting subnet_id" " %s ip_address %s", subnet["id"], ip_address)
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() if version == 6 and "mac_address" in kwargs and kwargs["mac_address"]: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. return [] sub_ids = [] if subnets: sub_ids = subnets else: if segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: raise exceptions.IpAddressGenerationFailure(net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "scope": db_api.ONE, "ip_address": ip_address, "lock_mode": True, "version": version, "order_by": "address", "do_not_use": False } if sub_ids: ip_kwargs["subnet_id"] = sub_ids # We never want to take the chance of an infinite loop here. Instead, # we'll clean up multiple bad IPs if we find them (assuming something # is really wrong) for retry in xrange(cfg.CONF.QUARK.ip_address_retry_max): get_policy = models.IPPolicy.get_ip_policy_cidrs try: with context.session.begin(): # NOTE(mdietz): Before I removed the lazy=joined, this # raised with an unknown column "address" # error. address = db_api.ip_address_find(elevated, **ip_kwargs) if address: # NOTE(mdietz): We should always be in the CIDR but we # also said that before :-/ subnet = address.get('subnet') if subnet: policy = get_policy(subnet) cidr = netaddr.IPNetwork(address["subnet"]["cidr"]) addr = netaddr.IPAddress(int(address["address"])) if address["subnet"]["ip_version"] == 4: addr = addr.ipv4() else: addr = addr.ipv6() if policy is not None and addr in policy: context.session.delete(address) continue if addr in cidr: updated_address = db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow()) return [updated_address] else: # Make sure we never find it again context.session.delete(address) else: break except Exception: LOG.exception("Error in reallocate ip...") return []
def _allocate_from_v6_subnet(self, context, net_id, subnet, port_id, reuse_after, ip_address=None, **kwargs): """This attempts to allocate v6 addresses as per RFC2462 and RFC3041. To accomodate this, we effectively treat all v6 assignment as a first time allocation utilizing the MAC address of the VIF. Because we recycle MACs, we will eventually attempt to recreate a previously generated v6 address. Instead of failing, we've opted to handle reallocating that address in this method. This should provide a performance boost over attempting to check each and every subnet in the existing reallocate logic, as we'd have to iterate over each and every subnet returned """ LOG.info("Attempting to allocate a v6 address - [{0}]".format( utils.pretty_kwargs(network_id=net_id, subnet=subnet, port_id=port_id, ip_address=ip_address))) if ip_address: LOG.info("IP %s explicitly requested, deferring to standard " "allocation" % ip_address) return self._allocate_from_subnet(context, net_id=net_id, subnet=subnet, port_id=port_id, reuse_after=reuse_after, ip_address=ip_address, **kwargs) else: mac = kwargs.get("mac_address") if mac: mac = kwargs["mac_address"].get("address") ip_policy_cidrs = models.IPPolicy.get_ip_policy_cidrs(subnet) for tries, ip_address in enumerate( generate_v6(mac, port_id, subnet["cidr"])): LOG.info("Attempt {0} of {1}".format( tries + 1, CONF.QUARK.v6_allocation_attempts)) if tries > CONF.QUARK.v6_allocation_attempts - 1: LOG.info("Exceeded v6 allocation attempts, bailing") raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_address = netaddr.IPAddress(ip_address).ipv6() LOG.info("Generated a new v6 address {0}".format( str(ip_address))) # NOTE(mdietz): treating the IPSet as a boolean caused netaddr # to attempt to enumerate the entire set! if (ip_policy_cidrs is not None and ip_address in ip_policy_cidrs): LOG.info("Address {0} excluded by policy".format( str(ip_address))) continue # TODO(mdietz): replace this with a compare-and-swap loop with context.session.begin(): address = db_api.ip_address_find( context, network_id=net_id, ip_address=ip_address, scope=db_api.ONE, reuse_after=reuse_after, deallocated=True, subnet_id=subnet["id"], lock_mode=True) if address: LOG.info("Address {0} exists, claiming".format( str(ip_address))) return db_api.ip_address_update( context, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow(), address_type=kwargs.get('address_type', ip_types.FIXED)) # This triggers when the IP is allocated to another tenant, # either because we missed it due to our filters above, or # in an extremely unlikely race between the find and here. try: with context.session.begin(): return db_api.ip_address_create( context, address=ip_address, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id, address_type=kwargs.get('address_type', ip_types.FIXED)) except db_exception.DBDuplicateEntry: LOG.info("{0} exists but was already " "allocated".format(str(ip_address))) LOG.debug("Duplicate entry found when inserting subnet_id" " %s ip_address %s", subnet["id"], ip_address)
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() LOG.info("Attempting to reallocate an IP (step 1 of 3) - [{0}]".format( utils.pretty_kwargs(network_id=net_id, port_id=port_id, version=version, segment_id=segment_id, subnets=subnets))) if version == 6 and "mac_address" in kwargs and kwargs["mac_address"]: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. LOG.info("Identified as v6 case, deferring to IP create path") return [] sub_ids = [] if subnets: sub_ids = subnets else: if segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: LOG.info("No subnets matching segment_id {0} could be " "found".format(segment_id)) raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "scope": db_api.ONE, "ip_address": ip_address, "lock_mode": True, "version": version, "order_by": "address", "do_not_use": False} if sub_ids: ip_kwargs["subnet_id"] = sub_ids # We never want to take the chance of an infinite loop here. Instead, # we'll clean up multiple bad IPs if we find them (assuming something # is really wrong) for retry in xrange(CONF.QUARK.ip_address_retry_max): LOG.info("Attempt {0} of {1}".format( retry + 1, CONF.QUARK.ip_address_retry_max)) get_policy = models.IPPolicy.get_ip_policy_cidrs try: with context.session.begin(): # NOTE(mdietz): Before I removed the lazy=joined, this # raised with an unknown column "address" # error. address = db_api.ip_address_find(elevated, **ip_kwargs) if address: # NOTE(mdietz): We should always be in the CIDR but we # also said that before :-/ LOG.info("Potentially reallocatable IP found: " "{0}".format(address["address_readable"])) subnet = address.get('subnet') if subnet: policy = get_policy(subnet) cidr = netaddr.IPNetwork(address["subnet"]["cidr"]) addr = netaddr.IPAddress(int(address["address"])) if address["subnet"]["ip_version"] == 4: addr = addr.ipv4() else: addr = addr.ipv6() if policy is not None and addr in policy: LOG.info("Deleting Address {0} due to policy " "violation".format( address["address_readable"])) context.session.delete(address) continue if addr in cidr: LOG.info("Marking Address {0} as " "allocated".format( address["address_readable"])) updated_address = db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow(), port_id=port_id, address_type=kwargs.get('address_type', ip_types.FIXED)) return [updated_address] else: # Make sure we never find it again LOG.info("Address {0} isn't in the subnet " "it claims to be in".format( address["address_readable"])) context.session.delete(address) else: LOG.info("Couldn't find any reallocatable addresses " "given the criteria") break except Exception: LOG.exception("Error in reallocate ip...") return []
def _allocate_from_v6_subnet(self, context, net_id, subnet, port_id, reuse_after, ip_address=None, **kwargs): """This attempts to allocate v6 addresses as per RFC2462 and RFC3041. To accomodate this, we effectively treat all v6 assignment as a first time allocation utilizing the MAC address of the VIF. Because we recycle MACs, we will eventually attempt to recreate a previously generated v6 address. Instead of failing, we've opted to handle reallocating that address in this method. This should provide a performance boost over attempting to check each and every subnet in the existing reallocate logic, as we'd have to iterate over each and every subnet returned """ if (ip_address or "mac_address" not in kwargs or not kwargs["mac_address"]): return self._allocate_from_subnet(context, net_id=net_id, subnet=subnet, port_id=port_id, reuse_after=reuse_after, ip_address=ip_address, **kwargs) else: ip_policy_cidrs = models.IPPolicy.get_ip_policy_cidrs(subnet) for tries, ip_address in enumerate( generate_v6(kwargs["mac_address"]["address"], port_id, subnet["cidr"])): if tries > CONF.QUARK.v6_allocation_attempts - 1: raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_address = netaddr.IPAddress(ip_address) # NOTE(mdietz): treating the IPSet as a boolean caused netaddr # to attempt to enumerate the entire set! if (ip_policy_cidrs is not None and ip_address in ip_policy_cidrs): continue # TODO(mdietz): replace this with a compare-and-swap loop with context.session.begin(): address = db_api.ip_address_find( context, network_id=net_id, ip_address=ip_address, scope=db_api.ONE, reuse_after=reuse_after, deallocated=True, subnet_id=subnet["id"], lock_mode=True) if address: return db_api.ip_address_update( context, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow()) # This triggers when the IP is allocated to another tenant, # either because we missed it due to our filters above, or # in an extremely unlikely race between the find and here. try: with context.session.begin(): return db_api.ip_address_create( context, address=ip_address, subnet_id=subnet["id"], version=subnet["ip_version"], network_id=net_id) except db_exception.DBDuplicateEntry: LOG.debug("Duplicate entry found when inserting subnet_id" " %s ip_address %s", subnet["id"], ip_address)
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() if version == 6 and "mac_address" in kwargs and kwargs["mac_address"]: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. return [] # We never want to take the chance of an infinite loop here. Instead, # we'll clean up multiple bad IPs if we find them (assuming something # is really wrong) sub_ids = [] if subnets: sub_ids = subnets else: if segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "scope": db_api.ONE, "ip_address": ip_address, "lock_mode": True, "version": version, "order_by": "address", "do_not_use": False} if sub_ids: ip_kwargs["subnet_id"] = sub_ids for retry in xrange(cfg.CONF.QUARK.ip_address_retry_max): get_policy = models.IPPolicy.get_ip_policy_cidrs try: with context.session.begin(): # NOTE(mdietz): Before I removed the lazy=joined, this # raised with an unknown column "address" # error. address = db_api.ip_address_find(elevated, **ip_kwargs) if address: # NOTE(mdietz): We should always be in the CIDR but we # also said that before :-/ subnet = address.get('subnet') if subnet: policy = get_policy(subnet) cidr = netaddr.IPNetwork(address["subnet"]["cidr"]) addr = netaddr.IPAddress(int(address["address"])) if address["subnet"]["ip_version"] == 4: addr = addr.ipv4() else: addr = addr.ipv6() if policy is not None and addr in policy: context.session.delete(address) continue if addr in cidr: updated_address = db_api.ip_address_update( elevated, address, deallocated=False, deallocated_at=None, used_by_tenant_id=context.tenant_id, allocated_at=timeutils.utcnow()) return [updated_address] else: # Make sure we never find it again context.session.delete(address) else: break except Exception: LOG.exception("Error in reallocate ip...") return []