2023-03-29 15:16:55 -07:00
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
import argparse
import json
import pprint
import sys
import re
from lib import YnlFamily
def args_to_req ( ynl , op_name , args , req ) :
"""
Verify and convert command - line arguments to the ynl - compatible request .
"""
valid_attrs = ynl . operation_do_attributes ( op_name )
valid_attrs . remove ( ' header ' ) # not user-provided
if len ( args ) == 0 :
print ( f ' no attributes, expected: { valid_attrs } ' )
sys . exit ( 1 )
i = 0
while i < len ( args ) :
attr = args [ i ]
if i + 1 > = len ( args ) :
print ( f ' expected value for \' { attr } \' ' )
sys . exit ( 1 )
if attr not in valid_attrs :
print ( f ' invalid attribute \' { attr } \' , expected: { valid_attrs } ' )
sys . exit ( 1 )
val = args [ i + 1 ]
i + = 2
req [ attr ] = val
def print_field ( reply , * desc ) :
"""
Pretty - print a set of fields from the reply . desc specifies the
fields and the optional type ( bool / yn ) .
"""
if len ( desc ) == 0 :
return print_field ( reply , * zip ( reply . keys ( ) , reply . keys ( ) ) )
for spec in desc :
try :
field , name , tp = spec
except :
field , name = spec
tp = ' int '
value = reply . get ( field , None )
if tp == ' yn ' :
value = ' yes ' if value else ' no '
elif tp == ' bool ' or isinstance ( value , bool ) :
value = ' on ' if value else ' off '
else :
value = ' n/a ' if value is None else value
print ( f ' { name } : { value } ' )
def print_speed ( name , value ) :
"""
Print out the speed - like strings from the value dict .
"""
speed_re = re . compile ( r ' [0-9]+base[^/]+/.+ ' )
speed = [ k for k , v in value . items ( ) if v and speed_re . match ( k ) ]
print ( f ' { name } : { " " . join ( speed ) } ' )
def doit ( ynl , args , op_name ) :
"""
Prepare request header , parse arguments and doit .
"""
req = {
' header ' : {
' dev-name ' : args . device ,
} ,
}
args_to_req ( ynl , op_name , args . args , req )
ynl . do ( op_name , req )
def dumpit ( ynl , args , op_name , extra = { } ) :
"""
Prepare request header , parse arguments and dumpit ( filtering out the
devices we ' re not interested in).
"""
reply = ynl . dump ( op_name , { ' header ' : { } } | extra )
if not reply :
return { }
for msg in reply :
if msg [ ' header ' ] [ ' dev-name ' ] == args . device :
if args . json :
pprint . PrettyPrinter ( ) . pprint ( msg )
sys . exit ( 0 )
msg . pop ( ' header ' , None )
return msg
print ( f " Not supported for device { args . device } " )
sys . exit ( 1 )
def bits_to_dict ( attr ) :
"""
Convert ynl - formatted bitmask to a dict of bit = value .
"""
ret = { }
if ' bits ' not in attr :
return dict ( )
if ' bit ' not in attr [ ' bits ' ] :
return dict ( )
for bit in attr [ ' bits ' ] [ ' bit ' ] :
if bit [ ' name ' ] == ' ' :
continue
name = bit [ ' name ' ]
value = bit . get ( ' value ' , False )
ret [ name ] = value
return ret
def main ( ) :
parser = argparse . ArgumentParser ( description = ' ethtool wannabe ' )
parser . add_argument ( ' --json ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' --show-priv-flags ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' --set-priv-flags ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' --show-eee ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' --set-eee ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -a ' , ' --show-pause ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -A ' , ' --set-pause ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -c ' , ' --show-coalesce ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -C ' , ' --set-coalesce ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -g ' , ' --show-ring ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -G ' , ' --set-ring ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -k ' , ' --show-features ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -K ' , ' --set-features ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -l ' , ' --show-channels ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -L ' , ' --set-channels ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -T ' , ' --show-time-stamping ' , action = argparse . BooleanOptionalAction )
parser . add_argument ( ' -S ' , ' --statistics ' , action = argparse . BooleanOptionalAction )
# TODO: --show-tunnels tunnel-info-get
# TODO: --show-module module-get
# TODO: --get-plca-cfg plca-get
# TODO: --get-plca-status plca-get-status
# TODO: --show-mm mm-get
# TODO: --show-fec fec-get
# TODO: --dump-module-eerpom module-eeprom-get
# TODO: pse-get
# TODO: rss-get
parser . add_argument ( ' device ' , metavar = ' device ' , type = str )
parser . add_argument ( ' args ' , metavar = ' args ' , type = str , nargs = ' * ' )
global args
args = parser . parse_args ( )
tools: ynl: Remove absolute paths to yaml files from ethtool testing tool
Absolute paths for the spec and schema files make the ethtool testing tool
unusable with freshly checked-out source trees. Replace absolute paths with
relative paths for both files in the Documentation/ directory.
Issue seen before the change
Traceback (most recent call last):
File "/home/binary-eater/Documents/mlx/linux/tools/net/ynl/./ethtool", line 424, in <module>
main()
File "/home/binary-eater/Documents/mlx/linux/tools/net/ynl/./ethtool", line 158, in main
ynl = YnlFamily(spec, schema)
File "/home/binary-eater/Documents/mlx/linux/tools/net/ynl/lib/ynl.py", line 342, in __init__
super().__init__(def_path, schema)
File "/home/binary-eater/Documents/mlx/linux/tools/net/ynl/lib/nlspec.py", line 333, in __init__
with open(spec_path, "r") as stream:
FileNotFoundError: [Errno 2] No such file or directory: '/usr/local/google/home/sdf/src/linux/Documentation/netlink/specs/ethtool.yaml'
Fixes: f3d07b02b2b8 ("tools: ynl: ethtool testing tool")
Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
Link: https://lore.kernel.org/r/20230413012252.184434-1-rrameshbabu@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-04-12 18:22:51 -07:00
spec = ' ../../../Documentation/netlink/specs/ethtool.yaml '
schema = ' ../../../Documentation/netlink/genetlink-legacy.yaml '
2023-03-29 15:16:55 -07:00
ynl = YnlFamily ( spec , schema )
if args . set_priv_flags :
# TODO: parse the bitmask
print ( " not implemented " )
return
if args . set_eee :
return doit ( ynl , args , ' eee-set ' )
if args . set_pause :
return doit ( ynl , args , ' pause-set ' )
if args . set_coalesce :
return doit ( ynl , args , ' coalesce-set ' )
if args . set_features :
# TODO: parse the bitmask
print ( " not implemented " )
return
if args . set_channels :
return doit ( ynl , args , ' channels-set ' )
if args . set_ring :
return doit ( ynl , args , ' rings-set ' )
if args . show_priv_flags :
flags = bits_to_dict ( dumpit ( ynl , args , ' privflags-get ' ) [ ' flags ' ] )
print_field ( flags )
return
if args . show_eee :
eee = dumpit ( ynl , args , ' eee-get ' )
ours = bits_to_dict ( eee [ ' modes-ours ' ] )
peer = bits_to_dict ( eee [ ' modes-peer ' ] )
if ' enabled ' in eee :
status = ' enabled ' if eee [ ' enabled ' ] else ' disabled '
if ' active ' in eee and eee [ ' active ' ] :
status = status + ' - active '
else :
status = status + ' - inactive '
else :
status = ' not supported '
print ( f ' EEE status: { status } ' )
print_field ( eee , ( ' tx-lpi-timer ' , ' Tx LPI ' ) )
print_speed ( ' Advertised EEE link modes ' , ours )
print_speed ( ' Link partner advertised EEE link modes ' , peer )
return
if args . show_pause :
print_field ( dumpit ( ynl , args , ' pause-get ' ) ,
( ' autoneg ' , ' Autonegotiate ' , ' bool ' ) ,
( ' rx ' , ' RX ' , ' bool ' ) ,
( ' tx ' , ' TX ' , ' bool ' ) )
return
if args . show_coalesce :
print_field ( dumpit ( ynl , args , ' coalesce-get ' ) )
return
if args . show_features :
reply = dumpit ( ynl , args , ' features-get ' )
available = bits_to_dict ( reply [ ' hw ' ] )
requested = bits_to_dict ( reply [ ' wanted ' ] ) . keys ( )
active = bits_to_dict ( reply [ ' active ' ] ) . keys ( )
never_changed = bits_to_dict ( reply [ ' nochange ' ] ) . keys ( )
for f in sorted ( available ) :
value = " off "
if f in active :
value = " on "
fixed = " "
if f not in available or f in never_changed :
fixed = " [fixed] "
req = " "
if f in requested :
if f in active :
req = " [requested on] "
else :
req = " [requested off] "
print ( f ' { f } : { value } { fixed } { req } ' )
return
if args . show_channels :
reply = dumpit ( ynl , args , ' channels-get ' )
print ( f ' Channel parameters for { args . device } : ' )
print ( f ' Pre-set maximums: ' )
print_field ( reply ,
( ' rx-max ' , ' RX ' ) ,
( ' tx-max ' , ' TX ' ) ,
( ' other-max ' , ' Other ' ) ,
( ' combined-max ' , ' Combined ' ) )
print ( f ' Current hardware settings: ' )
print_field ( reply ,
( ' rx-count ' , ' RX ' ) ,
( ' tx-count ' , ' TX ' ) ,
( ' other-count ' , ' Other ' ) ,
( ' combined-count ' , ' Combined ' ) )
return
if args . show_ring :
reply = dumpit ( ynl , args , ' channels-get ' )
print ( f ' Ring parameters for { args . device } : ' )
print ( f ' Pre-set maximums: ' )
print_field ( reply ,
( ' rx-max ' , ' RX ' ) ,
( ' rx-mini-max ' , ' RX Mini ' ) ,
( ' rx-jumbo-max ' , ' RX Jumbo ' ) ,
( ' tx-max ' , ' TX ' ) )
print ( f ' Current hardware settings: ' )
print_field ( reply ,
( ' rx ' , ' RX ' ) ,
( ' rx-mini ' , ' RX Mini ' ) ,
( ' rx-jumbo ' , ' RX Jumbo ' ) ,
( ' tx ' , ' TX ' ) )
print_field ( reply ,
( ' rx-buf-len ' , ' RX Buf Len ' ) ,
( ' cqe-size ' , ' CQE Size ' ) ,
( ' tx-push ' , ' TX Push ' , ' bool ' ) )
return
if args . statistics :
print ( f ' NIC statistics: ' )
# TODO: pass id?
strset = dumpit ( ynl , args , ' strset-get ' )
pprint . PrettyPrinter ( ) . pprint ( strset )
req = {
' groups ' : {
' size ' : 1 ,
' bits ' : {
' bit ' :
# TODO: support passing the bitmask
#[
#{ 'name': 'eth-phy', 'value': True },
{ ' name ' : ' eth-mac ' , ' value ' : True } ,
#{ 'name': 'eth-ctrl', 'value': True },
#{ 'name': 'rmon', 'value': True },
#],
} ,
} ,
}
rsp = dumpit ( ynl , args , ' stats-get ' , req )
pprint . PrettyPrinter ( ) . pprint ( rsp )
return
if args . show_time_stamping :
tsinfo = dumpit ( ynl , args , ' tsinfo-get ' )
print ( f ' Time stamping parameters for { args . device } : ' )
print ( ' Capabilities: ' )
[ print ( f ' \t { v } ' ) for v in bits_to_dict ( tsinfo [ ' timestamping ' ] ) ]
print ( f ' PTP Hardware Clock: { tsinfo [ " phc-index " ] } ' )
print ( ' Hardware Transmit Timestamp Modes: ' )
[ print ( f ' \t { v } ' ) for v in bits_to_dict ( tsinfo [ ' tx-types ' ] ) ]
print ( ' Hardware Receive Filter Modes: ' )
[ print ( f ' \t { v } ' ) for v in bits_to_dict ( tsinfo [ ' rx-filters ' ] ) ]
return
print ( f ' Settings for { args . device } : ' )
linkmodes = dumpit ( ynl , args , ' linkmodes-get ' )
ours = bits_to_dict ( linkmodes [ ' ours ' ] )
supported_ports = ( ' TP ' , ' AUI ' , ' BNC ' , ' MII ' , ' FIBRE ' , ' Backplane ' )
ports = [ p for p in supported_ports if ours . get ( p , False ) ]
print ( f ' Supported ports: [ { " " . join ( ports ) } ] ' )
print_speed ( ' Supported link modes ' , ours )
print_field ( ours , ( ' Pause ' , ' Supported pause frame use ' , ' yn ' ) )
print_field ( ours , ( ' Autoneg ' , ' Supports auto-negotiation ' , ' yn ' ) )
supported_fec = ( ' None ' , ' PS ' , ' BASER ' , ' LLRS ' )
fec = [ p for p in supported_fec if ours . get ( p , False ) ]
fec_str = " " . join ( fec )
if len ( fec ) == 0 :
fec_str = " Not reported "
print ( f ' Supported FEC modes: { fec_str } ' )
speed = ' Unknown! '
if linkmodes [ ' speed ' ] > 0 and linkmodes [ ' speed ' ] < 0xffffffff :
speed = f ' { linkmodes [ " speed " ] } Mb/s '
print ( f ' Speed: { speed } ' )
duplex_modes = {
0 : ' Half ' ,
1 : ' Full ' ,
}
duplex = duplex_modes . get ( linkmodes [ " duplex " ] , None )
if not duplex :
duplex = f ' Unknown! ( { linkmodes [ " duplex " ] } ) '
print ( f ' Duplex: { duplex } ' )
autoneg = " off "
if linkmodes . get ( " autoneg " , 0 ) != 0 :
autoneg = " on "
print ( f ' Auto-negotiation: { autoneg } ' )
ports = {
0 : ' Twisted Pair ' ,
1 : ' AUI ' ,
2 : ' MII ' ,
3 : ' FIBRE ' ,
4 : ' BNC ' ,
5 : ' Directly Attached Copper ' ,
0xef : ' None ' ,
}
linkinfo = dumpit ( ynl , args , ' linkinfo-get ' )
print ( f ' Port: { ports . get ( linkinfo [ " port " ] , " Other " ) } ' )
print_field ( linkinfo , ( ' phyaddr ' , ' PHYAD ' ) )
transceiver = {
0 : ' Internal ' ,
1 : ' External ' ,
}
print ( f ' Transceiver: { transceiver . get ( linkinfo [ " transceiver " ] , " Unknown " ) } ' )
mdix_ctrl = {
1 : ' off ' ,
2 : ' on ' ,
}
mdix = mdix_ctrl . get ( linkinfo [ ' tp-mdix-ctrl ' ] , None )
if mdix :
mdix = mdix + ' (forced) '
else :
mdix = mdix_ctrl . get ( linkinfo [ ' tp-mdix ' ] , ' Unknown (auto) ' )
print ( f ' MDI-X: { mdix } ' )
debug = dumpit ( ynl , args , ' debug-get ' )
msgmask = bits_to_dict ( debug . get ( " msgmask " , [ ] ) ) . keys ( )
print ( f ' Current message level: { " " . join ( msgmask ) } ' )
linkstate = dumpit ( ynl , args , ' linkstate-get ' )
detected_states = {
0 : ' no ' ,
1 : ' yes ' ,
}
# TODO: wol-get
detected = detected_states . get ( linkstate [ ' link ' ] , ' unknown ' )
print ( f ' Link detected: { detected } ' )
if __name__ == ' __main__ ' :
main ( )