xref: /linux/tools/net/ynl/lib/nlspec.py (revision 3e51f2cb)
137d9df22SJakub Kicinski# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
23aacf828SJakub Kicinski
33aacf828SJakub Kicinskiimport collections
45c6674f6SJakub Kicinskiimport importlib
53aacf828SJakub Kicinskiimport os
63aacf828SJakub Kicinskiimport yaml
73aacf828SJakub Kicinski
83aacf828SJakub Kicinski
95c6674f6SJakub Kicinski# To be loaded dynamically as needed
105c6674f6SJakub Kicinskijsonschema = None
115c6674f6SJakub Kicinski
125c6674f6SJakub Kicinski
133aacf828SJakub Kicinskiclass SpecElement:
143aacf828SJakub Kicinski    """Netlink spec element.
153aacf828SJakub Kicinski
163aacf828SJakub Kicinski    Abstract element of the Netlink spec. Implements the dictionary interface
173aacf828SJakub Kicinski    for access to the raw spec. Supports iterative resolution of dependencies
183aacf828SJakub Kicinski    across elements and class inheritance levels. The elements of the spec
193aacf828SJakub Kicinski    may refer to each other, and although loops should be very rare, having
203aacf828SJakub Kicinski    to maintain correct ordering of instantiation is painful, so the resolve()
213aacf828SJakub Kicinski    method should be used to perform parts of init which require access to
223aacf828SJakub Kicinski    other parts of the spec.
233aacf828SJakub Kicinski
243aacf828SJakub Kicinski    Attributes:
253aacf828SJakub Kicinski        yaml        raw spec as loaded from the spec file
263aacf828SJakub Kicinski        family      back reference to the full family
273aacf828SJakub Kicinski
283aacf828SJakub Kicinski        name        name of the entity as listed in the spec (optional)
293aacf828SJakub Kicinski        ident_name  name which can be safely used as identifier in code (optional)
303aacf828SJakub Kicinski    """
313aacf828SJakub Kicinski    def __init__(self, family, yaml):
323aacf828SJakub Kicinski        self.yaml = yaml
333aacf828SJakub Kicinski        self.family = family
343aacf828SJakub Kicinski
353aacf828SJakub Kicinski        if 'name' in self.yaml:
363aacf828SJakub Kicinski            self.name = self.yaml['name']
373aacf828SJakub Kicinski            self.ident_name = self.name.replace('-', '_')
383aacf828SJakub Kicinski
393aacf828SJakub Kicinski        self._super_resolved = False
403aacf828SJakub Kicinski        family.add_unresolved(self)
413aacf828SJakub Kicinski
423aacf828SJakub Kicinski    def __getitem__(self, key):
433aacf828SJakub Kicinski        return self.yaml[key]
443aacf828SJakub Kicinski
453aacf828SJakub Kicinski    def __contains__(self, key):
463aacf828SJakub Kicinski        return key in self.yaml
473aacf828SJakub Kicinski
483aacf828SJakub Kicinski    def get(self, key, default=None):
493aacf828SJakub Kicinski        return self.yaml.get(key, default)
503aacf828SJakub Kicinski
513aacf828SJakub Kicinski    def resolve_up(self, up):
523aacf828SJakub Kicinski        if not self._super_resolved:
533aacf828SJakub Kicinski            up.resolve()
543aacf828SJakub Kicinski            self._super_resolved = True
553aacf828SJakub Kicinski
563aacf828SJakub Kicinski    def resolve(self):
573aacf828SJakub Kicinski        pass
583aacf828SJakub Kicinski
593aacf828SJakub Kicinski
606517a60bSJakub Kicinskiclass SpecEnumEntry(SpecElement):
616517a60bSJakub Kicinski    """ Entry within an enum declared in the Netlink spec.
626517a60bSJakub Kicinski
636517a60bSJakub Kicinski    Attributes:
646517a60bSJakub Kicinski        doc         documentation string
656517a60bSJakub Kicinski        enum_set    back reference to the enum
666517a60bSJakub Kicinski        value       numerical value of this enum (use accessors in most situations!)
676517a60bSJakub Kicinski
686517a60bSJakub Kicinski    Methods:
696517a60bSJakub Kicinski        raw_value   raw value, i.e. the id in the enum, unlike user value which is a mask for flags
706517a60bSJakub Kicinski        user_value   user value, same as raw value for enums, for flags it's the mask
716517a60bSJakub Kicinski    """
726517a60bSJakub Kicinski    def __init__(self, enum_set, yaml, prev, value_start):
736517a60bSJakub Kicinski        if isinstance(yaml, str):
746517a60bSJakub Kicinski            yaml = {'name': yaml}
756517a60bSJakub Kicinski        super().__init__(enum_set.family, yaml)
766517a60bSJakub Kicinski
776517a60bSJakub Kicinski        self.doc = yaml.get('doc', '')
786517a60bSJakub Kicinski        self.enum_set = enum_set
796517a60bSJakub Kicinski
806517a60bSJakub Kicinski        if 'value' in yaml:
816517a60bSJakub Kicinski            self.value = yaml['value']
826517a60bSJakub Kicinski        elif prev:
836517a60bSJakub Kicinski            self.value = prev.value + 1
846517a60bSJakub Kicinski        else:
856517a60bSJakub Kicinski            self.value = value_start
866517a60bSJakub Kicinski
876517a60bSJakub Kicinski    def has_doc(self):
886517a60bSJakub Kicinski        return bool(self.doc)
896517a60bSJakub Kicinski
906517a60bSJakub Kicinski    def raw_value(self):
916517a60bSJakub Kicinski        return self.value
926517a60bSJakub Kicinski
934c6170d1SJakub Kicinski    def user_value(self, as_flags=None):
944c6170d1SJakub Kicinski        if self.enum_set['type'] == 'flags' or as_flags:
956517a60bSJakub Kicinski            return 1 << self.value
966517a60bSJakub Kicinski        else:
976517a60bSJakub Kicinski            return self.value
986517a60bSJakub Kicinski
996517a60bSJakub Kicinski
1006517a60bSJakub Kicinskiclass SpecEnumSet(SpecElement):
1016517a60bSJakub Kicinski    """ Enum type
1026517a60bSJakub Kicinski
1036517a60bSJakub Kicinski    Represents an enumeration (list of numerical constants)
1046517a60bSJakub Kicinski    as declared in the "definitions" section of the spec.
1056517a60bSJakub Kicinski
1066517a60bSJakub Kicinski    Attributes:
1076517a60bSJakub Kicinski        type            enum or flags
1086517a60bSJakub Kicinski        entries         entries by name
109c311aaa7SJakub Kicinski        entries_by_val  entries by value
1106517a60bSJakub Kicinski    Methods:
1116517a60bSJakub Kicinski        get_mask      for flags compute the mask of all defined values
1126517a60bSJakub Kicinski    """
1136517a60bSJakub Kicinski    def __init__(self, family, yaml):
1146517a60bSJakub Kicinski        super().__init__(family, yaml)
1156517a60bSJakub Kicinski
1166517a60bSJakub Kicinski        self.type = yaml['type']
1176517a60bSJakub Kicinski
1186517a60bSJakub Kicinski        prev_entry = None
1196517a60bSJakub Kicinski        value_start = self.yaml.get('value-start', 0)
1206517a60bSJakub Kicinski        self.entries = dict()
121c311aaa7SJakub Kicinski        self.entries_by_val = dict()
1226517a60bSJakub Kicinski        for entry in self.yaml['entries']:
1236517a60bSJakub Kicinski            e = self.new_entry(entry, prev_entry, value_start)
1246517a60bSJakub Kicinski            self.entries[e.name] = e
125c311aaa7SJakub Kicinski            self.entries_by_val[e.raw_value()] = e
1266517a60bSJakub Kicinski            prev_entry = e
1276517a60bSJakub Kicinski
1286517a60bSJakub Kicinski    def new_entry(self, entry, prev_entry, value_start):
1296517a60bSJakub Kicinski        return SpecEnumEntry(self, entry, prev_entry, value_start)
1306517a60bSJakub Kicinski
1316517a60bSJakub Kicinski    def has_doc(self):
1326517a60bSJakub Kicinski        if 'doc' in self.yaml:
1336517a60bSJakub Kicinski            return True
1346517a60bSJakub Kicinski        for entry in self.entries.values():
1356517a60bSJakub Kicinski            if entry.has_doc():
1366517a60bSJakub Kicinski                return True
1376517a60bSJakub Kicinski        return False
1386517a60bSJakub Kicinski
1394c6170d1SJakub Kicinski    def get_mask(self, as_flags=None):
1406517a60bSJakub Kicinski        mask = 0
141bf51d277SLorenzo Bianconi        for e in self.entries.values():
1424c6170d1SJakub Kicinski            mask += e.user_value(as_flags)
1436517a60bSJakub Kicinski        return mask
1446517a60bSJakub Kicinski
1456517a60bSJakub Kicinski
1463aacf828SJakub Kicinskiclass SpecAttr(SpecElement):
1477b4434a8SAlessandro Marcolini    """ Single Netlink attribute type
1483aacf828SJakub Kicinski
1493aacf828SJakub Kicinski    Represents a single attribute type within an attr space.
1503aacf828SJakub Kicinski
1513aacf828SJakub Kicinski    Attributes:
1527d4caf54SJakub Kicinski        type          string, attribute type
1533aacf828SJakub Kicinski        value         numerical ID when serialized
1543aacf828SJakub Kicinski        attr_set      Attribute Set containing this attr
155b423c3c8SDonald Hunter        is_multi      bool, attr may repeat multiple times
15626071913SDonald Hunter        struct_name   string, name of struct definition
157b423c3c8SDonald Hunter        sub_type      string, name of sub type
158d8eea68dSDonald Hunter        len           integer, optional byte length of binary types
159d8eea68dSDonald Hunter        display_hint  string, hint to help choose format specifier
160d8eea68dSDonald Hunter                      when displaying the value
1611769e2beSDonald Hunter        sub_message   string, name of sub message type
1621769e2beSDonald Hunter        selector      string, name of attribute used to select
1631769e2beSDonald Hunter                      sub-message type
1647d4caf54SJakub Kicinski
1657d4caf54SJakub Kicinski        is_auto_scalar bool, attr is a variable-size scalar
1663aacf828SJakub Kicinski    """
1673aacf828SJakub Kicinski    def __init__(self, family, attr_set, yaml, value):
1683aacf828SJakub Kicinski        super().__init__(family, yaml)
1693aacf828SJakub Kicinski
1707d4caf54SJakub Kicinski        self.type = yaml['type']
1713aacf828SJakub Kicinski        self.value = value
1723aacf828SJakub Kicinski        self.attr_set = attr_set
1733aacf828SJakub Kicinski        self.is_multi = yaml.get('multi-attr', False)
17426071913SDonald Hunter        self.struct_name = yaml.get('struct')
175b423c3c8SDonald Hunter        self.sub_type = yaml.get('sub-type')
1769f7cc57fSStanislav Fomichev        self.byte_order = yaml.get('byte-order')
177d8eea68dSDonald Hunter        self.len = yaml.get('len')
178d8eea68dSDonald Hunter        self.display_hint = yaml.get('display-hint')
1791769e2beSDonald Hunter        self.sub_message = yaml.get('sub-message')
1801769e2beSDonald Hunter        self.selector = yaml.get('selector')
1813aacf828SJakub Kicinski
1827d4caf54SJakub Kicinski        self.is_auto_scalar = self.type == "sint" or self.type == "uint"
1837d4caf54SJakub Kicinski
1843aacf828SJakub Kicinski
1853aacf828SJakub Kicinskiclass SpecAttrSet(SpecElement):
1863aacf828SJakub Kicinski    """ Netlink Attribute Set class.
1873aacf828SJakub Kicinski
1883aacf828SJakub Kicinski    Represents a ID space of attributes within Netlink.
1893aacf828SJakub Kicinski
1903aacf828SJakub Kicinski    Note that unlike other elements, which expose contents of the raw spec
1913aacf828SJakub Kicinski    via the dictionary interface Attribute Set exposes attributes by name.
1923aacf828SJakub Kicinski
1933aacf828SJakub Kicinski    Attributes:
1943aacf828SJakub Kicinski        attrs      ordered dict of all attributes (indexed by name)
1953aacf828SJakub Kicinski        attrs_by_val  ordered dict of all attributes (indexed by value)
1963aacf828SJakub Kicinski        subset_of  parent set if this is a subset, otherwise None
1973aacf828SJakub Kicinski    """
1983aacf828SJakub Kicinski    def __init__(self, family, yaml):
1993aacf828SJakub Kicinski        super().__init__(family, yaml)
2003aacf828SJakub Kicinski
2013aacf828SJakub Kicinski        self.subset_of = self.yaml.get('subset-of', None)
2023aacf828SJakub Kicinski
2033aacf828SJakub Kicinski        self.attrs = collections.OrderedDict()
2043aacf828SJakub Kicinski        self.attrs_by_val = collections.OrderedDict()
2053aacf828SJakub Kicinski
2067cf93538SJakub Kicinski        if self.subset_of is None:
207ad4fafcdSJakub Kicinski            val = 1
2083aacf828SJakub Kicinski            for elem in self.yaml['attributes']:
2093aacf828SJakub Kicinski                if 'value' in elem:
2103aacf828SJakub Kicinski                    val = elem['value']
2113aacf828SJakub Kicinski
2123aacf828SJakub Kicinski                attr = self.new_attr(elem, val)
2133aacf828SJakub Kicinski                self.attrs[attr.name] = attr
2143aacf828SJakub Kicinski                self.attrs_by_val[attr.value] = attr
2153aacf828SJakub Kicinski                val += 1
2167cf93538SJakub Kicinski        else:
2177cf93538SJakub Kicinski            real_set = family.attr_sets[self.subset_of]
2187cf93538SJakub Kicinski            for elem in self.yaml['attributes']:
2197cf93538SJakub Kicinski                attr = real_set[elem['name']]
2207cf93538SJakub Kicinski                self.attrs[attr.name] = attr
2217cf93538SJakub Kicinski                self.attrs_by_val[attr.value] = attr
2223aacf828SJakub Kicinski
2233aacf828SJakub Kicinski    def new_attr(self, elem, value):
2243aacf828SJakub Kicinski        return SpecAttr(self.family, self, elem, value)
2253aacf828SJakub Kicinski
2263aacf828SJakub Kicinski    def __getitem__(self, key):
2273aacf828SJakub Kicinski        return self.attrs[key]
2283aacf828SJakub Kicinski
2293aacf828SJakub Kicinski    def __contains__(self, key):
2303aacf828SJakub Kicinski        return key in self.attrs
2313aacf828SJakub Kicinski
2323aacf828SJakub Kicinski    def __iter__(self):
2333aacf828SJakub Kicinski        yield from self.attrs
2343aacf828SJakub Kicinski
2353aacf828SJakub Kicinski    def items(self):
2363aacf828SJakub Kicinski        return self.attrs.items()
2373aacf828SJakub Kicinski
2383aacf828SJakub Kicinski
239bec0b7a2SDonald Hunterclass SpecStructMember(SpecElement):
240bec0b7a2SDonald Hunter    """Struct member attribute
241bec0b7a2SDonald Hunter
242bec0b7a2SDonald Hunter    Represents a single struct member attribute.
243bec0b7a2SDonald Hunter
244bec0b7a2SDonald Hunter    Attributes:
245bec0b7a2SDonald Hunter        type        string, type of the member attribute
246bddd2e56SDonald Hunter        byte_order  string or None for native byte order
247313a7a80SDonald Hunter        enum        string, name of the enum definition
248d8eea68dSDonald Hunter        len         integer, optional byte length of binary types
249d8eea68dSDonald Hunter        display_hint  string, hint to help choose format specifier
250d8eea68dSDonald Hunter                      when displaying the value
251bf08f32cSDonald Hunter        struct      string, name of nested struct type
252bec0b7a2SDonald Hunter    """
253bec0b7a2SDonald Hunter    def __init__(self, family, yaml):
254bec0b7a2SDonald Hunter        super().__init__(family, yaml)
255bec0b7a2SDonald Hunter        self.type = yaml['type']
256bddd2e56SDonald Hunter        self.byte_order = yaml.get('byte-order')
257313a7a80SDonald Hunter        self.enum = yaml.get('enum')
258d8eea68dSDonald Hunter        self.len = yaml.get('len')
259d8eea68dSDonald Hunter        self.display_hint = yaml.get('display-hint')
260bf08f32cSDonald Hunter        self.struct = yaml.get('struct')
261bec0b7a2SDonald Hunter
262bec0b7a2SDonald Hunter
263bec0b7a2SDonald Hunterclass SpecStruct(SpecElement):
264bec0b7a2SDonald Hunter    """Netlink struct type
265bec0b7a2SDonald Hunter
266bec0b7a2SDonald Hunter    Represents a C struct definition.
267bec0b7a2SDonald Hunter
268bec0b7a2SDonald Hunter    Attributes:
269bec0b7a2SDonald Hunter        members   ordered list of struct members
270bec0b7a2SDonald Hunter    """
271bec0b7a2SDonald Hunter    def __init__(self, family, yaml):
272bec0b7a2SDonald Hunter        super().__init__(family, yaml)
273bec0b7a2SDonald Hunter
274bec0b7a2SDonald Hunter        self.members = []
275bec0b7a2SDonald Hunter        for member in yaml.get('members', []):
276bec0b7a2SDonald Hunter            self.members.append(self.new_member(family, member))
277bec0b7a2SDonald Hunter
278bec0b7a2SDonald Hunter    def new_member(self, family, elem):
279bec0b7a2SDonald Hunter        return SpecStructMember(family, elem)
280bec0b7a2SDonald Hunter
281bec0b7a2SDonald Hunter    def __iter__(self):
282bec0b7a2SDonald Hunter        yield from self.members
283bec0b7a2SDonald Hunter
284bec0b7a2SDonald Hunter    def items(self):
285bec0b7a2SDonald Hunter        return self.members.items()
286bec0b7a2SDonald Hunter
287bec0b7a2SDonald Hunter
2881769e2beSDonald Hunterclass SpecSubMessage(SpecElement):
2891769e2beSDonald Hunter    """ Netlink sub-message definition
2901769e2beSDonald Hunter
2911769e2beSDonald Hunter    Represents a set of sub-message formats for polymorphic nlattrs
2921769e2beSDonald Hunter    that contain type-specific sub messages.
2931769e2beSDonald Hunter
2941769e2beSDonald Hunter    Attributes:
2951769e2beSDonald Hunter        name     string, name of sub-message definition
2961769e2beSDonald Hunter        formats  dict of sub-message formats indexed by match value
2971769e2beSDonald Hunter    """
2981769e2beSDonald Hunter    def __init__(self, family, yaml):
2991769e2beSDonald Hunter        super().__init__(family, yaml)
3001769e2beSDonald Hunter
3011769e2beSDonald Hunter        self.formats = collections.OrderedDict()
3021769e2beSDonald Hunter        for elem in self.yaml['formats']:
3031769e2beSDonald Hunter            format = self.new_format(family, elem)
3041769e2beSDonald Hunter            self.formats[format.value] = format
3051769e2beSDonald Hunter
3061769e2beSDonald Hunter    def new_format(self, family, format):
3071769e2beSDonald Hunter        return SpecSubMessageFormat(family, format)
3081769e2beSDonald Hunter
3091769e2beSDonald Hunter
3101769e2beSDonald Hunterclass SpecSubMessageFormat(SpecElement):
3117b4434a8SAlessandro Marcolini    """ Netlink sub-message format definition
3121769e2beSDonald Hunter
3137b4434a8SAlessandro Marcolini    Represents a single format for a sub-message.
3141769e2beSDonald Hunter
3151769e2beSDonald Hunter    Attributes:
3161769e2beSDonald Hunter        value         attribute value to match against type selector
3171769e2beSDonald Hunter        fixed_header  string, name of fixed header, or None
3181769e2beSDonald Hunter        attr_set      string, name of attribute set, or None
3191769e2beSDonald Hunter    """
3201769e2beSDonald Hunter    def __init__(self, family, yaml):
3211769e2beSDonald Hunter        super().__init__(family, yaml)
3221769e2beSDonald Hunter
3231769e2beSDonald Hunter        self.value = yaml.get('value')
3241769e2beSDonald Hunter        self.fixed_header = yaml.get('fixed-header')
3251769e2beSDonald Hunter        self.attr_set = yaml.get('attribute-set')
3261769e2beSDonald Hunter
3271769e2beSDonald Hunter
3283aacf828SJakub Kicinskiclass SpecOperation(SpecElement):
3293aacf828SJakub Kicinski    """Netlink Operation
3303aacf828SJakub Kicinski
3313aacf828SJakub Kicinski    Information about a single Netlink operation.
3323aacf828SJakub Kicinski
3333aacf828SJakub Kicinski    Attributes:
3343aacf828SJakub Kicinski        value           numerical ID when serialized, None if req/rsp values differ
3353aacf828SJakub Kicinski
3363aacf828SJakub Kicinski        req_value       numerical ID when serialized, user -> kernel
3373aacf828SJakub Kicinski        rsp_value       numerical ID when serialized, user <- kernel
338*3e51f2cbSJakub Kicinski        modes           supported operation modes (do, dump, event etc.)
3393aacf828SJakub Kicinski        is_call         bool, whether the operation is a call
3403aacf828SJakub Kicinski        is_async        bool, whether the operation is a notification
3413aacf828SJakub Kicinski        is_resv         bool, whether the operation does not exist (it's just a reserved ID)
3423aacf828SJakub Kicinski        attr_set        attribute set name
343f036d936SDonald Hunter        fixed_header    string, optional name of fixed header struct
3443aacf828SJakub Kicinski
3453aacf828SJakub Kicinski        yaml            raw spec as loaded from the spec file
3463aacf828SJakub Kicinski    """
3473aacf828SJakub Kicinski    def __init__(self, family, yaml, req_value, rsp_value):
3483aacf828SJakub Kicinski        super().__init__(family, yaml)
3493aacf828SJakub Kicinski
3503aacf828SJakub Kicinski        self.value = req_value if req_value == rsp_value else None
3513aacf828SJakub Kicinski        self.req_value = req_value
3523aacf828SJakub Kicinski        self.rsp_value = rsp_value
3533aacf828SJakub Kicinski
354*3e51f2cbSJakub Kicinski        self.modes = yaml.keys() & {'do', 'dump', 'event', 'notify'}
3553aacf828SJakub Kicinski        self.is_call = 'do' in yaml or 'dump' in yaml
3563aacf828SJakub Kicinski        self.is_async = 'notify' in yaml or 'event' in yaml
3573aacf828SJakub Kicinski        self.is_resv = not self.is_async and not self.is_call
358f036d936SDonald Hunter        self.fixed_header = self.yaml.get('fixed-header', family.fixed_header)
3593aacf828SJakub Kicinski
3603aacf828SJakub Kicinski        # Added by resolve:
3613aacf828SJakub Kicinski        self.attr_set = None
3623aacf828SJakub Kicinski        delattr(self, "attr_set")
3633aacf828SJakub Kicinski
3643aacf828SJakub Kicinski    def resolve(self):
3653aacf828SJakub Kicinski        self.resolve_up(super())
3663aacf828SJakub Kicinski
3673aacf828SJakub Kicinski        if 'attribute-set' in self.yaml:
3683aacf828SJakub Kicinski            attr_set_name = self.yaml['attribute-set']
3693aacf828SJakub Kicinski        elif 'notify' in self.yaml:
3703aacf828SJakub Kicinski            msg = self.family.msgs[self.yaml['notify']]
3713aacf828SJakub Kicinski            attr_set_name = msg['attribute-set']
3723aacf828SJakub Kicinski        elif self.is_resv:
3733aacf828SJakub Kicinski            attr_set_name = ''
3743aacf828SJakub Kicinski        else:
3753aacf828SJakub Kicinski            raise Exception(f"Can't resolve attribute set for op '{self.name}'")
3763aacf828SJakub Kicinski        if attr_set_name:
3773aacf828SJakub Kicinski            self.attr_set = self.family.attr_sets[attr_set_name]
3783aacf828SJakub Kicinski
3793aacf828SJakub Kicinski
38088901b96SDonald Hunterclass SpecMcastGroup(SpecElement):
38188901b96SDonald Hunter    """Netlink Multicast Group
38288901b96SDonald Hunter
38388901b96SDonald Hunter    Information about a multicast group.
38488901b96SDonald Hunter
38588901b96SDonald Hunter    Value is only used for classic netlink families that use the
38688901b96SDonald Hunter    netlink-raw schema. Genetlink families use dynamic ID allocation
38788901b96SDonald Hunter    where the ids of multicast groups get resolved at runtime. Value
38888901b96SDonald Hunter    will be None for genetlink families.
38988901b96SDonald Hunter
39088901b96SDonald Hunter    Attributes:
39188901b96SDonald Hunter        name      name of the mulitcast group
39288901b96SDonald Hunter        value     integer id of this multicast group for netlink-raw or None
39388901b96SDonald Hunter        yaml      raw spec as loaded from the spec file
39488901b96SDonald Hunter    """
39588901b96SDonald Hunter    def __init__(self, family, yaml):
39688901b96SDonald Hunter        super().__init__(family, yaml)
39788901b96SDonald Hunter        self.value = self.yaml.get('value')
39888901b96SDonald Hunter
39988901b96SDonald Hunter
4003aacf828SJakub Kicinskiclass SpecFamily(SpecElement):
4013aacf828SJakub Kicinski    """ Netlink Family Spec class.
4023aacf828SJakub Kicinski
4033aacf828SJakub Kicinski    Netlink family information loaded from a spec (e.g. in YAML).
4043aacf828SJakub Kicinski    Takes care of unfolding implicit information which can be skipped
4053aacf828SJakub Kicinski    in the spec itself for brevity.
4063aacf828SJakub Kicinski
4073aacf828SJakub Kicinski    The class can be used like a dictionary to access the raw spec
4083aacf828SJakub Kicinski    elements but that's usually a bad idea.
4093aacf828SJakub Kicinski
4103aacf828SJakub Kicinski    Attributes:
4113aacf828SJakub Kicinski        proto     protocol type (e.g. genetlink)
412ff6db4b5SJakub Kicinski        msg_id_model   enum-model for operations (unified, directional etc.)
413cfab77c0SJakub Kicinski        license   spec license (loaded from an SPDX tag on the spec)
4143aacf828SJakub Kicinski
4153aacf828SJakub Kicinski        attr_sets  dict of attribute sets
4163aacf828SJakub Kicinski        msgs       dict of all messages (index by name)
4171769e2beSDonald Hunter        sub_msgs   dict of all sub messages (index by name)
4183aacf828SJakub Kicinski        ops        dict of all valid requests / responses
419ced15688SJakub Kicinski        ntfs       dict of all async events
4206517a60bSJakub Kicinski        consts     dict of all constants/enums
421f036d936SDonald Hunter        fixed_header  string, optional name of family default fixed header struct
42288901b96SDonald Hunter        mcast_groups  dict of all multicast groups (index by name)
423ba980f8dSJakub Kicinski        kernel_family   dict of kernel family attributes
4243aacf828SJakub Kicinski    """
425008bcd68SJakub Kicinski    def __init__(self, spec_path, schema_path=None, exclude_ops=None):
4263aacf828SJakub Kicinski        with open(spec_path, "r") as stream:
427cfab77c0SJakub Kicinski            prefix = '# SPDX-License-Identifier: '
428cfab77c0SJakub Kicinski            first = stream.readline().strip()
429cfab77c0SJakub Kicinski            if not first.startswith(prefix):
430cfab77c0SJakub Kicinski                raise Exception('SPDX license tag required in the spec')
431cfab77c0SJakub Kicinski            self.license = first[len(prefix):]
432cfab77c0SJakub Kicinski
433cfab77c0SJakub Kicinski            stream.seek(0)
4343aacf828SJakub Kicinski            spec = yaml.safe_load(stream)
4353aacf828SJakub Kicinski
4363aacf828SJakub Kicinski        self._resolution_list = []
4373aacf828SJakub Kicinski
4383aacf828SJakub Kicinski        super().__init__(self, spec)
4393aacf828SJakub Kicinski
440008bcd68SJakub Kicinski        self._exclude_ops = exclude_ops if exclude_ops else []
441008bcd68SJakub Kicinski
4423aacf828SJakub Kicinski        self.proto = self.yaml.get('protocol', 'genetlink')
443ff6db4b5SJakub Kicinski        self.msg_id_model = self.yaml['operations'].get('enum-model', 'unified')
4443aacf828SJakub Kicinski
4453aacf828SJakub Kicinski        if schema_path is None:
4463aacf828SJakub Kicinski            schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml'
4473aacf828SJakub Kicinski        if schema_path:
4485c6674f6SJakub Kicinski            global jsonschema
4495c6674f6SJakub Kicinski
4503aacf828SJakub Kicinski            with open(schema_path, "r") as stream:
4513aacf828SJakub Kicinski                schema = yaml.safe_load(stream)
4523aacf828SJakub Kicinski
4535c6674f6SJakub Kicinski            if jsonschema is None:
4545c6674f6SJakub Kicinski                jsonschema = importlib.import_module("jsonschema")
4555c6674f6SJakub Kicinski
4563aacf828SJakub Kicinski            jsonschema.validate(self.yaml, schema)
4573aacf828SJakub Kicinski
4583aacf828SJakub Kicinski        self.attr_sets = collections.OrderedDict()
4591769e2beSDonald Hunter        self.sub_msgs = collections.OrderedDict()
4603aacf828SJakub Kicinski        self.msgs = collections.OrderedDict()
4613aacf828SJakub Kicinski        self.req_by_value = collections.OrderedDict()
4623aacf828SJakub Kicinski        self.rsp_by_value = collections.OrderedDict()
4633aacf828SJakub Kicinski        self.ops = collections.OrderedDict()
464ced15688SJakub Kicinski        self.ntfs = collections.OrderedDict()
4656517a60bSJakub Kicinski        self.consts = collections.OrderedDict()
46688901b96SDonald Hunter        self.mcast_groups = collections.OrderedDict()
467ba980f8dSJakub Kicinski        self.kernel_family = collections.OrderedDict(self.yaml.get('kernel-family', {}))
4683aacf828SJakub Kicinski
4693aacf828SJakub Kicinski        last_exception = None
4703aacf828SJakub Kicinski        while len(self._resolution_list) > 0:
4713aacf828SJakub Kicinski            resolved = []
4723aacf828SJakub Kicinski            unresolved = self._resolution_list
4733aacf828SJakub Kicinski            self._resolution_list = []
4743aacf828SJakub Kicinski
4753aacf828SJakub Kicinski            for elem in unresolved:
4763aacf828SJakub Kicinski                try:
4773aacf828SJakub Kicinski                    elem.resolve()
4783aacf828SJakub Kicinski                except (KeyError, AttributeError) as e:
4793aacf828SJakub Kicinski                    self._resolution_list.append(elem)
4803aacf828SJakub Kicinski                    last_exception = e
4813aacf828SJakub Kicinski                    continue
4823aacf828SJakub Kicinski
4833aacf828SJakub Kicinski                resolved.append(elem)
4843aacf828SJakub Kicinski
4853aacf828SJakub Kicinski            if len(resolved) == 0:
486b9d3a3e4SJakub Kicinski                raise last_exception
4873aacf828SJakub Kicinski
4886517a60bSJakub Kicinski    def new_enum(self, elem):
4896517a60bSJakub Kicinski        return SpecEnumSet(self, elem)
4906517a60bSJakub Kicinski
4913aacf828SJakub Kicinski    def new_attr_set(self, elem):
4923aacf828SJakub Kicinski        return SpecAttrSet(self, elem)
4933aacf828SJakub Kicinski
494bec0b7a2SDonald Hunter    def new_struct(self, elem):
495bec0b7a2SDonald Hunter        return SpecStruct(self, elem)
496bec0b7a2SDonald Hunter
4971769e2beSDonald Hunter    def new_sub_message(self, elem):
4981769e2beSDonald Hunter        return SpecSubMessage(self, elem);
4991769e2beSDonald Hunter
5003aacf828SJakub Kicinski    def new_operation(self, elem, req_val, rsp_val):
5013aacf828SJakub Kicinski        return SpecOperation(self, elem, req_val, rsp_val)
5023aacf828SJakub Kicinski
50388901b96SDonald Hunter    def new_mcast_group(self, elem):
50488901b96SDonald Hunter        return SpecMcastGroup(self, elem)
50588901b96SDonald Hunter
5063aacf828SJakub Kicinski    def add_unresolved(self, elem):
5073aacf828SJakub Kicinski        self._resolution_list.append(elem)
5083aacf828SJakub Kicinski
5093aacf828SJakub Kicinski    def _dictify_ops_unified(self):
510f036d936SDonald Hunter        self.fixed_header = self.yaml['operations'].get('fixed-header')
511ad4fafcdSJakub Kicinski        val = 1
5123aacf828SJakub Kicinski        for elem in self.yaml['operations']['list']:
5133aacf828SJakub Kicinski            if 'value' in elem:
5143aacf828SJakub Kicinski                val = elem['value']
5153aacf828SJakub Kicinski
5163aacf828SJakub Kicinski            op = self.new_operation(elem, val, val)
5173aacf828SJakub Kicinski            val += 1
5183aacf828SJakub Kicinski
5193aacf828SJakub Kicinski            self.msgs[op.name] = op
5203aacf828SJakub Kicinski
5213aacf828SJakub Kicinski    def _dictify_ops_directional(self):
522f036d936SDonald Hunter        self.fixed_header = self.yaml['operations'].get('fixed-header')
523ad4fafcdSJakub Kicinski        req_val = rsp_val = 1
5243aacf828SJakub Kicinski        for elem in self.yaml['operations']['list']:
5256da3424fSJakub Kicinski            if 'notify' in elem or 'event' in elem:
5263aacf828SJakub Kicinski                if 'value' in elem:
5273aacf828SJakub Kicinski                    rsp_val = elem['value']
5283aacf828SJakub Kicinski                req_val_next = req_val
5293aacf828SJakub Kicinski                rsp_val_next = rsp_val + 1
5303aacf828SJakub Kicinski                req_val = None
5313aacf828SJakub Kicinski            elif 'do' in elem or 'dump' in elem:
5323aacf828SJakub Kicinski                mode = elem['do'] if 'do' in elem else elem['dump']
5333aacf828SJakub Kicinski
5343aacf828SJakub Kicinski                v = mode.get('request', {}).get('value', None)
5353aacf828SJakub Kicinski                if v:
5363aacf828SJakub Kicinski                    req_val = v
5373aacf828SJakub Kicinski                v = mode.get('reply', {}).get('value', None)
5383aacf828SJakub Kicinski                if v:
5393aacf828SJakub Kicinski                    rsp_val = v
5403aacf828SJakub Kicinski
5413aacf828SJakub Kicinski                rsp_inc = 1 if 'reply' in mode else 0
5423aacf828SJakub Kicinski                req_val_next = req_val + 1
5433aacf828SJakub Kicinski                rsp_val_next = rsp_val + rsp_inc
5443aacf828SJakub Kicinski            else:
5453aacf828SJakub Kicinski                raise Exception("Can't parse directional ops")
5463aacf828SJakub Kicinski
5479858bfc2SJakub Kicinski            if req_val == req_val_next:
5489858bfc2SJakub Kicinski                req_val = None
5499858bfc2SJakub Kicinski            if rsp_val == rsp_val_next:
5509858bfc2SJakub Kicinski                rsp_val = None
551008bcd68SJakub Kicinski
552008bcd68SJakub Kicinski            skip = False
553008bcd68SJakub Kicinski            for exclude in self._exclude_ops:
554008bcd68SJakub Kicinski                skip |= bool(exclude.match(elem['name']))
555008bcd68SJakub Kicinski            if not skip:
5563aacf828SJakub Kicinski                op = self.new_operation(elem, req_val, rsp_val)
557008bcd68SJakub Kicinski
5583aacf828SJakub Kicinski            req_val = req_val_next
5593aacf828SJakub Kicinski            rsp_val = rsp_val_next
5603aacf828SJakub Kicinski
5613aacf828SJakub Kicinski            self.msgs[op.name] = op
5623aacf828SJakub Kicinski
563f3d07b02SStanislav Fomichev    def find_operation(self, name):
564f3d07b02SStanislav Fomichev      """
565f3d07b02SStanislav Fomichev      For a given operation name, find and return operation spec.
566f3d07b02SStanislav Fomichev      """
567f3d07b02SStanislav Fomichev      for op in self.yaml['operations']['list']:
568f3d07b02SStanislav Fomichev        if name == op['name']:
569f3d07b02SStanislav Fomichev          return op
570f3d07b02SStanislav Fomichev      return None
571f3d07b02SStanislav Fomichev
5723aacf828SJakub Kicinski    def resolve(self):
5733aacf828SJakub Kicinski        self.resolve_up(super())
5743aacf828SJakub Kicinski
575054abb51SJakub Kicinski        definitions = self.yaml.get('definitions', [])
576054abb51SJakub Kicinski        for elem in definitions:
5776517a60bSJakub Kicinski            if elem['type'] == 'enum' or elem['type'] == 'flags':
5786517a60bSJakub Kicinski                self.consts[elem['name']] = self.new_enum(elem)
579bec0b7a2SDonald Hunter            elif elem['type'] == 'struct':
580bec0b7a2SDonald Hunter                self.consts[elem['name']] = self.new_struct(elem)
5816517a60bSJakub Kicinski            else:
5826517a60bSJakub Kicinski                self.consts[elem['name']] = elem
5836517a60bSJakub Kicinski
5843aacf828SJakub Kicinski        for elem in self.yaml['attribute-sets']:
5853aacf828SJakub Kicinski            attr_set = self.new_attr_set(elem)
5863aacf828SJakub Kicinski            self.attr_sets[elem['name']] = attr_set
5873aacf828SJakub Kicinski
5881769e2beSDonald Hunter        for elem in self.yaml.get('sub-messages', []):
5891769e2beSDonald Hunter            sub_message = self.new_sub_message(elem)
5901769e2beSDonald Hunter            self.sub_msgs[sub_message.name] = sub_message
5911769e2beSDonald Hunter
592ff6db4b5SJakub Kicinski        if self.msg_id_model == 'unified':
5933aacf828SJakub Kicinski            self._dictify_ops_unified()
594ff6db4b5SJakub Kicinski        elif self.msg_id_model == 'directional':
5953aacf828SJakub Kicinski            self._dictify_ops_directional()
5963aacf828SJakub Kicinski
5973aacf828SJakub Kicinski        for op in self.msgs.values():
5983aacf828SJakub Kicinski            if op.req_value is not None:
5993aacf828SJakub Kicinski                self.req_by_value[op.req_value] = op
6003aacf828SJakub Kicinski            if op.rsp_value is not None:
6013aacf828SJakub Kicinski                self.rsp_by_value[op.rsp_value] = op
6023aacf828SJakub Kicinski            if not op.is_async and 'attribute-set' in op:
6033aacf828SJakub Kicinski                self.ops[op.name] = op
604ced15688SJakub Kicinski            elif op.is_async:
605ced15688SJakub Kicinski                self.ntfs[op.name] = op
60688901b96SDonald Hunter
60788901b96SDonald Hunter        mcgs = self.yaml.get('mcast-groups')
60888901b96SDonald Hunter        if mcgs:
60988901b96SDonald Hunter            for elem in mcgs['list']:
61088901b96SDonald Hunter                mcg = self.new_mcast_group(elem)
61188901b96SDonald Hunter                self.mcast_groups[elem['name']] = mcg
612