nd_fabric_update_group - ND 4.2 software update groups#285
Open
allenrobel wants to merge 5 commits into
Open
Conversation
bff5d88 to
921294d
Compare
d537edc to
25ce389
Compare
3c80477 to
67167cd
Compare
Add the nd_fabric_update_group module to manage Fabric Software Management update groups (the ND 4.2 successor to NDFC/ND 3.x image policies). Built on the Gen 3 architecture: FabricUpdateGroupModel, FabricUpdateGroupOrchestrator, smart endpoints, and NDStateMachine. Supports merged, replaced, overridden, and deleted states. - update_group_switches / installation_order_devices accept switch IPs or serial numbers; IPs are resolved to switchIds via FabricContext. - Two ND 4.2.1 wire workarounds: installationOrderDevices is silently dropped on write (excluded from the idempotency diff, TODO(4.2.1)); the ND-managed default group named "None" is filtered from query results. - Adds UpdateGroupNameMixin to endpoints/mixins.py. - 58 unit tests (model + orchestrator) and an integration test target. Note: this work is postponed pending an ND-side fix for a zero-switch "ghost group" defect that breaks idempotent reconciliation of overridden state. A bug has been filed with the ND developers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The group-centric updateGroups CRUD endpoints have no minimum-switch guard and cannot represent a zero-switch group, so reusing a switch between groups silently left an unreadable "ghost" group behind. Rebuild the write path on the ghost-safe softwareUpdatePlan/actions API: attachGroup creates groups and assigns switches, detachGroup removes switches (ND auto-deletes an emptied group), and group settings are applied via PUT built from a GET of the current group so it never moves membership. Add a force_created option so a group whose membership trips an ND pre-flight warning can be applied. Reads stay on the group-centric GET endpoints; the now-unused group-centric POST endpoint is removed. Verified live on ND 4.2.1: detachGroup last-switch auto-delete, PUT membership no-op, and attachGroup forceCreated semantics (a warning status means ND applied nothing, so it always fails the task). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert the FabricUpdateGroupModel annotations from typing.Optional / List / Dict to the PEP 604 / PEP 585 forms (X | None, list[X], dict) per the collection's type-annotation standard, and trim the now-unused typing imports. Annotation-only change; verified clean against ansible-test sanity (compile / import / pep8) on Python 3.8-3.13. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expose ND's fabric-wide softwareUpdatePlan "propose" action as a new top-level auto_assign option (roleBased / evenOdd), letting ND auto-generate update groups instead of defining them explicitly in config. The auto-assign path bypasses NDStateMachine (its per-group config-diff state model does not fit a single fabric-level action) and derives changed from a before/after query_all snapshot. auto_assign is mutually exclusive with config and valid only with state merged or overridden; check mode reports no change since the propose action cannot be previewed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
67167cd to
fbf128d
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds the new nd_fabric_update_group Ansible module for managing Cisco Nexus Dashboard 4.2 Fabric Software Management update groups, including group lifecycle, switch-centric attach/detach actions, auto-assignment, and tests.
Changes:
- Adds the module entry point, Pydantic model, orchestrator, endpoint definitions, and endpoint mixin for update groups.
- Implements switch-centric write behavior via
softwareUpdatePlan/actionswhile retaining group-centric reads/settings updates. - Adds unit and integration coverage for models, endpoints, orchestrator behavior, state handling, and auto-assign workflows.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
plugins/modules/nd_fabric_update_group.py |
Adds the Ansible module and auto-assign execution path. |
plugins/module_utils/orchestrators/fabric_update_group.py |
Implements update group CRUD orchestration and switch ID/IP normalization. |
plugins/module_utils/models/fabric_update_group/fabric_update_group.py |
Adds the update group Pydantic model and module argument spec. |
plugins/module_utils/models/fabric_update_group/__init__.py |
Adds model package initialization. |
plugins/module_utils/endpoints/v1/manage/fabric_update_group.py |
Adds group-centric GET/PUT/DELETE endpoint classes. |
plugins/module_utils/endpoints/v1/manage/software_update_plan_actions.py |
Adds attach/detach/propose action endpoint classes. |
plugins/module_utils/endpoints/mixins.py |
Adds UpdateGroupNameMixin. |
tests/unit/module_utils/models/test_fabric_update_group.py |
Adds model/unit argument-spec tests. |
tests/unit/module_utils/orchestrators/test_fabric_update_group.py |
Adds orchestrator unit tests. |
tests/unit/module_utils/endpoints/test_software_update_plan_actions.py |
Adds action endpoint tests. |
tests/unit/module_utils/fixtures/fixture_data/test_fabric_update_group.json |
Adds orchestrator response fixtures. |
tests/integration/targets/nd_fabric_update_group/tasks/main.yaml |
Adds integration test entrypoint. |
tests/integration/targets/nd_fabric_update_group/tasks/setup.yaml |
Adds integration cleanup setup. |
tests/integration/targets/nd_fabric_update_group/tasks/merged.yaml |
Adds merged-state integration tests. |
tests/integration/targets/nd_fabric_update_group/tasks/replaced.yaml |
Adds replaced-state integration tests. |
tests/integration/targets/nd_fabric_update_group/tasks/overridden.yaml |
Adds overridden-state integration tests. |
tests/integration/targets/nd_fabric_update_group/tasks/deleted.yaml |
Adds deleted-state integration tests. |
tests/integration/targets/nd_fabric_update_group/tasks/auto_assign.yaml |
Adds auto-assign integration tests. |
tests/integration/targets/nd_fabric_update_group/vars/main.yaml |
Adds integration variables and sample group configs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+446
to
+470
| Update a fabric update group: reconcile membership against the current wire state | ||
| (`attachGroup` for added switches, `detachGroup` for removed switches), then apply settings | ||
| via PUT. | ||
|
|
||
| ## Raises | ||
|
|
||
| ### RuntimeError | ||
|
|
||
| - If `update_group_switches` resolves to an empty set (an empty update group is not permitted - | ||
| use `state: deleted`), a switch IP cannot be resolved, a request fails, or an action | ||
| endpoint reports a non-success status. | ||
| """ | ||
| try: | ||
| update_group_name = model_instance.update_group_name | ||
| current_raw = self._get_group_raw(update_group_name) | ||
| current_ids = list(current_raw.get("updateGroupSwitches") or []) | ||
| desired_ids = [self._resolve_switch_id(s) for s in (model_instance.update_group_switches or [])] | ||
| if not desired_ids: | ||
| raise RuntimeError("update_group_switches must be non-empty; an empty update group is not permitted (use state: deleted)") | ||
| to_add = [s for s in desired_ids if s not in current_ids] | ||
| to_remove = [s for s in current_ids if s not in desired_ids] | ||
| if to_add: | ||
| self._attach([self._attach_item(model_instance, switch_ids=to_add)]) | ||
| if to_remove: | ||
| self._detach([{"updateGroupName": update_group_name, "switchIds": to_remove}]) |
Comment on lines
+349
to
+352
| try: | ||
| return self._get_group_raw(update_group_name) | ||
| except Exception: # pylint: disable=broad-except | ||
| return None |
Comment on lines
+69
to
+72
| segments = ["fabrics", self.fabric_name, "updateGroups"] | ||
| if self.update_group_name is not None: | ||
| segments.append(self.update_group_name) | ||
| return BasePath.path(*segments) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Draft — stacked on
nd_interface_loopback(the PR base branch).Related Issue(s)
Proposed Changes
nd_fabric_update_group— manages Fabric Software Management update groups (the ND 4.2 successor to ND 3.x image policies) within a fabric.FabricUpdateGroupModel,FabricUpdateGroupOrchestrator, smart endpoints, andNDStateMachinewiring.merged,replaced,overridden,deleted.softwareUpdatePlan/actionsAPI, which is ghost-safe by construction:attachGroupcreates a group and assigns its switches (requires at least one switch).detachGroupremoves switches; ND auto-deletes a group once its last switch is removed.PUT /updateGroups/{name}, built by overlaying the user's fields onto a GET of the current group, so the PUT echoes the live membership and never moves switches.force_createdper-group option — ND raises a pre-flight warning when a group's switch set would cover all switches of a role;force_created: trueacknowledges the warning so the change is applied. A warning otherwise fails the task, since ND applies nothing when it warns.query_one/query_all, used byoverriddenreconciliation) stay on the group-centricGETendpoints.endpoints/v1/manage/software_update_plan_actions.py(attachGroup/detachGroup); the now-unused group-centricPOSTendpoint is removed.update_group_switches/installation_order_devicesaccept either switch IP addresses or serial numbers (switchIds); IPs are resolved to switchIds viaFabricContext.installationOrderDevicesis silently dropped by ND on write — excluded from the idempotency diff (TODO(4.2.1)marker). ND's system-managed default group (literally named"None") is filtered out of query results sooverriddendoes not try to delete it.Background — the "ghost group" defect this design sidesteps
The group-centric
updateGroupsCRUD endpoints have no minimum-switch guard and cannot represent a zero-switch group: reusing a switch between groups silently drained a group and left it an unreadable "ghost" (omitted from the list, HTTP 400 on the single-group GET, name still reserved). The switch-centric action API used here is ghost-safe by construction —attachGrouprequires at least one switch anddetachGroupcleans up emptied groups — so the module no longer depends on an ND-side fix. This module was previously postponed on that defect; the redesign removes the block.Test Notes
black/isort/mypyclean;ansible-test sanity --dockerpasses.detachGrouplast-switch auto-delete,PUTwith current membership is a membership no-op, andattachGroupforceCreatedsemantics (awarningstatus means ND applied nothing).tests/integration/targets/nd_fabric_update_group/—merged,replaced,overridden,deletedall pass against live ND 4.2.1.Cisco Nexus Dashboard Version
4.2.1
Related ND API Resource Category
Checklist