1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 3 4import argparse 5import json 6import pprint 7import time 8 9from lib import YnlFamily, Netlink, NlError 10 11 12class YnlEncoder(json.JSONEncoder): 13 def default(self, obj): 14 if isinstance(obj, bytes): 15 return bytes.hex(obj) 16 if isinstance(obj, set): 17 return list(obj) 18 return json.JSONEncoder.default(self, obj) 19 20 21def main(): 22 description = """ 23 YNL CLI utility - a general purpose netlink utility that uses YAML 24 specs to drive protocol encoding and decoding. 25 """ 26 epilog = """ 27 The --multi option can be repeated to include several do operations 28 in the same netlink payload. 29 """ 30 31 parser = argparse.ArgumentParser(description=description, 32 epilog=epilog) 33 parser.add_argument('--spec', dest='spec', type=str, required=True) 34 parser.add_argument('--schema', dest='schema', type=str) 35 parser.add_argument('--no-schema', action='store_true') 36 parser.add_argument('--json', dest='json_text', type=str) 37 38 group = parser.add_mutually_exclusive_group() 39 group.add_argument('--do', dest='do', metavar='DO-OPERATION', type=str) 40 group.add_argument('--multi', dest='multi', nargs=2, action='append', 41 metavar=('DO-OPERATION', 'JSON_TEXT'), type=str) 42 group.add_argument('--dump', dest='dump', metavar='DUMP-OPERATION', type=str) 43 44 parser.add_argument('--sleep', dest='sleep', type=int) 45 parser.add_argument('--subscribe', dest='ntf', type=str) 46 parser.add_argument('--replace', dest='flags', action='append_const', 47 const=Netlink.NLM_F_REPLACE) 48 parser.add_argument('--excl', dest='flags', action='append_const', 49 const=Netlink.NLM_F_EXCL) 50 parser.add_argument('--create', dest='flags', action='append_const', 51 const=Netlink.NLM_F_CREATE) 52 parser.add_argument('--append', dest='flags', action='append_const', 53 const=Netlink.NLM_F_APPEND) 54 parser.add_argument('--process-unknown', action=argparse.BooleanOptionalAction) 55 parser.add_argument('--output-json', action='store_true') 56 parser.add_argument('--dbg-small-recv', default=0, const=4000, 57 action='store', nargs='?', type=int) 58 args = parser.parse_args() 59 60 def output(msg): 61 if args.output_json: 62 print(json.dumps(msg, cls=YnlEncoder)) 63 else: 64 pprint.PrettyPrinter().pprint(msg) 65 66 if args.no_schema: 67 args.schema = '' 68 69 attrs = {} 70 if args.json_text: 71 attrs = json.loads(args.json_text) 72 73 ynl = YnlFamily(args.spec, args.schema, args.process_unknown, 74 recv_size=args.dbg_small_recv) 75 if args.dbg_small_recv: 76 ynl.set_recv_dbg(True) 77 78 if args.ntf: 79 ynl.ntf_subscribe(args.ntf) 80 81 if args.sleep: 82 time.sleep(args.sleep) 83 84 try: 85 if args.do: 86 reply = ynl.do(args.do, attrs, args.flags) 87 output(reply) 88 if args.dump: 89 reply = ynl.dump(args.dump, attrs) 90 output(reply) 91 if args.multi: 92 ops = [ (item[0], json.loads(item[1]), args.flags or []) for item in args.multi ] 93 reply = ynl.do_multi(ops) 94 output(reply) 95 except NlError as e: 96 print(e) 97 exit(1) 98 99 if args.ntf: 100 ynl.check_ntf() 101 output(ynl.async_msg_queue) 102 103 104if __name__ == "__main__": 105 main() 106