Skip to content

nd_maintenance_mode#284

Open
allenrobel wants to merge 2 commits into
developfrom
nd_maintenance_mode
Open

nd_maintenance_mode#284
allenrobel wants to merge 2 commits into
developfrom
nd_maintenance_mode

Conversation

@allenrobel
Copy link
Copy Markdown
Collaborator

Related Issue(s)

Proposed Changes

Add cisco.nd.nd_maintenance_mode, a new Ansible module that sets the system mode (normal or maintenance) of switches in a fabric via POST /api/v1/manage/fabrics/{fabric_name}/switchActions/changeSystemMode.

The endpoint is action-shaped — one POST mutates a batch of switches with a single mode. The module adapts that to NDStateMachine's CRUD-shaped driver via an identifier_strategy="singleton" model and a config-dict-to-list wrap in main(). Idempotency reads additionalData.intendedSystemMode per switch and only POSTs switches whose intent differs from the request. Any switch currently in migration mode triggers a hard-fail with a remediation message asking the operator to run 'Recalculate and Deploy' in the ND UI before retrying. The 207 response body is inspected per-item; any failed entry surfaces as a RuntimeError naming the offending switches by IP.

State support is merged only for now. The collection-wide query state is retired; gathered will be added once the underlying NDStateMachine/NDOutput framework support lands (the orchestrator's query_all already produces the per-switch snapshot it will need).

Two TODO(4.2.1) workaround markers flag observed ND wire/schema divergences:

  • GET /fabrics/{f}/switches/{sid} nests systemMode under additionalData, not at the top level the OpenAPI schema documents.
  • The aggregator additionalData.systemMode returns "inconsistent" (a value not in the OpenAPI enum) between an intent change and a deploy, so idempotency must compare against intendedSystemMode, not systemMode.

New / modified files:

  • plugins/modules/nd_maintenance_mode.py — module entry point (DOCUMENTATION / EXAMPLES / argspec / main()).
  • plugins/module_utils/orchestrators/maintenance_mode.pyMaintenanceModeOrchestrator inheriting NDBaseInterfaceOrchestrator for FabricContext plumbing.
  • plugins/module_utils/models/maintenance_mode/{__init__.py, maintenance_mode.py} — singleton model + per-switch get_diff.
  • plugins/module_utils/endpoints/v1/manage/manage_fabrics_switch_actions.py — new endpoint module for changeSystemMode.
  • plugins/module_utils/endpoints/v1/manage/manage_switches.py — added EpManageSwitchesGet for the per-switch GET used by the query_all fan-out.
  • Unit tests: endpoint (8), model (15), orchestrator (20).
  • Integration target nd_maintenance_mode with YAML-anchored single-switch, multi-switch, partial-mismatch, negative, and check_mode scenarios.

Test Notes

  • Unit tests: source env && python -m pytest tests/unit/module_utils/ — 485/485 pass (442 prior + 43 new, zero regressions). Coverage: orchestrator 93%, model 97%, endpoint 97%.
  • Lint: black, isort, pylint, mypy all clean against pyproject.toml. pylint 9.97/10 on tests, 9.75/10 on production. yamllint clean on integration target.
  • Integration: Verified live on ND 4.2.1.10 @ 192.168.7.7 / SITE1 against switches S1_BG1 (192.168.12.131) and S1_SP1 (192.168.12.141). All 40 tasks in the target passed (ok=40 changed=13 failed=0 ignored=2, where the two ignored are the expected-failure negative tests). Lab state verified back to intendedSystemMode=normal for all test switches after the run.
  • Smoke test: Eight standalone scenarios (baseline, set maintenance, idempotent, restore, idempotent, negative switch_ip, negative fabric, check_mode) all green prior to writing the integration target.
  • The radius auth domain on this lab is effectively read-only on SITE1/SITE2; live tests use -e ansible_httpapi_login_domain=local. Documented in the integration target's main.yaml.

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

Note: stacked PR — base is nd_interface_loopback (per .stack.maintenance_mode.json: develop -> nd_interface_loopback -> nd_maintenance_mode).

🤖 Generated with Claude Code

@allenrobel allenrobel changed the title Add nd_maintenance_mode module nd_maintenance_mode May 18, 2026
@allenrobel allenrobel self-assigned this May 18, 2026
@allenrobel allenrobel force-pushed the nd_interface_loopback branch from bff5d88 to 921294d Compare May 20, 2026 21:23
@allenrobel allenrobel force-pushed the nd_maintenance_mode branch from 277a298 to fb922d2 Compare May 20, 2026 21:23
Base automatically changed from nd_interface_loopback to develop May 21, 2026 13:25
allenrobel and others added 2 commits May 21, 2026 09:00
Wraps POST /api/v1/manage/fabrics/{fabric_name}/switchActions/changeSystemMode
to set switches in a fabric to `normal` or `maintenance` mode. The endpoint is
action-shaped (one POST mutates a batch of switches with a single mode), and
the module adapts it to NDStateMachine's CRUD-shaped driver via an
identifier_strategy="singleton" model and a config-dict-to-list wrap in main().

Idempotency reads `additionalData.intendedSystemMode` per switch and only POSTs
switches whose intent differs from the request. Any switch currently in
`migration` mode triggers a hard-fail with the remediation instruction to run
'Recalculate and Deploy' in the ND UI before retrying. The 207 response body is
inspected per-item; any failed entry surfaces as a RuntimeError naming the
offending switches by IP.

State support: `merged` only. `gathered` will be added when the underlying
NDStateMachine/NDOutput support lands; query_all already produces the per-switch
snapshot it will need.

TODO(4.2.1) markers flag two ND wire/schema divergences observed on ND 4.2.1.10:
the GET switch response nests systemMode under `additionalData` rather than at
the top level the OpenAPI schema documents, and the aggregator
`additionalData.systemMode` returns "inconsistent" (a value not in the OpenAPI
enum) between an intent change and a deploy.

- New endpoint module manage_fabrics_switch_actions.py with
  ChangeSystemModeEndpointParams and EpManageFabricsSwitchActionsChangeSystemModePost
- EpManageSwitchesGet added to manage_switches.py for the per-switch GET
  used by the query_all fan-out
- Pydantic feature model MaintenanceModeModel + MaintenanceModeSwitchModel
  with custom per-switch get_diff
- MaintenanceModeOrchestrator inheriting NDBaseInterfaceOrchestrator for
  FabricContext plumbing and switch IP resolution
- 43 unit tests with 93-97% coverage on the new modules
- Integration target with YAML-anchored single-switch, multi-switch,
  partial-mismatch, negative, and check_mode scenarios; verified live on
  ND 4.2.1.10 / SITE1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified against the live ND OpenAPI spec at
/api/v1/manage/openAPISpec (Nexus Dashboard Manage v1 1.1.411): the wire
shape we read in `_fetch_switch_modes` matches the spec exactly. The
`switchData` schema declares `additionalData` as a $ref to `additionalType`
(a `oneOf` discriminated by `usage`), and the non-ACI variant
`additionalSwitchData` documents `systemMode`, `intendedSystemMode`, and
`discoveredSystemMode` with enums that include "inconsistent",
"waiting", and "notApplicable".

The earlier TODO marker was based on a stale snapshot of the spec served
by the local OpenAPI MCP server (1.1.209). There is no workaround here —
we are reading the fields exactly as documented. Replaced the marker with
a plain comment explaining the design choice (compare against
`intendedSystemMode`, not the `systemMode` aggregator) so future maintainers
understand the idempotency rationale without thinking we owe a removal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@allenrobel allenrobel force-pushed the nd_maintenance_mode branch from fb922d2 to 0bb6a39 Compare May 21, 2026 19:41
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