From c879eb17a20fcd9512a319f3f2f74c9c8ec57526 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 24 Nov 2024 13:44:52 -0600 Subject: [PATCH 1/5] Case ivrf/11-multi-vrf-ospf working --- netsim/ansible/templates/ospf/cumulus_nvue.j2 | 64 +++++++++++-------- netsim/ansible/templates/vrf/cumulus_nvue.j2 | 8 +++ netsim/devices/cumulus_nvue.yml | 3 +- 3 files changed, 47 insertions(+), 28 deletions(-) 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.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 ] From 94f9975bc46a6458ca93634353ee97c5d223415f Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 24 Nov 2024 13:48:25 -0600 Subject: [PATCH 2/5] Update docs (also note VRF aware loopback, supported by previous PR) --- docs/module/vrf.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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] | ✅ | ✅ | ✅ | From 9ce3f8f6479f5c0cf08f995e499f0c49670110be Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 24 Nov 2024 15:12:14 -0600 Subject: [PATCH 3/5] Check for vrf.bgp support for topologies that use VPN address families --- netsim/modules/vrf.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/netsim/modules/vrf.py b/netsim/modules/vrf.py index 79e7ed0e65..1d9784dac3 100644 --- a/netsim/modules/vrf.py +++ b/netsim/modules/vrf.py @@ -527,6 +527,16 @@ def node_post_transform(self, node: Box, topology: Box) -> None: return + # If VPN address families are used, check that the node supports vrf.bgp feature + if node.get('af.vpnv4',False) or node.get('af.vpnv6',False): + if not features.get('vrf.bgp',False): + log.error( + f"Topology uses VPN address families but node '{node.name}'({node.device}) does not support 'vrf.bgp'", + category=AttributeError, + module='vrf', + hint='vrf.bgp') + return + node.vrfs = node.vrfs or {} # ... otherwise make sure the 'vrfs' dictionary is not empty vrfidx = 100 From 050b7f43766d95764b9beb6e0684398b5a737d43 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Mon, 25 Nov 2024 08:42:16 -0600 Subject: [PATCH 4/5] Move vrf.bgp check to device quirk --- netsim/devices/cumulus_nvue.py | 13 +++++++++++++ netsim/modules/vrf.py | 10 ---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/netsim/devices/cumulus_nvue.py b/netsim/devices/cumulus_nvue.py index 060f3cd79a..bf1be94160 100644 --- a/netsim/devices/cumulus_nvue.py +++ b/netsim/devices/cumulus_nvue.py @@ -37,6 +37,16 @@ def nvue_check_stp_features(node: Box, topology: Box) -> None: more_data=err_data, node=node) +""" +Checks for vrf.bgp usage which is not yet implemented +""" +def nvue_check_vrf_bgp(node: Box) -> None: + if node.get('af.vpnv4',False) or node.get('af.vpnv6',False): + log.error(f"Topology uses vrf BGP features which Cumulus NVUE node '{node.name}' does not support yet", + category=AttributeError, + module='vrf', + hint='vrf.bgp') + class Cumulus_Nvue(_Quirks): @classmethod @@ -49,3 +59,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_bgp(node) diff --git a/netsim/modules/vrf.py b/netsim/modules/vrf.py index 1d9784dac3..79e7ed0e65 100644 --- a/netsim/modules/vrf.py +++ b/netsim/modules/vrf.py @@ -527,16 +527,6 @@ def node_post_transform(self, node: Box, topology: Box) -> None: return - # If VPN address families are used, check that the node supports vrf.bgp feature - if node.get('af.vpnv4',False) or node.get('af.vpnv6',False): - if not features.get('vrf.bgp',False): - log.error( - f"Topology uses VPN address families but node '{node.name}'({node.device}) does not support 'vrf.bgp'", - category=AttributeError, - module='vrf', - hint='vrf.bgp') - return - node.vrfs = node.vrfs or {} # ... otherwise make sure the 'vrfs' dictionary is not empty vrfidx = 100 From bff2b986b8ef5ed54e2aeb4765a26874b1381861 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Mon, 25 Nov 2024 12:53:19 -0600 Subject: [PATCH 5/5] Rephrase quirk --- netsim/devices/cumulus_nvue.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/netsim/devices/cumulus_nvue.py b/netsim/devices/cumulus_nvue.py index bf1be94160..8a7d84ac26 100644 --- a/netsim/devices/cumulus_nvue.py +++ b/netsim/devices/cumulus_nvue.py @@ -38,14 +38,16 @@ def nvue_check_stp_features(node: Box, topology: Box) -> None: node=node) """ -Checks for vrf.bgp usage which is not yet implemented +Checks for vrf route leaking usage which is not yet implemented """ -def nvue_check_vrf_bgp(node: Box) -> None: - if node.get('af.vpnv4',False) or node.get('af.vpnv6',False): - log.error(f"Topology uses vrf BGP features which Cumulus NVUE node '{node.name}' does not support yet", - category=AttributeError, - module='vrf', - hint='vrf.bgp') +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): @@ -61,4 +63,4 @@ def device_quirks(self, node: Box, topology: Box) -> None: nvue_check_stp_features(node,topology) if 'vrf' in mods: - nvue_check_vrf_bgp(node) + nvue_check_vrf_route_leaking(node)