Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to allow for regeneration of pysnmp base MIBs #8

Merged
merged 6 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pysmi/codegen/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,15 @@ def str2int(self, s):

@staticmethod
def trans_opers(symbol):
"""Convert an ASN.1 name to a "Pythonized" symbol.

The resulting symbol must be usable as a name in Python code. As such,
dashes are replaced with underscores, and reserved Python keywords are
prefixed so as to make them usable as regular identifier names.

Calling this function on a symbol that is already "Pythonized", has no
effect, but should be avoided in general anyway.
"""
if iskeyword(symbol):
symbol = RESERVED_KEYWORDS_PREFIX + symbol
return symbol.replace("-", "_")
Expand Down
76 changes: 45 additions & 31 deletions pysmi/codegen/intermediate.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ class IntermediateCodeGen(AbstractCodeGen):

def __init__(self):
self._rows = set()
self._cols = {} # k, v = name, datatype
self._seenSyms = set()
self._importMap = {}
self._out = {} # k, v = name, generated code
Expand Down Expand Up @@ -349,8 +348,10 @@ def gen_notification_group(self, data):
if objects:
outDict["objects"] = [
{
"module": self._importMap.get(obj, self.moduleName[0]),
"object": self.trans_opers(obj),
"module": self._importMap.get(
self.trans_opers(obj), self.moduleName[0]
),
"object": obj,
}
for obj in objects
]
Expand Down Expand Up @@ -383,8 +384,10 @@ def gen_notification_type(self, data):
if objects:
outDict["objects"] = [
{
"module": self._importMap.get(obj, self.moduleName[0]),
"object": self.trans_opers(obj),
"module": self._importMap.get(
self.trans_opers(obj), self.moduleName[0]
),
"object": obj,
}
for obj in objects
]
Expand Down Expand Up @@ -414,8 +417,10 @@ def gen_object_group(self, data):
if objects:
outDict["objects"] = [
{
"module": self._importMap.get(obj, self.moduleName[0]),
"object": self.trans_opers(obj),
"module": self._importMap.get(
self.trans_opers(obj), self.moduleName[0]
),
"object": obj,
}
for obj in objects
]
Expand Down Expand Up @@ -490,8 +495,11 @@ def gen_object_type(self, data):
nodetype = syntax[0] == "Bits" and "scalar" or syntax[0] # Bits hack
# If this object type is used as a column, but it also has a
# "SEQUENCE OF" syntax, then it is really a table and not a column.
#
# Note that _symtable_cols contains original (non-Pythonized)
# symbol names! Thus, check with 'name' rather than 'pysmiName'.
isColumn = (
pysmiName in self.symbolTable[self.moduleName[0]]["_symtable_cols"]
name in self.symbolTable[self.moduleName[0]]["_symtable_cols"]
and syntax[1]
)
nodetype = isColumn and "column" or nodetype
Expand Down Expand Up @@ -547,8 +555,10 @@ def gen_trap_type(self, data):
if variables:
outDict["objects"] = [
{
"module": self._importMap.get(obj, self.moduleName[0]),
"object": self.trans_opers(obj),
"module": self._importMap.get(
self.trans_opers(obj), self.moduleName[0]
),
"object": obj,
}
for obj in variables
]
Expand Down Expand Up @@ -636,8 +646,7 @@ def gen_compliances(self, data):
for complianceModule in data[0]:
name = complianceModule[0] or self.moduleName[0]
compliances += [
{"object": self.trans_opers(compl), "module": name}
for compl in complianceModule[1]
{"object": compl, "module": name} for compl in complianceModule[1]
]

return compliances
Expand Down Expand Up @@ -712,14 +721,17 @@ def gen_def_val(self, data, objname=None):
else:
# oid
if defvalType[0][0] == "ObjectIdentifier" and (
defval in self.symbolTable[self.moduleName[0]]
or defval in self._importMap
self.trans_opers(defval) in self.symbolTable[self.moduleName[0]]
or self.trans_opers(defval) in self._importMap
):
module = self._importMap.get(defval, self.moduleName[0])
pysmiDefval = self.trans_opers(defval)
module = self._importMap.get(pysmiDefval, self.moduleName[0])

try:
val = str(
self.gen_numeric_oid(self.symbolTable[module][defval]["oid"])
self.gen_numeric_oid(
self.symbolTable[module][pysmiDefval]["oid"]
)
)

outDict.update(value=val, format="oid")
Expand All @@ -734,15 +746,22 @@ def gen_def_val(self, data, objname=None):
elif defvalType[0][0] in ("Integer32", "Integer") and isinstance(
defvalType[1], list
):
# For enumerations, the ASN.1 DEFVAL statements contain names,
# whereas the code generation template expects integer values
# (represented as strings).
nameToValueMap = dict(defvalType[1])

