xref: /linux/tools/net/ynl/cli.py (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
1981cbcb0SJakub Kicinski#!/usr/bin/env python3
237d9df22SJakub Kicinski# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
34e4480e8SJakub Kicinski
44e4480e8SJakub Kicinskiimport argparse
54e4480e8SJakub Kicinskiimport json
64e4480e8SJakub Kicinskiimport pprint
74e4480e8SJakub Kicinskiimport time
84e4480e8SJakub Kicinski
9771b7012SDonald Hunterfrom lib import YnlFamily, Netlink, NlError
104e4480e8SJakub Kicinski
114e4480e8SJakub Kicinski
12e2ece0bcSDonald Hunterclass YnlEncoder(json.JSONEncoder):
13e2ece0bcSDonald Hunter    def default(self, obj):
14e2ece0bcSDonald Hunter        if isinstance(obj, bytes):
15e2ece0bcSDonald Hunter            return bytes.hex(obj)
16e2ece0bcSDonald Hunter        if isinstance(obj, set):
17e2ece0bcSDonald Hunter            return list(obj)
18e2ece0bcSDonald Hunter        return json.JSONEncoder.default(self, obj)
19e2ece0bcSDonald Hunter
20e2ece0bcSDonald Hunter
214e4480e8SJakub Kicinskidef main():
22ba8be00fSDonald Hunter    description = """
23ba8be00fSDonald Hunter    YNL CLI utility - a general purpose netlink utility that uses YAML
24ba8be00fSDonald Hunter    specs to drive protocol encoding and decoding.
25ba8be00fSDonald Hunter    """
26ba8be00fSDonald Hunter    epilog = """
27ba8be00fSDonald Hunter    The --multi option can be repeated to include several do operations
28ba8be00fSDonald Hunter    in the same netlink payload.
29ba8be00fSDonald Hunter    """
30ba8be00fSDonald Hunter
31ba8be00fSDonald Hunter    parser = argparse.ArgumentParser(description=description,
32ba8be00fSDonald Hunter                                     epilog=epilog)
334e4480e8SJakub Kicinski    parser.add_argument('--spec', dest='spec', type=str, required=True)
344e4480e8SJakub Kicinski    parser.add_argument('--schema', dest='schema', type=str)
355c6674f6SJakub Kicinski    parser.add_argument('--no-schema', action='store_true')
364e4480e8SJakub Kicinski    parser.add_argument('--json', dest='json_text', type=str)
37ba8be00fSDonald Hunter
38ba8be00fSDonald Hunter    group = parser.add_mutually_exclusive_group()
39ba8be00fSDonald Hunter    group.add_argument('--do', dest='do', metavar='DO-OPERATION', type=str)
40ba8be00fSDonald Hunter    group.add_argument('--multi', dest='multi', nargs=2, action='append',
41ba8be00fSDonald Hunter                       metavar=('DO-OPERATION', 'JSON_TEXT'), type=str)
42ba8be00fSDonald Hunter    group.add_argument('--dump', dest='dump', metavar='DUMP-OPERATION', type=str)
43*3e51f2cbSJakub Kicinski    group.add_argument('--list-ops', action='store_true')
44*3e51f2cbSJakub Kicinski    group.add_argument('--list-msgs', action='store_true')
45ba8be00fSDonald Hunter
464e4480e8SJakub Kicinski    parser.add_argument('--sleep', dest='sleep', type=int)
474e4480e8SJakub Kicinski    parser.add_argument('--subscribe', dest='ntf', type=str)
481768d8a7SDonald Hunter    parser.add_argument('--replace', dest='flags', action='append_const',
491768d8a7SDonald Hunter                        const=Netlink.NLM_F_REPLACE)
501768d8a7SDonald Hunter    parser.add_argument('--excl', dest='flags', action='append_const',
511768d8a7SDonald Hunter                        const=Netlink.NLM_F_EXCL)
521768d8a7SDonald Hunter    parser.add_argument('--create', dest='flags', action='append_const',
531768d8a7SDonald Hunter                        const=Netlink.NLM_F_CREATE)
541768d8a7SDonald Hunter    parser.add_argument('--append', dest='flags', action='append_const',
551768d8a7SDonald Hunter                        const=Netlink.NLM_F_APPEND)
56d96e48a3SJiri Pirko    parser.add_argument('--process-unknown', action=argparse.BooleanOptionalAction)
57e2ece0bcSDonald Hunter    parser.add_argument('--output-json', action='store_true')
58c0111878SJakub Kicinski    parser.add_argument('--dbg-small-recv', default=0, const=4000,
59c0111878SJakub Kicinski                        action='store', nargs='?', type=int)
604e4480e8SJakub Kicinski    args = parser.parse_args()
614e4480e8SJakub Kicinski
62e2ece0bcSDonald Hunter    def output(msg):
63e2ece0bcSDonald Hunter        if args.output_json:
64e2ece0bcSDonald Hunter            print(json.dumps(msg, cls=YnlEncoder))
65e2ece0bcSDonald Hunter        else:
66e2ece0bcSDonald Hunter            pprint.PrettyPrinter().pprint(msg)
67e2ece0bcSDonald Hunter
685c6674f6SJakub Kicinski    if args.no_schema:
695c6674f6SJakub Kicinski        args.schema = ''
705c6674f6SJakub Kicinski
714e4480e8SJakub Kicinski    attrs = {}
724e4480e8SJakub Kicinski    if args.json_text:
734e4480e8SJakub Kicinski        attrs = json.loads(args.json_text)
744e4480e8SJakub Kicinski
75c0111878SJakub Kicinski    ynl = YnlFamily(args.spec, args.schema, args.process_unknown,
76c0111878SJakub Kicinski                    recv_size=args.dbg_small_recv)
77c0111878SJakub Kicinski    if args.dbg_small_recv:
78c0111878SJakub Kicinski        ynl.set_recv_dbg(True)
794e4480e8SJakub Kicinski
804e4480e8SJakub Kicinski    if args.ntf:
814e4480e8SJakub Kicinski        ynl.ntf_subscribe(args.ntf)
824e4480e8SJakub Kicinski
834e4480e8SJakub Kicinski    if args.sleep:
844e4480e8SJakub Kicinski        time.sleep(args.sleep)
854e4480e8SJakub Kicinski
86*3e51f2cbSJakub Kicinski    if args.list_ops:
87*3e51f2cbSJakub Kicinski        for op_name, op in ynl.ops.items():
88*3e51f2cbSJakub Kicinski            print(op_name, " [", ", ".join(op.modes), "]")
89*3e51f2cbSJakub Kicinski    if args.list_msgs:
90*3e51f2cbSJakub Kicinski        for op_name, op in ynl.msgs.items():
91*3e51f2cbSJakub Kicinski            print(op_name, " [", ", ".join(op.modes), "]")
92*3e51f2cbSJakub Kicinski
93771b7012SDonald Hunter    try:
948dfec0a8SJakub Kicinski        if args.do:
951768d8a7SDonald Hunter            reply = ynl.do(args.do, attrs, args.flags)
96e2ece0bcSDonald Hunter            output(reply)
978dfec0a8SJakub Kicinski        if args.dump:
988dfec0a8SJakub Kicinski            reply = ynl.dump(args.dump, attrs)
99e2ece0bcSDonald Hunter            output(reply)
100ba8be00fSDonald Hunter        if args.multi:
101ba8be00fSDonald Hunter            ops = [ (item[0], json.loads(item[1]), args.flags or []) for item in args.multi ]
102ba8be00fSDonald Hunter            reply = ynl.do_multi(ops)
103ba8be00fSDonald Hunter            output(reply)
104771b7012SDonald Hunter    except NlError as e:
105771b7012SDonald Hunter        print(e)
106771b7012SDonald Hunter        exit(1)
1074e4480e8SJakub Kicinski
1084e4480e8SJakub Kicinski    if args.ntf:
1094e4480e8SJakub Kicinski        ynl.check_ntf()
110e2ece0bcSDonald Hunter        output(ynl.async_msg_queue)
1114e4480e8SJakub Kicinski
1124e4480e8SJakub Kicinski
1134e4480e8SJakub Kicinskiif __name__ == "__main__":
1144e4480e8SJakub Kicinski    main()
115