Commit 09d7ff06 authored by Donald Hunter's avatar Donald Hunter Committed by Jakub Kicinski
Browse files

tools: ynl: parse extack for sub-messages



Extend the Python YNL extack decoding to handle sub-messages in the same
way that YNL C does. This involves retaining the input values so that
they are available during extack decoding.

./tools/net/ynl/pyynl/cli.py --family rt-link --do newlink --create \
    --json '{
        "linkinfo": {"kind": "netkit", "data": {"policy": 10} }
    }'
Netlink error: Invalid argument
nl_len = 92 (76) nl_flags = 0x300 nl_type = 2
	error: -22
	extack: {'msg': 'Provided default xmit policy not supported', 'bad-attr': '.linkinfo.data(netkit).policy'}

Signed-off-by: default avatarDonald Hunter <donald.hunter@gmail.com>
Link: https://patch.msgid.link/20250523103031.80236-1-donald.hunter@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 8bc3c234
Loading
Loading
Loading
Loading
+25 −14
Original line number Diff line number Diff line
@@ -594,7 +594,7 @@ class YnlFamily(SpecFamily):
            scalar_selector = self._get_scalar(attr, value["selector"])
            attr_payload = struct.pack("II", scalar_value, scalar_selector)
        elif attr['type'] == 'sub-message':
            msg_format = self._resolve_selector(attr, search_attrs)
            msg_format, _ = self._resolve_selector(attr, search_attrs)
            attr_payload = b''
            if msg_format.fixed_header:
                attr_payload += self._encode_struct(msg_format.fixed_header, value)
@@ -712,10 +712,10 @@ class YnlFamily(SpecFamily):
            raise Exception(f"No message format for '{value}' in sub-message spec '{sub_msg}'")

        spec = sub_msg_spec.formats[value]
        return spec
        return spec, value

    def _decode_sub_msg(self, attr, attr_spec, search_attrs):
        msg_format = self._resolve_selector(attr_spec, search_attrs)
        msg_format, _ = self._resolve_selector(attr_spec, search_attrs)
        decoded = {}
        offset = 0
        if msg_format.fixed_header:
@@ -787,7 +787,7 @@ class YnlFamily(SpecFamily):

        return rsp

    def _decode_extack_path(self, attrs, attr_set, offset, target):
    def _decode_extack_path(self, attrs, attr_set, offset, target, search_attrs):
        for attr in attrs:
            try:
                attr_spec = attr_set.attrs_by_val[attr.type]
@@ -801,26 +801,37 @@ class YnlFamily(SpecFamily):
            if offset + attr.full_len <= target:
                offset += attr.full_len
                continue
            if attr_spec['type'] != 'nest':

            pathname = attr_spec.name
            if attr_spec['type'] == 'nest':
                sub_attrs = self.attr_sets[attr_spec['nested-attributes']]
                search_attrs = SpaceAttrs(sub_attrs, search_attrs.lookup(attr_spec['name']))
            elif attr_spec['type'] == 'sub-message':
                msg_format, value = self._resolve_selector(attr_spec, search_attrs)
                if msg_format is None:
                    raise Exception(f"Can't resolve sub-message of {attr_spec['name']} for extack")
                sub_attrs = self.attr_sets[msg_format.attr_set]
                pathname += f"({value})"
            else:
                raise Exception(f"Can't dive into {attr.type} ({attr_spec['name']}) for extack")
            offset += 4
            subpath = self._decode_extack_path(NlAttrs(attr.raw),
                                               self.attr_sets[attr_spec['nested-attributes']],
                                               offset, target)
            subpath = self._decode_extack_path(NlAttrs(attr.raw), sub_attrs,
                                               offset, target, search_attrs)
            if subpath is None:
                return None
            return '.' + attr_spec.name + subpath
            return '.' + pathname + subpath

        return None

    def _decode_extack(self, request, op, extack):
    def _decode_extack(self, request, op, extack, vals):
        if 'bad-attr-offs' not in extack:
            return

        msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set), op)
        offset = self.nlproto.msghdr_size() + self._struct_size(op.fixed_header)
        search_attrs = SpaceAttrs(op.attr_set, vals)
        path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset,
                                        extack['bad-attr-offs'])
                                        extack['bad-attr-offs'], search_attrs)
        if path:
            del extack['bad-attr-offs']
            extack['bad-attr'] = path
@@ -1012,7 +1023,7 @@ class YnlFamily(SpecFamily):
        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, flags)
            reqs_by_seq[req_seq] = (op, vals, msg, flags)
            payload += msg
            req_seq += 1

@@ -1027,9 +1038,9 @@ class YnlFamily(SpecFamily):
            self._recv_dbg_print(reply, nms)
            for nl_msg in nms:
                if nl_msg.nl_seq in reqs_by_seq:
                    (op, req_msg, req_flags) = reqs_by_seq[nl_msg.nl_seq]
                    (op, vals, req_msg, req_flags) = reqs_by_seq[nl_msg.nl_seq]
                    if nl_msg.extack:
                        self._decode_extack(req_msg, op, nl_msg.extack)
                        self._decode_extack(req_msg, op, nl_msg.extack, vals)
                else:
                    op = None
                    req_flags = []