diff --git a/docs/module/vrf.md b/docs/module/vrf.md index 4252648c1b..74825b03d2 100644 --- a/docs/module/vrf.md +++ b/docs/module/vrf.md @@ -21,8 +21,8 @@ VRFs are supported on these platforms: | Cisco IOS | ✅ | ✅ | ✅ | | Cisco IOS XE[^18v] | ✅ | ✅ | ✅ | | Cisco Nexus OS | ✅ | ✅ | ✅ | -| Cumulus Linux | ✅ | ✅ | ✅ | -| Cumulus NVUE | ✅ | ❌ | ❌ | +| Cumulus Linux 4.x | ✅ | ✅ | ✅ | +| Cumulus 5.x (NVUE) | ✅ | ❌ | ✅ | | Dell OS10 | ✅ | ✅ | ✅ | | FRR [❗](caveats-frr) | ✅ | ✅ | ✅ | | Junos[^Junos] | ✅ | ✅ | ✅ | @@ -45,7 +45,8 @@ These platforms support routing protocols in VRFs: | Cisco IOS/IOSvL2 | ✅ [❗](caveats-iosv) | ✅ | ✅ | ✅ | ✅ | | Cisco IOS XE[^18v] | ✅ [❗](caveats-csr) | ✅ | ✅ | ✅ | ✅ | | Cisco Nexus OS | ✅ | ❌ | ✅ | -| Cumulus Linux | ✅ | ❌ | ✅ | ✅ | ✅ | +| Cumulus Linux 4.x | ✅ | ❌ | ✅ | ✅ | ✅ | +| Cumulus 5.x (NVUE) | ✅ | ❌ | ❌ | ❌ | ❌ | | Dell OS10 | ✅ | ❌ | ✅ | | FRR [❗](caveats-frr) | ✅ | ✅ | ✅ | ✅ | ✅ | | Junos[^Junos] | ✅ | ✅ | ✅ | diff --git a/netsim/ansible/templates/ospf/cumulus_nvue.j2 b/netsim/ansible/templates/ospf/cumulus_nvue.j2 index 8a10c7b761..db68cf2807 100644 --- a/netsim/ansible/templates/ospf/cumulus_nvue.j2 +++ b/netsim/ansible/templates/ospf/cumulus_nvue.j2 @@ -2,41 +2,51 @@ router: ospf: enable: on + +{% macro vrf_ospf(vrfname,vrf) %} +{% set _ospf = vrf.ospf %} +{% set _intfs = _ospf.interfaces|default([]) %} +{% if _intfs!=[] %} +- set: vrf: - default: + {{ vrfname }}: router: ospf: enable: on -{% if ospf.reference_bandwidth is defined %} - reference-bandwidth: {{ ospf.reference_bandwidth }} -{% endif %} -{% if 'router_id' in ospf %} - router-id: {{ ospf.router_id }} -{% endif %} -{% set node_area = ospf.area|default('0.0.0.0') %} - area: -{% for o_area in interfaces|json_query('[*].ospf.area')|union([node_area])|unique %} - '{{ o_area }}': - network: -{% if o_area == node_area and 'ipv4' in loopback %} - {{ loopback.ipv4 }}: {} -{% endif %} -{% for l in interfaces if 'ipv4' in l and l.ipv4 is string and l.ospf.area|default('') == o_area %} - {{ l.ipv4 }}: {} -{% endfor %} -{% endfor %} -{% for l in interfaces|default([]) if 'ospf' in l %} -{% if loop.first %} + redistribute: + connected: + enable: on +{% if _ospf.reference_bandwidth is defined %} + reference-bandwidth: {{ _ospf.reference_bandwidth }} +{% endif %} +{% if 'router_id' in _ospf %} + router-id: {{ _ospf.router_id }} +{% endif %} + +{% for l in _intfs %} +{% if loop.first %} interface: -{% endif %} +{% endif %} {{ l.ifname }}: router: ospf: -{% if l.ospf.cost is defined %} + area: {{ l.ospf.area|default(ospf.area) }} +{% if l.ospf.cost is defined %} cost: {{ l.ospf.cost }} -{% endif %} +{% endif %} network-type: {{ l.ospf.network_type|default('broadcast') }} -{% if l.ospf.passive | default(False) %} +{% if l.ospf.passive | default(False) %} passive: on -{% endif %} -{% endfor %} +{% endif %} +{% endfor %} + +{% else %} +# No OSPF interfaces in VRF {{ vrfname }} +{% endif %} +{% endmacro %} + +{% if ospf is defined %} +{% set _lo = (loopback + { 'ospf': { 'area': ospf.area|default('0.0.0.0') } }) if 'ipv4' in loopback else {} %} +{% set _ospf_intfs = [_lo] + interfaces|default([])|selectattr('ospf','defined')|list %} +{{ vrf_ospf("default", { 'ospf': ospf + { 'interfaces': _ospf_intfs } } ) }} +{% endif %} diff --git a/netsim/ansible/templates/vrf/cumulus_nvue.j2 b/netsim/ansible/templates/vrf/cumulus_nvue.j2 index bc3878c776..e6d7fc43ef 100644 --- a/netsim/ansible/templates/vrf/cumulus_nvue.j2 +++ b/netsim/ansible/templates/vrf/cumulus_nvue.j2 @@ -1,6 +1,14 @@ +{% from "ospf/cumulus_nvue.j2" import vrf_ospf with context %} + - set: vrf: {% for vname,vdata in vrfs.items() %} {{ vname }}: table: auto {% endfor %} + +{% for vname,vdata in vrfs.items() if 'ospf' in vdata %} +{% if vdata.af.ipv4|default(False) %} +{{ vrf_ospf(vname,vdata) }} +{% endif %} +{% endfor %} diff --git a/netsim/devices/cumulus_nvue.py b/netsim/devices/cumulus_nvue.py index 060f3cd79a..8a7d84ac26 100644 --- a/netsim/devices/cumulus_nvue.py +++ b/netsim/devices/cumulus_nvue.py @@ -37,6 +37,18 @@ 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 + class Cumulus_Nvue(_Quirks): @classmethod @@ -49,3 +61,6 @@ def device_quirks(self, node: Box, topology: Box) -> None: # NVUE specific quirks if 'stp' in mods: nvue_check_stp_features(node,topology) + + if 'vrf' in mods: + nvue_check_vrf_route_leaking(node) diff --git a/netsim/devices/cumulus_nvue.yml b/netsim/devices/cumulus_nvue.yml index 4bc291589f..ee32b003f7 100644 --- a/netsim/devices/cumulus_nvue.yml +++ b/netsim/devices/cumulus_nvue.yml @@ -35,7 +35,8 @@ features: model: switch svi_interface_name: "vlan{vlan}" subif_name: "{ifname}.{vlan.access_id}" - vrf: True + vrf: + ospfv2: True clab: kmods: initial: [ ebtables ]