Policy and Policy Groups 4.x Implementation#277
Conversation
Co-authored-by: Copilot <copilot@github.com>
…fabric_inventory.py
…y groups module code
dce6af1 to
354f7fe
Compare
354f7fe to
727754b
Compare
| # ============================================================================ | ||
|
|
||
|
|
||
| class PlaybookSwitchPolicyConfig(NDNestedModel): |
There was a problem hiding this comment.
@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): |
There was a problem hiding this comment.
@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> | |||
There was a problem hiding this comment.
@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): |
There was a problem hiding this comment.
@nikhilsrikrishna , Here also, you can remove Playbook from model name
| log = logging.getLogger("nd.GatheredPolicyGroup") | ||
|
|
||
|
|
||
| class GatheredPolicyGroup(NDBaseModel): |
There was a problem hiding this comment.
@nikhilsrikrishna , Same goes here, rename file name as well as model and keep onlu PolicyGroup as class name.
3440bfb to
70d202d
Compare
865b45c to
1994956
Compare
Summary
This PR adds the
nd_policyandnd_policy_groupmodules to thecisco.ndcollection 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 withgathered,merged, anddeletedstates.nd_policy_group— policy-group management withgathered,merged, anddeletedstates.Endpoint wrappers
manage_fabrics_policies.pymanage_fabrics_policy_actions.pymanage_fabrics_policy_groups.pymanage_fabrics_policy_group_actions.pymanage_fabrics_switch_actions.pymanage_config_templates.pyPydantic 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_idis aliased asswitch_ipin the argument spec; users can specify either form interchangeably. Pydantic validators normalize both to the field expected by the API.Template input validation
User-provided
template_inputsare 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 fromgatheredoutput 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
markDeleteTemplates whose
contentTypeisPYTHON(e.g.,switch_freeform,Ext_VRF_Lite_SVI) fail the normalmarkDeletestep. The controller returns this exact error string in the 207 response: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 viaswitchActions/deployto 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_policybypasses the create state machineWhen 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 newpolicyIdis surfaced in the result so the user can target it later.3. Policy groups with no
descriptionuse directpolicyGroupIdlookupThe 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
policyGroupIdlookup (taken from user input or from agatheredround-trip). This is required forgathered → mergedround-trips where the user wants to update a group they previously fetched without describing it.4. Gathered output includes
policy_id/policy_group_idThe
gatheredoutput for both modules includes the controller-assignedpolicy_id(e.g.,POLICY-28440) alongside the template name. When this output is fed back intostate=merged, thepolicy_idcan be used directly to identify the exact policy or group, avoiding any ambiguity from name/description matching.