Skip to content

nd_interface_port_channel_*: member-already-in-use preflight - #548

Open
allenrobel wants to merge 1 commit into
developfrom
nd_interface_port_channel_member_preflight
Open

nd_interface_port_channel_*: member-already-in-use preflight#548
allenrobel wants to merge 1 commit into
developfrom
nd_interface_port_channel_member_preflight

Conversation

@allenrobel

Copy link
Copy Markdown
Collaborator

Related Issue(s)

Closes #369

Proposed Changes

Add a client-side member-already-in-use preflight to the port-channel orchestrators (nd_interface_port_channel_access, nd_interface_port_channel_trunk_host), per the design agreed in #369.

ND 4.2.1 rejects a port-channel create whose ports member already belongs to another port-channel with a flat HTTP 500 {code, message} envelope (no results[]) -- a permanent 409-class conflict dressed as a server error (vault: port-channel-member-conflict-returns-500). Today that surfaces mid-run as Create failed for <po>: ..., after any earlier interfaces in the same POST batch were already accepted. The module now fails fast with a validation error naming each offending member and its current owner, before any write, in check mode too.

  • PortChannelBaseOrchestrator.query_all now reads each switch's inventory through the shared NDBaseInterfaceOrchestrator._switch_interfaces cache (mirroring the ethernet/loopback/SVI orchestrators). No behavior change; it was the last orchestrator still issuing its own per-switch _request. This warms the cache with the unfiltered records during the state machine's initial query_all.
  • PortChannelBaseOrchestrator.preflight override: runs the inherited capability preflight, then _validate_members_available. Because it reads the same cache, it issues zero additional requests for merged/replaced/overridden.
    • Owners are derived from every portChannel record's ports list regardless of policy type, so a member of an unmanaged flavor (e.g. Ethernet1/2 owned by port-channel500/vpcPeerlinkPo) is still a conflict -- the filtered before view would miss it.
    • Membership is read from intent, not operData.portChannelId (which stays -1 until the owner is deployed; ND still rejects the conflict).
    • A member whose own policyType ends in Member but that no port-channel record lists is treated as owned (owner reported as unknown) rather than free.
    • A member already owned by the port-channel under management passes (idempotent re-apply). Two proposed port-channels on one switch claiming the same member also fail (ND would accept the first and 500 on the second).
    • Hard-fails in --check mode: unlike the capability preflight (unpublished endpoint), this data comes from the standard interfaces GET.
    • Intra-task re-home (remove from A, add to B in one config) stays out of scope per the issue and is reported as a conflict.
  • Workaround site carries TODO(4.2.1) port-channel-member-conflict-returns-500.
  • Module description gets one line stating the fail-fast behavior; changelog fragment added.
  • Not done: the optional "aside" in nd_interface_port_channel_*: add member-already-in-use preflight (convert ND's opaque 500 to fail-fast validation) #369 about wiring the capability interface_type/interface_mode ClassVars for port-channels.

Test Notes

  • New unit tests in tests/unit/module_utils/orchestrators/test_port_channel_access_interface.py (00900-00970): free member passes; cross-PO conflict aggregates every offender naming member + owner; cross-type (vpcPeerlinkPo) owner conflicts; same-PO re-apply passes (case-insensitive); check mode still fails; intra-task duplicate claim fails; query_all then preflight consumes no extra response (generator exhaustion would raise) and the cache retains the unfiltered inventory; orphan *Member policy type fails. test_port_channel_trunk_host_interface.py 00900-00910 confirm inheritance.
  • Full unit suite: 4205 passed (ndpytest tests/unit/).
  • ndtest --test pylint / --test validate-modules on the changed plugin files pass; ndblack, ndisort clean; ndmypy error count on port_channel_base.py unchanged from develop (pre-existing NDBaseModel attr-defined pattern noted at the top of the file).
  • Not lab-run: the conflict shape itself was lab-verified on 2026-06-30 (issue body / vault note); the preflight is exercised against that recorded shape in unit tests.

Cisco Nexus Dashboard Version

4.2.1

Related ND API Resource Category

  • analyze
  • infra
  • manage
  • onemanage
  • other

Checklist

  • Latest commit is rebased from develop with merge conflicts resolved
  • New or updates to documentation has been made accordingly
  • Assigned the proper reviewers

🤖 Generated with Claude Code

https://claude.ai/code/session_012LpZPyFp9vCBb1mH97CXj4

@allenrobel allenrobel added the ready for review Submitter is requesting a PR review label Sep 2, 2026
@allenrobel allenrobel self-assigned this Sep 2, 2026
Reject, before any write, a port-channel whose member ethernet is already
owned by a different port-channel. ND 4.2.1 rejects this at create with a
flat HTTP 500 {code, message} envelope (vault:
port-channel-member-conflict-returns-500) that surfaced mid-run as
"Create failed for <po>"; the module now fails fast with a validation
error naming each member and its current owner, in check mode too.

- PortChannelBaseOrchestrator.query_all reads the per-switch inventory
  through the shared _switch_interfaces cache (no behavior change), so
  the preflight reads the unfiltered records the state machine's initial
  query_all already fetched: zero extra requests.
- preflight() override: capability preflight, then
  _validate_members_available. Owners come from every portChannel
  record's `ports` list regardless of policy type (a vpcPeerlinkPo owner
  is still a conflict), with a *Member policyType fallback for members
  no port-channel record lists. Same-PO ownership passes (idempotent
  re-apply); two proposed port-channels claiming the same member on one
  switch also fail.
- Module docs note the behavior; changelog fragment added.
- Unit tests: access 00900-00970 (free / cross-PO / cross-type /
  idempotent / check mode / intra-task duplicate / no extra request /
  orphan *Member), trunk_host 00900-00910.

Closes #369

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012LpZPyFp9vCBb1mH97CXj4
@allenrobel
allenrobel force-pushed the nd_interface_port_channel_member_preflight branch from eec96e8 to 740089d Compare September 2, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review Submitter is requesting a PR review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nd_interface_port_channel_*: add member-already-in-use preflight (convert ND's opaque 500 to fail-fast validation)

1 participant