Skip to content

lag module: Model peerlink as regular lag link #2084

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

Draft
wants to merge 48 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ce0d7b8
LAG: Refactor peerlinks to simply be another instance of a port-channel
jbemmel Mar 14, 2025
45ff46d
Working Cumulus
jbemmel Mar 14, 2025
1c888ee
Add custom mlag pool to enable ipv6 lla
jbemmel Mar 14, 2025
91f4942
Disable ipv6 configs on peerlink, still not quite working
jbemmel Mar 14, 2025
2cecb92
Working now
jbemmel Mar 14, 2025
a7ef831
Update test results, enable ospf v4
jbemmel Mar 14, 2025
4a09d45
Dell: Ignore 'peerlink' interfaces (not the most elegant solution)
jbemmel Mar 14, 2025
6b94b8c
Add peer vlan in 'irb' modei with prefix, downgrade to 'route' at nod…
jbemmel Mar 17, 2025
a6bfab8
Use existing untagged vlan if available
jbemmel Mar 17, 2025
84b22d1
Fixes
jbemmel Mar 17, 2025
2dba324
Fix STP disable
jbemmel Mar 19, 2025
375ab3d
Update test results
jbemmel Mar 25, 2025
5caadaa
Undo vlan change
jbemmel Mar 25, 2025
c56450f
Allow peer vlan to be configured on Dell OS10 too
jbemmel Mar 25, 2025
8e811a2
Allow peering vlan to be disabled (by setting to None / 0)
jbemmel Mar 25, 2025
8678709
Update docs
jbemmel Mar 25, 2025
5a7dd66
Filter peerlink, update tests
jbemmel Mar 25, 2025
7d29d7a
Determine assigned peer IP, don't generate one
jbemmel Mar 25, 2025
e27e856
Clarify error message
jbemmel Mar 25, 2025
2979d71
* Define custom pool in YAML
jbemmel Mar 26, 2025
0cc7ccf
Need to allow all vlans on the peerlink
jbemmel Mar 26, 2025
4eb1789
Newline
jbemmel Mar 26, 2025
fd1e273
Cannot use the default p2p pool because it does not specify 'p2p' all…
jbemmel Mar 26, 2025
ba163be
Updated test results
jbemmel Mar 26, 2025
4d5b6ad
Define mlag feature for 'none' device
jbemmel Mar 26, 2025
aeff546
Support unnumbered ipv4 too
jbemmel Mar 26, 2025
210a062
Prefer 'linklocal' if ipv6 is True
jbemmel Mar 26, 2025
95f3391
Use global p2p address pool instead of defining a special instance
jbemmel Mar 26, 2025
cf761bb
Updated test results
jbemmel Mar 26, 2025
070a3fa
Update error test result
jbemmel Mar 26, 2025
8c2d703
Add '_allow_all' flag for VLAN trunks
jbemmel Mar 26, 2025
46ec345
Add flag to VLAN attributes
jbemmel Mar 26, 2025
c686fbe
Update tests with new VLAN flag
jbemmel Mar 26, 2025
0f061f5
Undo p2p pool changes, make trunk configurable, add examples
jbemmel Mar 26, 2025
f20bb51
Undo error test changes
jbemmel Mar 26, 2025
cd5c658
Use 'all'
jbemmel Mar 27, 2025
4123ad4
Fix bug #2090
jbemmel Mar 27, 2025
182c423
Updated test results after fixing VLAN prefix allocation bug
jbemmel Mar 27, 2025
abb7134
Remove internal flag
jbemmel Mar 27, 2025
f86b71c
Don't configure 'p2p' pool on peerlink VLAN
jbemmel Mar 29, 2025
3d6a106
Update test results
jbemmel Mar 29, 2025
0f1c4f6
Update docs
jbemmel Mar 29, 2025
d1070f3
Use _allow_all flag
jbemmel Mar 29, 2025
860ba56
Fix Dell OS10 VLAN script
jbemmel Mar 29, 2025
05b8000
Let '_all_vlans' apply to native VLAN too, avoid creating L3 VLAN whe…
jbemmel Mar 29, 2025
ac70f89
Skip creation of default vlan
jbemmel Mar 29, 2025
ff96be8
Fix check for all vlans
jbemmel Mar 29, 2025
1b3e7e8
Guard against access_id not being set
jbemmel Mar 29, 2025
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
34 changes: 33 additions & 1 deletion docs/module/lag.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The following parameters can be set on individual links:
* **lag.ifindex**: Optional parameter that controls the naming of the LAG (bonding, port-channel) interface.
* **lag.mlag**: Optional dictionary with peer link parameters; see [below](lag-mlag)

This configuration module creates a virtual link with the link type set to **lag** between the **lag.members** and appends the links described in the **lag.members** list to the topology **links** list.
This configuration module creates virtual **lag** type interfaces on nodes that are listed in **lag.members** and appends the links described in the **lag.members** list to the topology **links** list.

## Example

Expand Down Expand Up @@ -113,6 +113,38 @@ links:
mlag.peergroup: True # (also) used to derive a unique MAC address for this group of MLAG peers
```

### Peerlink configuration
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This left me a bit confused. I think it would be nice to have two short examples, one of them documenting how the peer link can be used as pure routed link (although we know there are other things going on in the background), another one describing how to use VLANs on the peer link (example use case: VRF Lite)

It's also worth mentioning that you chose that netlab configures "allow all VLANs" on the peer link. That is not always the case in real-life deployments.


The **peerlink** between mlag peers is modeled as a one-to-one lag link, and hence all the various *Netlab* features - such as IP addressing, OSPF and BGP - can be configured on it. Different platforms use different approaches and defaults for implementing the peerlink - e.g. different vlan numbers, irb versus routed mode, etc.; some of these defaults can be changed through _device features_.

By default, *Netlab* will configure the peerlink as a trunk that allows all VLANs. To restrict this, include a VLAN trunk definition on the peerlink:
```
links:
- lag:
mlag.peergroup: True
vlan.trunk: [ vlan1000 ] # This will allow only vlan1000 plus the device-specific peerlink vlan and the default vlan
```

By default, on most platforms the global `lan` pool will be used to allocate IP addresses similar to regular IRB VLANs. If different addressing is desired, set a custom 'pool' or 'prefix' attribute on the peerlink:
```
addressing:
custom_peerlink:
ipv4: 169.254.0.0/16
prefix: 31
allocation: p2p

links:
- lag:
mlag.peergroup: True
pool: custom_peerlink

