Skip to content

Commit

Permalink
tools: ynl: move the enum classes to shared code
Browse files Browse the repository at this point in the history
Move bulk of the EnumSet and EnumEntry code to shared
code for reuse by cli.

Signed-off-by: Jakub Kicinski <[email protected]>
  • Loading branch information
kuba-moo committed Mar 9, 2023
1 parent 649c15c commit 6517a60
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 89 deletions.
7 changes: 4 additions & 3 deletions tools/net/ynl/lib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause

from .nlspec import SpecAttr, SpecAttrSet, SpecFamily, SpecOperation
from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \
SpecFamily, SpecOperation
from .ynl import YnlFamily

__all__ = ["SpecAttr", "SpecAttrSet", "SpecFamily", "SpecOperation",
"YnlFamily"]
__all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet",
"SpecFamily", "SpecOperation", "YnlFamily"]
96 changes: 96 additions & 0 deletions tools/net/ynl/lib/nlspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,91 @@ def resolve(self):
pass


class SpecEnumEntry(SpecElement):
""" Entry within an enum declared in the Netlink spec.
Attributes:
doc documentation string
enum_set back reference to the enum
value numerical value of this enum (use accessors in most situations!)
Methods:
raw_value raw value, i.e. the id in the enum, unlike user value which is a mask for flags
user_value user value, same as raw value for enums, for flags it's the mask
"""
def __init__(self, enum_set, yaml, prev, value_start):
if isinstance(yaml, str):
yaml = {'name': yaml}
super().__init__(enum_set.family, yaml)

self.doc = yaml.get('doc', '')
self.enum_set = enum_set

if 'value' in yaml:
self.value = yaml['value']
elif prev:
self.value = prev.value + 1
else:
self.value = value_start

def has_doc(self):
return bool(self.doc)

def raw_value(self):
return self.value

def user_value(self):
if self.enum_set['type'] == 'flags':
return 1 << self.value
else:
return self.value


class SpecEnumSet(SpecElement):
""" Enum type
Represents an enumeration (list of numerical constants)
as declared in the "definitions" section of the spec.
Attributes:
type enum or flags
entries entries by name
Methods:
get_mask for flags compute the mask of all defined values
"""
def __init__(self, family, yaml):
super().__init__(family, yaml)

self.type = yaml['type']

prev_entry = None
value_start = self.yaml.get('value-start', 0)
self.entries = dict()
for entry in self.yaml['entries']:
e = self.new_entry(entry, prev_entry, value_start)
self.entries[e.name] = e
prev_entry = e

def new_entry(self, entry, prev_entry, value_start):
return SpecEnumEntry(self, entry, prev_entry, value_start)

def has_doc(self):
if 'doc' in self.yaml:
return True
for entry in self.entries.values():
if entry.has_doc():
return True
return False

def get_mask(self):
mask = 0
idx = self.yaml.get('value-start', 0)
for _ in self.entries.values():
mask |= 1 << idx
idx += 1
return mask


class SpecAttr(SpecElement):
""" Single Netlink atttribute type
Expand Down Expand Up @@ -193,6 +278,7 @@ class SpecFamily(SpecElement):
msgs dict of all messages (index by name)
msgs_by_value dict of all messages (indexed by name)
ops dict of all valid requests / responses
consts dict of all constants/enums
"""
def __init__(self, spec_path, schema_path=None):
with open(spec_path, "r") as stream:
Expand Down Expand Up @@ -222,6 +308,7 @@ def __init__(self, spec_path, schema_path=None):
self.req_by_value = collections.OrderedDict()
self.rsp_by_value = collections.OrderedDict()
self.ops = collections.OrderedDict()
self.consts = collections.OrderedDict()

last_exception = None
while len(self._resolution_list) > 0:
Expand All @@ -242,6 +329,9 @@ def __init__(self, spec_path, schema_path=None):
if len(resolved) == 0:
raise last_exception

def new_enum(self, elem):
return SpecEnumSet(self, elem)

def new_attr_set(self, elem):
return SpecAttrSet(self, elem)

Expand Down Expand Up @@ -296,6 +386,12 @@ def _dictify_ops_directional(self):
def resolve(self):
self.resolve_up(super())

for elem in self.yaml['definitions']:
if elem['type'] == 'enum' or elem['type'] == 'flags':
self.consts[elem['name']] = self.new_enum(elem)
else:
self.consts[elem['name']] = elem

for elem in self.yaml['attribute-sets']:
attr_set = self.new_attr_set(elem)
self.attr_sets[elem['name']] = attr_set
Expand Down
107 changes: 21 additions & 86 deletions tools/net/ynl/ynl-gen-c.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import os
import yaml

