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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -165,6 +165,36 @@ func TestBuildBringUpConfig_ResourceCaps(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// R-50: with the island configured, bring-up attaches a static net1 on the island bridge; with it
|
||||
// unset (or half-set), NO net1 is emitted — byte-for-byte the pre-R-50 config on non-island hosts.
|
||||
// Pure-function check on buildBringUpConfig (the derivation that makes fresh installs F1-immune).
|
||||
func TestBuildBringUpConfig_IslandNIC(t *testing.T) {
|
||||
// island set → net1 present, exact shape, no hwaddr (PVE mints a fresh per-guest MAC)
|
||||
island := buildBringUpConfig(BringUpSpec{
|
||||
Mode: ModeProvision, IslandBridge: "vmbr9", IslandGuestAddr: "169.254.253.2/30",
|
||||
}, scratchCfg())
|
||||
if got, want := island["net1"], "name=eth1,bridge=vmbr9,ip=169.254.253.2/30"; got != want {
|
||||
t.Errorf("island net1 mismatch:\n got %q\nwant %q", got, want)
|
||||
}
|
||||
// DR mode too — a restored customer guest must also reach the island-bound agent on the host.
|
||||
dr := buildBringUpConfig(BringUpSpec{
|
||||
Mode: ModeDRGuestLoss, KeepMAC: true, IslandBridge: "vmbr9", IslandGuestAddr: "169.254.253.2/30",
|
||||
}, scratchCfg())
|
||||
if _, ok := dr["net1"]; !ok {
|
||||
t.Errorf("DR bring-up must also attach the island net1, got none")
|
||||
}
|
||||
// island unset → NO net1 key (non-island hosts unchanged; the pre-R-50 default)
|
||||
none := buildBringUpConfig(BringUpSpec{Mode: ModeProvision}, scratchCfg())
|
||||
if v, ok := none["net1"]; ok {
|
||||
t.Errorf("net1 must be ABSENT when the island is not configured, got %q", v)
|
||||
}
|
||||
// half-configured (bridge only) → still no net1 (all-or-nothing; config.Validate rejects the config too)
|
||||
half := buildBringUpConfig(BringUpSpec{Mode: ModeProvision, IslandBridge: "vmbr9"}, scratchCfg())
|
||||
if v, ok := half["net1"]; ok {
|
||||
t.Errorf("net1 must be ABSENT when only the bridge is set, got %q", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Both restore sites allocate the guest INTO the felhom pool (SPIKE 3b): the provision bring-up
|
||||
// threads spec.Pool, and the restore-test hardcodes DefaultPool — else a pool-scoped token 403s on
|
||||
// the created guest's config/start/destroy. Asserts via the fakeAPI's captured RestoreLXCOptions.
|
||||
|
||||
Reference in New Issue
Block a user