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 ()
0 commit comments