Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cumulus_NVUE: Add mlag support #1715

Merged
merged 3 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions docs/module/lag.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ The above diagram illustrates the 3 supported topologies:
* 2:2 dual mlag between 2 pairs of nodes (4 nodes in total)

MLAG related parameters:
* **lag.mlag.peerlink**: Used on peerlink to configure a unique ID for the pair of switches providing the MLAG. Can be set to *True* for auto-id generation, or an integer (that must be globally unique)
* **lag.mlag.mac**: Used at node or interface level to configure the MAC address for the peerlink. *Netlab* can auto-generate this, so it is normally not necessary to set this
* **lag.mlag.peergroup**: Used on peerlink to configure a unique ID for the pair of switches providing the MLAG. Can be set to *True* for auto-id generation, or an integer (that must be globally unique)

A simple example:
```
Expand All @@ -94,7 +95,7 @@ groups:
device: dellos10
hosts:
members: [h1,h2]
device: frr # 'linux' does not support the lag module yet
device: frr # 'linux' does not support the lag module yet

links:
- lag:
Expand All @@ -104,5 +105,5 @@ links:

# Inter-switch peer link(s) for MLAG sync
- lag:
members: [s1-s2] # Note that multiple physical links are allowed here
mlag.peerlink: True # (also) used to derive a unique MAC address for this group of MLAG peers
members: [s1-s2] # Note that multiple physical links are allowed here
mlag.peergroup: True # (also) used to derive a unique MAC address for this group of MLAG peers
30 changes: 30 additions & 0 deletions netsim/ansible/templates/lag/cumulus_nvue.j2
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
{% if lag.mlag is defined %}
- set:
interface:
peerlink:
bond:
member:
{% for intf in interfaces if intf.lag.mlag.peergroup is defined %}
{{ intf.ifname }}: {}
{% for ch in interfaces if ch.lag._parentindex|default(False) == intf.linkindex %}
{{ ch.ifname }}: {}
{% endfor %}
{% endfor %}
type: peerlink
peerlink.{{ lag.mlag.vlan }}:
base-interface: peerlink
type: sub
vlan: {{ lag.mlag.vlan }}
mlag:
backup:
{{ lag.mlag.peer_backup_ip }}: {}
enable: on
mac-address: {{ lag.mlag.mac | hwaddr('linux') }}
peer-ip: {{ lag.mlag.peer }}
{% endif %}

{% for i in interfaces if i.type == 'lag' %}
{% if loop.first %}
- set:
interface:
{% endif %}
{{ i.ifname }}:
bond:
{% if '_mlag' in i.lag %}
mlag:
enable: on
id: {{ i.lag.ifindex }}
{% endif %}
{% set _lacp = i.lag.lacp|default(lag.lacp) %}
{% if _lacp=='slow' %}
lacp-rate: slow
Expand Down
7 changes: 7 additions & 0 deletions netsim/devices/cumulus_nvue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ features:
activate_af: True
lag:
passive: False
mlag:
peer:
global: True # Use global node level settings for peerlink
mac: 44:38:39:ff:00:00 # Base to generate a virtual MAC. NVidia reserved range
vlan: 4094 # Use this vlan
ip: linklocal # Use this IP subnet
backup_ip: loopback.ipv4 # Use loopback.ipv4 as a backup IP address for peerlink
ospf:
unnumbered: True
stp:
Expand Down
57 changes: 34 additions & 23 deletions netsim/modules/lag.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,33 +325,44 @@ def process_lag_links(topology: Box) -> None:
# populate_mlag_peer - Lookup the IPv4 loopback address for the mlag peer, and derive a virtual MAC to use
#
def populate_mlag_peer(node: Box, intf: Box, topology: Box) -> None:
_n = intf.neighbors[0].node
peer = topology.nodes[_n]
peer = topology.nodes[intf.neighbors[0].node]
features = devices.get_device_features(node,topology.defaults)
_mlag_peer = features.get('lag.mlag.peer',{})
if 'ip' in _mlag_peer:
if 'loopback' in _mlag_peer.ip:
_ip = peer.get(_mlag_peer.ip,None)
if _ip:
intf.lag.mlag.peer = str(netaddr.IPNetwork(_ip).ip)
mlag_peer = features.get('lag.mlag.peer',{})
_target = node.lag if mlag_peer.get('global',False) else intf.lag # Set at node or intf level?
if 'ip' in mlag_peer:
if mlag_peer.ip == 'linklocal':
_target.mlag.peer = 'linklocal'
elif 'loopback' in mlag_peer.ip: # Could check if an IGP is configured
ip = peer.get(mlag_peer.ip,None)
if ip:
_target.mlag.peer = str(netaddr.IPNetwork(ip).ip)
else:
log.error(f'Node {peer.name} must have {_mlag_peer.ip} defined to support MLAG',
log.error(f'Node {peer.name} must have {mlag_peer.ip} defined to support MLAG',
category=log.IncorrectValue,
module='lag')
else:
_net = netaddr.IPNetwork(_mlag_peer.ip)
_id = 0 if node.id < peer.id else 1
intf.lag.mlag.peer = str(_net[_id])
intf.lag.mlag.self = f"{_net[1-_id]}/{_net.prefixlen}" # including /prefix
net = netaddr.IPNetwork(mlag_peer.ip)
id = 0 if node.id < peer.id else 1
_target.mlag.peer = str(net[1-id]) # Higher node ID gets .1
_target.mlag.self = f"{net[id]}/{net.prefixlen}" # including /prefix

if 'backup_ip' in mlag_peer:
bk_ip = peer.get(mlag_peer.backup_ip,None)
if bk_ip:
_target.mlag.peer_backup_ip = str(netaddr.IPNetwork(bk_ip).ip)
else:
log.error(f'Node {peer.name} must have "{mlag_peer.backup_ip}" defined to support backup MLAG peerlink',
category=log.IncorrectValue,
module='lag')

if 'mac' in _mlag_peer:
_mac = netaddr.EUI(_mlag_peer.mac) # Generate unique virtual MAC per MLAG group
_mac._set_value(_mac.value + intf.get(PEERLINK_ID_ATT,0) % 65536 ) # ...based on lag.mlag.peergroup
intf.lag.mlag.mac = str(_mac)
if 'mac' in mlag_peer and not isinstance(_target.get('mlag.mac',None),str):
mac = netaddr.EUI(mlag_peer.mac) # Generate unique virtual MAC per MLAG group
mac._set_value(mac.value + intf.get(PEERLINK_ID_ATT,0) % 65536 ) # ...based on lag.mlag.peergroup
_target.mlag.mac = str(mac)

for v in ['vlan','ifindex']:
if v in _mlag_peer:
intf.lag.mlag[v] = _mlag_peer[v]
if v in mlag_peer:
_target.mlag[v] = mlag_peer[v]

intf.pop('vlan',None) # Remove any VLANs provisioned on peerlinks

Expand Down Expand Up @@ -383,11 +394,11 @@ def node_post_transform(self, node: Box, topology: Box) -> None:
populate_mlag_peer(node,i,topology)
has_peerlink = True
elif i.type=='lag':
i.lag = node.get('lag',{}) + i.lag # Merge node level settings with interface overrides
# i.pop('mtu',None) # Next PR: Remove any MTU settings - inherited from members
lacp_mode = i.get('lag.lacp_mode') # Inheritance copying is done elsewhere
node_atts = { k:v for k,v in node.get('lag',{}).items() if k!='mlag'}
i.lag = node_atts + i.lag # Merge node level settings with interface overrides
lacp_mode = i.get('lag.lacp_mode')
if lacp_mode=='passive' and not features.lag.get('passive',False):
log.error(f'Node {node.name} does not support passive LACP configured on interface {i.ifname}',
log.error(f'Node {node.name}({node.device}) does not support passive LACP configured on interface {i.ifname}',
category=log.IncorrectAttr,
module='lag')
if i.lag.get('_mlag',False) is True:
Expand Down
2 changes: 2 additions & 0 deletions netsim/modules/lag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ attributes:
lacp:
lacp_mode:
mode:
mlag:
mac: mac # MAC to use for MLAG peering, auto-derived if not set
link: # Most should be consistent across both interfaces on the link
lacp: { copy: global }
lacp_mode: { copy: global }
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/lag/09-mlag-m-to-m.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ groups:
_auto_create: true
switches:
members: [a1, a2, b1, b2]
module: [lag, vlan]
module: [ lag, vlan ] # May need to add OSPF for backup peerlink reachability
hosts:
members: [h1, h2]
device: linux
Expand Down Expand Up @@ -36,8 +36,8 @@ validate:
ping:
description: Pinging H2 from H1
nodes: [h1]
wait_msg: Waiting for STP to enable the ports
wait: 45
wait_msg: Waiting for STP and MLAG init-delay timer to enable the ports
wait: 180
plugin: ping('h2')
ping_gw:
description: Pinging gateway from H1
Expand Down
16 changes: 8 additions & 8 deletions tests/topology/expected/lag-mlag-m_to_m.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ nodes:
lag:
mlag:
ifindex: 4094
peer: 169.254.127.0
peer: 169.254.127.1
peergroup: 1
self: 169.254.127.1/31
self: 169.254.127.0/31
vlan: 4094
linkindex: 1
name: a1 -> a2
Expand Down Expand Up @@ -330,9 +330,9 @@ nodes:
lag:
mlag:
ifindex: 4094
peer: 169.254.127.1
peer: 169.254.127.0
peergroup: 1
self: 169.254.127.0/31
self: 169.254.127.1/31
vlan: 4094
linkindex: 1
name: a2 -> a1
Expand Down Expand Up @@ -438,9 +438,9 @@ nodes:
lag:
mlag:
ifindex: 4094
peer: 169.254.127.0
peer: 169.254.127.1
peergroup: 2
self: 169.254.127.1/31
self: 169.254.127.0/31
vlan: 4094
linkindex: 2
name: b1 -> b2
Expand Down Expand Up @@ -546,9 +546,9 @@ nodes:
lag:
mlag:
ifindex: 4094
peer: 169.254.127.1
peer: 169.254.127.0
peergroup: 2
self: 169.254.127.0/31
self: 169.254.127.1/31
vlan: 4094
linkindex: 2
name: b2 -> b1
Expand Down
8 changes: 4 additions & 4 deletions tests/topology/expected/node.clone-plugin-lag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -747,9 +747,9 @@ nodes:
lag:
mlag:
ifindex: 4094
peer: 169.254.127.0
peer: 169.254.127.1
peergroup: 1
self: 169.254.127.1/31
self: 169.254.127.0/31
vlan: 4094
linkindex: 1
name: r1 -> r2
Expand Down Expand Up @@ -953,9 +953,9 @@ nodes:
lag:
mlag:
ifindex: 4094
peer: 169.254.127.1
peer: 169.254.127.0
peergroup: 1
self: 169.254.127.0/31
self: 169.254.127.1/31
vlan: 4094
linkindex: 1
name: r2 -> r1
Expand Down
Loading