From: Donald Hunter <donald.hunter@gmail.com>
To: netdev@vger.kernel.org, Jakub Kicinski <kuba@kernel.org>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Paolo Abeni <pabeni@redhat.com>, Jiri Pirko <jiri@resnulli.us>,
Jacob Keller <jacob.e.keller@intel.com>,
Stanislav Fomichev <sdf@google.com>
Cc: donald.hunter@redhat.com, Donald Hunter <donald.hunter@gmail.com>
Subject: [PATCH net-next v1 2/2] tools/net/ynl: Add multi message support to ynl
Date: Wed, 27 Mar 2024 18:17:00 +0000 [thread overview]
Message-ID: <20240327181700.77940-3-donald.hunter@gmail.com> (raw)
In-Reply-To: <20240327181700.77940-1-donald.hunter@gmail.com>
Add a "--multi <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}'
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
---
tools/net/ynl/cli.py | 22 ++++++++++++++++---
tools/net/ynl/lib/ynl.py | 47 +++++++++++++++++++++++++++-------------
2 files changed, 51 insertions(+), 18 deletions(-)
diff --git a/tools/net/ynl/cli.py b/tools/net/ynl/cli.py
index f131e33ac3ee..1b8f87b472ba 100755
--- a/tools/net/ynl/cli.py
+++ b/tools/net/ynl/cli.py
@@ -19,13 +19,23 @@ class YnlEncoder(json.JSONEncoder):
def main():
- parser = argparse.ArgumentParser(description='YNL CLI sample')
+ description = """
+ YNL CLI utility - a general purpose netlink utility that uses YNL specs
+ to drive protocol encoding and decoding.
+ """
+ epilog = """
+ The --multi option can be repeated to include several operations
+ in the same netlink payload.
+ """
+
+ parser = argparse.ArgumentParser(description=description,
+ epilog=epilog)
parser.add_argument('--spec', dest='spec', type=str, required=True)
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)
- parser.add_argument('--do', dest='do', type=str)
- parser.add_argument('--dump', dest='dump', type=str)
+ parser.add_argument('--do', dest='do', metavar='OPERATION', type=str)
+ parser.add_argument('--dump', dest='dump', metavar='OPERATION', type=str)
parser.add_argument('--sleep', dest='sleep', type=int)
parser.add_argument('--subscribe', dest='ntf', type=str)
parser.add_argument('--replace', dest='flags', action='append_const',
@@ -40,6 +50,8 @@ def main():
parser.add_argument('--output-json', action='store_true')
parser.add_argument('--dbg-small-recv', default=0, const=4000,
action='store', nargs='?', type=int)
+ parser.add_argument('--multi', dest='multi', nargs=2, action='append',
+ metavar=('OPERATION', 'JSON_TEXT'), type=str)
args = parser.parse_args()
def output(msg):
@@ -73,6 +85,10 @@ def main():
if args.dump:
reply = ynl.dump(args.dump, attrs)
output(reply)
+ if args.multi:
+ ops = [ (item[0], json.loads(item[1]), args.flags) for item in args.multi ]
+ reply = ynl.do_multi(ops)
+ output(reply)
except NlError as e:
print(e)
exit(1)
diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 557ef5a22b7d..cecd89db7d58 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -927,16 +927,11 @@ class YnlFamily(SpecFamily):
return op['do']['request']['attributes'].copy()
- def _op(self, method, vals, flags=None, dump=False):
- op = self.ops[method]
-
+ def _encode_message(self, op, vals, flags, req_seq):
nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK
for flag in flags or []:
nl_flags |= flag
- if dump:
- nl_flags |= Netlink.NLM_F_DUMP
- req_seq = random.randint(1024, 65535)
msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq)
if op.fixed_header:
msg += self._encode_struct(op.fixed_header, vals)
@@ -944,8 +939,20 @@ class YnlFamily(SpecFamily):
for name, value in vals.items():
msg += self._add_attr(op.attr_set.name, name, value, search_attrs)
msg = _genl_msg_finalize(msg)
+ return msg
- self.sock.send(msg, 0)
+ def _ops(self, ops):
+ reqs_by_seq = {}
+ req_seq = random.randint(1024, 65535)
+ payload = b''
+ for (method, vals, flags) in ops:
+ op = self.ops[method]
+ msg = self._encode_message(op, vals, flags, req_seq)
+ reqs_by_seq[req_seq] = (op, msg)
+ payload += msg
+ req_seq += 1
+
+ self.sock.send(payload, 0)
done = False
rsp = []
@@ -954,8 +961,9 @@ class YnlFamily(SpecFamily):
nms = NlMsgs(reply, attr_space=op.attr_set)
self._recv_dbg_print(reply, nms)
for nl_msg in nms:
- if nl_msg.extack:
- self._decode_extack(msg, op, nl_msg.extack)
+ if nl_msg.extack and nl_msg.nl_seq in reqs_by_seq:
+ (req_op, req_msg) = reqs_by_seq[nl_msg.nl_seq]
+ self._decode_extack(req_msg, req_op, nl_msg.extack)
if nl_msg.error:
raise NlError(nl_msg)
@@ -963,13 +971,15 @@ class YnlFamily(SpecFamily):
if nl_msg.extack:
print("Netlink warning:")
print(nl_msg)
+ del reqs_by_seq[nl_msg.nl_seq]
done = True
break
decoded = self.nlproto.decode(self, nl_msg)
+ rsp_op = self.rsp_by_value[decoded.cmd()]
# Check if this is a reply to our request
- if nl_msg.nl_seq != req_seq or decoded.cmd() != op.rsp_value:
+ if nl_msg.nl_seq not in reqs_by_seq or decoded.cmd() != rsp_op.rsp_value:
if decoded.cmd() in self.async_msg_ids:
self.handle_ntf(decoded)
continue
@@ -977,19 +987,26 @@ class YnlFamily(SpecFamily):
print('Unexpected message: ' + repr(decoded))
continue
- rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
+ rsp_msg = self._decode(decoded.raw_attrs, rsp_op.attr_set.name)
if op.fixed_header:
- rsp_msg.update(self._decode_struct(decoded.raw, op.fixed_header))
+ rsp_msg.update(self._decode_struct(decoded.raw, rsp_op.fixed_header))
rsp.append(rsp_msg)
if not rsp:
return None
- if not dump and len(rsp) == 1:
+ if not Netlink.NLM_F_DUMP in flags and len(rsp) == 1:
return rsp[0]
return rsp
+ def _op(self, method, vals, flags):
+ ops = [(method, vals, flags)]
+ return self._ops(ops)
+
def do(self, method, vals, flags=None):
- return self._op(method, vals, flags)
+ return self._op(method, vals, flags or [])
def dump(self, method, vals):
- return self._op(method, vals, [], dump=True)
+ return self._op(method, vals, [Netlink.NLM_F_DUMP])
+
+ def do_multi(self, ops):
+ return self._ops(ops)
--
2.44.0
next prev parent reply other threads:[~2024-03-27 18:17 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-27 18:16 [PATCH net-next v1 0/2] netlink: Add nftables spec w/ multi messages Donald Hunter
2024-03-27 18:16 ` [PATCH net-next v1 1/2] doc/netlink/specs: Add draft nftables spec Donald Hunter
2024-03-27 18:17 ` Donald Hunter [this message]
2024-03-29 0:57 ` [PATCH net-next v1 2/2] tools/net/ynl: Add multi message support to ynl Jakub Kicinski
2024-03-29 13:37 ` Donald Hunter
2024-03-29 15:43 ` Jakub Kicinski
2024-03-29 18:57 ` Donald Hunter
2024-03-29 21:01 ` Donald Hunter
2024-03-29 21:01 ` Donald Hunter
2024-03-29 21:46 ` Jakub Kicinski
2024-03-29 21:46 ` Jakub Kicinski
2024-03-29 22:12 ` Pablo Neira Ayuso
2024-03-27 22:45 ` [PATCH net-next v1 0/2] netlink: Add nftables spec w/ multi messages Pablo Neira Ayuso
2024-03-28 15:33 ` Donald Hunter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240327181700.77940-3-donald.hunter@gmail.com \
--to=donald.hunter@gmail.com \
--cc=davem@davemloft.net \
--cc=donald.hunter@redhat.com \
--cc=edumazet@google.com \
--cc=jacob.e.keller@intel.com \
--cc=jiri@resnulli.us \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sdf@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.