2023-01-31 02:33:54 +00:00
|
|
|
#!/usr/bin/env python3
|
2023-03-06 20:04:57 +00:00
|
|
|
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
2023-01-20 17:50:41 +00:00
|
|
|
|
|
|
|
import argparse
|
|
|
|
import json
|
|
|
|
import pprint
|
|
|
|
import time
|
|
|
|
|
tools/net/ynl: Report netlink errors without stacktrace
ynl does not handle NlError exceptions so they get reported like program
failures. Handle the NlError exceptions and report the netlink errors
more cleanly.
Example now:
Netlink error: No such file or directory
nl_len = 44 (28) nl_flags = 0x300 nl_type = 2
error: -2 extack: {'bad-attr': '.op'}
Example before:
Traceback (most recent call last):
File "/home/donaldh/net-next/./tools/net/ynl/cli.py", line 81, in <module>
main()
File "/home/donaldh/net-next/./tools/net/ynl/cli.py", line 69, in main
reply = ynl.dump(args.dump, attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/donaldh/net-next/tools/net/ynl/lib/ynl.py", line 906, in dump
return self._op(method, vals, [], dump=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/donaldh/net-next/tools/net/ynl/lib/ynl.py", line 872, in _op
raise NlError(nl_msg)
lib.ynl.NlError: Netlink error: No such file or directory
nl_len = 44 (28) nl_flags = 0x300 nl_type = 2
error: -2 extack: {'bad-attr': '.op'}
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240306231046.97158-3-donald.hunter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-03-06 23:10:42 +00:00
|
|
|
from lib import YnlFamily, Netlink, NlError
|
2023-01-20 17:50:41 +00:00
|
|
|
|
|
|
|
|
2024-01-29 22:34:46 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2023-01-20 17:50:41 +00:00
|
|
|
def main():
|
tools/net/ynl: Add multi message support to ynl
Add a "--multi <do-op> <json>" command line to ynl that makes it
possible to add several operations to a single netlink request payload.
The --multi command line option is repeated for each operation.
This is used by the nftables family for transaction batches. For
example:
./tools/net/ynl/cli.py \
--spec Documentation/netlink/specs/nftables.yaml \
--multi batch-begin '{"res-id": 10}' \
--multi newtable '{"name": "test", "nfgen-family": 1}' \
--multi newchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
--multi batch-end '{"res-id": 10}'
[None, None, None, None]
It can also be used for bundling get requests:
./tools/net/ynl/cli.py \
--spec Documentation/netlink/specs/nftables.yaml \
--multi gettable '{"name": "test", "nfgen-family": 1}' \
--multi getchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
--output-json
[{"name": "test", "use": 1, "handle": 1, "flags": [],
"nfgen-family": 1, "version": 0, "res-id": 2},
{"table": "test", "name": "chain", "handle": 1, "use": 0,
"nfgen-family": 1, "version": 0, "res-id": 2}]
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240418104737.77914-4-donald.hunter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-04-18 10:47:36 +00:00
|
|
|
description = """
|
|
|
|
YNL CLI utility - a general purpose netlink utility that uses YAML
|
|
|
|
specs to drive protocol encoding and decoding.
|
|
|
|
"""
|
|
|
|
epilog = """
|
|
|
|
The --multi option can be repeated to include several do operations
|
|
|
|
in the same netlink payload.
|
|
|
|
"""
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description=description,
|
|
|
|
epilog=epilog)
|
2023-01-20 17:50:41 +00:00
|
|
|
parser.add_argument('--spec', dest='spec', type=str, required=True)
|
|
|
|
parser.add_argument('--schema', dest='schema', type=str)
|
2023-01-31 02:33:50 +00:00
|
|
|
parser.add_argument('--no-schema', action='store_true')
|
2023-01-20 17:50:41 +00:00
|
|
|
parser.add_argument('--json', dest='json_text', type=str)
|
tools/net/ynl: Add multi message support to ynl
Add a "--multi <do-op> <json>" command line to ynl that makes it
possible to add several operations to a single netlink request payload.
The --multi command line option is repeated for each operation.
This is used by the nftables family for transaction batches. For
example:
./tools/net/ynl/cli.py \
--spec Documentation/netlink/specs/nftables.yaml \
--multi batch-begin '{"res-id": 10}' \
--multi newtable '{"name": "test", "nfgen-family": 1}' \
--multi newchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
--multi batch-end '{"res-id": 10}'
[None, None, None, None]
It can also be used for bundling get requests:
./tools/net/ynl/cli.py \
--spec Documentation/netlink/specs/nftables.yaml \
--multi gettable '{"name": "test", "nfgen-family": 1}' \
--multi getchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
--output-json
[{"name": "test", "use": 1, "handle": 1, "flags": [],
"nfgen-family": 1, "version": 0, "res-id": 2},
{"table": "test", "name": "chain", "handle": 1, "use": 0,
"nfgen-family": 1, "version": 0, "res-id": 2}]
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240418104737.77914-4-donald.hunter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-04-18 10:47:36 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2023-01-20 17:50:41 +00:00
|
|
|
parser.add_argument('--sleep', dest='sleep', type=int)
|
|
|
|
parser.add_argument('--subscribe', dest='ntf', type=str)
|
2023-08-25 12:27:52 +00:00
|
|
|
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)
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 09:25:25 +00:00
|
|
|
parser.add_argument('--process-unknown', action=argparse.BooleanOptionalAction)
|
2024-01-29 22:34:46 +00:00
|
|
|
parser.add_argument('--output-json', action='store_true')
|
2024-03-05 05:33:10 +00:00
|
|
|
parser.add_argument('--dbg-small-recv', default=0, const=4000,
|
|
|
|
action='store', nargs='?', type=int)
|
2023-01-20 17:50:41 +00:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
2024-01-29 22:34:46 +00:00
|
|
|
def output(msg):
|
|
|
|
if args.output_json:
|
|
|
|
print(json.dumps(msg, cls=YnlEncoder))
|
|
|
|
else:
|
|
|
|
pprint.PrettyPrinter().pprint(msg)
|
|
|
|
|
2023-01-31 02:33:50 +00:00
|
|
|
if args.no_schema:
|
|
|
|
args.schema = ''
|
|
|
|
|
2023-01-20 17:50:41 +00:00
|
|
|
attrs = {}
|
|
|
|
if args.json_text:
|
|
|
|
attrs = json.loads(args.json_text)
|
|
|
|
|
2024-03-05 05:33:10 +00:00
|
|
|
ynl = YnlFamily(args.spec, args.schema, args.process_unknown,
|
|
|
|
recv_size=args.dbg_small_recv)
|
|
|
|
if args.dbg_small_recv:
|
|
|
|
ynl.set_recv_dbg(True)
|
2023-01-20 17:50:41 +00:00
|
|
|
|
|
|
|
if args.ntf:
|
|
|
|
ynl.ntf_subscribe(args.ntf)
|
|
|
|
|
|
|
|
if args.sleep:
|
|
|
|
time.sleep(args.sleep)
|
|
|
|
|
tools/net/ynl: Report netlink errors without stacktrace
ynl does not handle NlError exceptions so they get reported like program
failures. Handle the NlError exceptions and report the netlink errors
more cleanly.
Example now:
Netlink error: No such file or directory
nl_len = 44 (28) nl_flags = 0x300 nl_type = 2
error: -2 extack: {'bad-attr': '.op'}
Example before:
Traceback (most recent call last):
File "/home/donaldh/net-next/./tools/net/ynl/cli.py", line 81, in <module>
main()
File "/home/donaldh/net-next/./tools/net/ynl/cli.py", line 69, in main
reply = ynl.dump(args.dump, attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/donaldh/net-next/tools/net/ynl/lib/ynl.py", line 906, in dump
return self._op(method, vals, [], dump=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/donaldh/net-next/tools/net/ynl/lib/ynl.py", line 872, in _op
raise NlError(nl_msg)
lib.ynl.NlError: Netlink error: No such file or directory
nl_len = 44 (28) nl_flags = 0x300 nl_type = 2
error: -2 extack: {'bad-attr': '.op'}
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240306231046.97158-3-donald.hunter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-03-06 23:10:42 +00:00
|
|
|
try:
|
|
|
|
if args.do:
|
|
|
|
reply = ynl.do(args.do, attrs, args.flags)
|
|
|
|
output(reply)
|
|
|
|
if args.dump:
|
|
|
|
reply = ynl.dump(args.dump, attrs)
|
|
|
|
output(reply)
|
tools/net/ynl: Add multi message support to ynl
Add a "--multi <do-op> <json>" command line to ynl that makes it
possible to add several operations to a single netlink request payload.
The --multi command line option is repeated for each operation.
This is used by the nftables family for transaction batches. For
example:
./tools/net/ynl/cli.py \
--spec Documentation/netlink/specs/nftables.yaml \
--multi batch-begin '{"res-id": 10}' \
--multi newtable '{"name": "test", "nfgen-family": 1}' \
--multi newchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
--multi batch-end '{"res-id": 10}'
[None, None, None, None]
It can also be used for bundling get requests:
./tools/net/ynl/cli.py \
--spec Documentation/netlink/specs/nftables.yaml \
--multi gettable '{"name": "test", "nfgen-family": 1}' \
--multi getchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
--output-json
[{"name": "test", "use": 1, "handle": 1, "flags": [],
"nfgen-family": 1, "version": 0, "res-id": 2},
{"table": "test", "name": "chain", "handle": 1, "use": 0,
"nfgen-family": 1, "version": 0, "res-id": 2}]
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240418104737.77914-4-donald.hunter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-04-18 10:47:36 +00:00
|
|
|
if args.multi:
|
|
|
|
ops = [ (item[0], json.loads(item[1]), args.flags or []) for item in args.multi ]
|
|
|
|
reply = ynl.do_multi(ops)
|
|
|
|
output(reply)
|
tools/net/ynl: Report netlink errors without stacktrace
ynl does not handle NlError exceptions so they get reported like program
failures. Handle the NlError exceptions and report the netlink errors
more cleanly.
Example now:
Netlink error: No such file or directory
nl_len = 44 (28) nl_flags = 0x300 nl_type = 2
error: -2 extack: {'bad-attr': '.op'}
Example before:
Traceback (most recent call last):
File "/home/donaldh/net-next/./tools/net/ynl/cli.py", line 81, in <module>
main()
File "/home/donaldh/net-next/./tools/net/ynl/cli.py", line 69, in main
reply = ynl.dump(args.dump, attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/donaldh/net-next/tools/net/ynl/lib/ynl.py", line 906, in dump
return self._op(method, vals, [], dump=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/donaldh/net-next/tools/net/ynl/lib/ynl.py", line 872, in _op
raise NlError(nl_msg)
lib.ynl.NlError: Netlink error: No such file or directory
nl_len = 44 (28) nl_flags = 0x300 nl_type = 2
error: -2 extack: {'bad-attr': '.op'}
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240306231046.97158-3-donald.hunter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-03-06 23:10:42 +00:00
|
|
|
except NlError as e:
|
|
|
|
print(e)
|
|
|
|
exit(1)
|
2023-01-20 17:50:41 +00:00
|
|
|
|
|
|
|
if args.ntf:
|
|
|
|
ynl.check_ntf()
|
2024-01-29 22:34:46 +00:00
|
|
|
output(ynl.async_msg_queue)
|
2023-01-20 17:50:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|