mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
test: Verify auto-negotiated speed/duplex (copper)
This commit is contained in:
@@ -29,6 +29,8 @@ include::bridge_veth/Readme.adoc[]
|
||||
|
||||
include::bridge_vlan/Readme.adoc[]
|
||||
|
||||
include::speed_duplex_copper/Readme.adoc[]
|
||||
|
||||
include::bridge_vlan_separation/Readme.adoc[]
|
||||
|
||||
include::dual_bridge/Readme.adoc[]
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
- name: vlan_qos
|
||||
case: vlan_qos/test.py
|
||||
|
||||
- name: speed_duplex_copper
|
||||
case: speed_duplex_copper/test.py
|
||||
|
||||
- name: verify_all_interface_types
|
||||
case: verify_all_interface_types/test.py
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
speed_duplex_copper.adoc
|
||||
@@ -0,0 +1,34 @@
|
||||
=== Interface Speed Duplex (Copper)
|
||||
==== Description
|
||||
Verify that auto-negotiation results in expected speed/duplex mode.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
image::{topdoc}../../test/case/ietf_interfaces/speed_duplex_copper/topology.svg[Interface Speed Duplex (Copper) topology]
|
||||
endif::topdoc[]
|
||||
ifndef::topdoc[]
|
||||
ifdef::testgroup[]
|
||||
image::speed_duplex_copper/topology.svg[Interface Speed Duplex (Copper) topology]
|
||||
endif::testgroup[]
|
||||
ifndef::testgroup[]
|
||||
image::topology.svg[Interface Speed Duplex (Copper) topology]
|
||||
endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Set up topology and attach to target DUT
|
||||
. Enable target interface
|
||||
. Set fixed 10/full
|
||||
. Set fixed 10/half
|
||||
. Set fixed 100/full
|
||||
. Set fixed 100/half
|
||||
. Switch to auto-negotiation mode for target and host
|
||||
. Configure host to advertise 10/Full only
|
||||
. Configure host to advertise 10/Half only
|
||||
. Configure host to advertise 100/Full only
|
||||
. Configure host to advertise 100/Half only
|
||||
. Configure host to advertise 10/half + 10/full + 100/half
|
||||
. Configure host to advertise 10/half + 10/full + 100/half + 100/full + 1000/full
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Interface Speed Duplex (Copper)
|
||||
|
||||
Verify that auto-negotiation results in expected speed/duplex mode.
|
||||
"""
|
||||
|
||||
import infamy
|
||||
import subprocess
|
||||
from infamy.util import until
|
||||
|
||||
ADVERTISE_MODES = {
|
||||
# Values from ethtool's ETHTOOL_LINK_MODE bit positions
|
||||
# See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ethtool.h
|
||||
"10half": 0x0001,
|
||||
"10full": 0x0002,
|
||||
"100half": 0x0004,
|
||||
"100full": 0x0008,
|
||||
"1000full": 0x0020
|
||||
}
|
||||
|
||||
def advertise_host_modes(interface, modes):
|
||||
mask = 0
|
||||
for mode in modes:
|
||||
mask |= ADVERTISE_MODES[mode]
|
||||
try:
|
||||
subprocess.run([
|
||||
"ethtool", "-s", interface,
|
||||
"advertise", hex(mask)
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to advertise modes via ethtool: {e}")
|
||||
|
||||
def get_target_speed_duplex(target, interface):
|
||||
data = target.get_data(f"/ietf-interfaces:interfaces/interface[name='{interface}']") \
|
||||
["interfaces"]["interface"][interface]
|
||||
eth = data.get("ethernet", {})
|
||||
|
||||
return eth.get("speed"), eth.get("duplex")
|
||||
|
||||
def set_target_speed_duplex(target, interface, speed, duplex):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": interface,
|
||||
"ethernet": {
|
||||
"auto-negotiation": {
|
||||
"enable": False
|
||||
},
|
||||
"speed": speed / 1000,
|
||||
"duplex": duplex
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
def set_host_speed_duplex(interface, speed, duplex):
|
||||
try:
|
||||
subprocess.run([
|
||||
"ethtool", "-s", interface,
|
||||
"speed", str(speed),
|
||||
"duplex", duplex,
|
||||
"autoneg", "off"
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to set speed/duplex via ethtool: {e}")
|
||||
|
||||
def verify_speed_duplex(target, ns, interface, exp_speed, exp_duplex):
|
||||
until(lambda: speed_duplex_present(target, interface))
|
||||
act_speed, act_duplex = get_target_speed_duplex(target, interface)
|
||||
if act_speed is None or act_duplex is None:
|
||||
print(f"Could not fetch speed or duplex from target for interface {interface}")
|
||||
test.fail()
|
||||
|
||||
exp_speed_gbps = exp_speed / 1000
|
||||
if float(act_speed) != exp_speed_gbps:
|
||||
print(f"act_speed: {act_speed}, exp_speed: {exp_speed_gbps}")
|
||||
test.fail()
|
||||
|
||||
if act_duplex.lower() != exp_duplex.lower():
|
||||
print(f"act_duplex: {act_duplex}, exp_duplex: {exp_duplex}")
|
||||
test.fail()
|
||||
|
||||
ns.must_reach("10.0.0.2")
|
||||
|
||||
print(f"Verified: {interface} is operating at {act_speed} Gbps, {act_duplex} duplex")
|
||||
|
||||
def speed_duplex_present(target, interface):
|
||||
speed, duplex = get_target_speed_duplex(target, interface)
|
||||
return speed is not None and duplex is not None
|
||||
|
||||
def enable_target_interface(target, interface):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": interface,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [
|
||||
{
|
||||
"ip": "10.0.0.2",
|
||||
"prefix-length": 24
|
||||
}
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
def enable_target_autoneg(target, interface):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": interface,
|
||||
"ethernet": {
|
||||
"auto-negotiation": {
|
||||
"enable": True
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
def enable_host_autoneg(interface):
|
||||
subprocess.run(["ethtool", "-s", interface, "autoneg", "on"], check=True)
|
||||
|
||||
def cleanup(target, hdata, tdata):
|
||||
"""
|
||||
Restore both host and target interfaces to autonegotiation mode
|
||||
to ensure clean state for future tests.
|
||||
"""
|
||||
|
||||
print("Restoring interfaces to default (autoneg on)")
|
||||
try:
|
||||
enable_host_autoneg(hdata)
|
||||
except Exception as e:
|
||||
print(f"Host autoneg restore failed: {e}")
|
||||
try:
|
||||
enable_target_interface(target, tdata)
|
||||
enable_target_autoneg(target, tdata)
|
||||
except Exception as e:
|
||||
print(f"Target autoneg restore failed: {e}")
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUT"):
|
||||
env = infamy.Env()
|
||||
target = env.attach("target", "mgmt")
|
||||
_, hdata = env.ltop.xlate("host", "data")
|
||||
_, tdata = env.ltop.xlate("target", "data")
|
||||
|
||||
# Append a test cleanup function
|
||||
test.push_test_cleanup(lambda: cleanup(target, hdata, tdata))
|
||||
|
||||
with test.step("Enable target interface"):
|
||||
enable_target_interface(target, tdata)
|
||||
|
||||
with infamy.IsolatedMacVlan(hdata) as ns:
|
||||
ns.addip("10.0.0.1")
|
||||
|
||||
# Fixed mode tests
|
||||
with test.step("Set fixed 10/full"):
|
||||
set_host_speed_duplex(hdata, 10, "full")
|
||||
set_target_speed_duplex(target, tdata, 10, "full")
|
||||
verify_speed_duplex(target, ns, tdata, 10, "full")
|
||||
|
||||
with test.step("Set fixed 10/half"):
|
||||
set_host_speed_duplex(hdata, 10, "half")
|
||||
set_target_speed_duplex(target, tdata, 10, "half")
|
||||
verify_speed_duplex(target, ns, tdata, 10, "half")
|
||||
|
||||
with test.step("Set fixed 100/full"):
|
||||
set_host_speed_duplex(hdata, 100, "full")
|
||||
set_target_speed_duplex(target, tdata, 100, "full")
|
||||
verify_speed_duplex(target, ns, tdata, 100, "full")
|
||||
|
||||
with test.step("Set fixed 100/half"):
|
||||
set_host_speed_duplex(hdata, 100, "half")
|
||||
set_target_speed_duplex(target, tdata, 100, "half")
|
||||
verify_speed_duplex(target, ns, tdata, 100, "half")
|
||||
|
||||
# Auto-negotiation tests: host advertises, Infix negotiates
|
||||
with test.step("Switch to auto-negotiation mode for target and host"):
|
||||
enable_host_autoneg(hdata)
|
||||
enable_target_autoneg(target, tdata)
|
||||
|
||||
with test.step("Configure host to advertise 10/Full only"):
|
||||
advertise_host_modes(hdata, ["10full"])
|
||||
verify_speed_duplex(target, ns, tdata, 10, "full")
|
||||
|
||||
with test.step("Configure host to advertise 10/Half only"):
|
||||
advertise_host_modes(hdata, ["10half"])
|
||||
verify_speed_duplex(target, ns, tdata, 10, "half")
|
||||
|
||||
with test.step("Configure host to advertise 100/Full only"):
|
||||
advertise_host_modes(hdata, ["100full"])
|
||||
verify_speed_duplex(target, ns, tdata, 100, "full")
|
||||
|
||||
with test.step("Configure host to advertise 100/Half only"):
|
||||
advertise_host_modes(hdata, ["100half"])
|
||||
verify_speed_duplex(target, ns, tdata, 100, "half")
|
||||
|
||||
with test.step("Configure host to advertise 10/half + 10/full + 100/half"):
|
||||
advertise_host_modes(hdata, ["10half", "10full", "100half"])
|
||||
verify_speed_duplex(target, ns, tdata, 100, "half")
|
||||
|
||||
with test.step("Configure host to advertise 10/half + 10/full + 100/half + 100/full + 1000/full"):
|
||||
advertise_host_modes(hdata, ["10half", "10full", "100half", "100full", "1000full"])
|
||||
verify_speed_duplex(target, ns, tdata, 1000, "full")
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1,24 @@
|
||||
graph "1x2" {
|
||||
layout="neato";
|
||||
overlap="false";
|
||||
esep="+80";
|
||||
|
||||
node [shape=record, fontname="DejaVu Sans Mono, Book"];
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
host [
|
||||
label="host | { <mgmt> mgmt | <data> data }",
|
||||
pos="0,12!",
|
||||
requires="controller",
|
||||
];
|
||||
|
||||
target [
|
||||
label="{ <mgmt> mgmt | <data> data } | target",
|
||||
pos="10,12!",
|
||||
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
host:mgmt -- target:mgmt [requires="mgmt", color=lightgrey]
|
||||
host:data -- target:data [color=black, requires="link-ctrl copper"]
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: 1x2 Pages: 1 -->
|
||||
<svg width="424pt" height="55pt"
|
||||
viewBox="0.00 0.00 424.03 55.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 51)">
|
||||
<title>1x2</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-51 420.03,-51 420.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-46.5 100,-46.5 100,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-46.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-23.5 100,-23.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
</g>
|
||||
<!-- target -->
|
||||
<g id="node2" class="node">
|
||||
<title>target</title>
|
||||
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-46.5 416.03,-46.5 416.03,-0.5 300.03,-0.5"/>
|
||||
<text text-anchor="middle" x="325.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="300.03,-23.5 350.03,-23.5 "/>
|
||||
<text text-anchor="middle" x="325.03" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-46.5 "/>
|
||||
<text text-anchor="middle" x="383.03" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--target:mgmt</title>
|
||||
<path fill="none" stroke="lightgrey" stroke-width="2" d="M100,-35.5C100,-35.5 300.03,-35.5 300.03,-35.5"/>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>host:data--target:data</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M100,-11.5C100,-11.5 300.03,-11.5 300.03,-11.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
Reference in New Issue
Block a user