Skip to content

Commit 6dec077

Browse files
authored
Merge branch 'netascode:develop' into update_defaults_bgp_password_enable
2 parents 0233a02 + 7028c05 commit 6dec077

99 files changed

Lines changed: 9712 additions & 1020 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: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,92 @@ 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+
1197
`0.7.2`_
1298
=====================
1399

@@ -520,6 +606,8 @@ The following roles have been added to the collection:
520606

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

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
523611
.. _0.7.2: https://github.com/netascode/ansible-dc-vxlan/compare/0.7.1...0.7.2
524612
.. _0.7.1: https://github.com/netascode/ansible-dc-vxlan/compare/0.7.0...0.7.1
525613
.. _0.7.0: https://github.com/netascode/ansible-dc-vxlan/compare/0.6.0...0.7.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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
namespace: cisco
33
name: nac_dc_vxlan
4-
version: 0.7.2-dev
4+
version: 0.8.1-dev
55
readme: README.md
66
authors:
77
- Charly Coueffe <ccoueffe>
@@ -32,5 +32,5 @@ tags: [cisco, dc, nd, ndfc, nxos, networking, vxlan, evpn, nac, sac]
3232

3333
dependencies:
3434
"ansible.netcommon": ">=4.1.0"
35-
"cisco.dcnm": ">=3.11.1"
35+
"cisco.dcnm": ">=3.12.1"
3636
"community.general": ">=8.5.0"

plugins/action/common/change_flag_manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def initialize_flags(self):
103103
'changes_detected_vpc_peering': False,
104104
'changes_detected_vpc_domain_id_resource': False,
105105
'changes_detected_vrfs': False,
106+
'changes_detected_underlay_ip_address': False,
106107
'changes_detected_any': False
107108
}
108109
if self.fabric_type == "ISN":

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: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,25 @@
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
22+
from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import (
23+
restructure_leaf_tor_data,
24+
resolve_switch_by_identifier,
25+
)
2326

2427

2528
class PreparePlugin:
2629
def __init__(self, **kwargs):
2730
self.kwargs = kwargs
2831
self.keys = []
2932

33+
def _resolve_mgmt_ip(self, identifier, topology_switches):
34+
found = resolve_switch_by_identifier(identifier, topology_switches)
35+
mgmt = found.get('management') or {}
36+
return (
37+
mgmt.get('management_ipv4_address')
38+
or mgmt.get('management_ipv6_address')
39+
)
40+
3041
def prepare(self):
3142
data_model = self.kwargs['results']['model_extended']
3243

@@ -48,14 +59,8 @@ def prepare(self):
4859
vrf_grp_name_list.append(grp['name'])
4960
for switch in grp['switches']:
5061
data_model['vxlan']['overlay']['vrf_attach_groups_dict'][grp['name']].append(switch)
51-
# If the switch is in the switch list and a hostname is used, replace the hostname with the management IP
5262
for switch in data_model['vxlan']['overlay']['vrf_attach_groups_dict'][grp['name']]:
53-
if any(sw['name'] == switch['hostname'] for sw in switches):
54-
found_switch = next((item for item in switches if item["name"] == switch['hostname']))
55-
if found_switch.get('management').get('management_ipv4_address'):
56-
switch['mgmt_ip_address'] = found_switch['management']['management_ipv4_address']
57-
elif found_switch.get('management').get('management_ipv6_address'):
58-
switch['mgmt_ip_address'] = found_switch['management']['management_ipv6_address']
63+
switch['mgmt_ip_address'] = self._resolve_mgmt_ip(switch['hostname'], switches)
5964

6065
# Remove vrf_attach_group from vrf if the group_name is not defined
6166
for vrf in data_model['vxlan']['overlay']['vrfs']:
@@ -78,31 +83,31 @@ def prepare(self):
7883

7984
for switch in grp['switches']:
8085
data_model['vxlan']['overlay']['network_attach_groups_dict'][grp['name']].append(switch)
81-
# If the switch is in the switch list and a hostname is used, replace the hostname with the management IP
8286
for switch in data_model['vxlan']['overlay']['network_attach_groups_dict'][grp['name']]:
83-
if any(sw['name'] == switch['hostname'] for sw in switches):
84-
found_switch = next((item for item in switches if item["name"] == switch['hostname']))
85-
if found_switch.get('management').get('management_ipv4_address'):
86-
switch['mgmt_ip_address'] = found_switch['management']['management_ipv4_address']
87-
elif found_switch.get('management').get('management_ipv6_address'):
88-
switch['mgmt_ip_address'] = found_switch['management']['management_ipv6_address']
89-
90-
# Process nested TOR entries and resolve their management IPs
87+
switch['mgmt_ip_address'] = self._resolve_mgmt_ip(switch['hostname'], switches)
88+
9189
if 'tors' in switch and switch['tors']:
9290
for tor in switch['tors']:
93-
tor_hostname = tor.get('hostname')
94-
if tor_hostname and any(sw['name'] == tor_hostname for sw in switches):
95-
found_tor = next((item for item in switches if item["name"] == tor_hostname))
96-
if found_tor.get('management').get('management_ipv4_address'):
97-
tor['mgmt_ip_address'] = found_tor['management']['management_ipv4_address']
98-
elif found_tor.get('management').get('management_ipv6_address'):
99-
tor['mgmt_ip_address'] = found_tor['management']['management_ipv6_address']
91+
tor_id = tor.get('hostname')
92+
if not tor_id:
93+
continue
94+
tor['mgmt_ip_address'] = self._resolve_mgmt_ip(tor_id, switches)
10095

10196
# Remove network_attach_group from net if the group_name is not defined
10297
for net in data_model['vxlan']['overlay']['networks']:
10398
if 'network_attach_group' in net:
10499
if net.get('network_attach_group') not in net_grp_name_list:
105100
del net['network_attach_group']
106101

102+
for net in data_model['vxlan']['overlay']['networks']:
103+
overrides = net.get('switch_attach_overrides')
104+
if not overrides:
105+
continue
106+
for override in overrides:
107+
override_id = override.get('hostname')
108+
if not override_id:
109+
continue
110+
override['mgmt_ip_address'] = self._resolve_mgmt_ip(override_id, switches)
111+
107112
self.kwargs['results']['model_extended'] = data_model
108113
return self.kwargs['results']

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)