Skip to content

Policy and Policy Groups 4.x Implementation#277

Open
nikhilsrikrishna wants to merge 21 commits into
CiscoDevNet:developfrom
nikhilsrikrishna:policy_groups_develop_pr
Open

Policy and Policy Groups 4.x Implementation#277
nikhilsrikrishna wants to merge 21 commits into
CiscoDevNet:developfrom
nikhilsrikrishna:policy_groups_develop_pr

Conversation

@nikhilsrikrishna
Copy link
Copy Markdown
Collaborator

Summary

This PR adds the nd_policy and nd_policy_group modules to the cisco.nd collection for switch policy and policy-group management. It includes the resource handlers, orchestrator, endpoint wrappers, Pydantic models, and integration tests for both modules.

What's Included

Modules

  • nd_policy — policy management with gathered, merged, and deleted states.
  • nd_policy_group — policy-group management with gathered, merged, and deleted states.

Endpoint wrappers

  • manage_fabrics_policies.py
  • manage_fabrics_policy_actions.py
  • manage_fabrics_policy_groups.py
  • manage_fabrics_policy_group_actions.py
  • manage_fabrics_switch_actions.py
  • manage_config_templates.py

Pydantic model layer

  • models/manage_policies/config_models.py, gathered_models.py, policy_actions.py, policy_base.py, policy_crud.py.
  • models/manage_policy_groups/config_models.py, gathered_models.py, policy_group_actions.py, policy_group_base.py, policy_group_crud.py.

These cover input validation, API response parsing, and gathered-output formatting.

Input handling

  • switch_id is aliased as switch_ip in the argument spec; users can specify either form interchangeably. Pydantic validators normalize both to the field expected by the API.
  • The common IP-to-serial translation code is used to resolve management IPs to switch serial numbers before validation.

Template input validation

User-provided template_inputs are validated at runtime against the template's parameter schema fetched from the controller — checking for unknown keys, missing required parameters, and basic type correctness. System-injected keys are stripped from gathered output so only user-defined variables are returned.

Unit + integration tests

Endpoint-level unit tests for every new endpoint class, plus full integration suites for both modules (merged, gathered, deleted, round-trip, special cases).


Quirks & Workarounds

These are intentional deviations from the normal state-machine flow because the controller does not behave the way the schema suggests.

1. PYTHON-content templates bypass markDelete

Templates whose contentType is PYTHON (e.g., switch_freeform, Ext_VRF_Lite_SVI) fail the normal markDelete step. The controller returns this exact error string in the 207 response:

"Content type is PYTHON, cannot mark for deletion"

Rather than maintain a hardcoded template-name allow-list, the module inspects the 207 response and any policy that fails with this specific message is automatically retried via direct DELETE /policies/{policyId}, then deployed via switchActions/deploy to push the removal down to the switch. This keeps the module forward-compatible with any future PYTHON-content template the controller adds.

2. create_additional_policy bypasses the create state machine

When a user explicitly requests an additional instance of a template that already exists on a switch (i.e., the same template applied multiple times intentionally), the normal "exists → skip / update" decision in the state machine would block the second create. The module bypasses the state machine in this case and goes directly to the bulk create endpoint, relying on the controller to assign a new policyId. The new policyId is surfaced in the result so the user can target it later.

3. Policy groups with no description use direct policyGroupId lookup

The controller's "find by description" path is the standard idempotency key, but a policy group may legitimately have no description. In that case the module bypasses the description-based state machine and falls back to direct policyGroupId lookup (taken from user input or from a gathered round-trip). This is required for gathered → merged round-trips where the user wants to update a group they previously fetched without describing it.

4. Gathered output includes policy_id / policy_group_id

The gathered output for both modules includes the controller-assigned policy_id (e.g., POLICY-28440) alongside the template name. When this output is fed back into state=merged, the policy_id can be used directly to identify the exact policy or group, avoiding any ambiguity from name/description matching.

@nikhilsrikrishna nikhilsrikrishna force-pushed the policy_groups_develop_pr branch from 354f7fe to 727754b Compare May 13, 2026 17:20
# ============================================================================


class PlaybookSwitchPolicyConfig(NDNestedModel):
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.

@nikhilsrikrishna, Do we really need to prefix Playbook in each config class name?
can't we have SwitchPolicyConfig instead. It applies for others too

log = logging.getLogger("nd.GatheredPolicy")


class GatheredPolicy(NDBaseModel):
Copy link
Copy Markdown
Collaborator

@jeetugangwar11 jeetugangwar11 May 14, 2026

Choose a reason for hiding this comment

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

@nikhilsrikrishna , we should not prefix the state gathered before model name. I think we should keep model name as Policy instead of GatheredPolicy.
Rename file name too.

@@ -0,0 +1,167 @@
# Copyright: (c) 2026, L Nikhil Sri Krishna (@nisaikri) <nisaikri@cisco.com>
Copy link
Copy Markdown
Collaborator

@jeetugangwar11 jeetugangwar11 May 14, 2026

Choose a reason for hiding this comment

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

@nikhilsrikrishna , You can combine both gathered_model.pt and policy_base.py and keep only one copy of Policy model. File name can be called policy_model.py which will have all props and supporting fuctions.

Same models can be used on policy_curd.py

# ============================================================================


class PlaybookPolicyGroupConfig(NDNestedModel):
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.

@nikhilsrikrishna , Here also, you can remove Playbook from model name

log = logging.getLogger("nd.GatheredPolicyGroup")


class GatheredPolicyGroup(NDBaseModel):
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.

@nikhilsrikrishna , Same goes here, rename file name as well as model and keep onlu PolicyGroup as class name.

@nikhilsrikrishna nikhilsrikrishna force-pushed the policy_groups_develop_pr branch from 3440bfb to 70d202d Compare May 14, 2026 09:32
@nikhilsrikrishna nikhilsrikrishna force-pushed the policy_groups_develop_pr branch from 865b45c to 1994956 Compare May 21, 2026 13:58
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.

3 participants