# buggy MIB: DEFVAL { { ... } }
if isinstance(defval, list):
defval = [dv for dv in defval if dv in dict(defvalType[1])]
defval = [dv for dv in defval if dv in nameToValueMap]
if defval:
outDict.update(value=defval[0], format="enum")
outDict.update(
value=str(nameToValueMap[defval[0]]), format="enum"
)

# good MIB: DEFVAL { ... }
elif defval in dict(defvalType[1]):
outDict.update(value=defval, format="enum")
elif defval in nameToValueMap:
outDict.update(value=str(nameToValueMap[defval]), format="enum")

elif defvalType[0][0] == "Bits":
defvalBits = []
Expand Down Expand Up @@ -825,7 +844,9 @@ def gen_fake_sym_dict(fakeSym, fakeOidSuffix, idxType):
fakeOidSuffix -= 1

index = OrderedDict()
index["module"] = self._importMap.get(idxName, self.moduleName[0])
index["module"] = self._importMap.get(
self.trans_opers(idxName), self.moduleName[0]
)
index["object"] = idxName
index["implied"] = isImplied
idxStrlist.append(index)
Expand Down Expand Up @@ -881,13 +902,9 @@ def gen_oid(self, data):

return ".".join([str(x) for x in self.gen_numeric_oid(out)]), parent

# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic,PyUnusedLocal
def gen_objects(self, data):
if data[0]:
return [
self.trans_opers(obj) for obj in data[0]
] # XXX self.transOpers or not??
return []
return data[0]

# noinspection PyMethodMayBeStatic,PyUnusedLocal
def gen_time(self, data):
Expand Down Expand Up @@ -942,10 +959,8 @@ def gen_row(self, data):
or self.gen_simple_syntax(data)
)

# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic,PyUnusedLocal
def gen_sequence(self, data):
cols = data[0]
self._cols.update(cols)
return "", ""

def gen_simple_syntax(self, data):
Expand Down Expand Up @@ -1055,7 +1070,6 @@ def gen_code(self, ast, symbolTable, **kwargs):
)
self.symbolTable = symbolTable
self._rows.clear()
self._cols.clear()
self._seenSyms.clear()
self._importMap.clear()
self._out.clear()
Expand Down
6 changes: 3 additions & 3 deletions pysmi/codegen/symtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ class SymtableCodeGen(AbstractCodeGen):
}

def __init__(self):
self._rows = set()
self._cols = {} # k, v = name, datatype
self._rows = set() # symbols
self._cols = {} # k, v = name, datatype [name is *not* a Pythonized symbol!]
self._sequenceTypes = set()
self._exports = set()
self._postponedSyms = {} # k, v = symbol, (parents, properties)
Expand Down Expand Up @@ -394,7 +394,7 @@ def gen_compliances(self, data, classmode=False):
def gen_conceptual_table(self, data, classmode=False):
row = data[0]
if row[0] and row[0][0]:
self._rows.add(self.trans_opers(row[0][0]))
self._rows.add(row[0][0]) # (already a Pythonized symbol)
return ("MibTable", ""), ""

# noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
Expand Down
37 changes: 17 additions & 20 deletions pysmi/codegen/templates/pysnmp/mib-definitions.j2
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ Managed Objects Instances.

{% block asn1_constraints_imports scoped %}
(ConstraintsIntersection,
ConstraintsUnion,
SingleValueConstraint,
ValueRangeConstraint,
ValueSizeConstraint,
ConstraintsUnion) = mibBuilder.importSymbols(
ValueSizeConstraint) = mibBuilder.importSymbols(
"ASN1-REFINEMENT",
"ConstraintsIntersection",
"ConstraintsUnion",
"SingleValueConstraint",
"ValueRangeConstraint",
"ValueSizeConstraint",
"ConstraintsUnion")
"ValueSizeConstraint")
{% endblock -%}

{% block smi_imports scoped %}
Expand Down Expand Up @@ -73,7 +73,8 @@ Managed Objects Instances.
{{ definition['oid'] }}
)
{% if 'revisions' in definition %}
{{ symbol }}.setRevisions(
if mibBuilder.loadTexts:
{{ symbol }}.setRevisions(
{% for revision in definition.get('revisions', ()) %}
{% if loop.first and loop.last %}
({{ revision['revision']|pythonstr }},)
Expand All @@ -85,17 +86,19 @@ Managed Objects Instances.
{{ revision['revision']|pythonstr }},
{% endif %}
{% endfor %}
)
)
{% endif %}
{% if 'lastupdated' in definition %}
{{ symbol }}.setLastUpdated({{ definition['lastupdated']|pythonstr }})
if mibBuilder.loadTexts:
{{ symbol }}.setLastUpdated({{ definition['lastupdated']|pythonstr }})
{% endif %}
{% if 'organization' in definition %}
if mibBuilder.loadTexts:
{{ symbol }}.setOrganization({{ definition['organization']|pythonstr }})
{% endif %}
{% if 'contactinfo' in definition %}
{{ symbol }}.setContactInfo({{ definition['contactinfo']|pythonstr }})
if mibBuilder.loadTexts:
{{ symbol }}.setContactInfo({{ definition['contactinfo']|pythonstr }})
{% endif %}
{% if 'description' in definition %}
if mibBuilder.loadTexts:
Expand Down Expand Up @@ -124,7 +127,7 @@ if mibBuilder.loadTexts:
)
)
namedValues = NamedValues(
{% for name, iden in spec['enumeration'].items()|sort %}
{% for name, iden in spec['enumeration'].items()|sort(attribute='1') %}
{% if loop.first and loop.last %}
("{{ name}}", {{ iden }})
{% elif loop.first %}
Expand Down Expand Up @@ -153,7 +156,7 @@ if mibBuilder.loadTexts:

{% macro bits(namedbits) %}
namedValues = NamedValues(
{% for name, iden in namedbits.items()|sort %}
{% for name, iden in namedbits.items()|sort(attribute='1') %}
{% if loop.first and loop.last %}
("{{ name}}", {{ iden }})
{% elif loop.first %}
Expand Down Expand Up @@ -187,12 +190,8 @@ if mibBuilder.loadTexts:
{% elif definition['default']['default']['format'] == 'string' %}
{# TODO: pyasn1 does not like defaulted strings #}
defaultValue = OctetString({{ definition['default']['default']['value']|pythonstr }})
{% elif definition['default']['default']['format'] == 'oid' %}
{% elif definition['default']['default']['format'] in ('oid', 'enum') %}
defaultValue = {{ definition['default']['default']['value'] }}
{% elif definition['default']['default']['format'] == 'enum'
and 'constraints' in definition['syntax'] %}
{# TODO: pyasn1 does not like default enum #}
defaultValue = {{ definition['syntax']['constraints']['enumeration'][definition['default']['default']['value']] }}
{% elif definition['default']['default']['format'] == 'bits' %}
{# TODO: pyasn1 does not like default named bits #}
defaultBinValue = "{{ definition['default']['default']['value']['bits'].values()|bitstring }}"
Expand Down Expand Up @@ -279,9 +278,7 @@ class _{{ symbol|capfirst }}_Type({{ definition['syntax']['type'] }}):
{{ bits(definition['syntax']['bits']) }}
{% endif %}

{% if 'constraints' in definition['syntax'] or 'bits' in definition['syntax'] %}
_{{ symbol|capfirst }}_Type.__name__ = "{{ definition['syntax']['type'] }}"
{% endif %}
{% else %}
_{{ symbol|capfirst }}_Type = {{ definition['syntax']['type'] }}
{% endif %}
Expand Down Expand Up @@ -367,7 +364,7 @@ if mibBuilder.loadTexts:
and 'augmention' in definition %}
{{ definition['augmention']['object'] }}.registerAugmentions(
("{{ mib['meta']['module'] }}",
"{{ symbol }}")
"{{ definition['name'] }}")
)
{{ symbol }}.setIndexNames(*{{ definition['augmention']['object'] }}.getIndexNames())
{% endfor %}
Expand Down Expand Up @@ -517,9 +514,9 @@ if mibBuilder.loadTexts:
{{ symbol }} = ModuleCompliance(
{{ definition['oid'] }}
)
{% if 'objects' in definition %}
{% if 'modulecompliance' in definition %}
{{ symbol }}.setObjects(
{% for obj in definition['objects'] %}
{% for obj in definition['modulecompliance'] %}
{% if loop.first and loop.last %}
("{{ obj['module'] }}", "{{ obj['object'] }}")
{% elif loop.first %}
Expand Down
3 changes: 2 additions & 1 deletion tests/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"test_objecttype_smiv1_pysnmp",
"test_objecttype_smiv2_pysnmp",
"test_smiv1_smiv2_pysnmp",
"test_traptype_smiv2_pysnmp",
"test_syntaxname_smiv2_pysnmp",
"test_traptype_smiv1_pysnmp",
"test_typedeclaration_smiv1_pysnmp",
"test_typedeclaration_smiv2_pysnmp",
"test_valuedeclaration_smiv2_pysnmp",
Expand Down
Loading
Loading