Skip to content

Commit cf16efe

Browse files
troglobitwkz
authored andcommitted
test: new test, verify degraded link aggregate members in LACP mode
Verify connectivity from host to the second DUT via the first, over a link aggregate. Both aggregate member links are connected to a TPMR link breaker to hide link down from the DUTs. Signed-off-by: Joachim Wiberg <[email protected]>
1 parent 4260d24 commit cf16efe

File tree

8 files changed

+324
-0
lines changed

8 files changed

+324
-0
lines changed

test/case/ietf_interfaces/Readme.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ include::dual_bridge/Readme.adoc[]
3535

3636
include::lag_basic/Readme.adoc[]
3737

38+
include::lag_failure/Readme.adoc[]
39+
3840
include::igmp_basic/Readme.adoc[]
3941

4042
include::igmp_vlan/Readme.adoc[]

test/case/ietf_interfaces/ietf_interfaces.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
- name: lag_basic
3939
case: lag_basic/test.py
4040

41+
- name: lag_failure
42+
case: lag_failure/test.py
43+
4144
- name: bridge_fwd_sgl_dut
4245
case: bridge_fwd_sgl_dut/test.py
4346

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lag_failure.adoc

test/case/ietf_interfaces/lag_failure/lag-failure.svg

+4
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== LACP Aggregate w/ Degraded Link
2+
==== Description
3+
Verify communication over an LACP link aggregate when individual member
4+
links stop forwarding traffic, without carrier loss.
5+
6+
.Logical network setup, link breakers (lb1 & lb2) here managed by host PC
7+
ifdef::topdoc[]
8+
image::../../test/case/ietf_interfaces/lag_failure/lag-failure.svg[]
9+
endif::topdoc[]
10+
ifndef::topdoc[]
11+
ifdef::testgroup[]
12+
image::lag_failure/lag-failure.svg[]
13+
endif::testgroup[]
14+
ifndef::testgroup[]
15+
image::lag-failure.svg[]
16+
endif::testgroup[]
17+
endif::topdoc[]
18+
19+
The host verifies connectivity with dut2 via dut1 over the aggregate for
20+
each failure mode step using the `mon` interface.
21+
22+
==== Topology
23+
ifdef::topdoc[]
24+
image::{topdoc}../../test/case/ietf_interfaces/lag_failure/topology.svg[LACP Aggregate w/ Degraded Link topology]
25+
endif::topdoc[]
26+
ifndef::topdoc[]
27+
ifdef::testgroup[]
28+
image::lag_failure/topology.svg[LACP Aggregate w/ Degraded Link topology]
29+
endif::testgroup[]
30+
ifndef::testgroup[]
31+
image::topology.svg[LACP Aggregate w/ Degraded Link topology]
32+
endif::testgroup[]
33+
endif::topdoc[]
34+
==== Test sequence
35+
. Set up topology and attach to target DUTs
36+
. Set up link aggregate, lag0, between dut1 and dut2
37+
. Initial connectivity check ...
38+
. Verify failure modes
39+
40+
41+
<<<
42+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env python3
2+
r"""LACP Aggregate w/ Degraded Link
3+
4+
Verify communication over an LACP link aggregate when individual member
5+
links stop forwarding traffic, without carrier loss.
6+
7+
.Logical network setup, link breakers (lb1 & lb2) here managed by host PC
8+
image::lag-failure.svg[]
9+
10+
The host verifies connectivity with dut2 via dut1 over the aggregate for
11+
each failure mode step using the `mon` interface.
12+
13+
"""
14+
from time import time
15+
import infamy
16+
from infamy.netns import TPMR
17+
from infamy.util import parallel
18+
19+
IPH = "192.168.2.1"
20+
IP1 = "192.168.2.41"
21+
IP2 = "192.168.2.42"
22+
23+
24+
class LinkBreaker:
25+
"""Encapsulates TPMR based link-breakers."""
26+
27+
def __init__(self, sys, netns):
28+
self.net = netns
29+
self.lb1 = TPMR(sys.ltop.xlate("host", "lb1a")[1],
30+
sys.ltop.xlate("host", "lb1b")[1]).start()
31+
self.lb2 = TPMR(sys.ltop.xlate("host", "lb2a")[1],
32+
sys.ltop.xlate("host", "lb2b")[1]).start()
33+
34+
def forward(self, lb1, lb2):
35+
"""Set link breakers in forwarding or blocking state."""
36+
getattr(self.lb1, lb1)()
37+
getattr(self.lb2, lb2)()
38+
39+
def fail_check(self, peer):
40+
"""Verify connectivity with a given peer during failure."""
41+
sequence = [
42+
("forward", "forward"),
43+
("forward", "block"),
44+
("block", "forward"),
45+
("forward", "forward")
46+
]
47+
48+
total_start = time()
49+
print(f"{'LB1':<8} | {'LB2':<8} | {'Status':<8}")
50+
print("---------|----------|---------")
51+
52+
for lb1, lb2 in sequence:
53+
state_start = time()
54+
try:
55+
print(f"{lb1:<8} | {lb2:<8} | {'...':<8}", end="\r# ")
56+
self.forward(lb1, lb2)
57+
self.net.must_reach(peer, timeout=30)
58+
print(f"{lb1:<8} | {lb2:<8} | {'OK':<8} in "
59+
f"{time() - state_start:.2f}s")
60+
except Exception as e:
61+
print(f"{lb1:<8} | {lb2:<8} | {'FAIL':<8} after "
62+
f"{time() - state_start:.2f}s")
63+
print(f"\nError encountered: {e}")
64+
print(f"Link breakers were in state: LB1='{lb1}', LB2='{lb2}'")
65+
raise
66+
67+
print(f"Total time: {time() - total_start:.2f}s")
68+
69+
70+
def net_init(host, addr):
71+
"""Set up DUT network, dut1 bridges host port with lag0"""
72+
if host:
73+
net = [{
74+
"name": "br0",
75+
"type": "infix-if-type:bridge",
76+
"ipv4": {
77+
"address": [{"ip": addr, "prefix-length": 24}]
78+
}
79+
}, {
80+
"name": host,
81+
"bridge-port": {"bridge": "br0"}
82+
}, {
83+
"name": "lag0",
84+
"bridge-port": {"bridge": "br0"}
85+
}]
86+
else:
87+
net = [{
88+
"name": "lag0",
89+
"ipv4": {
90+
"address": [{"ip": addr, "prefix-length": 24}]
91+
}
92+
}]
93+
return net
94+
95+
96+
def dut_init(dut, addr, peer):
97+
"""Configure each DUT specific according to LAG mode and peer"""
98+
net = net_init(dut["mon"], addr)
99+
100+
dut.put_config_dict("ietf-interfaces", {
101+
"interfaces": {
102+
"interface": [{
103+
"name": "lag0",
104+
"type": "infix-if-type:lag",
105+
"lag": {
106+
"mode": "lacp",
107+
"lacp": {"rate": "fast"},
108+
"link-monitor": {"interval": 100}
109+
}
110+
}, {
111+
"name": dut["link1"],
112+
"lag-port": {"lag": "lag0"}
113+
}, {
114+
"name": dut["link2"],
115+
"lag-port": {"lag": "lag0"}
116+
}] + net
117+
}
118+
})
119+
120+
121+
with infamy.Test() as test:
122+
with test.step("Set up topology and attach to target DUTs"):
123+
env = infamy.Env()
124+
dut1 = env.attach("dut1")
125+
dut2 = env.attach("dut2")
126+
127+
_, mon = env.ltop.xlate("host", "mon")
128+
with infamy.IsolatedMacVlan(mon) as ns:
129+
lb = LinkBreaker(env, ns)
130+
ns.addip(IPH)
131+
132+
print(f"Setting up lag0 in LACP mode between {dut1} and {dut2}")
133+
with test.step("Set up link aggregate, lag0, between dut1 and dut2"):
134+
parallel(lambda: dut_init(dut1, IP1, IP2),
135+
lambda: dut_init(dut2, IP2, IP1))
136+
137+
with test.step("Initial connectivity check ..."):
138+
ns.must_reach(IP2, timeout=30)
139+
140+
with test.step("Verify failure modes"):
141+
lb.fail_check(IP2)
142+
143+
test.succeed()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
graph "lag" {
2+
layout="neato";
3+
overlap="false";
4+
esep="+23";
5+
6+
node [shape=record, fontsize=12, fontname="DejaVu Sans Mono, Book"];
7+
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
8+
9+
host [
10+
label="{{ <mgmt1> mgmt1 | <mon> mon | <lb1a> lb1a | <lb2a> lb2a | <lb2b> lb2b | <lb1b> lb1b | <mgmt2> mgmt2 } | host}",
11+
pos="9,0!",
12+
requires="controller",
13+
];
14+
15+
dut1 [
16+
label="{ dut1\l | { <mgmt> mgmt | <mon> mon | <link1> link1 | <link2> link2 } }",
17+
pos="0,6!",
18+
requires="infix",
19+
];
20+
21+
dut2 [
22+
label="{ dut2\r | { <link2> link2 | <link1> link1 | <mgmt> mgmt } }",
23+
pos="18,6!",
24+
requires="infix",
25+
];
26+
27+
host:mgmt1 -- dut1:mgmt [requires="mgmt", color=lightgray]
28+
host:mon -- dut1:mon // Monitor connection to dut2 via dut1
29+
host:mgmt2 -- dut2:mgmt [requires="mgmt", color=lightgrey]
30+
31+
dut1:link1 -- host:lb1a [requires="ieee-mc", color=black, fontcolor=black]
32+
host:lb1b -- dut2:link1 [requires="ieee-mc", color=black, fontcolor=black]
33+
34+
dut1:link2 -- host:lb2a [requires="ieee-mc", color=black, fontcolor=black]
35+
host:lb2b -- dut2:link2 [requires="ieee-mc", color=black, fontcolor=black]
36+
}
Loading

0 commit comments

Comments
 (0)