Skip to content

feat(node): number pool attach/detach, provenance and declared ranges - #1459

Open
ajtmccarty wants to merge 4 commits into
release-1.12from
ajtm-10262026-number-pool-sdk-gaps
Open

ajtmccarty wants to merge 4 commits into
release-1.12from
ajtm-10262026-number-pool-sdk-gaps

Conversation

@ajtmccarty

@ajtmccarty ajtmccarty commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Why

Infrahub 1.12 adds number-pool features that SDK users cannot use today:

  • Detaching a number from its pool, by sending from_pool: null on an update (IFC-3333). The SDK has no way to send it. Attaching a number the user already holds (value and from_pool together) has the same problem: on create, the SDK sends only from_pool, so the pool allocates a new number instead of attaching the given one.
  • Reading whether the pool allocated each number or a user provided it, from the provenance field of InfrahubResourcePoolAllocated (IFC-3367). get_pool_allocated_resources() returns only the nodes and drops every row field.
  • Creating a number pool with its ranges in one call (IFC-3369). The pool mutations take ranges as NumberPoolRangeInput (start, end, allocation_weight), but the SDK sends every relationship peer as a reference, so ranges=[{"start": 1, "end": 10}] reaches Infrahub as [{}]: one range with no start and no end.

Closes IFC-3333, IFC-3367, IFC-3369

What changed

Attach and detach a number (IFC-3333)

  • Number attributes have attach_to_pool(pool) and detach_from_pool(). Both are sent on the next save.
    • attach_to_pool() sends the number the attribute holds with from_pool, so the pool tracks it as provided. It raises ValueError when the attribute holds no number, or when it is given a node that is not a resource pool or has not been saved.
    • detach_from_pool() sends from_pool: null. The attribute keeps its number. On a node that is not saved yet, the number is sent with the detach so the node is created with it.
  • An attribute built with both value and from_pool now sends both, so the pool attaches the given number instead of allocating a new one.
  • A from_pool request is sent once. A private pending flag is cleared after a successful mutation, so the next update does not send it again. _from_pool stays set, so is_from_pool_attribute() still returns True after a save.
  • The Integer and IntegerOptional protocols declare the two methods for typed code.

List a pool's allocations with their provenance (IFC-3367)

  • Resource pool nodes have get_pool_allocations(resource), async and sync. It returns PoolAllocation entries (node ID, kind, branch, display label, identifier, provenance) without fetching the nodes.
  • provenance is PoolAllocationProvenance.ALLOCATED, PoolAllocationProvenance.PROVIDED, or None for IP pools.
  • get_pool_allocated_resources() is unchanged. It shares the paging loop but does not request provenance, so it keeps working against servers older than 1.12.

Create a number pool with its ranges (IFC-3369)

  • A relationship whose peer kind is CoreNumberPoolRange sends the peer's fields instead of a reference, matching how Infrahub picks NumberPoolRangeInput for that peer kind. The rule lives in one place: INLINE_INPUT_PEER_KINDS and RelatedNodeBase.
  • client.create(kind="CoreNumberPool", ..., ranges=[{"start": 100, "end": 199, "allocation_weight": 10}]) creates the pool and its ranges in one mutation. save(allow_upsert=True) sets an existing pool's ranges to the declared list.
  • Single ranges of an existing pool are added, changed or deleted through CoreNumberPoolRange nodes, which already worked. Infrahub refuses edits made through the pool's ranges relationship (Field 'id' is not defined by type 'NumberPoolRangeInput').

What stayed the same

  • No schema or protocol generation changes.
  • Every relationship other than one whose peer kind is CoreNumberPoolRange is serialized as before.

How to review

Suggested order, one commit per ticket:

  1. 1d55171b attach and detach: node/attribute.py and the two node.py hunks (clearing the pending flag in _process_mutation_result, and keeping value next to from_pool in _strip_unmodified_dict).
  2. ef09423f provenance: PoolAllocation, the shared query builder and paging loop in node.py.
  3. 3204d16c ranges: node/related_node.py and node/constants.py. It also removes a second self._relationship_metadata = None in RelatedNodeBase.__init__, which the constructor already sets just above.
  4. 2ed22456 integration test.

docs/docs/python-sdk/sdk_ref/ is generated.

Impact & rollout

  • Backward compatibility:
    • A save no longer resends from_pool after it succeeded. Before, the next update sent {"from_pool": ...} again, which Infrahub ignored.
    • ranges dicts on a CoreNumberPool are now sent as range definitions instead of empty objects.
  • Deployment notes: the new features need Infrahub 1.12 or later.

Checklist

  • Tests added/updated
  • Changelog entry added
  • External docs updated (resource manager guide, generated SDK reference)
  • Internal .md docs updated (none needed)
  • reviewed LLM-generated code

🤖 Generated with Claude Code

ajtmccarty and others added 4 commits October 26, 2026 22:03
…333]

Number attributes have attach_to_pool() and detach_from_pool(), sent on the
next save. An attribute built with both value and from_pool now sends both,
so the pool tracks the given number instead of allocating a new one. A pool
write is sent once: a pending flag is cleared after a successful mutation,
while _from_pool stays set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resource pool nodes have get_pool_allocations(), which returns PoolAllocation
entries: the node id, kind, branch, display label, identifier and whether the
pool allocated the number or a user provided it (None for IP pools). It needs
Infrahub 1.12 or later. get_pool_allocated_resources() shares the paging loop
but still leaves provenance out of its query, so it keeps working on older
servers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3369]

