Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Unmanaged Policy #202

Merged
merged 16 commits into from
Nov 8, 2024
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ The following control variables are available in this collection.
| `inventory_delete_mode` | Remove inventory state as part of the remove role | `false` |
| `link_vpc_delete_mode` | Remove vpc link state as part of the remove role | `false` |
| `vpc_delete_mode` | Remove vpc pair state as part of the remove role | `false` |
| `policy_delete_mode` | Remove policy state as part of the remove role | `false` |

These variables are described in more detail in different sections of this document.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# SPDX-License-Identifier: MIT


from ...helper_functions import data_model_key_check
from ....plugin_utils.helper_functions import data_model_key_check


def update_nested_dict(nested_dict, keys, new_value):
Expand Down
198 changes: 198 additions & 0 deletions plugins/action/dtc/unmanaged_policy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Copyright (c) 2024 Cisco Systems, Inc. and its affiliates
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# SPDX-License-Identifier: MIT

from __future__ import absolute_import, division, print_function


__metaclass__ = type

from ansible.plugins.action import ActionBase
from ...plugin_utils.helper_functions import ndfc_get_nac_switch_policy_using_desc


class ActionModule(ActionBase):

def run(self, tmp=None, task_vars=None):
results = super(ActionModule, self).run(tmp, task_vars)
results['changed'] = False

# List of switch serial numbes obtained directly from NDFC
ndfc_sw_serial_numbers = self._task.args["switch_serial_numbers"]
# Data from data model
model_data = self._task.args["model_data"]

# Switches list from data model
dm_topology_switches = model_data["vxlan"]["topology"]["switches"]

# Policy, Poilcy Groups, and Switches Policy Group lists from data model
dm_policy_policies = model_data["vxlan"]["policy"]["policies"]
dm_policy_groups = model_data["vxlan"]["policy"]["groups"]
dm_policy_switches = model_data["vxlan"]["policy"]["switches"]

# Build list of VRF Lites from data model if entries exist
# Used to exclude matching on VRF Lites as part of the unmanaged policies
vrf_lites = []
if model_data["vxlan"].get("overlay_extensions", None):
if model_data["vxlan"]["overlay_extensions"].get("vrf_lites", None):
dm_vrf_lites = model_data["vxlan"]["overlay_extensions"]["vrf_lites"]
for dm_vrf_lite in dm_vrf_lites:
for dm_vrf_lite_switch in dm_vrf_lite["switches"]:
unique_name = f"nac_{dm_vrf_lite['name']}_{dm_vrf_lite_switch['name']}"
vrf_lites.append(unique_name)

# Set defaults for management IP addresses, current switch policies, and unmanaged policies
dm_management_ipv4_address = ""
dm_management_ipv6_address = ""
# For each switch current_sw_policies will be used to store a list of policies currently associated to the switch
current_sw_policies = []
# For each switch that has unmanaged policies, the switch IP address and the list of unmanaged policies will be stored
# This default dict is the start of what is required for the NDFC policy module
unmanaged_policies = [
{
"switch": []
}
]

# Loop over each serial number obtained from NDFC
for ndfc_sw_serial_number in ndfc_sw_serial_numbers:
# Check if the serial number from NDFC matches any serial number for a switch in the data model
# If found, grab the specific switch entry from the data model
# Also if a match, set the IP mgmt information for the current switch found
if any(switch["serial_number"] == ndfc_sw_serial_number for switch in dm_topology_switches):
dm_switch_found = next(
(dm_topology_switch for dm_topology_switch in dm_topology_switches if dm_topology_switch["serial_number"] == ndfc_sw_serial_number)
)
dm_management_ipv4_address = dm_switch_found["management"].get("management_ipv4_address", None)
dm_management_ipv6_address = dm_switch_found["management"].get("management_ipv6_address", None)

# Check if the name matching either the IPv4 or IPv6 mgmt address is found in the policy switches data model
# If found, grab the specific entry from the policy switches data model and store
# This stores the current switches policy group list
if any(
(switch["name"] == dm_management_ipv4_address for switch in dm_policy_switches) or
(switch["name"] == dm_management_ipv6_address for switch in dm_policy_switches)
):
dm_policy_switch = next(
(
dm_policy_switch for dm_policy_switch in dm_policy_switches
if dm_policy_switch["name"] == dm_management_ipv4_address or dm_policy_switch["name"] == dm_management_ipv6_address
)
)

