diff --git a/docs/module/vrf.md b/docs/module/vrf.md index ffcdee2e2..9f2416536 100644 --- a/docs/module/vrf.md +++ b/docs/module/vrf.md @@ -46,7 +46,7 @@ These platforms support routing protocols in VRFs: | Cisco IOS XE[^18v] | ✅ [❗](caveats-csr) | ✅ | ✅ | ✅ | ✅ | | Cisco Nexus OS | ✅ | ❌ | ✅ | | Cumulus Linux 4.x | ✅ | ❌ | ✅ | ✅ | ✅ | -| Cumulus 5.x (NVUE) | ✅ | ❌ | ❌ | ❌ | ❌ | +| Cumulus 5.x (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 7c73060f1..3516c5e87 100644 --- a/netsim/ansible/templates/bgp/cumulus_nvue.j2 +++ b/netsim/ansible/templates/bgp/cumulus_nvue.j2 @@ -1,114 +1,3 @@ -{% import "templates/routing/_redistribute.cumulus_nvue.j2" as redistribute with context %} -{% macro advertise_communities(comms) %} - community-advertise: -{% for c in ['standard', 'extended', 'large'] %} - {{ c.replace("standard","regular") }}: {{ 'on' if c in comms else 'off' }} -{% endfor %} -{% endmacro %} - -- set: - router: - bgp: - enable: on - vrf: - default: - router: - bgp: -{% if bgp.rr|default(False) %} - route-reflection: - enable: on -{% if bgp.rr_cluster_id is defined %} - cluster-id: {{ bgp.rr_cluster_id }} -{% endif %} -{% endif %} -{% for af in ['ipv4','ipv6'] if bgp[af] is defined %} -{% if loop.first %} - address-family: -{% endif %} - {{ af }}-unicast: - enable: on -{{ redistribute.config(bgp,af=af)|indent(16,first=True) }} -{% set _loopback = [ loopback[af] ] if loopback[af] is defined and bgp.advertise_loopback else [] %} -{% set data = namespace(networks=_loopback) %} -{% for l in interfaces|default([]) if l.bgp.advertise|default("") and l[af] is defined and not 'vrf' in l %} -{% set data.networks = data.networks + [ l[af] ] %} -{% endfor %} -{% for pfx in bgp.originate|default([]) if af == 'ipv4' %} -{% set data.networks = data.networks + [ pfx ] %} -{% endfor %} -{% if data.networks!=[] %} - network: -{% for pfx in data.networks %} - {{ pfx|ipaddr('0') }}: {} -{% endfor %} -{% endif %} -{% endfor %} - autonomous-system: {{ bgp.as }} -{% for n in bgp.neighbors %} -{% if loop.first %} - neighbor: -{% endif %} -{% if n.local_if is defined %} - {{ n.local_if }}: - type: unnumbered - remote-as: {{ n.as }} - address-family: - ipv4-unicast: - enable: {{ 'on' if n.ipv4_rfc8950|default(False) else 'off' }} - community-advertise: - extended: on - ipv6-unicast: - enable: {{ 'on' if n.ipv6|default(False) and n.activate.ipv6|default(False) else 'off' }} - community-advertise: - extended: on -{% endif %} -{% for af in ('ipv4','ipv6') if af in n and n[af] is string %} - {{ n[af] }}: - description: "{{ n.name }}" -{% if n._source_ifname is defined %} - update-source: {{ n._source_ifname }} -{% endif %} - remote-as: {{ 'internal' if n.as==bgp.as else n.as }} -{% if n.local_as is defined %} - local-as: - asn: {{ n.local_as }} - enable: on -{% if n.replace_global_as|default(True) %} - replace: on - prepend: off -{% endif %} -{% endif %} - address-family: -{# NVUE cannot turn off default IPv4 activation, so we have to implement a fix for IPv6 #} -{% if af == 'ipv6' %} - ipv4-unicast: - enable: off -{% endif %} - {{ af }}-unicast: - enable: {{ 'on' if n.activate[af]|default(False) else 'off' }} -{% if 'ibgp' in n.type %} -{% if bgp.next_hop_self|default(False) %} - nexthop-setting: self -{% endif %} -{% if bgp.rr|default('') and not n.rr|default('') %} - route-reflector-client: on -{% endif %} -{% endif %} -{% if n.type in bgp.community|default({}) %} -{{ advertise_communities( bgp.community[n.type] ) }} -{% endif %} -{% endfor %} -{% endfor %} -{% if 'router_id' in bgp %} - router-id: {{ bgp.router_id }} -{% endif %} -{% if bgp.originate is defined %} - static: -{% for pfx in bgp.originate|default([]) %} - {{ pfx|ipaddr('0') }}: - address-family: ipv4-unicast - via: - blackhole: - type: blackhole -{% endfor %} -{% endif %} +{% from "bgp/cumulus_nvue.macro.j2" import bgp_in_vrf with context %} +--- +{{ 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 new file mode 100644 index 000000000..efc81ccc8 --- /dev/null +++ b/netsim/ansible/templates/bgp/cumulus_nvue.macro.j2 @@ -0,0 +1,115 @@ +{% import "templates/routing/_redistribute.cumulus_nvue.j2" as redistribute with context %} +{% macro advertise_communities(comms) %} + community-advertise: +{% for c in ['standard', 'extended', 'large'] %} + {{ c.replace("standard","regular") }}: {{ 'on' if c in comms else 'off' }} +{% endfor %} +{% endmacro %} +{% macro bgp_in_vrf(vrf_name,vrf) %} +{% set vrf_bgp = vrf.bgp %} +- set: + vrf: + {{ vrf_name }}: + router: + bgp: + 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 }} +{% endif %} +{% if vrf_bgp.rr|default(False) %} + route-reflection: + enable: on +{% if vrf_bgp.rr_cluster_id is defined %} + cluster-id: {{ vrf_bgp.rr_cluster_id }} +{% endif %} +{% endif %} +{% for af in ['ipv4','ipv6'] if vrf.af[af] is defined %} +{% if loop.first %} + address-family: +{% endif %} + {{ 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 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] ] %} +{% endfor %} +{% for pfx in vrf_bgp.originate|default([]) if af == 'ipv4' %} +{% set data.networks = data.networks + [ pfx ] %} +{% endfor %} +{% if data.networks!=[] %} + network: +{% for pfx in data.networks %} + {{ pfx|ipaddr('0') }}: {} +{% endfor %} +{% endif %} +{% endfor %} +{% for n in vrf_bgp.neighbors|default([]) %} +{% if loop.first %} + neighbor: +{% endif %} +{% if n.local_if is defined %} + {{ n.local_if }}: + type: unnumbered + remote-as: {{ n.as }} + address-family: + ipv4-unicast: + enable: {{ 'on' if n.ipv4_rfc8950|default(False) else 'off' }} + community-advertise: + extended: on + ipv6-unicast: + enable: {{ 'on' if n.ipv6|default(False) and ('activate' not in n or n.activate.ipv6|default(False)) else 'off' }} + community-advertise: + extended: on +{% endif %} +{% for af in ('ipv4','ipv6') if af in n and n[af] is string %} + {{ n[af] }}: + description: "{{ n.name }}" +{% if n._source_ifname is defined %} + update-source: {{ n._source_ifname }} +{% endif %} + remote-as: {{ 'internal' if n.as==bgp.as else n.as }} +{% if n.local_as is defined %} + local-as: + asn: {{ n.local_as }} + enable: on +{% if n.replace_global_as|default(True) %} + replace: on + prepend: off +{% endif %} +{% endif %} + address-family: +{# NVUE cannot turn off default IPv4 activation, so we have to implement a fix for IPv6 #} +{% if af == 'ipv6' %} + ipv4-unicast: + enable: off +{% endif %} + {{ af }}-unicast: + enable: {{ 'on' if 'activate' not in n or n.activate[af]|default(False) else 'off' }} +{% if 'ibgp' in n.type %} +{% if vrf_bgp.next_hop_self|default(False) %} + nexthop-setting: self +{% endif %} +{% if vrf_bgp.rr|default('') and not n.rr|default('') %} + route-reflector-client: on +{% endif %} +{% endif %} +{% if n.type in vrf_bgp.community|default({}) %} +{{ advertise_communities( vrf_bgp.community[n.type] ) }} +{% endif %} +{% endfor %} +{% endfor %} +{% if vrf_bgp.originate is defined %} + static: +{% for pfx in vrf_bgp.originate|default([]) %} + {{ pfx|ipaddr('0') }}: + address-family: ipv4-unicast + via: + blackhole: + type: blackhole +{% endfor %} +{% endif %} +{% endmacro %} diff --git a/netsim/ansible/templates/evpn/cumulus_nvue.j2 b/netsim/ansible/templates/evpn/cumulus_nvue.j2 index 5cbe3e05a..a46d3a36e 100644 --- a/netsim/ansible/templates/evpn/cumulus_nvue.j2 +++ b/netsim/ansible/templates/evpn/cumulus_nvue.j2 @@ -96,8 +96,6 @@ {% endif %} router: bgp: - autonomous-system: {{ vdata.as|default(bgp.as) }} - enable: on rd: {{ vdata.rd }} route-export: to-evpn: @@ -115,15 +113,6 @@ route-export: to-evpn: enable: on -{# This part belongs in the VRF or BGP template, to be moved #} - enable: on - redistribute: - connected: - enable: on -{% if 'ospf' in vdata %} - ospf: - enable: on -{% endif %} {% endfor %} {% endfor %} {% endif %} diff --git a/netsim/ansible/templates/vrf/cumulus_nvue.j2 b/netsim/ansible/templates/vrf/cumulus_nvue.j2 index 1fbf2804f..70dd30b57 100644 --- a/netsim/ansible/templates/vrf/cumulus_nvue.j2 +++ b/netsim/ansible/templates/vrf/cumulus_nvue.j2 @@ -1,4 +1,5 @@ {% from "ospf/cumulus_nvue.j2" import vrf_ospf with context %} +{% from "bgp/cumulus_nvue.macro.j2" import bgp_in_vrf with context %} - set: vrf: @@ -9,10 +10,14 @@ {% for vname,vdata in vrfs.items() if 'ospf' in vdata %} {% if vdata.af.ipv4|default(False) %} -{{ vrf_ospf(vname,vdata) }} +{{ vrf_ospf(vname,vdata) }} {% endif %} {% endfor %} +{% for vname,vdata in vrfs.items() if 'bgp' in vdata %} +{{ bgp_in_vrf(vname, vdata) }} +{% endfor %} + {% for intf in netlab_interfaces if intf.vrf is defined %} - set: {% if intf.type=='loopback' %} diff --git a/netsim/devices/cumulus_nvue.yml b/netsim/devices/cumulus_nvue.yml index 69237a2c8..515d47369 100644 --- a/netsim/devices/cumulus_nvue.yml +++ b/netsim/devices/cumulus_nvue.yml @@ -29,7 +29,7 @@ features: lla: True bgp: activate_af: True - import: [ connected, ospf ] + import: [ connected, ospf, vrf ] ipv6_lla: True local_as: True local_as_ibgp: True @@ -65,6 +65,7 @@ features: subif_name: "{ifname}.{vlan.access_id}" vrf: ospfv2: True + bgp: True vxlan: True # vtep6: true # Waiting for https://github.com/CumulusNetworks/ifupdown2/pull/315 clab: