Files
infix/test/case/interfaces/speed_duplex_copper/test.py
T
Joachim Wiberg 1bbd80d8c7 test: drop leading ietf/infix prefix from directories
Let's drop the leading IETF or Infix prefixes from tests.  Initially the
idea was to mimnic the YANG models, but it's difficult to navigate and
does not provide any real benefit to developers or end-users.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-20 20:23:23 +01:00

220 lines
7.7 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Interface Speed Duplex (Copper)
Verify that the interface operates at the expected speed/duplex in two scenarios:
1. Fixed configuration host and target are both manually set to a specific speed/duplex
2. Auto-negotiation host advertises selectable modes and the target negotiates
to the highest common speed/duplex.
"""
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("Verify 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("Verify 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("Verify 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("Verify 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("Verify auto-negotiation to 10/Full only"):
advertise_host_modes(hdata, ["10full"])
verify_speed_duplex(target, ns, tdata, 10, "full")
with test.step("Verify auto-negotiation to 10/Half only"):
advertise_host_modes(hdata, ["10half"])
verify_speed_duplex(target, ns, tdata, 10, "half")
with test.step("Verify auto-negotiation to 100/Full only"):
advertise_host_modes(hdata, ["100full"])
verify_speed_duplex(target, ns, tdata, 100, "full")
with test.step("Verify auto-negotiation to 100/Half only"):
advertise_host_modes(hdata, ["100half"])
verify_speed_duplex(target, ns, tdata, 100, "half")
with test.step("Verify auto-negotiation to 10/half + 10/full + 100/half"):
advertise_host_modes(hdata, ["10half", "10full", "100half"])
verify_speed_duplex(target, ns, tdata, 100, "half")
with test.step("Verify auto-negotiation to 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()