# Loop over each policy group associated to the current switch in the data model
for dm_sw_policy_group in dm_policy_switch["groups"]:
# Check if the policy group name associated to the switch is found in the policy groups data model
# If found, store a list of current policies that are part of that policy group in the data model
# In the process of storing, reformat the policy description name to prepend "nac_" and replace white spaces with underscores
if any(dm_policy_group["name"] == dm_sw_policy_group for dm_policy_group in dm_policy_groups):
current_sw_policies = next(
(
["nac_" + policy["name"].replace(" ", "_") for policy in dm_policy_group["policies"]]
for dm_policy_group in dm_policy_groups if dm_policy_group["name"] == dm_sw_policy_group
)
)

# Query NDFC for the current switch's serial number to get back any policy that exists for that switch
# with the description prepended with "nac_"
ndfc_policies_with_nac_desc = ndfc_get_nac_switch_policy_using_desc(self, task_vars, tmp, ndfc_sw_serial_number)

# Currently, check two things to determine an unmanaged policy:
# Check no matching policy in the data model against the policy returned from NDFC for the current switch
# This check uses the prepended "nac_"
# Additionally, as of now, check no matching policy is from the VRF Lite policy of the data model
if any(
((ndfc_policy_with_desc["description"] not in current_sw_policies) and (ndfc_policy_with_desc["description"] not in vrf_lites))
for ndfc_policy_with_desc in ndfc_policies_with_nac_desc
):
# If found, do the following:
# Update Ansible result status
# Add the switch to unmanaged_policies payload
# Get the last index of where the switch was added
# Build specific unmanaged policy entry
# Add unmanaged policy entry to last switch added to list

# Update Ansible for a configuration change
results['changed'] = True

# Update unmanaged_policies with the IP address of the switch that now has unmanaged policy
# The NDFC policy module can take a list of various dictionaries with the switch key previously being pre-stored
# Given this, each update the switch element with a new switch entry is the zeroth reference location always in unmanaged_policies
# Example:
# [
# {
# "switch": [
# {
# "ip": <mgmt_ip_address>,
# }
# ]
# }
# ]
unmanaged_policies[0]["switch"].append(
{
"ip": dm_management_ipv4_address if dm_management_ipv4_address else dm_management_ipv6_address
}
)

# Grab the last index of a switch added
last_idx = len(unmanaged_policies[0]["switch"]) - 1

# Since initially found there is indeed an unmananged policy, build a list of unmanaged policy
_unmanaged_policies = [
{
"name": ndfc_policy_with_desc["policyId"],
"description": ndfc_policy_with_desc["description"]
}
for ndfc_policy_with_desc in ndfc_policies_with_nac_desc
if ((ndfc_policy_with_desc["description"] not in current_sw_policies) and (ndfc_policy_with_desc["description"] not in vrf_lites))
]

# Update the dictionary entry for the last switch with the expected policies key the NDFC policy module expects
unmanaged_policies[0]["switch"][last_idx].update(
{
"policies": _unmanaged_policies
}
)

# Example of unmanaged policy payload:
# [
# {
# "switch": [
# {
# "ip": '<ip_address>',
# "policies": [
# {
# "name": <Policy ID>,
# "description": "nac_<description>"
# },
# {
# "name": <Policy ID>,
# "description": "nac_<description>"
# },
# ]
# },
# ]
# }
# ]

# Store the unmanaged policy payload for return and usage in the NDFC policy module to delete from NDFC
results['unmanaged_policies'] = unmanaged_policies

return results
8 changes: 4 additions & 4 deletions plugins/action/dtc/update_switch_hostname_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
__metaclass__ = type

from ansible.plugins.action import ActionBase
from ..helper_functions import ndfc_get_switch_policy
from ...plugin_utils.helper_functions import ndfc_get_switch_policy_using_template


class ActionModule(ActionBase):
Expand All @@ -41,12 +41,12 @@ def run(self, tmp=None, task_vars=None):
policy_update = {}

for switch_serial_number in switch_serial_numbers:
policy_match = ndfc_get_switch_policy(
policy_match = ndfc_get_switch_policy_using_template(
self=self,
task_vars=task_vars,
tmp=tmp,
template_name=template_name,
switch_serial_number=switch_serial_number
switch_serial_number=switch_serial_number,
template_name=template_name
)

switch_match = next((item for item in model_data["vxlan"]["topology"]["switches"] if item["serial_number"] == switch_serial_number))
Expand Down
63 changes: 0 additions & 63 deletions plugins/action/helper_functions.py

This file was deleted.

Empty file added plugins/plugin_utils/.gitkeep
Empty file.
Loading