From a44affc73b7a15838e6c2e56cd3a3dd6f0b83df9 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 8 Dec 2024 10:01:16 -0600 Subject: [PATCH 01/46] Linux: add lag support (borrowed from frr) --- netsim/ansible/templates/initial/linux/ubuntu.j2 | 11 +++++++++++ netsim/devices/__init__.py | 2 +- netsim/devices/cumulus_nvue.py | 2 +- netsim/devices/dellos10.py | 4 ++-- netsim/devices/linux.yml | 9 ++++++--- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/netsim/ansible/templates/initial/linux/ubuntu.j2 b/netsim/ansible/templates/initial/linux/ubuntu.j2 index 1fa65faeb..422d92f51 100644 --- a/netsim/ansible/templates/initial/linux/ubuntu.j2 +++ b/netsim/ansible/templates/initial/linux/ubuntu.j2 @@ -64,10 +64,21 @@ 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 +{% if 'lag' in module|default([]) %} +# +# Install ethtool for lag support +# +if "$NEED_APT_UPDATE"; then + apt-get update -qq +fi +apt-get install -qq ethtool +{% endif %} + cat </etc/lldpd.d/system.conf configure lldp tx-interval 30 configure lldp tx-hold 3 diff --git a/netsim/devices/__init__.py b/netsim/devices/__init__.py index 947ba45bb..a2e6a9fd7 100644 --- a/netsim/devices/__init__.py +++ b/netsim/devices/__init__.py @@ -153,7 +153,7 @@ def report_quirk(text: str, node: Box, quirk: str, **kwargs: typing.Any) -> None q_state = topology.get(q_path,True) if not q_state: return - + # Add the 'this is how you disable this quirk' hint # q_hint = f'Set {q_path} to False to disable this check' diff --git a/netsim/devices/cumulus_nvue.py b/netsim/devices/cumulus_nvue.py index 0790e36b4..91ef60af6 100644 --- a/netsim/devices/cumulus_nvue.py +++ b/netsim/devices/cumulus_nvue.py @@ -73,7 +73,7 @@ def device_quirks(self, node: Box, topology: Box) -> None: # NVUE specific quirks if 'stp' in mods: nvue_check_stp_features(node,topology) - + if 'vrf' in mods: nvue_check_vrf_route_leaking(node) diff --git a/netsim/devices/dellos10.py b/netsim/devices/dellos10.py index 5887b8dc9..20f71abee 100644 --- a/netsim/devices/dellos10.py +++ b/netsim/devices/dellos10.py @@ -36,7 +36,7 @@ def check_anycast_gateways(node: Box) -> None: for intf in node.get('interfaces',[]): if intf.type != 'svi' and intf.get('gateway.anycast',None): err_data.append(f'Interface {intf.ifname}') - + if err_data: report_quirk( f'Dell OS10 (node {node.name}) does not support anycast on non-SVI interfaces', @@ -51,7 +51,7 @@ def device_quirks(self, node: Box, topology: Box) -> None: check_vlan_ospf(node,node.interfaces,'default') for vname,vdata in node.get('vrfs',{}).items(): check_vlan_ospf(node,vdata.get('ospf.interfaces',[]),vname) - + if 'gateway' in node.get('module',[]) and 'anycast' in node.get('gateway',{}): check_anycast_gateways(node) diff --git a/netsim/devices/linux.yml b/netsim/devices/linux.yml index 921dd459b..e1786e80d 100644 --- a/netsim/devices/linux.yml +++ b/netsim/devices/linux.yml @@ -1,5 +1,6 @@ description: Generic Linux host interface_name: eth{ifindex} +lag_interface_name: "bond{lag.ifindex}" mgmt_if: eth0 role: host features: @@ -9,12 +10,14 @@ features: ipv6: true server: true relay: true + lag: + passive: False libvirt: - image: generic/ubuntu2004 + image: debian/bookworm64 # generic/ubuntu2004 group_vars: netlab_linux_distro: ubuntu virtualbox: - image: generic/ubuntu2004 + image: debian/bookworm64 # generic/ubuntu2004 group_vars: netlab_linux_distro: ubuntu group_vars: @@ -27,7 +30,7 @@ group_vars: netlab_lldp_enable: False netlab_net_tools: False clab: - image: python:3.9-alpine + image: python:3.11-alpine # Matches Python version in debian/bookworm64 mtu: 1500 kmods: node: From 352c0d2a941df673899d888f618c644d8c91cc70 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 8 Dec 2024 11:36:36 -0600 Subject: [PATCH 02/46] Refactored, move lag bond creation to linux.j2 --- .../templates/initial/linux/vanilla.j2 | 9 +++- netsim/ansible/templates/lag/frr.j2 | 43 +------------------ tests/integration/lag/04-host-mlag.yml | 12 +++--- .../lag/04b-host-lag-active-standby.yml | 42 ++++++++++++++++++ 4 files changed, 56 insertions(+), 50 deletions(-) create mode 100644 tests/integration/lag/04b-host-lag-active-standby.yml diff --git a/netsim/ansible/templates/initial/linux/vanilla.j2 b/netsim/ansible/templates/initial/linux/vanilla.j2 index 8d548c825..6e37698e7 100644 --- a/netsim/ansible/templates/initial/linux/vanilla.j2 +++ b/netsim/ansible/templates/initial/linux/vanilla.j2 @@ -1,3 +1,5 @@ +{% from "lag/linux.j2" import create_bond_dev with context %} + ### One-Shot configuration (non-Ubuntu VM or container) # # Disable IPv4 and IPv6 forwarding @@ -24,9 +26,12 @@ 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=='lag' %} +{{ create_bond_dev(l) }} +{% endif %} ip link set dev {{ l.ifname }} up {% if l.ipv4 is defined %} set +e @@ -41,7 +46,7 @@ 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 %} diff --git a/netsim/ansible/templates/lag/frr.j2 b/netsim/ansible/templates/lag/frr.j2 index d4db8772c..0d93aebe7 100644 --- a/netsim/ansible/templates/lag/frr.j2 +++ b/netsim/ansible/templates/lag/frr.j2 @@ -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" %} \ No newline at end of file diff --git a/tests/integration/lag/04-host-mlag.yml b/tests/integration/lag/04-host-mlag.yml index af19232ef..b1c2b97b6 100644 --- a/tests/integration/lag/04-host-mlag.yml +++ b/tests/integration/lag/04-host-mlag.yml @@ -1,5 +1,5 @@ message: | - The device under is a pair of switches with a pair of L3 MC-LAG links connected to 2 Linux hosts. + The device under test is a pair of switches with a pair of L3 MC-LAG links connected to 2 Linux hosts. The hosts should be able to ping each other and their gateway groups: @@ -8,9 +8,9 @@ groups: members: [s1, s2] module: [lag, vlan] hosts: - members: [h1, h2] - module: [lag] # Host side must support lag to present single MAC on both interfaces - device: frr # linux does not support 'lag' module yet + members: [ h1, h2 ] + module: [ lag ] # Host side must support lag to present single MAC on both interfaces + device: linux vlans: red: @@ -35,8 +35,8 @@ validate: nodes: [h1] wait_msg: Waiting for STP to enable the ports wait: 45 - plugin: ping(nodes.h2.interfaces[0].ipv4,af='ipv4') + plugin: ping('h2') ping_gw: description: Pinging gateway from H1 nodes: [h1] - plugin: ping(nodes.s1.interfaces[5].ipv4,af='ipv4') + plugin: ping(nodes.s1.interfaces[-1].ipv4,af='ipv4') diff --git a/tests/integration/lag/04b-host-lag-active-standby.yml b/tests/integration/lag/04b-host-lag-active-standby.yml new file mode 100644 index 000000000..524865656 --- /dev/null +++ b/tests/integration/lag/04b-host-lag-active-standby.yml @@ -0,0 +1,42 @@ +--- +message: | + The device under test is a pair of switches with a pair of links connected to 2 Linux hosts. + The Linux hosts are using active-standby lags, which don't require any special support from the switches + + The hosts should be able to ping each other and their gateway + +lag.mode: active-backup + +groups: + _auto_create: True + switches: + members: [ s1, s2 ] + module: [ vlan ] # No 'lag' module support enabled + hosts: + members: [ h1, h2 ] + module: [ lag ] # Host side uses lag to implement active/standby bond + device: linux + +vlans: + red: + +links: +- lag: + members: [ h1-s1, h1-s2 ] + vlan.access: red +- lag: + members: [ h2-s1, h2-s2 ] + vlan.access: red + +validate: + ping: + description: Pinging H2 from H1 + nodes: [ h1 ] + wait_msg: Waiting for STP to enable the ports + wait: 45 + plugin: ping(nodes.h2.interfaces[0].ipv4,af='ipv4') + ping_gw: + description: Pinging gateway from H1 + nodes: [ h1 ] + plugin: ping(nodes.s1.interfaces[5].ipv4,af='ipv4') + From a64fb60ef07131722b469bf9eab598b8466d09f7 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 8 Dec 2024 13:02:49 -0600 Subject: [PATCH 03/46] Move to MLAG PR --- .../lag/04b-host-lag-active-standby.yml | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 tests/integration/lag/04b-host-lag-active-standby.yml diff --git a/tests/integration/lag/04b-host-lag-active-standby.yml b/tests/integration/lag/04b-host-lag-active-standby.yml deleted file mode 100644 index 524865656..000000000 --- a/tests/integration/lag/04b-host-lag-active-standby.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- -message: | - The device under test is a pair of switches with a pair of links connected to 2 Linux hosts. - The Linux hosts are using active-standby lags, which don't require any special support from the switches - - The hosts should be able to ping each other and their gateway - -lag.mode: active-backup - -groups: - _auto_create: True - switches: - members: [ s1, s2 ] - module: [ vlan ] # No 'lag' module support enabled - hosts: - members: [ h1, h2 ] - module: [ lag ] # Host side uses lag to implement active/standby bond - device: linux - -vlans: - red: - -links: -- lag: - members: [ h1-s1, h1-s2 ] - vlan.access: red -- lag: - members: [ h2-s1, h2-s2 ] - vlan.access: red - -validate: - ping: - description: Pinging H2 from H1 - nodes: [ h1 ] - wait_msg: Waiting for STP to enable the ports - wait: 45 - plugin: ping(nodes.h2.interfaces[0].ipv4,af='ipv4') - ping_gw: - description: Pinging gateway from H1 - nodes: [ h1 ] - plugin: ping(nodes.s1.interfaces[5].ipv4,af='ipv4') - From 1bf6110cd95f5f71c19ba7d4a5cdf844ce5e6a7d Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 8 Dec 2024 13:07:21 -0600 Subject: [PATCH 04/46] Undo changes to image version --- netsim/devices/linux.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netsim/devices/linux.yml b/netsim/devices/linux.yml index e1786e80d..00768186d 100644 --- a/netsim/devices/linux.yml +++ b/netsim/devices/linux.yml @@ -13,11 +13,11 @@ features: lag: passive: False libvirt: - image: debian/bookworm64 # generic/ubuntu2004 + image: generic/ubuntu2004 group_vars: netlab_linux_distro: ubuntu virtualbox: - image: debian/bookworm64 # generic/ubuntu2004 + image: generic/ubuntu2004 group_vars: netlab_linux_distro: ubuntu group_vars: @@ -30,7 +30,7 @@ group_vars: netlab_lldp_enable: False netlab_net_tools: False clab: - image: python:3.11-alpine # Matches Python version in debian/bookworm64 + image: python:3.9-alpine mtu: 1500 kmods: node: From 34f52f58cc1f3620071c8f88effdd0fb2c20225e Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 8 Dec 2024 16:38:38 -0600 Subject: [PATCH 05/46] Move FRR bond logic to generic Linux template --- netsim/ansible/templates/lag/linux.j2 | 51 +++++++++++++++++++++++++++ netsim/devices/__init__.py | 2 +- netsim/devices/cumulus_nvue.py | 2 +- netsim/devices/dellos10.py | 4 +-- 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 netsim/ansible/templates/lag/linux.j2 diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 new file mode 100644 index 000000000..43cfc61e1 --- /dev/null +++ b/netsim/ansible/templates/lag/linux.j2 @@ -0,0 +1,51 @@ +{% macro create_bond_dev(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 %} +{% 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 -%} +#!/bin/bash +# +set -e # Exit immediately when any command fails +# +# Create bonds for LAGs, if any. Requires kernel bonding module loaded +# +{% for l in interfaces if 'lag' in l %} +{{ create_bond_dev(l) }} +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 diff --git a/netsim/devices/__init__.py b/netsim/devices/__init__.py index a2e6a9fd7..947ba45bb 100644 --- a/netsim/devices/__init__.py +++ b/netsim/devices/__init__.py @@ -153,7 +153,7 @@ def report_quirk(text: str, node: Box, quirk: str, **kwargs: typing.Any) -> None q_state = topology.get(q_path,True) if not q_state: return - + # Add the 'this is how you disable this quirk' hint # q_hint = f'Set {q_path} to False to disable this check' diff --git a/netsim/devices/cumulus_nvue.py b/netsim/devices/cumulus_nvue.py index 91ef60af6..0790e36b4 100644 --- a/netsim/devices/cumulus_nvue.py +++ b/netsim/devices/cumulus_nvue.py @@ -73,7 +73,7 @@ def device_quirks(self, node: Box, topology: Box) -> None: # NVUE specific quirks if 'stp' in mods: nvue_check_stp_features(node,topology) - + if 'vrf' in mods: nvue_check_vrf_route_leaking(node) diff --git a/netsim/devices/dellos10.py b/netsim/devices/dellos10.py index 20f71abee..5887b8dc9 100644 --- a/netsim/devices/dellos10.py +++ b/netsim/devices/dellos10.py @@ -36,7 +36,7 @@ def check_anycast_gateways(node: Box) -> None: for intf in node.get('interfaces',[]): if intf.type != 'svi' and intf.get('gateway.anycast',None): err_data.append(f'Interface {intf.ifname}') - + if err_data: report_quirk( f'Dell OS10 (node {node.name}) does not support anycast on non-SVI interfaces', @@ -51,7 +51,7 @@ def device_quirks(self, node: Box, topology: Box) -> None: check_vlan_ospf(node,node.interfaces,'default') for vname,vdata in node.get('vrfs',{}).items(): check_vlan_ospf(node,vdata.get('ospf.interfaces',[]),vname) - + if 'gateway' in node.get('module',[]) and 'anycast' in node.get('gateway',{}): check_anycast_gateways(node) From 7910184b26f1f0dde312aa08dd9a97cf3bb66b8a Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Tue, 10 Dec 2024 11:44:53 -0600 Subject: [PATCH 06/46] Update docs --- docs/caveats.md | 4 ++++ docs/module/lag.md | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/caveats.md b/docs/caveats.md index 5ddf5bc82..2b280aa42 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -280,6 +280,10 @@ 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 +(caveats-linux)= +## lag module caveats +* The lag module requires the `ip` command to be available on the Linux host + (caveats-vptx)= ## Juniper vPTX diff --git a/docs/module/lag.md b/docs/module/lag.md index 3eaa021d0..618858ba4 100644 --- a/docs/module/lag.md +++ b/docs/module/lag.md @@ -13,6 +13,7 @@ LAG is currently supported on these platforms: | Cumulus 5.x (NVUE) | ✅ | ✅ | ❌ | ❌ | | Dell OS10 | ✅ | ✅ | ✅ | ✅ | | FRR | ✅ | ✅ | ❌ | ❌ | +| Generic Linux hosts [❗](caveats-linux) | ✅ | ✅ | ❌ | ❌ | ## Parameters @@ -31,7 +32,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. From 0a84671c6385b624869b4cacfa10200dcd379c0a Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Tue, 10 Dec 2024 11:47:07 -0600 Subject: [PATCH 07/46] Adjust title --- docs/caveats.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/caveats.md b/docs/caveats.md index 2b280aa42..fd1c87d60 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -281,7 +281,7 @@ We're not testing Fortinet implementation as part of the regular integration tes * Junos configuration template configures BFD timers within routing protocol configuration, not on individual interfaces (caveats-linux)= -## lag module caveats +## Generic Linux lag module caveats * The lag module requires the `ip` command to be available on the Linux host (caveats-vptx)= From 89266565f99e2f2d0f1769dc6d3b91a0e485f8f5 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Tue, 10 Dec 2024 11:50:05 -0600 Subject: [PATCH 08/46] Add iproute2 for good measure (in case we ever change the Ubuntu base image) --- netsim/ansible/templates/initial/linux/ubuntu.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netsim/ansible/templates/initial/linux/ubuntu.j2 b/netsim/ansible/templates/initial/linux/ubuntu.j2 index 422d92f51..41bf7a0ea 100644 --- a/netsim/ansible/templates/initial/linux/ubuntu.j2 +++ b/netsim/ansible/templates/initial/linux/ubuntu.j2 @@ -71,12 +71,12 @@ fi {% if 'lag' in module|default([]) %} # -# Install ethtool for lag support +# Install ethtool and iproute2(ip) for lag support # if "$NEED_APT_UPDATE"; then apt-get update -qq fi -apt-get install -qq ethtool +apt-get install -qq ethtool iproute2 {% endif %} cat </etc/lldpd.d/system.conf From b100be6579e4b4e9480ebcdaf1ee5c1ba024fa01 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Tue, 10 Dec 2024 11:58:51 -0600 Subject: [PATCH 09/46] Use single brackets that work with 'sh' shell too --- netsim/ansible/templates/lag/linux.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index 43cfc61e1..2ea6a008b 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -14,11 +14,11 @@ # # Make sure 'bonding' module is loaded # -if [[ ! -e /sys/module/bonding ]]; then +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 +if [ ! -e /sys/class/net/{{l.ifname}} ]; then ip link add dev {{l.ifname}} type bond mode {{_m}} fi {% endif %} From eebe51638f803baca3ff1aa65c734451c26c67b2 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Tue, 10 Dec 2024 12:19:54 -0600 Subject: [PATCH 10/46] Update test case - with a bond should be the same as without, modulo the device bond1 instead of eth1 IP allocation doesn't work the same - possibly some dependency on link.type --- tests/integration/lag/11-host-lag-active-standby.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/integration/lag/11-host-lag-active-standby.yml b/tests/integration/lag/11-host-lag-active-standby.yml index 84ea261d9..c93a93d04 100644 --- a/tests/integration/lag/11-host-lag-active-standby.yml +++ b/tests/integration/lag/11-host-lag-active-standby.yml @@ -4,7 +4,7 @@ message: | The hosts should be able to ping each other and their gateway -lag.mode: active-backup +plugin: [bonding] groups: _auto_create: true @@ -13,13 +13,17 @@ groups: module: [vlan] # No 'lag' module support enabled hosts: members: [h1, h2] - module: [lag] # Host side uses lag to implement active/standby bond device: linux vlans: red: + mode: bridge + # links: [h1-s1,h2-s2] # Case with bond should be the same as without links: +- s1: + s2: + vlan.trunk: [red] - lag: members: [h1-s1, h1-s2] vlan.access: red @@ -33,8 +37,8 @@ validate: nodes: [h1] wait_msg: Waiting for STP to enable the ports wait: 45 - plugin: ping(nodes.h2.interfaces[0].ipv4,af='ipv4') + plugin: ping('h1',af='ipv4') ping_gw: description: Pinging gateway from H1 nodes: [h1] - plugin: ping(nodes.s1.interfaces[5].ipv4,af='ipv4') + plugin: ping(nodes.s1.interfaces[-1].ipv4,af='ipv4') From 0858461fdf11bdabe30c0f9f2fd47a46fed064cf Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Tue, 10 Dec 2024 12:29:39 -0600 Subject: [PATCH 11/46] Treat lag links with a VLAN as LAN, not p2p ( this is an example why Ivan doesn't like link.type=='lag'... ) --- netsim/augment/links.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netsim/augment/links.py b/netsim/augment/links.py index 9eb992aa8..c590afa23 100644 --- a/netsim/augment/links.py +++ b/netsim/augment/links.py @@ -1168,7 +1168,8 @@ def transform(link_list: typing.Optional[Box], defaults: Box, nodes: Box, pools: continue set_link_bridge_name(link,defaults) - link_default_pools = ['p2p','lan'] if link.type in ['p2p','lag'] else ['lan'] + link_default_pools = ['p2p','lan'] if link.type=='p2p' or ( + link.type=='lag' and not 'vlan' in link) else ['lan'] assign_link_prefix(link,link_default_pools,pools,nodes,link._linkname) copy_link_gateway(link,nodes) assign_interface_addresses(link,pools,nodes,defaults) From f55c5601b8330d7877eeb792f4d494f42342233b Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Tue, 10 Dec 2024 13:13:43 -0600 Subject: [PATCH 12/46] In case of active-standby mlag, do create the interface (else link becomes stub). Some cleanup remains required --- netsim/modules/lag.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/netsim/modules/lag.py b/netsim/modules/lag.py index 73f8e033e..6799f1511 100644 --- a/netsim/modules/lag.py +++ b/netsim/modules/lag.py @@ -224,10 +224,12 @@ def analyze_lag(members: list, node_count: dict) -> tuple[bool,bool,str]: elif is_mlag: lag_mode = l.get('lag.mode',topology.get('lag.mode',"802.3ad")) if lag_mode == "active-backup": # Exception: active-backup lag to 2 nodes - continue # Skip adding the lag interface on M-side - if not check_mlag_support(node,l._linkname,topology): - return - ifatts.lag._mlag = True # Set internal flag + ifatts.pop('lag',None) # Remove the lag interface on M-side + ifatts.type = 'lan' # Convert into regular LAN interface + else: + if not check_mlag_support(node,l._linkname,topology): + return + ifatts.lag._mlag = True # Set internal flag if log.debug_active('lag'): print(f'LAG create_lag_member_links for node {node} -> collected ifatts {ifatts}') From 0b807818a7b5cdc39f583e0c7a0e19310e9e7bb4 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 17:27:21 -0600 Subject: [PATCH 13/46] lag: Remove active-backup as a valid value Moved to a new 'bonding' plugin as it is not link aggregation --- netsim/modules/lag.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netsim/modules/lag.yml b/netsim/modules/lag.yml index e6f295544..c093a6b5a 100644 --- a/netsim/modules/lag.yml +++ b/netsim/modules/lag.yml @@ -12,11 +12,11 @@ attributes: global: lacp: { type: str, valid_values: [ "off", "slow", "fast" ] } lacp_mode: { type: str, valid_values: [ "passive", "active" ] } - mode: { type: str, valid_values: [ "802.3ad", "balance-xor", "active-backup" ] } + mode: { type: str, valid_values: [ "802.3ad", "balance-xor" ] } node: lacp: lacp_mode: - mode: { type: str, valid_values: [ "802.3ad", "balance-xor", "active-backup" ] } + mode: { copy: global } link: # Most should be consistent across both interfaces on the link lacp: { copy: global } lacp_mode: { copy: global } @@ -24,7 +24,7 @@ attributes: # Optional, to control naming of the bonding interface ifindex: { type: int, min_value: 0, max_value: 10000 } members: - mode: { type: str, valid_values: [ "802.3ad", "balance-xor", "active-backup" ] } + mode: { copy: global } mlag: peergroup: # Optional, for MLAG interconnect links to peers type: int From f9dc7943cbe5ddb6116d0b08e7eff509556408ed Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 17:29:17 -0600 Subject: [PATCH 14/46] Remove special handling of active-backup in lag module --- netsim/modules/lag.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/netsim/modules/lag.py b/netsim/modules/lag.py index 6799f1511..7bf7f894e 100644 --- a/netsim/modules/lag.py +++ b/netsim/modules/lag.py @@ -222,14 +222,9 @@ def analyze_lag(members: list, node_count: dict) -> tuple[bool,bool,str]: if not check_lag_config(node,l._linkname,topology): return elif is_mlag: - lag_mode = l.get('lag.mode',topology.get('lag.mode',"802.3ad")) - if lag_mode == "active-backup": # Exception: active-backup lag to 2 nodes - ifatts.pop('lag',None) # Remove the lag interface on M-side - ifatts.type = 'lan' # Convert into regular LAN interface - else: - if not check_mlag_support(node,l._linkname,topology): - return - ifatts.lag._mlag = True # Set internal flag + if not check_mlag_support(node,l._linkname,topology): + return + ifatts.lag._mlag = True # Set internal flag if log.debug_active('lag'): print(f'LAG create_lag_member_links for node {node} -> collected ifatts {ifatts}') From 5fd5c332091af8e37e208515f79f8e7f9946eadf Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 17:34:49 -0600 Subject: [PATCH 15/46] Replace active-standby lag with bonding plugin --- .../lag/11-host-lag-active-standby.yml | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/tests/integration/lag/11-host-lag-active-standby.yml b/tests/integration/lag/11-host-lag-active-standby.yml index c93a93d04..c6d5a4493 100644 --- a/tests/integration/lag/11-host-lag-active-standby.yml +++ b/tests/integration/lag/11-host-lag-active-standby.yml @@ -1,34 +1,46 @@ message: | The device under test is a pair of switches with a pair of links connected to 2 Linux hosts. - The Linux hosts are using active-standby lags, which don't require any special support from the switches + The Linux hosts are using active-standby bonding, which doesn't require any special support from the switches + (and technically isn't "link aggregation") - The hosts should be able to ping each other and their gateway + The hosts should be able to ping each other -plugin: [bonding] +plugin: [ bonding ] # To be created + +bonding.mode: active-backup groups: _auto_create: true switches: - members: [s1, s2] + members: [ s1, s2 ] module: [vlan] # No 'lag' module support enabled hosts: - members: [h1, h2] + members: [ h1, h2 ] device: linux vlans: red: mode: bridge - # links: [h1-s1,h2-s2] # Case with bond should be the same as without links: - s1: s2: vlan.trunk: [red] -- lag: - members: [h1-s1, h1-s2] +- h1: + bonding.ifindex: 1 + s1: + vlan.access: red +- h1: + bonding.ifindex: 1 + s2: vlan.access: red -- lag: - members: [h2-s1, h2-s2] +- h2: + bonding.ifindex: 1 + s1: + vlan.access: red +- h2: + bonding.ifindex: 1 + s2: vlan.access: red validate: From 9f819d86a8916fecb2adc09f65450de39021d12e Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 19:01:25 -0600 Subject: [PATCH 16/46] Start of a bonding plugin for Linux active-standby bonds --- netsim/extra/bonding/plugin.py | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 netsim/extra/bonding/plugin.py diff --git a/netsim/extra/bonding/plugin.py b/netsim/extra/bonding/plugin.py new file mode 100644 index 000000000..24b4c280d --- /dev/null +++ b/netsim/extra/bonding/plugin.py @@ -0,0 +1,68 @@ +import typing +from box import Box +from netsim.utils import log +from netsim import api,data +from netsim.augment import devices + +_config_name = 'bonding' + +''' +add_bond_interfaces - append interface data to node.interfaces for bonding template to read and implement +''' +def add_bond_interfaces(node: Box, bonds: dict[int,list[Box]]) -> None: + last_linkindex = node.interfaces[-1].linkindex + for c,(ifindex,members) in enumerate(bonds.items()): + bond_if = { + 'linkindex': last_linkindex + 1 + c, + 'type': 'bond', + 'ifname': f'bond{ifindex}', # XXX hardcoded, could make this a device template attribute + 'name': f'bond {ifindex}', + 'bonding': { 'ifindex': ifindex, 'members': [ m.ifname for m in members ] }, + 'ifindex': 50000 + c, + 'virtual_interface': True + } + c = c + 1 + vlans = [ m.vlan for m in members if 'vlan' in m ] + if vlans: + bond_if.vlan = vlans[0] # Take the first VLAN, hopefully consistent + else: + for af in ['ipv4','ipv6']: + ips = [ m[af] for m in members if af in m ] + if ips: + bond_if[af] = ips[0] # Take the first one, if any + node.interfaces.append(bond_if) + +''' +post_transform hook + +Apply plugin config to nodes with interfaces marked with 'bonding.ifindex', for devices that support this plugin +''' +def post_transform(topology: Box) -> None: + global _config_name + for node in topology.nodes.values(): + features = devices.get_device_features(node,topology.defaults) + if 'bonding' in features: + bonds : dict[int,list[Box]] = {} + for intf in node.get('interfaces',[]): + bond_ifindex = intf.get('bonding.ifindex',None) + if not bond_ifindex: + continue + + if 'virtual_interface' in intf or len(intf.get('neighbors',[])!=1: + log.error( f"{intf.name}: 'bonding.ifindex' can only be applied to interfaces on physical p2p links", + category=log.IncorrectAttr,module=_config_name) + continue + + clone = data.get_box(intf) + if bond_ifindex in bonds: + bonds[ bond_ifindex ].append( clone ) + else: + bonds[ bond_ifindex ] = [ clone ] + + for att in ['ipv4','ipv6','vlan']: # Remove any ips or vlan + intf.pop(att,None) + intf.prefix = False # L2 interface + + if bonds: + add_bond_interfaces(node,bonds) + api.node_config(node,_config_name) # Remember that we have to do extra configuration From 95f8d2d00159d4540aba0ff4e6b72b46c7385911 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 19:14:15 -0600 Subject: [PATCH 17/46] bonding - data model extensions Supports the Linux bonding modes that work with autonomous ports --- netsim/extra/bonding/defaults.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 netsim/extra/bonding/defaults.yml diff --git a/netsim/extra/bonding/defaults.yml b/netsim/extra/bonding/defaults.yml new file mode 100644 index 000000000..c5b94cc69 --- /dev/null +++ b/netsim/extra/bonding/defaults.yml @@ -0,0 +1,18 @@ +# bonding plugin attributes +# +--- +devices: + linux: + features.bonding: True + frr: + features.bonding: True + +bonding: + attributes: + global: + mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } # Linux bonding modes that use autonomous ports + node: + mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } + interface: + ifindex: { type: int, min_value: 0, _required: True } + mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } From 732e3b962434452515779389debcd539df43f855 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 19:28:14 -0600 Subject: [PATCH 18/46] Reuse lag module create_bond_dev macro for bonding plugin --- netsim/extra/bonding/linux.j2 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 netsim/extra/bonding/linux.j2 diff --git a/netsim/extra/bonding/linux.j2 b/netsim/extra/bonding/linux.j2 new file mode 100644 index 000000000..f8a6884d8 --- /dev/null +++ b/netsim/extra/bonding/linux.j2 @@ -0,0 +1,14 @@ +{% from "templates/lag/linux.j2" import create_bond_dev with context %} +#!/bin/bash + +set -e + +{% for intf in interfaces|default([]) if intf.type=='bond' %} +{% set mode = intf.bonding.mode|default('active-backup') %} +{{ create_bond_dev(intf|combine( {'type':'lag','lag': { 'mode': mode, 'lacp': 'off' } }) ) }} +ip link set dev {{ intf.ifname }} down +{% for member in intf.members %} +ip link set dev {{ member }} master {{ intf.ifname }} +{% endfor %} +ip link set dev {{ intf.ifname }} up +{% endfor %} From becb920b02bb94dce2f9a71c1d21da69e82d5924 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 19:29:30 -0600 Subject: [PATCH 19/46] For frr, simply defer to linux.j2 --- netsim/extra/bonding/frr.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 netsim/extra/bonding/frr.j2 diff --git a/netsim/extra/bonding/frr.j2 b/netsim/extra/bonding/frr.j2 new file mode 100644 index 000000000..571230c3f --- /dev/null +++ b/netsim/extra/bonding/frr.j2 @@ -0,0 +1 @@ +{% include linux.j2 %} From 5c3fb6978604db559318f7593cdfac71684dd30f Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 19:34:00 -0600 Subject: [PATCH 20/46] Reformat attributes section --- netsim/extra/bonding/defaults.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/netsim/extra/bonding/defaults.yml b/netsim/extra/bonding/defaults.yml index c5b94cc69..e8d9fc313 100644 --- a/netsim/extra/bonding/defaults.yml +++ b/netsim/extra/bonding/defaults.yml @@ -7,12 +7,14 @@ devices: frr: features.bonding: True -bonding: - attributes: - global: +attributes: + global: + bonding: mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } # Linux bonding modes that use autonomous ports - node: + node: + bonding: mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } - interface: + interface: + bonding: ifindex: { type: int, min_value: 0, _required: True } mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } From 6cdc4df1dbcad30faf127bd3d8d02ee4d5689cd5 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 19:34:34 -0600 Subject: [PATCH 21/46] Fix missing ) --- netsim/extra/bonding/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netsim/extra/bonding/plugin.py b/netsim/extra/bonding/plugin.py index 24b4c280d..0d94e818b 100644 --- a/netsim/extra/bonding/plugin.py +++ b/netsim/extra/bonding/plugin.py @@ -48,7 +48,7 @@ def post_transform(topology: Box) -> None: if not bond_ifindex: continue - if 'virtual_interface' in intf or len(intf.get('neighbors',[])!=1: + if 'virtual_interface' in intf or len(intf.get('neighbors',[]))!=1: log.error( f"{intf.name}: 'bonding.ifindex' can only be applied to interfaces on physical p2p links", category=log.IncorrectAttr,module=_config_name) continue From 8cec816d2c13fc4e3f5df4cf103e17a89ae9a30c Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 20:18:09 -0600 Subject: [PATCH 22/46] bonding: Restore p2p link --- netsim/extra/bonding/plugin.py | 39 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/netsim/extra/bonding/plugin.py b/netsim/extra/bonding/plugin.py index 0d94e818b..840e36470 100644 --- a/netsim/extra/bonding/plugin.py +++ b/netsim/extra/bonding/plugin.py @@ -9,27 +9,22 @@ ''' add_bond_interfaces - append interface data to node.interfaces for bonding template to read and implement ''' -def add_bond_interfaces(node: Box, bonds: dict[int,list[Box]]) -> None: +def add_bond_interfaces(node: Box, bonds: dict[int,Box]) -> None: last_linkindex = node.interfaces[-1].linkindex - for c,(ifindex,members) in enumerate(bonds.items()): + for c,(ifindex,bond) in enumerate(bonds.items()): bond_if = { 'linkindex': last_linkindex + 1 + c, 'type': 'bond', 'ifname': f'bond{ifindex}', # XXX hardcoded, could make this a device template attribute 'name': f'bond {ifindex}', - 'bonding': { 'ifindex': ifindex, 'members': [ m.ifname for m in members ] }, + 'bonding': { 'ifindex': ifindex, 'members': [ m.ifname for m in bond['members'] ] }, + 'interfaces': bond['interfaces'], 'ifindex': 50000 + c, 'virtual_interface': True } - c = c + 1 - vlans = [ m.vlan for m in members if 'vlan' in m ] - if vlans: - bond_if.vlan = vlans[0] # Take the first VLAN, hopefully consistent - else: - for af in ['ipv4','ipv6']: - ips = [ m[af] for m in members if af in m ] - if ips: - bond_if[af] = ips[0] # Take the first one, if any + for af in ['ipv4','ipv6']: + if af in bond: + bond_if[af] = bond[af] # Take the first one, if any node.interfaces.append(bond_if) ''' @@ -42,26 +37,30 @@ def post_transform(topology: Box) -> None: for node in topology.nodes.values(): features = devices.get_device_features(node,topology.defaults) if 'bonding' in features: - bonds : dict[int,list[Box]] = {} + bonds : dict[int,Box] = {} for intf in node.get('interfaces',[]): bond_ifindex = intf.get('bonding.ifindex',None) if not bond_ifindex: continue - if 'virtual_interface' in intf or len(intf.get('neighbors',[]))!=1: - log.error( f"{intf.name}: 'bonding.ifindex' can only be applied to interfaces on physical p2p links", + link = topology.links[ intf.linkindex-1 ] + if 'virtual_interface' in intf or link.node_count!=2: + log.error( f"{intf.name}: 'bonding.ifindex' can only be applied to interfaces on direct p2p links", category=log.IncorrectAttr,module=_config_name) continue clone = data.get_box(intf) if bond_ifindex in bonds: - bonds[ bond_ifindex ].append( clone ) + bonds[bond_ifindex]['members'].append( clone ) else: - bonds[ bond_ifindex ] = [ clone ] + bonds[bond_ifindex] = { 'interfaces': intf.interfaces, 'members': [ clone ] } + for att in ['ipv4','ipv6']: # Move any ips + if att in intf: + bonds[bond_ifindex][att] = intf.pop(att,None) - for att in ['ipv4','ipv6','vlan']: # Remove any ips or vlan - intf.pop(att,None) - intf.prefix = False # L2 interface + intf.neighbors = [ { 'ifname': i.ifname, 'node': i.node } for i in link.interfaces if i.node!=node.name ] + intf.type = 'p2p' + intf.prefix = False # L2 p2p interface if bonds: add_bond_interfaces(node,bonds) From 77f94c420636c8c4b2b69ed2362e6a70561de225 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 23:10:41 -0600 Subject: [PATCH 23/46] Rename neighbor interfaces --- netsim/extra/bonding/plugin.py | 42 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/netsim/extra/bonding/plugin.py b/netsim/extra/bonding/plugin.py index 840e36470..5667d9247 100644 --- a/netsim/extra/bonding/plugin.py +++ b/netsim/extra/bonding/plugin.py @@ -10,14 +10,13 @@ add_bond_interfaces - append interface data to node.interfaces for bonding template to read and implement ''' def add_bond_interfaces(node: Box, bonds: dict[int,Box]) -> None: - last_linkindex = node.interfaces[-1].linkindex for c,(ifindex,bond) in enumerate(bonds.items()): + ifname = f'bond{ifindex}' # XXX hardcoded, could make this a device template attribute bond_if = { - 'linkindex': last_linkindex + 1 + c, - 'type': 'bond', - 'ifname': f'bond{ifindex}', # XXX hardcoded, could make this a device template attribute + 'type': 'lag', # TODO "bond"? + 'ifname': ifname, 'name': f'bond {ifindex}', - 'bonding': { 'ifindex': ifindex, 'members': [ m.ifname for m in bond['members'] ] }, + 'bonding': { 'ifindex': ifindex, 'members': bond['members'], 'mode': bond.mode }, 'interfaces': bond['interfaces'], 'ifindex': 50000 + c, 'virtual_interface': True @@ -34,10 +33,11 @@ def add_bond_interfaces(node: Box, bonds: dict[int,Box]) -> None: ''' def post_transform(topology: Box) -> None: global _config_name + bond_mode = topology.get('bonding.mode','active-backup') + bonds : Box = data.get_empty_box() # Map of bonds per node, indexed by bonding.ifindex for node in topology.nodes.values(): features = devices.get_device_features(node,topology.defaults) if 'bonding' in features: - bonds : dict[int,Box] = {} for intf in node.get('interfaces',[]): bond_ifindex = intf.get('bonding.ifindex',None) if not bond_ifindex: @@ -50,18 +50,30 @@ def post_transform(topology: Box) -> None: continue clone = data.get_box(intf) - if bond_ifindex in bonds: - bonds[bond_ifindex]['members'].append( clone ) + if node.name in bonds and bond_ifindex in bonds[node.name]: + bonds[node.name][bond_ifindex]['members'].append( clone.ifname ) + for att in ['ipv4','ipv6']: + intf.pop(att,None) else: - bonds[bond_ifindex] = { 'interfaces': intf.interfaces, 'members': [ clone ] } - for att in ['ipv4','ipv6']: # Move any ips + mode = intf.get('bonding.mode',bond_mode) + bonds[node.name][bond_ifindex] = { 'interfaces': intf.neighbors, 'members': [ clone.ifname ], 'mode': mode } + for att in ['ipv4','ipv6']: # Move any ips (from first member link) if att in intf: - bonds[bond_ifindex][att] = intf.pop(att,None) + bonds[node.name][bond_ifindex][att] = intf.pop(att,None) intf.neighbors = [ { 'ifname': i.ifname, 'node': i.node } for i in link.interfaces if i.node!=node.name ] intf.type = 'p2p' - intf.prefix = False # L2 p2p interface + intf.prefix = False # L2 p2p interface + intf.pop('name',None) - if bonds: - add_bond_interfaces(node,bonds) - api.node_config(node,_config_name) # Remember that we have to do extra configuration + for node in topology.nodes.values(): + if node.name in bonds: + for bond in bonds[node.name].values(): + for i in bond.interfaces: + if i.node in bonds: + for i2,b2 in bonds[i.node].items(): + if i.ifname in b2['members']: + i.ifname = f'bond{i2}' # Correct neighbor name + continue + add_bond_interfaces(node,bonds[node.name]) + api.node_config(node,_config_name) # Remember that we have to do extra configuration From 8682d9adc0fa976a2c7a0b249a3c68e7892bdec5 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 23:12:56 -0600 Subject: [PATCH 24/46] Add lag/bonding param to macro --- netsim/ansible/templates/lag/linux.j2 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index 2ea6a008b..3614fa12c 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -1,8 +1,8 @@ -{% macro create_bond_dev(l) %} +{% macro create_bond_dev(l,params) %} {% if l.type=='lag' %} -{% set _m = l.lag.mode|default(lag.mode) %} +{% set _m = l.lag.mode|default(params.mode|default("active-backup")) %} {% if _m=="802.3ad" %} -{% set _lacp = l.lag.lacp|default(lag.lacp) %} +{% set _lacp = l.lag.lacp|default(params.lacp|default('off')) %} {% 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 %} @@ -30,7 +30,7 @@ set -e # Exit immediately when any command fails # Create bonds for LAGs, if any. Requires kernel bonding module loaded # {% for l in interfaces if 'lag' in l %} -{{ create_bond_dev(l) }} +{{ create_bond_dev(l,lag) }} ip link set dev {{ l.ifname }} down {% endfor -%} @@ -39,7 +39,7 @@ ip link set dev {{ l.ifname }} down {% if node_provider != 'clab' %} ethtool -s {{ l.ifname }} autoneg off speed 1000 duplex full {% endif %} -ip link set dev {{ l.ifname }} master {% +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 %} From 3a88fbbd6242e4ad9bbfc5d1bad1c0ecd2aa30da Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 23:15:46 -0600 Subject: [PATCH 25/46] Set default params={} --- netsim/ansible/templates/lag/linux.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index 3614fa12c..7a4fd7884 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -1,4 +1,4 @@ -{% macro create_bond_dev(l,params) %} +{% macro create_bond_dev(l,params={}) %} {% if l.type=='lag' %} {% set _m = l.lag.mode|default(params.mode|default("active-backup")) %} {% if _m=="802.3ad" %} From 556c0f13f34ff3e8613c2309c358737d9bf227f9 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 23:17:20 -0600 Subject: [PATCH 26/46] bonding - define default mode --- netsim/extra/bonding/defaults.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netsim/extra/bonding/defaults.yml b/netsim/extra/bonding/defaults.yml index e8d9fc313..d90f455be 100644 --- a/netsim/extra/bonding/defaults.yml +++ b/netsim/extra/bonding/defaults.yml @@ -1,6 +1,9 @@ # bonding plugin attributes # --- +bonding: + mode: active-backup # Default bond mode + devices: linux: features.bonding: True From 1cb4cea1b988e53a333b1e6176324c893ac007e3 Mon Sep 17 00:00:00 2001 From: J vanBemmel Date: Wed, 11 Dec 2024 23:22:01 -0600 Subject: [PATCH 27/46] bonding - set intf down before up --- netsim/extra/bonding/linux.j2 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/netsim/extra/bonding/linux.j2 b/netsim/extra/bonding/linux.j2 index f8a6884d8..9eb167bb0 100644 --- a/netsim/extra/bonding/linux.j2 +++ b/netsim/extra/bonding/linux.j2 @@ -3,12 +3,13 @@ set -e -{% for intf in interfaces|default([]) if intf.type=='bond' %} -{% set mode = intf.bonding.mode|default('active-backup') %} -{{ create_bond_dev(intf|combine( {'type':'lag','lag': { 'mode': mode, 'lacp': 'off' } }) ) }} +{% for intf in interfaces|default([]) if intf.bonding.mode is defined %} +{{ create_bond_dev(intf,{ 'mode': intf.bonding.mode, 'lacp': 'off' } ) }} ip link set dev {{ intf.ifname }} down -{% for member in intf.members %} +{% for member in intf.bonding.members %} +ip link set dev {{ member }} down ip link set dev {{ member }} master {{ intf.ifname }} +ip link set dev {{ member }} up {% endfor %} ip link set dev {{ intf.ifname }} up {% endfor %} From c844d13892218496fcf6ebfa87d4a493393369d6 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 13:55:47 -0600 Subject: [PATCH 28/46] Include lag changes from latest PR --- netsim/ansible/templates/lag/linux.j2 | 7 +- netsim/defaults/attributes.yml | 2 +- netsim/modules/lag.py | 238 +++++++++++------- netsim/modules/lag.yml | 19 +- .../lag/11-host-lag-active-standby.yml | 2 + 5 files changed, 171 insertions(+), 97 deletions(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index 7a4fd7884..7ef1508b0 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -2,14 +2,15 @@ {% if l.type=='lag' %} {% set _m = l.lag.mode|default(params.mode|default("active-backup")) %} {% if _m=="802.3ad" %} -{% set _lacp = l.lag.lacp|default(params.lacp|default('off')) %} +{% set _lacp = l.lag.lacp|default(params.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 + " xmit_hash_policy encap3+4" + lacp_rate %} +{% set _m = _m + lacp_rate %} {% if node_provider == 'clab' %} {% set _m = _m + " lacp_active " + lacp_act %} {% endif %} {% endif %} +{% set _m = _m + " xmit_hash_policy encap3+4" %} {% if node_provider != 'clab' %} # # Make sure 'bonding' module is loaded @@ -40,7 +41,7 @@ ip link set dev {{ l.ifname }} down 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 }} + for i in interfaces if i.type=='lag' and i.lag.ifindex==l.lag._parentindex %}{{ i.ifname }} {% endfor %} {% endif %} ip link set dev {{ l.ifname }} up diff --git a/netsim/defaults/attributes.yml b/netsim/defaults/attributes.yml index ff247320c..b407de893 100644 --- a/netsim/defaults/attributes.yml +++ b/netsim/defaults/attributes.yml @@ -60,7 +60,7 @@ link: # Global link attributes _alt_types: [ bool_false, prefix_str, named_pfx ] role: id pool: id - type: { type: str, valid_values: [ lan, p2p, stub, loopback, tunnel, vlan_member, lag ] } + type: { type: str, valid_values: [ lan, p2p, stub, loopback, tunnel, vlan_member, virtual_lag ] } unnumbered: bool interfaces: mtu: { type: int, min_value: 64, max_value: 65535 } diff --git a/netsim/modules/lag.py b/netsim/modules/lag.py index 7bf7f894e..5c84d7efb 100644 --- a/netsim/modules/lag.py +++ b/netsim/modules/lag.py @@ -38,11 +38,11 @@ def populate_peerlink_id_set(topology: Box) -> None: create_l2_link_base - Create a L2 P2P link as base for member links """ def create_l2_link_base(l: Box, topology: Box) -> Box: - l2_ifdata = data.get_box({ 'type': "p2p", 'prefix': False, 'lag': {} }) # Construct an L2 member link - for a in list(topology.defaults.lag.attributes.lag_l2_ifattr): + l2_linkdata = data.get_box({ 'type': "p2p", 'prefix': False, 'lag': {} }) # Construct an L2 member link + for a in list(topology.defaults.lag.attributes.lag_l2_linkattr): if a in l: - l2_ifdata[a] = l[a] - return l2_ifdata + l2_linkdata[a] = l[a] + return l2_linkdata """ check_lag_config - check if the given node supports lag and has the module enabled @@ -144,28 +144,55 @@ def set_lag_ifindex(laglink: Box, intf: Box, is_mside: bool, topology: Box) -> b """ split_dual_mlag_link - Split dual-mlag pairs into 2 lag link groups, returns the new link """ -def split_dual_mlag_link(link: Box, topology: Box) -> Box: +def split_dual_mlag_link(link: Box, topology: Box) -> None: def no_peer(i: Box) -> Box: i.pop('_peer',None) # Remove internal _peer attribute - return i - + return i + split_copy = data.get_box(link) # Make a copy split_copy.linkindex = len(topology.links)+1 # Update its link index split_copy._linkname = split_copy._linkname + "-2" # Assign unique name - split_copy.lag.pop('members',None) # Clean up members first_pair = ( link.interfaces[0].node, link.interfaces[0]._peer ) split_copy.interfaces = [ no_peer(i) for i in link.interfaces if i.node in first_pair ] topology.links[link.linkindex-1].interfaces = [ no_peer(i) for i in link.interfaces if i.node not in first_pair ] + + for l in topology.links: + if 'lag' in l: + nodes = [ i.node for i in l.interfaces ] + if first_pair[0] in nodes and first_pair[1] in nodes: + l.lag._parentindex = split_copy.linkindex + if log.debug_active('lag'): print(f'LAG split_dual_mlag_links -> adding split link {split_copy}') print(f'LAG split_dual_mlag_links -> remaining link {topology.links[link.linkindex-1]}') topology.links.append(split_copy) - return split_copy """ create_lag_member_links -- expand lag.members for link l and create physical p2p links """ -def create_lag_member_links(l: Box, topology: Box) -> None: +def create_lag_member_links(l: Box, topology: Box) -> bool: + members = normalized_members(l,topology) # Build list of normalized member links + if not members: + return False + l.lag.members = members # Update for create_lag_interfaces + + l2_linkdata = create_l2_link_base(l,topology) + keep_attr = list(topology.defaults.lag.attributes.lag_member_linkattr) + keep_if = ['node','ifindex'] # Keep only 'node' and optional 'ifindex' + for member in members: + member = l2_linkdata + { k:v for k,v in member.items() if k in keep_attr } + member.linkindex = len(topology.links)+1 + member.interfaces = [ { k:v for k,v in i.items() if k in keep_if } for i in member.interfaces ] + member.lag._parentindex = l.linkindex # Keep track of parent, updated to lag.ifindex below + if log.debug_active('lag'): + print(f'LAG create_lag_member_links -> adding link {member}') + topology.links.append(member) + return True + +""" +create_lag_interfaces -- create interfaces of type "lag" for each link of type virtual_lag +""" +def create_lag_interfaces(l: Box, topology: Box) -> None: """ analyze_lag - figure out which type of LAG we're dealing with: @@ -175,19 +202,19 @@ def create_lag_member_links(l: Box, topology: Box) -> None: Returns updated node_count set, bool is_mlag, bool dual_mlag, string one_side """ - def analyze_lag(members: list, node_count: dict) -> tuple[bool,bool,str]: - for m in members: + def analyze_lag(node_count: dict) -> tuple[bool,bool,str]: + for m in l.lag.members: for i in m.interfaces: if i.node in node_count: node_count[ i.node ] = node_count[ i.node ] + 1 else: node_count[ i.node ] = 1 - + if len(node_count)==2: # Regular LAG between 2 nodes return (False,False,"") elif len(node_count)==3: # 1:2 MLAG or weird MLAG triangle for node_name,count in node_count.items(): - if count==len(members): + if count==len(l.lag.members): return (True,False,node_name) # Found the 1-side node elif len(node_count)==4: # 2:2 dual MLAG return (True,True,"") @@ -198,23 +225,28 @@ def analyze_lag(members: list, node_count: dict) -> tuple[bool,bool,str]: module='lag') return (False,False,"") - members = normalized_members(l,topology) # Build list of normalized member links - if not members: - return node_count: dict[str,int] = {} # Count how many times nodes are used - is_mlag, dual_mlag, one_side = analyze_lag(members,node_count) + is_mlag, dual_mlag, one_side = analyze_lag(node_count) if one_side=="": # Check for errors return - l.interfaces = [] # Build interface list for lag link + members = l.pop('lag.members',[]) # Remove lag.members skip_atts = list(topology.defaults.lag.attributes.lag_no_propagate) + copy_link_to_intf = ['vlan'] if 'vlan' in l else list(topology.defaults.lag.attributes.copy_link_to_intf) + link_atts = { k:v for k,v in l.items() if k in copy_link_to_intf } + l.interfaces = [] # Build interface list for lag link for node in node_count: - ifatts = data.get_box({ 'node': node }) + ifatts = data.get_box({ 'node': node, '_type': 'lag', 'lag': {} }) # use '_type', not 'type' (!) for m in members: # Collect attributes from member links - if node in [ i.node for i in m.interfaces ]:# ...in which is involved - ifatts = ifatts + { k:v for k,v in m.items() if k not in skip_atts } - if dual_mlag: - ifatts._peer = [ i.node for i in m.interfaces if i.node!=node ][0] + node_ifs = [ i for i in m.interfaces if i.node==node ] + if not node_ifs: # ...in which is involved + continue + ifatts = ifatts + { k:v for k,v in m.items() if k not in skip_atts } + node_ifs[0] + if dual_mlag: + ifatts._peer = [ i.node for i in m.interfaces if i.node!=node ][0] + if not 'vlan' in ifatts: # VLAN on interface overrides link IP settings + ifatts = link_atts + ifatts # include vlan, gateway or prefix settings from link + is_mside = is_mlag and node!=one_side # Set flag if this node is the M: side if not set_lag_ifindex(l,ifatts,is_mside,topology): return @@ -224,31 +256,19 @@ def analyze_lag(members: list, node_count: dict) -> tuple[bool,bool,str]: elif is_mlag: if not check_mlag_support(node,l._linkname,topology): return - ifatts.lag._mlag = True # Set internal flag + ifatts.lag._mlag = True # Set internal flag if log.debug_active('lag'): - print(f'LAG create_lag_member_links for node {node} -> collected ifatts {ifatts}') + print(f'LAG create_lag_interfaces for node {node} -> adding interface {ifatts} skip={skip_atts}') l.interfaces.append( ifatts ) - _sl = split_dual_mlag_link(l,topology) if dual_mlag else None - split_nodes = [ i.node for i in _sl.interfaces ] if _sl else [] - - l2_ifdata = create_l2_link_base(l,topology) - keep_attr = list(topology.defaults.lag.attributes.lag_member_ifattr) - for member in members: - member = l2_ifdata + member # Copy L2 data into member link - member = data.get_box({ k:v for k,v in member.items() if k in keep_attr }) # Filter out things not needed - member.linkindex = len(topology.links)+1 - parent = _sl if _sl and member.interfaces[0].node in split_nodes else l - member.lag._parentindex = parent.linkindex # Keep track of parent - if log.debug_active('lag'): - print(f'LAG create_lag_member_links -> adding link {member}') - topology.links.append(member) + if dual_mlag: # After creating interfaces, check if we need to split them + split_dual_mlag_link(l,topology) """ create_peer_links -- creates and configures physical link(s) for given peer link """ -def create_peer_links(l: Box, topology: Box) -> bool: +def create_peer_links(l: Box, topology: Box) -> None: """ check_same_pair - Verifies that the given member connects the same pair of nodes as the first @@ -264,10 +284,10 @@ def check_same_pair(member: Box) -> bool: return True members = normalized_members(l,topology,peerlink=True) - l2_ifdata = create_l2_link_base(l,topology) + l2_linkdata = create_l2_link_base(l,topology) for idx,member in enumerate(members): - member = l2_ifdata + member # Copy L2 data into member link + member = l2_linkdata + member # Copy L2 data into member link if idx==0: # For the first member, use the existing link topology.links[l.linkindex-1] = l + member # Update topology (l is a copy) first_pair = [ i.node for i in member.interfaces ] @@ -276,50 +296,47 @@ def check_same_pair(member: Box) -> bool: log.error(f'Nodes {first_pair} on MLAG peerlink {member._linkname} have different device types ({_devs})', category=log.IncorrectValue, module='lag') - return False + return for node in first_pair: if not check_mlag_support(node,l._linkname,topology): - return False + return if log.debug_active('lag'): print(f'LAG create_peer_links -> updated first link {l} from {member} -> {topology.links[l.linkindex-1]}') else: if not check_same_pair(member): # Check that any additional links connect the same nodes - return False + return member.linkindex = len(topology.links)+1 - member.lag._parentindex = l.linkindex # Keep track of parent + member.lag._peerlink = l.linkindex # Keep track of parent if log.debug_active('lag'): print(f'LAG create_peer_links -> adding link {member}') topology.links.append(member) - - return True + + topology.links[l.linkindex-1].pop("lag.members",None) # Cleanup """ -process_lag_links - process all links with 'lag' attribute +process_lag_link - process link with 'lag' attribute to create links for lag.members + Returns True iff a virtual_lag was created """ -def process_lag_links(topology: Box) -> None: - for l in list(topology.links): # Make a copy of the list, gets modified - if 'lag' not in l: - continue - elif not 'members' in l.lag: - log.error(f'must define "lag.members" on LAG link {l._linkname}', - category=log.IncorrectAttr, - module='lag') - continue - elif not _types.must_be_list(parent=l.lag,key='members',path=l._linkname,module='lag'): - continue - - peerlink_id = l.get(PEERLINK_ID_ATT,None) # Turn internal MLAG links into p2p links - if peerlink_id: - if peerlink_id is True: # Auto-assign peerlink ID if requested - l[PEERLINK_ID_ATT] = _dataplane.get_next_id(PEERLINK_ID_SET) - l.type = 'p2p' - l.prefix = False # L2-only - if not create_peer_links(l,topology): # Check for errors - return - else: - l.type = 'lag' - create_lag_member_links(l,topology) - topology.links[l.linkindex-1].lag.pop("members",None) +def process_lag_link(link: Box, topology: Box) -> bool: + if not 'members' in link.lag: + log.error(f'must define "lag.members" on LAG link {link._linkname}', + category=log.IncorrectAttr, + module='lag') + return False + elif not _types.must_be_list(parent=link.lag,key='members',path=link._linkname,module='lag'): + return False + + peerlink_id = link.get(PEERLINK_ID_ATT,None) # Turn internal MLAG links into p2p links + if peerlink_id: + if peerlink_id is True: # Auto-assign peerlink ID if requested + link[PEERLINK_ID_ATT] = _dataplane.get_next_id(PEERLINK_ID_SET) + link.type = 'p2p' + link.prefix = False # L2-only + create_peer_links(link,topology) + return False + else: + link.type = 'virtual_lag' # Temporary virtual link, removed in module_post_link_transform + return create_lag_member_links(link,topology) # # populate_mlag_peer - Lookup the IPv4 loopback address for the mlag peer, and derive a virtual MAC to use @@ -342,31 +359,71 @@ def populate_mlag_peer(node: Box, intf: Box, topology: Box) -> None: _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 + intf.lag.mlag.self = f"{_net[1-_id]}/{_net.prefixlen}" # including /prefix 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 + _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) for v in ['vlan','ifindex']: if v in _mlag_peer: intf.lag.mlag[v] = _mlag_peer[v] - - intf.pop('vlan',None) # Remove any VLANs provisioned on peerlinks + + intf.pop('vlan',None) # Remove any VLANs provisioned on peerlinks + +""" +configure_lag_mode - Check if the given lag mode configuration supports LACP, if not remove it +""" +def configure_lag_mode(intf: Box, topology: Box) -> None: + lag_mode = intf.get('lag.mode',topology.defaults.lag.mode) + if lag_mode != "802.3ad": + intf.pop('lag.lacp',None) + intf.pop('lag.lacp_mode',None) class LAG(_Module): """ - module_pre_transform -- Add links listed in each lag.members to the topology + module_pre_transform -- Analyze any user provided lag.ifindex values and peerlink ids, + convert lag.members to links and create 'lag' type interfaces + + Note: link.interfaces must be populated before vlan.link_pre_transform is called """ def module_pre_transform(self, topology: Box) -> None: if log.debug_active('lag'): - print(f'LAG module_pre_transform') + print(f'LAG module_pre_transform: Convert lag.members into additional topology.links') + + if not 'links' in topology: + return + populate_lag_id_set(topology) populate_peerlink_id_set(topology) - process_lag_links(topology) # Expand lag.members into additional p2p links + for link in list(topology.links): # Make a copy, may get modified + if 'lag' in link: + if process_lag_link(link,topology): + create_lag_interfaces(link,topology) # Create lag interfaces + + """ + link_pre_link_transform - rename interface '_type' to 'type' (after validation) + + The interface 'type' attribute is added internally, and cannot be defined in the data model. + It should have been called '_type' to begin with, but that ship has sailed a while ago. This module + implements a workaround by calling it '_type' during validation (allowing it to be skipped), and then + renaming it to 'type' here + """ + def link_pre_link_transform(self, link: Box, topology: Box) -> None: + for intf in link.interfaces: + if intf.get('_type',None)=='lag': + intf.type = intf.pop('_type') + + """ + module_post_link_transform - remove temporary 'virtual_lag' links + """ + def module_post_link_transform(self, topology: Box) -> None: + if log.debug_active('lag'): + print(f'LAG module_post_link_transform: Cleanup "virtual_lag" links') + topology.links = [ link for link in topology.links if link.type != 'virtual_lag' ] """ After attribute propagation and consolidation, verify that requested features are supported. @@ -379,19 +436,26 @@ def node_post_transform(self, node: Box, topology: Box) -> None: has_peerlink = False uses_mlag = False for i in node.interfaces: - if i.get(PEERLINK_ID_ATT,None): # Fill in peer loopback IP and vMAC for MLAG peer links + if i.get(PEERLINK_ID_ATT,None): # Fill in peer loopback IP and vMAC for MLAG peer links 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 - lacp_mode = i.get('lag.lacp_mode') # Inheritance copying is done elsewhere + elif i.get('type',None)=='lag': + i.lag = node.get('lag',{}) + i.lag # Merge node level settings with interface overrides + i.pop('mtu',None) # Remove any MTU settings - inherited from members + linkindex = i.pop('linkindex',None) # Remove linkindex (copied by create_node_interfaces) + for m in node.interfaces: # Update members to point to lag.ifindex, replacing linkindex + if m.get('lag._parentindex',None)==linkindex: + m.lag._parentindex = i.lag.ifindex # Make _parentindex point to lag.ifindex instead + + configure_lag_mode(i,topology) + lacp_mode = i.get('lag.lacp_mode') # Inheritance copying is done elsewhere 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}', category=log.IncorrectAttr, module='lag') if i.lag.get('mlag',False) is True: uses_mlag = True - + if uses_mlag and not has_peerlink: log.error(f'Node {node.name} uses MLAG but has no peerlink (lag with {PEERLINK_ID_ATT}) configured', category=log.IncorrectAttr, diff --git a/netsim/modules/lag.yml b/netsim/modules/lag.yml index c093a6b5a..37490be00 100644 --- a/netsim/modules/lag.yml +++ b/netsim/modules/lag.yml @@ -12,11 +12,13 @@ attributes: global: lacp: { type: str, valid_values: [ "off", "slow", "fast" ] } lacp_mode: { type: str, valid_values: [ "passive", "active" ] } - mode: { type: str, valid_values: [ "802.3ad", "balance-xor" ] } + + # All Linux bonding modes that require lag configuration on the switch side + mode: { type: str, valid_values: [ "802.3ad", "balance-rr", "balance-xor", "broadcast" ] } node: lacp: lacp_mode: - mode: { copy: global } + mode: link: # Most should be consistent across both interfaces on the link lacp: { copy: global } lacp_mode: { copy: global } @@ -36,21 +38,26 @@ attributes: # _mlag: bool # Copy only these L2 attributes into LAG physical link members - lag_l2_ifattr: + lag_l2_linkattr: mtu: bandwidth: # Keep only these attributes on member links - lag_member_ifattr: + lag_member_linkattr: mtu: bandwidth: - type: - prefix: _linkname: interfaces: + # prefix: # Don't copy these attributes to lag interfaces lag_no_propagate: interfaces: _linkname: name: + mtu: + + # Copy these attributes from lag links into lag interface-on-link data (if not existing) + copy_link_to_intf: + gateway: + pool: diff --git a/tests/integration/lag/11-host-lag-active-standby.yml b/tests/integration/lag/11-host-lag-active-standby.yml index c6d5a4493..02a44ed5d 100644 --- a/tests/integration/lag/11-host-lag-active-standby.yml +++ b/tests/integration/lag/11-host-lag-active-standby.yml @@ -5,6 +5,8 @@ message: | The hosts should be able to ping each other +defaults.device: frr + plugin: [ bonding ] # To be created bonding.mode: active-backup From 1bba22fc4ab3fc4f1d12dca18be032a925138625 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 14:00:56 -0600 Subject: [PATCH 29/46] Only configure xmit_hash_policy when needed --- netsim/ansible/templates/lag/linux.j2 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index 7ef1508b0..a743aab62 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -10,7 +10,9 @@ {% set _m = _m + " lacp_active " + lacp_act %} {% endif %} {% endif %} -{% set _m = _m + " xmit_hash_policy encap3+4" %} +{% if _m in ["802.3ad","balance-xor"] %} +{% set _m = _m + " xmit_hash_policy encap3+4" %} +{% endif %} {% if node_provider != 'clab' %} # # Make sure 'bonding' module is loaded From f31c698a396ee2ac85d7322a60794076d6ab7371 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 14:02:37 -0600 Subject: [PATCH 30/46] Also include bonding plugin modes --- netsim/ansible/templates/lag/linux.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index a743aab62..cd2482a35 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -10,7 +10,7 @@ {% set _m = _m + " lacp_active " + lacp_act %} {% endif %} {% endif %} -{% if _m in ["802.3ad","balance-xor"] %} +{% if _m in ["802.3ad","balance-xor","balance-alb","balance-tlb"] %} {% set _m = _m + " xmit_hash_policy encap3+4" %} {% endif %} {% if node_provider != 'clab' %} From 05c30302bd585a213cdd4c13ad959ad5d03d3c76 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 14:03:51 -0600 Subject: [PATCH 31/46] Fix trailing spaces --- tests/integration/lag/11-host-lag-active-standby.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/lag/11-host-lag-active-standby.yml b/tests/integration/lag/11-host-lag-active-standby.yml index 02a44ed5d..ef3e171e7 100644 --- a/tests/integration/lag/11-host-lag-active-standby.yml +++ b/tests/integration/lag/11-host-lag-active-standby.yml @@ -1,7 +1,7 @@ message: | The device under test is a pair of switches with a pair of links connected to 2 Linux hosts. The Linux hosts are using active-standby bonding, which doesn't require any special support from the switches - (and technically isn't "link aggregation") + (and technically isn't "link aggregation") The hosts should be able to ping each other From 4ef1127cc69e33100475574498f40fd17afc9ad7 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 14:06:29 -0600 Subject: [PATCH 32/46] Cleanup, shorten line length --- netsim/extra/bonding/defaults.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/netsim/extra/bonding/defaults.yml b/netsim/extra/bonding/defaults.yml index d90f455be..6732164f2 100644 --- a/netsim/extra/bonding/defaults.yml +++ b/netsim/extra/bonding/defaults.yml @@ -13,11 +13,12 @@ devices: attributes: global: bonding: - mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } # Linux bonding modes that use autonomous ports + # Subset of Linux bonding modes that use autonomous ports + mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } node: bonding: - mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } + mode: interface: bonding: ifindex: { type: int, min_value: 0, _required: True } - mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } + mode: { copy: global } From 21eaede67f3c378623e75696d84dc4bb73beb2eb Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 14:09:21 -0600 Subject: [PATCH 33/46] Update error --- netsim/extra/bonding/defaults.yml | 2 +- tests/errors/link-invalid-type.log | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netsim/extra/bonding/defaults.yml b/netsim/extra/bonding/defaults.yml index 6732164f2..03d9a81b3 100644 --- a/netsim/extra/bonding/defaults.yml +++ b/netsim/extra/bonding/defaults.yml @@ -13,7 +13,7 @@ devices: attributes: global: bonding: - # Subset of Linux bonding modes that use autonomous ports + # Subset of Linux bonding modes that use autonomous ports and no LACP mode: { type: str, valid_values: [ active-backup, balance-tlb, balance-alb ] } node: bonding: diff --git a/tests/errors/link-invalid-type.log b/tests/errors/link-invalid-type.log index e47a24f60..10c80ea23 100644 --- a/tests/errors/link-invalid-type.log +++ b/tests/errors/link-invalid-type.log @@ -1,4 +1,4 @@ IncorrectValue in links: attribute links[1].type has invalid value(s): wtf -... valid values are: lan,p2p,stub,loopback,tunnel,vlan_member,lag +... valid values are: lan,p2p,stub,loopback,tunnel,vlan_member,virtual_lag ... use 'netlab show attributes link' to display valid attributes Fatal error in netlab: Cannot proceed beyond this point due to errors, exiting From 45855f523f1b19445429317e5b81fd633faa3abf Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 14:22:57 -0600 Subject: [PATCH 34/46] Add bonding priority parameter Update test results --- netsim/extra/bonding/defaults.yml | 1 + netsim/extra/bonding/linux.j2 | 1 + .../lag/11-host-lag-active-standby.yml | 4 +- tests/topology/expected/lag-l2.yml | 25 -- .../topology/expected/lag-l3-access-vlan.yml | 35 --- tests/topology/expected/lag-l3-vlan-trunk.yml | 34 --- tests/topology/expected/lag-l3.yml | 33 +-- tests/topology/expected/lag-mlag-m_to_m.yml | 150 +++------- tests/topology/expected/lag-mlag.yml | 214 ++------------ .../expected/node.clone-plugin-lag.yml | 260 ++++-------------- 10 files changed, 125 insertions(+), 632 deletions(-) diff --git a/netsim/extra/bonding/defaults.yml b/netsim/extra/bonding/defaults.yml index 03d9a81b3..b01b629f6 100644 --- a/netsim/extra/bonding/defaults.yml +++ b/netsim/extra/bonding/defaults.yml @@ -22,3 +22,4 @@ attributes: bonding: ifindex: { type: int, min_value: 0, _required: True } mode: { copy: global } + priority: int # Priority to use this interface, highest priority becomes active diff --git a/netsim/extra/bonding/linux.j2 b/netsim/extra/bonding/linux.j2 index 9eb167bb0..b24b809b7 100644 --- a/netsim/extra/bonding/linux.j2 +++ b/netsim/extra/bonding/linux.j2 @@ -9,6 +9,7 @@ ip link set dev {{ intf.ifname }} down {% for member in intf.bonding.members %} ip link set dev {{ member }} down ip link set dev {{ member }} master {{ intf.ifname }} +ip link set dev {{ member }} type bond_slave prio {{ intf.bonding.priority|default(0) }} ip link set dev {{ member }} up {% endfor %} ip link set dev {{ intf.ifname }} up diff --git a/tests/integration/lag/11-host-lag-active-standby.yml b/tests/integration/lag/11-host-lag-active-standby.yml index ef3e171e7..1bd47ca42 100644 --- a/tests/integration/lag/11-host-lag-active-standby.yml +++ b/tests/integration/lag/11-host-lag-active-standby.yml @@ -41,7 +41,9 @@ links: s1: vlan.access: red - h2: - bonding.ifindex: 1 + bonding: + ifindex: 1 + priority: 1 # Prefer s2 as active switch for h2 s2: vlan.access: red diff --git a/tests/topology/expected/lag-l2.yml b/tests/topology/expected/lag-l2.yml index 0499a8fa2..22e52584f 100644 --- a/tests/topology/expected/lag-l2.yml +++ b/tests/topology/expected/lag-l2.yml @@ -6,27 +6,6 @@ lag: lacp_mode: active mode: 802.3ad links: -- _linkname: links[1] - bridge: input_1 - interfaces: - - ifindex: 30000 - ifname: port-channel1 - lag: - ifindex: 1 - node: r1 - - ifindex: 30000 - ifname: port-channel1 - lag: - ifindex: 1 - node: r2 - lag: {} - linkindex: 1 - mtu: 1600 - node_count: 2 - prefix: false - stp: - enable: false - type: lag - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -83,8 +62,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1600 name: r1 -> r2 neighbors: - ifname: port-channel1 @@ -164,8 +141,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1600 name: r2 -> r1 neighbors: - ifname: port-channel1 diff --git a/tests/topology/expected/lag-l3-access-vlan.yml b/tests/topology/expected/lag-l3-access-vlan.yml index 3e2d8927a..d53a3f241 100644 --- a/tests/topology/expected/lag-l3-access-vlan.yml +++ b/tests/topology/expected/lag-l3-access-vlan.yml @@ -6,37 +6,6 @@ lag: lacp_mode: active mode: 802.3ad links: -- _linkname: links[1] - bridge: input_1 - interfaces: - - _vlan_mode: irb - ifindex: 30000 - ifname: bond1 - ipv4: 172.16.0.1/24 - lag: - ifindex: 1 - node: r1 - vlan: - access: v1 - - _vlan_mode: irb - ifindex: 30000 - ifname: bond1 - ipv4: 172.16.0.2/24 - lag: - ifindex: 1 - node: r2 - vlan: - access: v1 - lag: - lacp: 'off' - linkindex: 1 - node_count: 2 - prefix: - allocation: id_based - ipv4: 172.16.0.0/24 - type: lag - vlan: - access: v1 - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -107,8 +76,6 @@ nodes: lacp: 'off' lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1500 name: '[Access VLAN v1] r1 -> r2' neighbors: - ifname: bond1 @@ -219,8 +186,6 @@ nodes: lacp: 'off' lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1500 name: '[Access VLAN v1] r2 -> r1' neighbors: - ifname: bond1 diff --git a/tests/topology/expected/lag-l3-vlan-trunk.yml b/tests/topology/expected/lag-l3-vlan-trunk.yml index f85f9de2e..fcdc00ccb 100644 --- a/tests/topology/expected/lag-l3-vlan-trunk.yml +++ b/tests/topology/expected/lag-l3-vlan-trunk.yml @@ -6,36 +6,6 @@ lag: lacp_mode: active mode: 802.3ad links: -- _linkname: links[1] - bridge: input_1 - interfaces: - - ifindex: 30000 - ifname: bond1 - lag: - ifindex: 1 - node: r1 - vlan: - trunk: - v1: {} - v2: {} - - ifindex: 30000 - ifname: bond1 - lag: - ifindex: 1 - node: r2 - vlan: - trunk: - v1: {} - v2: {} - lag: {} - linkindex: 1 - node_count: 2 - prefix: {} - type: lag - vlan: - trunk: - v1: {} - v2: {} - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -106,8 +76,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1500 name: r1 -> r2 neighbors: - ifname: bond1 @@ -264,8 +232,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1500 name: r2 -> r1 neighbors: - ifname: bond1 diff --git a/tests/topology/expected/lag-l3.yml b/tests/topology/expected/lag-l3.yml index 22342110c..2b0f4039c 100644 --- a/tests/topology/expected/lag-l3.yml +++ b/tests/topology/expected/lag-l3.yml @@ -6,27 +6,6 @@ lag: lacp_mode: active mode: 802.3ad links: -- _linkname: links[1] - bridge: input_1 - interfaces: - - ifindex: 30000 - ifname: port-channel1 - ipv4: 10.1.0.1/30 - lag: - ifindex: 1 - node: r1 - - ifindex: 30000 - ifname: port-channel1 - ipv4: 10.1.0.2/30 - lag: - ifindex: 1 - node: r2 - lag: {} - linkindex: 1 - node_count: 2 - prefix: - ipv4: 10.1.0.0/30 - type: lag - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -72,18 +51,16 @@ nodes: interfaces: - ifindex: 30000 ifname: port-channel1 - ipv4: 10.1.0.1/30 + ipv4: 172.16.0.1/24 lag: ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1500 name: r1 -> r2 neighbors: - ifname: port-channel1 - ipv4: 10.1.0.2/30 + ipv4: 172.16.0.2/24 node: r2 type: lag virtual_interface: true @@ -145,18 +122,16 @@ nodes: interfaces: - ifindex: 30000 ifname: port-channel1 - ipv4: 10.1.0.2/30 + ipv4: 172.16.0.2/24 lag: ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 1 - mtu: 1500 name: r2 -> r1 neighbors: - ifname: port-channel1 - ipv4: 10.1.0.1/30 + ipv4: 172.16.0.1/24 node: r1 type: lag virtual_interface: true diff --git a/tests/topology/expected/lag-mlag-m_to_m.yml b/tests/topology/expected/lag-mlag-m_to_m.yml index cbd4a9499..b5c513c02 100644 --- a/tests/topology/expected/lag-mlag-m_to_m.yml +++ b/tests/topology/expected/lag-mlag-m_to_m.yml @@ -52,77 +52,17 @@ links: node_count: 2 prefix: false type: p2p -- _linkname: links[3] - bridge: input_3 - interfaces: - - ifindex: 30000 - ifname: port-channel1 - lag: - _mlag: true - node: a2 - vlan: - trunk: - red: {} - - ifindex: 30000 - ifname: port-channel1 - lag: - _mlag: true - node: b2 - vlan: - trunk: - red: {} - lag: - ifindex: 1 - linkindex: 3 - node_count: 2 - prefix: {} - type: lag - vlan: - trunk: - red: {} -- _linkname: links[3]-2 - bridge: input_4 - interfaces: - - ifindex: 30000 - ifname: port-channel1 - lag: - _mlag: true - node: a1 - vlan: - trunk: - red: {} - - ifindex: 30000 - ifname: port-channel1 - lag: - _mlag: true - node: b1 - vlan: - trunk: - red: {} - lag: - ifindex: 1 - linkindex: 4 - node_count: 2 - prefix: {} - type: lag - vlan: - trunk: - red: {} - _linkname: links[3].lag[1] interfaces: - ifindex: 2 ifname: Ethernet2 - lag: - ifindex: 10 node: a1 - ifindex: 2 ifname: Ethernet2 - lag: - ifindex: 20 node: b1 lag: - _parentindex: 4 - linkindex: 5 + _parentindex: 6 + linkindex: 4 node_count: 2 prefix: false type: p2p @@ -130,17 +70,13 @@ links: interfaces: - ifindex: 2 ifname: Ethernet2 - lag: - ifindex: 10 node: a2 - ifindex: 2 ifname: Ethernet2 - lag: - ifindex: 20 node: b2 lag: _parentindex: 3 - linkindex: 6 + linkindex: 5 node_count: 2 prefix: false type: p2p @@ -219,18 +155,27 @@ nodes: - ifname: Ethernet1 node: a2 type: p2p + - ifindex: 2 + ifname: Ethernet2 + lag: + _parentindex: 10 + linkindex: 4 + name: a1 -> b1 + neighbors: + - ifname: Ethernet2 + node: b1 + type: p2p - ifindex: 30000 - ifname: port-channel1 + ifname: port-channel10 lag: _mlag: true - ifindex: 1 + ifindex: 10 lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 4 name: a1 -> b1 neighbors: - - ifname: port-channel1 + - ifname: port-channel20 node: b1 type: lag virtual_interface: true @@ -239,17 +184,6 @@ nodes: red: {} trunk_id: - 1000 - - ifindex: 2 - ifname: Ethernet2 - lag: - _parentindex: 4 - ifindex: 10 - linkindex: 5 - name: a1 -> b1 - neighbors: - - ifname: Ethernet2 - node: b1 - type: p2p - bridge: input_7 ifindex: 3 ifname: Ethernet3 @@ -341,17 +275,16 @@ nodes: node: a1 type: p2p - ifindex: 30000 - ifname: port-channel1 + ifname: port-channel10 lag: _mlag: true - ifindex: 1 + ifindex: 10 lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 3 name: a2 -> b2 neighbors: - - ifname: port-channel1 + - ifname: port-channel20 node: b2 type: lag virtual_interface: true @@ -363,9 +296,8 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 3 - ifindex: 10 - linkindex: 6 + _parentindex: 10 + linkindex: 5 name: a2 -> b2 neighbors: - ifname: Ethernet2 @@ -448,18 +380,27 @@ nodes: - ifname: Ethernet1 node: b2 type: p2p + - ifindex: 2 + ifname: Ethernet2 + lag: + _parentindex: 20 + linkindex: 4 + name: b1 -> a1 + neighbors: + - ifname: Ethernet2 + node: a1 + type: p2p - ifindex: 30000 - ifname: port-channel1 + ifname: port-channel20 lag: _mlag: true - ifindex: 1 + ifindex: 20 lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 4 name: b1 -> a1 neighbors: - - ifname: port-channel1 + - ifname: port-channel10 node: a1 type: lag virtual_interface: true @@ -468,17 +409,6 @@ nodes: red: {} trunk_id: - 1000 - - ifindex: 2 - ifname: Ethernet2 - lag: - _parentindex: 4 - ifindex: 20 - linkindex: 5 - name: b1 -> a1 - neighbors: - - ifname: Ethernet2 - node: a1 - type: p2p - bridge_group: 1 ifindex: 4 ifname: Vlan1000 @@ -557,17 +487,16 @@ nodes: node: b1 type: p2p - ifindex: 30000 - ifname: port-channel1 + ifname: port-channel20 lag: _mlag: true - ifindex: 1 + ifindex: 20 lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 3 name: b2 -> a2 neighbors: - - ifname: port-channel1 + - ifname: port-channel10 node: a2 type: lag virtual_interface: true @@ -579,9 +508,8 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 3 - ifindex: 20 - linkindex: 6 + _parentindex: 20 + linkindex: 5 name: b2 -> a2 neighbors: - ifname: Ethernet2 diff --git a/tests/topology/expected/lag-mlag.yml b/tests/topology/expected/lag-mlag.yml index 3ae998882..c4e04e63a 100644 --- a/tests/topology/expected/lag-mlag.yml +++ b/tests/topology/expected/lag-mlag.yml @@ -32,144 +32,6 @@ links: node_count: 2 prefix: false type: p2p -- _linkname: links[2] - bridge: input_2 - interfaces: - - _vlan_mode: irb - ifindex: 30000 - ifname: bond1 - ipv4: 172.16.0.3/24 - lag: - ifindex: 1 - node: h1 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30000 - ifname: port-channel1 - ipv4: 172.16.0.1/24 - lag: - _mlag: true - node: s1 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30000 - ifname: port-channel1 - ipv4: 172.16.0.2/24 - lag: - _mlag: true - node: s2 - vlan: - access: red - lag: - ifindex: 1 - linkindex: 2 - node_count: 3 - prefix: - allocation: id_based - ipv4: 172.16.0.0/24 - type: lag - vlan: - access: red -- _linkname: links[3] - bridge: input_3 - interfaces: - - ifindex: 30001 - ifname: bond2 - lag: - ifindex: 2 - node: h1 - - ifindex: 30001 - ifname: port-channel2 - lag: - ifindex: 2 - node: s1 - lag: {} - linkindex: 3 - node_count: 2 - pool: l2only - type: lag -- _linkname: links[4] - bridge: input_4 - interfaces: - - _vlan_mode: irb - ifindex: 30000 - ifname: bond1 - ipv4: 172.16.0.4/24 - lag: - ifindex: 1 - node: h2 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30002 - ifname: port-channel3 - ipv4: 172.16.0.1/24 - lag: - _mlag: true - node: s1 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30001 - ifname: port-channel3 - ipv4: 172.16.0.2/24 - lag: - _mlag: true - node: s2 - vlan: - access: red - lag: - ifindex: 3 - linkindex: 4 - node_count: 3 - prefix: - allocation: id_based - ipv4: 172.16.0.0/24 - type: lag - vlan: - access: red -- _linkname: links[5] - bridge: input_5 - interfaces: - - _vlan_mode: irb - ifindex: 30001 - ifname: bond2 - ipv4: 172.16.0.4/24 - lag: - ifindex: 2 - node: h2 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30003 - ifname: port-channel4 - ipv4: 172.16.0.1/24 - lag: - _mlag: true - node: s1 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30002 - ifname: port-channel4 - ipv4: 172.16.0.2/24 - lag: - _mlag: true - node: s2 - vlan: - access: red - lag: - ifindex: 4 - linkindex: 5 - node_count: 3 - prefix: - allocation: id_based - ipv4: 172.16.0.0/24 - type: lag - vlan: - access: red - _linkname: links[1].peerlink[2] interfaces: - ifindex: 2 @@ -179,7 +41,7 @@ links: ifname: ethernet1/1/2 node: s1 lag: - _parentindex: 1 + _peerlink: 1 linkindex: 6 node_count: 2 prefix: false @@ -380,8 +242,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 2 - mtu: 1500 name: '[Access VLAN red] h1 -> [s1,s2]' neighbors: - ifname: port-channel1 @@ -402,8 +262,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 3 - mtu: 1500 name: h1 -> s1 neighbors: - ifname: port-channel2 @@ -414,7 +272,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 7 mtu: 1500 name: h1 -> s1 @@ -425,7 +283,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 8 mtu: 1500 name: h1 -> s1 @@ -436,7 +294,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 9 mtu: 1500 name: h1 -> s2 @@ -447,7 +305,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 10 mtu: 1500 name: h1 -> s2 @@ -458,7 +316,7 @@ nodes: - ifindex: 5 ifname: eth5 lag: - _parentindex: 3 + _parentindex: 2 linkindex: 11 mtu: 1500 name: h1 -> s1 @@ -469,7 +327,7 @@ nodes: - ifindex: 6 ifname: eth6 lag: - _parentindex: 3 + _parentindex: 2 linkindex: 12 mtu: 1500 name: h1 -> s1 @@ -550,8 +408,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 4 - mtu: 1500 name: '[Access VLAN red] h2 -> [s1,s2]' neighbors: - ifname: port-channel3 @@ -572,8 +428,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 5 - mtu: 1500 name: '[Access VLAN red] h2 -> [s1,s2]' neighbors: - ifname: port-channel4 @@ -590,7 +444,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 4 + _parentindex: 1 linkindex: 13 mtu: 1500 name: h2 -> s1 @@ -601,7 +455,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 4 + _parentindex: 1 linkindex: 14 mtu: 1500 name: h2 -> s1 @@ -612,7 +466,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 4 + _parentindex: 1 linkindex: 15 mtu: 1500 name: h2 -> s2 @@ -623,7 +477,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 4 + _parentindex: 1 linkindex: 16 mtu: 1500 name: h2 -> s2 @@ -634,7 +488,7 @@ nodes: - ifindex: 5 ifname: eth5 lag: - _parentindex: 5 + _parentindex: 2 linkindex: 17 mtu: 1500 name: h2 -> s1 @@ -645,7 +499,7 @@ nodes: - ifindex: 6 ifname: eth6 lag: - _parentindex: 5 + _parentindex: 2 linkindex: 18 mtu: 1500 name: h2 -> s2 @@ -737,8 +591,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 2 - mtu: 1500 name: '[Access VLAN red] s1 -> [h1,s2]' neighbors: - ifname: bond1 @@ -759,8 +611,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 3 - mtu: 1500 name: s1 -> h1 neighbors: - ifname: bond2 @@ -776,8 +626,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 4 - mtu: 1500 name: '[Access VLAN red] s1 -> [h2,s2]' neighbors: - ifname: bond1 @@ -799,8 +647,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 5 - mtu: 1500 name: '[Access VLAN red] s1 -> [h2,s2]' neighbors: - ifname: bond2 @@ -819,7 +665,7 @@ nodes: ifindex: 2 ifname: ethernet1/1/2 lag: - _parentindex: 1 + _peerlink: 1 linkindex: 6 mtu: 1500 name: s1 -> s2 @@ -832,7 +678,7 @@ nodes: ifindex: 3 ifname: ethernet1/1/3 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 7 mtu: 1500 name: s1 -> h1 @@ -845,7 +691,7 @@ nodes: ifindex: 4 ifname: ethernet1/1/4 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 8 mtu: 1500 name: s1 -> h1 @@ -858,7 +704,7 @@ nodes: ifindex: 5 ifname: ethernet1/1/5 lag: - _parentindex: 3 + _parentindex: 2 linkindex: 11 mtu: 1500 name: s1 -> h1 @@ -871,7 +717,7 @@ nodes: ifindex: 6 ifname: ethernet1/1/6 lag: - _parentindex: 3 + _parentindex: 2 linkindex: 12 mtu: 1500 name: s1 -> h1 @@ -884,7 +730,7 @@ nodes: ifindex: 7 ifname: ethernet1/1/7 lag: - _parentindex: 4 + _parentindex: 3 linkindex: 13 mtu: 1500 name: s1 -> h2 @@ -897,7 +743,7 @@ nodes: ifindex: 8 ifname: ethernet1/1/8 lag: - _parentindex: 4 + _parentindex: 3 linkindex: 14 mtu: 1500 name: s1 -> h2 @@ -910,7 +756,7 @@ nodes: ifindex: 9 ifname: ethernet1/1/9 lag: - _parentindex: 5 + _parentindex: 4 linkindex: 17 mtu: 1500 name: s1 -> h2 @@ -1002,8 +848,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 2 - mtu: 1500 name: '[Access VLAN red] s2 -> [h1,s1]' neighbors: - ifname: bond1 @@ -1025,8 +869,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 4 - mtu: 1500 name: '[Access VLAN red] s2 -> [h2,s1]' neighbors: - ifname: bond1 @@ -1048,8 +890,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 5 - mtu: 1500 name: '[Access VLAN red] s2 -> [h2,s1]' neighbors: - ifname: bond2 @@ -1068,7 +908,7 @@ nodes: ifindex: 2 ifname: ethernet1/1/2 lag: - _parentindex: 1 + _peerlink: 1 linkindex: 6 mtu: 1500 name: s2 -> s1 @@ -1081,7 +921,7 @@ nodes: ifindex: 3 ifname: ethernet1/1/3 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 9 mtu: 1500 name: s2 -> h1 @@ -1094,7 +934,7 @@ nodes: ifindex: 4 ifname: ethernet1/1/4 lag: - _parentindex: 2 + _parentindex: 1 linkindex: 10 mtu: 1500 name: s2 -> h1 @@ -1107,7 +947,7 @@ nodes: ifindex: 5 ifname: ethernet1/1/5 lag: - _parentindex: 4 + _parentindex: 3 linkindex: 15 mtu: 1500 name: s2 -> h2 @@ -1120,7 +960,7 @@ nodes: ifindex: 6 ifname: ethernet1/1/6 lag: - _parentindex: 4 + _parentindex: 3 linkindex: 16 mtu: 1500 name: s2 -> h2 @@ -1133,7 +973,7 @@ nodes: ifindex: 7 ifname: ethernet1/1/7 lag: - _parentindex: 5 + _parentindex: 4 linkindex: 18 mtu: 1500 name: s2 -> h2 diff --git a/tests/topology/expected/node.clone-plugin-lag.yml b/tests/topology/expected/node.clone-plugin-lag.yml index e71c82dc3..ab79a5e27 100644 --- a/tests/topology/expected/node.clone-plugin-lag.yml +++ b/tests/topology/expected/node.clone-plugin-lag.yml @@ -30,152 +30,6 @@ links: node_count: 2 prefix: false type: p2p -- _linkname: links[2] - bridge: input_2 - interfaces: - - ifindex: 30000 - ifname: port-channel8 - node: r1 - - ifindex: 30000 - ifname: bond8 - node: h-01 - lag: - ifindex: 8 - linkindex: 2 - node_count: 2 - pool: l2only - type: lag -- _linkname: links[3] - bridge: input_3 - interfaces: - - ifindex: 30001 - ifname: port-channel9 - node: r1 - - ifindex: 30000 - ifname: bond9 - node: h-02 - lag: - ifindex: 9 - linkindex: 3 - node_count: 2 - pool: l2only - type: lag -- _linkname: links[4] - bridge: input_4 - interfaces: - - ifindex: 30000 - ifname: port-channel1 - lag: - ifindex: 1 - node: r2 - - ifindex: 30001 - ifname: bond9 - lag: - ifindex: 9 - node: h-01 - lag: {} - linkindex: 4 - node_count: 2 - pool: l2only - type: lag -- _linkname: links[5] - bridge: input_5 - interfaces: - - ifindex: 30001 - ifname: port-channel2 - lag: - ifindex: 2 - node: r2 - - ifindex: 30001 - ifname: bond10 - lag: - ifindex: 10 - node: h-02 - lag: {} - linkindex: 5 - node_count: 2 - pool: l2only - type: lag -- _linkname: links[6] - bridge: input_6 - interfaces: - - _vlan_mode: irb - ifindex: 30000 - ifname: bond1 - ipv4: 172.16.0.5/24 - lag: - ifindex: 1 - node: h2-01 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30002 - ifname: port-channel10 - ipv4: 172.16.0.1/24 - lag: - _mlag: true - node: r1 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30002 - ifname: port-channel10 - ipv4: 172.16.0.2/24 - lag: - _mlag: true - node: r2 - vlan: - access: red - lag: - ifindex: 10 - linkindex: 6 - node_count: 3 - prefix: - allocation: id_based - ipv4: 172.16.0.0/24 - type: lag - vlan: - access: red -- _linkname: links[7] - bridge: input_7 - interfaces: - - _vlan_mode: irb - ifindex: 30000 - ifname: bond1 - ipv4: 172.16.0.6/24 - lag: - ifindex: 1 - node: h2-02 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30003 - ifname: port-channel11 - ipv4: 172.16.0.1/24 - lag: - _mlag: true - node: r1 - vlan: - access: red - - _vlan_mode: irb - ifindex: 30003 - ifname: port-channel11 - ipv4: 172.16.0.2/24 - lag: - _mlag: true - node: r2 - vlan: - access: red - lag: - ifindex: 11 - linkindex: 7 - node_count: 3 - prefix: - allocation: id_based - ipv4: 172.16.0.0/24 - type: lag - vlan: - access: red - _linkname: links[2].lag[1] interfaces: - ifindex: 2 @@ -363,7 +217,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 2 name: h-01 -> r1 neighbors: - ifname: port-channel8 @@ -378,7 +231,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 4 name: h-01 -> r2 neighbors: - ifname: port-channel1 @@ -389,7 +241,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 2 + _parentindex: 8 linkindex: 8 name: h-01 -> r1 neighbors: @@ -399,7 +251,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 2 + _parentindex: 8 linkindex: 9 name: h-01 -> r1 neighbors: @@ -409,7 +261,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 4 + _parentindex: 9 linkindex: 12 name: h-01 -> r2 neighbors: @@ -419,7 +271,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 4 + _parentindex: 9 linkindex: 13 name: h-01 -> r2 neighbors: @@ -459,7 +311,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 3 name: h-02 -> r1 neighbors: - ifname: port-channel9 @@ -474,7 +325,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 5 name: h-02 -> r2 neighbors: - ifname: port-channel2 @@ -485,7 +335,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 3 + _parentindex: 9 linkindex: 10 name: h-02 -> r1 neighbors: @@ -495,7 +345,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 3 + _parentindex: 9 linkindex: 11 name: h-02 -> r1 neighbors: @@ -505,7 +355,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 5 + _parentindex: 10 linkindex: 14 name: h-02 -> r2 neighbors: @@ -515,7 +365,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 5 + _parentindex: 10 linkindex: 15 name: h-02 -> r2 neighbors: @@ -555,7 +405,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 6 name: '[Access VLAN red] h2-01 -> [r1,r2]' neighbors: - ifname: port-channel10 @@ -572,7 +421,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 6 + _parentindex: 1 linkindex: 16 name: h2-01 -> r1 neighbors: @@ -582,7 +431,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 6 + _parentindex: 1 linkindex: 17 name: h2-01 -> r2 neighbors: @@ -652,7 +501,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 7 name: '[Access VLAN red] h2-02 -> [r1,r2]' neighbors: - ifname: port-channel11 @@ -669,7 +517,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 7 + _parentindex: 1 linkindex: 18 name: h2-02 -> r1 neighbors: @@ -679,7 +527,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 7 + _parentindex: 1 linkindex: 19 name: h2-02 -> r2 neighbors: @@ -764,7 +612,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 2 name: r1 -> h-01 neighbors: - ifname: bond8 @@ -779,7 +626,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 3 name: r1 -> h-02 neighbors: - ifname: bond9 @@ -795,7 +641,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 6 name: '[Access VLAN red] r1 -> [h2-01,r2]' neighbors: - ifname: bond1 @@ -817,7 +662,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 7 name: '[Access VLAN red] r1 -> [h2-02,r2]' neighbors: - ifname: bond1 @@ -834,7 +678,7 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 2 + _parentindex: 8 linkindex: 8 name: r1 -> h-01 neighbors: @@ -844,7 +688,7 @@ nodes: - ifindex: 3 ifname: Ethernet3 lag: - _parentindex: 2 + _parentindex: 8 linkindex: 9 name: r1 -> h-01 neighbors: @@ -854,7 +698,7 @@ nodes: - ifindex: 4 ifname: Ethernet4 lag: - _parentindex: 3 + _parentindex: 9 linkindex: 10 name: r1 -> h-02 neighbors: @@ -864,7 +708,7 @@ nodes: - ifindex: 5 ifname: Ethernet5 lag: - _parentindex: 3 + _parentindex: 9 linkindex: 11 name: r1 -> h-02 neighbors: @@ -874,7 +718,7 @@ nodes: - ifindex: 6 ifname: Ethernet6 lag: - _parentindex: 6 + _parentindex: 10 linkindex: 16 name: r1 -> h2-01 neighbors: @@ -884,7 +728,7 @@ nodes: - ifindex: 7 ifname: Ethernet7 lag: - _parentindex: 7 + _parentindex: 11 linkindex: 18 name: r1 -> h2-02 neighbors: @@ -966,7 +810,7 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 6 + _parentindex: 10 linkindex: 17 name: r2 -> h2-01 neighbors: @@ -976,7 +820,7 @@ nodes: - ifindex: 3 ifname: Ethernet3 lag: - _parentindex: 7 + _parentindex: 11 linkindex: 19 name: r2 -> h2-02 neighbors: @@ -1013,17 +857,31 @@ nodes: - ifindex: 6 ifname: Ethernet6 lag: - _parentindex: 4 + _parentindex: 1 linkindex: 12 name: r2 -> h-01 neighbors: - ifname: eth3 node: h-01 type: p2p + - ifindex: 7 + ifname: port-channel1 + lag: + ifindex: 1 + lacp: fast + lacp_mode: active + mode: 802.3ad + name: r2 -> h-01 + neighbors: + - ifname: bond9 + node: h-01 + pool: l2only + type: lag + virtual_interface: true - ifindex: 7 ifname: Ethernet7 lag: - _parentindex: 4 + _parentindex: 1 linkindex: 13 name: r2 -> h-01 neighbors: @@ -1033,7 +891,7 @@ nodes: - ifindex: 8 ifname: Ethernet8 lag: - _parentindex: 5 + _parentindex: 2 linkindex: 14 name: r2 -> h-02 neighbors: @@ -1041,38 +899,12 @@ nodes: node: h-02 type: p2p - ifindex: 9 - ifname: Ethernet9 - lag: - _parentindex: 5 - linkindex: 15 - name: r2 -> h-02 - neighbors: - - ifname: eth4 - node: h-02 - type: p2p - - ifindex: 30000 - ifname: port-channel1 - lag: - ifindex: 1 - lacp: fast - lacp_mode: active - mode: 802.3ad - linkindex: 4 - name: r2 -> h-01 - neighbors: - - ifname: bond9 - node: h-01 - pool: l2only - type: lag - virtual_interface: true - - ifindex: 30001 ifname: port-channel2 lag: ifindex: 2 lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 5 name: r2 -> h-02 neighbors: - ifname: bond10 @@ -1080,7 +912,17 @@ nodes: pool: l2only type: lag virtual_interface: true - - ifindex: 30002 + - ifindex: 9 + ifname: Ethernet9 + lag: + _parentindex: 2 + linkindex: 15 + name: r2 -> h-02 + neighbors: + - ifname: eth4 + node: h-02 + type: p2p + - ifindex: 30000 ifname: port-channel10 lag: _mlag: true @@ -1088,7 +930,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 6 name: '[Access VLAN red] r2 -> [h2-01,r1]' neighbors: - ifname: bond1 @@ -1102,7 +943,7 @@ nodes: vlan: access: red access_id: 1000 - - ifindex: 30003 + - ifindex: 30001 ifname: port-channel11 lag: _mlag: true @@ -1110,7 +951,6 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad - linkindex: 7 name: '[Access VLAN red] r2 -> [h2-02,r1]' neighbors: - ifname: bond1 @@ -1129,7 +969,7 @@ nodes: lacp_mode: active mode: 802.3ad libvirt: - nic_adapter_count: 10 + nic_adapter_count: 12 loopback: ifindex: 0 ifname: Loopback0 From bde450a9bb9722f52c745f34fbe73d672e83ffee Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 14:51:57 -0600 Subject: [PATCH 35/46] Add 'primary' option to select a link as primary --- netsim/ansible/templates/lag/linux.j2 | 3 +++ netsim/extra/bonding/defaults.yml | 2 +- netsim/extra/bonding/linux.j2 | 3 +-- netsim/extra/bonding/plugin.py | 3 +++ tests/integration/lag/11-host-lag-active-standby.yml | 6 +----- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index cd2482a35..059831b5e 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -13,6 +13,9 @@ {% if _m in ["802.3ad","balance-xor","balance-alb","balance-tlb"] %} {% set _m = _m + " xmit_hash_policy encap3+4" %} {% endif %} +{% if params.primary|default("") %} +{% set _m = _m + " primary {{ params.primary }}" %} +{% endif %} {% if node_provider != 'clab' %} # # Make sure 'bonding' module is loaded diff --git a/netsim/extra/bonding/defaults.yml b/netsim/extra/bonding/defaults.yml index b01b629f6..20c8494ed 100644 --- a/netsim/extra/bonding/defaults.yml +++ b/netsim/extra/bonding/defaults.yml @@ -22,4 +22,4 @@ attributes: bonding: ifindex: { type: int, min_value: 0, _required: True } mode: { copy: global } - priority: int # Priority to use this interface, highest priority becomes active + primary: bool # Optional flag to use this interface as primary for bonding diff --git a/netsim/extra/bonding/linux.j2 b/netsim/extra/bonding/linux.j2 index b24b809b7..e4a788775 100644 --- a/netsim/extra/bonding/linux.j2 +++ b/netsim/extra/bonding/linux.j2 @@ -4,12 +4,11 @@ set -e {% for intf in interfaces|default([]) if intf.bonding.mode is defined %} -{{ create_bond_dev(intf,{ 'mode': intf.bonding.mode, 'lacp': 'off' } ) }} +{{ create_bond_dev(intf,{ 'mode': intf.bonding.mode, 'lacp': 'off', 'primary': intf.bonding.primary|default("") } ) }} ip link set dev {{ intf.ifname }} down {% for member in intf.bonding.members %} ip link set dev {{ member }} down ip link set dev {{ member }} master {{ intf.ifname }} -ip link set dev {{ member }} type bond_slave prio {{ intf.bonding.priority|default(0) }} ip link set dev {{ member }} up {% endfor %} ip link set dev {{ intf.ifname }} up diff --git a/netsim/extra/bonding/plugin.py b/netsim/extra/bonding/plugin.py index 5667d9247..340ee9f58 100644 --- a/netsim/extra/bonding/plugin.py +++ b/netsim/extra/bonding/plugin.py @@ -61,6 +61,9 @@ def post_transform(topology: Box) -> None: if att in intf: bonds[node.name][bond_ifindex][att] = intf.pop(att,None) + if intf.get('bonding.primary',False): + bonds[node.name][bond_ifindex]['primary'] = intf.ifname + intf.neighbors = [ { 'ifname': i.ifname, 'node': i.node } for i in link.interfaces if i.node!=node.name ] intf.type = 'p2p' intf.prefix = False # L2 p2p interface diff --git a/tests/integration/lag/11-host-lag-active-standby.yml b/tests/integration/lag/11-host-lag-active-standby.yml index 1bd47ca42..6999ed2a5 100644 --- a/tests/integration/lag/11-host-lag-active-standby.yml +++ b/tests/integration/lag/11-host-lag-active-standby.yml @@ -43,7 +43,7 @@ links: - h2: bonding: ifindex: 1 - priority: 1 # Prefer s2 as active switch for h2 + primary: True # Prefer s2 as active switch for h2 s2: vlan.access: red @@ -54,7 +54,3 @@ validate: wait_msg: Waiting for STP to enable the ports wait: 45 plugin: ping('h1',af='ipv4') - ping_gw: - description: Pinging gateway from H1 - nodes: [h1] - plugin: ping(nodes.s1.interfaces[-1].ipv4,af='ipv4') From 780a022e3f4be0dfc1f1828188ebdd55d646fb7d Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 15:15:13 -0600 Subject: [PATCH 36/46] Final touches Works on Libvirt, bonding mode on clab becomes round-rr for some reason --- netsim/ansible/templates/lag/linux.j2 | 7 +++---- netsim/extra/bonding/plugin.py | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index 059831b5e..1f7c9862e 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -1,6 +1,6 @@ {% macro create_bond_dev(l,params={}) %} {% if l.type=='lag' %} -{% set _m = l.lag.mode|default(params.mode|default("active-backup")) %} +{% set _m = l.lag.mode|default(params.mode|default("802.3ad")) %} {% if _m=="802.3ad" %} {% set _lacp = l.lag.lacp|default(params.lacp|default('fast')) %} {% set lacp_act = 'off' if _lacp=='off' else 'on' %} @@ -9,13 +9,12 @@ {% if node_provider == 'clab' %} {% set _m = _m + " lacp_active " + lacp_act %} {% endif %} +{% elif params.primary|default("") %} +{% set _m = _m + " primary " + params.primary %} {% endif %} {% if _m in ["802.3ad","balance-xor","balance-alb","balance-tlb"] %} {% set _m = _m + " xmit_hash_policy encap3+4" %} {% endif %} -{% if params.primary|default("") %} -{% set _m = _m + " primary {{ params.primary }}" %} -{% endif %} {% if node_provider != 'clab' %} # # Make sure 'bonding' module is loaded diff --git a/netsim/extra/bonding/plugin.py b/netsim/extra/bonding/plugin.py index 340ee9f58..d15db1f4b 100644 --- a/netsim/extra/bonding/plugin.py +++ b/netsim/extra/bonding/plugin.py @@ -21,6 +21,8 @@ def add_bond_interfaces(node: Box, bonds: dict[int,Box]) -> None: 'ifindex': 50000 + c, 'virtual_interface': True } + if 'primary' in bond: + bond_if['bonding']['primary'] = bond['primary'] for af in ['ipv4','ipv6']: if af in bond: bond_if[af] = bond[af] # Take the first one, if any From d64f7673ca26e64e279f7c49c97018b12a3835b8 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 15:30:09 -0600 Subject: [PATCH 37/46] Add docs --- docs/plugins.md | 1 + docs/plugins/bonding.md | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 docs/plugins/bonding.md 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..c34d938b9 --- /dev/null +++ b/docs/plugins/bonding.md @@ -0,0 +1,66 @@ +(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: +* **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* + +## 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 \ No newline at end of file From 3b6d4f656ded82f0f9e7994076a3f17cd8c2d854 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 16:56:43 -0600 Subject: [PATCH 38/46] Fix clab configuration case; don't create bond devices twice --- .../ansible/templates/initial/linux-clab.j2 | 3 ++- .../templates/initial/linux/vanilla.j2 | 2 +- netsim/ansible/templates/lag/linux.j2 | 21 +++++++--------- netsim/extra/bonding/linux.j2 | 12 ++++++++-- netsim/extra/bonding/plugin.py | 24 ++++++++++++------- 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/netsim/ansible/templates/initial/linux-clab.j2 b/netsim/ansible/templates/initial/linux-clab.j2 index b53b58187..5a93ea7c9 100644 --- a/netsim/ansible/templates/initial/linux-clab.j2 +++ b/netsim/ansible/templates/initial/linux-clab.j2 @@ -7,4 +7,5 @@ # /etc/hosts file is generated as a clab bind. # set -e -{% include 'linux/vanilla.j2' +%} +{% set distro = netlab_linux_distro|default('vanilla') %} +{% include 'linux/'+distro+'.j2' +%} diff --git a/netsim/ansible/templates/initial/linux/vanilla.j2 b/netsim/ansible/templates/initial/linux/vanilla.j2 index 6e37698e7..a78344e36 100644 --- a/netsim/ansible/templates/initial/linux/vanilla.j2 +++ b/netsim/ansible/templates/initial/linux/vanilla.j2 @@ -29,7 +29,7 @@ ip -6 addr add {{ loopback.ipv6 }} dev lo # Interface addressing, create any bond devices # {% for l in interfaces|default([]) %} -{% if l.type=='lag' %} +{% if l.type in ['lag','bond'] %} {{ create_bond_dev(l) }} {% endif %} ip link set dev {{ l.ifname }} up diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index 1f7c9862e..ba64e8cae 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -1,16 +1,16 @@ -{% macro create_bond_dev(l,params={}) %} -{% if l.type=='lag' %} -{% set _m = l.lag.mode|default(params.mode|default("802.3ad")) %} +{% macro create_bond_dev(l) %} +{% 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(params.lacp|default('fast')) %} +{% 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 params.primary|default("") %} -{% set _m = _m + " primary " + params.primary %} +{% 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" %} @@ -24,7 +24,7 @@ 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}} +ip link add dev {{l.ifname}} type bond mode {{ _m }} fi {% endif %} {% endmacro -%} @@ -32,13 +32,8 @@ fi # set -e # Exit immediately when any command fails # -# Create bonds for LAGs, if any. Requires kernel bonding module loaded +# Bond devices are created by 'initial' module - add members # -{% for l in interfaces if 'lag' in l %} -{{ create_bond_dev(l,lag) }} -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' %} diff --git a/netsim/extra/bonding/linux.j2 b/netsim/extra/bonding/linux.j2 index e4a788775..df751f040 100644 --- a/netsim/extra/bonding/linux.j2 +++ b/netsim/extra/bonding/linux.j2 @@ -3,8 +3,16 @@ set -e -{% for intf in interfaces|default([]) if intf.bonding.mode is defined %} -{{ create_bond_dev(intf,{ 'mode': intf.bonding.mode, 'lacp': 'off', 'primary': intf.bonding.primary|default("") } ) }} +# Check if the 'ip' command is available, if not install it +if ! command -v ip 2>&1 >/dev/null +then + echo "Trying to install the 'ip' command..." + apt-get -qq update + apt-get -qq install iproute2 -y +fi + +{# Bonding device is created by 'initial' module #} +{% for intf in interfaces|default([]) if intf.bonding.members is defined %} ip link set dev {{ intf.ifname }} down {% for member in intf.bonding.members %} ip link set dev {{ member }} down diff --git a/netsim/extra/bonding/plugin.py b/netsim/extra/bonding/plugin.py index d15db1f4b..3d00a8422 100644 --- a/netsim/extra/bonding/plugin.py +++ b/netsim/extra/bonding/plugin.py @@ -12,8 +12,13 @@ def add_bond_interfaces(node: Box, bonds: dict[int,Box]) -> None: for c,(ifindex,bond) in enumerate(bonds.items()): ifname = f'bond{ifindex}' # XXX hardcoded, could make this a device template attribute + + # + # TODO: Could make sure the layout is exactly like the 'lag' module needs it, to reuse provisioning logic + # That means converting 'bonding.ifindex' to 'lag._parentindex' too! + # bond_if = { - 'type': 'lag', # TODO "bond"? + 'type': 'bond', 'ifname': ifname, 'name': f'bond {ifindex}', 'bonding': { 'ifindex': ifindex, 'members': bond['members'], 'mode': bond.mode }, @@ -34,6 +39,7 @@ def add_bond_interfaces(node: Box, bonds: dict[int,Box]) -> None: Apply plugin config to nodes with interfaces marked with 'bonding.ifindex', for devices that support this plugin ''' def post_transform(topology: Box) -> None: +# def post_link_transform(topology: Box) -> None: # Use 'pre_node_transform' such that only 1 IP address gets assigned, nothing to move? global _config_name bond_mode = topology.get('bonding.mode','active-backup') bonds : Box = data.get_empty_box() # Map of bonds per node, indexed by bonding.ifindex @@ -71,14 +77,14 @@ def post_transform(topology: Box) -> None: intf.prefix = False # L2 p2p interface intf.pop('name',None) - for node in topology.nodes.values(): - if node.name in bonds: - for bond in bonds[node.name].values(): - for i in bond.interfaces: - if i.node in bonds: - for i2,b2 in bonds[i.node].items(): - if i.ifname in b2['members']: - i.ifname = f'bond{i2}' # Correct neighbor name + for node in topology.nodes.values(): # For each node + if node.name in bonds: # ...that has 1 or more bonds + for bond in bonds[node.name].values(): # ...for each bond + for i in bond.interfaces: # ...for each interface of that bond + if i.node in bonds: # ...check if the node also has bonds + for i2,b2 in bonds[i.node].items(): # If so, for each such bond + if i.ifname in b2['members']: # if the interface connecting to is a member + i.ifname = f'bond{i2}' # Set correct neighbor device name to bond continue add_bond_interfaces(node,bonds[node.name]) api.node_config(node,_config_name) # Remember that we have to do extra configuration From fe428abc209d20250eb349172e810773a1743a9c Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:01:49 -0600 Subject: [PATCH 39/46] Undo changes --- netsim/ansible/templates/initial/linux-clab.j2 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/netsim/ansible/templates/initial/linux-clab.j2 b/netsim/ansible/templates/initial/linux-clab.j2 index 5a93ea7c9..b53b58187 100644 --- a/netsim/ansible/templates/initial/linux-clab.j2 +++ b/netsim/ansible/templates/initial/linux-clab.j2 @@ -7,5 +7,4 @@ # /etc/hosts file is generated as a clab bind. # set -e -{% set distro = netlab_linux_distro|default('vanilla') %} -{% include 'linux/'+distro+'.j2' +%} +{% include 'linux/vanilla.j2' +%} From 2a5721f1b7e7299d0615e90de49fb3d1771ba2e2 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:03:22 -0600 Subject: [PATCH 40/46] Install both ethtool and iproute2 --- netsim/extra/bonding/linux.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netsim/extra/bonding/linux.j2 b/netsim/extra/bonding/linux.j2 index df751f040..6b6d89301 100644 --- a/netsim/extra/bonding/linux.j2 +++ b/netsim/extra/bonding/linux.j2 @@ -6,9 +6,9 @@ set -e # Check if the 'ip' command is available, if not install it if ! command -v ip 2>&1 >/dev/null then - echo "Trying to install the 'ip' command..." + echo "Trying to install the 'ip' and 'ethtool' commands..." apt-get -qq update - apt-get -qq install iproute2 -y + apt-get -qq install ethtool iproute2 fi {# Bonding device is created by 'initial' module #} From 7ee9bbd3a3d1e21b8cf3b4fa5bc6f78ee0f72343 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:25:35 -0600 Subject: [PATCH 41/46] Undo merge with LAG PR --- netsim/defaults/attributes.yml | 2 +- netsim/modules/lag.py | 236 ++++++---------- netsim/modules/lag.yml | 13 +- tests/topology/expected/lag-l2.yml | 25 ++ .../topology/expected/lag-l3-access-vlan.yml | 35 +++ tests/topology/expected/lag-l3-vlan-trunk.yml | 34 +++ tests/topology/expected/lag-l3.yml | 33 ++- tests/topology/expected/lag-mlag-m_to_m.yml | 150 +++++++--- tests/topology/expected/lag-mlag.yml | 214 ++++++++++++-- .../expected/node.clone-plugin-lag.yml | 260 ++++++++++++++---- 10 files changed, 722 insertions(+), 280 deletions(-) diff --git a/netsim/defaults/attributes.yml b/netsim/defaults/attributes.yml index b407de893..ff247320c 100644 --- a/netsim/defaults/attributes.yml +++ b/netsim/defaults/attributes.yml @@ -60,7 +60,7 @@ link: # Global link attributes _alt_types: [ bool_false, prefix_str, named_pfx ] role: id pool: id - type: { type: str, valid_values: [ lan, p2p, stub, loopback, tunnel, vlan_member, virtual_lag ] } + type: { type: str, valid_values: [ lan, p2p, stub, loopback, tunnel, vlan_member, lag ] } unnumbered: bool interfaces: mtu: { type: int, min_value: 64, max_value: 65535 } diff --git a/netsim/modules/lag.py b/netsim/modules/lag.py index 5c84d7efb..3eb5abd53 100644 --- a/netsim/modules/lag.py +++ b/netsim/modules/lag.py @@ -38,11 +38,11 @@ def populate_peerlink_id_set(topology: Box) -> None: create_l2_link_base - Create a L2 P2P link as base for member links """ def create_l2_link_base(l: Box, topology: Box) -> Box: - l2_linkdata = data.get_box({ 'type': "p2p", 'prefix': False, 'lag': {} }) # Construct an L2 member link - for a in list(topology.defaults.lag.attributes.lag_l2_linkattr): + l2_ifdata = data.get_box({ 'type': "p2p", 'prefix': False, 'lag': {} }) # Construct an L2 member link + for a in list(topology.defaults.lag.attributes.lag_l2_ifattr): if a in l: - l2_linkdata[a] = l[a] - return l2_linkdata + l2_ifdata[a] = l[a] + return l2_ifdata """ check_lag_config - check if the given node supports lag and has the module enabled @@ -144,55 +144,28 @@ def set_lag_ifindex(laglink: Box, intf: Box, is_mside: bool, topology: Box) -> b """ split_dual_mlag_link - Split dual-mlag pairs into 2 lag link groups, returns the new link """ -def split_dual_mlag_link(link: Box, topology: Box) -> None: +def split_dual_mlag_link(link: Box, topology: Box) -> Box: def no_peer(i: Box) -> Box: i.pop('_peer',None) # Remove internal _peer attribute - return i - + return i + split_copy = data.get_box(link) # Make a copy split_copy.linkindex = len(topology.links)+1 # Update its link index split_copy._linkname = split_copy._linkname + "-2" # Assign unique name + split_copy.lag.pop('members',None) # Clean up members first_pair = ( link.interfaces[0].node, link.interfaces[0]._peer ) split_copy.interfaces = [ no_peer(i) for i in link.interfaces if i.node in first_pair ] topology.links[link.linkindex-1].interfaces = [ no_peer(i) for i in link.interfaces if i.node not in first_pair ] - - for l in topology.links: - if 'lag' in l: - nodes = [ i.node for i in l.interfaces ] - if first_pair[0] in nodes and first_pair[1] in nodes: - l.lag._parentindex = split_copy.linkindex - if log.debug_active('lag'): print(f'LAG split_dual_mlag_links -> adding split link {split_copy}') print(f'LAG split_dual_mlag_links -> remaining link {topology.links[link.linkindex-1]}') topology.links.append(split_copy) + return split_copy """ create_lag_member_links -- expand lag.members for link l and create physical p2p links """ -def create_lag_member_links(l: Box, topology: Box) -> bool: - members = normalized_members(l,topology) # Build list of normalized member links - if not members: - return False - l.lag.members = members # Update for create_lag_interfaces - - l2_linkdata = create_l2_link_base(l,topology) - keep_attr = list(topology.defaults.lag.attributes.lag_member_linkattr) - keep_if = ['node','ifindex'] # Keep only 'node' and optional 'ifindex' - for member in members: - member = l2_linkdata + { k:v for k,v in member.items() if k in keep_attr } - member.linkindex = len(topology.links)+1 - member.interfaces = [ { k:v for k,v in i.items() if k in keep_if } for i in member.interfaces ] - member.lag._parentindex = l.linkindex # Keep track of parent, updated to lag.ifindex below - if log.debug_active('lag'): - print(f'LAG create_lag_member_links -> adding link {member}') - topology.links.append(member) - return True - -""" -create_lag_interfaces -- create interfaces of type "lag" for each link of type virtual_lag -""" -def create_lag_interfaces(l: Box, topology: Box) -> None: +def create_lag_member_links(l: Box, topology: Box) -> None: """ analyze_lag - figure out which type of LAG we're dealing with: @@ -202,19 +175,19 @@ def create_lag_interfaces(l: Box, topology: Box) -> None: Returns updated node_count set, bool is_mlag, bool dual_mlag, string one_side """ - def analyze_lag(node_count: dict) -> tuple[bool,bool,str]: - for m in l.lag.members: + def analyze_lag(members: list, node_count: dict) -> tuple[bool,bool,str]: + for m in members: for i in m.interfaces: if i.node in node_count: node_count[ i.node ] = node_count[ i.node ] + 1 else: node_count[ i.node ] = 1 - + if len(node_count)==2: # Regular LAG between 2 nodes return (False,False,"") elif len(node_count)==3: # 1:2 MLAG or weird MLAG triangle for node_name,count in node_count.items(): - if count==len(l.lag.members): + if count==len(members): return (True,False,node_name) # Found the 1-side node elif len(node_count)==4: # 2:2 dual MLAG return (True,True,"") @@ -225,28 +198,23 @@ def analyze_lag(node_count: dict) -> tuple[bool,bool,str]: module='lag') return (False,False,"") + members = normalized_members(l,topology) # Build list of normalized member links + if not members: + return node_count: dict[str,int] = {} # Count how many times nodes are used - is_mlag, dual_mlag, one_side = analyze_lag(node_count) + is_mlag, dual_mlag, one_side = analyze_lag(members,node_count) if one_side=="": # Check for errors return - members = l.pop('lag.members',[]) # Remove lag.members - skip_atts = list(topology.defaults.lag.attributes.lag_no_propagate) - copy_link_to_intf = ['vlan'] if 'vlan' in l else list(topology.defaults.lag.attributes.copy_link_to_intf) - link_atts = { k:v for k,v in l.items() if k in copy_link_to_intf } l.interfaces = [] # Build interface list for lag link + skip_atts = list(topology.defaults.lag.attributes.lag_no_propagate) for node in node_count: - ifatts = data.get_box({ 'node': node, '_type': 'lag', 'lag': {} }) # use '_type', not 'type' (!) + ifatts = data.get_box({ 'node': node }) for m in members: # Collect attributes from member links - node_ifs = [ i for i in m.interfaces if i.node==node ] - if not node_ifs: # ...in which is involved - continue - ifatts = ifatts + { k:v for k,v in m.items() if k not in skip_atts } + node_ifs[0] - if dual_mlag: - ifatts._peer = [ i.node for i in m.interfaces if i.node!=node ][0] - if not 'vlan' in ifatts: # VLAN on interface overrides link IP settings - ifatts = link_atts + ifatts # include vlan, gateway or prefix settings from link - + if node in [ i.node for i in m.interfaces ]:# ...in which is involved + ifatts = ifatts + { k:v for k,v in m.items() if k not in skip_atts } + if dual_mlag: + ifatts._peer = [ i.node for i in m.interfaces if i.node!=node ][0] is_mside = is_mlag and node!=one_side # Set flag if this node is the M: side if not set_lag_ifindex(l,ifatts,is_mside,topology): return @@ -259,16 +227,28 @@ def analyze_lag(node_count: dict) -> tuple[bool,bool,str]: ifatts.lag._mlag = True # Set internal flag if log.debug_active('lag'): - print(f'LAG create_lag_interfaces for node {node} -> adding interface {ifatts} skip={skip_atts}') + print(f'LAG create_lag_member_links for node {node} -> collected ifatts {ifatts}') l.interfaces.append( ifatts ) - if dual_mlag: # After creating interfaces, check if we need to split them - split_dual_mlag_link(l,topology) + _sl = split_dual_mlag_link(l,topology) if dual_mlag else None + split_nodes = [ i.node for i in _sl.interfaces ] if _sl else [] + + l2_ifdata = create_l2_link_base(l,topology) + keep_attr = list(topology.defaults.lag.attributes.lag_member_ifattr) + for member in members: + member = l2_ifdata + member # Copy L2 data into member link + member = data.get_box({ k:v for k,v in member.items() if k in keep_attr }) # Filter out things not needed + member.linkindex = len(topology.links)+1 + parent = _sl if _sl and member.interfaces[0].node in split_nodes else l + member.lag._parentindex = parent.linkindex # Keep track of parent + if log.debug_active('lag'): + print(f'LAG create_lag_member_links -> adding link {member}') + topology.links.append(member) """ create_peer_links -- creates and configures physical link(s) for given peer link """ -def create_peer_links(l: Box, topology: Box) -> None: +def create_peer_links(l: Box, topology: Box) -> bool: """ check_same_pair - Verifies that the given member connects the same pair of nodes as the first @@ -284,10 +264,10 @@ def check_same_pair(member: Box) -> bool: return True members = normalized_members(l,topology,peerlink=True) - l2_linkdata = create_l2_link_base(l,topology) + l2_ifdata = create_l2_link_base(l,topology) for idx,member in enumerate(members): - member = l2_linkdata + member # Copy L2 data into member link + member = l2_ifdata + member # Copy L2 data into member link if idx==0: # For the first member, use the existing link topology.links[l.linkindex-1] = l + member # Update topology (l is a copy) first_pair = [ i.node for i in member.interfaces ] @@ -296,47 +276,50 @@ def check_same_pair(member: Box) -> bool: log.error(f'Nodes {first_pair} on MLAG peerlink {member._linkname} have different device types ({_devs})', category=log.IncorrectValue, module='lag') - return + return False for node in first_pair: if not check_mlag_support(node,l._linkname,topology): - return + return False if log.debug_active('lag'): print(f'LAG create_peer_links -> updated first link {l} from {member} -> {topology.links[l.linkindex-1]}') else: if not check_same_pair(member): # Check that any additional links connect the same nodes - return + return False member.linkindex = len(topology.links)+1 - member.lag._peerlink = l.linkindex # Keep track of parent + member.lag._parentindex = l.linkindex # Keep track of parent if log.debug_active('lag'): print(f'LAG create_peer_links -> adding link {member}') topology.links.append(member) - - topology.links[l.linkindex-1].pop("lag.members",None) # Cleanup + + return True """ -process_lag_link - process link with 'lag' attribute to create links for lag.members - Returns True iff a virtual_lag was created +process_lag_links - process all links with 'lag' attribute """ -def process_lag_link(link: Box, topology: Box) -> bool: - if not 'members' in link.lag: - log.error(f'must define "lag.members" on LAG link {link._linkname}', - category=log.IncorrectAttr, - module='lag') - return False - elif not _types.must_be_list(parent=link.lag,key='members',path=link._linkname,module='lag'): - return False - - peerlink_id = link.get(PEERLINK_ID_ATT,None) # Turn internal MLAG links into p2p links - if peerlink_id: - if peerlink_id is True: # Auto-assign peerlink ID if requested - link[PEERLINK_ID_ATT] = _dataplane.get_next_id(PEERLINK_ID_SET) - link.type = 'p2p' - link.prefix = False # L2-only - create_peer_links(link,topology) - return False - else: - link.type = 'virtual_lag' # Temporary virtual link, removed in module_post_link_transform - return create_lag_member_links(link,topology) +def process_lag_links(topology: Box) -> None: + for l in list(topology.links): # Make a copy of the list, gets modified + if 'lag' not in l: + continue + elif not 'members' in l.lag: + log.error(f'must define "lag.members" on LAG link {l._linkname}', + category=log.IncorrectAttr, + module='lag') + continue + elif not _types.must_be_list(parent=l.lag,key='members',path=l._linkname,module='lag'): + continue + + peerlink_id = l.get(PEERLINK_ID_ATT,None) # Turn internal MLAG links into p2p links + if peerlink_id: + if peerlink_id is True: # Auto-assign peerlink ID if requested + l[PEERLINK_ID_ATT] = _dataplane.get_next_id(PEERLINK_ID_SET) + l.type = 'p2p' + l.prefix = False # L2-only + if not create_peer_links(l,topology): # Check for errors + return + else: + l.type = 'lag' + create_lag_member_links(l,topology) + topology.links[l.linkindex-1].lag.pop("members",None) # # populate_mlag_peer - Lookup the IPv4 loopback address for the mlag peer, and derive a virtual MAC to use @@ -359,71 +342,31 @@ def populate_mlag_peer(node: Box, intf: Box, topology: Box) -> None: _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 + intf.lag.mlag.self = f"{_net[1-_id]}/{_net.prefixlen}" # including /prefix 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 + _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) for v in ['vlan','ifindex']: if v in _mlag_peer: intf.lag.mlag[v] = _mlag_peer[v] - - intf.pop('vlan',None) # Remove any VLANs provisioned on peerlinks - -""" -configure_lag_mode - Check if the given lag mode configuration supports LACP, if not remove it -""" -def configure_lag_mode(intf: Box, topology: Box) -> None: - lag_mode = intf.get('lag.mode',topology.defaults.lag.mode) - if lag_mode != "802.3ad": - intf.pop('lag.lacp',None) - intf.pop('lag.lacp_mode',None) + + intf.pop('vlan',None) # Remove any VLANs provisioned on peerlinks class LAG(_Module): """ - module_pre_transform -- Analyze any user provided lag.ifindex values and peerlink ids, - convert lag.members to links and create 'lag' type interfaces - - Note: link.interfaces must be populated before vlan.link_pre_transform is called + module_pre_transform -- Add links listed in each lag.members to the topology """ def module_pre_transform(self, topology: Box) -> None: if log.debug_active('lag'): - print(f'LAG module_pre_transform: Convert lag.members into additional topology.links') - - if not 'links' in topology: - return - + print(f'LAG module_pre_transform') populate_lag_id_set(topology) populate_peerlink_id_set(topology) - for link in list(topology.links): # Make a copy, may get modified - if 'lag' in link: - if process_lag_link(link,topology): - create_lag_interfaces(link,topology) # Create lag interfaces - - """ - link_pre_link_transform - rename interface '_type' to 'type' (after validation) - - The interface 'type' attribute is added internally, and cannot be defined in the data model. - It should have been called '_type' to begin with, but that ship has sailed a while ago. This module - implements a workaround by calling it '_type' during validation (allowing it to be skipped), and then - renaming it to 'type' here - """ - def link_pre_link_transform(self, link: Box, topology: Box) -> None: - for intf in link.interfaces: - if intf.get('_type',None)=='lag': - intf.type = intf.pop('_type') - - """ - module_post_link_transform - remove temporary 'virtual_lag' links - """ - def module_post_link_transform(self, topology: Box) -> None: - if log.debug_active('lag'): - print(f'LAG module_post_link_transform: Cleanup "virtual_lag" links') - topology.links = [ link for link in topology.links if link.type != 'virtual_lag' ] + process_lag_links(topology) # Expand lag.members into additional p2p links """ After attribute propagation and consolidation, verify that requested features are supported. @@ -436,26 +379,19 @@ def node_post_transform(self, node: Box, topology: Box) -> None: has_peerlink = False uses_mlag = False for i in node.interfaces: - if i.get(PEERLINK_ID_ATT,None): # Fill in peer loopback IP and vMAC for MLAG peer links + if i.get(PEERLINK_ID_ATT,None): # Fill in peer loopback IP and vMAC for MLAG peer links populate_mlag_peer(node,i,topology) has_peerlink = True - elif i.get('type',None)=='lag': - i.lag = node.get('lag',{}) + i.lag # Merge node level settings with interface overrides - i.pop('mtu',None) # Remove any MTU settings - inherited from members - linkindex = i.pop('linkindex',None) # Remove linkindex (copied by create_node_interfaces) - for m in node.interfaces: # Update members to point to lag.ifindex, replacing linkindex - if m.get('lag._parentindex',None)==linkindex: - m.lag._parentindex = i.lag.ifindex # Make _parentindex point to lag.ifindex instead - - configure_lag_mode(i,topology) - lacp_mode = i.get('lag.lacp_mode') # Inheritance copying is done elsewhere + elif i.type=='lag': + i.lag = node.get('lag',{}) + i.lag # Merge node level settings with interface overrides + lacp_mode = i.get('lag.lacp_mode') # Inheritance copying is done elsewhere 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}', category=log.IncorrectAttr, module='lag') if i.lag.get('mlag',False) is True: uses_mlag = True - + if uses_mlag and not has_peerlink: log.error(f'Node {node.name} uses MLAG but has no peerlink (lag with {PEERLINK_ID_ATT}) configured', category=log.IncorrectAttr, diff --git a/netsim/modules/lag.yml b/netsim/modules/lag.yml index 37490be00..5ba974dc9 100644 --- a/netsim/modules/lag.yml +++ b/netsim/modules/lag.yml @@ -38,26 +38,21 @@ attributes: # _mlag: bool # Copy only these L2 attributes into LAG physical link members - lag_l2_linkattr: + lag_l2_ifattr: mtu: bandwidth: # Keep only these attributes on member links - lag_member_linkattr: + lag_member_ifattr: mtu: bandwidth: + type: + prefix: _linkname: interfaces: - # prefix: # Don't copy these attributes to lag interfaces lag_no_propagate: interfaces: _linkname: name: - mtu: - - # Copy these attributes from lag links into lag interface-on-link data (if not existing) - copy_link_to_intf: - gateway: - pool: diff --git a/tests/topology/expected/lag-l2.yml b/tests/topology/expected/lag-l2.yml index 22e52584f..0499a8fa2 100644 --- a/tests/topology/expected/lag-l2.yml +++ b/tests/topology/expected/lag-l2.yml @@ -6,6 +6,27 @@ lag: lacp_mode: active mode: 802.3ad links: +- _linkname: links[1] + bridge: input_1 + interfaces: + - ifindex: 30000 + ifname: port-channel1 + lag: + ifindex: 1 + node: r1 + - ifindex: 30000 + ifname: port-channel1 + lag: + ifindex: 1 + node: r2 + lag: {} + linkindex: 1 + mtu: 1600 + node_count: 2 + prefix: false + stp: + enable: false + type: lag - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -62,6 +83,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1600 name: r1 -> r2 neighbors: - ifname: port-channel1 @@ -141,6 +164,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1600 name: r2 -> r1 neighbors: - ifname: port-channel1 diff --git a/tests/topology/expected/lag-l3-access-vlan.yml b/tests/topology/expected/lag-l3-access-vlan.yml index d53a3f241..3e2d8927a 100644 --- a/tests/topology/expected/lag-l3-access-vlan.yml +++ b/tests/topology/expected/lag-l3-access-vlan.yml @@ -6,6 +6,37 @@ lag: lacp_mode: active mode: 802.3ad links: +- _linkname: links[1] + bridge: input_1 + interfaces: + - _vlan_mode: irb + ifindex: 30000 + ifname: bond1 + ipv4: 172.16.0.1/24 + lag: + ifindex: 1 + node: r1 + vlan: + access: v1 + - _vlan_mode: irb + ifindex: 30000 + ifname: bond1 + ipv4: 172.16.0.2/24 + lag: + ifindex: 1 + node: r2 + vlan: + access: v1 + lag: + lacp: 'off' + linkindex: 1 + node_count: 2 + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + type: lag + vlan: + access: v1 - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -76,6 +107,8 @@ nodes: lacp: 'off' lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1500 name: '[Access VLAN v1] r1 -> r2' neighbors: - ifname: bond1 @@ -186,6 +219,8 @@ nodes: lacp: 'off' lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1500 name: '[Access VLAN v1] r2 -> r1' neighbors: - ifname: bond1 diff --git a/tests/topology/expected/lag-l3-vlan-trunk.yml b/tests/topology/expected/lag-l3-vlan-trunk.yml index fcdc00ccb..f85f9de2e 100644 --- a/tests/topology/expected/lag-l3-vlan-trunk.yml +++ b/tests/topology/expected/lag-l3-vlan-trunk.yml @@ -6,6 +6,36 @@ lag: lacp_mode: active mode: 802.3ad links: +- _linkname: links[1] + bridge: input_1 + interfaces: + - ifindex: 30000 + ifname: bond1 + lag: + ifindex: 1 + node: r1 + vlan: + trunk: + v1: {} + v2: {} + - ifindex: 30000 + ifname: bond1 + lag: + ifindex: 1 + node: r2 + vlan: + trunk: + v1: {} + v2: {} + lag: {} + linkindex: 1 + node_count: 2 + prefix: {} + type: lag + vlan: + trunk: + v1: {} + v2: {} - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -76,6 +106,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1500 name: r1 -> r2 neighbors: - ifname: bond1 @@ -232,6 +264,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1500 name: r2 -> r1 neighbors: - ifname: bond1 diff --git a/tests/topology/expected/lag-l3.yml b/tests/topology/expected/lag-l3.yml index 2b0f4039c..22342110c 100644 --- a/tests/topology/expected/lag-l3.yml +++ b/tests/topology/expected/lag-l3.yml @@ -6,6 +6,27 @@ lag: lacp_mode: active mode: 802.3ad links: +- _linkname: links[1] + bridge: input_1 + interfaces: + - ifindex: 30000 + ifname: port-channel1 + ipv4: 10.1.0.1/30 + lag: + ifindex: 1 + node: r1 + - ifindex: 30000 + ifname: port-channel1 + ipv4: 10.1.0.2/30 + lag: + ifindex: 1 + node: r2 + lag: {} + linkindex: 1 + node_count: 2 + prefix: + ipv4: 10.1.0.0/30 + type: lag - _linkname: links[1].lag[1] interfaces: - ifindex: 1 @@ -51,16 +72,18 @@ nodes: interfaces: - ifindex: 30000 ifname: port-channel1 - ipv4: 172.16.0.1/24 + ipv4: 10.1.0.1/30 lag: ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1500 name: r1 -> r2 neighbors: - ifname: port-channel1 - ipv4: 172.16.0.2/24 + ipv4: 10.1.0.2/30 node: r2 type: lag virtual_interface: true @@ -122,16 +145,18 @@ nodes: interfaces: - ifindex: 30000 ifname: port-channel1 - ipv4: 172.16.0.2/24 + ipv4: 10.1.0.2/30 lag: ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 1 + mtu: 1500 name: r2 -> r1 neighbors: - ifname: port-channel1 - ipv4: 172.16.0.1/24 + ipv4: 10.1.0.1/30 node: r1 type: lag virtual_interface: true diff --git a/tests/topology/expected/lag-mlag-m_to_m.yml b/tests/topology/expected/lag-mlag-m_to_m.yml index b5c513c02..cbd4a9499 100644 --- a/tests/topology/expected/lag-mlag-m_to_m.yml +++ b/tests/topology/expected/lag-mlag-m_to_m.yml @@ -52,17 +52,77 @@ links: node_count: 2 prefix: false type: p2p +- _linkname: links[3] + bridge: input_3 + interfaces: + - ifindex: 30000 + ifname: port-channel1 + lag: + _mlag: true + node: a2 + vlan: + trunk: + red: {} + - ifindex: 30000 + ifname: port-channel1 + lag: + _mlag: true + node: b2 + vlan: + trunk: + red: {} + lag: + ifindex: 1 + linkindex: 3 + node_count: 2 + prefix: {} + type: lag + vlan: + trunk: + red: {} +- _linkname: links[3]-2 + bridge: input_4 + interfaces: + - ifindex: 30000 + ifname: port-channel1 + lag: + _mlag: true + node: a1 + vlan: + trunk: + red: {} + - ifindex: 30000 + ifname: port-channel1 + lag: + _mlag: true + node: b1 + vlan: + trunk: + red: {} + lag: + ifindex: 1 + linkindex: 4 + node_count: 2 + prefix: {} + type: lag + vlan: + trunk: + red: {} - _linkname: links[3].lag[1] interfaces: - ifindex: 2 ifname: Ethernet2 + lag: + ifindex: 10 node: a1 - ifindex: 2 ifname: Ethernet2 + lag: + ifindex: 20 node: b1 lag: - _parentindex: 6 - linkindex: 4 + _parentindex: 4 + linkindex: 5 node_count: 2 prefix: false type: p2p @@ -70,13 +130,17 @@ links: interfaces: - ifindex: 2 ifname: Ethernet2 + lag: + ifindex: 10 node: a2 - ifindex: 2 ifname: Ethernet2 + lag: + ifindex: 20 node: b2 lag: _parentindex: 3 - linkindex: 5 + linkindex: 6 node_count: 2 prefix: false type: p2p @@ -155,27 +219,18 @@ nodes: - ifname: Ethernet1 node: a2 type: p2p - - ifindex: 2 - ifname: Ethernet2 - lag: - _parentindex: 10 - linkindex: 4 - name: a1 -> b1 - neighbors: - - ifname: Ethernet2 - node: b1 - type: p2p - ifindex: 30000 - ifname: port-channel10 + ifname: port-channel1 lag: _mlag: true - ifindex: 10 + ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 4 name: a1 -> b1 neighbors: - - ifname: port-channel20 + - ifname: port-channel1 node: b1 type: lag virtual_interface: true @@ -184,6 +239,17 @@ nodes: red: {} trunk_id: - 1000 + - ifindex: 2 + ifname: Ethernet2 + lag: + _parentindex: 4 + ifindex: 10 + linkindex: 5 + name: a1 -> b1 + neighbors: + - ifname: Ethernet2 + node: b1 + type: p2p - bridge: input_7 ifindex: 3 ifname: Ethernet3 @@ -275,16 +341,17 @@ nodes: node: a1 type: p2p - ifindex: 30000 - ifname: port-channel10 + ifname: port-channel1 lag: _mlag: true - ifindex: 10 + ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 3 name: a2 -> b2 neighbors: - - ifname: port-channel20 + - ifname: port-channel1 node: b2 type: lag virtual_interface: true @@ -296,8 +363,9 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 10 - linkindex: 5 + _parentindex: 3 + ifindex: 10 + linkindex: 6 name: a2 -> b2 neighbors: - ifname: Ethernet2 @@ -380,27 +448,18 @@ nodes: - ifname: Ethernet1 node: b2 type: p2p - - ifindex: 2 - ifname: Ethernet2 - lag: - _parentindex: 20 - linkindex: 4 - name: b1 -> a1 - neighbors: - - ifname: Ethernet2 - node: a1 - type: p2p - ifindex: 30000 - ifname: port-channel20 + ifname: port-channel1 lag: _mlag: true - ifindex: 20 + ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 4 name: b1 -> a1 neighbors: - - ifname: port-channel10 + - ifname: port-channel1 node: a1 type: lag virtual_interface: true @@ -409,6 +468,17 @@ nodes: red: {} trunk_id: - 1000 + - ifindex: 2 + ifname: Ethernet2 + lag: + _parentindex: 4 + ifindex: 20 + linkindex: 5 + name: b1 -> a1 + neighbors: + - ifname: Ethernet2 + node: a1 + type: p2p - bridge_group: 1 ifindex: 4 ifname: Vlan1000 @@ -487,16 +557,17 @@ nodes: node: b1 type: p2p - ifindex: 30000 - ifname: port-channel20 + ifname: port-channel1 lag: _mlag: true - ifindex: 20 + ifindex: 1 lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 3 name: b2 -> a2 neighbors: - - ifname: port-channel10 + - ifname: port-channel1 node: a2 type: lag virtual_interface: true @@ -508,8 +579,9 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 20 - linkindex: 5 + _parentindex: 3 + ifindex: 20 + linkindex: 6 name: b2 -> a2 neighbors: - ifname: Ethernet2 diff --git a/tests/topology/expected/lag-mlag.yml b/tests/topology/expected/lag-mlag.yml index c4e04e63a..3ae998882 100644 --- a/tests/topology/expected/lag-mlag.yml +++ b/tests/topology/expected/lag-mlag.yml @@ -32,6 +32,144 @@ links: node_count: 2 prefix: false type: p2p +- _linkname: links[2] + bridge: input_2 + interfaces: + - _vlan_mode: irb + ifindex: 30000 + ifname: bond1 + ipv4: 172.16.0.3/24 + lag: + ifindex: 1 + node: h1 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30000 + ifname: port-channel1 + ipv4: 172.16.0.1/24 + lag: + _mlag: true + node: s1 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30000 + ifname: port-channel1 + ipv4: 172.16.0.2/24 + lag: + _mlag: true + node: s2 + vlan: + access: red + lag: + ifindex: 1 + linkindex: 2 + node_count: 3 + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + type: lag + vlan: + access: red +- _linkname: links[3] + bridge: input_3 + interfaces: + - ifindex: 30001 + ifname: bond2 + lag: + ifindex: 2 + node: h1 + - ifindex: 30001 + ifname: port-channel2 + lag: + ifindex: 2 + node: s1 + lag: {} + linkindex: 3 + node_count: 2 + pool: l2only + type: lag +- _linkname: links[4] + bridge: input_4 + interfaces: + - _vlan_mode: irb + ifindex: 30000 + ifname: bond1 + ipv4: 172.16.0.4/24 + lag: + ifindex: 1 + node: h2 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30002 + ifname: port-channel3 + ipv4: 172.16.0.1/24 + lag: + _mlag: true + node: s1 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30001 + ifname: port-channel3 + ipv4: 172.16.0.2/24 + lag: + _mlag: true + node: s2 + vlan: + access: red + lag: + ifindex: 3 + linkindex: 4 + node_count: 3 + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + type: lag + vlan: + access: red +- _linkname: links[5] + bridge: input_5 + interfaces: + - _vlan_mode: irb + ifindex: 30001 + ifname: bond2 + ipv4: 172.16.0.4/24 + lag: + ifindex: 2 + node: h2 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30003 + ifname: port-channel4 + ipv4: 172.16.0.1/24 + lag: + _mlag: true + node: s1 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30002 + ifname: port-channel4 + ipv4: 172.16.0.2/24 + lag: + _mlag: true + node: s2 + vlan: + access: red + lag: + ifindex: 4 + linkindex: 5 + node_count: 3 + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + type: lag + vlan: + access: red - _linkname: links[1].peerlink[2] interfaces: - ifindex: 2 @@ -41,7 +179,7 @@ links: ifname: ethernet1/1/2 node: s1 lag: - _peerlink: 1 + _parentindex: 1 linkindex: 6 node_count: 2 prefix: false @@ -242,6 +380,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 2 + mtu: 1500 name: '[Access VLAN red] h1 -> [s1,s2]' neighbors: - ifname: port-channel1 @@ -262,6 +402,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 3 + mtu: 1500 name: h1 -> s1 neighbors: - ifname: port-channel2 @@ -272,7 +414,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 7 mtu: 1500 name: h1 -> s1 @@ -283,7 +425,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 8 mtu: 1500 name: h1 -> s1 @@ -294,7 +436,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 9 mtu: 1500 name: h1 -> s2 @@ -305,7 +447,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 10 mtu: 1500 name: h1 -> s2 @@ -316,7 +458,7 @@ nodes: - ifindex: 5 ifname: eth5 lag: - _parentindex: 2 + _parentindex: 3 linkindex: 11 mtu: 1500 name: h1 -> s1 @@ -327,7 +469,7 @@ nodes: - ifindex: 6 ifname: eth6 lag: - _parentindex: 2 + _parentindex: 3 linkindex: 12 mtu: 1500 name: h1 -> s1 @@ -408,6 +550,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 4 + mtu: 1500 name: '[Access VLAN red] h2 -> [s1,s2]' neighbors: - ifname: port-channel3 @@ -428,6 +572,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 5 + mtu: 1500 name: '[Access VLAN red] h2 -> [s1,s2]' neighbors: - ifname: port-channel4 @@ -444,7 +590,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 1 + _parentindex: 4 linkindex: 13 mtu: 1500 name: h2 -> s1 @@ -455,7 +601,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 1 + _parentindex: 4 linkindex: 14 mtu: 1500 name: h2 -> s1 @@ -466,7 +612,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 1 + _parentindex: 4 linkindex: 15 mtu: 1500 name: h2 -> s2 @@ -477,7 +623,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 1 + _parentindex: 4 linkindex: 16 mtu: 1500 name: h2 -> s2 @@ -488,7 +634,7 @@ nodes: - ifindex: 5 ifname: eth5 lag: - _parentindex: 2 + _parentindex: 5 linkindex: 17 mtu: 1500 name: h2 -> s1 @@ -499,7 +645,7 @@ nodes: - ifindex: 6 ifname: eth6 lag: - _parentindex: 2 + _parentindex: 5 linkindex: 18 mtu: 1500 name: h2 -> s2 @@ -591,6 +737,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 2 + mtu: 1500 name: '[Access VLAN red] s1 -> [h1,s2]' neighbors: - ifname: bond1 @@ -611,6 +759,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 3 + mtu: 1500 name: s1 -> h1 neighbors: - ifname: bond2 @@ -626,6 +776,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 4 + mtu: 1500 name: '[Access VLAN red] s1 -> [h2,s2]' neighbors: - ifname: bond1 @@ -647,6 +799,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 5 + mtu: 1500 name: '[Access VLAN red] s1 -> [h2,s2]' neighbors: - ifname: bond2 @@ -665,7 +819,7 @@ nodes: ifindex: 2 ifname: ethernet1/1/2 lag: - _peerlink: 1 + _parentindex: 1 linkindex: 6 mtu: 1500 name: s1 -> s2 @@ -678,7 +832,7 @@ nodes: ifindex: 3 ifname: ethernet1/1/3 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 7 mtu: 1500 name: s1 -> h1 @@ -691,7 +845,7 @@ nodes: ifindex: 4 ifname: ethernet1/1/4 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 8 mtu: 1500 name: s1 -> h1 @@ -704,7 +858,7 @@ nodes: ifindex: 5 ifname: ethernet1/1/5 lag: - _parentindex: 2 + _parentindex: 3 linkindex: 11 mtu: 1500 name: s1 -> h1 @@ -717,7 +871,7 @@ nodes: ifindex: 6 ifname: ethernet1/1/6 lag: - _parentindex: 2 + _parentindex: 3 linkindex: 12 mtu: 1500 name: s1 -> h1 @@ -730,7 +884,7 @@ nodes: ifindex: 7 ifname: ethernet1/1/7 lag: - _parentindex: 3 + _parentindex: 4 linkindex: 13 mtu: 1500 name: s1 -> h2 @@ -743,7 +897,7 @@ nodes: ifindex: 8 ifname: ethernet1/1/8 lag: - _parentindex: 3 + _parentindex: 4 linkindex: 14 mtu: 1500 name: s1 -> h2 @@ -756,7 +910,7 @@ nodes: ifindex: 9 ifname: ethernet1/1/9 lag: - _parentindex: 4 + _parentindex: 5 linkindex: 17 mtu: 1500 name: s1 -> h2 @@ -848,6 +1002,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 2 + mtu: 1500 name: '[Access VLAN red] s2 -> [h1,s1]' neighbors: - ifname: bond1 @@ -869,6 +1025,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 4 + mtu: 1500 name: '[Access VLAN red] s2 -> [h2,s1]' neighbors: - ifname: bond1 @@ -890,6 +1048,8 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 5 + mtu: 1500 name: '[Access VLAN red] s2 -> [h2,s1]' neighbors: - ifname: bond2 @@ -908,7 +1068,7 @@ nodes: ifindex: 2 ifname: ethernet1/1/2 lag: - _peerlink: 1 + _parentindex: 1 linkindex: 6 mtu: 1500 name: s2 -> s1 @@ -921,7 +1081,7 @@ nodes: ifindex: 3 ifname: ethernet1/1/3 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 9 mtu: 1500 name: s2 -> h1 @@ -934,7 +1094,7 @@ nodes: ifindex: 4 ifname: ethernet1/1/4 lag: - _parentindex: 1 + _parentindex: 2 linkindex: 10 mtu: 1500 name: s2 -> h1 @@ -947,7 +1107,7 @@ nodes: ifindex: 5 ifname: ethernet1/1/5 lag: - _parentindex: 3 + _parentindex: 4 linkindex: 15 mtu: 1500 name: s2 -> h2 @@ -960,7 +1120,7 @@ nodes: ifindex: 6 ifname: ethernet1/1/6 lag: - _parentindex: 3 + _parentindex: 4 linkindex: 16 mtu: 1500 name: s2 -> h2 @@ -973,7 +1133,7 @@ nodes: ifindex: 7 ifname: ethernet1/1/7 lag: - _parentindex: 4 + _parentindex: 5 linkindex: 18 mtu: 1500 name: s2 -> h2 diff --git a/tests/topology/expected/node.clone-plugin-lag.yml b/tests/topology/expected/node.clone-plugin-lag.yml index ab79a5e27..e71c82dc3 100644 --- a/tests/topology/expected/node.clone-plugin-lag.yml +++ b/tests/topology/expected/node.clone-plugin-lag.yml @@ -30,6 +30,152 @@ links: node_count: 2 prefix: false type: p2p +- _linkname: links[2] + bridge: input_2 + interfaces: + - ifindex: 30000 + ifname: port-channel8 + node: r1 + - ifindex: 30000 + ifname: bond8 + node: h-01 + lag: + ifindex: 8 + linkindex: 2 + node_count: 2 + pool: l2only + type: lag +- _linkname: links[3] + bridge: input_3 + interfaces: + - ifindex: 30001 + ifname: port-channel9 + node: r1 + - ifindex: 30000 + ifname: bond9 + node: h-02 + lag: + ifindex: 9 + linkindex: 3 + node_count: 2 + pool: l2only + type: lag +- _linkname: links[4] + bridge: input_4 + interfaces: + - ifindex: 30000 + ifname: port-channel1 + lag: + ifindex: 1 + node: r2 + - ifindex: 30001 + ifname: bond9 + lag: + ifindex: 9 + node: h-01 + lag: {} + linkindex: 4 + node_count: 2 + pool: l2only + type: lag +- _linkname: links[5] + bridge: input_5 + interfaces: + - ifindex: 30001 + ifname: port-channel2 + lag: + ifindex: 2 + node: r2 + - ifindex: 30001 + ifname: bond10 + lag: + ifindex: 10 + node: h-02 + lag: {} + linkindex: 5 + node_count: 2 + pool: l2only + type: lag +- _linkname: links[6] + bridge: input_6 + interfaces: + - _vlan_mode: irb + ifindex: 30000 + ifname: bond1 + ipv4: 172.16.0.5/24 + lag: + ifindex: 1 + node: h2-01 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30002 + ifname: port-channel10 + ipv4: 172.16.0.1/24 + lag: + _mlag: true + node: r1 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30002 + ifname: port-channel10 + ipv4: 172.16.0.2/24 + lag: + _mlag: true + node: r2 + vlan: + access: red + lag: + ifindex: 10 + linkindex: 6 + node_count: 3 + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + type: lag + vlan: + access: red +- _linkname: links[7] + bridge: input_7 + interfaces: + - _vlan_mode: irb + ifindex: 30000 + ifname: bond1 + ipv4: 172.16.0.6/24 + lag: + ifindex: 1 + node: h2-02 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30003 + ifname: port-channel11 + ipv4: 172.16.0.1/24 + lag: + _mlag: true + node: r1 + vlan: + access: red + - _vlan_mode: irb + ifindex: 30003 + ifname: port-channel11 + ipv4: 172.16.0.2/24 + lag: + _mlag: true + node: r2 + vlan: + access: red + lag: + ifindex: 11 + linkindex: 7 + node_count: 3 + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + type: lag + vlan: + access: red - _linkname: links[2].lag[1] interfaces: - ifindex: 2 @@ -217,6 +363,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 2 name: h-01 -> r1 neighbors: - ifname: port-channel8 @@ -231,6 +378,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 4 name: h-01 -> r2 neighbors: - ifname: port-channel1 @@ -241,7 +389,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 8 + _parentindex: 2 linkindex: 8 name: h-01 -> r1 neighbors: @@ -251,7 +399,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 8 + _parentindex: 2 linkindex: 9 name: h-01 -> r1 neighbors: @@ -261,7 +409,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 9 + _parentindex: 4 linkindex: 12 name: h-01 -> r2 neighbors: @@ -271,7 +419,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 9 + _parentindex: 4 linkindex: 13 name: h-01 -> r2 neighbors: @@ -311,6 +459,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 3 name: h-02 -> r1 neighbors: - ifname: port-channel9 @@ -325,6 +474,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 5 name: h-02 -> r2 neighbors: - ifname: port-channel2 @@ -335,7 +485,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 9 + _parentindex: 3 linkindex: 10 name: h-02 -> r1 neighbors: @@ -345,7 +495,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 9 + _parentindex: 3 linkindex: 11 name: h-02 -> r1 neighbors: @@ -355,7 +505,7 @@ nodes: - ifindex: 3 ifname: eth3 lag: - _parentindex: 10 + _parentindex: 5 linkindex: 14 name: h-02 -> r2 neighbors: @@ -365,7 +515,7 @@ nodes: - ifindex: 4 ifname: eth4 lag: - _parentindex: 10 + _parentindex: 5 linkindex: 15 name: h-02 -> r2 neighbors: @@ -405,6 +555,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 6 name: '[Access VLAN red] h2-01 -> [r1,r2]' neighbors: - ifname: port-channel10 @@ -421,7 +572,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 1 + _parentindex: 6 linkindex: 16 name: h2-01 -> r1 neighbors: @@ -431,7 +582,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 1 + _parentindex: 6 linkindex: 17 name: h2-01 -> r2 neighbors: @@ -501,6 +652,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 7 name: '[Access VLAN red] h2-02 -> [r1,r2]' neighbors: - ifname: port-channel11 @@ -517,7 +669,7 @@ nodes: - ifindex: 1 ifname: eth1 lag: - _parentindex: 1 + _parentindex: 7 linkindex: 18 name: h2-02 -> r1 neighbors: @@ -527,7 +679,7 @@ nodes: - ifindex: 2 ifname: eth2 lag: - _parentindex: 1 + _parentindex: 7 linkindex: 19 name: h2-02 -> r2 neighbors: @@ -612,6 +764,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 2 name: r1 -> h-01 neighbors: - ifname: bond8 @@ -626,6 +779,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 3 name: r1 -> h-02 neighbors: - ifname: bond9 @@ -641,6 +795,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 6 name: '[Access VLAN red] r1 -> [h2-01,r2]' neighbors: - ifname: bond1 @@ -662,6 +817,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 7 name: '[Access VLAN red] r1 -> [h2-02,r2]' neighbors: - ifname: bond1 @@ -678,7 +834,7 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 8 + _parentindex: 2 linkindex: 8 name: r1 -> h-01 neighbors: @@ -688,7 +844,7 @@ nodes: - ifindex: 3 ifname: Ethernet3 lag: - _parentindex: 8 + _parentindex: 2 linkindex: 9 name: r1 -> h-01 neighbors: @@ -698,7 +854,7 @@ nodes: - ifindex: 4 ifname: Ethernet4 lag: - _parentindex: 9 + _parentindex: 3 linkindex: 10 name: r1 -> h-02 neighbors: @@ -708,7 +864,7 @@ nodes: - ifindex: 5 ifname: Ethernet5 lag: - _parentindex: 9 + _parentindex: 3 linkindex: 11 name: r1 -> h-02 neighbors: @@ -718,7 +874,7 @@ nodes: - ifindex: 6 ifname: Ethernet6 lag: - _parentindex: 10 + _parentindex: 6 linkindex: 16 name: r1 -> h2-01 neighbors: @@ -728,7 +884,7 @@ nodes: - ifindex: 7 ifname: Ethernet7 lag: - _parentindex: 11 + _parentindex: 7 linkindex: 18 name: r1 -> h2-02 neighbors: @@ -810,7 +966,7 @@ nodes: - ifindex: 2 ifname: Ethernet2 lag: - _parentindex: 10 + _parentindex: 6 linkindex: 17 name: r2 -> h2-01 neighbors: @@ -820,7 +976,7 @@ nodes: - ifindex: 3 ifname: Ethernet3 lag: - _parentindex: 11 + _parentindex: 7 linkindex: 19 name: r2 -> h2-02 neighbors: @@ -857,31 +1013,17 @@ nodes: - ifindex: 6 ifname: Ethernet6 lag: - _parentindex: 1 + _parentindex: 4 linkindex: 12 name: r2 -> h-01 neighbors: - ifname: eth3 node: h-01 type: p2p - - ifindex: 7 - ifname: port-channel1 - lag: - ifindex: 1 - lacp: fast - lacp_mode: active - mode: 802.3ad - name: r2 -> h-01 - neighbors: - - ifname: bond9 - node: h-01 - pool: l2only - type: lag - virtual_interface: true - ifindex: 7 ifname: Ethernet7 lag: - _parentindex: 1 + _parentindex: 4 linkindex: 13 name: r2 -> h-01 neighbors: @@ -891,7 +1033,7 @@ nodes: - ifindex: 8 ifname: Ethernet8 lag: - _parentindex: 2 + _parentindex: 5 linkindex: 14 name: r2 -> h-02 neighbors: @@ -899,12 +1041,38 @@ nodes: node: h-02 type: p2p - ifindex: 9 + ifname: Ethernet9 + lag: + _parentindex: 5 + linkindex: 15 + name: r2 -> h-02 + neighbors: + - ifname: eth4 + node: h-02 + type: p2p + - ifindex: 30000 + ifname: port-channel1 + lag: + ifindex: 1 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 4 + name: r2 -> h-01 + neighbors: + - ifname: bond9 + node: h-01 + pool: l2only + type: lag + virtual_interface: true + - ifindex: 30001 ifname: port-channel2 lag: ifindex: 2 lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 5 name: r2 -> h-02 neighbors: - ifname: bond10 @@ -912,17 +1080,7 @@ nodes: pool: l2only type: lag virtual_interface: true - - ifindex: 9 - ifname: Ethernet9 - lag: - _parentindex: 2 - linkindex: 15 - name: r2 -> h-02 - neighbors: - - ifname: eth4 - node: h-02 - type: p2p - - ifindex: 30000 + - ifindex: 30002 ifname: port-channel10 lag: _mlag: true @@ -930,6 +1088,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 6 name: '[Access VLAN red] r2 -> [h2-01,r1]' neighbors: - ifname: bond1 @@ -943,7 +1102,7 @@ nodes: vlan: access: red access_id: 1000 - - ifindex: 30001 + - ifindex: 30003 ifname: port-channel11 lag: _mlag: true @@ -951,6 +1110,7 @@ nodes: lacp: fast lacp_mode: active mode: 802.3ad + linkindex: 7 name: '[Access VLAN red] r2 -> [h2-02,r1]' neighbors: - ifname: bond1 @@ -969,7 +1129,7 @@ nodes: lacp_mode: active mode: 802.3ad libvirt: - nic_adapter_count: 12 + nic_adapter_count: 10 loopback: ifindex: 0 ifname: Loopback0 From b001e109484b9d78c58593d924796561d4978c79 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:27:40 -0600 Subject: [PATCH 42/46] Undo merge --- tests/errors/link-invalid-type.log | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/errors/link-invalid-type.log b/tests/errors/link-invalid-type.log index 10c80ea23..e47a24f60 100644 --- a/tests/errors/link-invalid-type.log +++ b/tests/errors/link-invalid-type.log @@ -1,4 +1,4 @@ IncorrectValue in links: attribute links[1].type has invalid value(s): wtf -... valid values are: lan,p2p,stub,loopback,tunnel,vlan_member,virtual_lag +... valid values are: lan,p2p,stub,loopback,tunnel,vlan_member,lag ... use 'netlab show attributes link' to display valid attributes Fatal error in netlab: Cannot proceed beyond this point due to errors, exiting From b8afe689d0dd9997d21bd5e4b204cd2fbe449ebf Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:34:48 -0600 Subject: [PATCH 43/46] Remove duplicate case --- tests/integration/lag/05-host-mlag-lag.yml | 43 ---------------------- 1 file changed, 43 deletions(-) delete mode 100644 tests/integration/lag/05-host-mlag-lag.yml diff --git a/tests/integration/lag/05-host-mlag-lag.yml b/tests/integration/lag/05-host-mlag-lag.yml deleted file mode 100644 index 23e037823..000000000 --- a/tests/integration/lag/05-host-mlag-lag.yml +++ /dev/null @@ -1,43 +0,0 @@ -message: | - The device under is a pair of switches with a pair of L3 MLAG links connected - to 2 Linux hosts, each connected through a LAG The hosts should be able to - ping each other and their gateway - -groups: - _auto_create: true - switches: - members: [s1, s2] - module: [lag, vlan] - hosts: - members: [h1, h2] - module: [lag] # Host side must support lag to present single MAC on both interfaces - device: frr # linux does not support 'lag' module yet - -vlans: - red: - -links: -- lag: - members: [s1-s2, s1-s2] - mlag.peergroup: 1 - # On OS10 in case of mlag, vlan.trunk is implied for all vlans - other platforms? -- lag: - members: [h1-s1, h1-s1, h1-s2, h1-s2] - # mlag: True - vlan.access: red -- lag: - members: [h2-s1, h2-s1, h2-s2, h2-s2] - # mlag: True - vlan.access: red - -validate: - ping: - description: Pinging H2 from H1 - nodes: [h1] - wait_msg: Waiting for STP to enable the ports - wait: 45 - plugin: ping(nodes.h2.interfaces[0].ipv4,af='ipv4') - ping_gw: - description: Pinging gateway from H1 - nodes: [h1] - plugin: ping(nodes.s1.interfaces[8].ipv4,af='ipv4') From 42717dae936ef1619269de465af60e296ea4eec8 Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:40:05 -0600 Subject: [PATCH 44/46] Undo change in ifindex matching, rework test case --- netsim/ansible/templates/lag/linux.j2 | 2 +- tests/integration/lag/06-host-mlag-anycast-gateway.yml | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index ba64e8cae..adf13fb3d 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -40,7 +40,7 @@ set -e # Exit immediately when any command fails 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.lag.ifindex==l.lag._parentindex %}{{ i.ifname }} + 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 diff --git a/tests/integration/lag/06-host-mlag-anycast-gateway.yml b/tests/integration/lag/06-host-mlag-anycast-gateway.yml index 1eccf38ef..6c8b9d013 100644 --- a/tests/integration/lag/06-host-mlag-anycast-gateway.yml +++ b/tests/integration/lag/06-host-mlag-anycast-gateway.yml @@ -11,7 +11,7 @@ groups: hosts: members: [h1, h2] module: [lag] # Host side must support lag to present single MAC on both interfaces - device: frr # linux does not support 'lag' module yet + device: linux vlans: red: @@ -21,14 +21,11 @@ links: - lag: members: [s1-s2] mlag.peergroup: 1 - # On OS10 in case of mlag, vlan.trunk is implied for all vlans - other platforms? - lag: members: [h1-s1, h1-s2] - # mlag: True vlan.access: red - lag: members: [h2-s1, h2-s2] - # mlag: True vlan.access: red validate: @@ -41,8 +38,8 @@ validate: ping_anycast_gw1: description: Pinging anycast gateway from H1 nodes: [h1] - plugin: ping(nodes.s1.interfaces[5].gateway.ipv4,af='ipv4') + plugin: ping(nodes.s1.interfaces[-1].gateway.ipv4,af='ipv4') ping_anycast_gw2: description: Pinging other non-anycast gateway from H1 nodes: [h1] - plugin: ping(nodes.s2.interfaces[5].ipv4,af='ipv4') + plugin: ping(nodes.s2.interfaces[-1].ipv4,af='ipv4') From ba639a6f18d79d0241afaffb8955180c58abda5b Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:47:12 -0600 Subject: [PATCH 45/46] Set device down before changing bond master --- netsim/ansible/templates/lag/linux.j2 | 1 + 1 file changed, 1 insertion(+) diff --git a/netsim/ansible/templates/lag/linux.j2 b/netsim/ansible/templates/lag/linux.j2 index adf13fb3d..9ce6d2725 100644 --- a/netsim/ansible/templates/lag/linux.j2 +++ b/netsim/ansible/templates/lag/linux.j2 @@ -39,6 +39,7 @@ set -e # Exit immediately when any command fails {% 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 %} From e4439bf836f24115675569865b20560613e333bf Mon Sep 17 00:00:00 2001 From: Jeroen van Bemmel Date: Sun, 15 Dec 2024 17:49:10 -0600 Subject: [PATCH 46/46] Rename test case --- ...-host-lag-active-standby.yml => 05-bonding-active-standby.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/integration/lag/{11-host-lag-active-standby.yml => 05-bonding-active-standby.yml} (100%) diff --git a/tests/integration/lag/11-host-lag-active-standby.yml b/tests/integration/lag/05-bonding-active-standby.yml similarity index 100% rename from tests/integration/lag/11-host-lag-active-standby.yml rename to tests/integration/lag/05-bonding-active-standby.yml