Skip to content

Compatibility analysis: Netbox 4.6.0-beta1 — test strategy & implementation roadmap #395

@ctrl-alt-automate

Description

@ctrl-alt-automate

Netbox 4.6.0-beta1 compatibility analysis

Release: v4.6.0-beta1 — published 2026-04-14
Target for stable ship: Netbox 4.6.0 final (ETA unknown, typically 4-6 weeks after beta1)
Current PowerNetbox target: 4.5.7 (via netbox-docker 4.0.2)

This issue documents a deep analysis of what needs to change in PowerNetbox to reach full 4.6 compatibility, and proposes a phased test strategy. It is intended as the parent tracking issue — follow-up implementation PRs will carve out the per-area work.


TL;DR

  • No netbox-docker image exists for 4.6 yet. Both release and develop branches still pin to VERSION=4.0.2 (for Netbox 4.5.x). This is the hard blocker for adding a 4.6 row to .github/workflows/compatibility.yml. We will need to build a custom Dockerfile against netbox:v4.6.0-beta1 ourselves — mirroring the pattern already used for Dockerfile.branching.
  • 3 brand-new models require 12 new PowerNetbox functions (Get/New/Set/Remove × 3): VirtualMachineType, CableBundle, RackGroup (reintroduced as a flat model separate from Location).
  • 2 structural REST API changes touch InvokeNetboxRequest itself — the central helper used by all 522 functions:
    • ETag + If-Match (#21356) — new response header + opt-in request header for optimistic locking, plus a new 412 Precondition Failed status the error handler must understand.
    • Cursor-based pagination (start param, #21363) — performance-oriented alternative to offset. Non-breaking, but worth plumbing through -All so large list endpoints stay constant-time.
  • Django 6.0 upgrade (#20984) is the biggest unmeasured risk. It is infrastructure, not feature work, but historically Django major bumps reshape error bodies, serializer edge cases, and auth backend behavior — exactly the surfaces PowerNetbox's InvokeNetboxRequest and BuildDetailedErrorMessage parse.
  • 1 semi-breaking change on VM creation: #12024 makes cluster optional on VirtualMachine (VMs can now attach to a device without a cluster). New-NBVirtualMachine -Cluster must relax from [Parameter(Mandatory)] to optional.
  • ValidateSet drift risk is moderate. The release notes don't list new interface types or status enums, but 4.5.x saw 3 drift bugs ([Bug] New-NBDCIMInterface #360, [Bug] Get-NBBranch -Status ValidateSet is incorrect — contains non-existent value and misses 9 real statuses #385, [Bug] -Cable_Profile ValidateSet on DCIMCable functions uses wrong API values (all 25 are broken) #389). Run scripts/Verify-ValidateSetParity.ps1 -NetboxVersion v4.6.0-beta1 early — it is the cheapest high-signal check we have.

1. Impact assessment

1.1 New models → new PowerNetbox functions

Model Endpoint New functions Notes
VirtualMachineType (#5795) /api/virtualization/virtual-machine-types/ Get/New/Set/Remove-NBVirtualMachineType Parallel to DeviceType for physical hardware. Will likely be FK'd from VirtualMachine via a new type field.
CableBundle (#20151) /api/dcim/cable-bundles/ Get/New/Set/Remove-NBDCIMCableBundle Groups cables into managed runs. Explicitly not for individual fiber strands — useful callout for docs.
RackGroup (#20961) /api/dcim/rack-groups/ Get/New/Set/Remove-NBDCIMRackGroup Reintroduction of a flat model removed in an earlier version. Independent of Location hierarchy; Rack gets a new optional FK; VLANGroup can scope to RackGroup. Watch for the FK addition on existing endpoints.

Effort estimate: 12 new functions × (implementation + ~6 unit tests each) ≈ 72 new tests. Follow the templates in CLAUDE.md — each function is ~30 LOC of the standard New/Set/Get/Remove pattern.

1.2 REST API structural changes (affect InvokeNetboxRequest)

ETag / If-Match (#21356) — HIGH impact on helper layer

Every GET on a single object now returns an ETag header derived from last_updated. Clients may opt in to optimistic locking by passing If-Match: <etag> on subsequent PUT/PATCH — on mismatch the server returns HTTP 412 Precondition Failed.

Required changes in Functions/Helpers/InvokeNetboxRequest.ps1:

  1. Capture ETag from the response headers and surface it on returned objects (either as PSObject.Properties.Add('_etag', ...) or via a parallel -OutVariable pattern).
  2. Add an optional -IfMatch parameter that injects the If-Match header.
  3. Map HTTP 412 to a new ErrorCategory (probably WriteError with a ConcurrentUpdateDetected error ID) in the central error handler so callers can catch it cleanly.
  4. No Set- function changes are strictly required* — ETag support is opt-in. But we should add a -IfMatch passthrough on Set-* functions that have high concurrent-edit risk (devices, interfaces, IP addresses) in a follow-up PR.

Test coverage gap: we have zero tests for 412 responses today. Add one to ErrorHandling.Tests.ps1.

Cursor-based pagination (start param, #21363) — MEDIUM impact

The new start parameter filters for pk >= start instead of scanning offset rows, so large list endpoints become O(page_size) instead of O(offset + page_size). Existing limit/offset is unchanged.

Options:

  • A. Do nothing. Existing pagination keeps working. Users on 4.6 just don't get the perf benefit.
  • B. Add -Start <uint64> to all Get functions. Trivial — one line in BuildURIComponents. Exposes the param surface but most users won't notice.
  • C. Teach InvokeNetboxRequest -All to prefer start over offset when the connected Netbox is ≥4.6. Biggest win, small risk. This is where PowerNetbox actually differentiates: -All on a device list of 50k rows goes from ~25 roundtrips of growing latency to 25 roundtrips of constant latency.

Recommendation: do B + C. Gate the InvokeNetboxRequest change on \$script:NetboxConfig.ParsedVersion -ge [version]'4.6.0' so it's zero-risk on older servers.

1.3 Enhancements with API-surface impact

# Change PowerNetbox impact
#12024 VMs can attach to devices without a cluster New-NBVirtualMachine -Cluster must become optional. Validate that either Cluster or Device is provided via [Parameter(ParameterSetName=...)]. Breaking if we enforce both.
#15513 Bulk creation for IP prefixes New-NBIPAMPrefix already supports bulk pipelines via Send-NBBulkRequest. Verify the bulk body shape still matches 4.6's expectations — no code change expected, but add an integration test.
#17654 ASN role assignment Add -Role param to New/Set/Get-NBIPAMASN. ~3 tests.
#19138 NAT addresses included in primary/OOB IP responses Pure schema enrichment on GET. No code change — existing Get functions pass through unknown fields. Worth documenting as a new field users can access.
#20123 REST options to control adoption/replication of device components New optional body params (likely adopt / replicate bools). Add to the relevant New-NBDCIM* component functions (Interface, Console/Power Ports, etc.).
#20152 disabled flag on module and device bays New [switch] (or [bool]?) param on New/Set/Get-NBDCIMDeviceBay and New/Set/Get-NBDCIMModuleBay. Decision needed: [bool]$Disabled is consistent with the 90+ other boolean API params (see memory: `Mark_Utilized` lesson from v4.5.4.2). Use [bool], not [switch].
#20162 Background job option for bulk component adds New -Background bool on bulk component creation. Medium complexity — response shape changes (returns a job URL instead of the created objects).
#20163 Changelog message for bulk device component creation New optional -ChangelogMessage param (string). Pair with #21780 (same feature on IP addresses).
#20698 total_vlan_ids on VLAN group Read-only field, no code change. Passthrough.
#21575 {vc_position} template variable Templating-only, no REST impact.
#21662 rf_channel_frequency precision bumped to 3 decimals PowerShell decimal handling should be unaffected (we pass through [object]). Add a test that preserves 3 decimal places round-trip.
#21702 Serialized HTTP request in webhooks Webhook payloads only — not touched by PowerNetbox. No change.
#21770 Column control on embedded tables UI-only. No change.
#21771 Partial tag assignment (add_tags / remove_tags) via REST New opportunity: add Add-NBTag / Remove-NBTag convenience cmdlets. Today users must fetch the object, mutate the tag list, and PATCH the whole thing. This is a quality-of-life win worth shipping separately.
#21780 Changelog message for bulk IP address creation Pair with #20163.
#21720 HTTP basic auth regex aligned with Django May affect EnhancedURLValidator-fed custom fields. Test edge cases in SSLVerificationTests.

1.4 Deprecations (ship in 4.6, removed in 4.7)

  • username and request_id event fields (#21284) — PowerNetbox doesn't consume events directly, so no code impact. Document for users who parse webhook payloads.
  • housekeeping management command (#21304) — backend only, no impact.
  • Legacy querystring template tag (#21331) — template-only, no impact.
  • Legacy Sentry config (#21881), DEFAULT_ACTION_PERMISSIONS (#21884), legacy view actions (#21887), models registry key (#21890) — all backend/plugin internals, no PowerNetbox impact.

Action: no code change required for deprecations in 4.6. Create a follow-up tracking issue for 4.7 to revisit these when they hard-remove.

1.5 Django 6.0 upgrade (#20984) — highest risk, lowest visibility

Django majors historically change:

  • Serializer error formats. Our BuildDetailedErrorMessage pattern-matches on specific keys (`detail`, `__all__`, per-field arrays). Django 6 may reshape these.
  • DateTimeField timezone handling. Some endpoints may emit ISO-8601 with different fractional-seconds precision — affects Get-NBCoreObjectChange time filter.
  • Auth backend signatures. Custom token authentication may surface different error classes.
  • HTTP method not allowed now typically returns 405 with a different body shape than Django 5.
  • URLValidator regex changes (related to #21720 above).

Mitigation: the only reliable check is to run the full integration matrix and observe what breaks. Unit tests mock at InvokeNetboxRequest level so they won't catch Django-layer shifts.


2. Test strategy — phased approach

Phase 0 — Build a 4.6-beta1 Docker image (blocker)

Since netbox-docker has no 4.6-ready image, we need a bespoke build. Two approaches:

Option A (recommended): extend the existing Dockerfile.branching pattern.
Create Dockerfile.46beta:

```dockerfile

Dockerfile.46beta — custom build for Netbox 4.6.0-beta1 compatibility testing

FROM netboxcommunity/netbox:v4.5.7-4.0.2 AS base

Pull 4.6.0-beta1 source over the top of an otherwise-working runtime

RUN cd /opt/netbox &&
git fetch --tags https://github.com/netbox-community/netbox.git v4.6.0-beta1 &&
git checkout v4.6.0-beta1 &&
/opt/netbox/venv/bin/pip install -r requirements.txt &&
/opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py migrate
```

This is fragile (base image's Python/PostgreSQL versions may not match 4.6's requirements — specifically Django 6.0 may bump minimum Python), but it's the fastest path to a running instance.

Option B (cleaner but slower): fork netbox-docker at the develop branch and bump its VERSION file + Dockerfile ARGs to 4.6.0-beta1.
This mirrors what netbox-docker maintainers will eventually do. Run ./build.sh v4.6.0-beta1 from the fork and push the image to GHCR under ctrl-alt-automate/netbox-46beta.

Decision needed: start with A for signal, upgrade to B once we see it's viable.

Phase 1 — Smoke test (ValidateSet parity, cheap & fast)

Before any Docker work, run the parity script against the 4.6 source directly:

```powershell
cd PowerNetbox
./scripts/Verify-ValidateSetParity.ps1 -NetboxVersion v4.6.0-beta1 -FailOnMismatch -OutputFormat Json | Out-File temp/parity-46.json
```

Expected findings:

  • New interface types (watch for 800G variants — 4.5.6 added 1.6TB, 4.6 may push further)
  • New status values on VirtualMachine if VirtualMachineType introduces lifecycle states
  • New cable_type values if CableBundle brings its own
  • The script's exclusions file (scripts/validateset-parity-exclusions.txt) will need review — new legitimate PowerNetbox-internal values may surface as false positives.

Effort: ~10 minutes. This is the single highest-signal cheap check.

Phase 2 — Core smoke test on 4.6-beta1

Once the Docker image runs:

```bash
NETBOX_VERSION=v4.6.0-beta1-custom docker compose -f docker-compose.ci.yml up -d

Wait for migrations (Django 6.0 may slow first-boot substantially)

```

Run the existing compatibility.yml step inline: Tests 1-8 (Get-NBContentType, Core*, VPN, Wireless, DCIMSite, IPAMPrefix). Expected: all pass.

Then add 4.6-specific probes:

  • GET /api/virtualization/virtual-machine-types/ → 200
  • GET /api/dcim/cable-bundles/ → 200
  • GET /api/dcim/rack-groups/ → 200
  • GET /api/dcim/devices/1/ and verify response has an ETag header
  • GET /api/dcim/devices/?start=1&limit=5 → 200 with cursor results

Phase 3 — Full integration suite

Run the existing 94-test integration suite:

```powershell
$env:NETBOX_HOST = 'localhost:8000'
$env:NETBOX_TOKEN = ''
Invoke-Pester ./Tests/Integration.Tests.ps1 -Tag 'Integration'
```

This is where Django 6.0 fallout will surface. Expect 1-5 unexpected failures in areas we don't normally think about:

  • Error-body parsing in ErrorHandling.Tests.ps1
  • Date/time round-trips in Get-NBCoreObjectChange
  • Any test that relies on a specific error-response shape

Treat each failure as a triage item — do not paper over with try/catch at the function level (see CLAUDE.md: error handling is centralized by design).

Phase 4 — Targeted new-feature tests

Once the suite is green, add integration tests for the 3 new models and the 5 enhancement areas (VM without cluster, ASN role, disabled module/device bays, adopt/replicate flags, changelog messages).

Phase 5 — New helper-layer features

Implement in this order (each as a separate PR):

  1. InvokeNetboxRequest ETag capture + -IfMatch passthrough + 412 error mapping → ~1 day, high test coverage on ErrorHandling.Tests.ps1.
  2. Cursor pagination (-Start param + version-gated -All optimization) → ~1 day, focused on BuildURIComponents and the -All loop.
  3. 12 new functions for VirtualMachineType, CableBundle, RackGroup → ~3 days, template-driven.
  4. Enhancement params (role, disabled, adopt/replicate, etc.) → ~2 days, mostly param additions + tests.
  5. Add-NBTag / Remove-NBTag convenience cmdlets (#21771) → ~0.5 day, opportunistic quality-of-life improvement.

3. Risks & open questions

  1. When will netbox-docker publish a 4.6-compatible image? If it's before we ship our compatibility branch, we throw away the custom Dockerfile and use the official image. We should watch the netbox-docker repo's develop branch VERSION file and switch as soon as it bumps.
  2. Does Django 6.0 require a Python version bump? If Netbox 4.6 raises Python minimum beyond what the existing base image ships, Option A Dockerfile fails and we must go straight to Option B.
  3. Will Cluster on New-NBVirtualMachine become optional in a backward-compatible way? Users who rely on PowerShell's [Parameter(Mandatory)] prompt will see behavior change. We should deprecate the old behavior with a Write-Warning in v4.6.0.0 and remove the mandatory marker in v4.6.1.0.
  4. ETag support surface area. Do we expose _etag on every returned object (adds a property everywhere) or only when the caller opts in via -IncludeMetadata? Decision deferred to the implementation PR.
  5. Should 4.6 support land on dev, or on a new beta branch? CLAUDE.md already describes a beta branch reserved for pre-release testing. This is exactly what it was designed for. Proposal: cherry-pick the compatibility branch to beta, run the custom-image matrix there, and merge to dev only when 4.6.0 stable releases.

4. Action items (sub-issues to open after this one)

  • Phase 0a: Build `Dockerfile.46beta` and verify it boots (Option A)
  • Phase 0b: Fork netbox-docker and produce a proper build if Option A fails (Option B fallback)
  • Phase 1: Run Verify-ValidateSetParity.ps1 -NetboxVersion v4.6.0-beta1 and triage findings
  • Phase 2: Add a 4.6 row to `.github/workflows/compatibility.yml` (gated on the custom image)
  • Phase 3: Run full integration suite, file bugs for Django 6.0 fallout
  • Phase 5.1: InvokeNetboxRequest ETag + -IfMatch + 412 error mapping
  • Phase 5.2: Cursor pagination (-Start + version-gated -All)
  • Phase 5.3: VirtualMachineType endpoint (4 functions)
  • Phase 5.4: CableBundle endpoint (4 functions)
  • Phase 5.5: RackGroup endpoint (4 functions + verify Rack.rack_group FK + VLANGroup.scope bump)
  • Phase 5.6: Enhancement params batch (VM-without-cluster, ASN role, bay disabled, component adopt/replicate, changelog_message on bulk creates)
  • Phase 5.7 (optional): Add-NBTag / Remove-NBTag cmdlets for #21771

5. Recommendations (the advice part)

  1. Don't rush to ship 4.6 support until the stable release. Beta1 is explicitly marked "no upgrade path to future releases" — building against beta1 means we may need to redo work if beta2/rc1 changes the schema. Use beta1 for discovery, not for delivery.
  2. Run Phase 1 (parity script) this week. It's free signal. Even without a 4.6 Docker image we can validate ValidateSet decorators against the 4.6 choices.py source.
  3. Reserve the beta branch for 4.6 work. CLAUDE.md already describes this pattern. Merge to dev only after 4.6.0 stable + all 94 integration tests green.
  4. The helper-layer work (Phase 5.1 and 5.2) is independently valuable. ETag support and cursor pagination can land on dev today against 4.5.7 — they're backward-compatible and version-gated. Treat them as standalone improvements rather than waiting for 4.6.
  5. Follow the proven ValidateSet drift process. The memory note says we've hit this bug class 5 times. PR tooling: Add Verify-ValidateSetParity.ps1 — automated ValidateSet drift detection #391 added the parity script specifically to catch the 6th. Run it on every compat bump and make it a CI gate once it proves stable.
  6. Plan for a 4.7 deprecation issue. When we open this issue, immediately open a companion tracking issue for 4.7 removals (event field username/request_id, housekeeping, legacy Sentry config, etc.). Future-you will thank present-you.

Analysis generated by Claude based on the v4.6.0-beta1 release notes, the current PowerNetbox 4.5.7 codebase, existing compatibility.yml workflow, and scripts/Verify-ValidateSetParity.ps1. Reviewed against netbox-docker's develop branch (VERSION=4.0.2 as of 2026-04-15).

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions