Skip to content

Commit 60e3fa0

Browse files
author
Ahmed Karic
committed
test: Verify auto-negotiated speed/duplex (copper)
1 parent f3c6b0a commit 60e3fa0

File tree

7 files changed

+244
-0
lines changed

7 files changed

+244
-0
lines changed

test/case/ietf_interfaces/Readme.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ include::bridge_veth/Readme.adoc[]
2929

3030
include::bridge_vlan/Readme.adoc[]
3131

32+
include::speed_duplex_copper/Readme.adoc[]
33+
3234
include::bridge_vlan_separation/Readme.adoc[]
3335

3436
include::dual_bridge/Readme.adoc[]

test/case/ietf_interfaces/ietf_interfaces.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
- name: vlan_qos
2121
case: vlan_qos/test.py
2222

23+
- name: speed_duplex_copper
24+
case: speed_duplex_copper/test.py
25+
2326
- name: verify_all_interface_types
2427
case: verify_all_interface_types/test.py
2528

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
speed_duplex_copper.adoc
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== Interface Speed Duplex (Copper)
2+
==== Description
3+
Verify that auto-negotiation results in expected speed/duplex mode.
4+
5+
==== Topology
6+
ifdef::topdoc[]
7+
image::{topdoc}../../test/case/ietf_interfaces/speed_duplex_copper/topology.svg[Interface Speed Duplex (Copper) topology]
8+
endif::topdoc[]
9+
ifndef::topdoc[]
10+
ifdef::testgroup[]
11+
image::speed_duplex_copper/topology.svg[Interface Speed Duplex (Copper) topology]
12+
endif::testgroup[]
13+
ifndef::testgroup[]
14+
image::topology.svg[Interface Speed Duplex (Copper) topology]
15+
endif::testgroup[]
16+
endif::topdoc[]
17+
==== Test sequence
18+
. Set up topology and attach to target DUT
19+
. Enable target interface and autonegotiation
20+
. Advertise 10/Full only
21+
. Advertise 10/Half only
22+
. Advertise 100/Full only
23+
. Advertise 100/Half only
24+
. Advertise 10/half + 10/full + 100/half
25+
. Advertise 10/half + 10/full + 100/half + 100/full + 1000/full
26+
27+
28+
<<<
29+
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Interface Speed Duplex (Copper)
4+
5+
Verify that auto-negotiation results in expected speed/duplex mode.
6+
"""
7+
8+
import infamy
9+
import infamy.iface as iface
10+
import subprocess
11+
from infamy.util import until
12+
13+
ADVERTISE_MODES = {
14+
# Values from ethtool's ETHTOOL_LINK_MODE bit positions
15+
# See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ethtool.h
16+
"10half": 0x0001,
17+
"10full": 0x0002,
18+
"100half": 0x0004,
19+
"100full": 0x0008,
20+
"1000full": 0x0020
21+
}
22+
23+
def advertise_host_modes(interface, modes):
24+
mask = 0
25+
for mode in modes:
26+
mask |= ADVERTISE_MODES[mode]
27+
try:
28+
subprocess.run([
29+
"ethtool", "-s", interface,
30+
"autoneg", "on",
31+
"advertise", hex(mask)
32+
], check=True)
33+
except subprocess.CalledProcessError as e:
34+
raise RuntimeError(f"Failed to advertise modes via ethtool: {e}")
35+
36+
def get_target_speed_duplex(target, interface):
37+
path = iface.get_xpath(interface)
38+
content = target.get_data(path)
39+
if not content or "interfaces" not in content:
40+
return None, None
41+
42+
for intf in content["interfaces"].get("interface", []):
43+
if intf.get("name") != interface:
44+
continue
45+
46+
eth = intf.get("ethernet") or intf.get("ieee802-ethernet-interface:ethernet")
47+
if not eth:
48+
return None, None
49+
50+
return eth.get("speed"), eth.get("duplex")
51+
52+
return None, None
53+
54+
def set_target_speed_duplex(target, interface, speed, duplex):
55+
target.put_config_dicts({
56+
"ietf-interfaces": {
57+
"interfaces": {
58+
"interface": [{
59+
"name": interface,
60+
"ethernet": {
61+
"auto-negotiation": {
62+
"enable": False
63+
},
64+
"speed": speed / 1000,
65+
"duplex": duplex
66+
}
67+
}]
68+
}
69+
}
70+
})
71+
72+
def verify_speed_duplex(target, interface, exp_speed, exp_duplex):
73+
until(lambda: speed_duplex_present(target, interface))
74+
act_speed, act_duplex = get_target_speed_duplex(target, interface)
75+
if act_speed is None or act_duplex is None:
76+
print(f"Could not fetch speed or duplex from target for interface {interface}")
77+
test.fail()
78+
79+
exp_speed_gbps = exp_speed / 1000
80+
if float(act_speed) != exp_speed_gbps:
81+
print(f"act_speed: {act_speed}, exp_speed: {exp_speed_gbps}")
82+
test.fail()
83+
84+
if act_duplex.lower() != exp_duplex.lower():
85+
print(f"act_duplex: {act_duplex}, exp_duplex: {exp_duplex}")
86+
test.fail()
87+
88+
def speed_duplex_present(target, interface):
89+
speed, duplex = get_target_speed_duplex(target, interface)
90+
return speed is not None and duplex is not None
91+
92+
def enable_target_autoneg(target, interface):
93+
target.put_config_dicts({
94+
"ietf-interfaces": {
95+
"interfaces": {
96+
"interface": [{
97+
"name": interface,
98+
"enabled": True,
99+
"ethernet": {
100+
"auto-negotiation": {
101+
"enable": True
102+
}
103+
}
104+
}]
105+
}
106+
}
107+
})
108+
109+
with infamy.Test() as test:
110+
with test.step("Set up topology and attach to target DUT"):
111+
env = infamy.Env()
112+
target = env.attach("target", "mgmt")
113+
_, hdata = env.ltop.xlate("host", "data")
114+
_, tdata = env.ltop.xlate("target", "data")
115+
116+
with test.step("Enable target interface and autonegotiation"):
117+
enable_target_autoneg(target, tdata)
118+
119+
with test.step("Advertise 10/Full only"):
120+
advertise_host_modes(hdata, ["10full"])
121+
verify_speed_duplex(target, tdata, 10, "full")
122+
123+
with test.step("Advertise 10/Half only"):
124+
advertise_host_modes(hdata, ["10half"])
125+
verify_speed_duplex(target, tdata, 10, "half")
126+
127+
with test.step("Advertise 100/Full only"):
128+
advertise_host_modes(hdata, ["100full"])
129+
verify_speed_duplex(target, tdata, 100, "full")
130+
131+
with test.step("Advertise 100/Half only"):
132+
advertise_host_modes(hdata, ["100half"])
133+
verify_speed_duplex(target, tdata, 100, "half")
134+
135+
with test.step("Advertise 10/half + 10/full + 100/half"):
136+
advertise_host_modes(hdata, ["10half", "10full", "100half"])
137+
verify_speed_duplex(target, tdata, 100, "half")
138+
139+
with test.step("Advertise 10/half + 10/full + 100/half + 100/full + 1000/full"):
140+
advertise_host_modes(hdata, ["10half", "10full", "100half", "100full", "1000full"])
141+
verify_speed_duplex(target, tdata, 1000, "full")
142+
143+
test.succeed()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
graph "1x2" {
2+
layout="neato";
3+
overlap="false";
4+
esep="+80";
5+
6+
node [shape=record, fontname="DejaVu Sans Mono, Book"];
7+
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
8+
9+
host [
10+
label="host | { <mgmt> mgmt | <data> data }",
11+
pos="0,12!",
12+
requires="controller",
13+
];
14+
15+
target [
16+
label="{ <mgmt> mgmt | <data> data } | target",
17+
pos="10,12!",
18+
19+
requires="infix",
20+
];
21+
22+
host:mgmt -- target:mgmt [requires="mgmt", color=lightgrey]
23+
host:data -- target:data [color=black, requires="link-ctrl copper"]
24+
}
Lines changed: 42 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)