test: new test, verify basic lag setup

Verify connectivity from host to the second DUT via the first, over a
link aggregate.  The lag starts in static mode and then changes to an
LACP aggregate.  This verifies not just basic aggregate functionality,
but also changing mode, which is quite tricky to get right.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-02-12 23:21:08 +01:00
committed by Tobias Waldekranz
parent 3ef1da1096
commit 4260d24143
8 changed files with 327 additions and 0 deletions
+2
View File
@@ -33,6 +33,8 @@ include::bridge_vlan_separation/Readme.adoc[]
include::dual_bridge/Readme.adoc[]
include::lag_basic/Readme.adoc[]
include::igmp_basic/Readme.adoc[]
include::igmp_vlan/Readme.adoc[]
@@ -35,6 +35,9 @@
- name: ipv4_autoconf
case: ipv4_autoconf/test.py
- name: lag_basic
case: lag_basic/test.py
- name: bridge_fwd_sgl_dut
case: bridge_fwd_sgl_dut/test.py
+1
View File
@@ -0,0 +1 @@
lag_basic.adoc
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 465 KiB

@@ -0,0 +1,43 @@
=== Ling Aggregation Basic
==== Description
Verify communication over a link aggregate in static and LACP operating
modes during basic failure scenarios.
.Internal network setup, PC verifies connectivity with dut2 via dut1
ifdef::topdoc[]
image::../../test/case/ietf_interfaces/lag_basic/lag-basic.svg[Internal networks]
endif::topdoc[]
ifndef::topdoc[]
ifdef::testgroup[]
image::lag_basic/lag-basic.svg[Internal networks]
endif::testgroup[]
ifndef::testgroup[]
image::lag-basic.svg[Internal networks]
endif::testgroup[]
endif::topdoc[]
The host verifies connectivity with dut2 via dut1 over the aggregate for
each test step using the `mon` interface.
==== Topology
ifdef::topdoc[]
image::{topdoc}../../test/case/ietf_interfaces/lag_basic/topology.svg[Ling Aggregation Basic topology]
endif::topdoc[]
ifndef::topdoc[]
ifdef::testgroup[]
image::lag_basic/topology.svg[Ling Aggregation Basic topology]
endif::testgroup[]
ifndef::testgroup[]
image::topology.svg[Ling Aggregation Basic topology]
endif::testgroup[]
endif::topdoc[]
==== Test sequence
. Set up topology and attach to target DUTs
. Set up LACP link aggregate, lag0, on dut1 and dut2
. Verify failure modes for lacp mode
. Set up static link aggregate, lag0, on dut1 and dut2
. Verify failure modes for static mode
<<<
+165
View File
@@ -0,0 +1,165 @@
#!/usr/bin/env python3
r"""Ling Aggregation Basic
Verify communication over a link aggregate in static and LACP operating
modes during basic failure scenarios.
.Internal network setup, PC verifies connectivity with dut2 via dut1
image::lag-basic.svg[Internal networks]
The host verifies connectivity with dut2 via dut1 over the aggregate for
each test step using the `mon` interface.
"""
from time import sleep, time
from datetime import datetime
import infamy
from infamy.util import parallel, until
class DumbLinkBreaker:
"""Encapsulates basic, dumb link-breaking ops over SSH."""
def __init__(self, sys, dut, netns):
self.env = sys
self.dut = dut
self.net = netns
self.tgt = {}
for i, (name, _) in dut.items():
self.tgt[i] = env.attach(name, "mgmt", "ssh")
def set_link(self, link, updown):
"""Set link up or down, verify before returning."""
def set_and_verify(i):
name, dut = self.dut[i]
cmd = self.tgt[i].runsh(f"sudo ip link set {dut[link]} {updown}")
if cmd.returncode:
for out in [cmd.stdout, cmd.stderr]:
if out:
print(f"{name}: {out.rstrip()}")
raise RuntimeError(f"{name}: failed setting {link} {updown}")
for _ in range(10):
sleep(0.1)
check = self.tgt[i].runsh(f"ip link show {dut[link]}")
if f"state {updown.upper()}" in check.stdout:
break
else:
raise RuntimeError(f"{name}: {dut[link]} did not go {updown}")
parallel(*[lambda i=i: set_and_verify(i) for i in self.dut])
def fail_check(self, peer):
"""Verify connectivity with peer during link failure."""
sequence = [
[("link1", "up"), ("link2", "up")],
[("link1", "down"), ("link2", "up")],
[("link1", "up"), ("link2", "down")],
[("link1", "up"), ("link2", "up")]
]
total_start = time()
for state in sequence:
state_start = time()
print(f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]} {state}")
for link, updown in state:
self.set_link(link, updown)
self.net.must_reach(peer, timeout=10)
print(f"Completed in {time() - state_start:.2f}s")
print(f"Total time: {time() - total_start:.2f}s")
def lag_init(mode):
"""Set up mode specific attributes for the LAG"""
if mode == "lacp":
lag = [{
"name": "lag0",
"lag": {"lacp": {"rate": "fast"}}
}]
else:
lag = []
return lag
def net_init(host, addr):
"""Set up DUT network, dut1 bridges host port with lag0"""
if host:
net = [{
"name": "br0",
"type": "infix-if-type:bridge",
}, {
"name": host,
"bridge-port": {"bridge": "br0"}
}, {
"name": "lag0",
"bridge-port": {"bridge": "br0"}
}]
else:
net = [{
"name": "lag0",
"ipv4": {
"address": [{"ip": addr, "prefix-length": 24}]
}
}]
return net
def dut_init(dut, mode, addr):
"""Set up link aggregate on dut"""
net = net_init(dut["mon"], addr)
lag = lag_init(mode)
dut.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [{
"name": "lag0",
"type": "infix-if-type:lag",
"lag": {
"mode": mode,
"link-monitor": {"interval": 100}
}
}, {
"name": dut["link1"],
"lag-port": {"lag": "lag0"}
}, {
"name": dut["link2"],
"lag-port": {"lag": "lag0"}
}] + net + lag
}
})
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
dut1 = env.attach("dut1", "mgmt")
dut2 = env.attach("dut2", "mgmt")
_, mon = env.ltop.xlate("host", "mon")
with infamy.IsolatedMacVlan(mon) as ns:
dm = {
'1': ("dut1", dut1),
'2': ("dut2", dut2)
}
lb = DumbLinkBreaker(env, dm, ns)
ns.addip("192.168.2.1")
with test.step("Set up LACP link aggregate, lag0, on dut1 and dut2"):
parallel(lambda: dut_init(dut1, "lacp", None),
lambda: dut_init(dut2, "lacp", "192.168.2.42"))
with test.step("Verify failure modes for lacp mode"):
lb.fail_check("192.168.2.42")
with test.step("Set up static link aggregate, lag0, on dut1 and dut2"):
parallel(lambda: dut_init(dut1, "static", None),
lambda: dut_init(dut2, "static", "192.168.2.42"))
with test.step("Verify failure modes for static mode"):
lb.fail_check("192.168.2.42")
test.succeed()
@@ -0,0 +1,33 @@
graph "lag" {
layout="neato";
overlap="false";
esep="+23";
node [shape=record, fontsize=12, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
host [
label="host | { <mgmt1> mgmt1 | <mon> mon | \n\n\n\n | <mgmt2> mgmt2 }",
pos="0,15!",
requires="controller",
];
dut1 [
label="{ <mgmt> mgmt | <mon> mon } | { dut1\r | { <link1> link1 | <link2> link2 } }",
pos="2,15.25!",
requires="infix",
];
dut2 [
label="<mgmt> mgmt | { { <link1> link1 | <link2> link2 } | dut2\r }",
pos="2,14.75!",
requires="infix",
];
host:mgmt1 -- dut1:mgmt [requires="mgmt", color=lightgray]
host:mon -- dut1:mon // Monitor connection to dut2 via dut1
host:mgmt2 -- dut2:mgmt [requires="mgmt", color=lightgrey]
dut1:link1 -- dut2:link1 [color=black, fontcolor=black, penwidth=3]
dut1:link2 -- dut2:link2 [color=black, fontcolor=black, penwidth=3]
}
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: lag Pages: 1 -->
<svg width="538pt" height="152pt"
viewBox="0.00 0.00 538.04 151.51" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 147.51)">
<title>lag</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-147.51 534.04,-147.51 534.04,4 -4,4"/>
<!-- host -->
<g id="node1" class="node">
<title>host</title>
<polygon fill="none" stroke="black" points="0,-8.26 0,-135.26 101,-135.26 101,-8.26 0,-8.26"/>
<text text-anchor="middle" x="23.5" y="-68.66" font-family="DejaVu Sans Mono, Book" font-size="12.00">host</text>
<polyline fill="none" stroke="black" points="47,-8.26 47,-135.26 "/>
<text text-anchor="middle" x="74" y="-121.66" font-family="DejaVu Sans Mono, Book" font-size="12.00">mgmt1</text>
<polyline fill="none" stroke="black" points="47,-114.26 101,-114.26 "/>
<text text-anchor="middle" x="74" y="-100.66" font-family="DejaVu Sans Mono, Book" font-size="12.00">mon</text>
<polyline fill="none" stroke="black" points="47,-93.26 101,-93.26 "/>
<polyline fill="none" stroke="black" points="47,-29.26 101,-29.26 "/>
<text text-anchor="middle" x="74" y="-15.66" font-family="DejaVu Sans Mono, Book" font-size="12.00">mgmt2</text>
</g>
<!-- dut1 -->
<g id="node2" class="node">
<title>dut1</title>
<polygon fill="none" stroke="black" points="375.04,-101.01 375.04,-143.01 530.04,-143.01 530.04,-101.01 375.04,-101.01"/>
<text text-anchor="middle" x="398.54" y="-129.41" font-family="DejaVu Sans Mono, Book" font-size="12.00">mgmt</text>
<polyline fill="none" stroke="black" points="375.04,-122.01 422.04,-122.01 "/>
<text text-anchor="middle" x="398.54" y="-108.41" font-family="DejaVu Sans Mono, Book" font-size="12.00">mon</text>
<polyline fill="none" stroke="black" points="422.04,-101.01 422.04,-143.01 "/>
<text text-anchor="end" x="522.04" y="-129.41" font-family="DejaVu Sans Mono, Book" font-size="12.00">dut1</text>
<polyline fill="none" stroke="black" points="422.04,-122.01 530.04,-122.01 "/>
<text text-anchor="middle" x="449.04" y="-108.41" font-family="DejaVu Sans Mono, Book" font-size="12.00">link1</text>
<polyline fill="none" stroke="black" points="476.04,-101.01 476.04,-122.01 "/>
<text text-anchor="middle" x="503.04" y="-108.41" font-family="DejaVu Sans Mono, Book" font-size="12.00">link2</text>
</g>
<!-- host&#45;&#45;dut1 -->
<g id="edge1" class="edge">
<title>host:mgmt1&#45;&#45;dut1:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M101.5,-124.76C101.5,-124.76 374.54,-133.01 374.54,-133.01"/>
</g>
<!-- host&#45;&#45;dut1 -->
<g id="edge2" class="edge">
<title>host:mon&#45;&#45;dut1:mon</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M101.5,-103.76C101.5,-103.76 374.54,-111.01 374.54,-111.01"/>
</g>
<!-- dut2 -->
<g id="node3" class="node">
<title>dut2</title>
<polygon fill="none" stroke="black" points="375.04,-0.5 375.04,-42.5 530.04,-42.5 530.04,-0.5 375.04,-0.5"/>
<text text-anchor="middle" x="398.54" y="-18.4" font-family="DejaVu Sans Mono, Book" font-size="12.00">mgmt</text>
<polyline fill="none" stroke="black" points="422.04,-0.5 422.04,-42.5 "/>
<text text-anchor="middle" x="449.04" y="-28.9" font-family="DejaVu Sans Mono, Book" font-size="12.00">link1</text>
<polyline fill="none" stroke="black" points="476.04,-21.5 476.04,-42.5 "/>
<text text-anchor="middle" x="503.04" y="-28.9" font-family="DejaVu Sans Mono, Book" font-size="12.00">link2</text>
<polyline fill="none" stroke="black" points="422.04,-21.5 530.04,-21.5 "/>
<text text-anchor="end" x="522.04" y="-7.9" font-family="DejaVu Sans Mono, Book" font-size="12.00">dut2</text>
</g>
<!-- host&#45;&#45;dut2 -->
<g id="edge3" class="edge">
<title>host:mgmt2&#45;&#45;dut2:mgmt</title>
<path fill="none" stroke="lightgrey" stroke-width="2" d="M101.5,-18.76C101.5,-18.76 374.54,-21.5 374.54,-21.5"/>
</g>
<!-- dut1&#45;&#45;dut2 -->
<g id="edge4" class="edge">
<title>dut1:link1&#45;&#45;dut2:link1</title>
<path fill="none" stroke="black" stroke-width="3" d="M448.54,-101.01C448.54,-101.01 448.54,-42.5 448.54,-42.5"/>
</g>
<!-- dut1&#45;&#45;dut2 -->
<g id="edge5" class="edge">
<title>dut1:link2&#45;&#45;dut2:link2</title>
<path fill="none" stroke="black" stroke-width="3" d="M503.54,-101.01C503.54,-101.01 503.54,-42.5 503.54,-42.5"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB