Files
infix/test/case/ietf_interfaces/lag_failure/test.py
T
Tobias Waldekranz fb394db981 test: lag_*: Ensure that links making up a LAG are compatible
Do not allow the topology matcher to setup a LAG where one link is
running at 10G while the other one runs at 1G, since the bond will
never use the 1G port as long as there the 10G link is up.
2025-02-12 23:22:10 +01:00

145 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
r"""LACP Aggregate w/ Degraded Link
Verify communication over an LACP link aggregate when individual member
links stop forwarding traffic, without carrier loss.
.Logical network setup, link breakers (lb1 & lb2) here managed by host PC
image::lag-failure.svg[]
The host verifies connectivity with dut2 via dut1 over the aggregate for
each failure mode step using the `mon` interface.
"""
from time import time
import infamy
import infamy.lag
from infamy.netns import TPMR
from infamy.util import parallel
IPH = "192.168.2.1"
IP1 = "192.168.2.41"
IP2 = "192.168.2.42"
class LinkBreaker:
"""Encapsulates TPMR based link-breakers."""
def __init__(self, sys, netns):
self.net = netns
self.lb1 = TPMR(sys.ltop.xlate("host", "lb1a")[1],
sys.ltop.xlate("host", "lb1b")[1]).start()
self.lb2 = TPMR(sys.ltop.xlate("host", "lb2a")[1],
sys.ltop.xlate("host", "lb2b")[1]).start()
def forward(self, lb1, lb2):
"""Set link breakers in forwarding or blocking state."""
getattr(self.lb1, lb1)()
getattr(self.lb2, lb2)()
def fail_check(self, peer):
"""Verify connectivity with a given peer during failure."""
sequence = [
("forward", "forward"),
("forward", "block"),
("block", "forward"),
("forward", "forward")
]
total_start = time()
print(f"{'LB1':<8} | {'LB2':<8} | {'Status':<8}")
print("---------|----------|---------")
for lb1, lb2 in sequence:
state_start = time()
try:
print(f"{lb1:<8} | {lb2:<8} | {'...':<8}", end="\r# ")
self.forward(lb1, lb2)
self.net.must_reach(peer, timeout=30)
print(f"{lb1:<8} | {lb2:<8} | {'OK':<8} in "
f"{time() - state_start:.2f}s")
except Exception as e:
print(f"{lb1:<8} | {lb2:<8} | {'FAIL':<8} after "
f"{time() - state_start:.2f}s")
print(f"\nError encountered: {e}")
print(f"Link breakers were in state: LB1='{lb1}', LB2='{lb2}'")
raise
print(f"Total time: {time() - total_start:.2f}s")
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",
"ipv4": {
"address": [{"ip": addr, "prefix-length": 24}]
}
}, {
"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, addr, peer):
"""Configure each DUT specific according to LAG mode and peer"""
net = net_init(dut["mon"], addr)
dut.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [{
"name": "lag0",
"type": "infix-if-type:lag",
"lag": {
"mode": "lacp",
"lacp": {"rate": "fast"},
"link-monitor": {"interval": 100}
}
}, {
"name": dut["link1"],
"lag-port": {"lag": "lag0"}
}, {
"name": dut["link2"],
"lag-port": {"lag": "lag0"}
}] + net
}
})
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env(edge_mappings=infamy.lag.edge_mappings)
dut1 = env.attach("dut1")
dut2 = env.attach("dut2")
_, mon = env.ltop.xlate("host", "mon")
with infamy.IsolatedMacVlan(mon) as ns:
lb = LinkBreaker(env, ns)
ns.addip(IPH)
print(f"Setting up lag0 in LACP mode between {dut1} and {dut2}")
with test.step("Set up link aggregate, lag0, between dut1 and dut2"):
parallel(lambda: dut_init(dut1, IP1, IP2),
lambda: dut_init(dut2, IP2, IP1))
with test.step("Initial connectivity check ..."):
ns.must_reach(IP2, timeout=30)
with test.step("Verify failure modes"):
lb.fail_check(IP2)
test.succeed()