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