Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,10 @@ def prepare(self):
if fabric_type in ['VXLAN_EVPN', 'External', 'eBGP_VXLAN']:
fn = self.model_data['vxlan']['fabric']['name']
if not bool(self.model_data['vxlan'].get('underlay')):
msg = "((vxlan.underlay)) data is empty! Check your host_vars model data for fabric {fn}."
msg = f"((vxlan.underlay)) data is empty! Check your host_vars model data for fabric {fn}."
display.warning(msg=msg, formatted=True)
if not bool(self.model_data['vxlan'].get('global')):
msg = "((vxlan.global)) data is empty! Check your host_vars model data for fabric {fn}."
msg = f"((vxlan.global)) data is empty! Check your host_vars model data for fabric {fn}."
display.warning(msg=msg, formatted=True)

self.kwargs['results']['model_extended'] = self.model_data
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Copyright (c) 2025 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


# Convert schema values of L2 MTU interfaces to the required format ('default' or 'jumbo')

from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import data_model_key_check
from ansible_collections.cisco.nac_dc_vxlan.plugins.plugin_utils.helper_functions import normalize_interface_name


class PreparePlugin:
def __init__(self, **kwargs):
self.kwargs = kwargs
self.keys = []

def get_parent_interface_name_mtu(self, sub_interface_name, switch_interfaces, default_routed_int_mtu, default_port_channel_mtu):
parent_interface_name = normalize_interface_name(sub_interface_name).split('.')[0]
parent_interface_mtu = 0
for interface in switch_interfaces:
if parent_interface_name == normalize_interface_name(interface.get('name')):
if interface.get('mtu'):
parent_interface_mtu = interface.get('mtu')
break
if not parent_interface_mtu:
if 'Port-channel' in parent_interface_name:
parent_interface_mtu = default_port_channel_mtu
else:
parent_interface_mtu = default_routed_int_mtu
return parent_interface_name, parent_interface_mtu

# layer2_host_interface_mtu must be an even int
def standarize_global_l2_mtu(self, mtu):
n = None
match mtu:
case 'default':
return 1500
case int(n) if n % 2 == 0:
return mtu
case _:
self.kwargs['results']['failed'] = True
self.kwargs['results']['msg'] = f'vxlan.underlay.general.layer2_host_interface_mtu is not a valid value ({mtu}). MTU cannot be an odd number.'

# Validate that the interfaces mtu is correct, either 1500 or the value of layer2_host_interface_mtu (sysmtem jumbomtu)
def standarize_l2_mtu(self, switch_name, interface, system_mtu):
n = None
interface_mtu = interface.get('mtu')
interface_name = normalize_interface_name(interface.get('name'))
match interface_mtu:
case 1500:
return 'default'
case int(n) if n == system_mtu:
return 'jumbo'
case str(n) if n == 'jumbo' or n == 'default':
return interface_mtu
case _:
self.kwargs['results']['failed'] = True
# Adding space if previous errors exist if not initialize str
if self.kwargs['results']['msg']:
self.kwargs['results']['msg'] += ' '
else:
self.kwargs['results']['msg'] = ''

self.kwargs['results']['msg'] += f'vxlan.topology.switches.{switch_name}.interfaces.{interface_name}.mtu ({interface_mtu}) is not a valid \
value. MTU must be 1500 or {system_mtu}.'
return None

def prepare(self):
model_data = self.kwargs['results']['model_extended']
l2_jumbomtu = self.kwargs['default_values']['vxlan']['underlay']['general']['layer2_host_interface_mtu']
default_sub_interface_mtu = self.kwargs['default_values']['vxlan']['topology']['switches']['interfaces']['topology_switch_routed_sub_interface']['mtu']
default_routed_int_mtu = self.kwargs['default_values']['vxlan']['topology']['switches']['interfaces']['topology_switch_routed_interface']['mtu']
default_port_channel_mtu = self.kwargs['default_values']['vxlan']['topology']['switches']['interfaces']['topology_switch_routed_po_interface']['mtu']

