xref: /openbmc/linux/tools/net/ynl/lib/nlspec.py (revision 008bcd68)
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):
1473aacf828SJakub Kicinski    """ Single Netlink atttribute type
1483aacf828SJakub Kicinski
1493aacf828SJakub Kicinski    Represents a single attribute type within an attr space.
1503aacf828SJakub Kicinski
1513aacf828SJakub Kicinski    Attributes:
1523aacf828SJakub Kicinski        value         numerical ID when serialized
1533aacf828SJakub Kicinski        attr_set      Attribute Set containing this attr
154b423c3c8SDonald Hunter        is_multi      bool, attr may repeat multiple times
15526071913SDonald Hunter        struct_name   string, name of struct definition
156b423c3c8SDonald Hunter        sub_type      string, name of sub type
1573aacf828SJakub Kicinski    """
1583aacf828SJakub Kicinski    def __init__(self, family, attr_set, yaml, value):
1593aacf828SJakub Kicinski        super().__init__(family, yaml)
1603aacf828SJakub Kicinski
1613aacf828SJakub Kicinski        self.value = value
1623aacf828SJakub Kicinski        self.attr_set = attr_set
1633aacf828SJakub Kicinski        self.is_multi = yaml.get('multi-attr', False)
16426071913SDonald Hunter        self.struct_name = yaml.get('struct')
165b423c3c8SDonald Hunter        self.sub_type = yaml.get('sub-type')
1669f7cc57fSStanislav Fomichev        self.byte_order = yaml.get('byte-order')
1673aacf828SJakub Kicinski
1683aacf828SJakub Kicinski
1693aacf828SJakub Kicinskiclass SpecAttrSet(SpecElement):
1703aacf828SJakub Kicinski    """ Netlink Attribute Set class.
1713aacf828SJakub Kicinski
1723aacf828SJakub Kicinski    Represents a ID space of attributes within Netlink.
1733aacf828SJakub Kicinski
1743aacf828SJakub Kicinski    Note that unlike other elements, which expose contents of the raw spec
1753aacf828SJakub Kicinski    via the dictionary interface Attribute Set exposes attributes by name.
1763aacf828SJakub Kicinski
1773aacf828SJakub Kicinski    Attributes:
1783aacf828SJakub Kicinski        attrs      ordered dict of all attributes (indexed by name)
1793aacf828SJakub Kicinski        attrs_by_val  ordered dict of all attributes (indexed by value)
1803aacf828SJakub Kicinski        subset_of  parent set if this is a subset, otherwise None
1813aacf828SJakub Kicinski    """
1823aacf828SJakub Kicinski    def __init__(self, family, yaml):
1833aacf828SJakub Kicinski        super().__init__(family, yaml)
1843aacf828SJakub Kicinski
1853aacf828SJakub Kicinski        self.subset_of = self.yaml.get('subset-of', None)
1863aacf828SJakub Kicinski
1873aacf828SJakub Kicinski        self.attrs = collections.OrderedDict()
1883aacf828SJakub Kicinski        self.attrs_by_val = collections.OrderedDict()
1893aacf828SJakub Kicinski
1907cf93538SJakub Kicinski        if self.subset_of is None:
191ad4fafcdSJakub Kicinski            val = 1
1923aacf828SJakub Kicinski            for elem in self.yaml['attributes']:
1933aacf828SJakub Kicinski                if 'value' in elem:
1943aacf828SJakub Kicinski                    val = elem['value']
1953aacf828SJakub Kicinski
1963aacf828SJakub Kicinski                attr = self.new_attr(elem, val)
1973aacf828SJakub Kicinski                self.attrs[attr.name] = attr
1983aacf828SJakub Kicinski                self.attrs_by_val[attr.value] = attr
1993aacf828SJakub Kicinski                val += 1
2007cf93538SJakub Kicinski        else:
2017cf93538SJakub Kicinski            real_set = family.attr_sets[self.subset_of]
2027cf93538SJakub Kicinski            for elem in self.yaml['attributes']:
2037cf93538SJakub Kicinski                attr = real_set[elem['name']]
2047cf93538SJakub Kicinski                self.attrs[attr.name] = attr
2057cf93538SJakub Kicinski                self.attrs_by_val[attr.value] = attr
2063aacf828SJakub Kicinski
2073aacf828SJakub Kicinski    def new_attr(self, elem, value):
2083aacf828SJakub Kicinski        return SpecAttr(self.family, self, elem, value)
2093aacf828SJakub Kicinski
2103aacf828SJakub Kicinski    def __getitem__(self, key):
2113aacf828SJakub Kicinski        return self.attrs[key]
2123aacf828SJakub Kicinski
2133aacf828SJakub Kicinski    def __contains__(self, key):
2143aacf828SJakub Kicinski        return key in self.attrs
2153aacf828SJakub Kicinski
2163aacf828SJakub Kicinski    def __iter__(self):
2173aacf828SJakub Kicinski        yield from self.attrs
2183aacf828SJakub Kicinski
2193aacf828SJakub Kicinski    def items(self):
2203aacf828SJakub Kicinski        return self.attrs.items()
2213aacf828SJakub Kicinski
2223aacf828SJakub Kicinski
223bec0b7a2SDonald Hunterclass SpecStructMember(SpecElement):
224bec0b7a2SDonald Hunter    """Struct member attribute
225bec0b7a2SDonald Hunter
226bec0b7a2SDonald Hunter    Represents a single struct member attribute.
227bec0b7a2SDonald Hunter
228bec0b7a2SDonald Hunter    Attributes:
229bec0b7a2SDonald Hunter        type        string, type of the member attribute
230bddd2e56SDonald Hunter        byte_order  string or None for native byte order
231313a7a80SDonald Hunter        enum        string, name of the enum definition
232bec0b7a2SDonald Hunter    """
233bec0b7a2SDonald Hunter    def __init__(self, family, yaml):
234bec0b7a2SDonald Hunter        super().__init__(family, yaml)
235bec0b7a2SDonald Hunter        self.type = yaml['type']
236bddd2e56SDonald Hunter        self.byte_order = yaml.get('byte-order')
237313a7a80SDonald Hunter        self.enum = yaml.get('enum')
238bec0b7a2SDonald Hunter
239bec0b7a2SDonald Hunter
240bec0b7a2SDonald Hunterclass SpecStruct(SpecElement):
241bec0b7a2SDonald Hunter    """Netlink struct type
242bec0b7a2SDonald Hunter
243bec0b7a2SDonald Hunter    Represents a C struct definition.
244bec0b7a2SDonald Hunter
245bec0b7a2SDonald Hunter    Attributes:
246bec0b7a2SDonald Hunter        members   ordered list of struct members
247bec0b7a2SDonald Hunter    """
248bec0b7a2SDonald Hunter    def __init__(self, family, yaml):
249bec0b7a2SDonald Hunter        super().__init__(family, yaml)
250bec0b7a2SDonald Hunter
251bec0b7a2SDonald Hunter        self.members = []
252bec0b7a2SDonald Hunter        for member in yaml.get('members', []):
253bec0b7a2SDonald Hunter            self.members.append(self.new_member(family, member))
254bec0b7a2SDonald Hunter
255bec0b7a2SDonald Hunter    def new_member(self, family, elem):
256bec0b7a2SDonald Hunter        return SpecStructMember(family, elem)
257bec0b7a2SDonald Hunter
258bec0b7a2SDonald Hunter    def __iter__(self):
259bec0b7a2SDonald Hunter        yield from self.members
260bec0b7a2SDonald Hunter
261bec0b7a2SDonald Hunter    def items(self):
262bec0b7a2SDonald Hunter        return self.members.items()
263bec0b7a2SDonald Hunter
264bec0b7a2SDonald Hunter
2653aacf828SJakub Kicinskiclass SpecOperation(SpecElement):
2663aacf828SJakub Kicinski    """Netlink Operation
2673aacf828SJakub Kicinski
2683aacf828SJakub Kicinski    Information about a single Netlink operation.
2693aacf828SJakub Kicinski
2703aacf828SJakub Kicinski    Attributes:
2713aacf828SJakub Kicinski        value           numerical ID when serialized, None if req/rsp values differ
2723aacf828SJakub Kicinski
2733aacf828SJakub Kicinski        req_value       numerical ID when serialized, user -> kernel
2743aacf828SJakub Kicinski        rsp_value       numerical ID when serialized, user <- kernel
2753aacf828SJakub Kicinski        is_call         bool, whether the operation is a call
2763aacf828SJakub Kicinski        is_async        bool, whether the operation is a notification
2773aacf828SJakub Kicinski        is_resv         bool, whether the operation does not exist (it's just a reserved ID)
2783aacf828SJakub Kicinski        attr_set        attribute set name
279f036d936SDonald Hunter        fixed_header    string, optional name of fixed header struct
2803aacf828SJakub Kicinski
2813aacf828SJakub Kicinski        yaml            raw spec as loaded from the spec file
2823aacf828SJakub Kicinski    """
2833aacf828SJakub Kicinski    def __init__(self, family, yaml, req_value, rsp_value):
2843aacf828SJakub Kicinski        super().__init__(family, yaml)
2853aacf828SJakub Kicinski
2863aacf828SJakub Kicinski        self.value = req_value if req_value == rsp_value else None
2873aacf828SJakub Kicinski        self.req_value = req_value
2883aacf828SJakub Kicinski        self.rsp_value = rsp_value
2893aacf828SJakub Kicinski
2903aacf828SJakub Kicinski        self.is_call = 'do' in yaml or 'dump' in yaml
2913aacf828SJakub Kicinski        self.is_async = 'notify' in yaml or 'event' in yaml
2923aacf828SJakub Kicinski        self.is_resv = not self.is_async and not self.is_call
293f036d936SDonald Hunter        self.fixed_header = self.yaml.get('fixed-header', family.fixed_header)
2943aacf828SJakub Kicinski
2953aacf828SJakub Kicinski        # Added by resolve:
2963aacf828SJakub Kicinski        self.attr_set = None
2973aacf828SJakub Kicinski        delattr(self, "attr_set")
2983aacf828SJakub Kicinski
2993aacf828SJakub Kicinski    def resolve(self):
3003aacf828SJakub Kicinski        self.resolve_up(super())
3013aacf828SJakub Kicinski
3023aacf828SJakub Kicinski        if 'attribute-set' in self.yaml:
3033aacf828SJakub Kicinski            attr_set_name = self.yaml['attribute-set']
3043aacf828SJakub Kicinski        elif 'notify' in self.yaml:
3053aacf828SJakub Kicinski            msg = self.family.msgs[self.yaml['notify']]
3063aacf828SJakub Kicinski            attr_set_name = msg['attribute-set']
3073aacf828SJakub Kicinski        elif self.is_resv:
3083aacf828SJakub Kicinski            attr_set_name = ''
3093aacf828SJakub Kicinski        else:
3103aacf828SJakub Kicinski            raise Exception(f"Can't resolve attribute set for op '{self.name}'")
3113aacf828SJakub Kicinski        if attr_set_name:
3123aacf828SJakub Kicinski            self.attr_set = self.family.attr_sets[attr_set_name]
3133aacf828SJakub Kicinski
3143aacf828SJakub Kicinski
3153aacf828SJakub Kicinskiclass SpecFamily(SpecElement):
3163aacf828SJakub Kicinski    """ Netlink Family Spec class.
3173aacf828SJakub Kicinski
3183aacf828SJakub Kicinski    Netlink family information loaded from a spec (e.g. in YAML).
3193aacf828SJakub Kicinski    Takes care of unfolding implicit information which can be skipped
3203aacf828SJakub Kicinski    in the spec itself for brevity.
3213aacf828SJakub Kicinski
3223aacf828SJakub Kicinski    The class can be used like a dictionary to access the raw spec
3233aacf828SJakub Kicinski    elements but that's usually a bad idea.
3243aacf828SJakub Kicinski
3253aacf828SJakub Kicinski    Attributes:
3263aacf828SJakub Kicinski        proto     protocol type (e.g. genetlink)
327ff6db4b5SJakub Kicinski        msg_id_model   enum-model for operations (unified, directional etc.)
328cfab77c0SJakub Kicinski        license   spec license (loaded from an SPDX tag on the spec)
3293aacf828SJakub Kicinski
3303aacf828SJakub Kicinski        attr_sets  dict of attribute sets
3313aacf828SJakub Kicinski        msgs       dict of all messages (index by name)
3323aacf828SJakub Kicinski        ops        dict of all valid requests / responses
333ced15688SJakub Kicinski        ntfs       dict of all async events
3346517a60bSJakub Kicinski        consts     dict of all constants/enums
335f036d936SDonald Hunter        fixed_header  string, optional name of family default fixed header struct
3363aacf828SJakub Kicinski    """
337*008bcd68SJakub Kicinski    def __init__(self, spec_path, schema_path=None, exclude_ops=None):
3383aacf828SJakub Kicinski        with open(spec_path, "r") as stream:
339cfab77c0SJakub Kicinski            prefix = '# SPDX-License-Identifier: '
340cfab77c0SJakub Kicinski            first = stream.readline().strip()
341cfab77c0SJakub Kicinski            if not first.startswith(prefix):
342cfab77c0SJakub Kicinski                raise Exception('SPDX license tag required in the spec')
343cfab77c0SJakub Kicinski            self.license = first[len(prefix):]
344cfab77c0SJakub Kicinski
345cfab77c0SJakub Kicinski            stream.seek(0)
3463aacf828SJakub Kicinski            spec = yaml.safe_load(stream)
3473aacf828SJakub Kicinski
3483aacf828SJakub Kicinski        self._resolution_list = []
3493aacf828SJakub Kicinski
3503aacf828SJakub Kicinski        super().__init__(self, spec)
3513aacf828SJakub Kicinski
352*008bcd68SJakub Kicinski        self._exclude_ops = exclude_ops if exclude_ops else []
353*008bcd68SJakub Kicinski
3543aacf828SJakub Kicinski        self.proto = self.yaml.get('protocol', 'genetlink')
355ff6db4b5SJakub Kicinski        self.msg_id_model = self.yaml['operations'].get('enum-model', 'unified')
3563aacf828SJakub Kicinski
3573aacf828SJakub Kicinski        if schema_path is None:
3583aacf828SJakub Kicinski            schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml'
3593aacf828SJakub Kicinski        if schema_path:
3605c6674f6SJakub Kicinski            global jsonschema
3615c6674f6SJakub Kicinski
3623aacf828SJakub Kicinski            with open(schema_path, "r") as stream:
3633aacf828SJakub Kicinski                schema = yaml.safe_load(stream)
3643aacf828SJakub Kicinski
3655c6674f6SJakub Kicinski            if jsonschema is None:
3665c6674f6SJakub Kicinski                jsonschema = importlib.import_module("jsonschema")
3675c6674f6SJakub Kicinski
3683aacf828SJakub Kicinski            jsonschema.validate(self.yaml, schema)
3693aacf828SJakub Kicinski
3703aacf828SJakub Kicinski        self.attr_sets = collections.OrderedDict()
3713aacf828SJakub Kicinski        self.msgs = collections.OrderedDict()
3723aacf828SJakub Kicinski        self.req_by_value = collections.OrderedDict()
3733aacf828SJakub Kicinski        self.rsp_by_value = collections.OrderedDict()
3743aacf828SJakub Kicinski        self.ops = collections.OrderedDict()
375ced15688SJakub Kicinski        self.ntfs = collections.OrderedDict()
3766517a60bSJakub Kicinski        self.consts = collections.OrderedDict()
3773aacf828SJakub Kicinski
3783aacf828SJakub Kicinski        last_exception = None
3793aacf828SJakub Kicinski        while len(self._resolution_list) > 0:
3803aacf828SJakub Kicinski            resolved = []
3813aacf828SJakub Kicinski            unresolved = self._resolution_list
3823aacf828SJakub Kicinski            self._resolution_list = []
3833aacf828SJakub Kicinski
3843aacf828SJakub Kicinski            for elem in unresolved:
3853aacf828SJakub Kicinski                try:
3863aacf828SJakub Kicinski                    elem.resolve()
3873aacf828SJakub Kicinski                except (KeyError, AttributeError) as e:
3883aacf828SJakub Kicinski                    self._resolution_list.append(elem)
3893aacf828SJakub Kicinski                    last_exception = e
3903aacf828SJakub Kicinski                    continue
3913aacf828SJakub Kicinski
3923aacf828SJakub Kicinski                resolved.append(elem)
3933aacf828SJakub Kicinski
3943aacf828SJakub Kicinski            if len(resolved) == 0:
395b9d3a3e4SJakub Kicinski                raise last_exception
3963aacf828SJakub Kicinski
3976517a60bSJakub Kicinski    def new_enum(self, elem):
3986517a60bSJakub Kicinski        return SpecEnumSet(self, elem)
3996517a60bSJakub Kicinski
4003aacf828SJakub Kicinski    def new_attr_set(self, elem):
4013aacf828SJakub Kicinski        return SpecAttrSet(self, elem)
4023aacf828SJakub Kicinski
403bec0b7a2SDonald Hunter    def new_struct(self, elem):
404bec0b7a2SDonald Hunter        return SpecStruct(self, elem)
405bec0b7a2SDonald Hunter
4063aacf828SJakub Kicinski    def new_operation(self, elem, req_val, rsp_val):
4073aacf828SJakub Kicinski        return SpecOperation(self, elem, req_val, rsp_val)
4083aacf828SJakub Kicinski
4093aacf828SJakub Kicinski    def add_unresolved(self, elem):
4103aacf828SJakub Kicinski        self._resolution_list.append(elem)
4113aacf828SJakub Kicinski
4123aacf828SJakub Kicinski    def _dictify_ops_unified(self):
413f036d936SDonald Hunter        self.fixed_header = self.yaml['operations'].get('fixed-header')
414ad4fafcdSJakub Kicinski        val = 1
4153aacf828SJakub Kicinski        for elem in self.yaml['operations']['list']:
4163aacf828SJakub Kicinski            if 'value' in elem:
4173aacf828SJakub Kicinski                val = elem['value']
4183aacf828SJakub Kicinski
4193aacf828SJakub Kicinski            op = self.new_operation(elem, val, val)
4203aacf828SJakub Kicinski            val += 1
4213aacf828SJakub Kicinski
4223aacf828SJakub Kicinski            self.msgs[op.name] = op
4233aacf828SJakub Kicinski
4243aacf828SJakub Kicinski    def _dictify_ops_directional(self):
425f036d936SDonald Hunter        self.fixed_header = self.yaml['operations'].get('fixed-header')
426ad4fafcdSJakub Kicinski        req_val = rsp_val = 1
4273aacf828SJakub Kicinski        for elem in self.yaml['operations']['list']:
4286da3424fSJakub Kicinski            if 'notify' in elem or 'event' in elem:
4293aacf828SJakub Kicinski                if 'value' in elem:
4303aacf828SJakub Kicinski                    rsp_val = elem['value']
4313aacf828SJakub Kicinski                req_val_next = req_val
4323aacf828SJakub Kicinski                rsp_val_next = rsp_val + 1
4333aacf828SJakub Kicinski                req_val = None
4343aacf828SJakub Kicinski            elif 'do' in elem or 'dump' in elem:
4353aacf828SJakub Kicinski                mode = elem['do'] if 'do' in elem else elem['dump']
4363aacf828SJakub Kicinski
4373aacf828SJakub Kicinski                v = mode.get('request', {}).get('value', None)
4383aacf828SJakub Kicinski                if v:
4393aacf828SJakub Kicinski                    req_val = v
4403aacf828SJakub Kicinski                v = mode.get('reply', {}).get('value', None)
4413aacf828SJakub Kicinski                if v:
4423aacf828SJakub Kicinski                    rsp_val = v
4433aacf828SJakub Kicinski
4443aacf828SJakub Kicinski                rsp_inc = 1 if 'reply' in mode else 0
4453aacf828SJakub Kicinski                req_val_next = req_val + 1
4463aacf828SJakub Kicinski                rsp_val_next = rsp_val + rsp_inc
4473aacf828SJakub Kicinski            else:
4483aacf828SJakub Kicinski                raise Exception("Can't parse directional ops")
4493aacf828SJakub Kicinski
4509858bfc2SJakub Kicinski            if req_val == req_val_next:
4519858bfc2SJakub Kicinski                req_val = None
4529858bfc2SJakub Kicinski            if rsp_val == rsp_val_next:
4539858bfc2SJakub Kicinski                rsp_val = None
454*008bcd68SJakub Kicinski
455*008bcd68SJakub Kicinski            skip = False
456*008bcd68SJakub Kicinski            for exclude in self._exclude_ops:
457*008bcd68SJakub Kicinski                skip |= bool(exclude.match(elem['name']))
458*008bcd68SJakub Kicinski            if not skip:
4593aacf828SJakub Kicinski                op = self.new_operation(elem, req_val, rsp_val)
460*008bcd68SJakub Kicinski
4613aacf828SJakub Kicinski            req_val = req_val_next
4623aacf828SJakub Kicinski            rsp_val = rsp_val_next
4633aacf828SJakub Kicinski
4643aacf828SJakub Kicinski            self.msgs[op.name] = op
4653aacf828SJakub Kicinski
466f3d07b02SStanislav Fomichev    def find_operation(self, name):
467f3d07b02SStanislav Fomichev      """
468f3d07b02SStanislav Fomichev      For a given operation name, find and return operation spec.
469f3d07b02SStanislav Fomichev      """
470f3d07b02SStanislav Fomichev      for op in self.yaml['operations']['list']:
471f3d07b02SStanislav Fomichev        if name == op['name']:
472f3d07b02SStanislav Fomichev          return op
473f3d07b02SStanislav Fomichev      return None
474f3d07b02SStanislav Fomichev
4753aacf828SJakub Kicinski    def resolve(self):
4763aacf828SJakub Kicinski        self.resolve_up(super())
4773aacf828SJakub Kicinski
478054abb51SJakub Kicinski        definitions = self.yaml.get('definitions', [])
479054abb51SJakub Kicinski        for elem in definitions:
4806517a60bSJakub Kicinski            if elem['type'] == 'enum' or elem['type'] == 'flags':
4816517a60bSJakub Kicinski                self.consts[elem['name']] = self.new_enum(elem)
482bec0b7a2SDonald Hunter            elif elem['type'] == 'struct':
483bec0b7a2SDonald Hunter                self.consts[elem['name']] = self.new_struct(elem)
4846517a60bSJakub Kicinski            else:
4856517a60bSJakub Kicinski                self.consts[elem['name']] = elem
4866517a60bSJakub Kicinski
4873aacf828SJakub Kicinski        for elem in self.yaml['attribute-sets']:
4883aacf828SJakub Kicinski            attr_set = self.new_attr_set(elem)
4893aacf828SJakub Kicinski            self.attr_sets[elem['name']] = attr_set
4903aacf828SJakub Kicinski
491ff6db4b5SJakub Kicinski        if self.msg_id_model == 'unified':
4923aacf828SJakub Kicinski            self._dictify_ops_unified()
493ff6db4b5SJakub Kicinski        elif self.msg_id_model == 'directional':
4943aacf828SJakub Kicinski            self._dictify_ops_directional()
4953aacf828SJakub Kicinski
4963aacf828SJakub Kicinski        for op in self.msgs.values():
4973aacf828SJakub Kicinski            if op.req_value is not None:
4983aacf828SJakub Kicinski                self.req_by_value[op.req_value] = op
4993aacf828SJakub Kicinski            if op.rsp_value is not None:
5003aacf828SJakub Kicinski                self.rsp_by_value[op.rsp_value] = op
5013aacf828SJakub Kicinski            if not op.is_async and 'attribute-set' in op:
5023aacf828SJakub Kicinski                self.ops[op.name] = op
503ced15688SJakub Kicinski            elif op.is_async:
504ced15688SJakub Kicinski                self.ntfs[op.name] = op
505