- lag:
mlag.peergroup: True
prefix:
ipv4: 169.254.127.0/31
allocation: p2p
```

### Advanced MLAG Parameters

The **lag.mlag.peer.backup_ip** _device feature_ specifies a property from the node data model that should be used to determine the MLAG peer's backup IP address (the IP address an MLAG node tries to reach when resolving a split-brain scenario). This parameter is part of the device features and can be changed only for a device type, not individual nodes.
Expand Down
24 changes: 14 additions & 10 deletions netsim/ansible/templates/initial/cumulus_nvue.j2
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% macro decl_interface(i) %}
{{ i.ifname }}:
type: {{ 'svi' if i.type=='svi' else 'sub' if i.type=='vlan_member' else 'bond' if i.type=='lag' else 'swp' }}
type: {{ 'svi' if i.type=='svi' else 'sub' if i.type=='vlan_member' else 'bond' if i.type=='lag'
else 'peerlink' if i.ifname=='peerlink' else 'swp' }}
link:
{% if i.mtu is defined %}
mtu: {{ i.mtu }}
Expand All @@ -12,28 +13,30 @@
{% elif i.type|default("") == "stub" %}
description: Stub interface
{% endif %}
{% if 'peerlink' not in i.ifname or i.ipv4 is defined %}
ip:
{% if i.ipv4 is defined or (i.ipv6 is string and i.ipv6|ipv6) %}
{% if i.ipv4 is defined or (i.ipv6 is string and i.ipv6|ipv6) %}
address:
{% if i.ipv4 is defined %}
{% if i.ipv4 is defined %}
{{ (loopback if i.ipv4 == True else i).ipv4 }}: {}
{% endif %}
{% if i.ipv6 is string and i.ipv6|ipv6 %}
{% endif %}
{% if i.ipv6 is string and i.ipv6|ipv6 %}
{{ i.ipv6 }}: {}
{% endif %}
{% endif %}
{% endif %}
ipv6:
{% if i.ipv6 is defined %}
{% if i.ipv6 is defined %}
forward: on
{% if 'ipv6' not in i.dhcp.client|default({}) %}
{% if 'ipv6' not in i.dhcp.client|default({}) %}
neighbor-discovery:
enable: on
router-advertisement:
enable: on
interval: 5000
{% endif %}
{% else %}
{% endif %}
{% else %}
enable: off
{% endif %}
{% endif %}
{% endmacro %}

Expand All @@ -57,6 +60,7 @@
address:
dhcp: {}
type: eth

{# Avoid creating L2-only sub interfaces #}
{% for l in interfaces|default([]) if l.type not in ['loopback','vlan_member']
or (l.type=='vlan_member' and ('ipv4' in l or 'ipv6' in l)) %}
Expand Down
2 changes: 1 addition & 1 deletion netsim/ansible/templates/initial/dellos10.j2
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface {{ mgmt.ifname|default('mgmt1/1/1') }}
no lldp transmit
no lldp receive
!
{% for l in interfaces|default([]) %}
{% for l in interfaces|default([]) if l.ifname!='peerlink' %}
interface {{ l.ifname }}
no shutdown
{% if l.virtual_interface is not defined or (l.type=='lag' and ('ipv4' in l or 'ipv6' in l)) %}
Expand Down
26 changes: 6 additions & 20 deletions netsim/ansible/templates/lag/arubacx.j2
Original file line number Diff line number Diff line change
@@ -1,23 +1,3 @@
{# Provision VSX peerlink #}
{% for intf in interfaces if intf.lag.mlag.peergroup is defined %}

interface lag {{ intf.lag.mlag.ifindex }}
no shutdown
no routing
vlan trunk native 1
vlan trunk allowed all
lacp mode active
description VSX ISL

!
{% for ch in ([intf]+interfaces) if ch==intf or ch.lag._peerlink|default(0) == intf.linkindex %}
interface {{ ch.ifname }}
no shutdown
description {{ ch.name }} (ISL in lag {{intf.lag.mlag.ifindex }})
lag {{ intf.lag.mlag.ifindex }}
!
{% endfor %}

!
! VSX Config
!
Expand All @@ -36,6 +16,11 @@ vsx
interface {{ intf.ifname }}{{ mclag_intf }}{{ mclag_intf_static }}
{% if '_mlag' in intf.lag %}
description {{ intf.name }} (VSX LAG {{ intf.lag.ifindex }})
{% elif intf.lag.mlag.peergroup is defined %}
no routing
vlan trunk native 1
vlan trunk allowed all
description VSX ISL
{% else %}
description {{ intf.name }}
{% endif %}
Expand All @@ -57,6 +42,7 @@ interface {{ intf.ifname }}{{ mclag_intf }}{{ mclag_intf_static }}
{% for ch in interfaces if ch.lag._parentindex|default(None) == intf.lag.ifindex %}
!
interface {{ ch.ifname }}
no shutdown
description {{ ch.name }} in lag {{ intf.lag.ifindex }}
lag {{ intf.lag.ifindex }}
!
Expand Down
24 changes: 5 additions & 19 deletions netsim/ansible/templates/lag/cumulus_nvue.j2
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
{% if lag.mlag is defined %}
- set:
interface:
peerlink:
bridge:
domain:
br_default:
learning: off # As recommended by NVidia
bond:
member:
{% for intf in interfaces if intf.lag.mlag.peergroup is defined %}
{{ intf.ifname }}: {}
{% for ch in interfaces if ch.lag._peerlink|default(0) == intf.linkindex %}
{{ ch.ifname }}: {}
{% endfor %}
{% endfor %}
type: peerlink
peerlink.{{ lag.mlag.vlan }}:
base-interface: peerlink
type: sub
vlan: {{ lag.mlag.vlan }}
mlag:
init-delay: 10
backup:
Expand Down Expand Up @@ -54,6 +35,11 @@
bridge:
domain:
br_default: {}
{% elif i.ifname=='peerlink' %}
bridge:
domain:
br_default:
learning: off # As recommended by NVidia
{% endif %}
bond:
{% if '_mlag' in i.lag %}
Expand Down
7 changes: 3 additions & 4 deletions netsim/ansible/templates/lag/dellos10.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
! OS10 internally uses port-channel 1000 and VLAN 4094 for VLT
{% for intf in interfaces if intf.lag.mlag.peergroup is defined %}
vlt-domain {{ intf.lag.mlag.peergroup }}
backup destination {{ intf.lag.mlag.peer }}
backup destination {{ intf.lag.mlag.peer_backup_ip }}
vlt-mac {{ intf.lag.mlag.mac | hwaddr('linux') }}
{# Reduce default 90-second delay timer to avoid wasting time waiting #}
delay-restore 30
discovery-interface {{ intf.ifname }}
{% for ch in interfaces if ch.lag._peerlink|default(0) == intf.linkindex %}
{% for ch in interfaces if ch.lag._parentindex|default(None) == intf.lag.ifindex %}
discovery-interface {{ ch.ifname }}
{% endfor %}
{% endfor %}

{% for intf in interfaces if intf.type == 'lag' %}
{% for intf in interfaces if intf.type == 'lag' and intf.ifname != 'peerlink' %}
interface {{ intf.ifname }}
{% if '_mlag' in intf.lag %}
{% set desc = intf.name.replace(",", "\\\\,") + " in vlt-port-channel " + intf.lag.ifindex|string %}
Expand Down
32 changes: 12 additions & 20 deletions netsim/ansible/templates/lag/eos.j2
Original file line number Diff line number Diff line change
@@ -1,42 +1,34 @@
{% if lag.mlag is defined %}
{# Provision mlag peerlink, EOS supports at most 1 peerlink #}
{% for intf in interfaces if intf.lag.mlag.peergroup is defined %}

vlan {{ intf.lag.mlag.vlan }}
vlan {{ lag.mlag.vlan }}
name "MLAG-peerlink"
trunk group m1peer
!
no spanning-tree vlan-id {{ intf.lag.mlag.vlan }}
no spanning-tree vlan-id {{ lag.mlag.vlan }}

interface port-channel {{ intf.lag.mlag.ifindex }}
description MLAG peerlink(s) {{ intf.name }}
interface port-channel {{ lag.mlag.ifindex }}
description MLAG peerlink(s)
switchport mode trunk
switchport trunk group m1peer
!

interface vlan {{ intf.lag.mlag.vlan }}
interface vlan {{ lag.mlag.vlan }}
description MLAG peerlink local-interface
ip address {{ intf.lag.mlag.self }}
no autostate
!

mlag
domain-id mlag{{ intf.lag.mlag.peergroup }}
local-interface vlan {{ intf.lag.mlag.vlan }}
peer-address {{ intf.lag.mlag.peer }}
peer-link port-channel {{ intf.lag.mlag.ifindex }}
domain-id mlagDomain
local-interface vlan {{ lag.mlag.vlan }}
peer-address {{ lag.mlag.peer }}
peer-link port-channel {{ lag.mlag.ifindex }}
no shutdown
!
{% for ch in ([intf]+interfaces) if ch==intf or ch.lag._peerlink|default(0) == intf.linkindex %}
interface {{ ch.ifname }}
description {{ ch.name }} (peerlink in channel-group {{intf.lag.mlag.ifindex }})
channel-group {{ intf.lag.mlag.ifindex }} mode active
!
{% endfor %}

{% if 'gateway' in module %}
{% if 'gateway' in module %}
ip virtual-router mac-address mlag-peer
{% endif %}
{% endif %}
{% endfor %}

{% for intf in interfaces if intf.type == 'lag' %}
interface {{ intf.ifname }}
Expand Down
4 changes: 2 additions & 2 deletions netsim/ansible/templates/vlan/arubacx.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
interface {{ ifdata.ifname }}
{% if ifdata.vlan.trunk_id is defined %}
no routing
vlan trunk allow {{ ifdata.vlan.trunk_id|sort|join(",") }}
{% if ifdata.vlan.native is defined %}
vlan trunk allow {{ 'all' if '_allow_all' in ifdata.vlan else ifdata.vlan.trunk_id|sort|join(",") }}
{% if ifdata.vlan.native is defined or '_allow_all' in ifdata.vlan %}
vlan trunk native {{ ifdata.vlan.access_id }}
{% elif 1 in ifdata.vlan.trunk_id %}
vlan trunk native 1 tag
Expand Down
5 changes: 3 additions & 2 deletions netsim/ansible/templates/vlan/cumulus_nvue.j2
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{% for i in interfaces if (i.vlan.access_id is defined or i.subif_index is defined) and
i.vlan.mode|default(None) != 'route' and
(i.virtual_interface is not defined or i.type in ["vlan_member","lag"]) %}
{% set _allow_all_vlans = '_allow_all' in i.vlan|default({}) %}
- set:
interface:
{{ i.parent_ifname if i.type=='vlan_member' else i.ifname }}:
Expand All @@ -27,11 +28,11 @@
vlan:
'{{ i.vlan.access_id }}': {}
{% elif i.subif_index is defined %}
{% if i.vlan.access_id is defined %}
{% if i.vlan.access_id is defined and not _allow_all_vlans %}
vlan:
'{{ i.vlan.access_id }}': {}
{% endif %}
untagged: {{ i.vlan.access_id if i.vlan.native is defined else 'none' }}
untagged: {{ i.vlan.access_id|default(1) if i.vlan.native is defined or _allow_all_vlans else 'none' }}
{% else %}
access: {{ i.vlan.access_id }}
{% endif %}
Expand Down
6 changes: 4 additions & 2 deletions netsim/ansible/templates/vlan/dellos10.j2
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ interface vlan {{ vlan.id }}
{% endfor %}
{% endif %}
!
{% for ifdata in interfaces if ifdata.vlan is defined %}
{% for ifdata in interfaces if ifdata.vlan is defined and ifdata.ifname != 'peerlink' %}
!
interface {{ ifdata.ifname }}
{% if ifdata.vlan.trunk_id is defined %}
switchport mode trunk
{% if ifdata.vlan.native is defined or 1 in ifdata.vlan.trunk_id %}
{% if ifdata.vlan.native is defined or 1 in ifdata.vlan.trunk_id or '_allow_all' in ifdata.vlan %}
switchport access vlan {{ ifdata.vlan.access_id|default(1) }}
{% else %}
no switchport access vlan
{% endif %}
{% if '_allow_all' not in ifdata.vlan %}
switchport trunk allowed vlan {{ ifdata.vlan.trunk_id|difference([ifdata.vlan.access_id|default(1)])|join(",") }}
{% endif %}
{% elif ifdata.vlan.access_id is defined %}
switchport mode access
switchport access vlan {{ ifdata.vlan.access_id }}
Expand Down
6 changes: 3 additions & 3 deletions netsim/ansible/templates/vlan/eos.j2
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ interface {{ ifdata.ifname }}
{% if ifdata.vlan.trunk_id is defined %}
switchport
switchport mode trunk
switchport trunk allowed vlan {{ ifdata.vlan.trunk_id|sort|join(",") }}
{% if ifdata.vlan.native is defined %}
switchport trunk native vlan {{ ifdata.vlan.access_id }}
switchport trunk allowed vlan {{ 'all' if '_allow_all' in ifdata.vlan else ifdata.vlan.trunk_id|sort|join(",") }}
{% if ifdata.vlan.native is defined or '_allow_all' in ifdata.vlan %}
switchport trunk native vlan {{ ifdata.vlan.access_id|default(1) }}
{% else %}
switchport trunk native vlan tag
{% endif %}
Expand Down
5 changes: 3 additions & 2 deletions netsim/devices/arubacx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ features:
lag:
mlag:
peer:
ifindex: 255 # Use this port-channel
mac: 0600.0000.0000 # Base to generate a virtual MAC
ifindex: 255 # Use this port-channel
ifname: "lag{ifindex}"
mac: 0600.0000.0000 # Base to generate a virtual MAC
passive: True
mpls:
ldp: true
Expand Down
6 changes: 5 additions & 1 deletion netsim/devices/cumulus_nvue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ group_vars:
ansible_network_os: cumulus_nvue
ansible_connection: paramiko
ansible_python_interpreter: auto_silent
netlab_show_command: [ sudo, vtysh, -c, 'show $@' ]
features:
initial:
system_mtu: True
Expand Down Expand Up @@ -47,7 +48,10 @@ features:
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
vlan_mode: route
ifindex: 4094 # Use this (dummy) ifindex
ifname: peerlink
pool: mlag_linklocal # Use this pool by default, unless overridden
backup_ip: loopback.ipv4 # Use loopback.ipv4 as a backup IP address for peerlink
ospf:
import: [ bgp, connected, vrf ]
Expand Down
8 changes: 5 additions & 3 deletions netsim/devices/dellos10.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ features:
gateway:
protocol: [ anycast, vrrp ]
lag:
reserved_ifindex_range: [ 1000 ]
mlag:
peer:
mac: 0200.01a9.0000 # Base to generate a virtual MAC
ip: loopback.ipv4 # Use loopback.ipv4 for peer IP address
mac: 0200.01a9.0000 # Base to generate a virtual MAC
backup_ip: loopback.ipv4 # Use loopback.ipv4 for peer IP address
ifindex: 1000 # OS10 uses port-channel 1000 for mlag peering
vlan: 1002 # VLAN 4094 is used for VLT peering, but not configurable. 1002 is reserved on IOS
vlan_mode: irb # Routed subintf not supported
passive: True
ospf:
default: True
Expand Down
Loading