diff --git a/docs/caveats.md b/docs/caveats.md index 4d4b727b5..18f09b6c9 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -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 diff --git a/docs/labs/linux.md b/docs/labs/linux.md index b3c0546d1..ee69c5ee5 100644 --- a/docs/labs/linux.md +++ b/docs/labs/linux.md @@ -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. @@ -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. \ No newline at end of file diff --git a/docs/module/lag.md b/docs/module/lag.md index 7e403aec3..09f899d3e 100644 --- a/docs/module/lag.md +++ b/docs/module/lag.md @@ -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 @@ -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. @@ -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) diff --git a/docs/plugins.md b/docs/plugins.md index 4244c8126..10fc72480 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -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 diff --git a/docs/plugins/bonding.md b/docs/plugins/bonding.md new file mode 100644 index 000000000..8a01578e5 --- /dev/null +++ b/docs/plugins/bonding.md @@ -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` + +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 diff --git a/netsim/ansible/templates/initial/frr.j2 b/netsim/ansible/templates/initial/frr.j2 index be79b5a5f..97b2ee13f 100644 --- a/netsim/ansible/templates/initial/frr.j2 +++ b/netsim/ansible/templates/initial/frr.j2 @@ -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' %} @@ -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 %} diff --git a/netsim/ansible/templates/initial/linux/create-bond.j2 b/netsim/ansible/templates/initial/linux/create-bond.j2 new file mode 100644 index 000000000..5480039f8 --- /dev/null +++ b/netsim/ansible/templates/initial/linux/create-bond.j2 @@ -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 -%} diff --git a/netsim/ansible/templates/initial/linux/ubuntu.j2 b/netsim/ansible/templates/initial/linux/ubuntu.j2 index 4b46e4799..e57a046c4 100644 --- a/netsim/ansible/templates/initial/linux/ubuntu.j2 +++ b/netsim/ansible/templates/initial/linux/ubuntu.j2 @@ -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 @@ -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 <