from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation
from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry


def c_upper(name):
Expand Down Expand Up @@ -567,97 +567,37 @@ def set_inherited(self, new_inherited):
self.inherited = [c_lower(x) for x in sorted(self._inherited)]


class EnumEntry:
class EnumEntry(SpecEnumEntry):
def __init__(self, enum_set, yaml, prev, value_start):
if isinstance(yaml, str):
self.name = yaml
yaml = {}
self.doc = ''
else:
self.name = yaml['name']
self.doc = yaml.get('doc', '')

self.yaml = yaml
self.enum_set = enum_set
self.c_name = c_upper(enum_set.value_pfx + self.name)

if 'value' in yaml:
self.value = yaml['value']
if prev:
self.value_change = (self.value != prev.value + 1)
elif prev:
self.value_change = False
self.value = prev.value + 1
super().__init__(enum_set, yaml, prev, value_start)

if prev:
self.value_change = (self.value != prev.value + 1)
else:
self.value = value_start
self.value_change = (self.value != 0)

self.value_change = self.value_change or self.enum_set['type'] == 'flags'

def __getitem__(self, key):
return self.yaml[key]

def __contains__(self, key):
return key in self.yaml

def has_doc(self):
return bool(self.doc)
# Added by resolve:
self.c_name = None
delattr(self, "c_name")

# raw value, i.e. the id in the enum, unlike user value which is a mask for flags
def raw_value(self):
return self.value
def resolve(self):
self.resolve_up(super())

# user value, same as raw value for enums, for flags it's the mask
def user_value(self):
if self.enum_set['type'] == 'flags':
return 1 << self.value
else:
return self.value
self.c_name = c_upper(self.enum_set.value_pfx + self.name)


class EnumSet:
class EnumSet(SpecEnumSet):
def __init__(self, family, yaml):
self.yaml = yaml
self.family = family

self.render_name = c_lower(family.name + '-' + yaml['name'])
self.enum_name = 'enum ' + self.render_name

self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-")

self.type = yaml['type']

prev_entry = None
value_start = self.yaml.get('value-start', 0)
self.entries = {}
self.entry_list = []
for entry in self.yaml['entries']:
e = EnumEntry(self, entry, prev_entry, value_start)
self.entries[e.name] = e
self.entry_list.append(e)
prev_entry = e

def __getitem__(self, key):
return self.yaml[key]

def __contains__(self, key):
return key in self.yaml

def has_doc(self):
if 'doc' in self.yaml:
return True
for entry in self.entry_list:
if entry.has_doc():
return True
return False
super().__init__(family, yaml)

def get_mask(self):
mask = 0
idx = self.yaml.get('value-start', 0)
for _ in self.entry_list:
mask |= 1 << idx
idx += 1
return mask
def new_entry(self, entry, prev_entry, value_start):
return EnumEntry(self, entry, prev_entry, value_start)


class AttrSet(SpecAttrSet):
Expand Down Expand Up @@ -792,8 +732,6 @@ def resolve(self):

self.mcgrps = self.yaml.get('mcast-groups', {'list': []})

self.consts = dict()

self.hooks = dict()
for when in ['pre', 'post']:
self.hooks[when] = dict()
Expand All @@ -820,6 +758,9 @@ def resolve(self):
if self.kernel_policy == 'global':
self._load_global_policy()

def new_enum(self, elem):
return EnumSet(self, elem)

def new_attr_set(self, elem):
return AttrSet(self, elem)

Expand All @@ -837,12 +778,6 @@ def _mock_up_events(self):
}

def _dictify(self):
for elem in self.yaml['definitions']:
if elem['type'] == 'enum' or elem['type'] == 'flags':
self.consts[elem['name']] = EnumSet(self, elem)
else:
self.consts[elem['name']] = elem

ntf = []
for msg in self.msgs.values():
if 'notify' in msg:
Expand Down Expand Up @@ -1980,15 +1915,15 @@ def render_uapi(family, cw):
if 'doc' in enum:
doc = ' - ' + enum['doc']
cw.write_doc_line(enum.enum_name + doc)
for entry in enum.entry_list:
for entry in enum.entries.values():
if entry.has_doc():
doc = '@' + entry.c_name + ': ' + entry['doc']
cw.write_doc_line(doc)
cw.p(' */')

uapi_enum_start(family, cw, const, 'name')
name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
for entry in enum.entry_list:
for entry in enum.entries.values():
suffix = ','
if entry.value_change:
suffix = f" = {entry.user_value()}" + suffix
Expand Down

0 comments on commit 6517a60

Please sign in to comment.