Skip to content

Commit 1e71f74

Browse files
authored
Merge branch 'netascode:develop' into add_support_manual_allocation_ebgp
2 parents 514bc52 + 11588f4 commit 1e71f74

84 files changed

Lines changed: 9270 additions & 1053 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.rst

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,107 @@ This project adheres to `Semantic Versioning <http://semver.org/>`_.
88

99
.. contents:: ``Release Versions``
1010

11+
`0.8.1`_
12+
=====================
13+
14+
**Release Date:** ``2026-07-15``
15+
16+
Added
17+
-----
18+
19+
* Added support for ND 4.2.1
20+
* Added BGP ASN auto allocation support
21+
* Added greenfield cleanup option
22+
* Added bootstrap cross-reference validation rules
23+
24+
Modified
25+
--------
26+
27+
* With release 0.8.1, the ptp.vlan_id key has been removed from the data model for vxlan.global.ebgp, vxlan.global.external, and vxlan.multisite.isn as the PTP VLAN ID is not supported for these fabric types.
28+
* With release 0.8.1, NaC VXLAN supports the following eBGP fabric scenarios with Nexus Dashboard 4.2 or later releases (See https://netascode.cisco.com for more info):
29+
• Multi-AS Mode with ASN Auto Allocation
30+
• Multi-AS Mode with Allow Same ASN On Leafs
31+
• Same-Tier-AS Mode
32+
33+
Fixed
34+
-----
35+
36+
* https://github.com/netascode/ansible-dc-vxlan/issues/688
37+
* https://github.com/netascode/ansible-dc-vxlan/issues/727
38+
* https://github.com/netascode/ansible-dc-vxlan/issues/749
39+
* https://github.com/netascode/ansible-dc-vxlan/issues/758
40+
* https://github.com/netascode/ansible-dc-vxlan/issues/799
41+
* https://github.com/netascode/ansible-dc-vxlan/issues/810
42+
* https://github.com/netascode/ansible-dc-vxlan/issues/813
43+
* https://github.com/netascode/ansible-dc-vxlan/issues/814
44+
* https://github.com/netascode/ansible-dc-vxlan/issues/822
45+
* https://github.com/netascode/ansible-dc-vxlan/issues/825
46+
* https://github.com/netascode/ansible-dc-vxlan/issues/829
47+
* https://github.com/netascode/ansible-dc-vxlan/issues/834
48+
49+
`0.8.0`_
50+
=====================
51+
52+
**Release Date:** ``2026-05-31``
53+
54+
Added
55+
-----
56+
* This release delivers the DTC Pythonic Consolidation
57+
* This is a major architectural refactor of the Direct-to-Controller (DTC) roles that replaces per-fabric YAML task files with a unified, data-driven pipeline powered by Python action plugins and externalized YAML registries.
58+
* ``cr_manage_vrfs_networks`` split into independent ``cr_manage_vrfs`` and ``cr_manage_networks`` tags
59+
* Added ``role_create`` and ``role_remove`` top-level role tags
60+
* Added ``cr_manage_links`` and ``cr_manage_tor_pairing`` to iBGP and eBGP VXLAN tag sets
61+
* General performance improvements
62+
63+
Fixed
64+
-----
65+
66+
* https://github.com/netascode/ansible-dc-vxlan/issues/767
67+
* https://github.com/netascode/ansible-dc-vxlan/issues/783
68+
* https://github.com/netascode/ansible-dc-vxlan/issues/786
69+
70+
* Template Fixes
71+
72+
* ndfc_bgw_anycast_vip.j2, ndfc_underlay_ip_address.j2, ndfc_vpc_domain_id_resource.j2
73+
74+
* Switched from bare ``vxlan.*`` references to fully-qualified ``data_model_extended.vxlan.*``
75+
76+
* ndfc_fabric_links.j2
77+
78+
* Added ``peer1_ipv4_addr`` / ``peer2_ipv4_addr`` support for numbered fabric links
79+
80+
* ndfc_policy.j2
81+
82+
* Added guard for empty switches list to prevent empty policy render
83+
84+
* dc_vxlan_fabric_attach_vrfs_loopbacks.j2, msd_fabric_attach_vrfs_loopbacks.j2, mcfg_fabric_attach_vrfs_loopbacks.j2
85+
86+
* Widened loopback attach condition to include ``loopback_ipv4``, ``loopback_ipv6``, and ``freeform_config``
87+
88+
* ndfc_underlay_ip_address.j2
89+
90+
* Added anycast RP resource allocation for multicast replication mode
91+
92+
* mcfg_fabric_attach_vrfs_loopbacks.j2
93+
94+
* Fixed ``vlan_id`` type (quoted string for NDFC API)
95+
96+
97+
`0.7.2`_
98+
=====================
99+
100+
**Release Date:** ``2026-05-01``
101+
102+
Added
103+
-----
104+
* Added support for network attachments on TOR devices that are part of Multisite fabrics
105+
106+
Fixed
107+
-----
108+
109+
* https://github.com/netascode/ansible-dc-vxlan/issues/786
110+
111+
11112
`0.7.1`_
12113
=====================
13114

@@ -505,6 +606,9 @@ The following roles have been added to the collection:
505606

506607
This version of the collection includes support for an IPv4 Underlay only. Support for IPv6 Underlay will be available in the next release.
507608

609+
.. _0.8.1: https://github.com/netascode/ansible-dc-vxlan/compare/0.8.0...0.8.1
610+
.. _0.8.0: https://github.com/netascode/ansible-dc-vxlan/compare/0.7.2...0.8.0
611+
.. _0.7.2: https://github.com/netascode/ansible-dc-vxlan/compare/0.7.1...0.7.2
508612
.. _0.7.1: https://github.com/netascode/ansible-dc-vxlan/compare/0.7.0...0.7.1
509613
.. _0.7.0: https://github.com/netascode/ansible-dc-vxlan/compare/0.6.0...0.7.0
510614
.. _0.6.0: https://github.com/netascode/ansible-dc-vxlan/compare/0.5.1...0.6.0

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,14 @@ Inside the [example repository](https://github.com/netascode/ansible-dc-vxlan-ex
5050

5151
```yaml
5252
# Control Parameters for 'Remove' role tasks in VXLAN EVPN fabric
53+
edge_connections_delete_mode: false
5354
interface_delete_mode: false
5455
inventory_delete_mode: false
5556
link_fabric_delete_mode: false
5657
link_vpc_delete_mode: false
5758
network_delete_mode: false
5859
policy_delete_mode: false
60+
tor_pairing_delete_mode: false
5961
vpc_delete_mode: false
6062
vrf_delete_mode: false
6163

@@ -83,16 +85,19 @@ The following control variables are available in this collection.
8385
| Variable | Description | Default Value |
8486
| -------- | ------- | ------- |
8587
| `force_run_all` | Force all roles in the collection to run | `false` |
88+
| `edge_connections_delete_mode` | Remove edge_connections state as part of the remove role | `false` |
8689
| `interface_delete_mode` | Remove interface state as part of the remove role | `false` |
8790
| `inventory_delete_mode` | Remove inventory state as part of the remove role | `false` |
91+
| `link_fabric_delete_mode` | Remove fabric link state as part of the remove role | `false` |
8892
| `link_vpc_delete_mode` | Remove vpc link state as part of the remove role | `false` |
8993
| `multisite_child_fabric_delete_mode` | Remove child fabric from MSD fabric as part of the remove role | `false` |
9094
| `multisite_network_delete_mode` | Remove network state as part of the remove role for multisite (MSD) fabrics | `false` |
9195
| `multisite_vrf_delete_mode` | Remove vrf state as part of the remove role for multisite (MSD) fabrics | `false` |
9296
| `network_delete_mode` | Remove network state as part of the remove role | `false` |
9397
| `policy_delete_mode` | Remove policy state as part of the remove role | `false` |
94-
| `vrf_delete_mode` | Remove vrf state as part of the remove role | `false` |
98+
| `tor_pairing_delete_mode` | Remove tor pairing state as part of the remove role | `false` |
9599
| `vpc_delete_mode` | Remove vpc pair state as part of the remove role | `false` |
100+
| `vrf_delete_mode` | Remove vrf state as part of the remove role | `false` |
96101

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

@@ -462,7 +467,7 @@ We welcome community contributions to this collection. If you find problems, ple
462467

463468
MIT License
464469

465-
Copyright (c) 2024-2025 Cisco and/or its affiliates.
470+
Copyright (c) 2024-2026 Cisco and/or its affiliates.
466471

467472
Permission is hereby granted, free of charge, to any person obtaining a copy
468473
of this software and associated documentation files (the "Software"), to deal

galaxy.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
22
namespace: cisco
33
name: nac_dc_vxlan
4-
version: 0.7.1-dev
4+
version: 0.8.1-dev
55
readme: README.md
66
authors:
7+
- Charly Coueffe <ccoueffe>
78
- Devendra Gupta <devegupt>
89
- Jesus Casero Diaz-Cano <JesusCaseroDiazCano>
910
- Justin Burnette <juburnet>
@@ -31,5 +32,5 @@ tags: [cisco, dc, nd, ndfc, nxos, networking, vxlan, evpn, nac, sac]
3132

3233
dependencies:
3334
"ansible.netcommon": ">=4.1.0"
34-
"cisco.dcnm": ">=3.11.0"
35+
"cisco.dcnm": ">=3.12.1"
3536
"community.general": ">=8.5.0"

plugins/action/common/get_credentials.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,7 @@ def run(self, tmp=None, task_vars=None):
207207
display.vvv(f"Using group_vars discovery credentials from model data for device {device_ip}")
208208

209209
results['updated_inv_list'] = updated_inv_list
210+
# Convention: post-hooks that produce module-ready data set 'module_data'
211+
# so build_resource_data can pass it directly to downstream NDFC modules.
212+
results['module_data'] = updated_inv_list
210213
return results

plugins/action/common/nac_dc_validate.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ def run(self, tmp=None, task_vars=None):
8989
schema = DEFAULT_SCHEMA
9090

9191
rules_list = []
92+
data_model_loaded = None
9293
if rules and task_vars['role_path'] in rules:
9394
# Load in-memory data model using iac-validate
9495
# Perform the load in this if block to avoid loading the data model multiple times when custom enhanced rules are provided
9596
results['data'] = load_yaml_files([mdata])
97+
data_model_loaded = results['data']
9698

9799
# Introduce common directory to the rules list by default once vrf and network rules are updated
98100
parent_keys = ['vxlan', 'fabric']
@@ -156,11 +158,17 @@ def run(self, tmp=None, task_vars=None):
156158
# Else block to pickup custom enhanced rules provided by the user
157159
rules_list.append(f'{rules}')
158160

161+
syntax_validated = False
159162
for rules_item in rules_list:
160163
validator = nac_validate.validator.Validator(schema, rules_item)
161-
if schema:
164+
if schema and not syntax_validated and validator.schema is not None:
162165
validator.validate_syntax([mdata])
166+
syntax_validated = True
163167
if rules_item:
168+
if data_model_loaded is None:
169+
data_model_loaded = load_yaml_files([mdata])
170+
results['data'] = data_model_loaded
171+
validator.data = data_model_loaded
164172
validator.validate_semantics([mdata])
165173

166174
msg = ""

plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py

Lines changed: 4 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -19,96 +19,14 @@
1919
#
2020
# SPDX-License-Identifier: MIT
2121

22+
from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import restructure_leaf_tor_data
23+
24+
2225
class PreparePlugin:
2326
def __init__(self, **kwargs):
2427
self.kwargs = kwargs
2528
self.keys = []
2629

27-
def _restructure_flat_to_nested(self, switches_list, topology_switches, tor_peers):
28-
"""Transform a flat switches list into the nested structure with TOR
29-
entries under their parent leaf switches.
30-
31-
Each TOR is placed under ALL of its parent leaves that are present in the
32-
attach group. If a parent leaf is not explicitly listed in the attach group,
33-
a synthetic leaf entry is created automatically from topology data.
34-
35-
Args:
36-
switches_list: List of switch dicts from network_attach_group
37-
topology_switches: List of switch dicts from vxlan.topology.switches
38-
tor_peers: List of tor_peers dicts from vxlan.topology.tor_peers
39-
40-
Returns:
41-
Restructured switches list with TOR entries nested under parent leaves
42-
"""
43-
# Build set of TOR hostnames from topology
44-
tor_hostnames = {
45-
sw['name'] for sw in topology_switches if sw.get("role") == "tor"
46-
}
47-
48-
# Build TOR -> parent leaves mapping from tor_peers
49-
# Each TOR maps to a list of its parent leaf hostnames
50-
tor_to_parents = {}
51-
for peer in tor_peers:
52-
parent_leaves = []
53-
if peer.get("parent_leaf1"):
54-
parent_leaves.append(peer['parent_leaf1'])
55-
if peer.get("parent_leaf2"):
56-
parent_leaves.append(peer['parent_leaf2'])
57-
58-
for key in ("tor1", "tor2"):
59-
tor_name = peer.get(key)
60-
if tor_name:
61-
tor_to_parents[tor_name] = parent_leaves
62-
63-
# Build topology hostname -> switch data mapping for creating synthetic entries
64-
topology_by_name = {sw['name']: sw for sw in topology_switches}
65-
66-
# Separate leaf entries and TOR entries
67-
leaf_entries = []
68-
tor_entries = []
69-
70-
for sw in switches_list:
71-
hostname = sw.get("hostname", "")
72-
if hostname in tor_hostnames:
73-
tor_entries.append(sw)
74-
else:
75-
leaf_entries.append(sw)
76-
77-
# Initialize empty 'tors' list for each leaf entry
78-
for leaf in leaf_entries:
79-
if "tors" not in leaf:
80-
leaf['tors'] = []
81-
82-
# Build hostname -> leaf entry mapping for quick lookup
83-
leaf_by_hostname = {leaf['hostname']: leaf for leaf in leaf_entries}
84-
85-
# Collect all required parent leaf hostnames from TOR entries
86-
# and create synthetic leaf entries for any that are not in the attach group
87-
for tor_entry in tor_entries:
88-
tor_hostname = tor_entry['hostname']
89-
parent_leaves = tor_to_parents.get(tor_hostname, [])
90-
for parent_hostname in parent_leaves:
91-
if parent_hostname not in leaf_by_hostname:
92-
# Create a synthetic leaf entry from topology data
93-
synthetic_leaf = {'hostname': parent_hostname, 'tors': []}
94-
leaf_entries.append(synthetic_leaf)
95-
leaf_by_hostname[parent_hostname] = synthetic_leaf
96-
97-
# Assign each TOR to ALL of its parent leaves present in this attach group
98-
for tor_entry in tor_entries:
99-
tor_hostname = tor_entry['hostname']
100-
parent_leaves = tor_to_parents.get(tor_hostname, [])
101-
valid_parents = [p for p in parent_leaves if p in leaf_by_hostname]
102-
103-
if valid_parents:
104-
for parent_hostname in valid_parents:
105-
leaf_by_hostname[parent_hostname]['tors'].append(tor_entry)
106-
else:
107-
# No parent leaf defined in tor_peers - keep TOR as top-level entry
108-
leaf_entries.append(tor_entry)
109-
110-
return leaf_entries
111-
11230
def prepare(self):
11331
data_model = self.kwargs['results']['model_extended']
11432

@@ -154,7 +72,7 @@ def prepare(self):
15472
net_grp_name_list.append(grp['name'])
15573

15674
# Restructure flat TOR entries under parent leaves
157-
grp['switches'] = self._restructure_flat_to_nested(
75+
grp['switches'] = restructure_leaf_tor_data(
15876
grp['switches'], switches, tor_peers
15977
)
16078

plugins/action/common/read_run_map.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ def run(self, tmp=None, task_vars=None):
4040
results['diff_run'] = True
4141
results['validate_only_run'] = False
4242

43+
fabric_name = self._task.args.get('fabric_name')
4344
data_model = self._task.args.get('data_model')
4445
play_tags = self._task.args.get('play_tags')
45-
fabric_name = data_model['vxlan']['fabric']['name']
46+
if fabric_name is None:
47+
fabric_name = data_model['vxlan']['fabric']['name']
4648

4749
if 'dtc' in task_vars['role_path']:
4850
common_role_path = os.path.dirname(task_vars['role_path'])

plugins/action/common/run_map.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@ def run(self, tmp=None, task_vars=None):
4141
results = super(ActionModule, self).run(tmp, task_vars)
4242
results['failed'] = False
4343

44+
fabric_name = self._task.args.get('fabric_name')
4445
data_model = self._task.args.get('data_model')
4546
stage = self._task.args['stage']
4647

47-
fabric_name = data_model['vxlan']['fabric']['name']
48+
if fabric_name is None:
49+
fabric_name = data_model['vxlan']['fabric']['name']
4850

4951
if 'dtc' in task_vars['role_path']:
5052
common_role_path = os.path.dirname(task_vars['role_path'])

0 commit comments

Comments
 (0)