diff --git a/test/case/ietf_interfaces/Readme.adoc b/test/case/ietf_interfaces/Readme.adoc index 42f6061e..de861eb0 100644 --- a/test/case/ietf_interfaces/Readme.adoc +++ b/test/case/ietf_interfaces/Readme.adoc @@ -35,6 +35,8 @@ include::dual_bridge/Readme.adoc[] include::lag_basic/Readme.adoc[] +include::lag_failure/Readme.adoc[] + include::igmp_basic/Readme.adoc[] include::igmp_vlan/Readme.adoc[] diff --git a/test/case/ietf_interfaces/ietf_interfaces.yaml b/test/case/ietf_interfaces/ietf_interfaces.yaml index 4709465b..88841fd3 100644 --- a/test/case/ietf_interfaces/ietf_interfaces.yaml +++ b/test/case/ietf_interfaces/ietf_interfaces.yaml @@ -38,6 +38,9 @@ - name: lag_basic case: lag_basic/test.py +- name: lag_failure + case: lag_failure/test.py + - name: bridge_fwd_sgl_dut case: bridge_fwd_sgl_dut/test.py diff --git a/test/case/ietf_interfaces/lag_failure/Readme.adoc b/test/case/ietf_interfaces/lag_failure/Readme.adoc new file mode 120000 index 00000000..ede0d845 --- /dev/null +++ b/test/case/ietf_interfaces/lag_failure/Readme.adoc @@ -0,0 +1 @@ +lag_failure.adoc \ No newline at end of file diff --git a/test/case/ietf_interfaces/lag_failure/lag-failure.svg b/test/case/ietf_interfaces/lag_failure/lag-failure.svg new file mode 100644 index 00000000..15963fed --- /dev/null +++ b/test/case/ietf_interfaces/lag_failure/lag-failure.svg @@ -0,0 +1,4 @@ + + + +
lag
dut1
eth
eth
eth
bridge
eth
eth
lag
ip
pc
eth
ip
dut2
lb1
lb2
\ No newline at end of file diff --git a/test/case/ietf_interfaces/lag_failure/lag_failure.adoc b/test/case/ietf_interfaces/lag_failure/lag_failure.adoc new file mode 100644 index 00000000..92fa04f6 --- /dev/null +++ b/test/case/ietf_interfaces/lag_failure/lag_failure.adoc @@ -0,0 +1,42 @@ +=== LACP Aggregate w/ Degraded Link +==== Description +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 +ifdef::topdoc[] +image::../../test/case/ietf_interfaces/lag_failure/lag-failure.svg[] +endif::topdoc[] +ifndef::topdoc[] +ifdef::testgroup[] +image::lag_failure/lag-failure.svg[] +endif::testgroup[] +ifndef::testgroup[] +image::lag-failure.svg[] +endif::testgroup[] +endif::topdoc[] + +The host verifies connectivity with dut2 via dut1 over the aggregate for +each failure mode step using the `mon` interface. + +==== Topology +ifdef::topdoc[] +image::{topdoc}../../test/case/ietf_interfaces/lag_failure/topology.svg[LACP Aggregate w/ Degraded Link topology] +endif::topdoc[] +ifndef::topdoc[] +ifdef::testgroup[] +image::lag_failure/topology.svg[LACP Aggregate w/ Degraded Link topology] +endif::testgroup[] +ifndef::testgroup[] +image::topology.svg[LACP Aggregate w/ Degraded Link topology] +endif::testgroup[] +endif::topdoc[] +==== Test sequence +. Set up topology and attach to target DUTs +. Set up link aggregate, lag0, between dut1 and dut2 +. Initial connectivity check ... +. Verify failure modes + + +<<< + diff --git a/test/case/ietf_interfaces/lag_failure/test.py b/test/case/ietf_interfaces/lag_failure/test.py new file mode 100755 index 00000000..315c2236 --- /dev/null +++ b/test/case/ietf_interfaces/lag_failure/test.py @@ -0,0 +1,143 @@ +#!/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 +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() + 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() diff --git a/test/case/ietf_interfaces/lag_failure/topology.dot b/test/case/ietf_interfaces/lag_failure/topology.dot new file mode 100644 index 00000000..8b24b884 --- /dev/null +++ b/test/case/ietf_interfaces/lag_failure/topology.dot @@ -0,0 +1,36 @@ +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="{{ mgmt1 | mon | lb1a | lb2a | lb2b | lb1b | mgmt2 } | host}", + pos="9,0!", + requires="controller", + ]; + + dut1 [ + label="{ dut1\l | { mgmt | mon | link1 | link2 } }", + pos="0,6!", + requires="infix", + ]; + + dut2 [ + label="{ dut2\r | { link2 | link1 | mgmt } }", + pos="18,6!", + 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 -- host:lb1a [requires="ieee-mc", color=black, fontcolor=black] + host:lb1b -- dut2:link1 [requires="ieee-mc", color=black, fontcolor=black] + + dut1:link2 -- host:lb2a [requires="ieee-mc", color=black, fontcolor=black] + host:lb2b -- dut2:link2 [requires="ieee-mc", color=black, fontcolor=black] +} diff --git a/test/case/ietf_interfaces/lag_failure/topology.svg b/test/case/ietf_interfaces/lag_failure/topology.svg new file mode 100644 index 00000000..44888247 --- /dev/null +++ b/test/case/ietf_interfaces/lag_failure/topology.svg @@ -0,0 +1,93 @@ + + + + + + +lag + + + +host + +mgmt1 + +mon + +lb1a + +lb2a + +lb2b + +lb1b + +mgmt2 + +host + + + +dut1 + +dut1 + +mgmt + +mon + +link1 + +link2 + + + +host:mgmt1--dut1:mgmt + + + + +host:mon--dut1:mon + + + + +dut2 + +dut2 + +link2 + +link1 + +mgmt + + + +host:mgmt2--dut2:mgmt + + + + +host:lb1b--dut2:link1 + + + + +host:lb2b--dut2:link2 + + + + +dut1:link1--host:lb1a + + + + +dut1:link2--host:lb2a + + + +