How to migrate number pools to ranges
With Infrahub version 1.12, a number pool can hold several weighted ranges. start_range and
end_range are deprecated in favor of ranges. They still work as a way to declare a single
range, and will be removed in a later version.
What you migrate depends on how the pool was created:
- Schema pools. Infrahub creates one pool for each
NumberPoolattribute of your schema and sets its ranges from the attribute'sparameters. You change these ranges only in the schema. Follow Migrate schema pools. - User pools. Every other number pool, created in the web interface, with a GraphQL mutation or with the Python SDK. You change their ranges on the pool. Follow Migrate user pools.
To find out which kind a pool is, go to Object Management → Resource Managers and open the pool. The page of a schema pool shows a Managed by schema tag next to the pool name:

In GraphQL and the Python SDK, the pool's pool_type is Schema for a schema pool and User for a
user pool.
For both kinds of pools, upgrade infrahubctl and the Python SDK and update the scripts that read pools. If two pools for the same unique attribute have overlapping ranges, also separate their ranges.
What the upgrade changes:
- All pools: Each pool holds one range with the bounds of its former
start_rangeandend_range, and keeps the numbers it allocated. - All pools: Infrahub refuses a change to a pool's ranges that leaves an allocated number outside every range, and lists the objects that hold those numbers.
- Schema pools: A schema load that uses
start_rangeorend_rangereturns a deprecation warning. - Schema pools: The first schema load after the upgrade reports a change on each
NumberPoolattribute that declares onlystart_rangeor onlyend_range. The change is expected, and the pool allocates from the same numbers. - Pools on a unique attribute: Infrahub refuses a range that overlaps a range of another pool for the same unique attribute. Pools whose ranges already overlap keep their ranges.
Schema used in this guide
The examples use the following schema. Infrahub creates a pool for the NumberPool attribute
service_identifier. For the Number attribute vlan_id, you create a pool yourself.
Adapt the type names to match your own schema.
nodes:
- name: Service
namespace: Infra
display_label: "{{ name__value }}"
attributes:
- name: name
kind: Text
unique: true
- name: service_identifier
kind: NumberPool
read_only: true
parameters:
start_range: 100
end_range: 4094
- name: VLAN
namespace: Ipam
attributes:
- name: name
kind: Text
unique: true
- name: vlan_id
kind: Number
parameters:
min_value: 1
max_value: 4094
Upgrade infrahubctl and the Python SDK​
With Infrahub 1.12, use infrahub-sdk version 1.24.0 or later:
pip install --upgrade 'infrahub-sdk[ctl]>=1.24.0'
Earlier versions do not support ranges, and the SDK can create a pool with the wrong ranges without
an error.
Migrate schema pools​
Update schema files​
In the parameters of each NumberPool attribute, replace start_range and end_range with
ranges. Each range has a start and an end, both included and both optional, and an optional
weight.
Both bounds​
Before:
parameters:
start_range: 100
end_range: 4094
After:
parameters:
ranges:
- start: 100
end: 4094
One bound​
Declare only the bound that the old declaration sets:
Before:
parameters:
start_range: 100
After:
parameters:
ranges:
- start: 100
Infrahub stores no value for the missing bound. A range without a start begins at 1, and a range
without an end stops at the largest number Infrahub supports. The pool page shows that number as
MAX.
Several ranges​
The range with the highest weight is allocated first. A missing weight counts as 0; on a tie,
the lowest start comes first. Nothing between two ranges is allocated.
parameters:
ranges:
- start: 100
end: 199
weight: 100
- start: 300
end: 399
No range​
For an attribute that declares neither ranges nor start_range / end_range, Infrahub creates a
pool with one range without a start or an end. The range runs from 1 to the largest number Infrahub
supports, like the default range before 1.12. When a later load declares neither, the pool keeps the
ranges it holds.
With ranges: [], Infrahub creates a pool that allocates nothing, or removes every range from an
existing pool. The load is refused while an object holds a number from the pool.
Refused declarations​
| Declaration | Error |
|---|---|
start_range or end_range with ranges | start_range/end_range cannot be combined with ranges |
| Overlapping ranges | Range 100 - 200 overlaps 150 - 300 |
end lower than start | Range end (200) cannot be lower than start (300) |
A range shortened below a number already allocated, for example 100–200 changed to 100–100 while 101 is allocated | Attribute-level 'number_pool' constraint violation on schema 'InfraService'. Node (<SERVICE-NAME>) is not compliant. The error relates to field service_identifier=101. |
Load the schema​
If you're using Git integration, commit the changes and let Infrahub import them.
If you're using infrahubctl:
-
Check the schema:
infrahubctl schema check path/to/your/schema/files -
Load it on the default branch:
infrahubctl schema load path/to/your/schema/files
The load returns a deprecation warning for each attribute still using start_range or end_range.
A schema pool is shared by every branch, and Infrahub sets its ranges from the schema of the default branch. When you load a range change on another branch, Infrahub accepts the load, but the pool keeps its current ranges until you merge the branch into the default branch.
If the load returns no deprecation warning and every NumberPool attribute declares ranges,
your schema files are migrated.
Change schema pool ranges only in the schema​
Infrahub refuses a change to the ranges of a schema pool made with a GraphQL mutation or with the Python SDK:
- Setting
start_rangeorend_rangeon the pool:start_range or end_range can't be updated on schema defined pools, update the schema in the default branch instead. - Setting
rangeson the pool, or creating, changing or deleting one of itsCoreNumberPoolRangeobjects:ranges can't be updated on schema defined pools, update the schema in the default branch instead.
Migrate user pools​
In GraphQL​
In GraphQL, the weight is allocation_weight.
Create a pool​
Before:
mutation {
CoreNumberPoolCreate(
data: {
name: {value: "VLAN ID pool"}
node: {value: "IpamVLAN"}
node_attribute: {value: "vlan_id"}
start_range: {value: 100}
end_range: {value: 1000}
}
) {
ok
}
}
After:
mutation {
CoreNumberPoolCreate(
data: {
name: {value: "VLAN ID pool"}
node: {value: "IpamVLAN"}
node_attribute: {value: "vlan_id"}
ranges: [{start: 100, end: 1000}]
}
) {
ok
}
}
A range can leave out start or end, for example ranges: [{start: 100}]. Infrahub reads the
missing bound from the attribute the pool allocates for:
- A range without a
startbegins at the attribute'smin_value, or at1when the attribute has nomin_valueor amin_valuebelow1. - A range without an
endstops at the attribute'smax_value, or at the largest number Infrahub supports when the attribute has nomax_value.
When a CoreNumberPoolCreate sends none of ranges, start_range and end_range, Infrahub creates
the pool with one range without a start or an end. The account that sends the mutation needs the
permission to create CoreNumberPoolRange objects. To create a pool that holds no range, send
ranges: [].
Update a pool​
ranges replaces all the pool's ranges. A range with the same bounds as a stored one keeps its
weight unless allocation_weight is set. ranges: [] removes every range.
mutation {
CoreNumberPoolUpdate(
data: {
id: "<POOL-ID>"
ranges: [
{start: 100, end: 199, allocation_weight: 100}
{start: 300, end: 399}
]
}
) {
ok
}
}
Each range must stay within the min_value and max_value of the attribute the pool allocates
for. With min_value: 1 and max_value: 4094 on vlan_id, a range from 100 to 5000 is refused.
A range that starts below min_value is refused the same way.
With the Python SDK​
The SDK takes the same fields as GraphQL: each range is a dictionary with start, end and
allocation_weight, any of which can be left out.
Create a pool​
Before:
pool = await client.create(
kind="CoreNumberPool",
name="VLAN ID pool",
node="IpamVLAN",
node_attribute="vlan_id",
start_range=100,
end_range=1000,
)
await pool.save()
After:
pool = await client.create(
kind="CoreNumberPool",
name="VLAN ID pool",
node="IpamVLAN",
node_attribute="vlan_id",
ranges=[{"start": 100, "end": 1000}],
)
await pool.save()
Update a pool​
To replace all the ranges of an existing pool, declare the pool with its complete list of ranges
and save it with allow_upsert=True. Infrahub finds the pool by its name, keeps the ranges whose
bounds match, creates the new ones and deletes the others.
pool = await client.create(
kind="CoreNumberPool",
name="VLAN ID pool",
node="IpamVLAN",
node_attribute="vlan_id",
ranges=[
{"start": 100, "end": 199, "allocation_weight": 100},
{"start": 300, "end": 399},
],
)
await pool.save(allow_upsert=True)
To add, change or delete a single range, create, update or delete the matching
CoreNumberPoolRange object:
pool = await client.get(kind="CoreNumberPool", name__value="VLAN ID pool")
new_range = await client.create(kind="CoreNumberPoolRange", start=3000, end=3999, pool=pool)
await new_range.save()
new_range.allocation_weight.value = 50
await new_range.save()
await new_range.delete()
Do not change ranges with add(), extend() or remove() on the pool's ranges relationship.
The CoreNumberPool mutations accept ranges as bounds, not as references to CoreNumberPoolRange
objects.
How start_range and end_range behave on writes​
Existing scripts that set start_range and end_range, in GraphQL or with the SDK, keep working
while each pool holds one range, which is the case for every user pool after the upgrade.
- On a pool with one range, setting
start_rangeorend_rangechanges the bounds of that range, for exampleend_range: 299changes100–199to100–299. The bound you leave out keeps its value, and setting a bound to null removes it from the range. - On a pool with several ranges, setting either field is refused. The error lists the ranges to edit instead.
- A request that creates a pool can set
start_rangealone orend_rangealone. The range then has no value for the other bound.
Update scripts that read pools​
This section applies to schema pools and user pools.
In GraphQL​
Before:
query {
CoreNumberPool(ids: ["<POOL-ID>"]) {
edges {
node {
start_range { value }
end_range { value }
}
}
}
}
After:
query {
CoreNumberPool(ids: ["<POOL-ID>"]) {
edges {
node {
ranges {
edges {
node {
start { value }
end { value }
allocation_weight { value }
}
}
}
}
}
}
}
start and end return null for a range without that bound.
Reading the deprecated fields:
- On a pool with one range,
start_rangeandend_rangereturn the bounds of that range, and null for a bound the range leaves out. - On a pool with zero or several ranges, both return null.
With the Python SDK​
pool = await client.get(kind="CoreNumberPool", name__value="VLAN ID pool", include=["ranges"])
await pool.ranges.fetch()
for pool_range in pool.ranges.peers:
print(pool_range.peer.start.value, pool_range.peer.end.value, pool_range.peer.allocation_weight.value)
start.value and end.value are None for a range without that bound.
Separate pools that overlap on a unique attribute​
With Infrahub version 1.12, Infrahub refuses a range that overlaps a range of another pool for the same kind and unique attribute, as described in Pools for the same unique attribute. Infrahub checks ranges only when you save them, so pools whose ranges overlapped before the upgrade still overlap after it.
When one of those pools allocates a number that the other pool tracks for an object, Infrahub refuses the save. The message names the pool, the number, the object that holds the number and the other pool, and tells you to attach the numbers in the overlap to one of the pools and change the ranges of the other pool.
To separate the two pools:
- Choose the pool that keeps the numbers in the overlap.
- List the numbers the other pool tracks in the overlap, in its allocations table or with
InfrahubNumberPoolAllocations, as described in View how much of a pool is in use. - Attach each of those numbers to the pool you chose: update the object with the number as
valueand that pool asfrom_pool. Infrahub removes the number from the other pool, as described in Move a number to another pool. - Change the ranges of the other pool so that they no longer overlap. Infrahub refuses this change while the pool still tracks a number outside its new ranges, as described in Change the ranges of a pool. For a schema pool, change the ranges in the schema, as described in Update schema files.
Related resources​
- Number pools in the schema: declare a
NumberPoolattribute and the ranges of its pool. - Allocate numbers: create a number pool and allocate numbers from it.
- Weighted allocation: set the order in which IP address and IP prefix pools allocate their resources.