dm_check = data_model_key_check(model_data, ['vxlan', 'underlay', 'general'])

# Check if vxlan.underlay.general is defined
if 'general' in dm_check['keys_data']:
if 'layer2_host_interface_mtu' in model_data['vxlan']['underlay']['general']:
l2_host_interface_mtu = model_data['vxlan']['underlay']['general']['layer2_host_interface_mtu']
l2_host_interface_mtu_standardized = self.standarize_global_l2_mtu(l2_host_interface_mtu)
model_data['vxlan']['underlay']['general']['layer2_host_interface_mtu'] = l2_host_interface_mtu_standardized
l2_jumbomtu = l2_host_interface_mtu_standardized

if 'intra_fabric_interface_mtu' in model_data['vxlan']['underlay']['general']:
intra_fabric_interface_mtu = model_data['vxlan']['underlay']['general']['intra_fabric_interface_mtu']
# intra_fabric_interface_mtu must be an even int
if intra_fabric_interface_mtu % 2 != 0:
self.kwargs['results']['failed'] = True

# Adding space if previous errors exist if not initialize str
if self.kwargs['results']['msg']:
self.kwargs['results']['msg'] += ' '
else:
self.kwargs['results']['msg'] = ''

self.kwargs['results']['msg'] += f'vxlan.underlay.general.intra_fabric_interface_mtu is not a valid value \
({intra_fabric_interface_mtu}). MTU cannot be an odd number.'

dm_check = data_model_key_check(model_data, ['vxlan', 'topology', 'switches'])

# Check if vxlan.topology is defined
if 'switches' in dm_check['keys_data']:
for switch_index, switch in enumerate(model_data.get('vxlan').get('topology').get('switches')):

switch_name = switch.get('name')

# loop through interfaces
for interface_index, interface in enumerate(switch.get('interfaces')):
interface_mode = interface.get('mode')
interface_name = normalize_interface_name(interface.get('name'))

# L2 interfaces
if interface_mode in ['access', 'trunk', 'dot1q']:
if interface.get('mtu'):
interface_mtu = self.standarize_l2_mtu(switch_name, interface, l2_jumbomtu)
model_data['vxlan']['topology']['switches'][switch_index]['interfaces'][interface_index]['mtu'] = interface_mtu

# L3 interfaces
if interface_mode in ['routed', 'routed_po', 'routed_sub']:
if interface.get('mtu'):
interface_mtu = interface.get('mtu')
# MTU must be an even number
if interface_mtu % 2 != 0:
self.kwargs['results']['failed'] = True
# Adding space if previous errors exist if not initialize str
if self.kwargs['results']['msg']:
self.kwargs['results']['msg'] += ' '
else:
self.kwargs['results']['msg'] = ''

self.kwargs['results']['msg'] += f'vxlan.topology.switches.{switch_name}.interfaces.{interface_name} is not a valid value \
({interface_mtu}). MTU cannot be an odd number.'

# Sub interfaces
if interface_mode == 'routed_sub':
parent_interface_name, parent_interface_mtu = self.get_parent_interface_name_mtu(interface_name, switch.get('interfaces'),
default_routed_int_mtu,
default_port_channel_mtu)
sub_interface_mtu = interface.get('mtu') if interface.get('mtu') else default_sub_interface_mtu
if parent_interface_mtu < sub_interface_mtu:
self.kwargs['results']['failed'] = True
# Adding space if previous errors exist if not initialize str
if self.kwargs['results']['msg']:
self.kwargs['results']['msg'] += ' '
else:
self.kwargs['results']['msg'] = ''

self.kwargs['results']['msg'] += f'vxlan.topology.switches.{switch_name}.interfaces.\
{normalize_interface_name(interface_name)}.mtu is not a valid value. {normalize_interface_name(interface_name)} MTU ({sub_interface_mtu}) \
cannot be greater than {parent_interface_name} MTU ({parent_interface_mtu}).'

