v0.96.0 — R-50 island NIC: provision attaches the guest island net1

- LocalAPIConfig.island_bridge + island_guest_addr (+ IslandEnabled, Validate
  all-or-nothing + CIDR guard)
- buildBringUpConfig attaches static net1 (island) on provision + DR when set;
  absent otherwise (pre-R-50 byte-for-byte). Plumbed from cfg.LocalAPI at both
  RunBringUp sites. Endpoint already follows listen_addr (A0: no template change).
- healer stays eth0-only (A3 verify-only) — red-proof test locks the scoping
- example config + firewall example rewritten for the island; REUSE updated
- 3 non-hollow tests; full green. MinAgent unchanged.

Coupling: host-install island config requires agent >= 0.96.0 (vouch first).
This commit is contained in:
2026-07-25 14:16:23 +02:00
parent 36ed6594d4
commit dfd5d731ee
11 changed files with 223 additions and 68 deletions
+15
View File
@@ -193,6 +193,12 @@ type BringUpSpec struct {
Mounts []GuestMount // additive mpN mounts (slice 7 may pass empty/test)
KeepMAC bool // DR knob: keep the archived MAC (true) unless a source may be live
BootTimeout time.Duration // 0 → DefaultBootTimeout; bounds the link-up liveness wait
// IslandBridge + IslandGuestAddr (R-50): when BOTH are set, the guest gets a static net1 on the
// host-internal island bridge, so the controller reaches the agent over a fixed private address
// that survives any LAN/DHCP/site move (the F1 fix). Empty (default) = no net1, byte-for-byte the
// pre-R-50 config. Set from cfg.LocalAPI (island_bridge/island_guest_addr) at both call sites.
IslandBridge string // e.g. "vmbr9"
IslandGuestAddr string // guest net1 CIDR, e.g. "169.254.253.2/30"
}
// BringUpResult is the outcome. It reuses the restore-test's WARNINGS surface
@@ -606,6 +612,15 @@ func buildBringUpConfig(spec BringUpSpec, cfg proxmox.GuestConfig) map[string]st
params["net0"] = withoutHwaddr(net0) // omit hwaddr → PVE generates a fresh MAC (F1)
}
}
// R-50 island NIC: attach a static net1 on the host-internal bridge so the control plane
// (controller→agent local API) rides a fixed private address, immune to any LAN/DHCP/site move.
// Both modes: a provisioned guest AND a DR-restored guest need to reach the island-bound agent on
// the target host. No hwaddr → PVE mints a fresh per-guest MAC (the /30 is one guest per host, so
// a MAC would not collide either way, but a fresh one keeps net1 symmetric with net0). Additive:
// omitted entirely when the island is not configured, keeping non-island hosts unchanged.
if strings.TrimSpace(spec.IslandBridge) != "" && strings.TrimSpace(spec.IslandGuestAddr) != "" {
params["net1"] = fmt.Sprintf("name=eth1,bridge=%s,ip=%s", spec.IslandBridge, spec.IslandGuestAddr)
}
if spec.Mode == ModeProvision && spec.Hostname != "" {
params["hostname"] = spec.Hostname
}