Conversation
b953cba to
80062f3
Compare
80062f3 to
d5d7788
Compare
d5d7788 to
6555e0b
Compare
7fe5f9a to
ab6a616
Compare
nikhilsrikrishna
left a comment
There was a problem hiding this comment.
Post-write verification after a partial bulk failure
One scenario that may be worth considering is a bulk create for resources A, B, and C with verify.enabled: true.
As I understand the current flow, _manage_create_update_state() first builds the complete predicted state:
predicted after = [A, B, C]
The controller could then return a mixed 207 Multi-Status response:
A: success
B: failure
C: successThe response handling correctly detects the failure. However, _request() raises, the error is propagated as an NDStateMachineError, and execution moves directly to the module’s exception handler. This means finalize_result() is not reached, so the optional readback does not run.
The failure response may therefore contain:
changed: true
after:
- A
- B
- Cwhile the controller actually contains only A and C.
The reverse can happen for bulk deletes: if A and C are deleted but B fails, after may still contain all three because delete_many() is not reached.
The state machine also reads module.params.get("ignore_errors", False), but the production argument specifications do not currently expose this option. Ansible’s task-level ignore_errors: true only allows the playbook to continue after the module fails; it does not suppress the exception inside the state machine or allow finalization to run.
Would it make sense to make the final readback boundary reachable after an attempted mutation fails as well? When verification is enabled, it could requery the controller to correct after while preserving the original mutation error. If the readback also fails, the mutation error could remain the primary error and after would remain predictive rather than being presented as controller-confirmed.
ab6a616 to
579e306
Compare
Integrate nd_interface_ethernet_routed with the shared verification contract and finalize its result after pending interface actions. Update the routed module tests to verify successful finalization while preserving the existing failure-path behavior.
579e306 to
95c536d
Compare
allenrobel
left a comment
There was a problem hiding this comment.
Code review
Two comments inline: one on the verify.retries deprecation shim in nd_manage_vpc_pair, and one question about how a failed verification readback is reported. Neither overlaps the points nikhilsrikrishna already raised.
Note for whoever rebases: a trial merge against current develop conflicts only in tests/unit/module_utils/orchestrators/test_base_interface.py; the source files auto-merge, and finalize_result() lands after deploy_pending() so it composes correctly with the #547 failure-path finalizer.
🤖 Generated with Claude Code
| def _handle_verify_compatibility(module: AnsibleModule, raw_module_args: dict[str, Any]) -> None: | ||
| """Reject ambiguous aliases and warn for released vPC verification fields.""" | ||
| raw_verify = raw_module_args.get("verify") | ||
| if not isinstance(raw_verify, dict): | ||
| return | ||
| if "attempts" in raw_verify and "retries" in raw_verify: | ||
| module.fail_json(msg="verify.attempts and verify.retries cannot be used together") | ||
| if "retries" in raw_verify: | ||
| module.deprecate( | ||
| "verify.retries is deprecated; use verify.attempts instead", | ||
| version="2.0.0", | ||
| collection_name="cisco.nd", | ||
| ) | ||
| normalized_verify = getattr(module, "params", {}).get("verify") | ||
| if isinstance(normalized_verify, dict): | ||
| normalized_verify["attempts"] = normalized_verify.get("retries", raw_verify["retries"]) | ||
| if "timeout" in raw_verify: | ||
| module.deprecate( | ||
| "verify.timeout is deprecated and is retained only for existing vPC query helpers", | ||
| version="2.0.0", | ||
| collection_name="cisco.nd", | ||
| ) |
There was a problem hiding this comment.
verify.retries has never shipped: nd_manage_vpc_pair is not in the v1.5.0 tag (git ls-tree v1.5.0 -- plugins/modules/nd_manage_vpc_pair.py is empty) and galaxy.yml is still 1.5.0, so the module and this option first appear in 2.0.0. Deprecating it with version="2.0.0" announces removal in the same release it is introduced.
Since no user has the old spelling, I think this whole shim can go: the retries alias + deprecate() calls here, the retries field/argspec entry in vpc_pair_model.py, and the retries fallback in common.get_verify_settings. A plain rename to attempts/interval would leave one code path instead of three.
Minor, only if the shim stays: the module.params["verify"]["attempts"] = ... mutation on lines 486-489 is never executed by the unit tests in test_manage_vpc_pair_verify.py, because _CompatibilityModule has no params attribute, so getattr(module, "params", {}) short-circuits.
| try: | ||
| response_data = self.model_orchestrator.query_final_state(context, policy) | ||
| refreshed = NDConfigCollection.from_api_response(response_data=response_data, model_class=self.model_class) | ||
| except Exception as error: | ||
| self._finalized = True | ||
| raise NDStateMachineError(f"Failed to refresh final after-state from controller: {error}") from error |
There was a problem hiding this comment.
Question on the failure semantics here. By the time finalize_result() runs, every create/update/delete, remove, deploy and save has already succeeded. If only the readback fails (retry policy exhausted on a transient 5xx, or from_api_response rejecting an unexpected shape), this raises NDStateMachineError, the same type a real mutation failure raises, so the module reports failed: true for a task whose configuration fully landed. A playbook cannot tell "config landed, confirmation failed" from "config never landed", and the doc fragment does not mention this outcome.
Was module.warn(...) plus the predicted after (and changed: true) considered for this case, reserving failed for the mutation path? If failing hard is the intended contract for verify.enabled, a sentence in doc_fragments/verification.py saying so would help users set attempts/interval accordingly.
Related Issue(s)
Fixes #385
Proposed Changes
verify.enabledverify.attemptsverify.intervalretriesandtimeout.RestSendretry policy with:FinalizationContextandfinalize_result()as the explicit finalization boundary after all write, action, save, and deploy operations.changedand replaceafteratomically only after a complete successful readback.Related PRs and Merge Order Sugestion
This PR overlaps several open shared-core and module-specific changes:
config_actionsframework and overlapsnd_argument_specs.config_actionshandling and overlaps interface orchestrators, module call sites, and lifecycle tests.NDStateMachineand result handling.Recommended conflict-minimizing merge order:
config_actionsfoundation.PR #525 is a design-reconciliation dependency, not currently a required merge predecessor. If its result contract is accepted first, this PR must instead rebase and populate those canonical fields rather than adding a competing schema.
Test Notes
418 passed4246 passedansible-test sanity: passedCisco Nexus Dashboard Version
ND v4.2+
Related ND API Resource Category
Checklist