Skip to content

fix(topology): stop duplicated hypervisor CT chips (slice aliasing + PVE dedup + guest pinning) - #1379

Merged
gnacho merged 2 commits into
mainfrom
fix/1378-pve-ct-dup
Oct 12, 2026
Merged

gnacho merged 2 commits into
mainfrom
fix/1378-pve-ct-dup

Conversation

@gnacho

@gnacho gnacho commented Oct 12, 2026

Copy link
Copy Markdown
Owner

Bug (#1378)

On the topology map, a Proxmox host's CT/VM chips rendered duplicated, and during drags guests appeared stuck with ghost copies until the layout was saved.

Root causes (three, two benign-looking but real)

  1. Slice aliasing in the topology: inferred distribution node shadows a managed switch (map shows gateway -> blob -> switch instead of a direct link) #1374 absorption (the actual cause of the live duplicates): the managed-switch-shadow absorption filtered dists in place (kept := dists[:0]), rewriting the shared backing array while the caller kept its original length. The last element (dist-pve-default-citadel-02) appeared twice in /api/overview#distributionNodes, and the frontend drew that host's CT grid twice (verified live: two identical distnodes in the API, two chip copies in the DOM).
  2. PVE inventory keys resolving to the same host (defense in depth): two inventory keys (repeated IP, or IP+name match) could emit the same hypervisor distnode twice; the loop now marks the host as emitted.
  3. Guest pinning with stale saved positions: a CT with its own saved layout position and a host without one was painted twice (host-derived grid chip + pinned seed). Hypervisor guests now never pin by their own seed; they always follow the host grid.

Verification

  • New tests: PVE duplicate-inventory-key case; absorption keeps the caller's slice intact and exposes TopoSemantics.AbsorbedDists; live.go filters with a fresh slice.
  • Full suite 48/48.
  • Live E2E (Playwright against the preview): one chip per CT, dragging a hypervisor moves its guests with no ghost copies, no cards open in edit mode.

Closes #1378

Two duplication paths for Proxmox guest chips:

- Server: applyPVEInfra emitted the hypervisor distnode once per
  inventory key, and two keys can resolve to the same host (repeated IP
  or IP+name match), so citadel-02's distnode (and its CT grid) went out
  twice. Mark the host as emitted inside the loop.
- Frontend lock-layout: a CT with its own saved position and a host
  without a seed was painted twice (host-derived grid chip + pinned
  seed). Hypervisor guests now never pin by their own seed; they always
  follow the host grid.

References #1378
… element (#1378)

The real cause of the duplicated hypervisor CTs: the #1374 absorption
filtered dists in place (kept := dists[:0]), rewriting the shared
backing array while the caller kept its original length - the last
element (dist-pve-default-citadel-02) showed up twice in the overview,
and the frontend drew that host's CT grid twice.

- Absorption now builds a fresh kept slice and exposes the absorbed ids
  via TopoSemantics.AbsorbedDists.
- live.go filters its distNodes with a fresh slice using those ids.
- Test asserts the caller's slice stays intact.

The PVE-side hardening and the frontend no-pin-for-guests fix from the
previous commit stay (both real, but neither was the cause).
@gnacho
gnacho merged commit 518d618 into main Oct 12, 2026
2 checks passed
@gnacho
gnacho deleted the fix/1378-pve-ct-dup branch October 12, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

topology map edit mode: dragging a Proxmox hypervisor leaves its CTs behind, duplicates them until save, and shows CT chip cards

1 participant