Skip to content

Commit 4260d24

Browse files
troglobitwkz
authored andcommitted
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 <[email protected]>
1 parent 3ef1da1 commit 4260d24

File tree

8 files changed

+327
-0
lines changed

8 files changed

+327
-0
lines changed

test/case/ietf_interfaces/Readme.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ include::bridge_vlan_separation/Readme.adoc[]
3333

3434
include::dual_bridge/Readme.adoc[]
3535

36+
include::lag_basic/Readme.adoc[]
37+
3638
include::igmp_basic/Readme.adoc[]
3739

3840
include::igmp_vlan/Readme.adoc[]

test/case/ietf_interfaces/ietf_interfaces.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
- name: ipv4_autoconf
3636
case: ipv4_autoconf/test.py
3737

38+
- name: lag_basic
39+
case: lag_basic/test.py
40+
3841
- name: bridge_fwd_sgl_dut
3942
case: bridge_fwd_sgl_dut/test.py
4043

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

test/case/ietf_interfaces/lag_basic/lag-basic.svg

+4
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
=== Ling Aggregation Basic
2+
==== Description
3+
Verify communication over a link aggregate in static and LACP operating
4+
modes during basic failure scenarios.
5+
6+
.Internal network setup, PC verifies connectivity with dut2 via dut1
7+
ifdef::topdoc[]
8+
image::../../test/case/ietf_interfaces/lag_basic/lag-basic.svg[Internal networks]
9+
endif::topdoc[]
10+
ifndef::topdoc[]
11+
ifdef::testgroup[]
12+
image::lag_basic/lag-basic.svg[Internal networks]
13+
endif::testgroup[]
14+
ifndef::testgroup[]
15+
image::lag-basic.svg[Internal networks]
16+
endif::testgroup[]
17+
endif::topdoc[]
18+
19+
The host verifies connectivity with dut2 via dut1 over the aggregate for
20+
each test step using the `mon` interface.
21+
22+
==== Topology
23+
ifdef::topdoc[]
24+
image::{topdoc}../../test/case/ietf_interfaces/lag_basic/topology.svg[Ling Aggregation Basic topology]
25+
endif::topdoc[]
26+
ifndef::topdoc[]
27+
ifdef::testgroup[]
28+
image::lag_basic/topology.svg[Ling Aggregation Basic topology]
29+
endif::testgroup[]
30+
ifndef::testgroup[]
31+
image::topology.svg[Ling Aggregation Basic topology]
32+
endif::testgroup[]
33+
endif::topdoc[]
34+
==== Test sequence
35+
. Set up topology and attach to target DUTs
36+
. Set up LACP link aggregate, lag0, on dut1 and dut2
37+
. Verify failure modes for lacp mode
38+
. Set up static link aggregate, lag0, on dut1 and dut2
39+
. Verify failure modes for static mode
40+
41+
42+
<<<
43+
+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/usr/bin/env python3
2+
r"""Ling Aggregation Basic
3+
4+
Verify communication over a link aggregate in static and LACP operating
5+
modes during basic failure scenarios.
6+
7+
.Internal network setup, PC verifies connectivity with dut2 via dut1
8+
image::lag-basic.svg[Internal networks]
9+
10+
The host verifies connectivity with dut2 via dut1 over the aggregate for
11+
each test step using the `mon` interface.
12+
13+
"""
14+
from time import sleep, time
15+
from datetime import datetime
16+
import infamy
17+
from infamy.util import parallel, until
18+
19+
20+
class DumbLinkBreaker:
21+
"""Encapsulates basic, dumb link-breaking ops over SSH."""
22+
23+
def __init__(self, sys, dut, netns):
24+
self.env = sys
25+
self.dut = dut
26+
self.net = netns
27+
self.tgt = {}
28+
for i, (name, _) in dut.items():
29+
self.tgt[i] = env.attach(name, "mgmt", "ssh")
30+
31+
def set_link(self, link, updown):
32+
"""Set link up or down, verify before returning."""
33+
def set_and_verify(i):
34+
name, dut = self.dut[i]
35+
36+
cmd = self.tgt[i].runsh(f"sudo ip link set {dut[link]} {updown}")
37+
if cmd.returncode:
38+
for out in [cmd.stdout, cmd.stderr]:
39+
if out:
40+
print(f"{name}: {out.rstrip()}")
41+
raise RuntimeError(f"{name}: failed setting {link} {updown}")
42+
43+
for _ in range(10):
44+
sleep(0.1)
45+
check = self.tgt[i].runsh(f"ip link show {dut[link]}")
46+
if f"state {updown.upper()}" in check.stdout:
47+
break
48+
else:
49+
raise RuntimeError(f"{name}: {dut[link]} did not go {updown}")
50+
51+
parallel(*[lambda i=i: set_and_verify(i) for i in self.dut])
52+
53+
def fail_check(self, peer):
54+
"""Verify connectivity with peer during link failure."""
55+
sequence = [
56+
[("link1", "up"), ("link2", "up")],
57+
[("link1", "down"), ("link2", "up")],
58+
[("link1", "up"), ("link2", "down")],
59+
[("link1", "up"), ("link2", "up")]
60+
]
61+
62+
total_start = time()
63+
for state in sequence:
64+
state_start = time()
65+
print(f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]} {state}")
66+
67+
for link, updown in state:
68+
self.set_link(link, updown)
69+
self.net.must_reach(peer, timeout=10)
70+
71+
print(f"Completed in {time() - state_start:.2f}s")
72+
73+
print(f"Total time: {time() - total_start:.2f}s")
74+
75+
76+
def lag_init(mode):
77+
"""Set up mode specific attributes for the LAG"""
78+
if mode == "lacp":
79+
lag = [{
80+
"name": "lag0",
81+
"lag": {"lacp": {"rate": "fast"}}
82+
}]
83+
else:
84+
lag = []
85+
return lag
86+
87+
88+
def net_init(host, addr):
89+
"""Set up DUT network, dut1 bridges host port with lag0"""
90+
if host:
91+
net = [{
92+
"name": "br0",
93+
"type": "infix-if-type:bridge",
94+
}, {
95+
"name": host,
96+
"bridge-port": {"bridge": "br0"}
97+
}, {
98+
"name": "lag0",
99+
"bridge-port": {"bridge": "br0"}
100+
}]
101+
else:
102+
net = [{
103+
"name": "lag0",
104+
"ipv4": {
105+
"address": [{"ip": addr, "prefix-length": 24}]
106+
}
107+
}]
108+
return net
109+
110+
111+
def dut_init(dut, mode, addr):
112+
"""Set up link aggregate on dut"""
113+
net = net_init(dut["mon"], addr)
114+
lag = lag_init(mode)
115+
116+
dut.put_config_dict("ietf-interfaces", {
117+
"interfaces": {
118+
"interface": [{
119+
"name": "lag0",
120+
"type": "infix-if-type:lag",
121+
"lag": {
122+
"mode": mode,
123+
"link-monitor": {"interval": 100}
124+
}
125+
}, {
126+
"name": dut["link1"],
127+
"lag-port": {"lag": "lag0"}
128+
}, {
129+
"name": dut["link2"],
130+
"lag-port": {"lag": "lag0"}
131+
}] + net + lag
132+
}
133+
})
134+
135+
136+
with infamy.Test() as test:
137+
with test.step("Set up topology and attach to target DUTs"):
138+
env = infamy.Env()
139+
dut1 = env.attach("dut1", "mgmt")
140+
dut2 = env.attach("dut2", "mgmt")
141+
142+
_, mon = env.ltop.xlate("host", "mon")
143+
with infamy.IsolatedMacVlan(mon) as ns:
144+
dm = {
145+
'1': ("dut1", dut1),
146+
'2': ("dut2", dut2)
147+
}
148+
lb = DumbLinkBreaker(env, dm, ns)
149+
ns.addip("192.168.2.1")
150+
151+
with test.step("Set up LACP link aggregate, lag0, on dut1 and dut2"):
152+
parallel(lambda: dut_init(dut1, "lacp", None),
153+
lambda: dut_init(dut2, "lacp", "192.168.2.42"))
154+
155+
with test.step("Verify failure modes for lacp mode"):
156+
lb.fail_check("192.168.2.42")
157+
158+
with test.step("Set up static link aggregate, lag0, on dut1 and dut2"):
159+
parallel(lambda: dut_init(dut1, "static", None),
160+
lambda: dut_init(dut2, "static", "192.168.2.42"))
161+
162+
with test.step("Verify failure modes for static mode"):
163+
lb.fail_check("192.168.2.42")
164+
165+
test.succeed()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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="host | { <mgmt1> mgmt1 | <mon> mon | \n\n\n\n | <mgmt2> mgmt2 }",
11+
pos="0,15!",
12+
requires="controller",
13+
];
14+
15+
dut1 [
16+
label="{ <mgmt> mgmt | <mon> mon } | { dut1\r | { <link1> link1 | <link2> link2 } }",
17+
pos="2,15.25!",
18+
requires="infix",
19+
];
20+
21+
dut2 [
22+
label="<mgmt> mgmt | { { <link1> link1 | <link2> link2 } | dut2\r }",
23+
pos="2,14.75!",
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 -- dut2:link1 [color=black, fontcolor=black, penwidth=3]
32+
dut1:link2 -- dut2:link2 [color=black, fontcolor=black, penwidth=3]
33+
}
Loading

0 commit comments

Comments
 (0)