def select_subnet(self, context, net_id, ip_address, segment_id, subnet_ids=None, **filters): LOG.info("Selecting subnet(s) - (Step 2 of 3) [{0}]".format( utils.pretty_kwargs(network_id=net_id, ip_address=ip_address, segment_id=segment_id, subnet_ids=subnet_ids, ip_version=filters.get("ip_version")))) # TODO(mdietz): Invert the iterator and the session, should only be # one subnet per attempt. We should also only be fetching # the subnet and usage when we need to. Otherwise # we're locking every subnet for a segment, and once # we stop locking, we're looking at stale data. with context.session.begin(): for subnet, ips_in_subnet in self._select_subnet( context, net_id, ip_address, segment_id, subnet_ids, **filters): if subnet is None: continue ipnet = netaddr.IPNetwork(subnet["cidr"]) LOG.info("Trying subnet ID: {0} - CIDR: {1}".format( subnet["id"], subnet["_cidr"])) if not self._ip_in_subnet(subnet, subnet_ids, ipnet, ip_address): continue if self._should_mark_subnet_full(context, subnet, ipnet, ip_address, ips_in_subnet): LOG.info("Marking subnet {0} as full".format(subnet["id"])) updated = db_api.subnet_update_set_full(context, subnet) # Ensure the session is aware of the changes to the subnet if updated: context.session.refresh(subnet) continue if not ip_address and subnet["ip_version"] == 4: auto_inc = db_api.subnet_update_next_auto_assign_ip updated = auto_inc(context, subnet) if updated: context.session.refresh(subnet) else: # This means the subnet was marked full # while we were checking out policies. # Fall out and go back to the outer retry # loop. return LOG.info("Subnet {0} - {1} {2} looks viable, " "returning".format(subnet["id"], subnet["_cidr"], subnet["next_auto_assign_ip"])) return subnet
def select_subnet(self, context, net_id, ip_address, segment_id, subnet_ids=None, **filters): LOG.info("Selecting subnet(s) - (Step 2 of 3) [{0}]".format( utils.pretty_kwargs(network_id=net_id, ip_address=ip_address, segment_id=segment_id, subnet_ids=subnet_ids, ip_version=filters.get("ip_version")))) # TODO(mdietz): Invert the iterator and the session, should only be # one subnet per attempt. We should also only be fetching # the subnet and usage when we need to. Otherwise # we're locking every subnet for a segment, and once # we stop locking, we're looking at stale data. with context.session.begin(): for subnet, ips_in_subnet in self._select_subnet(context, net_id, ip_address, segment_id, subnet_ids, **filters): if subnet is None: continue ipnet = netaddr.IPNetwork(subnet["cidr"]) LOG.info("Trying subnet ID: {0} - CIDR: {1}".format( subnet["id"], subnet["_cidr"])) if not self._ip_in_subnet(subnet, subnet_ids, ipnet, ip_address): continue if self._should_mark_subnet_full(context, subnet, ipnet, ip_address, ips_in_subnet): LOG.info("Marking subnet {0} as full".format(subnet["id"])) updated = db_api.subnet_update_set_full(context, subnet) # Ensure the session is aware of the changes to the subnet if updated: context.session.refresh(subnet) continue if not ip_address and subnet["ip_version"] == 4: auto_inc = db_api.subnet_update_next_auto_assign_ip updated = auto_inc(context, subnet) if updated: context.session.refresh(subnet) else: # This means the subnet was marked full # while we were checking out policies. # Fall out and go back to the outer retry # loop. return LOG.info("Subnet {0} - {1} {2} looks viable, " "returning".format(subnet["id"], subnet["_cidr"], subnet["next_auto_assign_ip"])) return subnet
def test_subnet_set_full(self): cidr4 = "0.0.0.0/30" # 2 bits net4 = netaddr.IPNetwork(cidr4) with self._fixtures([self._create_models(cidr4, 4, net4[0])]) as net: subnet = db_api.subnet_find(self.context, network_id=net['id'], scope=db_api.ALL)[0] with self.context.session.begin(): updated = db_api.subnet_update_set_full(self.context, subnet) self.context.session.refresh(subnet) self.assertTrue(updated) self.assertEqual(subnet["next_auto_assign_ip"], -1)
def test_subnet_set_full(self): cidr4 = "0.0.0.0/30" # 2 bits net4 = netaddr.IPNetwork(cidr4) with self._fixtures([ self._create_models(cidr4, 4, net4[0]) ]) as net: subnet = db_api.subnet_find(self.context, network_id=net['id'], scope=db_api.ALL)[0] with self.context.session.begin(): updated = db_api.subnet_update_set_full(self.context, subnet) self.context.session.refresh(subnet) self.assertTrue(updated) self.assertEqual(subnet["next_auto_assign_ip"], -1)
def select_subnet(self, context, net_id, ip_address, segment_id, subnet_ids=None, **filters): LOG.info("Selecting subnet(s) - (Step 2 of 3) [{0}]".format( utils.pretty_kwargs(network_id=net_id, ip_address=ip_address, segment_id=segment_id, subnet_ids=subnet_ids, ip_version=filters.get("ip_version")))) with context.session.begin(): subnets = db_api.subnet_find_ordered_by_most_full( context, net_id, segment_id=segment_id, scope=db_api.ALL, subnet_id=subnet_ids, **filters) if not subnets: LOG.info("No subnets found given the search criteria!") for subnet, ips_in_subnet in subnets: ipnet = netaddr.IPNetwork(subnet["cidr"]) LOG.info("Trying subnet ID: {0} - CIDR: {1}".format( subnet["id"], subnet["_cidr"])) if ip_address: requested_ip = netaddr.IPAddress(ip_address) if ipnet.version == 4 and requested_ip.version != 4: requested_ip = requested_ip.ipv4() if requested_ip not in ipnet: if subnet_ids is not None: LOG.info("Requested IP {0} not in subnet {1}, " "retrying".format(str(requested_ip), str(ipnet))) raise q_exc.IPAddressNotInSubnet( ip_addr=ip_address, subnet_id=subnet["id"]) continue ip_policy = None if not ip_address: # Policies don't prevent explicit assignment, so we only # need to check if we're allocating a new IP ip_policy = subnet.get("ip_policy") policy_size = ip_policy["size"] if ip_policy else 0 if ipnet.size > (ips_in_subnet + policy_size - 1): if not ip_address and subnet["ip_version"] == 4: ip = subnet["next_auto_assign_ip"] # NOTE(mdietz): When atomically updated, this probably # doesn't need the lower bounds check but # I'm not comfortable removing it yet. updated = 0 if ip < subnet["first_ip"] or ip > subnet["last_ip"]: LOG.info("Marking subnet {0} as full".format( subnet["id"])) updated = db_api.subnet_update_set_full(context, subnet) else: auto_inc = db_api.subnet_update_next_auto_assign_ip updated = auto_inc(context, subnet) if updated: context.session.refresh(subnet) else: # This means the subnet was marked full # while we were checking out policies. # Fall out and go back to the outer retry # loop. return LOG.info("Subnet {0} - {1} {2} looks viable, " "returning".format(subnet["id"], subnet["_cidr"], subnet["next_auto_assign_ip"])) return subnet else: LOG.info("Marking subnet {0} as full".format(subnet["id"])) db_api.subnet_update_set_full(context, subnet)