diff options
Diffstat (limited to 'tools/net/ynl/pyynl')
| -rwxr-xr-x | tools/net/ynl/pyynl/cli.py | 297 | ||||
| -rwxr-xr-x | tools/net/ynl/pyynl/ethtool.py | 447 | ||||
| -rw-r--r-- | tools/net/ynl/pyynl/lib/__init__.py | 11 | ||||
| -rw-r--r-- | tools/net/ynl/pyynl/lib/doc_generator.py | 12 | ||||
| -rw-r--r-- | tools/net/ynl/pyynl/lib/nlspec.py | 77 | ||||
| -rw-r--r-- | tools/net/ynl/pyynl/lib/ynl.py | 493 | ||||
| -rwxr-xr-x | tools/net/ynl/pyynl/ynl_gen_c.py | 178 | ||||
| -rwxr-xr-x | tools/net/ynl/pyynl/ynl_gen_rst.py | 2 |
8 files changed, 742 insertions, 775 deletions
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py index af02a5b7e5a2..8275a806cf73 100755 --- a/tools/net/ynl/pyynl/cli.py +++ b/tools/net/ynl/pyynl/cli.py @@ -1,43 +1,85 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +""" +YNL cli tool +""" + import argparse import json import os import pathlib import pprint +import shutil import sys import textwrap +# pylint: disable=no-name-in-module,wrong-import-position sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix()) -from lib import YnlFamily, Netlink, NlError, SpecFamily +from lib import YnlFamily, Netlink, NlError, SpecFamily, SpecException, YnlException + +SYS_SCHEMA_DIR='/usr/share/ynl' +RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' + +# pylint: disable=too-few-public-methods,too-many-locals +class Colors: + """ANSI color and font modifier codes""" + RESET = '\033[0m' + + BOLD = '\033[1m' + ITALICS = '\033[3m' + UNDERLINE = '\033[4m' + INVERT = '\033[7m' + + +def color(text, modifiers): + """Add color to text if output is a TTY + + Returns: + Colored text if stdout is a TTY, otherwise plain text + """ + if sys.stdout.isatty(): + # Join the colors if they are a list, if it's a string this a noop + modifiers = "".join(modifiers) + return f"{modifiers}{text}{Colors.RESET}" + return text -sys_schema_dir='/usr/share/ynl' -relative_schema_dir='../../../../Documentation/netlink' +def term_width(): + """ Get terminal width in columns (80 if stdout is not a terminal) """ + return shutil.get_terminal_size().columns def schema_dir(): + """ + Return the effective schema directory, preferring in-tree before + system schema directory. + """ script_dir = os.path.dirname(os.path.abspath(__file__)) - schema_dir = os.path.abspath(f"{script_dir}/{relative_schema_dir}") - if not os.path.isdir(schema_dir): - schema_dir = sys_schema_dir - if not os.path.isdir(schema_dir): - raise Exception(f"Schema directory {schema_dir} does not exist") - return schema_dir + schema_dir_ = os.path.abspath(f"{script_dir}/{RELATIVE_SCHEMA_DIR}") + if not os.path.isdir(schema_dir_): + schema_dir_ = SYS_SCHEMA_DIR + if not os.path.isdir(schema_dir_): + raise YnlException(f"Schema directory {schema_dir_} does not exist") + return schema_dir_ def spec_dir(): - spec_dir = schema_dir() + '/specs' - if not os.path.isdir(spec_dir): - raise Exception(f"Spec directory {spec_dir} does not exist") - return spec_dir + """ + Return the effective spec directory, relative to the effective + schema directory. + """ + spec_dir_ = schema_dir() + '/specs' + if not os.path.isdir(spec_dir_): + raise YnlException(f"Spec directory {spec_dir_} does not exist") + return spec_dir_ class YnlEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, bytes): - return bytes.hex(obj) - if isinstance(obj, set): - return list(obj) - return json.JSONEncoder.default(self, obj) + """A custom encoder for emitting JSON with ynl-specific instance types""" + def default(self, o): + if isinstance(o, bytes): + return bytes.hex(o) + if isinstance(o, set): + return sorted(o) + return json.JSONEncoder.default(self, o) def print_attr_list(ynl, attr_names, attr_set, indent=2): @@ -46,7 +88,7 @@ def print_attr_list(ynl, attr_names, attr_set, indent=2): for attr_name in attr_names: if attr_name in attr_set.attrs: attr = attr_set.attrs[attr_name] - attr_info = f'{prefix}- {attr_name}: {attr.type}' + attr_info = f'{prefix}- {color(attr_name, Colors.BOLD)}: {attr.type}' if 'enum' in attr.yaml: enum_name = attr.yaml['enum'] attr_info += f" (enum: {enum_name})" @@ -54,7 +96,8 @@ def print_attr_list(ynl, attr_names, attr_set, indent=2): if enum_name in ynl.consts: const = ynl.consts[enum_name] enum_values = list(const.entries.keys()) - attr_info += f"\n{prefix} {const.type.capitalize()}: {', '.join(enum_values)}" + type_fmted = color(const.type.capitalize(), Colors.ITALICS) + attr_info += f"\n{prefix} {type_fmted}: {', '.join(enum_values)}" # Show nested attributes reference and recursively display them nested_set_name = None @@ -63,7 +106,10 @@ def print_attr_list(ynl, attr_names, attr_set, indent=2): attr_info += f" -> {nested_set_name}" if attr.yaml.get('doc'): - doc_text = textwrap.indent(attr.yaml['doc'], prefix + ' ') + doc_prefix = prefix + ' ' * 4 + doc_text = textwrap.fill(attr.yaml['doc'], width=term_width(), + initial_indent=doc_prefix, + subsequent_indent=doc_prefix) attr_info += f"\n{doc_text}" print(attr_info) @@ -77,24 +123,62 @@ def print_attr_list(ynl, attr_names, attr_set, indent=2): print_attr_list(ynl, nested_names, nested_set, indent + 4) -def print_mode_attrs(ynl, mode, mode_spec, attr_set, print_request=True): +def print_mode_attrs(ynl, mode, mode_spec, attr_set, consistent_dd_reply=None): """Print a given mode (do/dump/event/notify).""" mode_title = mode.capitalize() - if print_request and 'request' in mode_spec and 'attributes' in mode_spec['request']: + if 'request' in mode_spec and 'attributes' in mode_spec['request']: print(f'\n{mode_title} request attributes:') print_attr_list(ynl, mode_spec['request']['attributes'], attr_set) if 'reply' in mode_spec and 'attributes' in mode_spec['reply']: - print(f'\n{mode_title} reply attributes:') - print_attr_list(ynl, mode_spec['reply']['attributes'], attr_set) + if consistent_dd_reply and mode == "do": + title = None # Dump handling will print in combined format + elif consistent_dd_reply and mode == "dump": + title = 'Do and Dump' + else: + title = f'{mode_title}' + if title: + print(f'\n{title} reply attributes:') + print_attr_list(ynl, mode_spec['reply']['attributes'], attr_set) + - if 'attributes' in mode_spec: - print(f'\n{mode_title} attributes:') - print_attr_list(ynl, mode_spec['attributes'], attr_set) +def do_doc(ynl, op): + """Handle --list-attrs $op, print the attr information to stdout""" + print(f'Operation: {color(op.name, Colors.BOLD)}') + print(op.yaml['doc']) + consistent_dd_reply = False + if 'do' in op.yaml and 'dump' in op.yaml and 'reply' in op.yaml['do'] and \ + op.yaml['do']['reply'] == op.yaml['dump'].get('reply'): + consistent_dd_reply = True + for mode in ['do', 'dump']: + if mode in op.yaml: + print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set, + consistent_dd_reply=consistent_dd_reply) + + if 'attributes' in op.yaml.get('event', {}): + print('\nEvent attributes:') + print_attr_list(ynl, op.yaml['event']['attributes'], op.attr_set) + + if 'notify' in op.yaml: + mode_spec = op.yaml['notify'] + ref_spec = ynl.msgs.get(mode_spec).yaml.get('do') + if not ref_spec: + ref_spec = ynl.msgs.get(mode_spec).yaml.get('dump') + if ref_spec: + print('\nNotification attributes:') + print_attr_list(ynl, ref_spec['reply']['attributes'], op.attr_set) + + if 'mcgrp' in op.yaml: + print(f"\nMulticast group: {op.yaml['mcgrp']}") + + +# pylint: disable=too-many-locals,too-many-branches,too-many-statements def main(): + """YNL cli tool""" + description = """ YNL CLI utility - a general purpose netlink utility that uses YAML specs to drive protocol encoding and decoding. @@ -105,54 +189,87 @@ def main(): """ parser = argparse.ArgumentParser(description=description, - epilog=epilog) - spec_group = parser.add_mutually_exclusive_group(required=True) - spec_group.add_argument('--family', dest='family', type=str, - help='name of the netlink FAMILY') - spec_group.add_argument('--list-families', action='store_true', - help='list all netlink families supported by YNL (has spec)') - spec_group.add_argument('--spec', dest='spec', type=str, - help='choose the family by SPEC file path') - - parser.add_argument('--schema', dest='schema', type=str) - parser.add_argument('--no-schema', action='store_true') - parser.add_argument('--json', dest='json_text', type=str) - - group = parser.add_mutually_exclusive_group() - group.add_argument('--do', dest='do', metavar='DO-OPERATION', type=str) - group.add_argument('--multi', dest='multi', nargs=2, action='append', - metavar=('DO-OPERATION', 'JSON_TEXT'), type=str) - group.add_argument('--dump', dest='dump', metavar='DUMP-OPERATION', type=str) - group.add_argument('--list-ops', action='store_true') - group.add_argument('--list-msgs', action='store_true') - group.add_argument('--list-attrs', dest='list_attrs', metavar='OPERATION', type=str, - help='List attributes for an operation') - group.add_argument('--validate', action='store_true') - - parser.add_argument('--duration', dest='duration', type=int, - help='when subscribed, watch for DURATION seconds') - parser.add_argument('--sleep', dest='duration', type=int, - help='alias for duration') - parser.add_argument('--subscribe', dest='ntf', type=str) - parser.add_argument('--replace', dest='flags', action='append_const', - const=Netlink.NLM_F_REPLACE) - parser.add_argument('--excl', dest='flags', action='append_const', - const=Netlink.NLM_F_EXCL) - parser.add_argument('--create', dest='flags', action='append_const', - const=Netlink.NLM_F_CREATE) - parser.add_argument('--append', dest='flags', action='append_const', - const=Netlink.NLM_F_APPEND) - parser.add_argument('--process-unknown', action=argparse.BooleanOptionalAction) - parser.add_argument('--output-json', action='store_true') - parser.add_argument('--dbg-small-recv', default=0, const=4000, - action='store', nargs='?', type=int) + epilog=epilog, add_help=False) + + gen_group = parser.add_argument_group('General options') + gen_group.add_argument('-h', '--help', action='help', + help='show this help message and exit') + + spec_group = parser.add_argument_group('Netlink family selection') + spec_sel = spec_group.add_mutually_exclusive_group(required=True) + spec_sel.add_argument('--list-families', action='store_true', + help=('list Netlink families supported by YNL ' + '(which have a spec available in the standard ' + 'system path)')) + spec_sel.add_argument('--family', dest='family', type=str, + help='name of the Netlink FAMILY to use') + spec_sel.add_argument('--spec', dest='spec', type=str, + help='full file path to the YAML spec file') + + ops_group = parser.add_argument_group('Operations') + ops = ops_group.add_mutually_exclusive_group() + ops.add_argument('--do', dest='do', metavar='DO-OPERATION', type=str) + ops.add_argument('--dump', dest='dump', metavar='DUMP-OPERATION', type=str) + ops.add_argument('--multi', dest='multi', nargs=2, action='append', + metavar=('DO-OPERATION', 'JSON_TEXT'), type=str, + help="Multi-message operation sequence (for nftables)") + ops.add_argument('--list-ops', action='store_true', + help="List available --do and --dump operations") + ops.add_argument('--list-msgs', action='store_true', + help="List all messages of the family (incl. notifications)") + ops.add_argument('--list-attrs', '--doc', dest='list_attrs', metavar='MSG', + type=str, help='List attributes for a message / operation') + ops.add_argument('--validate', action='store_true', + help="Validate the spec against schema and exit") + + io_group = parser.add_argument_group('Input / Output') + io_group.add_argument('--json', dest='json_text', type=str, + help=('Specify attributes of the message to send ' + 'to the kernel in JSON format. Can be left out ' + 'if the message is expected to be empty.')) + io_group.add_argument('--output-json', action='store_true', + help='Format output as JSON') + + ntf_group = parser.add_argument_group('Notifications') + ntf_group.add_argument('--subscribe', dest='ntf', type=str) + ntf_group.add_argument('--duration', dest='duration', type=int, + help='when subscribed, watch for DURATION seconds') + ntf_group.add_argument('--sleep', dest='duration', type=int, + help='alias for duration') + + nlflags = parser.add_argument_group('Netlink message flags (NLM_F_*)', + ('Extra flags to set in nlmsg_flags of ' + 'the request, used mostly by older ' + 'Classic Netlink families.')) + nlflags.add_argument('--replace', dest='flags', action='append_const', + const=Netlink.NLM_F_REPLACE) + nlflags.add_argument('--excl', dest='flags', action='append_const', + const=Netlink.NLM_F_EXCL) + nlflags.add_argument('--create', dest='flags', action='append_const', + const=Netlink.NLM_F_CREATE) + nlflags.add_argument('--append', dest='flags', action='append_const', + const=Netlink.NLM_F_APPEND) + + schema_group = parser.add_argument_group('Development options') + schema_group.add_argument('--schema', dest='schema', type=str, + help="JSON schema to validate the spec") + schema_group.add_argument('--no-schema', action='store_true') + + dbg_group = parser.add_argument_group('Debug options') + io_group.add_argument('--policy', action='store_true', + help='Query kernel policy for the operation instead of executing it') + dbg_group.add_argument('--dbg-small-recv', default=0, const=4000, + action='store', nargs='?', type=int, metavar='INT', + help="Length of buffers used for recv()") + dbg_group.add_argument('--process-unknown', action=argparse.BooleanOptionalAction) + args = parser.parse_args() def output(msg): if args.output_json: print(json.dumps(msg, cls=YnlEncoder)) else: - pprint.PrettyPrinter().pprint(msg) + pprint.pprint(msg, width=term_width(), compact=True) if args.list_families: for filename in sorted(os.listdir(spec_dir())): @@ -172,18 +289,18 @@ def main(): else: spec = args.spec if not os.path.isfile(spec): - raise Exception(f"Spec file {spec} does not exist") + raise YnlException(f"Spec file {spec} does not exist") if args.validate: try: SpecFamily(spec, args.schema) - except Exception as error: + except SpecException as error: print(error) - exit(1) + sys.exit(1) return if args.family: # set behaviour when using installed specs - if args.schema is None and spec.startswith(sys_schema_dir): + if args.schema is None and spec.startswith(SYS_SCHEMA_DIR): args.schema = '' # disable schema validation when installed if args.process_unknown is None: args.process_unknown = True @@ -193,6 +310,16 @@ def main(): if args.dbg_small_recv: ynl.set_recv_dbg(True) + if args.policy: + if args.do: + pol = ynl.get_policy(args.do, 'do') + output(pol.to_dict() if pol else None) + args.do = None + if args.dump: + pol = ynl.get_policy(args.dump, 'dump') + output(pol.to_dict() if pol else None) + args.dump = None + if args.ntf: ynl.ntf_subscribe(args.ntf) @@ -207,23 +334,9 @@ def main(): op = ynl.msgs.get(args.list_attrs) if not op: print(f'Operation {args.list_attrs} not found') - exit(1) - - print(f'Operation: {op.name}') - print(op.yaml['doc']) - - for mode in ['do', 'dump', 'event']: - if mode in op.yaml: - print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set, True) - - if 'notify' in op.yaml: - mode_spec = op.yaml['notify'] - ref_spec = ynl.msgs.get(mode_spec).yaml.get('do') - if ref_spec: - print_mode_attrs(ynl, 'notify', ref_spec, op.attr_set, False) + sys.exit(1) - if 'mcgrp' in op.yaml: - print(f"\nMulticast group: {op.yaml['mcgrp']}") + do_doc(ynl, op) try: if args.do: @@ -242,7 +355,7 @@ def main(): output(msg) except NlError as e: print(e) - exit(1) + sys.exit(1) except KeyboardInterrupt: pass except BrokenPipeError: diff --git a/tools/net/ynl/pyynl/ethtool.py b/tools/net/ynl/pyynl/ethtool.py deleted file mode 100755 index fd0f6b8d54d1..000000000000 --- a/tools/net/ynl/pyynl/ethtool.py +++ /dev/null @@ -1,447 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause - -import argparse -import pathlib -import pprint -import sys -import re -import os - -sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix()) -from lib import YnlFamily -from cli import schema_dir, spec_dir - -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 not reply: - return - - if len(desc) == 0: - return print_field(reply, *zip(reply.keys(), reply.keys())) - - for spec in desc: - try: - field, name, tp = spec - except ValueError: - 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() - - spec = os.path.join(spec_dir(), 'ethtool.yaml') - schema = os.path.join(schema_dir(), 'genetlink-legacy.yaml') - - 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('Pre-set maximums:') - print_field(reply, - ('rx-max', 'RX'), - ('tx-max', 'TX'), - ('other-max', 'Other'), - ('combined-max', 'Combined')) - - print('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('Pre-set maximums:') - print_field(reply, - ('rx-max', 'RX'), - ('rx-mini-max', 'RX Mini'), - ('rx-jumbo-max', 'RX Jumbo'), - ('tx-max', 'TX')) - - print('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('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: - req = { - 'header': { - 'flags': 'stats', - }, - } - - tsinfo = dumpit(ynl, args, 'tsinfo-get', req) - - 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.get("phc-index", "none")}') - - if 'tx-types' in tsinfo: - print('Hardware Transmit Timestamp Modes:') - [print(f'\t{v}') for v in bits_to_dict(tsinfo['tx-types'])] - else: - print('Hardware Transmit Timestamp Modes: none') - - if 'rx-filters' in tsinfo: - print('Hardware Receive Filter Modes:') - [print(f'\t{v}') for v in bits_to_dict(tsinfo['rx-filters'])] - else: - print('Hardware Receive Filter Modes: none') - - if 'stats' in tsinfo and tsinfo['stats']: - print('Statistics:') - [print(f'\t{k}: {v}') for k, v in tsinfo['stats'].items()] - - 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() diff --git a/tools/net/ynl/pyynl/lib/__init__.py b/tools/net/ynl/pyynl/lib/__init__.py index ec9ea00071be..be741985ae4e 100644 --- a/tools/net/ynl/pyynl/lib/__init__.py +++ b/tools/net/ynl/pyynl/lib/__init__.py @@ -1,11 +1,16 @@ # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +""" YNL library """ + from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \ - SpecFamily, SpecOperation, SpecSubMessage, SpecSubMessageFormat -from .ynl import YnlFamily, Netlink, NlError + SpecFamily, SpecOperation, SpecSubMessage, SpecSubMessageFormat, \ + SpecException +from .ynl import YnlFamily, Netlink, NlError, NlPolicy, YnlException from .doc_generator import YnlDocGenerator __all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet", "SpecFamily", "SpecOperation", "SpecSubMessage", "SpecSubMessageFormat", - "YnlFamily", "Netlink", "NlError", "YnlDocGenerator"] + "SpecException", + "YnlFamily", "Netlink", "NlError", "NlPolicy", "YnlException", + "YnlDocGenerator"] diff --git a/tools/net/ynl/pyynl/lib/doc_generator.py b/tools/net/ynl/pyynl/lib/doc_generator.py index 3a16b8eb01ca..74f5d408e048 100644 --- a/tools/net/ynl/pyynl/lib/doc_generator.py +++ b/tools/net/ynl/pyynl/lib/doc_generator.py @@ -109,8 +109,7 @@ class RstFormatters: 'fixed-header': 'definition', 'nested-attributes': 'attribute-set', 'struct': 'definition'} - if prefix in mappings: - prefix = mappings[prefix] + prefix = mappings.get(prefix, prefix) return f":ref:`{namespace}-{prefix}-{name}`" def rst_header(self) -> str: @@ -166,13 +165,13 @@ class YnlDocGenerator: continue lines.append(self.fmt.rst_paragraph(self.fmt.bold(key), level + 1)) if key in ['request', 'reply']: - lines.append(self.parse_do_attributes(do_dict[key], level + 1) + "\n") + lines.append(self.parse_op_attributes(do_dict[key], level + 1) + "\n") else: lines.append(self.fmt.headroom(level + 2) + do_dict[key] + "\n") return "\n".join(lines) - def parse_do_attributes(self, attrs: Dict[str, Any], level: int = 0) -> str: + def parse_op_attributes(self, attrs: Dict[str, Any], level: int = 0) -> str: """Parse 'attributes' section""" if "attributes" not in attrs: return "" @@ -184,7 +183,7 @@ class YnlDocGenerator: def parse_operations(self, operations: List[Dict[str, Any]], namespace: str) -> str: """Parse operations block""" - preprocessed = ["name", "doc", "title", "do", "dump", "flags"] + preprocessed = ["name", "doc", "title", "do", "dump", "flags", "event"] |
