Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/net/ynl/tests/ethtool.py
170958 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
3
#
4
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
5
# pylint: disable=too-many-return-statements
6
7
""" YNL ethtool utility """
8
9
import argparse
10
import pathlib
11
import pprint
12
import sys
13
import re
14
import os
15
16
# pylint: disable=no-name-in-module,wrong-import-position
17
sys.path.append(pathlib.Path(__file__).resolve().parent.parent.joinpath('pyynl').as_posix())
18
# pylint: disable=import-error
19
from cli import schema_dir, spec_dir
20
from lib import YnlFamily
21
22
23
def args_to_req(ynl, op_name, args, req):
24
"""
25
Verify and convert command-line arguments to the ynl-compatible request.
26
"""
27
valid_attrs = ynl.operation_do_attributes(op_name)
28
valid_attrs.remove('header') # not user-provided
29
30
if len(args) == 0:
31
print(f'no attributes, expected: {valid_attrs}')
32
sys.exit(1)
33
34
i = 0
35
while i < len(args):
36
attr = args[i]
37
if i + 1 >= len(args):
38
print(f'expected value for \'{attr}\'')
39
sys.exit(1)
40
41
if attr not in valid_attrs:
42
print(f'invalid attribute \'{attr}\', expected: {valid_attrs}')
43
sys.exit(1)
44
45
val = args[i+1]
46
i += 2
47
48
req[attr] = val
49
50
def print_field(reply, *desc):
51
"""
52
Pretty-print a set of fields from the reply. desc specifies the
53
fields and the optional type (bool/yn).
54
"""
55
if not reply:
56
return
57
58
if len(desc) == 0:
59
print_field(reply, *zip(reply.keys(), reply.keys()))
60
return
61
62
for spec in desc:
63
try:
64
field, name, tp = spec
65
except ValueError:
66
field, name = spec
67
tp = 'int'
68
69
value = reply.get(field, None)
70
if tp == 'yn':
71
value = 'yes' if value else 'no'
72
elif tp == 'bool' or isinstance(value, bool):
73
value = 'on' if value else 'off'
74
else:
75
value = 'n/a' if value is None else value
76
77
print(f'{name}: {value}')
78
79
def print_speed(name, value):
80
"""
81
Print out the speed-like strings from the value dict.
82
"""
83
speed_re = re.compile(r'[0-9]+base[^/]+/.+')
84
speed = [ k for k, v in value.items() if v and speed_re.match(k) ]
85
print(f'{name}: {" ".join(speed)}')
86
87
def do_set(ynl, args, op_name):
88
"""
89
Prepare request header, parse arguments and do a set operation.
90
"""
91
req = {
92
'header': {
93
'dev-name': args.device,
94
},
95
}
96
97
args_to_req(ynl, op_name, args.args, req)
98
ynl.do(op_name, req)
99
100
def do_get(ynl, args, op_name, extra=None):
101
"""
102
Prepare request header and get info for a specific device using doit.
103
"""
104
extra = extra or {}
105
req = {'header': {'dev-name': args.device}}
106
req['header'].update(extra.pop('header', {}))
107
req.update(extra)
108
109
reply = ynl.do(op_name, req)
110
if not reply:
111
return {}
112
113
if args.json:
114
pprint.PrettyPrinter().pprint(reply)
115
sys.exit(0)
116
reply.pop('header', None)
117
return reply
118
119
def bits_to_dict(attr):
120
"""
121
Convert ynl-formatted bitmask to a dict of bit=value.
122
"""
123
ret = {}
124
if 'bits' not in attr:
125
return {}
126
if 'bit' not in attr['bits']:
127
return {}
128
for bit in attr['bits']['bit']:
129
if bit['name'] == '':
130
continue
131
name = bit['name']
132
value = bit.get('value', False)
133
ret[name] = value
134
return ret
135
136
def main():
137
""" YNL ethtool utility """
138
139
parser = argparse.ArgumentParser(description='ethtool wannabe')
140
parser.add_argument('--json', action=argparse.BooleanOptionalAction)
141
parser.add_argument('--show-priv-flags', action=argparse.BooleanOptionalAction)
142
parser.add_argument('--set-priv-flags', action=argparse.BooleanOptionalAction)
143
parser.add_argument('--show-eee', action=argparse.BooleanOptionalAction)
144
parser.add_argument('--set-eee', action=argparse.BooleanOptionalAction)
145
parser.add_argument('-a', '--show-pause', action=argparse.BooleanOptionalAction)
146
parser.add_argument('-A', '--set-pause', action=argparse.BooleanOptionalAction)
147
parser.add_argument('-c', '--show-coalesce', action=argparse.BooleanOptionalAction)
148
parser.add_argument('-C', '--set-coalesce', action=argparse.BooleanOptionalAction)
149
parser.add_argument('-g', '--show-ring', action=argparse.BooleanOptionalAction)
150
parser.add_argument('-G', '--set-ring', action=argparse.BooleanOptionalAction)
151
parser.add_argument('-k', '--show-features', action=argparse.BooleanOptionalAction)
152
parser.add_argument('-K', '--set-features', action=argparse.BooleanOptionalAction)
153
parser.add_argument('-l', '--show-channels', action=argparse.BooleanOptionalAction)
154
parser.add_argument('-L', '--set-channels', action=argparse.BooleanOptionalAction)
155
parser.add_argument('-T', '--show-time-stamping', action=argparse.BooleanOptionalAction)
156
parser.add_argument('-S', '--statistics', action=argparse.BooleanOptionalAction)
157
# TODO: --show-tunnels tunnel-info-get
158
# TODO: --show-module module-get
159
# TODO: --get-plca-cfg plca-get
160
# TODO: --get-plca-status plca-get-status
161
# TODO: --show-mm mm-get
162
# TODO: --show-fec fec-get
163
# TODO: --dump-module-eerpom module-eeprom-get
164
# TODO: pse-get
165
# TODO: rss-get
166
parser.add_argument('device', metavar='device', type=str)
167
parser.add_argument('args', metavar='args', type=str, nargs='*')
168
169
dbg_group = parser.add_argument_group('Debug options')
170
dbg_group.add_argument('--dbg-small-recv', default=0, const=4000,
171
action='store', nargs='?', type=int, metavar='INT',
172
help="Length of buffers used for recv()")
173
174
args = parser.parse_args()
175
176
spec = os.path.join(spec_dir(), 'ethtool.yaml')
177
schema = os.path.join(schema_dir(), 'genetlink-legacy.yaml')
178
179
ynl = YnlFamily(spec, schema, recv_size=args.dbg_small_recv)
180
if args.dbg_small_recv:
181
ynl.set_recv_dbg(True)
182
183
if args.set_priv_flags:
184
# TODO: parse the bitmask
185
print("not implemented")
186
return
187
188
if args.set_eee:
189
do_set(ynl, args, 'eee-set')
190
return
191
192
if args.set_pause:
193
do_set(ynl, args, 'pause-set')
194
return
195
196
if args.set_coalesce:
197
do_set(ynl, args, 'coalesce-set')
198
return
199
200
if args.set_features:
201
# TODO: parse the bitmask
202
print("not implemented")
203
return
204
205
if args.set_channels:
206
do_set(ynl, args, 'channels-set')
207
return
208
209
if args.set_ring:
210
do_set(ynl, args, 'rings-set')
211
return
212
213
if args.show_priv_flags:
214
flags = bits_to_dict(do_get(ynl, args, 'privflags-get')['flags'])
215
print_field(flags)
216
return
217
218
if args.show_eee:
219
eee = do_get(ynl, args, 'eee-get')
220
ours = bits_to_dict(eee['modes-ours'])
221
peer = bits_to_dict(eee['modes-peer'])
222
223
if 'enabled' in eee:
224
status = 'enabled' if eee['enabled'] else 'disabled'
225
if 'active' in eee and eee['active']:
226
status = status + ' - active'
227
else:
228
status = status + ' - inactive'
229
else:
230
status = 'not supported'
231
232
print(f'EEE status: {status}')
233
print_field(eee, ('tx-lpi-timer', 'Tx LPI'))
234
print_speed('Advertised EEE link modes', ours)
235
print_speed('Link partner advertised EEE link modes', peer)
236
237
return
238
239
if args.show_pause:
240
print_field(do_get(ynl, args, 'pause-get'),
241
('autoneg', 'Autonegotiate', 'bool'),
242
('rx', 'RX', 'bool'),
243
('tx', 'TX', 'bool'))
244
return
245
246
if args.show_coalesce:
247
print_field(do_get(ynl, args, 'coalesce-get'))
248
return
249
250
if args.show_features:
251
reply = do_get(ynl, args, 'features-get')
252
available = bits_to_dict(reply['hw'])
253
requested = bits_to_dict(reply['wanted']).keys()
254
active = bits_to_dict(reply['active']).keys()
255
never_changed = bits_to_dict(reply['nochange']).keys()
256
257
for f in sorted(available):
258
value = "off"
259
if f in active:
260
value = "on"
261
262
fixed = ""
263
if f not in available or f in never_changed:
264
fixed = " [fixed]"
265
266
req = ""
267
if f in requested:
268
if f in active:
269
req = " [requested on]"
270
else:
271
req = " [requested off]"
272
273
print(f'{f}: {value}{fixed}{req}')
274
275
return
276
277
if args.show_channels:
278
reply = do_get(ynl, args, 'channels-get')
279
print(f'Channel parameters for {args.device}:')
280
281
print('Pre-set maximums:')
282
print_field(reply,
283
('rx-max', 'RX'),
284
('tx-max', 'TX'),
285
('other-max', 'Other'),
286
('combined-max', 'Combined'))
287
288
print('Current hardware settings:')
289
print_field(reply,
290
('rx-count', 'RX'),
291
('tx-count', 'TX'),
292
('other-count', 'Other'),
293
('combined-count', 'Combined'))
294
295
return
296
297
if args.show_ring:
298
reply = do_get(ynl, args, 'channels-get')
299
300
print(f'Ring parameters for {args.device}:')
301
302
print('Pre-set maximums:')
303
print_field(reply,
304
('rx-max', 'RX'),
305
('rx-mini-max', 'RX Mini'),
306
('rx-jumbo-max', 'RX Jumbo'),
307
('tx-max', 'TX'))
308
309
print('Current hardware settings:')
310
print_field(reply,
311
('rx', 'RX'),
312
('rx-mini', 'RX Mini'),
313
('rx-jumbo', 'RX Jumbo'),
314
('tx', 'TX'))
315
316
print_field(reply,
317
('rx-buf-len', 'RX Buf Len'),
318
('cqe-size', 'CQE Size'),
319
('tx-push', 'TX Push', 'bool'))
320
321
return
322
323
if args.statistics:
324
print('NIC statistics:')
325
326
# TODO: pass id?
327
strset = do_get(ynl, args, 'strset-get')
328
pprint.PrettyPrinter().pprint(strset)
329
330
req = {
331
'groups': {
332
'size': 1,
333
'bits': {
334
'bit':
335
# TODO: support passing the bitmask
336
#[
337
#{ 'name': 'eth-phy', 'value': True },
338
{ 'name': 'eth-mac', 'value': True },
339
#{ 'name': 'eth-ctrl', 'value': True },
340
#{ 'name': 'rmon', 'value': True },
341
#],
342
},
343
},
344
}
345
346
rsp = do_get(ynl, args, 'stats-get', req)
347
pprint.PrettyPrinter().pprint(rsp)
348
return
349
350
if args.show_time_stamping:
351
req = {
352
'header': {
353
'flags': 'stats',
354
},
355
}
356
357
tsinfo = do_get(ynl, args, 'tsinfo-get', req)
358
359
print(f'Time stamping parameters for {args.device}:')
360
361
print('Capabilities:')
362
_ = [print(f'\t{v}') for v in bits_to_dict(tsinfo['timestamping'])]
363
364
print(f'PTP Hardware Clock: {tsinfo.get("phc-index", "none")}')
365
366
if 'tx-types' in tsinfo:
367
print('Hardware Transmit Timestamp Modes:')
368
_ = [print(f'\t{v}') for v in bits_to_dict(tsinfo['tx-types'])]
369
else:
370
print('Hardware Transmit Timestamp Modes: none')
371
372
if 'rx-filters' in tsinfo:
373
print('Hardware Receive Filter Modes:')
374
_ = [print(f'\t{v}') for v in bits_to_dict(tsinfo['rx-filters'])]
375
else:
376
print('Hardware Receive Filter Modes: none')
377
378
if 'stats' in tsinfo and tsinfo['stats']:
379
print('Statistics:')
380
_ = [print(f'\t{k}: {v}') for k, v in tsinfo['stats'].items()]
381
382
return
383
384
print(f'Settings for {args.device}:')
385
linkmodes = do_get(ynl, args, 'linkmodes-get')
386
ours = bits_to_dict(linkmodes['ours'])
387
388
supported_ports = ('TP', 'AUI', 'BNC', 'MII', 'FIBRE', 'Backplane')
389
ports = [ p for p in supported_ports if ours.get(p, False)]
390
print(f'Supported ports: [ {" ".join(ports)} ]')
391
392
print_speed('Supported link modes', ours)
393
394
print_field(ours, ('Pause', 'Supported pause frame use', 'yn'))
395
print_field(ours, ('Autoneg', 'Supports auto-negotiation', 'yn'))
396
397
supported_fec = ('None', 'PS', 'BASER', 'LLRS')
398
fec = [ p for p in supported_fec if ours.get(p, False)]
399
fec_str = " ".join(fec)
400
if len(fec) == 0:
401
fec_str = "Not reported"
402
403
print(f'Supported FEC modes: {fec_str}')
404
405
speed = 'Unknown!'
406
if linkmodes['speed'] > 0 and linkmodes['speed'] < 0xffffffff:
407
speed = f'{linkmodes["speed"]}Mb/s'
408
print(f'Speed: {speed}')
409
410
duplex_modes = {
411
0: 'Half',
412
1: 'Full',
413
}
414
duplex = duplex_modes.get(linkmodes["duplex"], None)
415
if not duplex:
416
duplex = f'Unknown! ({linkmodes["duplex"]})'
417
print(f'Duplex: {duplex}')
418
419
autoneg = "off"
420
if linkmodes.get("autoneg", 0) != 0:
421
autoneg = "on"
422
print(f'Auto-negotiation: {autoneg}')
423
424
ports = {
425
0: 'Twisted Pair',
426
1: 'AUI',
427
2: 'MII',
428
3: 'FIBRE',
429
4: 'BNC',
430
5: 'Directly Attached Copper',
431
0xef: 'None',
432
}
433
linkinfo = do_get(ynl, args, 'linkinfo-get')
434
print(f'Port: {ports.get(linkinfo["port"], "Other")}')
435
436
print_field(linkinfo, ('phyaddr', 'PHYAD'))
437
438
transceiver = {
439
0: 'Internal',
440
1: 'External',
441
}
442
print(f'Transceiver: {transceiver.get(linkinfo["transceiver"], "Unknown")}')
443
444
mdix_ctrl = {
445
1: 'off',
446
2: 'on',
447
}
448
mdix = mdix_ctrl.get(linkinfo['tp-mdix-ctrl'], None)
449
if mdix:
450
mdix = mdix + ' (forced)'
451
else:
452
mdix = mdix_ctrl.get(linkinfo['tp-mdix'], 'Unknown (auto)')
453
print(f'MDI-X: {mdix}')
454
455
debug = do_get(ynl, args, 'debug-get')
456
msgmask = bits_to_dict(debug.get("msgmask", [])).keys()
457
print(f'Current message level: {" ".join(msgmask)}')
458
459
linkstate = do_get(ynl, args, 'linkstate-get')
460
detected_states = {
461
0: 'no',
462
1: 'yes',
463
}
464
# TODO: wol-get
465
detected = detected_states.get(linkstate['link'], 'unknown')
466
print(f'Link detected: {detected}')
467
468
if __name__ == '__main__':
469
main()
470
471