diff --git a/docs/caveats.md b/docs/caveats.md index d687ddd7f..caa3c93d8 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -238,8 +238,8 @@ defaults.devices.cumulus.libvirt.memory: 2048 Other caveats: * The default MTU value is 1500 to match the implementation defaults from other vendors and enable things like seamless OSPF peering. -* Older Cumulus Linux releases (up to at least 5.9.2) do not support asymmetrical IRB over VXLAN. * *netlab* uses Cumulus VX 5.3 containers created by Michael Kashin and downloaded from his Docker Hub account. These containers are severely out-of-date, are not tested in our integration tests, and might not work as expected. +* Some features - such as VRF route leaking and route advertisement in the default VRF (used in certain EVPN scenarios) - are not supported by NVUE, and require the use of custom config *snippets*. Only one such snippet is supported per configuration file (e.g. /etc/frr/frr.conf), which means a topology using a combination of multiple features that all require *snippets* will not work. (caveats-os10)= ## Dell OS10 diff --git a/docs/module/vrf.md b/docs/module/vrf.md index 9f2416536..474f6e51d 100644 --- a/docs/module/vrf.md +++ b/docs/module/vrf.md @@ -22,7 +22,7 @@ VRFs are supported on these platforms: | Cisco IOS XE[^18v] | ✅ | ✅ | ✅ | | Cisco Nexus OS | ✅ | ✅ | ✅ | | Cumulus Linux 4.x | ✅ | ✅ | ✅ | -| Cumulus 5.x (NVUE) | ✅ | ❌ | ✅ | +| Cumulus 5.x (NVUE) | ✅ | ✅ [❗](caveats-cumulus-nvue) | ✅ | | Dell OS10 | ✅ | ✅ | ✅ | | FRR [❗](caveats-frr) | ✅ | ✅ | ✅ | | Junos[^Junos] | ✅ | ✅ | ✅ | diff --git a/netsim/ansible/templates/bgp/cumulus_nvue.j2 b/netsim/ansible/templates/bgp/cumulus_nvue.j2 index 3516c5e87..8732f1d3c 100644 --- a/netsim/ansible/templates/bgp/cumulus_nvue.j2 +++ b/netsim/ansible/templates/bgp/cumulus_nvue.j2 @@ -1,3 +1,10 @@ {% from "bgp/cumulus_nvue.macro.j2" import bgp_in_vrf with context %} ---- +- set: + router: + bgp: + enable: on + autonomous-system: {{ bgp.as }} +{% if bgp.router_id|ipv4 %} + router-id: {{ bgp.router_id }} +{% endif %} {{ bgp_in_vrf('default', { 'bgp': bgp, 'loopback': loopback, 'af': af } ) }} diff --git a/netsim/ansible/templates/bgp/cumulus_nvue.macro.j2 b/netsim/ansible/templates/bgp/cumulus_nvue.macro.j2 index a388e8104..d54fc1eb9 100644 --- a/netsim/ansible/templates/bgp/cumulus_nvue.macro.j2 +++ b/netsim/ansible/templates/bgp/cumulus_nvue.macro.j2 @@ -15,8 +15,8 @@ enable: on autonomous-system: {{ bgp.as }} router-id: {{ vrf_bgp.router_id|default(bgp.router_id) }} -{% if vrf_bgp.rd is defined %} - rd: {{ vrf_bgp.rd }} +{% if vrf.rd is defined %} + rd: {{ vrf.rd }} {% endif %} {% if vrf_bgp.rr|default(False) %} route-reflection: @@ -32,7 +32,7 @@ {{ af }}-unicast: enable: on {{ redistribute.config(vrf_bgp,af=af)|indent(16,first=True) }} -{% set _loopback = [ vrf.loopback[af] ] if vrf.loopback[af] is defined and vrf_bgp.advertise_loopback else [] %} +{% set _loopback = [ vrf.loopback[af] ] if vrf.loopback[af] is defined and bgp.advertise_loopback|default(True) else [] %} {% set data = namespace(networks=_loopback) %} {% for l in interfaces|default([]) if l.bgp.advertise|default("") and l[af] is defined and l.vrf|default('default')==vrf_name %} {% set data.networks = data.networks + [ l[af] ] %} diff --git a/netsim/ansible/templates/vrf/cumulus_nvue.j2 b/netsim/ansible/templates/vrf/cumulus_nvue.j2 index 70dd30b57..546532c92 100644 --- a/netsim/ansible/templates/vrf/cumulus_nvue.j2 +++ b/netsim/ansible/templates/vrf/cumulus_nvue.j2 @@ -1,6 +1,6 @@ {% from "ospf/cumulus_nvue.j2" import vrf_ospf with context %} {% from "bgp/cumulus_nvue.macro.j2" import bgp_in_vrf with context %} - +--- - set: vrf: {% for vname,vdata in vrfs.items() %} @@ -15,9 +15,37 @@ {% endfor %} {% for vname,vdata in vrfs.items() if 'bgp' in vdata %} +{% if loop.first and 'bgp' not in module %} +{# In case the vrf module is used without the bgp module, need to repeat the base router config #} +{% include "bgp/cumulus_nvue.j2" %} +{% endif %} {{ bgp_in_vrf(vname, vdata) }} {% endfor %} +{# Workaround for lack of NVUE support for VRF route leaking - note can only create 1 snippet for frr.conf globally #} +{% set import_lengths = vrfs.values()|map(attribute='import',default=[])|map('length')|list %} +{% if import_lengths|max > 1 %} +- set: + system: + config: + snippet: + frr.conf: | +{% for vname,vdata in vrfs.items() %} + router bgp {{ bgp.as }} vrf {{ vname }} +{% for _af in ['ipv4','ipv6'] if _af in vdata.af %} + address-family {{ _af }} unicast + label vpn export auto + export vpn + import vpn + rd vpn export {{ vdata.rd }} + rt vpn import {{ vdata.import|join(" ") }} + rt vpn export {{ vdata.export|join(" ") }} + exit-address-family +{% endfor %} + ! +{% endfor %} +{% endif %} + {% for intf in netlab_interfaces if intf.vrf is defined %} - set: {% if intf.type=='loopback' %} diff --git a/netsim/devices/cumulus_nvue.py b/netsim/devices/cumulus_nvue.py index 2b10bc28a..85e339d76 100644 --- a/netsim/devices/cumulus_nvue.py +++ b/netsim/devices/cumulus_nvue.py @@ -39,18 +39,6 @@ def nvue_check_stp_features(node: Box, topology: Box) -> None: more_data=err_data, node=node) -""" -Checks for vrf route leaking usage which is not yet implemented -""" -def nvue_check_vrf_route_leaking(node: Box) -> None: - for vname,vdata in node.get("vrfs",{}).items(): - if len(vdata.get('export',[]))>1 or len(vdata.get('import',[]))>1: - log.error(f"Topology uses vrf route leaking which Netlab does not implement (yet) for Cumulus NVUE node '{node.name}'", - category=log.FatalError, - module='vrf', - hint='route leaking') - return - """ Checks for OSPFv3 which is not supported by NVUE configuration command """ @@ -174,12 +162,8 @@ def device_quirks(self, node: Box, topology: Box) -> None: nvue_check_ospfv3(node) nvue_merge_ospf_loopbacks(node) - # NVUE specific quirks if 'stp' in mods: nvue_check_stp_features(node,topology) - - if 'vrf' in mods: - nvue_check_vrf_route_leaking(node) if 'vxlan' in mods: mark_shared_mlag_vtep(node,topology)