Skip to content

Config Actions: Add Common Framework for Repository-Wide Save and Deploy Handling - #498

Open
AKDRG wants to merge 7 commits into
CiscoDevNet:developfrom
AKDRG:config_actions_common_framework
Open

Config Actions: Add Common Framework for Repository-Wide Save and Deploy Handling#498
AKDRG wants to merge 7 commits into
CiscoDevNet:developfrom
AKDRG:config_actions_common_framework

Conversation

@AKDRG

@AKDRG AKDRG commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Related Issue(s)

Related to #368

Proposed Changes

  • Add a repository-wide config_actions framework for parsing, validating, planning, and executing save/deploy intent.
  • Introduce policy-driven config action behavior for module families:
    • interface
    • fabric
    • switch
    • vPC pair
    • resource-oriented modules
    • legacy compatibility
  • Add shared support for:
    • defaulting omitted config_actions through module-family policies
    • rejecting explicit empty config_actions: {}
    • validating unsupported options and unsupported deploy types
    • enforcing deploy=true / save=false restrictions where required
    • read-only state handling for gathered workflows
    • resource-level deploy gating through config_actions.type: resource
    • per-resource deploy override tracking
  • Add ConfigActionsController and backend protocol so module-specific integrations can plug in their own save/deploy endpoint implementations without changing the common framework.
  • Add ConfigActionsMixin facade for orchestrators that need to execute normalized config action plans.
  • Update shared nd_argument_specs.config_actions_spec() to delegate to the policy-based implementation while preserving compatibility with existing call patterns.
  • Add focused unit coverage for argument specs, parser behavior, raw Ansible argument detection, validation, policies, controller behavior, and mixin delegation.

Test Notes

  • Ran focused unit tests:

    PYENV_VERSION=ndfclab PYTHONPATH=$PWD/collections python -m pytest -q tests/unit/module_utils/config_actions tests/unit/module_utils/orchestrators/test_config_actions_mixin.py

  • Result:

    52 passed in 0.25s

Cisco Nexus Dashboard Version

N/A - common framework only. Module-specific endpoint integrations and live controller validation will be handled in follow-up PRs.

Related ND API Resource Category

  • analyze
  • infa
  • 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

@AKDRG
AKDRG force-pushed the config_actions_common_framework branch from 217ee4f to 9202c39 Compare August 10, 2026 07:30
@AKDRG AKDRG self-assigned this Aug 10, 2026
@AKDRG AKDRG added the ready for review Submitter is requesting a PR review label Aug 10, 2026

@allenrobel allenrobel left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Review of the common config-actions framework. Findings posted inline; the switch-module wiring on top of this framework was reviewed separately on #499.

🤖 Generated with Claude Code

resource_deploy_overrides=resource_overrides,
)

if state in policy.read_only_states and not provided:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The read-only-state force-off only fires when config_actions is omitted entirely (not provided). If a task in gathered state supplies config_actions with only a non-write suboption — e.g. config_actions: {type: switch} — then provided is true, this branch is skipped, and Ansible's default fill-in leaves save/deploy at the policy defaults (True for five of the six shipped policies). validate_config_actions() doesn't catch it either: its read-only check only fires when save/deploy are explicitly named in the raw args (explicit_options & {"save", "deploy"}), not when they're merely default-filled.

So parse_config_actions() can return ConfigActions(deploy=True) in a read-only state with no error — a caller trusting actions.deploy would deploy during gathered. Suggest forcing save/deploy off in read-only states regardless of provided (unless explicitly set, which validation already rejects), and adding a test for the type-only-in-gathered case — the new suite only covers the omitted-dict and explicitly-named cases.

policy=self.config_actions_policy,
backend=selected_backend,
)
return controller.execute(actions, context)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The controller's skip paths (no_fabrics, ineligible, actions_disabled, no_targets) return status: "skipped" but nothing surfaces that to the user — no rest_send.warn(), and this facade returns the result unchanged. This is the same visibility gap fixed in this file in #491, where the switchless-fabric skip in execute_config_actions() was switched from logger.info() (invisible unless ND_LOGGING_CONFIG is set) to self.rest_send.warn(...).

The controller is transport-agnostic so it can't warn itself, which makes this facade the natural place: after controller.execute(), translate skipped steps into self.rest_send.warn(...) so integrations get the surfaced-skip behavior by default instead of each caller having to remember to do it.

`type` accepts `resource`, `switch`, and `global`, per the contract in issue #368. The companion per-resource `deploy` key described in that
issue is not part of this fragment yet: it lives in each module's `config` suboptions and its interaction with `config_actions` (mutually
exclusive, or gated on `type == "resource"`) is still under discussion on #368. It will be added as a separate fragment once settled.
This compatibility wrapper delegates to the policy-based implementation in `config_actions.argument_spec`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two pieces of load-bearing docstring content are dropped here without being re-homed in config_actions/argument_spec.py:

  1. The allowlist rationale on _select_options() ("An allowlist (rather than an exclude list) is deliberate: options added to a shared fragment in the future must never silently appear in modules that composed the fragment before the addition") — the mechanics survive, but the reason it must stay an allowlist is now undocumented in both the wrapper and the new canonical implementation.
  2. The note that the per-resource deploy key's interaction with config_actions ("mutually exclusive, or gated on type == 'resource'") was still under discussion on Module Support for config_actions + resource level deploy #368. This PR implements one specific resolution (RESOURCE_CONFIG_ACTIONS.resource_interaction = "type_resource_gated") — Module Support for config_actions + resource level deploy #368 is still open, so it would be good to note the decision on that issue and carry the rationale into policies.py/argument_spec.py rather than silently settling it.


## Raises

Exception

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All four Protocol methods (save here, deploy_global L48, deploy_switches L59, deploy_resources L70) list a bare Exception under ## Raises. CLAUDE.md: "use ### ExceptionType subheadings listing conditions as a Markdown list".


## Raises

Exception

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

execute() (here) and _deploy() (L127) list a bare Exception under ## Raises — same format issue as backend.py. CLAUDE.md: "use ### ExceptionType subheadings listing conditions as a Markdown list".

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.

2 participants