A relationship whose peer kind is CoreNumberPoolRange takes the peer's fields
as mutation input instead of a reference, as Infrahub's NumberPoolRangeInput
does. A CoreNumberPool can therefore be created with ranges given as dicts of
start, end and allocation_weight, and an upsert sets an existing pool's ranges
to the declared list. Before, each range was sent as an empty object, which
Infrahub read as one range without a start or an end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n Infrahub 1.12

Covers creating a pool with declared ranges and setting them by upsert,
attaching and detaching numbers (including a detach before the first save),
the provenance get_pool_allocations reports, and the attribute's source after
an attach. The class is skipped unless INFRAHUB_TESTING_IMAGE_VER is 1.12 or
later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ajtmccarty ajtmccarty added the type/feature New feature or request label Oct 27, 2026
@github-actions github-actions Bot added the type/documentation Improvements or additions to documentation label Oct 27, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying infrahub-sdk-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2ed2245
Status: ✅  Deploy successful!
Preview URL: https://fe4bfd45.infrahub-sdk-python.pages.dev
Branch Preview URL: https://ajtm-10262026-number-pool-sd.infrahub-sdk-python.pages.dev

View logs

@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.61017% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
infrahub_sdk/protocols_base.py 0.00% 0 Missing and 4 partials ⚠️
@@               Coverage Diff               @@
##             release-1.12    #1459   +/-   ##
===============================================
  Coverage                ?   87.52%           
===============================================
  Files                   ?      154           
  Lines                   ?    15347           
  Branches                ?     2073           
===============================================
  Hits                    ?    13433           
  Misses                  ?     1355           
  Partials                ?      559           
Flag Coverage Δ
integration-tests 42.41% <8.47%> (?)
python-3.10 61.81% <66.94%> (?)
python-3.11 61.81% <66.94%> (?)
python-3.12 61.82% <66.94%> (?)
python-3.13 61.81% <66.94%> (?)
python-3.14 61.82% <66.94%> (?)
python-3.15 61.81% <66.94%> (?)
python-filler-3.12 23.20% <29.66%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
infrahub_sdk/constants.py 94.44% <100.00%> (ø)
infrahub_sdk/node/__init__.py 100.00% <100.00%> (ø)
infrahub_sdk/node/attribute.py 100.00% <100.00%> (ø)
infrahub_sdk/node/constants.py 100.00% <100.00%> (ø)
infrahub_sdk/node/node.py 90.19% <100.00%> (ø)
infrahub_sdk/node/related_node.py 95.39% <100.00%> (ø)
infrahub_sdk/protocols_base.py 75.18% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ajtmccarty
ajtmccarty marked this pull request as ready for review October 27, 2026 05:54
@ajtmccarty
ajtmccarty requested a review from a team as a code owner October 27, 2026 05:54
@polmichel
polmichel self-requested a review October 27, 2026 13:34
@@ -0,0 +1 @@
An attribute created with both `value` and `from_pool` now sends both, so the number pool tracks the given number instead of allocating a new one. A `from_pool` request is sent once: after a successful save, the next update no longer sends it again.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this fix two different issues? If so, should we split them into separate changelogs?

Regarding the first one, since the new SDK version will ship with Infrahub 1.12, shouldn’t we include that fix changelog in the 1.12 release notes since it was not even possible to do so before 1.12? I’m not entirely sure how we handle release notes in this case.

def _pool_write_payload(self) -> _GraphQLPayloadAttribute:
# A pending write that names no pool is a detach.
if self._from_pool is None:
# A node that is not saved yet must still send the number it is created with.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this comment be just before the next block? If so, it may mislead human and agents

node._get_attribute("vlan_id").detach_from_pool()

input_data = node._generate_input_data(exclude_unmodified=True)["data"]["data"]
assert input_data == {"id": FETCHED_VLAN_ID, "vlan_id": {"from_pool": None}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is useful, but would an additional assertion verifying that the value is still attached to the node be a good idea?

@@ -0,0 +1 @@
Resource pool nodes have `get_pool_allocations()`, which lists the resources a pool holds as `PoolAllocation` entries: the node ID, kind, branch, display label (the number itself for a number pool), identifier and provenance. The provenance is `PoolAllocationProvenance.ALLOCATED` when the number pool allocated the number, `PoolAllocationProvenance.PROVIDED` when a user attached it, and `None` for IP pools. It requires Infrahub 1.12 or later. `get_pool_allocated_resources()` is unchanged.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be subjective: maybe is this changelog too detailed?


Fetch the resources the pool holds for a given resource, with how each one came to be held.

Requires Infrahub 1.12 or later. Unlike :meth:`get_pool_allocated_resources`, it does not fetch the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More a question than a review comment, but I’m not sure we should mention the Infrahub release in each of these methods’ docstrings. We would likely forget to remove them later on, and if we do this for every method, it would be hard to read. Wdyt?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it's a recommendation from /infrahub-writing-docs skill?

Comment thread infrahub_sdk/node/node.py
Comment on lines +2217 to +2218
if response[POOL_ALLOCATED_QUERY_NAME].get("count", 0) <= page_offset + self._client.pagination_size:
return rows

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional comment: maybe not that important but it looks like sending a pagination equals to 0 creates a never ending loop. I think this is preexisting.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great test!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/documentation Improvements or additions to documentation type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants