Implement dynamic attribute space selection and decoding to the ynl library. Encode support is not yet implemented. Support for dynamic selectors at a different nest level from the key attribute is not yet supported. Signed-off-by: Donald Hunter <donald.hunter@xxxxxxxxx> --- tools/net/ynl/lib/nlspec.py | 27 +++++++++++++++++++++++++++ tools/net/ynl/lib/ynl.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/tools/net/ynl/lib/nlspec.py b/tools/net/ynl/lib/nlspec.py index 92889298b197..c32c85ddf8fb 100644 --- a/tools/net/ynl/lib/nlspec.py +++ b/tools/net/ynl/lib/nlspec.py @@ -142,6 +142,29 @@ class SpecEnumSet(SpecElement): mask += e.user_value(as_flags) return mask +class SpecDynAttr(SpecElement): + """ Single Dynamic Netlink atttribute type + + Represents a choice of dynamic attribute type within an attr space. + + Attributes: + value attribute value to match against dynamic type selector + struct_name string, name of struct definition + sub_type string, name of sub type + len integer, optional byte length of binary types + display_hint string, hint to help choose format specifier + when displaying the value + """ + def __init__(self, family, parent, yaml): + super().__init__(family, yaml) + + self.value = yaml.get('value') + self.struct_name = yaml.get('struct') + self.sub_type = yaml.get('sub-type') + self.byte_order = yaml.get('byte-order') + self.len = yaml.get('len') + self.display_hint = yaml.get('display-hint') + class SpecAttr(SpecElement): """ Single Netlink atttribute type @@ -173,9 +196,13 @@ class SpecAttr(SpecElement): self.byte_order = yaml.get('byte-order') self.len = yaml.get('len') self.display_hint = yaml.get('display-hint') + self.dynamic_types = {} self.is_auto_scalar = self.type == "sint" or self.type == "uint" + if 'selector' in yaml: + for item in yaml.get('selector').get('list', []): + self.dynamic_types[item['value']] = SpecDynAttr(family, self, item) class SpecAttrSet(SpecElement): """ Netlink Attribute Set class. diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 92995bca14e1..5ce01ce37573 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -549,6 +549,36 @@ class YnlFamily(SpecFamily): else: rsp[name] = [decoded] + def _resolve_selector(self, attr_spec, vals): + if 'selector' in attr_spec: + selector = attr_spec['selector'] + key = selector['attribute'] + if key in vals: + value = vals[key] + if value in attr_spec.dynamic_types: + spec = attr_spec.dynamic_types[value] + return spec + else: + raise Exception(f"No entry for {key}={value} in selector for '{attr_spec['name']}'") + else: + raise Exception(f"There is no value for {key} to use in selector for '{attr_spec['name']}'") + else: + raise Exception("type=dynamic requires a selector in '{attr_spec['name']}'") + + def _decode_dynamic(self, attr, attr_spec, rsp): + dyn_spec = self._resolve_selector(attr_spec, rsp) + if dyn_spec['type'] == 'binary': + decoded = self._decode_binary(attr, dyn_spec) + elif dyn_spec['type'] == 'nest': + attr_space = dyn_spec['nested-attributes'] + if attr_space in self.attr_sets: + decoded = self._decode(NlAttrs(attr.raw), attr_space) + else: + raise Exception(f"Unknown attribute-set '{attr_space}'") + else: + raise Exception(f"Unknown type '{spec['type']}' for value '{value}'") + 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"] == 'dynamic': + decoded = self._decode_dynamic(attr, attr_spec, rsp) else: if not self.process_unknown: raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') -- 2.42.0