Commit 509afc74 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'tools-net-ynl-add-sub-message-support-to-ynl'

Donald Hunter says:

====================
tools/net/ynl: Add 'sub-message' support to ynl

This patchset adds a 'sub-message' attribute type to the netlink-raw
schema and implements it in ynl. This provides support for kind-specific
options attributes as used in rt_link and tc raw netlink families.

A description of the new 'sub-message' attribute type and the
corresponding sub-message definitions is provided in patch 3.

The patchset includes updates to the rt_link spec and a new tc spec that
make use of the new 'sub-message' attribute type.

As mentioned in patch 4, encode support is not yet implemented in ynl
and support for sub-message selectors at a different nest level from the
key attribute is not yet supported. I plan to work on these in follow-up
patches.

Patches 1 is code cleanup in ynl
Patches 2-4 add sub-message support to the schema and ynl with
documentation updates.
Patch 5 adds binary and pad support to structs in netlink-raw.
Patches 6-8 contain specs that use the sub-message attribute type.
Patches 9-13 update ynl-gen-rst and its make target
====================

Link: https://lore.kernel.org/r/20231215093720.18774-1-donald.hunter@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 610a689d 9b0aa224
......@@ -106,10 +106,10 @@ YNL_RST_FILES_TMP := $(patsubst %.yaml,%.rst,$(wildcard $(YNL_YAML_DIR)/*.yaml))
YNL_RST_FILES := $(patsubst $(YNL_YAML_DIR)%,$(YNL_RST_DIR)%, $(YNL_RST_FILES_TMP))
$(YNL_INDEX): $(YNL_RST_FILES)
@$(YNL_TOOL) -o $@ -x
$(Q)$(YNL_TOOL) -o $@ -x
$(YNL_RST_DIR)/%.rst: $(YNL_YAML_DIR)/%.yaml
@$(YNL_TOOL) -i $< -o $@
$(YNL_RST_DIR)/%.rst: $(YNL_YAML_DIR)/%.yaml $(YNL_TOOL)
$(Q)$(YNL_TOOL) -i $< -o $@
htmldocs: $(YNL_INDEX)
@$(srctree)/scripts/sphinx-pre-install --version-check
......
......@@ -126,8 +126,10 @@ properties:
name:
type: string
type:
description: The netlink attribute type
enum: [ u8, u16, u32, u64, s8, s16, s32, s64, string, binary ]
description: |
The netlink attribute type. Members of type 'binary' or 'pad'
must also have the 'len' property set.
enum: [ u8, u16, u32, u64, s8, s16, s32, s64, string, binary, pad ]
len:
$ref: '#/$defs/len-or-define'
byte-order:
......@@ -150,6 +152,14 @@ properties:
the right formatting mechanism when displaying values of this
type.
enum: [ hex, mac, fddi, ipv4, ipv6, uuid ]
if:
properties:
type:
oneOf:
- const: binary
- const: pad
then:
required: [ len ]
# End genetlink-legacy
attribute-sets:
......@@ -202,7 +212,8 @@ properties:
description: The netlink attribute type
enum: [ unused, pad, flag, binary, bitfield32,
u8, u16, u32, u64, s8, s16, s32, s64,
string, nest, array-nest, nest-type-value ]
string, nest, array-nest, nest-type-value,
sub-message ]
doc:
description: Documentation of the attribute.
type: string
......@@ -261,6 +272,17 @@ properties:
description: Name of the struct type used for the attribute.
type: string
# End genetlink-legacy
# Start netlink-raw
sub-message:
description: |
Name of the sub-message definition to use for the attribute.
type: string
selector:
description: |
Name of the attribute to use for dynamic selection of sub-message
format specifier.
type: string
# End netlink-raw
# Make sure name-prefix does not appear in subsets (subsets inherit naming)
dependencies:
......@@ -283,6 +305,43 @@ properties:
items:
required: [ type ]
# Start netlink-raw
sub-messages:
description: Definition of sub message attributes
type: array
items:
type: object
additionalProperties: False
required: [ name, formats ]
properties:
name:
description: Name of the sub-message definition
type: string
formats:
description: Dynamically selected format specifiers
type: array
items:
type: object
additionalProperties: False
required: [ value ]
properties:
value:
description: |
Value to match for dynamic selection of sub-message format
specifier.
type: string
fixed-header:
description: |
Name of the struct definition to use as the fixed header
for the sub message.
type: string
attribute-set:
description: |
Name of the attribute space from which to resolve attributes
in the sub message.
type: string
# End netlink-raw
operations:
description: Operations supported by the protocol.
type: object
......
......@@ -66,8 +66,9 @@ definitions:
name: ifi-family
type: u8
-
name: padding
type: u8
name: pad
type: pad
len: 1
-
name: ifi-type
type: u16
......@@ -82,6 +83,18 @@ definitions:
-
name: ifi-change
type: u32
-
name: ifla-bridge-id
type: struct
members:
-
name: prio
type: u16
-
name: addr
type: binary
len: 6
display-hint: mac
-
name: ifla-cacheinfo
type: struct
......@@ -707,11 +720,9 @@ definitions:
name: family
type: u8
-
name: pad1
type: u8
-
name: pad2
type: u16
name: pad
type: pad
len: 3
-
name: ifindex
type: u32
......@@ -966,8 +977,9 @@ attribute-sets:
type: string
-
name: data
type: binary
# kind specific nest, e.g. linkinfo-bridge-attrs
type: sub-message
sub-message: linkinfo-data-msg
selector: kind
-
name: xstats
type: binary
......@@ -976,10 +988,12 @@ attribute-sets:
type: string
-
name: slave-data
type: binary
# kind specific nest
type: sub-message
sub-message: linkinfo-member-data-msg
selector: slave-kind
-
name: linkinfo-bridge-attrs
name-prefix: ifla-br-
attributes:
-
name: forward-delay
......@@ -1011,9 +1025,11 @@ attribute-sets:
-
name: root-id
type: binary
struct: ifla-bridge-id
-
name: bridge-id
type: binary
struct: ifla-bridge-id
-
name: root-port
type: u16
......@@ -1041,6 +1057,7 @@ attribute-sets:
-
name: group-addr
type: binary
display-hint: mac
-
name: fdb-flush
type: binary
......@@ -1123,6 +1140,376 @@ attribute-sets:
-
name: mcast-querier-state
type: binary
-
name: linkinfo-brport-attrs
name-prefix: ifla-brport-
attributes:
-
name: state
type: u8
-
name: priority
type: u16
-
name: cost
type: u32
-
name: mode
type: flag
-
name: guard
type: flag
-
name: protect
type: flag
-
name: fast-leave
type: flag
-
name: learning
type: flag
-
name: unicast-flood
type: flag
-
name: proxyarp
type: flag
-
name: learning-sync
type: flag
-
name: proxyarp-wifi
type: flag
-
name: root-id
type: binary
struct: ifla-bridge-id
-
name: bridge-id
type: binary
struct: ifla-bridge-id
-
name: designated-port
type: u16
-
name: designated-cost
type: u16
-
name: id
type: u16
-
name: "no"
type: u16
-
name: topology-change-ack
type: u8
-
name: config-pending
type: u8
-
name: message-age-timer
type: u64
-
name: forward-delay-timer
type: u64
-
name: hold-timer
type: u64
-
name: flush
type: flag
-
name: multicast-router
type: u8
-
name: pad
type: pad
-
name: mcast-flood
type: flag
-
name: mcast-to-ucast
type: flag
-
name: vlan-tunnel
type: flag
-
name: bcast-flood
type: flag
-
name: group-fwd-mask
type: u16
-
name: neigh-suppress
type: flag
-
name: isolated
type: flag
-
name: backup-port
type: u32
-
name: mrp-ring-open
type: flag
-
name: mrp-in-open
type: flag
-
name: mcast-eht-hosts-limit
type: u32
-
name: mcast-eht-hosts-cnt
type: u32
-
name: locked
type: flag
-
name: mab
type: flag
-
name: mcast-n-groups
type: u32
-
name: mcast-max-groups
type: u32
-
name: neigh-vlan-suppress
type: flag
-
name: backup-nhid
type: u32
-
name: linkinfo-gre-attrs
name-prefix: ifla-gre-
attributes:
-
name: link
type: u32
-
name: iflags
type: u16
-
name: oflags
type: u16
-
name: ikey
type: u32
-
name: okey
type: u32
-
name: local
type: binary
display-hint: ipv4
-
name: remote
type: binary
display-hint: ipv4
-
name: ttl
type: u8
-
name: tos
type: u8
-
name: pmtudisc
type: u8
-
name: encap-limit
type: u32
-
name: flowinfo
type: u32
-
name: flags
type: u32
-
name: encap-type
type: u16
-
name: encap-flags
type: u16
-
name: encap-sport
type: u16
-
name: encap-dport
type: u16
-
name: collect-metadata
type: flag
-
name: ignore-df
type: u8
-
name: fwmark
type: u32
-
name: erspan-index
type: u32
-
name: erspan-ver
type: u8
-
name: erspan-dir
type: u8
-
name: erspan-hwid
type: u16
-
name: linkinfo-geneve-attrs
name-prefix: ifla-geneve-
attributes:
-
name: id
type: u32
-
name: remote
type: binary
display-hint: ipv4
-
name: ttl
type: u8
-
name: tos
type: u8
-
name: port
type: u16
-
name: collect-metadata
type: flag
-
name: remote6
type: binary
display-hint: ipv6
-
name: udp-csum
type: u8
-
name: udp-zero-csum6-tx
type: u8
-
name: udp-zero-csum6-rx
type: u8
-
name: label
type: u32
-
name: ttl-inherit
type: u8
-
name: df
type: u8
-
name: inner-proto-inherit
type: flag
-
name: linkinfo-iptun-attrs
name-prefix: ifla-iptun-
attributes:
-
name: link
type: u32
-
name: local
type: binary
display-hint: ipv4
-
name: remote
type: binary
display-hint: ipv4
-
name: ttl
type: u8
-
name: tos
type: u8
-
name: encap-limit
type: u8
-
name: flowinfo
type: u32
-
name: flags
type: u16
-
name: proto
type: u8
-
name: pmtudisc
type: u8
-
name: 6rd-prefix
type: binary
display-hint: ipv6
-
name: 6rd-relay-prefix
type: binary
display-hint: ipv4
-
name: 6rd-prefixlen
type: u16
-
name: 6rd-relay-prefixlen
type: u16
-
name: encap-type
type: u16
-
name: encap-flags
type: u16
-
name: encap-sport
type: u16
-
name: encap-dport
type: u16
-
name: collect-metadata
type: flag
-
name: fwmark
type: u32
-
name: linkinfo-tun-attrs
name-prefix: ifla-tun-
attributes:
-
name: owner
type: u32
-
name: group
type: u32
-
name: type
type: u8
-
name: pi
type: u8
-
name: vnet-hdr
type: u8
-
name: persist
type: u8
-
name: multi-queue
type: u8
-
name: num-queues
type: u32
-
name: num-disabled-queues
type: u32
-
name: linkinfo-vrf-attrs
name-prefix: ifla-vrf-
attributes:
-
name: table
type: u32
-
name: xdp-attrs
attributes:
......@@ -1241,6 +1628,46 @@ attribute-sets:
name: used
type: u8
sub-messages:
-
name: linkinfo-data-msg
formats:
-
value: bridge
attribute-set: linkinfo-bridge-attrs
-
value: erspan
attribute-set: linkinfo-gre-attrs
-
value: gre
attribute-set: linkinfo-gre-attrs
-
value: gretap
attribute-set: linkinfo-gre-attrs
-
value: geneve
attribute-set: linkinfo-geneve-attrs
-
value: ipip
attribute-set: linkinfo-iptun-attrs
-
value: sit
attribute-set: linkinfo-iptun-attrs
-
value: tun
attribute-set: linkinfo-tun-attrs
-
value: vrf
attribute-set: linkinfo-vrf-attrs
-
name: linkinfo-member-data-msg
formats:
-
value: bridge
attribute-set: linkinfo-brport-attrs
-
value: bond
operations:
enum-model: directional
list:
......
This diff is collapsed.
......@@ -14,7 +14,8 @@ Specification
The netlink-raw schema extends the :doc:`genetlink-legacy <genetlink-legacy>`
schema with properties that are needed to specify the protocol numbers and
multicast IDs used by raw netlink families. See :ref:`classic_netlink` for more
information.
information. The raw netlink families also make use of type-specific
sub-messages.
Globals
-------
......@@ -56,3 +57,96 @@ group registration.
-
name: rtnlgrp-mctp-ifaddr
value: 34
Sub-messages
------------
Several raw netlink families such as
:doc:`rt_link<../../networking/netlink_spec/rt_link>` and
:doc:`tc<../../networking/netlink_spec/tc>` use attribute nesting as an
abstraction to carry module specific information.
Conceptually it looks as follows::
[OUTER NEST OR MESSAGE LEVEL]
[GENERIC ATTR 1]
[GENERIC ATTR 2]
[GENERIC ATTR 3]
[GENERIC ATTR - wrapper]
[MODULE SPECIFIC ATTR 1]
[MODULE SPECIFIC ATTR 2]
The ``GENERIC ATTRs`` at the outer level are defined in the core (or rt_link or
core TC), while specific drivers, TC classifiers, qdiscs etc. can carry their
own information wrapped in the ``GENERIC ATTR - wrapper``. Even though the
example above shows attributes nesting inside the wrapper, the modules generally
have full freedom to define the format of the nest. In practice the payload of
the wrapper attr has very similar characteristics to a netlink message. It may
contain a fixed header / structure, netlink attributes, or both. Because of
those shared characteristics we refer to the payload of the wrapper attribute as
a sub-message.
A sub-message attribute uses the value of another attribute as a selector key to
choose the right sub-message format. For example if the following attribute has
already been decoded:
.. code-block:: json
{ "kind": "gre" }
and we encounter the following attribute spec:
.. code-block:: yaml
-
name: data
type: sub-message
sub-message: linkinfo-data-msg
selector: kind
Then we look for a sub-message definition called ``linkinfo-data-msg`` and use
the value of the ``kind`` attribute i.e. ``gre`` as the key to choose the
correct format for the sub-message:
.. code-block:: yaml
sub-messages:
name: linkinfo-data-msg
formats:
-
value: bridge
attribute-set: linkinfo-bridge-attrs
-
value: gre
attribute-set: linkinfo-gre-attrs
-
value: geneve
attribute-set: linkinfo-geneve-attrs
This would decode the attribute value as a sub-message with the attribute-set
called ``linkinfo-gre-attrs`` as the attribute space.
A sub-message can have an optional ``fixed-header`` followed by zero or more
attributes from an ``attribute-set``. For example the following
``tc-options-msg`` sub-message defines message formats that use a mixture of
``fixed-header``, ``attribute-set`` or both together:
.. code-block:: yaml
sub-messages:
-
name: tc-options-msg
formats:
-
value: bfifo
fixed-header: tc-fifo-qopt
-
value: cake
attribute-set: tc-cake-attrs
-
value: netem
fixed-header: tc-netem-qopt
attribute-set: tc-netem-attrs
Note that a selector attribute must appear in a netlink message before any
sub-message attributes that depend on it.
......@@ -158,6 +158,9 @@ class SpecAttr(SpecElement):
len integer, optional byte length of binary types
display_hint string, hint to help choose format specifier
when displaying the value
sub_message string, name of sub message type
selector string, name of attribute used to select
sub-message type
is_auto_scalar bool, attr is a variable-size scalar
"""
......@@ -173,6 +176,8 @@ class SpecAttr(SpecElement):
self.byte_order = yaml.get('byte-order')
self.len = yaml.get('len')
self.display_hint = yaml.get('display-hint')
self.sub_message = yaml.get('sub-message')
self.selector = yaml.get('selector')
self.is_auto_scalar = self.type == "sint" or self.type == "uint"
......@@ -278,6 +283,47 @@ class SpecStruct(SpecElement):
return self.members.items()
class SpecSubMessage(SpecElement):
""" Netlink sub-message definition
Represents a set of sub-message formats for polymorphic nlattrs
that contain type-specific sub messages.
Attributes:
name string, name of sub-message definition
formats dict of sub-message formats indexed by match value
"""
def __init__(self, family, yaml):
super().__init__(family, yaml)
self.formats = collections.OrderedDict()
for elem in self.yaml['formats']:
format = self.new_format(family, elem)
self.formats[format.value] = format
def new_format(self, family, format):
return SpecSubMessageFormat(family, format)
class SpecSubMessageFormat(SpecElement):
""" Netlink sub-message definition
Represents a set of sub-message formats for polymorphic nlattrs
that contain type-specific sub messages.
Attributes:
value attribute value to match against type selector
fixed_header string, name of fixed header, or None
attr_set string, name of attribute set, or None
"""
def __init__(self, family, yaml):
super().__init__(family, yaml)
self.value = yaml.get('value')
self.fixed_header = yaml.get('fixed-header')
self.attr_set = yaml.get('attribute-set')
class SpecOperation(SpecElement):
"""Netlink Operation
......@@ -365,6 +411,7 @@ class SpecFamily(SpecElement):
attr_sets dict of attribute sets
msgs dict of all messages (index by name)
sub_msgs dict of all sub messages (index by name)
ops dict of all valid requests / responses
ntfs dict of all async events
consts dict of all constants/enums
......@@ -405,6 +452,7 @@ class SpecFamily(SpecElement):
jsonschema.validate(self.yaml, schema)
self.attr_sets = collections.OrderedDict()
self.sub_msgs = collections.OrderedDict()
self.msgs = collections.OrderedDict()
self.req_by_value = collections.OrderedDict()
self.rsp_by_value = collections.OrderedDict()
......@@ -441,6 +489,9 @@ class SpecFamily(SpecElement):
def new_struct(self, elem):
return SpecStruct(self, elem)
def new_sub_message(self, elem):
return SpecSubMessage(self, elem);
def new_operation(self, elem, req_val, rsp_val):
return SpecOperation(self, elem, req_val, rsp_val)
......@@ -529,6 +580,10 @@ class SpecFamily(SpecElement):
attr_set = self.new_attr_set(elem)
self.attr_sets[elem['name']] = attr_set
for elem in self.yaml.get('sub-messages', []):
sub_message = self.new_sub_message(elem)
self.sub_msgs[sub_message.name] = sub_message
if self.msg_id_model == 'unified':
self._dictify_ops_unified()
elif self.msg_id_model == 'directional':
......
......@@ -98,12 +98,12 @@ class NlAttr:
}
def __init__(self, raw, offset):
self._len, self._type = struct.unpack("HH", raw[offset:offset + 4])
self._len, self._type = struct.unpack("HH", raw[offset : offset + 4])
self.type = self._type & ~Netlink.NLA_TYPE_MASK
self.is_nest = self._type & Netlink.NLA_F_NESTED
self.payload_len = self._len
self.full_len = (self.payload_len + 3) & ~3
self.raw = raw[offset + 4:offset + self.payload_len]
self.raw = raw[offset + 4 : offset + self.payload_len]
@classmethod
def get_format(cls, attr_type, byte_order=None):
......@@ -154,7 +154,7 @@ class NlAttr:
for m in members:
# TODO: handle non-scalar members
if m.type == 'binary':
decoded = self.raw[offset:offset+m['len']]
decoded = self.raw[offset : offset + m['len']]
offset += m['len']
elif m.type in NlAttr.type_formats:
format = self.get_format(m.type, m.byte_order)
......@@ -170,10 +170,9 @@ class NlAttr:
class NlAttrs:
def __init__(self, msg):
def __init__(self, msg, offset=0):
self.attrs = []
offset = 0
while offset < len(msg):
attr = NlAttr(msg, offset)
offset += attr.full_len
......@@ -193,12 +192,12 @@ class NlAttrs:
class NlMsg:
def __init__(self, msg, offset, attr_space=None):
self.hdr = msg[offset:offset + 16]
self.hdr = msg[offset : offset + 16]
self.nl_len, self.nl_type, self.nl_flags, self.nl_seq, self.nl_portid = \
struct.unpack("IHHII", self.hdr)
self.raw = msg[offset + 16:offset + self.nl_len]
self.raw = msg[offset + 16 : offset + self.nl_len]
self.error = 0
self.done = 0
......@@ -371,8 +370,8 @@ class NetlinkProtocol:
fixed_header_size = 0
if ynl:
op = ynl.rsp_by_value[msg.cmd()]
fixed_header_size = ynl._fixed_header_size(op)
msg.raw_attrs = NlAttrs(msg.raw[fixed_header_size:])
fixed_header_size = ynl._fixed_header_size(op.fixed_header)
msg.raw_attrs = NlAttrs(msg.raw, fixed_header_size)
return msg
def get_mcast_id(self, mcast_name, mcast_groups):
......@@ -549,6 +548,37 @@ class YnlFamily(SpecFamily):
else:
rsp[name] = [decoded]
def _resolve_selector(self, attr_spec, vals):
sub_msg = attr_spec.sub_message
if sub_msg not in self.sub_msgs:
raise Exception(f"No sub-message spec named {sub_msg} for {attr_spec.name}")
sub_msg_spec = self.sub_msgs[sub_msg]
selector = attr_spec.selector
if selector not in vals:
raise Exception(f"There is no value for {selector} to resolve '{attr_spec.name}'")
value = vals[selector]
if value not in sub_msg_spec.formats:
raise Exception(f"No message format for '{value}' in sub-message spec '{sub_msg}'")
spec = sub_msg_spec.formats[value]
return spec
def _decode_sub_msg(self, attr, attr_spec, rsp):
msg_format = self._resolve_selector(attr_spec, rsp)
decoded = {}
offset = 0
if msg_format.fixed_header:
decoded.update(self._decode_fixed_header(attr, msg_format.fixed_header));
offset = self._fixed_header_size(msg_format.fixed_header)
if msg_format.attr_set:
if msg_format.attr_set in self.attr_sets:
subdict = self._decode(NlAttrs(attr.raw, offset), msg_format.attr_set)
decoded.update(subdict)
else:
raise Exception(f"Unknown attribute-set '{attr_space}' when decoding '{attr_spec.name}'")
return decoded
def _decode(self, attrs, space):
if space:
attr_space = self.attr_sets[space]
......@@ -586,6 +616,8 @@ class YnlFamily(SpecFamily):
value = self._decode_enum(value, attr_spec)
selector = self._decode_enum(selector, attr_spec)
decoded = {"value": value, "selector": selector}
elif attr_spec["type"] == 'sub-message':
decoded = self._decode_sub_msg(attr, attr_spec, rsp)
else:
if not self.process_unknown:
raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
......@@ -626,20 +658,23 @@ class YnlFamily(SpecFamily):
return
msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set))
offset = 20 + self._fixed_header_size(op)
offset = 20 + self._fixed_header_size(op.fixed_header)
path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset,
extack['bad-attr-offs'])
if path:
del extack['bad-attr-offs']
extack['bad-attr'] = path
def _fixed_header_size(self, op):
if op.fixed_header:
fixed_header_members = self.consts[op.fixed_header].members
def _fixed_header_size(self, name):
if name:
fixed_header_members = self.consts[name].members
size = 0
for m in fixed_header_members:
format = NlAttr.get_format(m.type, m.byte_order)
size += format.size
if m.type in ['pad', 'binary']:
size += m.len
else:
format = NlAttr.get_format(m.type, m.byte_order)
size += format.size
return size
else:
return 0
......@@ -649,12 +684,20 @@ class YnlFamily(SpecFamily):
fixed_header_attrs = dict()
offset = 0
for m in fixed_header_members:
format = NlAttr.get_format(m.type, m.byte_order)
[ value ] = format.unpack_from(msg.raw, offset)
offset += format.size
if m.enum:
value = self._decode_enum(value, m)
fixed_header_attrs[m.name] = value
value = None
if m.type == 'pad':
offset += m.len
elif m.type == 'binary':
value = msg.raw[offset : offset + m.len]
offset += m.len
else:
format = NlAttr.get_format(m.type, m.byte_order)
[ value ] = format.unpack_from(msg.raw, offset)
offset += format.size
if value is not None:
if m.enum:
value = self._decode_enum(value, m)
fixed_header_attrs[m.name] = value
return fixed_header_attrs
def handle_ntf(self, decoded):
......@@ -721,8 +764,13 @@ class YnlFamily(SpecFamily):
fixed_header_members = self.consts[op.fixed_header].members
for m in fixed_header_members:
value = vals.pop(m.name) if m.name in vals else 0
format = NlAttr.get_format(m.type, m.byte_order)
msg += format.pack(value)
if m.type == 'pad':
msg += bytearray(m.len)
elif m.type == 'binary':
msg += bytes.fromhex(value)
else:
format = NlAttr.get_format(m.type, m.byte_order)
msg += format.pack(value)
for name, value in vals.items():
msg += self._add_attr(op.attr_set.name, name, value)
msg = _genl_msg_finalize(msg)
......
......@@ -69,7 +69,7 @@ def rst_paragraph(paragraph: str, level: int = 0) -> str:
def rst_bullet(item: str, level: int = 0) -> str:
"""Return a formatted a bullet"""
return headroom(level) + f" - {item}"
return headroom(level) + f"- {item}"
def rst_subsection(title: str) -> str:
......@@ -240,7 +240,7 @@ def parse_attr_sets(entries: List[Dict[str, Any]]) -> str:
lines.append(rst_section(entry["name"]))
for attr in entry["attributes"]:
type_ = attr.get("type")
attr_line = bold(attr["name"])
attr_line = attr["name"]
if type_:
# Add the attribute type in the same line
attr_line += f" ({inline(type_)})"
......@@ -250,7 +250,25 @@ def parse_attr_sets(entries: List[Dict[str, Any]]) -> str:
for k in attr.keys():
if k in preprocessed + ignored:
continue
lines.append(rst_fields(k, sanitize(attr[k]), 2))
lines.append(rst_fields(k, sanitize(attr[k]), 0))
lines.append("\n")
return "\n".join(lines)
def parse_sub_messages(entries: List[Dict[str, Any]]) -> str:
"""Parse sub-message definitions"""
lines = []
for entry in entries:
lines.append(rst_section(entry["name"]))
for fmt in entry["formats"]:
value = fmt["value"]
lines.append(rst_bullet(bold(value)))
for attr in ['fixed-header', 'attribute-set']:
if attr in fmt:
lines.append(rst_fields(attr, fmt[attr], 1))
lines.append("\n")
return "\n".join(lines)
......@@ -292,6 +310,11 @@ def parse_yaml(obj: Dict[str, Any]) -> str:
lines.append(rst_subtitle("Attribute sets"))
lines.append(parse_attr_sets(obj["attribute-sets"]))
# Sub-messages
if "sub-messages" in obj:
lines.append(rst_subtitle("Sub-messages"))
lines.append(parse_sub_messages(obj["sub-messages"]))
return "\n".join(lines)
......@@ -360,7 +383,7 @@ def generate_main_index_rst(output: str) -> None:
index_dir = os.path.dirname(output)
logging.debug("Looking for .rst files in %s", index_dir)
for filename in os.listdir(index_dir):
for filename in sorted(os.listdir(index_dir)):
if not filename.endswith(".rst") or filename == "index.rst":
continue
lines.append(f" {filename.replace('.rst', '')}\n")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment