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

Linux: add lag support and bonding plugin for non-lag cases #1624

Merged
merged 68 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5a6d11b
Linux: add lag support (borrowed from frr)
jbemmel Dec 8, 2024
483adf0
Refactored, move lag bond creation to linux.j2
jbemmel Dec 8, 2024
cb3ad9f
Move to MLAG PR
jbemmel Dec 8, 2024
4a61608
Undo changes to image version
jbemmel Dec 8, 2024
46f632a
Move FRR bond logic to generic Linux template
jbemmel Dec 8, 2024
7e09c25
Update docs
jbemmel Dec 10, 2024
0bd66c0
Adjust title
jbemmel Dec 10, 2024
3e47c52
Add iproute2 for good measure (in case we ever change the Ubuntu base…
jbemmel Dec 10, 2024
7fb58f3
Use single brackets that work with 'sh' shell too
jbemmel Dec 10, 2024
eea3d41
Update test case - with a bond should be the same as without, modulo …
jbemmel Dec 10, 2024
ed06e3c
Treat lag links with a VLAN as LAN, not p2p
jbemmel Dec 10, 2024
4dfdff3
In case of active-standby mlag, do create the interface (else link be…
jbemmel Dec 10, 2024
6ab6fa5
lag: Remove active-backup as a valid value
jbemmel Dec 11, 2024
d0bf188
Remove special handling of active-backup in lag module
jbemmel Dec 11, 2024
6090f4e
Replace active-standby lag with bonding plugin
jbemmel Dec 11, 2024
a4e952b
Start of a bonding plugin for Linux active-standby bonds
jbemmel Dec 12, 2024
b169a88
bonding - data model extensions
jbemmel Dec 12, 2024
5386f46
Reuse lag module create_bond_dev macro for bonding plugin
jbemmel Dec 12, 2024
ea87335
For frr, simply defer to linux.j2
jbemmel Dec 12, 2024
d3eaaba
Reformat attributes section
jbemmel Dec 12, 2024
ae95dc1
Fix missing )
jbemmel Dec 12, 2024
9b6a927
bonding: Restore p2p link
jbemmel Dec 12, 2024
b59bfe4
Rename neighbor interfaces
jbemmel Dec 12, 2024
1db36c3
Add lag/bonding param to macro
jbemmel Dec 12, 2024
1e4234c
Set default params={}
jbemmel Dec 12, 2024
0990185
bonding - define default mode
jbemmel Dec 12, 2024
28ff6bb
bonding - set intf down before up
jbemmel Dec 12, 2024
bbde4f6
Include lag changes from latest PR
jbemmel Dec 15, 2024
916b710
Only configure xmit_hash_policy when needed
jbemmel Dec 15, 2024
c24f569
Also include bonding plugin modes
jbemmel Dec 15, 2024
ac589fc
Fix trailing spaces
jbemmel Dec 15, 2024
5d89320
Cleanup, shorten line length
jbemmel Dec 15, 2024
a5569db
Update error
jbemmel Dec 15, 2024
a59adc5
Add bonding priority parameter
jbemmel Dec 15, 2024
8c7a020
Add 'primary' option to select a link as primary
jbemmel Dec 15, 2024
e0139ce
Final touches
jbemmel Dec 15, 2024
127c162
Add docs
jbemmel Dec 15, 2024
db9588e
Fix clab configuration case; don't create bond devices twice
jbemmel Dec 15, 2024
90e4f9b
Undo changes
jbemmel Dec 15, 2024
7eee46a
Install both ethtool and iproute2
jbemmel Dec 15, 2024
fd0481b
Undo merge with LAG PR
jbemmel Dec 15, 2024
a2e4942
Undo merge
jbemmel Dec 15, 2024
cd703d4
Remove duplicate case
jbemmel Dec 15, 2024
5308048
Undo change in ifindex matching, rework test case
jbemmel Dec 15, 2024
5b4cdb7
Set device down before changing bond master
jbemmel Dec 15, 2024
a0918b4
Rename test case
jbemmel Dec 15, 2024
5b20520
Merge with dev branch changes
jbemmel Dec 23, 2024
9572146
Update caveat docs
jbemmel Dec 23, 2024
b8bade9
Run lag script from within ip netns context in case of clab provider
jbemmel Dec 23, 2024
fa3b59a
Fix block syntax, revert to old style lag matching
jbemmel Dec 23, 2024
68fc4bc
Restructure
jbemmel Dec 23, 2024
42a7944
Fix Ubuntu netplan config
jbemmel Dec 23, 2024
5632707
Remove reference to non-existing section
jbemmel Dec 23, 2024
07d1ffd
Updated in response to comments
jbemmel Dec 24, 2024
0fa252f
'interfaces' should be 'neighbors' - clarify
jbemmel Dec 24, 2024
05473f4
Fix English
jbemmel Dec 25, 2024
c122053
* Add 'primary' config on Ubuntu
jbemmel Dec 25, 2024
c2b0af1
Fix typing
jbemmel Dec 25, 2024
0928c4a
Take advantage of netns execution where available
jbemmel Dec 25, 2024
5783a24
Remove endif and indent
jbemmel Dec 25, 2024
3624fa1
Only install ip
jbemmel Dec 25, 2024
8958005
Add caveat
jbemmel Dec 25, 2024
0559587
Make sure to remove any MTU setting from lag interfaces
jbemmel Dec 25, 2024
c89d6d4
Make sure #!/bin/bash line appears at top
jbemmel Dec 25, 2024
9ef8a77
Remove MTU from lag interfaces
jbemmel Dec 25, 2024
a03b3eb
Postpone MTU removal to next PR
jbemmel Dec 25, 2024
484d3bd
Fix indentation, check if IP addresses are strings
jbemmel Dec 25, 2024
4c5f63a
Add caveat about multi-provider labs, addressed in separate PR
jbemmel Dec 25, 2024
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
3 changes: 3 additions & 0 deletions docs/caveats.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ We're not testing Fortinet implementation as part of the regular integration tes
* Junos cannot have more than one loopback interface per routing instance. Using **loopback** links on Junos devices will result in configuration errors.
* Junos configuration template configures BFD timers within routing protocol configuration, not on individual interfaces

## Linux caveats
See [](generic-linux-devices)

(caveats-vptx)=
## Juniper vPTX

Expand Down
3 changes: 2 additions & 1 deletion docs/labs/linux.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(generic-linux-devices)=
# Generic Linux Devices

You can run Linux hosts or routers in virtual machines or containers. The default image used for a Linux virtual machine is Ubuntu 20.04, the default container image is Python 3.9 container running on Alpine Linux.
Expand Down Expand Up @@ -144,7 +145,7 @@ _netlab_ initial configuration script will skip Ubuntu package installation if i
The initial configuration process (**[netlab initial](../netlab/initial.md)**) does not rely on commands executed within Linux containers:

* The `/etc/hosts` file is generated during the **[netlab create](../netlab/create.md)** process from the ```templates/provider/clab/frr/hosts.j2``` template (see [](clab-config-template)).
* Interface IP addresses and static routes to the default gateway (see [](linux-routes)) are configured with **ip** commands executed on the Linux host but within the container network namespace.
* Interface IP addresses, static routes to the default gateway (see [](linux-routes)) and any lag bonding interfaces are configured with **ip** commands executed on the Linux host but within the container network namespace.
* Static default route points to the management interface.

You can, therefore, use any container image as a Linux node.
9 changes: 7 additions & 2 deletions docs/module/lag.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ LAG is currently supported on these platforms:
| Aruba AOS-CX | ✅ | ✅ | ✅ | ✅ |
| Cumulus Linux 4.x | ✅ | ✅ | ❌ | ❌ |
| Cumulus 5.x (NVUE) | ✅ | ✅ | ❌ | ❌ |
| Dell OS10 | ✅ | ✅ | ✅ | ✅ |
| Dell OS10 | ✅ | ✅ | ✅ | ✅ |
| FRR | ✅ | ✅ | ❌ | ❌ |
| Generic Linux hosts | ✅ | ✅ | ❌ | ❌ |

## Parameters

Expand All @@ -32,7 +33,7 @@ The following parameters can be set on individual links:

* **lag.members**: Mandatory list of links that form the LAG. It uses the [same format as the topology **links** list](link-formats).
* **lag.ifindex**: Optional parameter that controls the naming of the LAG (bonding, port-channel) interface.
* **lag.mlag**: Optional Boolean or dict with peer link parameters; see [below](mlag)
* **lag.mlag**: Optional dict with peer link parameters; see [below](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.

Expand Down Expand Up @@ -66,6 +67,10 @@ links:
ifindex: 50
```

### Caveat: Multi-provider Labs

There is a known issue with multi-provider labs, where 'lag' type links get converted into 'lan'; this breaks the lag module

(mlag)=
## Multi-chassis Link Aggregation (MLAG)

Expand Down
1 change: 1 addition & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
plugins/bgp.domain.md
plugins/bgp.session.md
plugins/bgp.policy.md
plugins/bonding.md
plugins/ebgp.multihop.md
plugins/bgp.originate.md
plugins/check.config.md
Expand Down
71 changes: 71 additions & 0 deletions docs/plugins/bonding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
(plugin-bonding)=
# Host-side Link Bonding

Linux networking has long supported *bonding*, the ability to use multiple links simultaneously. Netlab supports bonding with LACP through the *lag* module,
this plugin adds support for the other bonding modes (that don't require any special configuration on peers)

```eval_rst
.. contents:: Table of Contents
:depth: 2
:local:
:backlinks: none
```

## Using the Plugin

* Add `plugin: [ bonding ]` to the lab topology.
* Include the **bonding.ifindex** attribute in any links that need to be bonded

### Supported attributes

The plugin adds the following attributes defined at global, node or interface level:
* **bonding.mode** (string, one of active-backup, balance-tlb, or balance-alb) -- the bonding mode to use, default `active-backup`
jbemmel marked this conversation as resolved.
Show resolved Hide resolved

Additional interface level attributes:
* **bonding.ifindex** (int,mandatory) -- the interface index for the bonding device; links with matching ifindex are bonded together
* **bonding.primary** (bool) -- optional flag to mark this interface as primary, default *False*. If none of the interfaces are marked as `primary`, the selection is left to the Linux default behavior

### Caveats

The plugin uses the `ip` command to create bond devices and add member links; in case of Linux VMs that are not Ubuntu, the plugin attempts to install this command when not available.
This installation uses `apt-get` which may not work on some Linux VMs

## Examples

(active-backup-bonding)=
### Connect a host to a pair of switches using active-backup bonding

```yaml
plugin: [ bonding ]

bonding.mode: active-backup # Default

vlans:
v1:

groups:
_auto_create: True
hosts:
members: [ h1 ]
device: linux
switches:
members: [ s1, s2 ]
module: [ vlan ]

links:
- s1:
s2:
vlan.trunk: [ v1 ]

# Bonded interfaces eth1/eth2
- s1:
h1:
bonding.ifindex: 1
- s2:
h1:
bonding:
ifindex: 1
primary: True # Use this interface as primary
```

Note how there are no bonding specific modules enabled on the switches
4 changes: 2 additions & 2 deletions netsim/ansible/templates/initial/frr.j2
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fi

# Disable IPv6 (for IPv4-only interfaces) or SLAAC (if the device is a router)
#
{% for l in interfaces if l.type in ['lan','p2p','stub'] %}
{% for l in interfaces if l.type in ['lan','p2p','stub','lag','bond'] %}
{% if l.ipv6 is not defined %}
sysctl -qw net.ipv6.conf.{{ l.ifname }}.disable_ipv6=1
{% elif role|default('router') != 'host' %}
Expand All @@ -100,7 +100,7 @@ ip link set {{ l.ifname }} up
echo "service integrated-vtysh-config" >/etc/frr/vtysh.conf
#
# Set Ethernet interface MTU
{% for l in interfaces if l.mtu is defined and l.get('type',"")!='lag' %}
{% for l in interfaces if l.mtu is defined and l.type!='lag' %}
ip link set {{ l.ifname }} mtu {{ l.mtu }}
{% endfor %}

Expand Down
36 changes: 36 additions & 0 deletions netsim/ansible/templates/initial/linux/create-bond.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#
# This macro is used by the initial script to create bond devices
# (which needs to be done early such that IP addresses can be assigned and/or VLAN interfaces created)
#
{% macro create_bond_dev(l,node_provider) %}
{% if l.type in ['lag','bond'] %}
{% set _m = l.lag.mode|default(l.bonding.mode|default("802.3ad")) %}
{% if _m=="802.3ad" %}
{% set _lacp = l.lag.lacp|default('fast') %}
{% set lacp_act = 'off' if _lacp=='off' else 'on' %}
{% set lacp_rate = (' lacp_rate ' + _lacp) if _lacp!='off' else '' %}
{% set _m = _m + lacp_rate %}
{% if node_provider == 'clab' %}
{% set _m = _m + " lacp_active " + lacp_act %}
{% endif %}
{% elif l.bonding.primary is defined %}
{% set _m = _m + " primary " + l.bonding.primary %}
{% endif %}
{% if _m in ["802.3ad","balance-xor","balance-alb","balance-tlb"] %}
{% set _m = _m + " xmit_hash_policy encap3+4" %}
{% endif -%}

{% if node_provider!='clab' %}
#
# Make sure 'bonding' module is loaded
#
if [ ! -e /sys/module/bonding ]; then
modprobe bonding miimon=100 mode=802.3ad lacp_rate=fast
fi
{% endif -%}

if [ ! -e /sys/class/net/{{l.ifname}} ]; then
ip link add dev {{l.ifname}} type bond mode {{ _m }}
fi
{% endif %}
{% endmacro -%}
49 changes: 43 additions & 6 deletions netsim/ansible/templates/initial/linux/ubuntu.j2
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ if systemctl is-active --quiet lldpd.service; then
else
if "$NEED_APT_UPDATE"; then
apt-get update -qq
NEED_APT_UPDATE=
fi
apt-get install -qq lldpd
fi
Expand Down Expand Up @@ -114,29 +115,65 @@ network:
SCRIPT
{% endif %}

# Interface addressing
{% for l in interfaces|default([]) if (l.ipv4 is defined or l.ipv6 is defined or l.dhcp is defined)%}
# Interface addressing and bonds
{% for l in interfaces|default([]) if (l.ipv4 is defined or l.ipv6 is defined or l.dhcp is defined or l.type in ['lag','bond'])%}
cat <<SCRIPT > /etc/netplan/03-eth-{{ l.ifname }}.yaml
network:
version: 2
renderer: networkd
ethernets:
{% if l.type in ['lag','bond'] %}
{% if l.type=='lag' %}
{% for i in interfaces if i.lag._parentindex|default(0)==l.linkindex %}
{{ i.ifname }}:
dhcp4: no
{% endfor %}
{% elif l.bonding.members is defined %}
{% for m in l.bonding.members %}
{{ m }}:
dhcp4: no
{% endfor %}
{% endif %}
bonds:
{{ l.ifname }}:
interfaces:
{% if l.type=='lag' %}
{% for i in interfaces if i.lag._parentindex|default(0)==l.linkindex %}
- {{ i.ifname }}
{% endfor %}
{% elif l.bonding.members is defined %}
{% for m in l.bonding.members %}
- {{ m }}
{% endfor %}
{% endif %}
{% set _m = l.lag.mode|default(l.bonding.mode|default("802.3ad")) %}
parameters:
mode: {{ _m }}
{% if _m=='802.3ad' %}
lacp-rate: {{ l.lag.lacp|default('fast') }}
{% elif l.bonding.primary is defined %}
primary: {{ l.bonding.primary }}
{% endif %}
mii-monitor-interval: 100
transmit-hash-policy: encap3+4
{% else %}
{{ l.ifname }}:
{% if l.mtu is defined %}
mtu: {{ l.mtu }}
{% endif %}
{% endif %}
{% if l.dhcp.client.ipv4|default(False) %}
dhcp4: true
{% endif %}
{% if l.dhcp.client.ipv6|default(False) %}
dhcp6: true
{% endif %}
{% for af in ('ipv4','ipv6') if af in l %}
{% for af in ('ipv4','ipv6') if af in l and l[af] is string %}
{% if loop.first %}
addresses:
{% endif %}
- {{ l[af] }}
{% endfor %}
{% if l.mtu is defined %}
mtu: {{ l.mtu }}
{% endif %}
SCRIPT
{% endfor %}

Expand Down
13 changes: 9 additions & 4 deletions netsim/ansible/templates/initial/linux/vanilla.j2
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{% from "initial/linux/create-bond.j2" import create_bond_dev %}

### One-Shot configuration (non-Ubuntu VM or container)
#
# Disable IPv4 and IPv6 forwarding
Expand All @@ -24,24 +26,27 @@ ip -6 addr add {{ loopback.ipv6 }} dev lo
{% endif %}
{% endif %}
#
# Interface addressing
# Interface addressing, create any bond devices
#
{% for l in interfaces|default([]) %}
{% if l.type in ['lag','bond'] %}
{{ create_bond_dev(l,node_provider) }}
{% endif %}
ip link set dev {{ l.ifname }} up
{% if l.ipv4 is defined %}
{% if l.ipv4 is defined and l.ipv4 is string %}
set +e
ip addr del {{ l.ipv4 }} dev {{ l.ifname }} 2>/dev/null
set -e
ip addr add {{ l.ipv4 }} dev {{ l.ifname }}
{% endif %}
{% if l.ipv6 is defined %}
{% if l.ipv6 is defined and l.ipv6 is string %}
sysctl -w net.ipv6.conf.{{ l.ifname }}.disable_ipv6=0
set +e
ip -6 addr del {{ l.ipv6 }} dev {{ l.ifname }} 2>/dev/null
set -e
ip -6 addr add {{ l.ipv6 }} dev {{ l.ifname }}
{% endif %}
{% if l.mtu is defined %}
{% if l.mtu is defined and l.type != 'lag' %}
ip link set {{ l.ifname }} mtu {{ l.mtu }}
{% endif %}
{% endfor %}
Expand Down
43 changes: 1 addition & 42 deletions netsim/ansible/templates/lag/frr.j2
Original file line number Diff line number Diff line change
@@ -1,42 +1 @@
#!/bin/bash
#
set -e # Exit immediately when any command fails
#
{% if node_provider != 'clab' %}
modprobe bonding miimon=100 mode=802.3ad lacp_rate=fast
{% endif %}
#
# Create bonds for LAGs, if any. Requires kernel bonding module loaded
#
{% for l in interfaces if 'lag' in l %}
{% if l.type=='lag' %}
{% set _m = l.lag.mode|default(lag.mode) %}
{% if _m=="802.3ad" %}
{% set _lacp = l.lag.lacp|default(lag.lacp) %}
{% set lacp_act = 'off' if _lacp=='off' else 'on' %}
{% set lacp_rate = (' lacp_rate ' + _lacp) if _lacp!='off' else '' %}
{% set _m = _m + " xmit_hash_policy encap3+4" + lacp_rate %}
{% if node_provider == 'clab' %}
{% set _m = _m + " lacp_active " + lacp_act %}
{% endif %}
{% endif %}
ip link add dev {{l.ifname}} type bond mode {{_m}}
{% endif %}
ip link set dev {{ l.ifname }} down
{% endfor %}

{% for l in interfaces if 'lag' in l and l.type != 'lag' %}
{% if l.type=='p2p' %}
{% if node_provider != 'clab' %}
ethtool -s {{ l.ifname }} autoneg off speed 1000 duplex full
{% endif %}
ip link set dev {{ l.ifname }} master {%
for i in interfaces if i.type=='lag' and i.linkindex==l.lag._parentindex %}{{ i.ifname }}
{% endfor %}
{% endif %}
ip link set dev {{ l.ifname }} up
{% endfor %}
{% for l in interfaces if 'lag' in l and l.type == 'lag' %}
ip link set dev {{ l.ifname }} up
{% endfor %}
exit 0
{% include "linux.j2" %}
5 changes: 5 additions & 0 deletions netsim/ansible/templates/lag/linux-clab.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% include "linux.j2" %}

#
# Note: The above script is executed within the netns context on the host
#
24 changes: 24 additions & 0 deletions netsim/ansible/templates/lag/linux.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{% if node_provider=='clab' or netlab_linux_distro|default("") != "ubuntu" %}
#!/bin/bash
#
set -e # Exit immediately when any command fails
#
# Bond devices are created by 'initial' module - add members
#
{% for l in interfaces if 'lag' in l and l.type not in ['lag','bond'] %}
{% if l.type=='p2p' %}
{% if node_provider!='clab' %}
ethtool -s {{ l.ifname }} autoneg off speed 1000 duplex full
{% endif %}
ip link set dev {{ l.ifname }} down
ip link set dev {{ l.ifname }} master {%
for i in interfaces if i.type=='lag' and i.linkindex==l.lag._parentindex %}{{ i.ifname }}
{% endfor %}
{% endif %}
ip link set dev {{ l.ifname }} up
{% endfor %}
{% for l in interfaces if 'lag' in l and l.type in ['lag','bond'] %}
ip link set dev {{ l.ifname }} up
{% endfor %}
exit 0
{% endif %}
1 change: 1 addition & 0 deletions netsim/devices/linux.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
description: Generic Linux host
interface_name: eth{ifindex}
lag_interface_name: "bond{lag.ifindex}"
mgmt_if: eth0
role: host
features:
Expand Down
Loading
Loading