@@ -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.
@@ -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"]}')
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@gmail.com> --- tools/net/ynl/lib/nlspec.py | 27 +++++++++++++++++++++++++++ tools/net/ynl/lib/ynl.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+)