self.kwargs['results']['model_extended'] = model_data
return self.kwargs['results']
49 changes: 49 additions & 0 deletions plugins/plugin_utils/helper_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,55 @@
# For example in prepare_serice_model.py we can do the following:
# from ..helper_functions import do_something

import re


def normalize_interface_name(interface_name):
"""
Normalize the interface name to the complete syntaxis:
Ethernet
Port-channel
Loopback

:Parameters:
:Interface Name (str): Name of the interfaces.

:Returns:
:interface_name (str): Normalized interface name.

:Raises:
N/A
"""
# Replace 'eth' or 'e' followed by digits with 'Ethernet' followed by the same digits
interface_name = re.sub(
r"(?i)^(?:e|eth(?:ernet)?)(\d(?:\/\d+){1,2})$",
r"Ethernet\1",
interface_name,
flags=re.IGNORECASE,
)
# Replace 'Po' followed by digits with 'Port-channel' followed by the same digits
interface_name = re.sub(
r"(?i)^(po|port-channel)([1-9]|[1-9][0-9]{1,3}|[1-3][0-9]{3}|40([0-8][0-9]|9[0-6]))$",
r"Port-channel\2",
interface_name,
flags=re.IGNORECASE,
)
# Replace 'eth' or 'e' followed by digits with 'Ethernet' followed by the same digits (for sub interface)
interface_name = re.sub(
r"(?i)^(?:e|eth(?:ernet)?)(\d(?:\/\d+){1,2}\.\d{1,4})$",
r"Ethernet\1",
interface_name,
flags=re.IGNORECASE,
)
# Replace 'Lo' or 'Loopback' followed by digits with 'Loopback' followed by the same digits
interface_name = re.sub(
r"(?i)^(lo|loopback)([0-9]|[1-9][0-9]{1,2}|10[0-1][0-9]|102[0-3])$",
r"Loopback\2",
interface_name,
flags=re.IGNORECASE,
)
return interface_name


def data_model_key_check(tested_object, keys):
"""
Expand Down
7 changes: 4 additions & 3 deletions tests/sanity/ignore-2.14.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ plugins/action/common/prepare_plugins/prep_003_list_defaults.py action-plugin-do
plugins/action/common/prepare_plugins/prep_104_topology_switches.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_106_topology_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_107_topology_vpc_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_108_vrf_lites.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_109_route_control.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_107_topology_interfaces_mtu.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_108_topology_vpc_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_109_vrf_lites.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_110_route_control.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_118_topology_edge_connections.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_999_verify.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_service_model.py action-plugin-docs # action plugin has no matching module to provide documentation
Expand Down
7 changes: 4 additions & 3 deletions tests/sanity/ignore-2.15.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ plugins/action/common/prepare_plugins/prep_003_list_defaults.py action-plugin-do
plugins/action/common/prepare_plugins/prep_104_topology_switches.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_106_topology_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_107_topology_vpc_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_108_vrf_lites.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_109_route_control.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_107_topology_interfaces_mtu.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_108_topology_vpc_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_109_vrf_lites.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_110_route_control.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_118_topology_edge_connections.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_999_verify.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_service_model.py action-plugin-docs # action plugin has no matching module to provide documentation
Expand Down
7 changes: 4 additions & 3 deletions tests/sanity/ignore-2.16.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ plugins/action/common/prepare_plugins/prep_003_list_defaults.py action-plugin-do
plugins/action/common/prepare_plugins/prep_104_topology_switches.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_105_fabric_overlay.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_106_topology_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_107_topology_vpc_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_108_vrf_lites.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_109_route_control.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_107_topology_interfaces_mtu.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_108_topology_vpc_interfaces.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_109_vrf_lites.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_110_route_control.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_118_topology_edge_connections.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_plugins/prep_999_verify.py action-plugin-docs # action plugin has no matching module to provide documentation
plugins/action/common/prepare_service_model.py action-plugin-docs # action plugin has no matching module to provide documentation
Expand Down
Loading