# NetBox imports -- v1 (IPv4-only) and VR1 DC-DC (multi-DC, dual-stack) scope

This directory contains the NetBox import scripts for both the original v1
(IPv4-only, single-site) deployment and the VR1 DC-DC (two-DC, dual-stack)
buildout. Each script is idempotent; re-running is safe.

> **DOCFIX-195 (operator ruling 2026-07-15) -- TARGET NetBox during VR1.** The VR1 IPAM apex is
> `office1-netbox` (`http://10.10.1.10:8000`) -- a working draft, edited as the buildout proceeds, and
> the NetBox every consuming system (OpenTofu, MAAS, overlays, bundle) derives its values from. Point
> the importers there (it is in `SANDBOX_HOSTS`, so the writes are unguarded). `netbox.baldurkeep.com` is held as a READ-ONLY v1 reference draft and takes
> NO write during VR1 -- the `NETBOX_URL=https://netbox.baldurkeep.com` shown in the examples below is
> for the DEFERRED end-of-deployment merge-back ONLY, and each apex write additionally requires
> `--yes-write-upstream`. See `docs/dc-dc-netbox-buildout-scope.md` section 8.

## VR1 DC-DC scripts (2026-07-09, closes tooling gap register #3)

### `dc-dc-prefixes-import.py`

Extends the v1 single-site IPv4-only pattern to VR1's two-DC, dual-stack
model (D-101, `docs/design-decisions.md`). Per DC (`--dc vr1-dc0` or `--dc vr1-dc1`):
creates the six-plane IPv4 structure plus the D-101 family-matrix IPv6 legs
(ULA for metal-admin/metal-internal/data-tenant/storage/replication, GUA for
provider-public).

**Nothing is invented.** D-101 names three literals as NetBox-assigned and
explicitly "NOT hardcoded in this decision": the org ULA /48, the per-DC GUA
carve out of the real ARIN 2602:f3e2::/32 block, and DC2's v4 supernet. All
three are REQUIRED environment variables with no defaults --
`ORG_ULA_48`, `DC_GUA_PREFIX`, and (for `--dc vr1-dc1` only) `VR1_DC1_V4_SUPERNET`.
The script validates format/containment and FAILS LOUD if any is missing or
malformed; it never silently substitutes DC1's values for DC2 or invents a
/48. DC1's v4 planes ARE hardcoded here, deliberately -- D-101 rules DC1
inherits DC0's layout unchanged, so those six CIDRs are the ADOPTED decision
text itself, not an inferred value.

The exact per-plane subdivision of the ULA /48 and the GUA carve (which /64
within the /56/whatever goes to which plane) is this script's OWN PROPOSED
scheme -- documented in the script's module docstring -- and has NOT itself
been operator-ratified as a D-NNN sub-decision. Review it before trusting
what this script would write to a real NetBox.

```
NETBOX_URL=https://netbox.baldurkeep.com NETBOX_TOKEN=<token> \
    ORG_ULA_48=fd00:1234:5678::/48 DC_GUA_PREFIX=2602:f3e2:1000::/40 \
    python3 dc-dc-prefixes-import.py --dc vr1-dc0

NETBOX_URL=... NETBOX_TOKEN=... \
    ORG_ULA_48=fd00:1234:5678::/48 DC_GUA_PREFIX=2602:f3e2:2000::/40 \
    VR1_DC1_V4_SUPERNET=10.13.0.0/19 \
    python3 dc-dc-prefixes-import.py --dc vr1-dc1
```

**Role slugs used here are NEW** (`provider-public`/`metal-admin`/
`metal-internal`/`data-tenant`/`storage`/`replication`, matching the CURRENT
six-plane model in `scripts/lib-net.sh`) -- NOT the same slugs as
`ipv4-prefixes-import.py` below, which predates the D-052/D-053 six-plane
cutover. This staleness in the v1 script is flagged, not fixed, here (out of
scope -- that script is a v1/DC0 artifact this delivery doesn't touch).
These roles must exist in NetBox before running (create out of band; this
script does not create IPAM roles, same assumption the v1 scripts make).

**Site collision note:** DC1 may create prefixes numerically identical to
the existing `vr0-dc0` site's entries (D-101's inherit-unchanged ruling).
NetBox permits duplicate global prefixes (soft warning, not a hard block),
but whether to retire the `vr0-dc0` entries once DC1 supersedes that
rehearsal environment, or keep both for history, is an operator IPAM-hygiene
call this script does not make for you (it prints a note, does not decide).

**UNVALIDATED**: no live NetBox / `pynetbox`-reachable instance was
available this session (operator's workstation, not the vopenstack-jesse
jumphost) to run this end-to-end against a real server. Tested via
`tests/dc-dc-prefixes-import/` -- an in-memory fake NetBox client exercising
the full `main()` path for both DCs, idempotency, and every fail-loud case
(37/37 PASS) -- but a real NetBox run has not confirmed the actual API
shape/behavior.

Run order: after `ipv4-prefixes-import.py`/`ipv6-mark-reserved.py` are no
longer relevant to VR1 (they are v1/DC0-scoped), and before Stage 2/3 of
`docs/dc-dc-deployment-workflow.md` (which cite this pipeline's literals).

## v1 (IPv4-only, single-site) scripts

### `ipv4-prefixes-import.py`

Adds the IPv4 prefixes required for v1:

- Metal `/22` (10.12.8.0/22)
- Provider `/22` (10.12.4.0/22) with Provider IP Ranges (FIP pool + API VIPs)
- LBaaS Mgmt `/22` (10.12.32.0/22)
- IPv4 tenant pool `/16` (default 10.20.0.0/16; configurable via
  `TENANT_POOL_CIDR` env var)

Run order: first.

```
NETBOX_URL=https://netbox.baldurkeep.com NETBOX_TOKEN=<token> \
    python3 ipv4-prefixes-import.py
```

### `ipv6-mark-reserved.py`

Marks existing IPv6 prefixes scoped to VR0 DC0 as **reservation status** per
**D-015** (v1/v2 fork). The IPv6 entries from earlier session work are NOT
deleted -- they're preserved to document v2 design intent without implying
they are active during v1.

Run order: second (after IPv4 prefixes are in place).

```
NETBOX_URL=https://netbox.baldurkeep.com NETBOX_TOKEN=<token> \
    python3 ipv6-mark-reserved.py
```

Use `--dry-run` to preview without changes. Use `--revert` only when v2
work begins (sets IPv6 prefixes back to active).

## Deferred to v2

### VLAN imports (`vlans-import.py` -- removed)

Per the v1/v2 fork session Q2, additional VR0 DC0-VLANs group entries
(VIDs 50, 200, 220, 221, 222, 230, 260, 270) are **not imported in v1**.

Rationale: MAAS currently uses untagged-per-fabric on the existing network
layout. Modeling additional VLAN entries in NetBox without corresponding
network-side VLAN tagging would be misleading documentation. The VID 240
(OS-Provider) entry imported during prior session work is sufficient for v1
since it pairs with the Provider /22 prefix.

When v2 work begins, the VLAN import script will be re-introduced under
the v2 design with VLAN tagging actually in play.

### IPv6 active prefixes

The IPv6 prefix entries previously imported (Provider /60 + sub-/64s, Metal
/60, Data /60, Storage /60, Replication /60, LBaaS Mgmt /60, OOB /60) are
**reserved** in NetBox via `ipv6-mark-reserved.py`. They become active
again under the planned `/60 -> /64` refactor and full re-import when v2
work begins.

## NetBox version

These scripts assume NetBox 4.x:

- Prefix scope: `scope_type="dcim.site"` + `scope_id=<id>` (not legacy `site=` field)
- Prefix read: `p.scope` (not `p.site`)
- Status field: `"reserved"` / `"active"` as lowercase choice slugs

If your NetBox is older, the scripts will need adjustment.

## Sequence for v1 deploy gate

1. `python3 ipv4-prefixes-import.py` -- succeeds; verification block clean
2. `python3 ipv6-mark-reserved.py` -- succeeds; all IPv6 prefixes now reserved
3. Bundle drafting can proceed; values traced to NetBox are now authoritative
