aboutsummaryrefslogtreecommitdiff
path: root/tools/net/ynl/pyynl
diff options
context:
space:
mode:
Diffstat (limited to 'tools/net/ynl/pyynl')
-rwxr-xr-xtools/net/ynl/pyynl/cli.py297
-rwxr-xr-xtools/net/ynl/pyynl/ethtool.py447
-rw-r--r--tools/net/ynl/pyynl/lib/__init__.py11
-rw-r--r--tools/net/ynl/pyynl/lib/doc_generator.py12
-rw-r--r--tools/net/ynl/pyynl/lib/nlspec.py77
-rw-r--r--tools/net/ynl/pyynl/lib/ynl.py493
-rwxr-xr-xtools/net/ynl/pyynl/ynl_gen_c.py178
-rwxr-xr-xtools/net/ynl/pyynl/ynl_gen_rst.py2
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"]