Skip to content

nd_interface_svi#268

Open
allenrobel wants to merge 14 commits into
nd_interface_ethernet_trunk_hostfrom
nd_interface_svi
Open

nd_interface_svi#268
allenrobel wants to merge 14 commits into
nd_interface_ethernet_trunk_hostfrom
nd_interface_svi

Conversation

@allenrobel
Copy link
Copy Markdown
Collaborator

@allenrobel allenrobel commented Apr 27, 2026

Related Issue(s)

N/A — new interface module in the interface module roadmap series.

Proposed Changes

Adds nd_interface_svi to manage SVI (switched virtual interface) configurations on Cisco Nexus Dashboard via the Manage Interfaces API.

  • plugins/module_utils/models/interfaces/svi_interface.pySviInterfaceModel plus nested SviPolicyModel, SviNetworkOSModel, SviConfigDataModel, SviOperDataModel. Composite identifier (switch_ip, interface_name) where interface_name is normalized to lowercase vlan<id>.
  • plugins/module_utils/orchestrators/svi_interface.pySviInterfaceOrchestrator inheriting from NDBaseInterfaceOrchestrator (not the ethernet base; SVIs have no port-channel concerns and support real DELETE via interfaceActions/remove + interfaceActions/deploy).
  • plugins/modules/nd_interface_svi.py — module entry point with vlan_ids: [333, 334] expansion to per-VLAN config items.
  • plugins/module_utils/models/interfaces/enums.py — adds SviPolicyTypeEnum.SVI = "svi".

Phase 1 exposes the policy fields the ND GUI sends on create: admin_state, description, ip/prefix, ipv6/v6prefix, ip_redirects, pim_sparse, pim_dr_priority, hsrp_group, hsrp_version, preempt, advertise_subnet_in_underlay, mtu, extra_config, netflow. OSPF / ISIS / BFD / proper HSRP sub-block modeling deferred to a follow-up release.

One SVI-specific deviation from the loopback / ethernet-access pattern, validated by Bruno testing against a live testbed:

  1. from_response() strips hsrpVersion. ND's GET returns hsrpVersion: 1 (integer) as a server-side default even when HSRP is unconfigured; re-emitting that on PUT — as either int 1 or string "1" — triggers a generic 500 ("unexpected error during policy execution"). hsrpGroup: 1 round-trips fine, so only hsrpVersion is poisoned. Proper HSRP sub-block modeling is deferred to phase 2.

Test Notes

Full create / update / query / idempotency / delete lifecycle verified against an ND 4.2 testbed (fabric_1, switch LE1 at 192.168.12.151, VLANs 333/334/335).

  • Unit tests: tests/unit/.../test_svi_interface*.py (commit 434b221).
  • Integration tests: tests/integration/targets/nd_interface_svi/ (commit f8b2223).
  • POST: 201 with single and multi-VLAN config items.
  • PUT: 200 on partial body (only description + policyType) — server merges.
  • GET: round-trips through model with oper_data populated.
  • Idempotency: second-run reports changed: false with empty diff.
  • DELETE: interfaceActions/remove (204) followed by interfaceActions/deploy confirmed via post-delete check_mode query (before: []).

Cisco Nexus Dashboard Version

4.2

Related ND API Resource Category

  • analyze
  • infra
  • manage
  • onemanage
  • other

Checklist

  • Latest commit is rebased from develop with merge conflicts resolved (this branch is stacked on nd_interface_ethernet_trunk_host; rebase will follow that branch's merge to develop)
  • New or updates to documentation has been made accordingly (module DOCUMENTATION + EXAMPLES populated)
  • Assigned the proper reviewers

@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 63772b4 to 4ef3a29 Compare April 28, 2026 01:43
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 4ef3a29 to c053c9d Compare April 28, 2026 20:25
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from c053c9d to bba7fdc Compare April 28, 2026 20:36
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from bba7fdc to a92738c Compare April 29, 2026 18:47
@allenrobel allenrobel force-pushed the nd_interface_svi branch 2 times, most recently from 35265ae to bc7600c Compare April 30, 2026 23:20
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from e1fd3d9 to 8c985eb Compare April 30, 2026 23:20
@allenrobel allenrobel marked this pull request as ready for review May 1, 2026 00:21
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from a9bf5c3 to a16b20c Compare May 4, 2026 03:44
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from a16b20c to d8d3870 Compare May 4, 2026 05:02
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from d8d3870 to 56fa96c Compare May 4, 2026 19:34
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 56fa96c to ac41b4b Compare May 5, 2026 19:44
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from ac41b4b to e80a7f8 Compare May 5, 2026 20:23
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 724ba60 to 567f385 Compare May 9, 2026 00:36
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 567f385 to 61e8706 Compare May 11, 2026 20:31
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 61e8706 to b115f74 Compare May 14, 2026 18:39
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from b115f74 to 3961059 Compare May 14, 2026 20:44
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 3961059 to 915bdc3 Compare May 20, 2026 21:23
@allenrobel allenrobel changed the title nd_interface_svi module nd_interface_svi May 20, 2026
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 915bdc3 to 5539d87 Compare May 21, 2026 19:41
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 5539d87 to 2e6c252 Compare May 22, 2026 00:26
@allenrobel allenrobel force-pushed the nd_interface_ethernet_trunk_host branch from 2e6c252 to c8a68ac Compare May 22, 2026 17:14
allenrobel and others added 14 commits May 22, 2026 11:21
Manages SVI interfaces (interfaceType: svi, policyType: svi) on Cisco
Nexus Dashboard via the Manage Interfaces API. Mirrors the loopback /
ethernet-access patterns: composite identifier (switch_ip, vlan_ids
expanded to vlan<id>), bulk POST per switch, partial PUT, real DELETE
followed by interfaceActions/deploy.

Phase 1 covers the policy fields the ND GUI sends on create:
admin_state, description, ip/prefix, ipv6/v6prefix, ip_redirects,
pim_sparse, pim_dr_priority, hsrp_group, hsrp_version, preempt,
advertise_subnet_in_underlay, mtu, extra_config, netflow.

Two SVI-specific deviations from the ethernet/loopback pattern:
- description rejects non-ASCII (Cisco backend 500s on UTF-8; bug filed)
- from_response strips hsrpVersion (GET-side server default of int 1
  poisons round-trip on PUT)

OSPF / ISIS / BFD / proper HSRP modeling deferred to phase 2.

Test notes: full create/update/query/idempotency/delete lifecycle
verified manually against ND 4.2 testbed (vlan333/334/335 on LE1 in
fabric_1). Unit tests and integration test target to follow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Model tests (57): defaults, field aliases, ASCII description validator
(8 parametrized cases), policy_type normalize/serialize, numeric range
validation (16 parametrized cases), description max_length, payload
camelCase serialization, NetworkOS / ConfigData / OperData containers,
interface_name normalization (7 parametrized cases for case + bare-int),
composite identifier, payload exclusion of switch_ip / oper_data,
from_response strip of hsrpVersion (and that hsrpGroup round-trips),
from_response idempotency on caller dict, robustness to missing nested
keys, full from_response/to_payload round trip, from_config equivalence,
argument_spec shape, interface_type default.

Orchestrator tests (12): model_class, bulk-support flags, query_all
filtering by interfaceType=svi AND policyType=svi (rejects ethernet and
underlaySvi), query_all fabric-not-found, query_one, create + deploy
queue, update + deploy queue, delete queues remove + deploy without
immediate API call, remove_pending and deploy_pending issuing the
respective interfaceActions endpoints, create_bulk grouping per switch,
canonical PUT payload shape (policyType discriminator, no switchId /
switchIp / operData / hsrpVersion).

Fixture file follows the existing pattern under fixtures/fixture_data/.
Full unit suite remains green: 567 passed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the inline `description_must_be_ascii` field validator with
the shared `AsciiDescription` Annotated type introduced in
nd_interface_loopback. Behavior identical (same error message, same
rejected character set); test docstring updated to reference the
shared validator path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirrors the trunk_host integration target structure with SVI-specific
adjustments:
- Uses vlan_ids: [333..338] instead of interface_names
- Reserves VLANs 333-338 for the test run; cleanup runs first
- Verifies the client-side ASCII validator rejects non-ASCII before any
  API call (em-dash test)
- DELETE assertions check that the SVI disappears from `after` (true
  delete via interfaceActions/remove + deploy), not "reset to defaults"
  like physical ethernet
- overridden assertions verify removed SVIs are gone (not at defaults)

Files:
- tasks/main.yaml — orchestration with optional logging env
- vars/main.yaml — base + updated SVI configs, cleanup helper
- tasks/merged.yaml — single create, fan-out, multi-config-group,
  idempotency, update + idempotency, fan-out update, ASCII validation,
  deploy: false, large fan-out + idempotency
- tasks/replaced.yaml — single replace + idempotency, fan-out replace
- tasks/overridden.yaml — reduce-set + idempotency, grow-set verifying
  removed SVIs disappear from after
- tasks/deleted.yaml — single delete + idempotency, fan-out delete,
  bulk teardown

Run with:
  ansible-test integration nd_interface_svi

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`FabricContext.validate_for_mutation` now also fetches
`/deploymentFreeze` after the fabric summary, so the orchestrator's
`query_all` consumes one extra response from the queue. Insert a
`deploymentFreeze: false` fixture between the summary and switch list
yields to keep the response generator aligned.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Expand SviPolicyModel to cover the full policyType:svi schema in the ND
Manage API: HSRP block (hsrp/hsrp_vip/hsrp_vipv6/hsrp_group/hsrp_groupv6/
hsrp_version/hsrp_priority/preempt/mac), DHCP relay (3 servers + per-server
VRFs), vrf_interface, routing_tag, netflow_monitor, and netflow_sampler.

Lab-verified the original phase-1 hsrpVersion 500 is fixed in current ND
builds, so SviInterfaceModel.from_response stripping is removed.
hsrp_version is now Literal[1, 2] per the OpenAPI schema (was wrongly typed
str). Fix prefixv6 alias (API rejects v6prefix with 400) and relax mtu
constraint to 68-9216 to match the int_vlan template.

Add a coerce-int-to-string validator for routing_tag to handle ND 4.2's
GET-side type drift (returns int despite OpenAPI declaring string); marked
TODO(ND 4.3) for removal once 4.2 is deprecated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per-policy interface modules (one module = one policyType) must not
expose policy_type as a user-facing argspec option. The
nd_interface_svi module targets the svi policy only, so policy_type
was dead surface area.

The model field already had a hardcoded default
(SviPolicyTypeEnum.SVI); the work here is to remove the user-facing
exposure (argspec entry, DOCUMENTATION block) and the no-op
normalize/serialize helpers that were kept "for symmetry" with
ethernet models.

- Remove policy_type from argspec and DOCUMENTATION
- Drop normalize_policy_type validator and serialize_policy_type
  field serializer (both were already no-ops since the API and
  Ansible names are both "svi")
- Strip policy_type from integration test inputs
- Update unit tests (72/72 pass)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace verbatim-repeated module argument blocks (check / normal / idempotent
runs of the same call) with YAML anchors. Removes a class of typo where one
of the duplicated blocks could drift from its siblings. Applied across all
five SVI task files (merged, replaced, overridden, deleted, hsrp_dhcp).

Anchors are defined at first use within each section to keep them local to
the reader.
Move the top-of-file SETUP block from merged.yaml into a dedicated
tasks/setup.yaml and call it from main.yaml before the state-test blocks.
Includes the post-cleanup DEBUG task alongside the cleanup since both are
file-level observability rather than test-specific.

Intra-test setup that's coupled to specific tests stays inline:
- vlan337 cleanup after the no-deploy test in merged.yaml
- The bulk-cleanup teardown at the bottom of deleted.yaml
- hsrp_dhcp.yaml's local SETUP and TEARDOWN — both are tightly coupled to
  the HSRP/DHCP-relay tests that file owns
Demonstrates the alternative to the structured hsrp_* fields: a raw NX-OS
hierarchical config block (preempt, priority, authentication, track)
applied through the extra_config literal-block scalar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each SVI is L3 with its own IP/VRF/HSRP identity, so the previous
ethernet-style fan-out (vlan_ids: [333, 334, 335] sharing one config_data
block) silently produced duplicate-IP-in-same-VRF configurations. Replace
with one config item per interface_name, matching the wire payload.

- Remove expand_config() from the module entry point; argument spec now
  requires interface_name (str) directly.
- Update DOCUMENTATION, EXAMPLES, and unit-test argspec assertion.
- Split test vars (svi_334_335, svi_337_338) into per-VLAN entries with
  unique IPs; replace cleanup_vlan_ids list-of-int with cleanup_config
  list-of-dict.
- Update merged/replaced/overridden/deleted/hsrp_dhcp/setup integration
  tasks to use interface_name; rename "fan-out" tests to "multi-SVI".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
validate_for_mutation reads freeze status from cached fabric_summary
(see fabric_context.py:166), so query_all no longer issues a separate
GET to /deploymentFreeze. The extra _freeze response was being
consumed by the switches request, returning a payload without the
"switches" key, so switch_map ended up empty and query_all returned [].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
interface_type, mode, and network_os_type were exposed as user options
but had no real choice for this module - they are fixed by scope (svi /
managed / nx-os) and only served to mirror the ND payload shape.
Removing them from get_argument_spec() collapses the user-visible config
to switch_ip + interface_name + the policy fields, with no documentation
burden or invalid-value footguns.

The fields stay in the Pydantic model with hardcoded defaults so
to_payload() / from_response() keep working; their types are narrowed to
Literal["svi"] / Literal["managed"] / Literal["nx-os"] so a stray API
value would surface as a validation error instead of silently round-
tripping. policy_type was already locked to SviPolicyTypeEnum.SVI.

Mirrors the loopback treatment in 56a45a8.

Also fixes a pre-existing YAML-parse bug in the existing notes block:
"C(policyType: vpcBackupSvi)" had a colon inside the C() macro that
broke yaml.safe_load on the DOCUMENTATION string. Reformatted to put the
field name outside C() and only the value inside, matching the loopback
note style.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
interface_type, mode, network_os_type, and policy_type are scope-fixed
for this module ("svi"/"managed"/"nx-os"/SviPolicyTypeEnum.SVI). The
Literal annotations plus NDBaseModel's validate_assignment=True already
block reassignment to a wrong value, but frozen=True also blocks
no-op reassignment to the same value, which more accurately expresses
the "hardcoded scaffolding" intent and protects against accidental
mutations that happen to round-trip the current value.

Pydantic uses construction (not assignment) for model_validate,
from_response, and model_copy(update=...), so frozen=True does not
interfere with the API round-trip or the merge/replaced/overridden
state-machine flows.

Reviewer-recommended pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant