diff --git a/README.md b/README.md index be4a1bf90..d18dd4c8f 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,6 @@ This will clone the example repository into the directory `nac-vxlan`. Next dele In this directory create a new virtual environment and install a Python version of your choice. At the time writing, Python version 3.10.13 is commonly used. Command `pyenv install 3.10.13` will install this version. For detailed instructions please visit the [pyenv](https://github.com/pyenv/pyenv) site. - ```bash cd nac-vxlan pyenv virtualenv nac-nd @@ -234,7 +233,6 @@ The data model is **required** to exist under the `host_vars` directory structur The collection is **pre-built** to utilize the `group_vars` and `host_vars` matching what is already constructed in the repository. Currently this methodology is a 1:1 relationship between code repository and NDFC fabric. For more complex environments, the inventory file can be expanded to include multiple groups and hosts including the usage of multi-site fabrics as explained in a separate document. - #### Step 1 - Update the Inventory File In the provided `inventory.yaml` file in the project's root directory, update @@ -242,7 +240,6 @@ the `ansible_host` variable to point to your ND controller by replacing `"{{ loo ND controller or setting the ```ND_HOST``` environment variable as described in Step 3. - #### Step 2 - Configure Ansible Connection File In the directory `group_vars/nd` is a file called `connection.yaml` that contains example data as: @@ -304,9 +301,9 @@ export NDFC_SW_PASSWORD=Admin_123 This collection supports flexible credential management for network switches with three security levels: -- **🔐 Ansible Vault**: Encrypted credentials for production deployments -- **✅ Environment Variables**: Secure credential injection for CI/CD pipelines -- **⚠️ Plain Text**: Simple credentials for lab testing only +* **🔐 Ansible Vault**: Encrypted credentials for production deployments +* **✅ Environment Variables**: Secure credential injection for CI/CD pipelines +* **⚠️ Plain Text**: Simple credentials for lab testing only The system supports both switch-specific credentials and group-level defaults with automatic fallback. Environment variable lookups can be configured in group_vars for enhanced security and automation compatibility. @@ -328,7 +325,7 @@ This collection is intended for use with the following release versions: ## Ansible Version Compatibility -This collection has been tested against following Ansible Core versions: +This collection has been tested against following Ansible Core versions: * `2.14.x` * `2.15.x` * `2.16.x` @@ -378,7 +375,6 @@ The first role is `cisco.nac_dc_vxlan.validate` which is going to validate the d The subsequent roles are the `cisco.nac_dc_vxlan.dtc.create`, `cisco.nac_dc_vxlan.dtc.deploy`, and `cisco.nac_dc_vxlan.dtc.remove` roles. These roles are the primary roles that will invoke changes in ND as described earlier. - > [!WARNING] > For your safety as indicated [earlier](#remove-role), the `remove` role also > requires setting some variables to `true` under the `group_vars` directory. @@ -417,13 +413,13 @@ For example, if VRFs and Networks are added/changed/removed in the model data fi This capability is not available under the following conditions: - * Control flag `force_run_all` under group_vars is set to `true`. - * When using ansible tags to control execution. - * When one of the following roles failed to complete on the previous run. - * `cisco.nac_dc_vxlan.validate` - * `cisco.nac_dc_vxlan.create` - * `cisco.nac_dc_vxlan.deploy` - * `cisco.nac_dc_vxlan.remove` +* Control flag `force_run_all` under group_vars is set to `true`. +* When using ansible tags to control execution. +* When one of the following roles failed to complete on the previous run. + * `cisco.nac_dc_vxlan.validate` + * `cisco.nac_dc_vxlan.create` + * `cisco.nac_dc_vxlan.deploy` + * `cisco.nac_dc_vxlan.remove` If any of these conditions is true then all roles/sections will be run. @@ -432,12 +428,15 @@ This capability is not available under the following conditions: * [Ansible Using Collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details. ## Multi-Site Domain for VXLAN BGP EVPN Fabrics + A Multi-Site Domain (MSD) is a multifabric administrative domain that is created to manage multiple member fabrics. An MSD is a single point of control for definition of overlay VRFs and Networks that are shared across member fabrics. When you move fabrics under the MSD as child fabrics, the child fabrics share the VRFs and networks created at the MSD-level. This way, you can consistently provision VRFs and networks for different fabrics, at one go. It significantly reduces the time and complexity involving multiple fabric provisionings. -### To configure and manage MSD fabrics with VXLAN as Code, you should use the following workflow: +### To configure and manage MSD fabrics with VXLAN as Code, you should use the following workflow + 1. Create each member/child fabric that will be managed by MSD using the normal data models for each fabric (or use a combined data model with all child fabrics) 2. Create the MSD fabric. In the data model set the fabric type to MSD and specify each child fabric that will be managed. Reference the [VXLAN MultiSite Data Model](https://netascode.cisco.com/docs/data_models/vxlan/multisite/multisite/) + > [!NOTE] > Any additional changes can be done using the MSD fabric data model or on an individual fabric basis using the respective playbooks. @@ -453,10 +452,10 @@ We welcome community contributions to this collection. If you find problems, ple ## More Information -- [Cisco Nexus Dashboard and Services Deployment and Upgrade Guide](https://www.cisco.com/c/en/us/td/docs/dcn/nd/3x/deployment/cisco-nexus-dashboard-and-services-deployment-guide-321.html) -- [Cisco Nexus Dashboard Fabric Controller (NDFC) User Content for LAN Configuration Guide](https://www.cisco.com/c/en/us/td/docs/dcn/ndfc/1222/collections/ndfc-user-content-1222-lan.html) -- [Ansible User Guide](https://docs.ansible.com/ansible/latest/user_guide/index.html) -- [Ansible Developer Guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html) +* [Cisco Nexus Dashboard and Services Deployment and Upgrade Guide](https://www.cisco.com/c/en/us/td/docs/dcn/nd/3x/deployment/cisco-nexus-dashboard-and-services-deployment-guide-321.html) +* [Cisco Nexus Dashboard Fabric Controller (NDFC) User Content for LAN Configuration Guide](https://www.cisco.com/c/en/us/td/docs/dcn/ndfc/1222/collections/ndfc-user-content-1222-lan.html) +* [Ansible User Guide](https://docs.ansible.com/ansible/latest/user_guide/index.html) +* [Ansible Developer Guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html) ## Licensing diff --git a/plugins/action/common/nac_dc_validate.py b/plugins/action/common/nac_dc_validate.py index 859265a50..f285823b9 100644 --- a/plugins/action/common/nac_dc_validate.py +++ b/plugins/action/common/nac_dc_validate.py @@ -107,7 +107,7 @@ def run(self, tmp=None, task_vars=None): rules_list.append(f'{rules}common') rules_list.append(f'{rules}ebgp_vxlan/') rules_list.append(f'{rules}common_vxlan') - elif results['data']['vxlan']['fabric']['type'] in ('MSD', 'MCF'): + elif results['data']['vxlan']['fabric']['type'] in ('MSD', 'MCFG'): rules_list.append(f'{rules}multisite/') elif results['data']['vxlan']['fabric']['type'] in ('ISN'): rules_list.append(f'{rules}isn/') @@ -136,7 +136,7 @@ def run(self, tmp=None, task_vars=None): rules_list.append(f'{rules}common') rules_list.append(f'{rules}ibgp_vxlan/') rules_list.append(f'{rules}common_vxlan') - elif results['data']['vxlan']['global']['fabric_type'] in ('MSD', 'MCF'): + elif results['data']['vxlan']['global']['fabric_type'] in ('MSD', 'MCFG'): rules_list.append(f'{rules}multisite/') elif results['data']['vxlan']['global']['fabric_type'] in ('ISN'): rules_list.append(f'{rules}isn/') diff --git a/plugins/action/common/prepare_plugins/prep_104_topology_switches.py b/plugins/action/common/prepare_plugins/prep_104_topology_switches.py index 35034b7cc..6c184f7dc 100644 --- a/plugins/action/common/prepare_plugins/prep_104_topology_switches.py +++ b/plugins/action/common/prepare_plugins/prep_104_topology_switches.py @@ -31,7 +31,7 @@ def prepare(self): model_data = self.kwargs['results']['model_extended'] # This plugin does not apply to the follwing fabric types - if model_data['vxlan']['fabric']['type'] in ['MSD', 'MFD']: + if model_data['vxlan']['fabric']['type'] in ['MSD', 'MCFG']: return self.kwargs['results'] else: switches = model_data['vxlan']['topology']['switches'] diff --git a/plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py b/plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py index 92aecef5c..3585b16bd 100644 --- a/plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py +++ b/plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py @@ -28,7 +28,7 @@ def prepare(self): model_data = self.kwargs['results']['model_extended'] # We don't have switches for Multisite fabrics so need special handling - if model_data['vxlan']['fabric']['type'] in ('MSD', 'MFD'): + if model_data['vxlan']['fabric']['type'] in ('MSD', 'MCFG'): switches = [] else: switches = model_data['vxlan']['topology']['switches'] @@ -82,7 +82,7 @@ def prepare(self): if net.get('network_attach_group') not in net_grp_name_list: del net['network_attach_group'] - if model_data['vxlan']['fabric']['type'] in ('MSD', 'MCF'): + if model_data['vxlan']['fabric']['type'] in ('MSD', 'MCFG'): # Rebuild sm_data['vxlan']['multisite']['overlay']['vrf_attach_groups'] into # a structure that is easier to use. vrf_grp_name_list = [] diff --git a/plugins/action/common/prepare_plugins/prep_106_topology_interfaces.py b/plugins/action/common/prepare_plugins/prep_106_topology_interfaces.py index bfb686da9..7f922ee76 100644 --- a/plugins/action/common/prepare_plugins/prep_106_topology_interfaces.py +++ b/plugins/action/common/prepare_plugins/prep_106_topology_interfaces.py @@ -37,7 +37,7 @@ def prepare(self): model_data = self.kwargs['results']['model_extended'] # This plugin does not apply to the follwing fabric types - if model_data['vxlan']['fabric']['type'] in ['MSD', 'MCF']: + if model_data['vxlan']['fabric']['type'] in ['MSD', 'MCFG']: return self.kwargs['results'] model_data['vxlan']['topology']['interfaces'] = {} diff --git a/plugins/action/common/prepare_plugins/prep_118_topology_edge_connections.py b/plugins/action/common/prepare_plugins/prep_118_topology_edge_connections.py index 23c6d3af4..e3e0755b7 100644 --- a/plugins/action/common/prepare_plugins/prep_118_topology_edge_connections.py +++ b/plugins/action/common/prepare_plugins/prep_118_topology_edge_connections.py @@ -28,7 +28,7 @@ def prepare(self): model_data = self.kwargs['results']['model_extended'] # This plugin does not apply to the follwing fabric types - if model_data['vxlan']['fabric']['type'] in ['MSD', 'MFD']: + if model_data['vxlan']['fabric']['type'] in ['MSD', 'MCFG']: return self.kwargs['results'] else: switches = model_data['vxlan']['topology']['switches'] diff --git a/plugins/action/dtc/fed_overlay_check.py b/plugins/action/dtc/fed_overlay_check.py new file mode 100644 index 000000000..57bd25ddf --- /dev/null +++ b/plugins/action/dtc/fed_overlay_check.py @@ -0,0 +1,234 @@ +# 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.utils.display import Display +from ansible.plugins.action import ActionBase + +display = Display() + + +class ActionModule(ActionBase): + """ + This class is used to compare the existing links with the links that you are + looking to add to the fabric. If the link already exists, it will be added to + the not_required_links list. + """ + def run(self, tmp=None, task_vars=None): + results = super(ActionModule, self).run(tmp, task_vars) + check_type = self._task.args['check_type'] + model_data = self._task.args['model_data'] + normal_model_data = [] + ndfc_data = self._task.args['ndfc_data'] + ndfc_attachment_data = self._task.args['ndfc_attachment_data'] + normal_ndfc_data = [] + restructured_data = [] + restructured_attachment_data = [] + deployment = False + deploy_payload = [] + # normalise data for comparison + if check_type == 'network_attach': + switch_data = ndfc_data + for attached_network in ndfc_attachment_data: + for network_attached_group in attached_network['lanAttachList']: + if network_attached_group['isLanAttached'] is True: + normal_ndfc_data.append({ + 'networkName': network_attached_group['networkName'], + 'switchName': network_attached_group['switchName'], + 'serialNumber': network_attached_group['switchSerialNo'], + 'portNames': network_attached_group['portNames'], + 'deployment': deployment, + 'fabric': network_attached_group['fabricName'] + }) + for network in model_data['vxlan']['multisite']['overlay']['networks']: + for network_attach_group in model_data['vxlan']['multisite']['overlay']['network_attach_groups']: + if network.get('network_attach_group') == network_attach_group['name']: + for switch in network_attach_group['switches']: + for switch_entry in switch_data: + if switch['hostname'] == switch_entry['logicalName']: + if switch.get('tors'): + main = f"{switch['hostname']}({','.join(switch['ports'])})" + # Format each tor entry + tors = ' '.join( + f"{tor['hostname']}({','.join(tor['ports'])})" + for tor in switch.get('tors', []) + ) + # Combine main and tors + ports = f"{main} {tors}".strip() + + normal_model_data.append({ + 'networkName': network['name'], + 'switchName': switch['hostname'], + 'serialNumber': switch_entry['serialNumber'], + 'portNames': ports, + 'deployment': deployment, + 'fabric': switch_entry['fabricName'] + }) + else: + normal_model_data.append({ + 'networkName': network['name'], + 'switchName': switch['hostname'], + 'serialNumber': switch_entry['serialNumber'], + 'portNames': (",".join(switch['ports'])), + 'deployment': deployment, + 'fabric': switch_entry['fabricName'] + }) + difference = [item for item in normal_ndfc_data if item not in normal_model_data] + + # Restructure in case of just port removal + for item in difference: + if item['portNames'] != "": + for network in model_data['vxlan']['multisite']['overlay']['networks']: + for network_attach_group in model_data['vxlan']['multisite']['overlay']['network_attach_groups']: + if network.get('network_attach_group') == network_attach_group['name'] and item['networkName'] == network['name']: + for switch in network_attach_group['switches']: + if switch['hostname'] == item['switchName']: + port_difference = [port for port in item['portNames'].split(',') if port not in switch['ports']] + if switch.get('ports'): + item['switchPorts'] = ",".join(switch['ports']) + else: + item['switchPorts'] = "" + item['detachSwitchPorts'] = ",".join(port_difference) + item['deployment'] = True + item.pop('portNames') + # psuedo code for vpc pair removal when only 1 switch has changes + # for switches in nddfc ndfc_data + # if switch in ndfc has vpc = true and switch hostname = difference switch hostname + # if found vpc serial is not in difference: + # add vpc paired device payloiad from ndfc ndfc_data + + # Restructure the difference data into payload format + network_dict = {} + for item in difference: + network_name = item['networkName'] + if network_name not in network_dict: + network_dict[network_name] = {'networkName': network_name, 'lanAttachList': []} + network_dict[network_name]['lanAttachList'].append(item) + deploy_payload.append(item['serialNumber']) + restructured_attachment_data = list(network_dict.values()) + + if check_type == 'network': + network_attachment_dict = {} + for network in model_data['vxlan']['multisite']['overlay']['networks']: + normal_model_data.append(network['name']) + for network in ndfc_data: + normal_ndfc_data.append(network['networkName']) + network_difference = [network for network in normal_ndfc_data if network not in normal_model_data] + restructured_data = network_difference + for network in network_difference: + for attached_network in ndfc_attachment_data: + for network_attached_group in attached_network['lanAttachList']: + if (network == attached_network['networkName'] and + network == network_attached_group['networkName'] and + network_attached_group['isLanAttached'] is True): + if network not in network_attachment_dict: + network_attachment_dict[network] = { + 'networkName': network, + 'lanAttachList': [] + } + network_attachment_dict[network]['lanAttachList'].append({ + 'networkName': network_attached_group['networkName'], + 'switchName': network_attached_group['switchName'], + 'serialNumber': network_attached_group['switchSerialNo'], + 'portNames': network_attached_group['portNames'], + 'deployment': deployment, + 'fabric': network_attached_group['fabricName'] + }) + network_attachment_dict[network]['lanAttachList'].append({ + 'networkName': network_attached_group['networkName'], + 'switchName': network_attached_group['switchName'], + 'serialNumber': network_attached_group['switchSerialNo'], + 'portNames': network_attached_group['portNames'], + 'deployment': deployment, + 'fabric': network_attached_group['fabricName'] + }) + deploy_payload.append(network_attached_group['switchSerialNo']) + restructured_attachment_data = list(network_attachment_dict.values()) + + elif check_type == 'vrf_attach': + switch_data = ndfc_data + for attached_vrf in ndfc_attachment_data: + for vrf_attached_group in attached_vrf['lanAttachList']: + if vrf_attached_group['isLanAttached'] is True: + normal_ndfc_data.append({ + 'fabric': vrf_attached_group['fabricName'], + 'deployment': deployment, + 'vrfName': vrf_attached_group['vrfName'], + 'serialNumber': vrf_attached_group['switchSerialNo'] + }) + for vrf in model_data['vxlan']['multisite']['overlay']['vrfs']: + for vrf_attach_group in model_data['vxlan']['multisite']['overlay']['vrf_attach_groups']: + if vrf['vrf_attach_group'] == vrf_attach_group['name']: + for switch in vrf_attach_group['switches']: + for switch_entry in switch_data: + if switch['hostname'] == switch_entry['logicalName']: + normal_model_data.append({ + 'fabric': switch_entry['fabricName'], + 'deployment': deployment, + 'vrfName': vrf['name'], + 'serialNumber': switch_entry['serialNumber'] + }) + difference = [item for item in normal_ndfc_data if item not in normal_model_data] + + # Restructure the difference data + vrf_dict = {} + + for item in difference: + vrf_name = item['vrfName'] + if vrf_name not in vrf_dict: + vrf_dict[vrf_name] = {'vrfName': vrf_name, 'lanAttachList': []} + vrf_dict[vrf_name]['lanAttachList'].append(item) + deploy_payload.append(item['serialNumber']) + restructured_attachment_data = list(vrf_dict.values()) + + elif check_type == 'vrf': + vrf_attachment_dict = {} + for vrf in model_data['vxlan']['multisite']['overlay']['vrfs']: + normal_model_data.append(vrf['name']) + for vrf in ndfc_data: + normal_ndfc_data.append(vrf['vrfName']) + vrf_difference = [vrf for vrf in normal_ndfc_data if vrf not in normal_model_data] + restructured_data = vrf_difference + for vrf in vrf_difference: + for attached_vrf in ndfc_attachment_data: + for vrf_attached_group in attached_vrf['lanAttachList']: + if vrf == attached_vrf['vrfName'] and vrf == vrf_attached_group['vrfName'] and vrf_attached_group['isLanAttached'] is True: + if vrf not in vrf_attachment_dict: + vrf_attachment_dict[vrf] = {'vrfName': vrf, 'lanAttachList': []} + vrf_attachment_dict[vrf]['lanAttachList'].append({ + 'vrfName': vrf_attached_group['vrfName'], + 'serialNumber': vrf_attached_group['switchSerialNo'], + 'deployment': deployment, + 'fabric': vrf_attached_group['fabricName'] + }) + deploy_payload.append(vrf_attached_group['switchSerialNo']) + restructured_attachment_data = list(vrf_attachment_dict.values()) + + if deploy_payload != []: + deploy_payload = set(deploy_payload) + results['payload'] = restructured_data + results['attachments_payload'] = restructured_attachment_data + results['deploy_payload'] = deploy_payload + + return results diff --git a/plugins/action/dtc/manage_child_fabric_networks.py b/plugins/action/dtc/manage_child_fabric_networks.py index 83f1966a2..5e15a0bf0 100644 --- a/plugins/action/dtc/manage_child_fabric_networks.py +++ b/plugins/action/dtc/manage_child_fabric_networks.py @@ -61,6 +61,7 @@ def run(self, tmp=None, task_vars=None): nd_version = self._task.args["nd_version"] msite_data = self._task.args["msite_data"] + fabric_type = self._task.args["fabric_type"] # Extract major, minor, patch and patch letter from nd_version # that is set in nac_dc_vxlan.dtc.connectivity_check role @@ -88,6 +89,7 @@ def run(self, tmp=None, task_vars=None): child_fabric_type = child_fabrics[child_fabric]['type'] if child_fabric_type in ['Switch_Fabric']: child_fabric_attributes = child_fabrics[child_fabric]['attributes'] + child_fabric_cluster = child_fabrics[child_fabric].get('cluster') network_child_fabric = [] if network_child_fabrics: @@ -139,12 +141,17 @@ def run(self, tmp=None, task_vars=None): # results['failed'] = True # results['msg'] = error_msg # return results + if fabric_type == 'MCFG': + get_path = (f"/onepath/{child_fabric_cluster}/appcenter/cisco/ndfc/api/v1/lan-fabric/" + f"rest/top-down/fabrics/{child_fabric}/networks/{network['name']}") + else: + get_path = f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/networks/{network['name']}" ndfc_net = self._execute_module( module_name="cisco.dcnm.dcnm_rest", module_args={ "method": "GET", - "path": f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/networks/{network['name']}", + "path": get_path, }, task_vars=task_vars, tmp=tmp @@ -207,11 +214,17 @@ def run(self, tmp=None, task_vars=None): rendered_content = templar.template(template_content) rendered_to_nice_json = templar.environment.filters['to_nice_json'](rendered_content) + if fabric_type == 'MCFG': + put_path = (f"/onepath/{child_fabric_cluster}/appcenter/cisco/ndfc/api/v1/lan-fabric/" + f"rest/top-down/fabrics/{child_fabric}/networks/{network['name']}") + else: + put_path = f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/networks/{network['name']}" + ndfc_net_update = self._execute_module( module_name="cisco.dcnm.dcnm_rest", module_args={ "method": "PUT", - "path": f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/networks/{network['name']}", + "path": put_path, "data": rendered_to_nice_json }, task_vars=task_vars, @@ -241,7 +254,6 @@ def run(self, tmp=None, task_vars=None): if ndfc_net_update.get('response'): if ndfc_net_update['response']['RETURN_CODE'] == 200: results['changed'] = True - # Failed response: # { # "failed": true, diff --git a/plugins/action/dtc/manage_child_fabric_vrfs.py b/plugins/action/dtc/manage_child_fabric_vrfs.py index 41ba1c587..7cdee49a4 100644 --- a/plugins/action/dtc/manage_child_fabric_vrfs.py +++ b/plugins/action/dtc/manage_child_fabric_vrfs.py @@ -72,6 +72,7 @@ def run(self, tmp=None, task_vars=None): nd_version = self._task.args["nd_version"] msite_data = self._task.args["msite_data"] + fabric_type = self._task.args["fabric_type"] # Extract major, minor, patch and patch letter from nd_version # that is set in nac_dc_vxlan.dtc.connectivity_check role @@ -99,6 +100,7 @@ def run(self, tmp=None, task_vars=None): child_fabric_type = child_fabrics[child_fabric]['type'] if child_fabric_type in ['Switch_Fabric']: child_fabric_attributes = child_fabrics[child_fabric]['attributes'] + child_fabric_cluster = child_fabrics[child_fabric].get('cluster') vrf_child_fabric = [] if vrf_child_fabrics: @@ -150,12 +152,17 @@ def run(self, tmp=None, task_vars=None): # results['failed'] = True # results['msg'] = error_msg # return results + if fabric_type == 'MCFG': + get_path = (f"/onepath/{child_fabric_cluster}/appcenter/cisco/ndfc/api/v1/lan-fabric/" + f"rest/top-down/fabrics/{child_fabric}/vrfs/{vrf['name']}") + else: + get_path = f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/vrfs/{vrf['name']}" ndfc_vrf = self._execute_module( module_name="cisco.dcnm.dcnm_rest", module_args={ "method": "GET", - "path": f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/vrfs/{vrf['name']}" + "path": get_path, }, task_vars=task_vars, tmp=tmp @@ -218,11 +225,17 @@ def run(self, tmp=None, task_vars=None): rendered_content = templar.template(template_content) rendered_to_nice_json = templar.environment.filters['to_nice_json'](rendered_content) + if fabric_type == 'MCFG': + put_path = (f"/onepath/{child_fabric_cluster}/appcenter/cisco/ndfc/api/v1/lan-fabric/" + f"rest/top-down/fabrics/{child_fabric}/vrfs/{vrf['name']}") + else: + put_path = f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/vrfs/{vrf['name']}" + ndfc_vrf_update = self._execute_module( module_name="cisco.dcnm.dcnm_rest", module_args={ "method": "PUT", - "path": f"/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{child_fabric}/vrfs/{vrf['name']}", + "path": put_path, "data": rendered_to_nice_json }, task_vars=task_vars, diff --git a/plugins/action/dtc/prepare_msite_data.py b/plugins/action/dtc/prepare_msite_data.py index ddc9b18ac..902d27ca4 100644 --- a/plugins/action/dtc/prepare_msite_data.py +++ b/plugins/action/dtc/prepare_msite_data.py @@ -28,6 +28,8 @@ from ansible.plugins.action import ActionBase from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import ndfc_get_fabric_attributes from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import ndfc_get_fabric_switches +from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import ndfc_get_fabric_attributes_onepath +from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import ndfc_get_fabric_switches_onepath import re display = Display() @@ -46,34 +48,65 @@ def run(self, tmp=None, task_vars=None): # This is actaully not an accurrate API endpoint as it returns all fabrics in NDFC, not just the fabrics associated with MSD # Therefore, we need to get the fabric associations response and filter out the fabrics that are not associated with the parent fabric (MSD) - msd_fabric_associations = self._execute_module( - module_name="cisco.dcnm.dcnm_rest", - module_args={ - "method": "GET", - "path": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/msd/fabric-associations", - }, - task_vars=task_vars, - tmp=tmp - ) - - # Build a list of child fabrics that are associated with the parent fabric (MSD) - associated_child_fabrics = [] - for fabric in msd_fabric_associations.get('response').get('DATA'): - if fabric.get('fabricParent') == parent_fabric: - associated_child_fabrics.append(fabric.get('fabricName')) + if model_data["vxlan"]["fabric"]["type"] == "MCFG": + mcfg_fabric_associations = self._execute_module( + module_name="cisco.dcnm.dcnm_rest", + module_args={ + "method": "GET", + "path": "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics", + }, + task_vars=task_vars, + tmp=tmp + ) + associated_child_fabrics = [] + for fabric in mcfg_fabric_associations.get('response').get('DATA'): + if fabric.get('fabricName') == parent_fabric: + for member in fabric["members"]: + associated_child_fabrics.append({ + 'fabric': member.get('fabricName'), + 'cluster': member.get('clusterName'), + 'type': member.get('fabricType') + }) + + child_fabrics_data = {} + for fabric in associated_child_fabrics: + fabric_name = fabric['fabric'] + child_fabrics_data.update({fabric_name: {}}) + child_fabrics_data[fabric_name].update({'type': fabric['type']}) + child_fabrics_data[fabric_name].update({'attributes': ndfc_get_fabric_attributes_onepath(self, task_vars, tmp, fabric_name, fabric['cluster'])}) + child_fabrics_data[fabric_name].update({'switches': ndfc_get_fabric_switches_onepath(self, task_vars, tmp, fabric_name, fabric['cluster'])}) + child_fabrics_data[fabric_name].update({'cluster': fabric['cluster']}) + results['child_fabrics_data'] = child_fabrics_data + + else: + msd_fabric_associations = self._execute_module( + module_name="cisco.dcnm.dcnm_rest", + module_args={ + "method": "GET", + "path": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/msd/fabric-associations", + }, + task_vars=task_vars, + tmp=tmp + ) + + # Build a list of child fabrics that are associated with the parent fabric (MSD) + associated_child_fabrics = [] + for fabric in msd_fabric_associations.get('response').get('DATA'): + if fabric.get('fabricParent') == parent_fabric: + associated_child_fabrics.append(fabric.get('fabricName')) # Get the fabric attributes and switches for each child fabric # These queries are potentially trying to get data for a fabric that is not associated with the parent fabric (MSD) yet - child_fabrics_data = {} - for fabric in associated_child_fabrics: - child_fabrics_data.update({fabric: {}}) - child_fabrics_data[fabric].update( - {'type': [_fabric['fabricType'] for _fabric in msd_fabric_associations['response']['DATA'] if _fabric['fabricName'] == fabric][0]} - ) - child_fabrics_data[fabric].update({'attributes': ndfc_get_fabric_attributes(self, task_vars, tmp, fabric)}) - child_fabrics_data[fabric].update({'switches': ndfc_get_fabric_switches(self, task_vars, tmp, fabric)}) - - results['child_fabrics_data'] = child_fabrics_data + child_fabrics_data = {} + for fabric in associated_child_fabrics: + child_fabrics_data.update({fabric: {}}) + child_fabrics_data[fabric].update( + {'type': [_fabric['fabricType'] for _fabric in msd_fabric_associations['response']['DATA'] if _fabric['fabricName'] == fabric][0]} + ) + child_fabrics_data[fabric].update({'attributes': ndfc_get_fabric_attributes(self, task_vars, tmp, fabric)}) + child_fabrics_data[fabric].update({'switches': ndfc_get_fabric_switches(self, task_vars, tmp, fabric)}) + + results['child_fabrics_data'] = child_fabrics_data # Rebuild sm_data['vxlan']['multisite']['overlay']['vrf_attach_groups'] into # a structure that is easier to use just like MD_Extended. diff --git a/plugins/plugin_utils/data_model_keys.py b/plugins/plugin_utils/data_model_keys.py index 2a5f6e170..a08459146 100644 --- a/plugins/plugin_utils/data_model_keys.py +++ b/plugins/plugin_utils/data_model_keys.py @@ -26,7 +26,7 @@ # from ..helper_functions import do_something root_key = 'vxlan' -model_keys = {'VXLAN_EVPN': {}, 'MSD': {}, 'MCF': {}, 'ISN': {}, 'External': {}, 'eBGP_VXLAN': {}} +model_keys = {'VXLAN_EVPN': {}, 'MSD': {}, 'MCFG': {}, 'ISN': {}, 'External': {}, 'eBGP_VXLAN': {}} # VXLAN_EVPN KEYS # iBGP VXLAN KEYS @@ -150,3 +150,16 @@ model_keys['MSD']['multisite.overlay.networks'] = [root_key, 'multisite', 'overlay', 'networks', 'LIST'] model_keys['MSD']['multisite.overlay.network_attach_groups'] = [root_key, 'multisite', 'overlay', 'network_attach_groups', 'LIST'] model_keys['MSD']['multisite.overlay.network_attach_groups.switches'] = [root_key, 'multisite', 'overlay', 'network_attach_groups', 'switches', 'LIST_INDEX'] + +# MCFG KEYS + +# --- +model_keys['MCFG']['multisite'] = [root_key, 'multisite', 'KEY'] +model_keys['MCFG']['multisite.child_fabrics'] = [root_key, 'multisite', 'child_fabrics', 'KEY'] +model_keys['MCFG']['multisite.overlay'] = [root_key, 'multisite', 'overlay', 'KEY'] +model_keys['MCFG']['multisite.overlay.vrfs'] = [root_key, 'multisite', 'overlay', 'vrfs', 'LIST'] +model_keys['MCFG']['multisite.overlay.vrf_attach_groups'] = [root_key, 'multisite', 'overlay', 'vrf_attach_groups', 'LIST'] +model_keys['MCFG']['multisite.overlay.vrf_attach_groups.switches'] = [root_key, 'multisite', 'overlay', 'vrf_attach_groups', 'switches', 'LIST_INDEX'] +model_keys['MCFG']['multisite.overlay.networks'] = [root_key, 'multisite', 'overlay', 'networks', 'LIST'] +model_keys['MCFG']['multisite.overlay.network_attach_groups'] = [root_key, 'multisite', 'overlay', 'network_attach_groups', 'LIST'] +model_keys['MCFG']['multisite.overlay.network_attach_groups.switches'] = [root_key, 'multisite', 'overlay', 'network_attach_groups', 'switches', 'LIST_INDEX'] diff --git a/plugins/plugin_utils/helper_functions.py b/plugins/plugin_utils/helper_functions.py index 6aee32949..c1db27d57 100644 --- a/plugins/plugin_utils/helper_functions.py +++ b/plugins/plugin_utils/helper_functions.py @@ -25,7 +25,6 @@ # For example in prepare_serice_model.py we can do the following: # from ..helper_functions import do_something - def data_model_key_check(tested_object, keys): """ Check if key(s) are found and exist in the data model. @@ -200,6 +199,36 @@ def ndfc_get_fabric_attributes(self, task_vars, tmp, fabric): return fabric_attributes +def ndfc_get_fabric_attributes_onepath(self, task_vars, tmp, fabric, cluster): + """ + Get NDFC fabric attributes. + + :Parameters: + :self: Ansible action plugin instance object. + :task_vars (dict): Ansible task vars. + :tmp (None, optional): Ansible tmp object. Defaults to None via Action Plugin. + :fabric (str): The fabric name to be retrieved. + + :Returns: + :fabric_attributes: The NDFC fabric attributes data for the given fabric. + + :Raises: + N/A + """ + fabric_response = self._execute_module( + module_name="cisco.dcnm.dcnm_rest", + module_args={ + "method": "GET", + "path": f"/onepath/{cluster}/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{fabric}", + }, + task_vars=task_vars, + tmp=tmp + ) + fabric_attributes = fabric_response['response']['DATA']['nvPairs'] + + return fabric_attributes + + def ndfc_get_fabric_switches(self, task_vars, tmp, fabric): """ Get NDFC fabric switches. @@ -237,3 +266,42 @@ def ndfc_get_fabric_switches(self, task_vars, tmp, fabric): ) return fabric_switches + + +def ndfc_get_fabric_switches_onepath(self, task_vars, tmp, fabric, cluster): + """ + Get NDFC fabric switches. + + :Parameters: + :self: Ansible action plugin instance object. + :task_vars (dict): Ansible task vars. + :tmp (None, optional): Ansible tmp object. Defaults to None via Action Plugin. + :fabric (str): The fabric name to be retrieved. + + :Returns: + :fabric_switches: The NDFC fabric switches data for the given fabric. + + :Raises: + N/A + """ + fabric_response = self._execute_module( + module_name="cisco.dcnm.dcnm_rest", + module_args={ + "method": "GET", + "path": f"/onepath/{cluster}/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{fabric}/inventory/switchesByFabric", + }, + task_vars=task_vars, + tmp=tmp + ) + + fabric_switches = [] + for fabric_switch in fabric_response['response']['DATA']: + if 'logicalName' in fabric_switch: + fabric_switches.append( + { + 'hostname': fabric_switch['logicalName'], + 'mgmt_ip_address': fabric_switch['ipAddress'] + } + ) + + return fabric_switches diff --git a/roles/dtc/common/tasks/main.yml b/roles/dtc/common/tasks/main.yml index 1a2dff901..6f4e188ae 100644 --- a/roles/dtc/common/tasks/main.yml +++ b/roles/dtc/common/tasks/main.yml @@ -55,6 +55,11 @@ tags: "{{ nac_tags.common_role }}" when: MD_Extended.vxlan.fabric.type == 'MSD' +- name: Import Role Tasks for MCFG Fabric + ansible.builtin.import_tasks: sub_main_mcfg.yml + tags: "{{ nac_tags.common_role }}" # Tags defined in roles/common_global/vars/main.yml + when: MD_Extended.vxlan.fabric.type == 'MCFG' + - name: Import Role Tasks for External Fabric ansible.builtin.import_tasks: sub_main_external.yml tags: "{{ nac_tags.common_role }}" diff --git a/roles/dtc/common/tasks/mcfg/ndfc_children.yml b/roles/dtc/common/tasks/mcfg/ndfc_children.yml new file mode 100644 index 000000000..39b61cfd9 --- /dev/null +++ b/roles/dtc/common/tasks/mcfg/ndfc_children.yml @@ -0,0 +1,77 @@ +# 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 +--- + +- debug: msg="Building NDFC MCFG Child Fabric Data" + +- name: Initialize changes_detected Var + ansible.builtin.set_fact: + changes_detected_fabric: false + delegate_to: localhost + +- name: Set file_name Var + ansible.builtin.set_fact: + file_name: "ndfc_fabric_children.yml" + delegate_to: localhost + +- name: Stat Previous File If It Exists + ansible.builtin.stat: + path: "{{ path_name }}{{ file_name }}" + register: data_file_previous + delegate_to: localhost + +- name: Backup Previous Data File If It Exists + ansible.builtin.copy: + src: "{{ path_name }}{{ file_name }}" + dest: "{{ path_name }}{{ file_name }}.old" + when: data_file_previous.stat.exists + +- name: Delete Previous Data File If It Exists + ansible.builtin.file: + state: absent + path: "{{ path_name }}{{ file_name }}" + delegate_to: localhost + when: data_file_previous.stat.exists + +- name: Build Fabric Creation Parameters From Template + ansible.builtin.template: + src: ndfc_fabric_children.j2 + dest: "{{ path_name }}{{ file_name }}" + delegate_to: localhost + +- ansible.builtin.set_fact: + fabric_children: "{{ lookup('file', path_name + file_name) | from_yaml }}" + delegate_to: localhost + +- name: Diff Previous and Current Data Files + cisco.nac_dc_vxlan.dtc.diff_model_changes: + file_name_previous: "{{ path_name }}{{ file_name }}.old" + file_name_current: "{{ path_name }}{{ file_name }}" + register: file_diff_result + delegate_to: localhost + +- name: Set File Change Flag Based on File Diff Result + ansible.builtin.set_fact: + changes_detected_fabric: true + delegate_to: localhost + when: + - file_diff_result.file_data_changed + - check_roles['save_previous'] diff --git a/roles/dtc/common/tasks/mcfg/ndfc_fabric.yml b/roles/dtc/common/tasks/mcfg/ndfc_fabric.yml new file mode 100644 index 000000000..f3863e2f7 --- /dev/null +++ b/roles/dtc/common/tasks/mcfg/ndfc_fabric.yml @@ -0,0 +1,85 @@ +# 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 + +--- + +- name: Initialize changes_detected Var + ansible.builtin.set_fact: + changes_detected_fabric: false + delegate_to: localhost + +- name: Set file_name Var + ansible.builtin.set_fact: + file_name: "ndfc_fabric.yml" + delegate_to: localhost + +- name: Stat Previous File If It Exists + ansible.builtin.stat: + path: "{{ path_name }}{{ file_name }}" + register: data_file_previous + delegate_to: localhost + # TODO: Add capability to overridde path variable above for CI/CD pipeline + +- name: Backup Previous Data File If It Exists + ansible.builtin.copy: + src: "{{ path_name }}{{ file_name }}" + dest: "{{ path_name }}{{ file_name }}.old" + mode: preserve + when: data_file_previous.stat.exists + +- name: Delete Previous Data File If It Exists + ansible.builtin.file: + state: absent + path: "{{ path_name }}{{ file_name }}" + delegate_to: localhost + when: data_file_previous.stat.exists + +- name: Build Fabric Creation Parameters From Template + ansible.builtin.template: + src: ndfc_fabric.j2 + dest: "{{ path_name }}{{ file_name }}" + mode: '0644' + delegate_to: localhost + +- name: Set fabric_config Var default + ansible.builtin.set_fact: + fabric_config: [] + delegate_to: localhost + +- name: Set fabric_config Var + ansible.builtin.set_fact: + fabric_config: "{{ lookup('file', path_name + file_name) | from_yaml }}" + delegate_to: localhost + +- name: Diff Previous and Current Data Files + cisco.nac_dc_vxlan.dtc.diff_model_changes: + file_name_previous: "{{ path_name }}{{ file_name }}.old" + file_name_current: "{{ path_name }}{{ file_name }}" + register: file_diff_result + delegate_to: localhost + +- name: Set File Change Flag Based on File Diff Result + ansible.builtin.set_fact: + changes_detected_fabric: true + delegate_to: localhost + when: + - file_diff_result.file_data_changed + - check_roles['save_previous'] diff --git a/roles/dtc/common/tasks/mcfg/ndfc_get_inventory.yml b/roles/dtc/common/tasks/mcfg/ndfc_get_inventory.yml new file mode 100644 index 000000000..4892db628 --- /dev/null +++ b/roles/dtc/common/tasks/mcfg/ndfc_get_inventory.yml @@ -0,0 +1,40 @@ +# 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 + +--- + +- name: Get all the switches in the MCFG Fabric Group + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD.vxlan.fabric.name }}/inventory/switchesByFabric" + register: switch_results + delegate_to: "{{ inventory_hostname }}" + failed_when: false + +- name: Store switches in the fabric for MCFG Fabric Group + ansible.builtin.set_fact: + switches_in_fabric: "{{ switch_results.response.DATA }}" + when: switch_results is defined and switch_results.response.DATA is defined + +- name: Set switches_in_fabric to empty list if no switches are found + ansible.builtin.set_fact: + switches_in_fabric: [] + when: switch_results is not defined or switch_results.response.DATA is not defined diff --git a/roles/dtc/common/tasks/mcfg/ndfc_networks.yml b/roles/dtc/common/tasks/mcfg/ndfc_networks.yml new file mode 100644 index 000000000..2c35cc08c --- /dev/null +++ b/roles/dtc/common/tasks/mcfg/ndfc_networks.yml @@ -0,0 +1,126 @@ +# 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 + +--- +- name: Initialize changes_detected Var + ansible.builtin.set_fact: + changes_detected_networks: false + delegate_to: localhost + +- name: Set file_name Var + ansible.builtin.set_fact: + file_name: "ndfc_attach_networks.yml" + delegate_to: localhost + +- name: Stat Previous File If It Exists + ansible.builtin.stat: + path: "{{ path_name }}{{ file_name }}" + register: data_file_previous + delegate_to: localhost + +- name: Backup Previous Data File If It Exists + ansible.builtin.copy: + src: "{{ path_name }}{{ file_name }}" + dest: "{{ path_name }}{{ file_name }}.old" + when: data_file_previous.stat.exists + +- name: Delete Previous Data File If It Exists + ansible.builtin.file: + state: absent + path: "{{ path_name }}{{ file_name }}" + delegate_to: localhost + when: data_file_previous.stat.exists + +- name: Build Networks Attach List From Template + ansible.builtin.template: + src: "{{ role_path }}/../common/templates/ndfc_attach_networks.j2" + dest: "{{ path_name }}{{ file_name }}" + mode: '0644' + delegate_to: localhost + +- name: Set net_config Var + ansible.builtin.set_fact: + net_config: [] + delegate_to: localhost + +- name: Set net_config Var + ansible.builtin.set_fact: + net_config: "{{ lookup('file', path_name + file_name) | from_yaml }}" + when: (MD_Extended.vxlan.multisite.overlay.networks | default([])) | length > 0 + delegate_to: localhost + +- name: Diff Previous and Current Data Files + cisco.nac_dc_vxlan.dtc.diff_model_changes: + file_name_previous: "{{ path_name }}{{ file_name }}.old" + file_name_current: "{{ path_name }}{{ file_name }}" + register: file_diff_result + delegate_to: localhost + +- name: Set File Change Flag Based on File Diff Result + ansible.builtin.set_fact: + changes_detected_networks: true + delegate_to: localhost + when: + - file_diff_result.file_data_changed + - check_roles['save_previous'] + +- name: Set file_name Var for switches and port attachments + ansible.builtin.set_fact: + file_name: "ndfc_attach_network_switches_ports.yml" + file_name2: "ndfc_attach_network_switches.yml" + switches: [] # check why this is defined here to be used in the template + networks: [] # same for this + network_switches: [] + network_switches_ports: [] + delegate_to: localhost + +- name: Build Network Attach List for switches From Template + ansible.builtin.template: + src: "{{ role_path }}/../common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches.j2" + dest: "{{ path_name }}{{ file_name }}" + mode: '0644' + delegate_to: localhost + +- name: Build Network Attach List for switches and ports From Template + ansible.builtin.template: + src: "{{ role_path }}/../common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches_ports.j2" + dest: "{{ path_name }}{{ file_name2 }}" + mode: '0644' + delegate_to: localhost + +# - name: Build Network attach list for switches on Federated +# ansible.builtin.template: +# src: ndfc_attach_networks_switches_fed.j2 +# dest: "{{ path_name }}{{ file_name }}" +# delegate_to: localhost + +# - name: Build Network attach list for switches and ports on Federated +# ansible.builtin.template: +# src: ndfc_attach_networks_switches_ports_fed.j2 +# dest: "{{ path_name }}{{ file_name2 }}" +# delegate_to: localhost + +- name: Set network_switches Var + ansible.builtin.set_fact: + network_switches: "{{ lookup('file', path_name + file_name) | from_yaml }}" + network_switches_ports: "{{ lookup('file', path_name + file_name2) | from_yaml }}" + when: (MD_Extended.vxlan.multisite.overlay.networks | default([])) | length > 0 + delegate_to: localhost diff --git a/roles/dtc/common/tasks/mcfg/ndfc_vrfs.yml b/roles/dtc/common/tasks/mcfg/ndfc_vrfs.yml new file mode 100644 index 000000000..1414fa87e --- /dev/null +++ b/roles/dtc/common/tasks/mcfg/ndfc_vrfs.yml @@ -0,0 +1,118 @@ +# 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 + +--- +- name: Initialize changes_detected Var + ansible.builtin.set_fact: + changes_detected_vrfs: false + delegate_to: localhost + +- name: Set file_name Var + ansible.builtin.set_fact: + file_name: "ndfc_attach_vrfs.yml" + delegate_to: localhost + +- name: Stat Previous File If It Exists + ansible.builtin.stat: + path: "{{ path_name }}{{ file_name }}" + register: data_file_previous + delegate_to: localhost + +- name: Backup Previous Data File If It Exists + ansible.builtin.copy: + src: "{{ path_name }}{{ file_name }}" + dest: "{{ path_name }}{{ file_name }}.old" + when: data_file_previous.stat.exists + +- name: Delete Previous File If It Exists + ansible.builtin.file: + state: absent + path: "{{ path_name }}{{ file_name }}" + delegate_to: localhost + when: data_file_previous.stat.exists + +# - name: Build VRFs Attach List From Template +# ansible.builtin.template: +# src: ndfc_attach_vrfs_fed.j2 +# dest: "{{ path_name }}{{ file_name }}" +# delegate_to: localhost + +- name: Build VRFs Attach List From Template + ansible.builtin.template: + src: "{{ role_path }}/../common/templates/ndfc_attach_vrfs.j2" + dest: "{{ path_name }}{{ file_name }}" + mode: '0644' + delegate_to: localhost + +- name: Create Empty vrf_config Var + ansible.builtin.set_fact: + vrf_config: [] + delegate_to: localhost + +- name: Set vrf_config Var + ansible.builtin.set_fact: + vrf_config: "{{ lookup('file', path_name + file_name ) | from_yaml }}" + when: (MD_Extended.vxlan.multisite.overlay.vrfs | default([])) | length > 0 + delegate_to: localhost + +- name: Diff Previous and Current Data Files + cisco.nac_dc_vxlan.dtc.diff_model_changes: + file_name_previous: "{{ path_name }}{{ file_name }}.old" + file_name_current: "{{ path_name }}{{ file_name }}" + register: file_diff_result + delegate_to: localhost + +- name: Set File Change Flag Based on File Diff Result + ansible.builtin.set_fact: + changes_detected_vrfs: true + delegate_to: localhost + when: + - file_diff_result.file_data_changed + - check_roles['save_previous'] + +- name: Set file_name Var for loopback attachments + ansible.builtin.set_fact: + file_name: "ndfc_attach_vrfs_loopbacks.yml" + delegate_to: localhost + +- name: Build VRFs Loopback Attach List From Template + ansible.builtin.template: + src: "{{ role_path }}/../common/templates/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs_loopbacks.j2" + dest: "{{ path_name }}{{ file_name }}" + mode: '0644' + delegate_to: localhost + +# - name: Build VRFs Attach List From Template for loopback on Federated +# ansible.builtin.template: +# src: ndfc_attach_vrfs_loopbacks_fed.j2 +# dest: "{{ path_name }}{{ file_name }}" +# delegate_to: localhost + +- name: Create Empty vrf_loopbacks Var + ansible.builtin.set_fact: + vrf_attach_config: [] + delegate_to: localhost + +- name: Set vrf_attach_config Var + ansible.builtin.set_fact: + vrf_attach_config: "{{ lookup('file', path_name + file_name) | from_yaml }}" + when: (MD_Extended.vxlan.multisite.overlay.vrfs | default([])) | length > 0 + delegate_to: localhost diff --git a/roles/dtc/common/tasks/sub_main_external.yml b/roles/dtc/common/tasks/sub_main_external.yml index 24fa53654..5fb333fb0 100644 --- a/roles/dtc/common/tasks/sub_main_external.yml +++ b/roles/dtc/common/tasks/sub_main_external.yml @@ -191,6 +191,7 @@ interface_trunk_po: "{{ interface_trunk_po }}" interface_trunk: "{{ interface_trunk }}" interface_dot1q: "{{ interface_dot1q }}" + interface_vpc: "{{ interface_vpc }}" inv_config: "{{ inv_config }}" poap_data: "{{ poap_data }}" policy_config: "{{ policy_config }}" diff --git a/roles/dtc/common/tasks/sub_main_mcfg.yml b/roles/dtc/common/tasks/sub_main_mcfg.yml new file mode 100644 index 000000000..6c376196f --- /dev/null +++ b/roles/dtc/common/tasks/sub_main_mcfg.yml @@ -0,0 +1,99 @@ +# 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 + +--- +- ansible.builtin.fail: msg="Service Model Not Defined. Role cisco.nac_dc_vxlan.validate Must Be Called First" + when: MD is undefined + delegate_to: localhost + +# -------------------------------------------------------------------- +# Remove all files from the previous run if run_map requires it +# -------------------------------------------------------------------- +- name: Set path_name Var + ansible.builtin.set_fact: + path_name: "{{ role_path }}/files/mcfg/{{ MD_Extended.vxlan.fabric.name }}/" + delegate_to: localhost + +- name: Cleanup Files from Previous Run if run_map requires it + ansible.builtin.import_tasks: cleanup_files.yml + when: + - not run_map_read_result.diff_run or ((force_run_all is defined) and (force_run_all is true|bool)) + +# -------------------------------------------------------------------- +# Build Create Fabric Parameter List From Template +# -------------------------------------------------------------------- + +- name: Build Fabric Create Parameters + ansible.builtin.import_tasks: mcfg/ndfc_fabric.yml + +# -------------------------------------------------------------------- +# Build Create Fabric Children Parameter List From Template +# -------------------------------------------------------------------- + +- name: Build Fabric Create Parameters + ansible.builtin.import_tasks: mcfg/ndfc_children.yml + +# -------------------------------------------------------------------- +# Build NDFC Fabric Get Switch Inventory when Federated Fabric +# -------------------------------------------------------------------- + +- name: Build NDFC Fabric Switch inventory + ansible.builtin.import_tasks: mcfg/ndfc_get_inventory.yml + +# -------------------------------------------------------------------- +# Build NDFC Fabric VRFs Attach List From Template +# -------------------------------------------------------------------- + +- name: Build NDFC Fabric VRFs Attach List From Template + ansible.builtin.import_tasks: mcfg/ndfc_vrfs.yml + +# -------------------------------------------------------------------- +# Build NDFC Fabric Networks Attach List From Template +# -------------------------------------------------------------------- + +- name: Build NDFC Fabric Networks Attach List From Template + ansible.builtin.import_tasks: mcfg/ndfc_networks.yml + +- name: Save Local Variables With Namespace Context + ansible.builtin.set_fact: + vars_common_mcfg: + changes_detected_fabric: "{{ changes_detected_fabric | default(false) }}" + changes_detected_vrfs: "{{ changes_detected_vrfs }}" + changes_detected_networks: "{{ changes_detected_networks }}" + fabric_config: "{{ fabric_config }}" + fabric_children: "{{ fabric_children }}" + switches_in_fabric: "{{ switches_in_fabric }}" + net_config: "{{ net_config }}" + network_switches: "{{ network_switches }}" + network_switches_ports: "{{ network_switches_ports }}" + vrf_config: "{{ vrf_config }}" + vrf_attach_config: "{{ vrf_attach_config }}" +- name: Run Diff Flags + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Fabric Changes Detected - [ {{ vars_common_mcfg.changes_detected_fabric }} ]" + - "+ VRFs Changes Detected - [ {{ vars_common_mcfg.changes_detected_vrfs }} ]" + - "+ Networks Changes Detected - [ {{ vars_common_mcfg.changes_detected_networks }} ]" + - "+ ----- Run Map -----" + - "+ Run Map Diff Run - [ {{ run_map_read_result.diff_run }} ]" + - "+ Force Run Flag - [ {{ force_run_all }} ]" + - "----------------------------------------------------------------" diff --git a/roles/dtc/common/templates/ndfc_attach_networks.j2 b/roles/dtc/common/templates/ndfc_attach_networks.j2 index f0b764f28..7d2d774af 100644 --- a/roles/dtc/common/templates/ndfc_attach_networks.j2 +++ b/roles/dtc/common/templates/ndfc_attach_networks.j2 @@ -13,5 +13,10 @@ {# Include NDFC MSD Base Template #} {% include '/ndfc_networks/msd_fabric/msd_fabric_networks.j2' %} -{# Supported fabric types are: DC VXLAN EVPN and MSDs #} +{% elif vxlan.fabric.type == 'MCFG'%} + +{# Include NDFC MCFG Base Template #} +{% include '/ndfc_networks/mcfg_fabric/ndfc_attach_networks.j2' %} + +{# Supported fabric types are: DC VXLAN EVPN, MSDs and MCFGs #} {% endif %} \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_attach_vrfs.j2 b/roles/dtc/common/templates/ndfc_attach_vrfs.j2 index f9a538451..b2a4b67ae 100644 --- a/roles/dtc/common/templates/ndfc_attach_vrfs.j2 +++ b/roles/dtc/common/templates/ndfc_attach_vrfs.j2 @@ -14,5 +14,10 @@ {% include '/ndfc_vrfs/msd_fabric/msd_fabric_vrfs.j2' %} {# {% include '/ndfc_vrfs/msd_fabric/child_fabric/msd_child_fabric_vrfs.j2' %} #} -{# Supported fabric types are: DC VXLAN EVPN and ISN #} +{% elif vxlan.fabric.type == 'MCFG'%} + +{# Include NDFC MCFG Base Template #} +{% include '/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs.j2' %} + +{# Supported fabric types are: DC VXLAN EVPN, MSDs and MCFGs #} {% endif %} diff --git a/roles/dtc/common/templates/ndfc_fabric.j2 b/roles/dtc/common/templates/ndfc_fabric.j2 index 6c8613088..40251a2c6 100644 --- a/roles/dtc/common/templates/ndfc_fabric.j2 +++ b/roles/dtc/common/templates/ndfc_fabric.j2 @@ -19,6 +19,11 @@ {# Include NDFC MSD Base Template #} {% include '/ndfc_fabric/msd_fabric/msd_fabric_base.j2' %} +{% elif vxlan.fabric.type == 'MCFG'%} + +{# Include NDFC MCFG Base Template #} +{% include '/ndfc_fabric/mcfg_fabric/mcfg_fabric_base.j2' %} + {% elif vxlan.fabric.type == 'ISN'%} {# Include NDFC ISN Base Template #} @@ -29,5 +34,5 @@ {# Include NDFC External Fabric Base Template #} {% include '/ndfc_fabric/dc_external_fabric/dc_external_fabric_base.j2' %} -{# Supported fabric types are: DC VXLAN EVPN, MSD, ISN, External #} +{# Supported fabric types are: DC VXLAN EVPN, MSD, MCFG, ISN, External #} {% endif %} diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/.gitkeep b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/dci/.gitkeep b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/dci/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/dci/mcfg_fabric_dci.j2 b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/dci/mcfg_fabric_dci.j2 new file mode 100644 index 000000000..f9778341e --- /dev/null +++ b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/dci/mcfg_fabric_dci.j2 @@ -0,0 +1,29 @@ +{# Auto-generated NDFC MSD DCI config data structure for fabric {{ vxlan.fabric.name }} #} + BORDER_GWY_CONNECTIONS: {{ vxlan.multisite.overlay_dci.deployment_method | default(defaults.vxlan.multisite.overlay_dci.deployment_method) }} +{% if ( (vxlan.multisite.overlay_dci.deployment_method | default(defaults.vxlan.multisite.overlay_dci.deployment_method) == 'Centralized_To_Route_Server') and + (vxlan.multisite.overlay_dci.route_server is defined and vxlan.multisite.overlay_dci.route_server) ) %} + RP_SERVER_IP: "{{ vxlan.multisite.overlay_dci.route_server.peers | map(attribute='ip_address') | list | join(",") | trim }}" + BGP_RP_ASN: "{{ vxlan.multisite.overlay_dci.route_server.peers | map(attribute='bgp_asn') | list | join(",") | trim }}" + ENABLE_RS_REDIST_DIRECT: {{ vxlan.multisite.overlay_dci.route_server.redistribute_direct | default(defaults.vxlan.multisite.overlay_dci.route_server.redistribute_direct) }} +{% if vxlan.multisite.overlay_dci.route_server.redistribute_direct | default(defaults.vxlan.multisite.overlay_dci.route_server.redistribute_direct) %} + RS_ROUTING_TAG: {{ vxlan.multisite.overlay_dci.route_server.ip_tag | default(defaults.vxlan.multisite.overlay_dci.route_server.ip_tag) }} +{% endif %} +{% endif %} + MS_UNDERLAY_AUTOCONFIG: {{ vxlan.multisite.overlay_dci.underlay_autoconfig | default(defaults.vxlan.multisite.overlay_dci.underlay_autoconfig) }} +{% if (vxlan.multisite.overlay_dci.underlay_autoconfig | default(defaults.vxlan.multisite.overlay_dci.underlay_autoconfig) | ansible.builtin.bool) is sameas false %} + ENABLE_BGP_SEND_COMM: {{ vxlan.multisite.overlay_dci.enable_bgp_send_community | default(defaults.vxlan.multisite.overlay_dci.enable_bgp_send_community) }} + ENABLE_BGP_LOG_NEIGHBOR_CHANGE: {{ vxlan.multisite.overlay_dci.enable_bgp_log_neighbor_change | default(defaults.vxlan.multisite.overlay_dci.enable_bgp_log_neighbor_change) | lower }} + ENABLE_BGP_BFD: {{ vxlan.multisite.overlay_dci.enable_bgp_bfd | default(defaults.vxlan.multisite.overlay_dci.enable_bgp_bfd) }} +{% endif %} + DELAY_RESTORE: {{ vxlan.multisite.overlay_dci.delay_restore | default(defaults.vxlan.multisite.overlay_dci.delay_restore) }} + MS_IFC_BGP_PASSWORD_ENABLE: {{ vxlan.multisite.overlay_dci.enable_ebgp_password | default(defaults.vxlan.multisite.overlay_dci.enable_ebgp_password) }} +{% if vxlan.multisite.overlay_dci.enable_ebgp_password | default(defaults.vxlan.multisite.overlay_dci.enable_ebgp_password) %} + MS_IFC_BGP_PASSWORD: {{ vxlan.multisite.overlay_dci.ebgp_password }} + MS_IFC_BGP_AUTH_KEY_TYPE: {{ vxlan.multisite.overlay_dci.ebgp_password_encryption_type }} +{% else %} + MS_IFC_BGP_PASSWORD: "" + MS_IFC_BGP_AUTH_KEY_TYPE: "" + MS_IFC_BGP_PASSWORD_ENABLE_PREV: "" + MS_IFC_BGP_AUTH_KEY_TYPE_PREV: "" + MS_IFC_BGP_PASSWORD_PREV: "" +{% endif %} \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/general/.gitkeep b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/general/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/general/mcfg_fabric_general.j2 b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/general/mcfg_fabric_general.j2 new file mode 100644 index 000000000..ebb452421 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/general/mcfg_fabric_general.j2 @@ -0,0 +1,21 @@ +{# Auto-generated NDFC MSD General config data structure for fabric {{ vxlan.fabric.name }} #} + VXLAN_UNDERLAY_IS_V6: {{ vxlan.multisite.enable_ipv6_underlay | default(defaults.vxlan.multisite.enable_ipv6_underlay) }} +{% if not vxlan.multisite.enable_ipv6_underlay | default(defaults.vxlan.multisite.enable_ipv6_underlay) %} + ENABLE_PVLAN: false +{% endif %} + ANYCAST_GW_MAC: {{ vxlan.multisite.anycast_gateway_mac | default(defaults.vxlan.multisite.anycast_gateway_mac) }} + MS_LOOPBACK_ID: "{{ vxlan.multisite.vtep_loopback_id | default(defaults.vxlan.multisite.vtep_loopback_id) }}" + BGW_ROUTING_TAG: "{{ vxlan.multisite.bgw_ip_tag | default(defaults.vxlan.multisite.bgw_ip_tag) }}" + TOR_AUTO_DEPLOY: false + FF: "MSD" + FABRIC_TYPE: "MFD" + FABRIC_NAME: "{{ vxlan.fabric.name }}" + L2_SEGMENT_ID_RANGE: "{{ (vxlan.multisite.layer2_vni_range.from | default(defaults.vxlan.multisite.layer2_vni_range.from)) ~ "-" ~ (vxlan.multisite.layer2_vni_range.to | default(defaults.vxlan.multisite.layer2_vni_range.to)) }}" + L3_PARTITION_ID_RANGE: "{{ (vxlan.multisite.layer3_vni_range.from | default(defaults.vxlan.multisite.layer3_vni_range.from)) ~ "-" ~ (vxlan.multisite.layer3_vni_range.to | default(defaults.vxlan.multisite.layer3_vni_range.to)) }}" + default_vrf: "Default_VRF_Universal" + default_network: "Default_Network_Universal" + vrf_extension_template: "Default_VRF_Extension_Universal" + network_extension_template: "Default_Network_Extension_Universal" + enableScheduledBackup: "" + scheduledTime: "" +{# #} \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/mcfg_fabric_base.j2 b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/mcfg_fabric_base.j2 new file mode 100644 index 000000000..414aca7f9 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/mcfg_fabric_base.j2 @@ -0,0 +1,13 @@ +{# Auto-generated NDFC MultiSite Federated Domain (MCFG) Base config data structure for fabric {{ vxlan.fabric.name }} #} + +{# Include NDFC DC VXLAN EVPN General Template #} +{% include '/ndfc_fabric/mcfg_fabric/general/mcfg_fabric_general.j2' %} + +{# Include NDFC DC VXLAN EVPN DCI Template #} +{% include '/ndfc_fabric/mcfg_fabric/dci/mcfg_fabric_dci.j2' %} + +{# Include NDFC DC VXLAN EVPN Security Template #} +{% include '/ndfc_fabric/mcfg_fabric/security/mcfg_fabric_security.j2' %} + +{# Include NDFC DC VXLAN EVPN Resources Template #} +{% include '/ndfc_fabric/mcfg_fabric/resources/mcfg_fabric_resources.j2' %} diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/resources/.gitkeep b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/resources/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/resources/mcfg_fabric_resources.j2 b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/resources/mcfg_fabric_resources.j2 new file mode 100644 index 000000000..8ba6bad0e --- /dev/null +++ b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/resources/mcfg_fabric_resources.j2 @@ -0,0 +1,13 @@ +{# Auto-generated NDFC MSD Resources config data structure for fabric {{ vxlan.fabric.name }} #} +{% if not defaults.vxlan.multisite.enable_ipv6_underlay %} + LOOPBACK100_IP_RANGE: {{ vxlan.multisite.ipv4_vtep_loopback_range | default(defaults.vxlan.multisite.ipv4_vtep_loopback_range) }} + DCI_SUBNET_RANGE: {{ vxlan.multisite.overlay_dci.ipv4_dci_subnet_range | default(defaults.vxlan.multisite.overlay_dci.ipv4_dci_subnet_range) }} + DCI_SUBNET_TARGET_MASK: "{{ vxlan.multisite.overlay_dci.ipv4_dci_subnet_mask | default(defaults.vxlan.multisite.overlay_dci.ipv4_dci_subnet_mask) }}" + LOOPBACK100_IPV6_RANGE: "" + V6_DCI_SUBNET_RANGE: "" + V6_DCI_SUBNET_TARGET_MASK: "" +{% else %} + LOOPBACK100_IPV6_RANGE: {{ vxlan.multisite.ipv6_vtep_loopback_range | default(defaults.vxlan.multisite.ipv6_vtep_loopback_range) }} + V6_DCI_SUBNET_RANGE: {{ vxlan.multisite.overlay_dci.ipv6_dci_subnet_range | default(defaults.vxlan.multisite.overlay_dci.ipv6_dci_subnet_range) }} + V6_DCI_SUBNET_TARGET_MASK: "{{ vxlan.multisite.overlay_dci.ipv6_dci_subnet_mask | default(defaults.vxlan.multisite.overlay_dci.ipv6_dci_subnet_mask) }}" +{% endif %} \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/security/.gitkeep b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/security/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/security/mcfg_fabric_security.j2 b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/security/mcfg_fabric_security.j2 new file mode 100644 index 000000000..e7cb3ec27 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_fabric/mcfg_fabric/security/mcfg_fabric_security.j2 @@ -0,0 +1,7 @@ +{# Auto-generated NDFC MSD Security config data structure for fabric {{ vxlan.fabric.name }} #} + ENABLE_SGT: "off" + CLOUDSEC_KEY_STRING: "" + CLOUDSEC_ALGORITHM: "" + CLOUDSEC_ENFORCEMENT: "" + CLOUDSEC_REPORT_TIMER: "" + CLOUDSEC_AUTOCONFIG: "false" diff --git a/roles/dtc/common/templates/ndfc_fabric_children.j2 b/roles/dtc/common/templates/ndfc_fabric_children.j2 new file mode 100644 index 000000000..f5c698fb0 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_fabric_children.j2 @@ -0,0 +1,9 @@ +[ +{% for child in MD_Extended.vxlan.multisite.child_fabrics %} +{ + 'operation': "add", + 'clusterName': "{{ child.cluster }}", + 'fabricName': "{{ child.name }}" +}{% if not loop.last %},{% endif %} +{% endfor %} +] \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_interfaces/ndfc_interface_breakout.j2 b/roles/dtc/common/templates/ndfc_interfaces/ndfc_interface_breakout.j2 index 819a7de4b..8c8804c4d 100644 --- a/roles/dtc/common/templates/ndfc_interfaces/ndfc_interface_breakout.j2 +++ b/roles/dtc/common/templates/ndfc_interfaces/ndfc_interface_breakout.j2 @@ -2,7 +2,6 @@ # This NDFC breakout interface data structure is auto-generated # DO NOT EDIT MANUALLY # - {% for switch in MD_Extended.vxlan.topology.switches %} {% if switch.interface_breakouts is defined %} {% for breakout in switch.interface_breakouts %} diff --git a/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks.j2 b/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks.j2 new file mode 100644 index 000000000..4a1254ef4 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks.j2 @@ -0,0 +1,17 @@ + +[{% for net in MD_Extended.vxlan.multisite.overlay.networks %} +{ + "networkName": "{{ net['name'] }}", + "networkId":"{{ net['net_id'] }}", + "vrf":"{{ net['vrf_name'] | default('NA') }}", + "networkTemplate":"Default_Network_Universal", + "networkExtensionTemplate":"Default_Network_Extension_Universal", + "networkTemplateConfig":"{\"gatewayIpAddress\":\"{{net['gw_ip_address'] | default('')}}\",\"gatewayIpV6Address\":\"\",\"vlanName\":\"{{net['vlan_name'] | default(defaults.vxlan.multisite.overlay.networks.vlan_name)}}\",\"intfDescription\":\"{{net['int_desc'] | default(defaults.vxlan.multisite.overlay.networks.net_description)}}\",\"mtu\":\"{{net['mtu_l3intf'] | default(defaults.vxlan.multisite.overlay.networks.mtu_l3intf)}}\",\"secondaryGW1\":\"\",\"secondaryGW2\":\"\",\"secondaryGW3\":\"\",\"secondaryGW4\":\"\",\"type\":\"\",\"suppressArp\":{{net['arp_suppress'] | default(defaults.vxlan.multisite.overlay.networks.arp_supress)|string|lower}},\"tag\":\"{{net['route_tag'] | default(defaults.vxlan.multisite.overlay.networks.route_tag)}}\",\"rtBothAuto\":\"false\",\"vlanId\":\"{{net['vlan_id']}}\",\"segmentId\":\"{{ net['name']}}\",\"vrfName\":\"{{net['vrf_name'] | default('NA')}}\",\"networkName\":\"{{net['name']}}\",\"nveId\":\"1\",\"isLayer2Only\":{{net['is_l2_only'] | default(defaults.vxlan.multisite.overlay.networks.is_l2_only) |string|lower}},\"gen_address\":\"\",\"gen_mask\":\"\",\"flagSet\":\"\",\"isIpDhcpRelay\":\"\",\"isIp6DhcpRelay\":\"\",\"switchRole\":\"\"}", + "fabric":"{{ MD.vxlan.fabric.name }}", + "type":null, + "source":null, + "serviceNetworkTemplate":null, + "displayName":"{{ net['name'] }}" +}{% if not loop.last %},{% endif %} +{% endfor %} +] \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches.j2 b/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches.j2 new file mode 100644 index 000000000..f09fe6277 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches.j2 @@ -0,0 +1,16 @@ +{ + {% for attach_group in MD_Extended.vxlan.multisite.overlay.network_attach_groups %} + {% for switch in attach_group['switches']%} + {% for switch_inv in switches_in_fabric%} + {% if switch_inv.logicalName == switch['hostname']%} + {{switches.append(switch_inv.serialNumber)}} + {% endif%} + {% endfor%} + {% endfor%} + {% endfor%} + {% for network in MD_Extended.vxlan.multisite.overlay.networks %} + {{ networks.append(network.name )}} + {% endfor %} + 'networks':'{{ networks | join(",")}}', + 'switches':'{{switches |unique|list | join(",")}}' +} \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches_ports.j2 b/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches_ports.j2 new file mode 100644 index 000000000..605271403 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_networks/mcfg_fabric/ndfc_attach_networks_switches_ports.j2 @@ -0,0 +1,52 @@ +[ +{% for network in MD_Extended.vxlan.multisite.overlay.networks %} +{% if network['network_attach_group'] is defined %} + {% for attach_group in MD_Extended.vxlan.multisite.overlay.network_attach_groups %} + {% if network.network_attach_group is defined and network.network_attach_group == attach_group.name %} + {% for switch in attach_group['switches'] %} + { + 'networkName':'{{ network['name'] }}', + 'switchName': '{{ switch['hostname'] }}', + {% for switch_inv in switches_in_fabric %} + {% if switch_inv.logicalName == switch['hostname'] %} + 'switchIP':'{{ switch_inv.ipAddress }}', + 'switchSN':'{{ switch_inv.serialNumber }}', + 'peerSwitchName':'{{ switch_inv.intentedpeerName | default("") }}', + 'fabricName':'{{ switch_inv.fabricName }}', + 'clusterName':'{{ switch_inv.clusterName }}', + 'ndfcIpAddress':'{{ switch_inv.clusterIp }}', + {% if switch.tors is defined %} + {% set torString = '' %} + {% set torPortString = '' %} + {% for tor in switch.tors %} + {% if loop.last %} + {% set torString = torString ~ tor.hostname %} + {% set torPortString = torPortString ~ ' ' ~ tor.hostname ~ '(' ~ tor.ports | join(",") ~ ')' %} + 'allSwitches':'["{{ switch['hostname'] }}","{{ switch_inv.intentedpeerName }}","{{ torString }}"]', + 'ports':'{{ switch.hostname }}({{ switch.ports | join(",") }}){{ torPortString }}', + 'torSwitches':'{{ torString }}' + {% else %} + {% set torPortString = torPortString ~ ' ' ~ tor.hostname ~ '(' ~ tor.ports | join(",") ~ '),' %} + {% set torString = torString ~ tor.hostname ~ '",' %} + {% endif %} + {% endfor %} + {% else %} + 'ports':'{{ switch.hostname }}({{ switch.ports | join(",") }})', + 'torSwitches': null, + 'allSwitches':["{{ switch['hostname'] }}","{{ switch_inv.intentedpeerName }}"] + {% endif %} + {% endif %} + {% endfor %} + + + + + } + {% if not loop.last %},{% endif %} + {% endfor %} + {% endif %} + {% endfor %} + {% if not loop.last %},{% endif %} +{% endif %} +{% endfor %} +] \ No newline at end of file diff --git a/roles/dtc/common/templates/ndfc_policy.j2 b/roles/dtc/common/templates/ndfc_policy.j2 index 8a227fac6..ec8842431 100644 --- a/roles/dtc/common/templates/ndfc_policy.j2 +++ b/roles/dtc/common/templates/ndfc_policy.j2 @@ -35,7 +35,7 @@ {{ value | indent(14) }} {% elif key in ["BANNERDELIMITER"] %} {{ key }}: "{{ value }}" -{% elif key in ["asn", "BGP_AS", "BGP_ASN", "NEIGHBOR_ASN"] %} +{% elif key in ["asn", "BGP_AS", "BGP_ASN", "NEIGHBOR_ASN", "COM"] %} {{ key }}: "{{ value | string | indent(12) | trim }}" {% elif value is iterable and '\n' in value %} {{ key }}: |- diff --git a/roles/dtc/common/templates/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs.j2 b/roles/dtc/common/templates/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs.j2 new file mode 100644 index 000000000..c47444268 --- /dev/null +++ b/roles/dtc/common/templates/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs.j2 @@ -0,0 +1,15 @@ +[ + {% for vrf in MD_Extended.vxlan.multisite.overlay.vrfs %} + { + "fabric":"{{ MD.vxlan.fabric.name }}", + "vrfName":"{{ vrf.name}}", + "vrfId":"{{ vrf.vrf_id }}", + "vrfTemplate":"Default_VRF_Universal", + "vrfTemplateConfig": "{\"vrfVlanName\":\"{{ vrf.vrf_vlan_name | default(vrf.vlan_id)}}\",\"vrfIntfDescription\":\"{{ vrf.vrf_intf_desc | default(defaults.vxlan.multisite.overlay.vrfs.vrf_intf_desc) }}\",\"vrfDescription\":\"{{ vrf.vrf_description | default(defaults.vxlan.multisite.overlay.vrfs.vrf_description) }}\",\"mtu\":\"{{ vrf.vrf_int_mtu | default(defaults.vxlan.multisite.overlay.vrfs.vrf_int_mtu) }}\",\"tag\":\"{{ vrf.loopback_route_tag | default(defaults.vxlan.multisite.overlay.vrfs.loopback_route_tag)}}\",\"vrfRouteMap\":\"{{ vrf.redist_direct_routemap | default(defaults.vxlan.multisite.overlay.vrfs.redist_direct_routemap) }}\",\"v6VrfRouteMap\":\"\",\"maxBgpPaths\":\"{{ vrf.max_bgp_paths | default(defaults.vxlan.multisite.overlay.vrfs.max_bgp_paths) }}\",\"maxIbgpPaths\":\"{{ vrf['max_ibgp_paths'] | default(defaults.vxlan.multisite.overlay.vrfs.max_ibgp_paths) }}\",\"ipv6LinkLocalFlag\":\"{{ vrf['ipv6_linklocal_enable'] | default(defaults.vxlan.multisite.overlay.vrfs.ipv6_linklocal_enable) }}\",\"disableRtAuto\":\"{{ vrf['disable_rt_auto'] | default(defaults.vxlan.multisite.overlay.vrfs.disable_rt_auto)}}\",\"routeTargetImport\":\"\",\"routeTargetExport\":\"\",\"routeTargetImportEvpn\":\"\",\"routeTargetExportEvpn\":\"\",\"vrfName\":\"{{ vrf.name }}\",\"vrfVlanId\":\"{{ vrf.vlan_id }}\",\"vrfSegmentId\":\"{{ vrf.vrf_id }}\",\"nveId\":\"1\",\"asn\":\"\"}", + "serviceVrfTemplate": null, + "displayName": "{{ vrf.name}}", + "vrfExtensionTemplate":"Default_VRF_Extension_Universal" + }{% if not loop.last %},{% endif %} + {% endfor %} +] + diff --git a/roles/dtc/common/templates/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs_loopbacks.j2 b/roles/dtc/common/templates/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs_loopbacks.j2 new file mode 100644 index 000000000..a94e57ebe --- /dev/null +++ b/roles/dtc/common/templates/ndfc_vrfs/mcfg_fabric/ndfc_attach_vrfs_loopbacks.j2 @@ -0,0 +1,29 @@ +[ + {% for vrf in MD_Extended.vxlan.multisite.overlay.vrfs %} + {% if vrf['vrf_attach_group'] is defined %} + { + "vrfName": "{{ vrf["name"] }}", + "lanAttachList": + [ + {% for attach in MD_Extended.vxlan.multisite.overlay.vrf_attach_groups_dict[vrf["vrf_attach_group"]] %} + { + + "vrfName": "{{ vrf["name"] }}", + {% for switch in switches_in_fabric %} + {% if switch.logicalName == attach["hostname"] %} + "fabric": "{{ switch.fabricName }}", + "serialNumber": "{{ switch.serialNumber }}", + {% endif %} + {% endfor %} + "freeformConfig": "{{ attach['freeform_config'] | default('') | replace('\n', '\\n') | replace('"', '\\"') }}", + "extensionValues": "", + "vlan": {{ vrf["vlan_id"] }}, + "deployment": true, + "instanceValues": "{\"loopbackId\": \"{{ attach['loopback_id'] | default('') }}\", \"loopbackIpAddress\": \"{{ attach['loopback_ipv4'] | default('') }}\", \"loopbackIpV6Address\":\"{{ attach['loopback_ipv6'] | default('') }}\"}" + }{% if not loop.last %},{% endif %} + {% endfor %} + ] + }{% if not loop.last %},{% endif %} + {% endif %} + {% endfor %} +] diff --git a/roles/dtc/create/tasks/main.yml b/roles/dtc/create/tasks/main.yml index 2b99ea444..fedd8d97d 100644 --- a/roles/dtc/create/tasks/main.yml +++ b/roles/dtc/create/tasks/main.yml @@ -43,6 +43,10 @@ ansible.builtin.import_tasks: sub_main_msd.yml when: MD_Extended.vxlan.fabric.type == 'MSD' +- name: Import MCFG Fabric Role Tasks + ansible.builtin.import_tasks: sub_main_mcfg.yml + when: MD_Extended.vxlan.fabric.type == 'MCFG' + - name: Import External Fabric Role Tasks ansible.builtin.import_tasks: sub_main_external.yml when: diff --git a/roles/dtc/create/tasks/mcfg/child_fabrics.yml b/roles/dtc/create/tasks/mcfg/child_fabrics.yml new file mode 100644 index 000000000..91cd8c916 --- /dev/null +++ b/roles/dtc/create/tasks/mcfg/child_fabrics.yml @@ -0,0 +1,37 @@ +# 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 + +--- + +- name: Manage Fabric Children Entry Point + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Manage Fabric Children {{ MD_Extended.vxlan.fabric.name }}" + - "----------------------------------------------------------------" + +- name: Manage fabric Federated {{ MD_Extended.vxlan.fabric.name }} in NDFC (PUT) + cisco.dcnm.dcnm_rest: + method: PUT + path: '/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/members' + json_data: '{{ child | to_json }}' + register: post_result + when: child.fabricName not in existing_fabric_names \ No newline at end of file diff --git a/roles/dtc/create/tasks/mcfg/fabric.yml b/roles/dtc/create/tasks/mcfg/fabric.yml new file mode 100644 index 000000000..296743248 --- /dev/null +++ b/roles/dtc/create/tasks/mcfg/fabric.yml @@ -0,0 +1,85 @@ +# 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 + +--- + +- name: Manage Fabric Entry Point + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Manage Fabric {{ MD_Extended.vxlan.fabric.name }}" + - "----------------------------------------------------------------" + +- name: Check if fabric MCFG {{ MD_Extended.vxlan.fabric.name }} exists in NDFC + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}" + register: get_result + failed_when: false + +- name: Set payload + set_fact: + payload: + templateName: "MSD_Fabric" + fabricName: "{{ MD_Extended.vxlan.fabric.name }}" + fabricType: "MFD" + fabricTechnology: "VXLANFabric" + nvPairs: "{{ fabric_config }}" + +- name: Manage fabric federated {{ MD_Extended.vxlan.fabric.name }} in NDFC (PUT) + cisco.dcnm.dcnm_rest: + method: PUT + path: '/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}' + json_data: '{{ payload | to_json }}' + when: + - get_result.response is defined + - get_result.response.RETURN_CODE == 200 + register: put_result + +- name: Manage fabric federated {{ MD_Extended.vxlan.fabric.name }} in NDFC (POST) + cisco.dcnm.dcnm_rest: + method: POST + path: '/appcenter/cisco/ndfc/api/v1/onemanage/fabrics' + json_data: '{{ payload | to_json }}' + when: get_result.response is not defined + register: post_result + +- ansible.builtin.debug: + var: fabric_children + +- name: Check if fabric already a member of NDFC + cisco.dcnm.dcnm_rest: + method: GET + path: '/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/members' + register: member_result + +- name: Set fact for existing fabric names + set_fact: + existing_fabric_names: >- + {{ + member_result.response.DATA | map(attribute='fabrics') | map('dict2items') | flatten | map(attribute='key') | list + }} + +- name: Add Child Fabrics To Fabric - {{ MD_Extended.vxlan.fabric.name }} + include_tasks: child_fabrics.yml + loop: "{{ fabric_children }}" + loop_control: + loop_var: child diff --git a/roles/dtc/create/tasks/mcfg/network_fed_config.yml b/roles/dtc/create/tasks/mcfg/network_fed_config.yml new file mode 100644 index 000000000..b35a99f6e --- /dev/null +++ b/roles/dtc/create/tasks/mcfg/network_fed_config.yml @@ -0,0 +1,55 @@ +# 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 + +--- +- name: Manage Networks for Federated NDFC + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Manage Networks for Federated Fabrics {{ MD.vxlan.fabric.name }}" + - "----------------------------------------------------------------" + tags: "{{ nac_tags.create_vrfs_networks }}" +# -------------------------------------------------------------------- +# Manage Network Configuration on NDFC +# -------------------------------------------------------------------- + +- name: Get Existing Network ID for Federated Fabric + set_fact: + network_id_list: "{{ fed_networks_existing | json_query(network_query) }}" + vars: + network_query: "response.DATA[?displayName=='{{network.displayName}}'].networkId" + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Update Network for Federated NDFCs + cisco.dcnm.dcnm_rest: + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD.vxlan.fabric.name }}/networks/{{ network.displayName }}" + method: "PUT" + json_data: "{{ network | to_json}}" + when: network_id_list|default(None)|length != 0 + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Configure Network for Federated NDFCs + cisco.dcnm.dcnm_rest: + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD.vxlan.fabric.name }}/networks" + method: "POST" + json_data: "{{ network | to_json}}" + when: network_id_list|default(None)|length == 0 + tags: "{{ nac_tags.create_vrfs_networks }}" \ No newline at end of file diff --git a/roles/dtc/create/tasks/mcfg/vrf_fed_config.yml b/roles/dtc/create/tasks/mcfg/vrf_fed_config.yml new file mode 100644 index 000000000..53b92ad32 --- /dev/null +++ b/roles/dtc/create/tasks/mcfg/vrf_fed_config.yml @@ -0,0 +1,63 @@ +# 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 + +--- + +- name: Manage VRFs for Federated NDFC + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Manage VRFs for Federated Fabrics {{ MD_Extended.vxlan.fabric.name }} and {{ vrf.displayName }} " + - "----------------------------------------------------------------" + tags: "{{ nac_tags.create_vrfs_networks }}" +# -------------------------------------------------------------------- +# Manage VRF Configuration on NDFC +# -------------------------------------------------------------------- + +- name: Get existing VRFs in Federated Fabric + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs" + register: dcnm_vrf_existing + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Check if VRF Exists + set_fact: + vrf_exist_result: "{{ dcnm_vrf_existing | json_query(vrf_query) }}" + vars: + vrf_query: "response.DATA[?vrfName=='{{vrf.displayName}}'].vrfName" + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Update VRF for Federated NDFCs + cisco.dcnm.dcnm_rest: + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs/{{ vrf.displayName }}" + method: "PUT" + json_data: "{{ vrf | to_json}}" + when: vrf_exist_result|default(None)|length != 0 + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Configure VRF for Federated NDFCs + cisco.dcnm.dcnm_rest: + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs" + method: "POST" + json_data: "{{ vrf | to_json}}" + when: vrf_exist_result|default(None)|length == 0 + tags: "{{ nac_tags.create_vrfs_networks }}" \ No newline at end of file diff --git a/roles/dtc/create/tasks/mcfg/vrf_loopbacks.yml b/roles/dtc/create/tasks/mcfg/vrf_loopbacks.yml new file mode 100644 index 000000000..d05daa984 --- /dev/null +++ b/roles/dtc/create/tasks/mcfg/vrf_loopbacks.yml @@ -0,0 +1,67 @@ +# 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 + +--- +- name: Manage VRFs and Networks Entry Point + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Manage VRFs and Networks Fabric {{ MD.vxlan.fabric.name }}" + - "----------------------------------------------------------------" + +# -------------------------------------------------------------------- +# Manage VRF Configuration on NDFC +# -------------------------------------------------------------------- + +- name: Set file_name Var + ansible.builtin.set_fact: + file_name: "{{vrf.name}}_attach_vrfs_loopbacks.yml" + fabric_name: "{{ MD.vxlan.fabric.name }}" + delegate_to: localhost + +- name: Stat Previous File If It Exists + ansible.builtin.stat: + path: "{{ role_path }}/../common/files/mcfg/{{ MD.vxlan.fabric.name}}/{{ file_name }}" + register: data_file_previous + delegate_to: localhost + +- name: Set vrf_loopback_config Var + ansible.builtin.set_fact: + vrf_loopback_config: "{{ lookup('file', role_path + '/../common/files/mcfg/'+ fabric_name +'/' + file_name) | from_json }}" + when: (MD_Extended.vxlan.multisite.overlay.vrfs | default([])) | length > 0 and data_file_previous.stat.exists + delegate_to: localhost + +- name: Create complete payload for attaching VRF to Switches. + set_fact: + attach_payload: "{{ attach_payload | default([]) + [{'vrfName':vrf.name, 'lanAttachList':vrf_loopback_config}]}}" + +- name: Call API to attach VRF to Leaf Switches for Federated NDFCs + cisco.dcnm.dcnm_rest: + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD.vxlan.fabric.name }}/vrfs/attachments" + method: "POST" + json_data: "{{ attach_payload | to_json}}" + +- name: Clear payload + set_fact: + vrf_loopback_config: [] + attach_payload: [] + switches: [] + attach_list: [] diff --git a/roles/dtc/create/tasks/mcfg/vrfs_networks.yml b/roles/dtc/create/tasks/mcfg/vrfs_networks.yml new file mode 100644 index 000000000..e7e90cdfd --- /dev/null +++ b/roles/dtc/create/tasks/mcfg/vrfs_networks.yml @@ -0,0 +1,223 @@ +# 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 + +--- +- name: Manage VRFs and Networks for MCFG Entry Point + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Manage VRFs and Networks MCFG Fabric {{ MD_Extended.vxlan.fabric.name }}" + - "----------------------------------------------------------------" + tags: "{{ nac_tags.create_vrfs_networks }}" +# -------------------------------------------------------------------- +# Manage VRF Configuration for Federated NDFCs +# -------------------------------------------------------------------- + +# - name: Set filename for federated +# ansible.builtin.set_fact: +# file_name: "ndfc_fed_attach_vrfs.yml" +# when: +# - MD_Extended.vxlan.multisite.overlay.vrfs is defined +# - changes_detected_vrfs +# - MD_Extended.vxlan.fabric.type == 'MCFG' + +# - name: Load VRF federated details from file +# ansible.builtin.set_fact: +# fed_vrf_config: "{{ lookup('file', role_path + '/../common/files/mcfg/{{ MD_Extended.vxlan.fabric.name }}/' + file_name) | from_json }}" +# when: +# - MD_Extended.vxlan.multisite.overlay.vrfs is defined +# - changes_detected_vrfs +# - MD_Extended.vxlan.fabric.type == 'MCFG' + +- name: Get existing VRFs in Federated Fabric + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs" + register: dcnm_vrf_existing + when: + - MD_Extended.vxlan.multisite.overlay.vrfs is defined + - MD_Extended.vxlan.multisite.overlay.vrfs + - vars_common_mcfg.changes_detected_vrfs + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Manage NDFC Fabric VRFs for Federated + ansible.builtin.include_tasks: vrf_fed_config.yml + loop: "{{ vars_common_mcfg.vrf_config }}" + loop_control: + loop_var: vrf + when: + - MD_Extended.vxlan.multisite.overlay.vrfs is defined + - MD_Extended.vxlan.multisite.overlay.vrfs + - vars_common_mcfg.changes_detected_vrfs + tags: "{{ nac_tags.create_vrfs_networks }}" + +# -------------------------------------------------------------------- +# Manage Loopback VRF attachments on NDFC (MCFG) +# -------------------------------------------------------------------- + +- name: Call API to attach VRF to Leaf Switches for Federated NDFCs + cisco.dcnm.dcnm_rest: + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs/attachments + method: POST + json_data: "{{ vars_common_mcfg.vrf_attach_config | to_json }}" + when: + - MD_Extended.vxlan.multisite.overlay.vrfs is defined + - MD_Extended.vxlan.multisite.overlay.vrfs + - vars_common_mcfg.changes_detected_vrfs + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Get vrf attachments list + cisco.dcnm.dcnm_rest: + method: GET + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs/attachments + register: vrfAttachmentList + when: + - MD_Extended.vxlan.multisite.overlay.vrfs is defined + - MD_Extended.vxlan.multisite.overlay.vrfs + - vars_common_mcfg.changes_detected_vrfs + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Filter vrf attachments to be removed + cisco.nac_dc_vxlan.dtc.fed_overlay_check: + model_data: "{{ MD_Extended }}" + ndfc_data: "{{ switch_list_create.response.DATA }}" + ndfc_attachment_data: "{{ vrfAttachmentList.response.DATA }}" + check_type: "vrf_attach" + register: not_required_vrfs + when: + - MD_Extended.vxlan.multisite.overlay.vrfs is defined + - MD_Extended.vxlan.multisite.overlay.vrfs + - vars_common_mcfg.changes_detected_vrfs + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Remove vrf attachments + cisco.dcnm.dcnm_rest: + method: POST + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs/attachments + json_data: "{{ not_required_vrfs.attachments_payload | to_json }}" + register: vrf_attachment_result + when: + - MD_Extended.vxlan.multisite.overlay.vrfs is defined + - MD_Extended.vxlan.multisite.overlay.vrfs + - vars_common_mcfg.changes_detected_vrfs + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Manage NDFC Child Fabric VRFs + cisco.nac_dc_vxlan.dtc.manage_child_fabric_vrfs: + nd_version: "{{ nd_version }}" + msite_data: "{{ MD_Multisite }}" + fabric_type: "{{ MD_Extended.vxlan.fabric.type }}" + register: child_fabric_vrf_results + when: + - MD_Extended.vxlan.multisite.overlay.vrfs is defined + - MD_Extended.vxlan.multisite.overlay.vrfs + - vars_common_mcfg.changes_detected_vrfs + tags: "{{ nac_tags.create_vrfs_networks }}" + +# -------------------------------------------------------------------- +# Manage Network Configuration for Federated NDFCs +# -------------------------------------------------------------------- +- name: Query existing network.switches_to_attach.split in Federated Fabric + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/networks" + register: fed_networks_existing + when: + - MD_Extended.vxlan.multisite.overlay.networks is defined + - MD_Extended.vxlan.multisite.overlay.networks + - vars_common_mcfg.changes_detected_networks + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Manage NDFC Fabric Networks for Federated + ansible.builtin.include_tasks: network_fed_config.yml + loop: "{{ vars_common_mcfg.net_config }}" + loop_control: + loop_var: network + when: + - MD_Extended.vxlan.multisite.overlay.networks is defined + - MD_Extended.vxlan.multisite.overlay.networks + - vars_common_mcfg.changes_detected_networks + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Attach switches for all networks + cisco.dcnm.dcnm_rest: + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/networks/attachments" + method: "POST" + json_data: "{{ vars_common_mcfg.network_switches | to_json }}" + when: + - MD_Extended.vxlan.multisite.overlay.networks is defined + - MD_Extended.vxlan.multisite.overlay.networks + - vars_common_mcfg.changes_detected_networks + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Attach network to switches and ports for all networks + cisco.dcnm.dcnm_rest: + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/networks/multiattach" + method: "POST" + json_data: "{{ vars_common_mcfg.network_switches_ports | to_json }}" + when: + - MD_Extended.vxlan.multisite.overlay.networks is defined + - MD_Extended.vxlan.multisite.overlay.networks + - vars_common_mcfg.changes_detected_networks + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Get network attachments list + cisco.dcnm.dcnm_rest: + method: GET + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/networks/attachments + register: networkAttachmentList + when: + - MD_Extended.vxlan.multisite.overlay.networks is defined + - MD_Extended.vxlan.multisite.overlay.networks + - vars_common_mcfg.changes_detected_networks + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Filter network attachments to be removed + cisco.nac_dc_vxlan.dtc.fed_overlay_check: + model_data: "{{ MD_Extended }}" + ndfc_data: "{{ switch_list_create.response.DATA }}" + ndfc_attachment_data: "{{ networkAttachmentList.response.DATA }}" + check_type: "network_attach" + register: network_attachments_payload + when: + - MD_Extended.vxlan.multisite.overlay.networks is defined + - MD_Extended.vxlan.multisite.overlay.networks + - vars_common_mcfg.changes_detected_networks + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Attach network to switches and ports for all networks + cisco.dcnm.dcnm_rest: + method: POST + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/networks/attachments + json_data: "{{ network_attachments_payload.attachments_payload | to_json }}" + when: + - MD_Extended.vxlan.multisite.overlay.networks is defined + - MD_Extended.vxlan.multisite.overlay.networks + - vars_common_mcfg.changes_detected_networks + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Manage NDFC Child Fabric Networks + cisco.nac_dc_vxlan.dtc.manage_child_fabric_networks: + nd_version: "{{ nd_version }}" + msite_data: "{{ MD_Multisite }}" + fabric_type: "{{ MD_Extended.vxlan.fabric.type }}" + register: child_fabric_network_results + tags: "{{ nac_tags.create_vrfs_networks }}" diff --git a/roles/dtc/create/tasks/msd/vrfs_networks.yml b/roles/dtc/create/tasks/msd/vrfs_networks.yml index e08e018a2..49c46218d 100644 --- a/roles/dtc/create/tasks/msd/vrfs_networks.yml +++ b/roles/dtc/create/tasks/msd/vrfs_networks.yml @@ -141,6 +141,7 @@ cisco.nac_dc_vxlan.dtc.manage_child_fabric_vrfs: nd_version: "{{ nd_version }}" msite_data: "{{ MD_Multisite }}" + fabric_type: "{{ MD_Extended.vxlan.fabric.type }}" register: child_fabric_vrf_results # -------------------------------------------------------------------- @@ -176,6 +177,7 @@ cisco.nac_dc_vxlan.dtc.manage_child_fabric_networks: nd_version: "{{ nd_version }}" msite_data: "{{ MD_Multisite }}" + fabric_type: "{{ MD_Extended.vxlan.fabric.type }}" register: child_fabric_network_results - name: Update Child Fabrics VRFs and Networks Changed State diff --git a/roles/dtc/create/tasks/sub_main_mcfg.yml b/roles/dtc/create/tasks/sub_main_mcfg.yml new file mode 100644 index 000000000..480320ad1 --- /dev/null +++ b/roles/dtc/create/tasks/sub_main_mcfg.yml @@ -0,0 +1,68 @@ +# 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 + +--- +- name: Role Entry Point - [cisco.nac_dc_vxlan.dtc.create] + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Calling Role - [cisco.nac_dc_vxlan.dtc.create] +" + - "----------------------------------------------------------------" + tags: "{{ nac_tags.create }}" # Tags defined in roles/common_global/vars/main.yml + +- name: Debug message + ansible.builtin.debug: msg="Configuring NXOS Devices using NDFC (Direct to Controller)" + tags: "{{ nac_tags.create }}" + +- name: Create NDFC Fabric + ansible.builtin.import_tasks: mcfg/fabric.yml + when: + - MD_Extended.vxlan.fabric.name is defined + - vars_common_mcfg.changes_detected_fabric + tags: "{{ nac_tags.create_fabric }}" + +- name: Debug message + ansible.builtin.debug: msg="Query NDFC for List of Fabric Switches" + tags: "{{ nac_tags.create }}" + +- name: Collect switch data from NDFC + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/inventory/switchesByFabric" + register: result + tags: "{{ nac_tags.create }}" + +- name: Set facts for result + ansible.builtin.set_fact: + switch_list_create: "{{ result }}" + +- name: Prepare Multisite Data + cisco.nac_dc_vxlan.dtc.prepare_msite_data: + model_data: "{{ MD_Extended }}" + parent_fabric: "{{ MD_Extended.vxlan.fabric.name }}" + register: MD_Multisite + tags: "{{ nac_tags.create_vrfs_networks }}" + +- name: Manage NDFC Fabric VRFs and Networks + ansible.builtin.import_tasks: mcfg/vrfs_networks.yml + when: + - MD_Extended.vxlan.multisite.overlay is defined + - vars_common_mcfg.changes_detected_vrfs or vars_common_mcfg.changes_detected_networks diff --git a/roles/dtc/deploy/tasks/main.yml b/roles/dtc/deploy/tasks/main.yml index 14ee51d69..636efee7a 100644 --- a/roles/dtc/deploy/tasks/main.yml +++ b/roles/dtc/deploy/tasks/main.yml @@ -28,6 +28,13 @@ - MD_Extended.vxlan.fabric.type == 'VXLAN_EVPN' - change_flags.changes_detected_any +- name: Import Role Tasks + ansible.builtin.import_tasks: sub_main_ebgp_vxlan.yml + tags: "{{ nac_tags.deploy }}" + when: + - MD_Extended.vxlan.fabric.type == 'eBGP_VXLAN' + - change_flags.changes_detected_any + - name: Import MSD Fabric Role Tasks ansible.builtin.import_tasks: sub_main_msd.yml tags: "{{ nac_tags.deploy }}" @@ -36,6 +43,13 @@ (change_flags.changes_detected_any or (child_fabrics_vrfs_networks_changed is defined and child_fabrics_vrfs_networks_changed | length > 0)) +- name: Import MCFG Fabric Role Tasks + ansible.builtin.import_tasks: sub_main_mcfg.yml + tags: "{{ nac_tags.deploy }}" # Tags defined in roles/common_global/vars/main.yml + when: > + (MD_Extended.vxlan.fabric.type == 'MCFG') and + (change_flags.changes_detected_any) + - name: Import ISN Fabric Role Tasks ansible.builtin.import_tasks: sub_main_isn.yml tags: "{{ nac_tags.deploy }}" @@ -50,13 +64,6 @@ - MD_Extended.vxlan.fabric.type == 'External' - change_flags.changes_detected_any -- name: Import Role Tasks - ansible.builtin.import_tasks: sub_main_ebgp_vxlan.yml - tags: "{{ nac_tags.deploy }}" - when: - - MD_Extended.vxlan.fabric.type == 'eBGP_VXLAN' - - change_flags.changes_detected_any - - name: Mark Stage Role Deploy Completed cisco.nac_dc_vxlan.common.run_map: model_data: "{{ MD_Extended }}" diff --git a/roles/dtc/deploy/tasks/sub_main_mcfg.yml b/roles/dtc/deploy/tasks/sub_main_mcfg.yml new file mode 100644 index 000000000..11e915054 --- /dev/null +++ b/roles/dtc/deploy/tasks/sub_main_mcfg.yml @@ -0,0 +1,64 @@ +# 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 + +--- + +- name: Role Entry Point - [cisco.nac_dc_vxlan.dtc.deploy] + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Calling Role - [cisco.nac_dc_vxlan.dtc.deploy] +" + - "----------------------------------------------------------------" + +- name: Message + ansible.builtin.debug: + msg: "Configuring NXOS Devices using NDFC (Direct to Controller)" + +- name: Config-Save block + block: + - name: Config-Save for Fabric {{ MD_Extended.vxlan.fabric.name }} + cisco.dcnm.dcnm_rest: + method: POST + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/config-save" + register: config_save + when: > + MD_Extended.vxlan.fabric.type == 'MCFG' + + rescue: + - name: Config-Save for Fabric {{ MD_Extended.vxlan.fabric.name }} - Failed + ansible.builtin.debug: + msg: "{{ config_save.msg.DATA }}" + +- name: Extract serial numbers from switches_in_fabric + ansible.builtin.set_fact: + serial_numbers: "{{ switches_in_fabric | json_query('[*].serialNumber') }}" + +- name: Join serial numbers into a comma-separated list + ansible.builtin.set_fact: + serial_numbers_str: "{{ serial_numbers | join(',') }}" + +- name: Deploy for Federated Overlay + cisco.dcnm.dcnm_rest: + method: POST + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/config-deploy/{{ serial_numbers_str }}?forceShowRun=false" + vars: + ansible_command_timeout: 3000 + ansible_connect_timeout: 3000 diff --git a/roles/dtc/remove/tasks/main.yml b/roles/dtc/remove/tasks/main.yml index db39ca2f0..cc3acb3ab 100644 --- a/roles/dtc/remove/tasks/main.yml +++ b/roles/dtc/remove/tasks/main.yml @@ -29,6 +29,14 @@ - MD_Extended.vxlan.fabric.type == 'VXLAN_EVPN' - change_flags.changes_detected_any +- name: Import eBGP Role Tasks + ansible.builtin.import_tasks: sub_main_ebgp_vxlan.yml + # Check with Matt on changes_detected_policy here + # Was not there previously + when: + - MD_Extended.vxlan.fabric.type == 'eBGP_VXLAN' + - change_flags.changes_detected_any + - name: Import MSD Fabric Role Tasks ansible.builtin.import_tasks: sub_main_msd.yml when: @@ -48,14 +56,6 @@ - MD_Extended.vxlan.fabric.type == 'External' - change_flags.changes_detected_any -- name: Import eBGP Role Tasks - ansible.builtin.import_tasks: sub_main_ebgp_vxlan.yml - # Check with Matt on changes_detected_policy here - # Was not there previously - when: - - MD_Extended.vxlan.fabric.type == 'eBGP_VXLAN' - - change_flags.changes_detected_any - - name: Deploy Remove Changes ansible.builtin.include_role: name: cisco.nac_dc_vxlan.dtc.deploy diff --git a/roles/dtc/remove/tasks/mcfg/child_fabrics.yml b/roles/dtc/remove/tasks/mcfg/child_fabrics.yml new file mode 100644 index 000000000..d98d1ea29 --- /dev/null +++ b/roles/dtc/remove/tasks/mcfg/child_fabrics.yml @@ -0,0 +1,70 @@ +# 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 + +--- + +- name: Manage Fabric Children Entry Point + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Manage Fabric Children {{ MD_Extended.vxlan.fabric.name }}" + - "----------------------------------------------------------------" + +- name: Check if fabric already a member of NDFC + cisco.dcnm.dcnm_rest: + method: GET + path: '/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/members' + register: member_result + +- name: Set fact for existing fabric names and cluster names + ansible.builtin.set_fact: + existing_fabrics: >- + {{ + member_result.response.DATA | map(attribute='fabrics') | map('dict2items') | flatten | list + }} + +- name: Set fact for child fabric names + ansible.builtin.set_fact: + child_fabric_names: >- + {{ + fabric_children | map(attribute='fabricName') | list + }} + +- name: Generate payload for fabrics to be removed + ansible.builtin.set_fact: + remove_payload: >- + [ + {% for fabric in existing_fabrics if fabric.key not in child_fabric_names %} + { + "operation": "remove", + "clusterName": "{{ fabric.value.clusterName }}", + "fabricName": "{{ fabric.key }}" + }{% if not loop.last %},{% endif %} + {% endfor %} + ] + +- name: Remove fabrics from NDFC + cisco.dcnm.dcnm_rest: + method: PUT + path: '/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/members' + json_data: '{{ item | to_json }}' + loop: "{{ remove_payload }}" + when: remove_payload | length > 0 diff --git a/roles/dtc/remove/tasks/mcfg/networks_fed.yml b/roles/dtc/remove/tasks/mcfg/networks_fed.yml new file mode 100644 index 000000000..ceba659c3 --- /dev/null +++ b/roles/dtc/remove/tasks/mcfg/networks_fed.yml @@ -0,0 +1,92 @@ +# 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 +--- + +- name: Initial network remove message + ansible.builtin.debug: msg="Removing Unmanaged Fabric Networks. This could take several minutes..." + when: + - switch_list.response.DATA | length > 0 + - (network_delete_mode is defined) and (network_delete_mode is true|bool) + +- name: Get network list + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/networks" + register: networkList + +- name: Get network attachments list + cisco.dcnm.dcnm_rest: + method: GET + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/networks/attachments + register: networkAttachmentList + +- name: Filter networks to be removed + cisco.nac_dc_vxlan.dtc.fed_overlay_check: + model_data: "{{ MD_Extended }}" + ndfc_data: "{{ networkList.response.DATA }}" + ndfc_attachment_data: "{{ networkAttachmentList.response.DATA }}" + check_type: "network" + register: not_required_networks + +- name: Remove network attachments + cisco.dcnm.dcnm_rest: + method: POST + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/networks/attachments + json_data: "{{ not_required_networks.attachments_payload | to_json }}" + register: network_attachment_result + when: + - switch_list.response.DATA | length > 0 + - not_required_networks.attachments_payload | length > 0 + - (network_delete_mode is defined) and (network_delete_mode is true|bool) + +- name: Config-Save for Fabric {{ MD_Extended.vxlan.fabric.name }} + cisco.dcnm.dcnm_rest: + method: POST + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/config-save" + register: config_save + ignore_errors: true + +- name: Deploy for network attachments removal + cisco.dcnm.dcnm_rest: + method: POST + path: /appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/config-deploy/{{ not_required_networks.deploy_payload | join(',') }}?forceShowRun=false + when: + - switch_list.response.DATA | length > 0 + - not_required_networks.deploy_payload | length > 0 + - (network_delete_mode is defined) and (network_delete_mode is true|bool) + +- name: Remove networks + cisco.dcnm.dcnm_rest: + method: DELETE + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/bulk-delete/networks?network-names={{ not_required_networks.payload | join(',') }} + register: network_attachment_result + when: + - switch_list.response.DATA | length > 0 + - not_required_networks.payload | length > 0 + - (network_delete_mode is defined) and (network_delete_mode is true|bool) + +- name: Networks skipping message + ansible.builtin.debug: + msg: + - "---------------------------------------------------------------------------------------------------------------" + - "+ SKIPPING Remove Unmanaged Fabric Networks task because network_delete_mode flag is set to False +" + - "---------------------------------------------------------------------------------------------------------------" + when: not ((network_delete_mode is defined) and (network_delete_mode is true|bool)) diff --git a/roles/dtc/remove/tasks/mcfg/vrfs_fed.yml b/roles/dtc/remove/tasks/mcfg/vrfs_fed.yml new file mode 100644 index 000000000..7d5ed7fba --- /dev/null +++ b/roles/dtc/remove/tasks/mcfg/vrfs_fed.yml @@ -0,0 +1,95 @@ +# 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 +--- + +- name: Initial VRF remove message + ansible.builtin.debug: msg="Removing Unmanaged Fabric VRFs. This could take several minutes..." + when: + - switch_list.response.DATA | length > 0 + - (vrf_delete_mode is defined) and (vrf_delete_mode is true|bool) + +- name: Get vrf list + cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs" + register: vrfList + +- name: Get vrf attachments list + cisco.dcnm.dcnm_rest: + method: GET + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs/attachments + register: vrfAttachmentList + +- name: Filter vrfs to be removed + cisco.nac_dc_vxlan.dtc.fed_overlay_check: + model_data: "{{ MD_Extended }}" + ndfc_data: "{{ vrfList.response.DATA }}" + ndfc_attachment_data: "{{ vrfAttachmentList.response.DATA }}" + check_type: "vrf" + register: not_required_vrfs + +- name: Remove vrf attachments + cisco.dcnm.dcnm_rest: + method: POST + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/vrfs/attachments + json_data: "{{ not_required_vrfs.attachments_payload | to_json }}" + register: vrf_attachment_result + when: + - switch_list.response.DATA | length > 0 + - not_required_vrfs.attachments_payload | length > 0 + - (vrf_delete_mode is defined) and (vrf_delete_mode is true|bool) + +- name: Config-Save for Fabric {{ MD_Extended.vxlan.fabric.name }} + cisco.dcnm.dcnm_rest: + method: POST + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/config-save" + register: config_save + ignore_errors: true + +- name: Deploy for vrf attachments removal + cisco.dcnm.dcnm_rest: + method: POST + path: /appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/config-deploy/{{ not_required_vrfs.deploy_payload | join(',') }}?forceShowRun=false + vars: + ansible_command_timeout: 3000 + ansible_connect_timeout: 3000 + when: + - switch_list.response.DATA | length > 0 + - not_required_vrfs.deploy_payload | length > 0 + - (vrf_delete_mode is defined) and (vrf_delete_mode is true|bool) + +- name: Remove vrfs + cisco.dcnm.dcnm_rest: + method: DELETE + path: /appcenter/cisco/ndfc/api/v1/onemanage/top-down/fabrics/{{ MD_Extended.vxlan.fabric.name }}/bulk-delete/vrfs?vrf-names={{ not_required_vrfs.payload | join(',') }} + register: network_attachment_result + when: + - switch_list.response.DATA | length > 0 + - not_required_vrfs.payload | length > 0 + - (vrf_delete_mode is defined) and (vrf_delete_mode is true|bool) + +- name: VRF skipping message + ansible.builtin.debug: + msg: + - "--------------------------------------------------------------------------------------------------------" + - "+ SKIPPING Remove Unmanaged Fabric VRFs task because vrf_delete_mode flag is set to False +" + - "--------------------------------------------------------------------------------------------------------" + when: not ((vrf_delete_mode is defined) and (vrf_delete_mode is true|bool)) diff --git a/roles/dtc/remove/tasks/sub_main_mcfg.yml b/roles/dtc/remove/tasks/sub_main_mcfg.yml new file mode 100644 index 000000000..829f0454c --- /dev/null +++ b/roles/dtc/remove/tasks/sub_main_mcfg.yml @@ -0,0 +1,62 @@ +# 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 + +--- + +- name: Role Entry Point - [cisco.nac_dc_vxlan.dtc.remove] + ansible.builtin.debug: + msg: + - "----------------------------------------------------------------" + - "+ Calling Role - [cisco.nac_dc_vxlan.dtc.remove] +" + - "----------------------------------------------------------------" + tags: "{{ nac_tags.remove }}" # Tags defined in roles/common_global/vars/main.yml + +- ansible.builtin.debug: msg="Configuring NXOS Devices using NDFC (Direct to Controller)" + tags: "{{ nac_tags.remove }}" + +- ansible.builtin.debug: msg="Query NDFC for List of Fabric Switches" + tags: "{{ nac_tags.remove }}" + +- cisco.dcnm.dcnm_rest: + method: GET + path: "/appcenter/cisco/ndfc/api/v1/onemanage/fabrics/{{ MD_Extended.vxlan.fabric.name }}/inventory/switchesByFabric" + register: result + tags: "{{ nac_tags.remove }}" + +- name: set facts for result + ansible.builtin.set_fact: + switch_list: "{{result}}" + +- name: Remove Fabric Networks + ansible.builtin.import_tasks: mcfg/networks_fed.yml + tags: "{{ nac_tags.remove_networks }}" + when: + - vars_common_mcfg.changes_detected_networks + +- name: Remove Fabric VRFs + ansible.builtin.import_tasks: mcfg/vrfs_fed.yml + tags: "{{ nac_tags.remove_vrfs }}" + when: + - vars_common_mcfg.changes_detected_vrfs + +- name: Remove Child Fabrics + ansible.builtin.import_tasks: mcfg/child_fabrics.yml + # tags: "{{ nac_tags.remove_child_fabrics }}" diff --git a/roles/validate/files/defaults.yml b/roles/validate/files/defaults.yml index 3856e9cd3..7982bb6c0 100644 --- a/roles/validate/files/defaults.yml +++ b/roles/validate/files/defaults.yml @@ -448,6 +448,36 @@ factory_defaults: bgw_ip_tag: 54321 ipv4_vtep_loopback_range: 10.10.0.0/24 ipv6_vtep_loopback_range: fd00::a10:0/120 + overlay: + vrfs: + vrf_description: "Configured by Ansible NetAsCode" + vrf_intf_desc: "Configured by Ansible NetAsCode" + vrf_int_mtu: 9216 + loopback_route_tag: 12345 + max_bgp_paths: 1 + max_ibgp_paths: 2 + ipv6_linklocal_enable: true + adv_host_routes: false + adv_default_routes: true + static_default_route: true + disable_rt_auto: false + netflow_enable: false + no_rp: false + rp_external: false + redist_direct_routemap: FABRIC-RMAP-REDIST-SUBNET + trm_enable: false + trm_bgw_msite: false + networks: + net_description: "Configured by Ansible NetAsCode" + is_l2_only: false + arp_supress: false + l3gw_on_border: false + mtu_l3intf: 9216 + multicast_group_address: 239.1.1.1 + netflow_enable: false + route_target_both: false + route_tag: 12345 + trm_enable: false overlay_dci: deployment_method: Direct_To_BGWS route_server: diff --git a/tests/sanity/ignore-2.14.txt b/tests/sanity/ignore-2.14.txt index 2ff904334..e10a34806 100644 --- a/tests/sanity/ignore-2.14.txt +++ b/tests/sanity/ignore-2.14.txt @@ -16,6 +16,7 @@ plugins/action/common/get_credentials.py action-plugin-docs # action plugin has plugins/action/common/run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/read_run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/merge_defaults.py action-plugin-docs # action plugin has no matching module to provide documentation +plugins/action/dtc/fed_overlay_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/add_device_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/manage_child_fabrics.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/prepare_msite_child_fabrics_data.py action-plugin-docs # action plugin has no matching module to provide documentation diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 2ff904334..e10a34806 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -16,6 +16,7 @@ plugins/action/common/get_credentials.py action-plugin-docs # action plugin has plugins/action/common/run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/read_run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/merge_defaults.py action-plugin-docs # action plugin has no matching module to provide documentation +plugins/action/dtc/fed_overlay_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/add_device_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/manage_child_fabrics.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/prepare_msite_child_fabrics_data.py action-plugin-docs # action plugin has no matching module to provide documentation diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt index 2ff904334..e10a34806 100644 --- a/tests/sanity/ignore-2.16.txt +++ b/tests/sanity/ignore-2.16.txt @@ -16,6 +16,7 @@ plugins/action/common/get_credentials.py action-plugin-docs # action plugin has plugins/action/common/run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/read_run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/merge_defaults.py action-plugin-docs # action plugin has no matching module to provide documentation +plugins/action/dtc/fed_overlay_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/add_device_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/manage_child_fabrics.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/prepare_msite_child_fabrics_data.py action-plugin-docs # action plugin has no matching module to provide documentation diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index 2ff904334..e10a34806 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -16,6 +16,7 @@ plugins/action/common/get_credentials.py action-plugin-docs # action plugin has plugins/action/common/run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/read_run_map.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/common/merge_defaults.py action-plugin-docs # action plugin has no matching module to provide documentation +plugins/action/dtc/fed_overlay_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/add_device_check.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/manage_child_fabrics.py action-plugin-docs # action plugin has no matching module to provide documentation plugins/action/dtc/prepare_msite_child_fabrics_data.py action-plugin-docs # action plugin has no matching module to provide documentation