Skip to main content

Allocate numbers per site with allocation scopes

Some numbers only have to be distinct within a site, a pod or another part of the network. VLAN IDs are the usual case: VLAN 100 can exist in every site, but two VLANs in one site cannot share an ID. Without a scope, each site needs its own number pool, you create a new pool every time you add a site, and nothing checks that a VLAN took its number from the pool of its own site.

Scope one number pool by the attributes or relationships that set where a number must be distinct, such as the site relationship of the VLAN. The pool then allocates separately within each allocation scope, and gives each VLAN the lowest free number among the VLANs of the same site.

The examples on this page use the IpamVLAN kind from Allocate numbers, with a required site relationship of cardinality one added to it.

Allocation scopes​

A pool's scope is a list of attributes and relationships of the pool's kind, called scope elements. An allocation scope is one combination of their values: the peer of a relationship, and the value of an attribute.

With a pool scoped by site, every site is an allocation scope:

  • A VLAN in site A and a VLAN in site B can both receive 100.
  • Two VLANs in site A never receive the same number from the pool.

With a pool scoped by site and role, each combination of a site and a role is an allocation scope, so a VLAN with role user and a VLAN with role voice in site A can both receive 100.

A pool without a scope allocates from one space shared by every object, as described in Allocate numbers.

The GraphQL API calls an allocation scope a division, as in the InfrahubNumberPoolDivisions query.

Choose one scoped pool or one pool per site​

Every allocation scope of a pool shares the pool's ranges and their weights. Choose by whether your sites use the same number plan:

  • Same ranges in every site: create one pool scoped by site. A new site needs no new pool, and the usage of each site is listed on the pool page.
  • Different ranges per site, for example 100–199 in one site and 2000–2099 in another: create one pool per site, without a scope.

What can be a scope element​

Each scope element is the name of an attribute or a relationship of the pool's kind, as it exists on the default branch. Infrahub refuses an element that is:

  • not declared on the kind itself. On a pool for a generic, the element must be declared on the generic, not only on the kinds that inherit from it.
  • written as a path, such as role__value. Write the name, role.
  • an optional attribute or relationship. Each element must be required.
  • a relationship of cardinality many
  • an attribute of kind List, JSON or Any
  • the attribute the pool allocates
  • listed twice

A pool cannot have a scope when the attribute it allocates is unique, because a number that must be distinct across every object cannot be allocated separately in each allocation scope. Infrahub refuses it with <kind>.<attribute> is unique; a globally unique number cannot be allocated per division.

Create a scoped pool​

Set the scope when you create the pool.

warning

You cannot change the scope of a pool after you create it. Infrahub refuses the change with allocation_scope can't be changed after the pool is created. To divide numbers differently, create another pool.

Create the number pool as described in Allocate numbers. Under What it allocates, choose the attributes and relationships in Scoped by. Scoped by lists only the fields that can be part of a scope. When you edit the pool later, Scoped by is read-only.

Allocate from a scoped pool​

Allocate as from any number pool: choose the pool in the From pool tab, or send from_pool on the attribute. The VLAN receives the lowest free number in its own allocation scope, following the order of the ranges.

mutation {
IpamVLANCreate(
data: {
name: {value: "users-site-a"}
site: {id: "<SITE-A-ID>"}
vlan_id: {from_pool: {id: "<POOL-ID>"}}
}
) {
ok
object {
vlan_id {
value
}
}
}
}

Infrahub handles allocations for one allocation scope at a time. Requests for different allocation scopes of the same pool do not wait for each other.

A number you provide, as described in Track existing numbers in a number pool, is recorded under the allocation scope of the object. The pool records it even when it already tracks that number for another object in the same allocation scope. To have Infrahub refuse duplicates within a site, add a uniqueness constraint to the kind, as described in Branches.

When an object moves to another allocation scope​

When you move a VLAN to another site, the VLAN keeps its number. Infrahub does not check the number against the numbers of the new site.

To give the VLAN a number from its new site, send value: null with the pool in the same update:

vlan_id: {value: null, from_pool: {id: "<POOL-ID>"}}

Branches​

A number allocated from a scoped pool is reserved on every branch, like any number from a pool, as described in Branch-agnostic allocation. Infrahub counts a number in an allocation scope only when, on one branch, the object holds that number and belongs to that allocation scope. A VLAN created on a branch counts in its site on that branch.

Merging two branches can still leave a duplicate within one allocation scope. If you change the number of a VLAN on one branch, and move the same VLAN to another site on another branch, merging both branches can leave two VLANs in that site with the same number. To have Infrahub refuse the duplicate, add a uniqueness constraint that combines the scope with the number:

nodes:
- name: VLAN
namespace: Ipam
uniqueness_constraints:
- ["site", "vlan_id__value"]

See Nodes and attributes for how uniqueness constraints work.

View the usage of each allocation scope​

Open the pool from Resource Managers. For a scoped pool, the pool page first lists its allocation scopes, under Allocation scopes, with one column for each scope element and a Usage column. Search and sort the list. Click an allocation scope to view its ranges and the numbers allocated in it, and switch to another allocation scope from the Allocation scope card.

In the GraphQL API, InfrahubNumberPoolDivisions lists the allocation scopes that hold at least one number, ordered from the most used:

query {
InfrahubNumberPoolDivisions(pool_id: "<POOL-ID>") {
count
divisions {
display_label
entries {
path
value
display_label
}
figures {
size
used
utilization
}
}
}
}

To read the figures or the numbers of one allocation scope, pass it as division to InfrahubNumberPoolUtilization or InfrahubNumberPoolAllocations, with one entry for each scope element. For a relationship, the value is the ID of the peer. For an attribute, it is the value of the attribute.

query {
InfrahubNumberPoolUtilization(
pool_id: "<POOL-ID>"
division: [{path: "site", value: "<SITE-A-ID>"}]
) {
figures {
size
used
utilization
}
}
}

On a scoped pool, InfrahubNumberPoolUtilization requires a division with a value for every scope element. InfrahubNumberPoolAllocations also accepts some of the scope elements, and then lists the numbers of every matching allocation scope. InfrahubResourcePoolUtilization and InfrahubResourcePoolAllocated return the figures of the whole pool and ignore its scope.

Schema changes refused while a pool uses an element​

A pool's scope depends on the attributes and relationships it names. While the pool exists, Infrahub refuses a schema change that would:

  • remove an element
  • make an element optional
  • change a relationship element to cardinality many
  • change an attribute element to kind List, JSON or Any
  • make the attribute the pool allocates unique
  • rename the pool's kind, by its name or its namespace
  • rename the attribute the pool allocates
  • make the pool's kind inherit from another generic

The error names the pool and the element, for example pool VLAN IDs per site divides its numbers by IpamVLAN.site; a scope element cannot be removed while the pool exists. The last three changes are described in Schema migration. Renaming an element is allowed. Once the rename is in the schema of the default branch, Infrahub updates the name the pool shows for that element.

When the schema of the branch you work on does not have a scope element, allocation from the pool fails with the scope element "<name>" of pool <pool> does not exist on <kind> on branch <branch>.

Next​