def test_ordering_subnets_find_allocc_when_counts_unequal_size_equal(self): models = [] cidrs = ["0.0.0.0/31", "1.1.1.0/31", "2.2.2.0/31"] for cidr in cidrs: last = netaddr.IPNetwork(cidr).last models.append(self._create_models(cidr, 4, last)) with self._fixtures(models) as net: self._create_ip_address("2.2.2.1", 4, "2.2.2.0/31", net["id"]) self._create_ip_address("2.2.2.2", 4, "2.2.2.0/31", net["id"]) self._create_ip_address("1.1.1.1", 4, "1.1.1.0/31", net["id"]) subnets = db_api.subnet_find_ordered_by_most_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(len(subnets), 3) self.assertEqual(subnets[0][0].cidr, "2.2.2.0/31") self.assertEqual(subnets[0][1], 2) self.assertEqual(subnets[1][0].cidr, "1.1.1.0/31") self.assertEqual(subnets[1][1], 1) self.assertEqual(subnets[2][0].cidr, "0.0.0.0/31") self.assertEqual(subnets[2][1], 0) subnets = db_api.subnet_find_ordered_by_least_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(len(subnets), 3) self.assertEqual(subnets[0][0].cidr, "0.0.0.0/31") self.assertEqual(subnets[0][1], 0) self.assertEqual(subnets[1][0].cidr, "1.1.1.0/31") self.assertEqual(subnets[1][1], 1) self.assertEqual(subnets[2][0].cidr, "2.2.2.0/31") self.assertEqual(subnets[2][1], 2)
def test_ordering_subnets_ip_version(self): """Order by ip_version primarily. Order by ip_version primarily, even when IPv4 is less full than IPv6 subnet. """ cidr4 = "0.0.0.0/30" # 2 bits last4 = netaddr.IPNetwork(cidr4).last cidr6 = "fffc::/127" # 1 bits last6 = netaddr.IPNetwork(cidr6).last with self._fixtures([ self._create_models(cidr4, 4, last4), self._create_models(cidr6, 6, last6) ]) as net: subnets = db_api.subnet_find_ordered_by_most_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(subnets[0][0].ip_version, 4) self.assertEqual(subnets[1][0].ip_version, 6) subnets = db_api.subnet_find_ordered_by_least_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(subnets[0][0].ip_version, 4) self.assertEqual(subnets[1][0].ip_version, 6)
def test_ordering_subnets_ip_version(self): """Order by ip_version primarily. Order by ip_version primarily, even when IPv4 is less full than IPv6 subnet. """ cidr4 = "0.0.0.0/30" # 2 bits last4 = netaddr.IPNetwork(cidr4).last cidr6 = "fffc::/127" # 1 bits last6 = netaddr.IPNetwork(cidr6).last with self._fixtures([ self._create_models(cidr4, 4, last4), self._create_models(cidr6, 6, last6) ]) as net: subnets = db_api.subnet_find_ordered_by_most_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(subnets[0][0].ip_version, 4) self.assertEqual(subnets[1][0].ip_version, 6) subnets = db_api.subnet_find_ordered_by_least_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(subnets[0][0].ip_version, 4) self.assertEqual(subnets[1][0].ip_version, 6)
def test_ordering_subnets_find_allocc_when_counts_unequal_size_equal(self): models = [] cidrs = ["0.0.0.0/31", "1.1.1.0/31", "2.2.2.0/31"] for cidr in cidrs: last = netaddr.IPNetwork(cidr).last models.append(self._create_models(cidr, 4, last)) with self._fixtures(models) as net: self._create_ip_address("2.2.2.1", 4, "2.2.2.0/31", net["id"]) self._create_ip_address("2.2.2.2", 4, "2.2.2.0/31", net["id"]) self._create_ip_address("1.1.1.1", 4, "1.1.1.0/31", net["id"]) subnets = db_api.subnet_find_ordered_by_most_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(len(subnets), 3) self.assertEqual(subnets[0][0].cidr, "2.2.2.0/31") self.assertEqual(subnets[0][1], 2) self.assertEqual(subnets[1][0].cidr, "1.1.1.0/31") self.assertEqual(subnets[1][1], 1) self.assertEqual(subnets[2][0].cidr, "0.0.0.0/31") self.assertEqual(subnets[2][1], 0) subnets = db_api.subnet_find_ordered_by_least_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(len(subnets), 3) self.assertEqual(subnets[0][0].cidr, "0.0.0.0/31") self.assertEqual(subnets[0][1], 0) self.assertEqual(subnets[1][0].cidr, "1.1.1.0/31") self.assertEqual(subnets[1][1], 1) self.assertEqual(subnets[2][0].cidr, "2.2.2.0/31") self.assertEqual(subnets[2][1], 2)
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")))) 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: na_ip = netaddr.IPAddress(ip_address) if ipnet.version == 4 and na_ip.version != 4: na_ip = na_ip.ipv4() if na_ip not in ipnet: if subnet_ids is not None: LOG.info("Requested IP {0} not in subnet {1}, " "retrying".format(str(na_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: ip = subnet["next_auto_assign_ip"] # If ip is somehow -1 in here don't touch it anymore if ip != -1: ip += 1 # and even then if it is outside the valid range set it to # -1 to be safe if ip < subnet["first_ip"] or ip > subnet["last_ip"]: LOG.info("Marking subnet {0} as full".format( subnet["id"])) ip = -1 self._set_subnet_next_auto_assign_ip(context, subnet, ip) LOG.info("Subnet {0} - {1} looks viable, returning".format( subnet["id"], subnet["_cidr"])) return subnet else: LOG.info("Marking subnet {0} as full".format(subnet["id"])) self._set_subnet_next_auto_assign_ip(context, subnet, -1)
def test_get_subnet_do_not_use_not_returned(self): network = dict(name="public", tenant_id="fake", network_plugin="BASE") subnet = dict(id=1, ip_version=4, next_auto_assign_ip=2, cidr="0.0.0.0/24", first_ip=0, last_ip=255, ip_policy=None, tenant_id="fake") with self._stubs(network, subnet) as (net, sub1, sub2): subnets = db_api.subnet_find_ordered_by_most_full(self.context, net["id"]).all() self.assertEqual(len(subnets), 1) self.assertEqual(subnets[0][0]["id"], "1")
def test_get_subnet_do_not_use_not_returned(self): network = dict(name="public", tenant_id="fake", network_plugin="BASE") subnet = dict(id=1, ip_version=4, next_auto_assign_ip=2, cidr="0.0.0.0/24", first_ip=0, last_ip=255, ip_policy=None, tenant_id="fake") with self._stubs(network, subnet) as (net, sub1, sub2): subnets = db_api.subnet_find_ordered_by_most_full(self.context, net["id"]).all() self.assertEqual(len(subnets), 1) self.assertEqual(subnets[0][0]["id"], "1")
def test_ordering_subnets_find_allocation_counts_when_count_equal(self): models = [] cidrs = ["0.0.0.0/31", "1.1.1.0/31", "2.2.2.0/31"] for cidr in cidrs: last = netaddr.IPNetwork(cidr).last models.append(self._create_models(cidr, 4, last)) with self._fixtures(models) as net: subnets = db_api.subnet_find_ordered_by_most_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(len(subnets), 3) for subnet in subnets: self.assertIn(subnet[0]["cidr"], cidrs)
def test_ordering_subnets_find_allocation_counts_when_count_equal(self): models = [] cidrs = ["0.0.0.0/31", "1.1.1.0/31", "2.2.2.0/31"] for cidr in cidrs: last = netaddr.IPNetwork(cidr).last models.append(self._create_models(cidr, 4, last)) with self._fixtures(models) as net: subnets = db_api.subnet_find_ordered_by_most_full( self.context, net['id'], segment_id=None, scope=db_api.ALL).all() self.assertEqual(len(subnets), 3) for subnet in subnets: self.assertIn(subnet[0]["cidr"], cidrs)
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)