You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1453 lines
48 KiB
1453 lines
48 KiB
#
|
|
# This file is part of pysmi software.
|
|
#
|
|
# Copyright (c) 2015-2019, Ilya Etingof <etingof@gmail.com>
|
|
# License: http://snmplabs.com/pysmi/license.html
|
|
#
|
|
import os
|
|
import sys
|
|
import ply.yacc as yacc
|
|
from pysmi.lexer.smi import lexerFactory
|
|
from pysmi.parser.base import AbstractParser
|
|
from pysmi import error
|
|
from pysmi import debug
|
|
|
|
YACC_VERSION = [int(x) for x in yacc.__version__.split('.')]
|
|
|
|
|
|
# noinspection PyMethodMayBeStatic,PyIncorrectDocstring
|
|
class SmiV2Parser(AbstractParser):
|
|
defaultLexer = lexerFactory()
|
|
|
|
def __init__(self, startSym='mibFile', tempdir=''):
|
|
if tempdir:
|
|
tempdir = os.path.join(tempdir, startSym)
|
|
try:
|
|
os.makedirs(tempdir)
|
|
except OSError:
|
|
if sys.exc_info()[1].errno != 17:
|
|
raise error.PySmiError('Failed to create cache directory %s: %s' % (tempdir, sys.exc_info()[1]))
|
|
|
|
self.lexer = self.defaultLexer(tempdir=tempdir)
|
|
|
|
# tokens are required for parser
|
|
self.tokens = self.lexer.tokens
|
|
|
|
if YACC_VERSION < [3, 0]:
|
|
self.parser = yacc.yacc(module=self,
|
|
start=startSym,
|
|
write_tables=bool(tempdir),
|
|
debug=False,
|
|
outputdir=tempdir)
|
|
else:
|
|
if debug.logger & debug.flagParser:
|
|
logger = debug.logger.getCurrentLogger()
|
|
else:
|
|
logger = yacc.NullLogger()
|
|
|
|
if debug.logger & debug.flagGrammar:
|
|
debuglogger = debug.logger.getCurrentLogger()
|
|
else:
|
|
debuglogger = None
|
|
|
|
self.parser = yacc.yacc(module=self,
|
|
start=startSym,
|
|
write_tables=bool(tempdir),
|
|
debug=False,
|
|
outputdir=tempdir,
|
|
debuglog=debuglogger,
|
|
errorlog=logger)
|
|
|
|
def reset(self):
|
|
# Ply requires lexer reinitialization for (at least) resetting lineno
|
|
self.lexer.reset()
|
|
|
|
def parse(self, data, **kwargs):
|
|
debug.logger & debug.flagParser and debug.logger(
|
|
'source MIB size is %s characters, first 50 characters are "%s..."' % (len(data), data[:50]))
|
|
|
|
ast = self.parser.parse(data, lexer=self.lexer.lexer)
|
|
|
|
self.reset()
|
|
|
|
if ast and ast[0] == 'mibFile' and ast[1]: # mibfile is not empty
|
|
return ast[1]
|
|
else:
|
|
return []
|
|
|
|
#
|
|
# SMIv2 grammar follows
|
|
#
|
|
|
|
def p_mibFile(self, p):
|
|
"""mibFile : modules
|
|
| empty"""
|
|
p[0] = ('mibFile', p[1])
|
|
|
|
def p_modules(self, p):
|
|
"""modules : modules module
|
|
| module"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = p[1] + [p[2]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_module(self, p):
|
|
"""module : moduleName moduleOid DEFINITIONS COLON_COLON_EQUAL BEGIN exportsClause linkagePart declarationPart END"""
|
|
p[0] = (p[1], # name
|
|
p[2], # oid
|
|
p[7], # linkage (imports)
|
|
p[8]) # declaration
|
|
|
|
def p_moduleOid(self, p):
|
|
"""moduleOid : '{' objectIdentifier '}'
|
|
| empty"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[2]
|
|
|
|
def p_linkagePart(self, p):
|
|
"""linkagePart : linkageClause
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_linkageClause(self, p):
|
|
"""linkageClause : IMPORTS importPart ';'"""
|
|
p[0] = p[2]
|
|
|
|
def p_exportsClause(self, p):
|
|
"""exportsClause : EXPORTS
|
|
| empty"""
|
|
|
|
def p_importPart(self, p):
|
|
"""importPart : imports
|
|
| empty"""
|
|
# libsmi: TODO: ``IMPORTS ;'' allowed? refer ASN.1!
|
|
if p[1]:
|
|
importDict = {}
|
|
for imp in p[1]: # don't do just dict() because moduleNames may be repeated
|
|
fromModule, symbols = imp
|
|
if fromModule in importDict:
|
|
importDict[fromModule] += symbols
|
|
else:
|
|
importDict[fromModule] = symbols
|
|
|
|
p[0] = importDict
|
|
|
|
def p_imports(self, p):
|
|
"""imports : imports import
|
|
| import"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = p[1] + [p[2]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_import(self, p):
|
|
"""import : importIdentifiers FROM moduleName"""
|
|
# libsmi: TODO: multiple clauses with same moduleName allowed?
|
|
# I guess so. refer ASN.1!
|
|
p[0] = (p[3], # moduleName
|
|
p[1]) # ids
|
|
|
|
def p_importIdentifiers(self, p):
|
|
"""importIdentifiers : importIdentifiers ',' importIdentifier
|
|
| importIdentifier"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
# Note that some named types must not be imported, REF:RFC1902,590
|
|
def p_importIdentifier(self, p):
|
|
"""importIdentifier : LOWERCASE_IDENTIFIER
|
|
| UPPERCASE_IDENTIFIER
|
|
| importedKeyword"""
|
|
p[0] = p[1]
|
|
|
|
def p_importedKeyword(self, p):
|
|
"""importedKeyword : importedSMIKeyword
|
|
| BITS
|
|
| INTEGER32
|
|
| IPADDRESS
|
|
| MANDATORY_GROUPS
|
|
| MODULE_COMPLIANCE
|
|
| MODULE_IDENTITY
|
|
| OBJECT_GROUP
|
|
| OBJECT_IDENTITY
|
|
| OBJECT_TYPE
|
|
| OPAQUE
|
|
| TEXTUAL_CONVENTION
|
|
| TIMETICKS
|
|
| UNSIGNED32"""
|
|
p[0] = p[1]
|
|
|
|
def p_importedSMIKeyword(self, p):
|
|
"""importedSMIKeyword : AGENT_CAPABILITIES
|
|
| COUNTER32
|
|
| COUNTER64
|
|
| GAUGE32
|
|
| NOTIFICATION_GROUP
|
|
| NOTIFICATION_TYPE
|
|
| TRAP_TYPE"""
|
|
p[0] = p[1]
|
|
|
|
def p_moduleName(self, p):
|
|
"""moduleName : UPPERCASE_IDENTIFIER"""
|
|
p[0] = p[1]
|
|
|
|
def p_declarationPart(self, p):
|
|
"""declarationPart : declarations
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_declarations(self, p):
|
|
"""declarations : declarations declaration
|
|
| declaration"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = p[1] + [p[2]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_declaration(self, p):
|
|
"""declaration : typeDeclaration
|
|
| valueDeclaration
|
|
| objectIdentityClause
|
|
| objectTypeClause
|
|
| trapTypeClause
|
|
| notificationTypeClause
|
|
| moduleIdentityClause
|
|
| moduleComplianceClause
|
|
| objectGroupClause
|
|
| notificationGroupClause
|
|
| agentCapabilitiesClause
|
|
| macroClause"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_macroClause(self, p):
|
|
"""macroClause : macroName MACRO END"""
|
|
|
|
def p_macroName(self, p):
|
|
"""macroName : MODULE_IDENTITY
|
|
| OBJECT_TYPE
|
|
| TRAP_TYPE
|
|
| NOTIFICATION_TYPE
|
|
| OBJECT_IDENTITY
|
|
| TEXTUAL_CONVENTION
|
|
| OBJECT_GROUP
|
|
| NOTIFICATION_GROUP
|
|
| MODULE_COMPLIANCE
|
|
| AGENT_CAPABILITIES"""
|
|
|
|
def p_choiceClause(self, p):
|
|
"""choiceClause : CHOICE """
|
|
|
|
# libsmi: The only ASN.1 value declarations are for OIDs, REF:RFC1902,491.
|
|
def p_fuzzy_lowercase_identifier(self, p):
|
|
"""fuzzy_lowercase_identifier : LOWERCASE_IDENTIFIER
|
|
| UPPERCASE_IDENTIFIER"""
|
|
p[0] = p[1]
|
|
|
|
def p_valueDeclaration(self, p):
|
|
"""valueDeclaration : fuzzy_lowercase_identifier OBJECT IDENTIFIER COLON_COLON_EQUAL '{' objectIdentifier '}'"""
|
|
p[0] = ('valueDeclaration', p[1], # id
|
|
p[6]) # objectIdentifier
|
|
|
|
def p_typeDeclaration(self, p):
|
|
"""typeDeclaration : typeName COLON_COLON_EQUAL typeDeclarationRHS"""
|
|
p[0] = ('typeDeclaration', p[1], # name
|
|
p[3]) # declarationRHS
|
|
|
|
def p_typeName(self, p):
|
|
"""typeName : UPPERCASE_IDENTIFIER
|
|
| typeSMI"""
|
|
p[0] = p[1]
|
|
|
|
def p_typeSMI(self, p):
|
|
"""typeSMI : typeSMIandSPPI
|
|
| typeSMIonly"""
|
|
p[0] = p[1]
|
|
|
|
def p_typeSMIandSPPI(self, p):
|
|
"""typeSMIandSPPI : IPADDRESS
|
|
| TIMETICKS
|
|
| OPAQUE
|
|
| INTEGER32
|
|
| UNSIGNED32"""
|
|
p[0] = p[1]
|
|
|
|
def p_typeSMIonly(self, p):
|
|
"""typeSMIonly : COUNTER32
|
|
| GAUGE32
|
|
| COUNTER64"""
|
|
p[0] = p[1]
|
|
|
|
def p_typeDeclarationRHS(self, p):
|
|
"""typeDeclarationRHS : Syntax
|
|
| TEXTUAL_CONVENTION DisplayPart STATUS Status DESCRIPTION Text ReferPart SYNTAX Syntax
|
|
| choiceClause"""
|
|
if p[1]:
|
|
if p[1] == 'TEXTUAL-CONVENTION':
|
|
p[0] = ('typeDeclarationRHS', p[2], # display
|
|
p[4], # status
|
|
(p[5], p[6]), # description
|
|
p[7], # reference
|
|
p[9]) # syntax
|
|
else:
|
|
p[0] = ('typeDeclarationRHS', p[1])
|
|
# ignore the choiceClause
|
|
|
|
def p_conceptualTable(self, p):
|
|
"""conceptualTable : SEQUENCE OF row"""
|
|
p[0] = ('conceptualTable', p[3])
|
|
|
|
def p_row(self, p):
|
|
"""row : UPPERCASE_IDENTIFIER"""
|
|
# libsmi: TODO: this must be an entryType
|
|
p[0] = ('row', p[1])
|
|
|
|
def p_entryType(self, p):
|
|
"""entryType : SEQUENCE '{' sequenceItems '}'"""
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_sequenceItems(self, p):
|
|
"""sequenceItems : sequenceItems ',' sequenceItem
|
|
| sequenceItem"""
|
|
# libsmi: TODO: might this list be emtpy?
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_sequenceItem(self, p):
|
|
"""sequenceItem : LOWERCASE_IDENTIFIER sequenceSyntax"""
|
|
p[0] = (p[1], p[2])
|
|
|
|
def p_Syntax(self, p):
|
|
"""Syntax : ObjectSyntax
|
|
| BITS '{' NamedBits '}'"""
|
|
# libsmi: TODO: standalone `BITS' ok? seen in RMON2-MIB
|
|
# libsmi: -> no, it's only allowed in a SEQUENCE {...}
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = p[1]
|
|
elif n == 5:
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_sequenceSyntax(self, p):
|
|
"""sequenceSyntax : BITS
|
|
| UPPERCASE_IDENTIFIER anySubType
|
|
| sequenceObjectSyntax"""
|
|
p[0] = p[1] # no subtype or complex syntax supported
|
|
|
|
def p_NamedBits(self, p):
|
|
"""NamedBits : NamedBits ',' NamedBit
|
|
| NamedBit"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_NamedBit(self, p):
|
|
"""NamedBit : LOWERCASE_IDENTIFIER '(' NUMBER ')'"""
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_objectIdentityClause(self, p):
|
|
"""objectIdentityClause : LOWERCASE_IDENTIFIER OBJECT_IDENTITY STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
|
|
p[0] = ('objectIdentityClause', p[1], # id
|
|
# p[2], # OBJECT_IDENTITY
|
|
p[4], # status
|
|
(p[5], p[6]), # description
|
|
p[7], # reference
|
|
p[10]) # objectIdentifier
|
|
|
|
def p_objectTypeClause(self, p):
|
|
"""objectTypeClause : LOWERCASE_IDENTIFIER OBJECT_TYPE SYNTAX Syntax UnitsPart MaxOrPIBAccessPart STATUS Status descriptionClause ReferPart IndexPart MibIndex DefValPart COLON_COLON_EQUAL '{' ObjectName '}'"""
|
|
p[0] = ('objectTypeClause', p[1], # id
|
|
# p[2], # OBJECT_TYPE
|
|
p[4], # syntax
|
|
p[5], # UnitsPart
|
|
p[6], # MaxOrPIBAccessPart
|
|
p[8], # status
|
|
p[9], # descriptionClause
|
|
p[10], # reference
|
|
p[11], # augmentions
|
|
p[12], # index
|
|
p[13], # DefValPart
|
|
p[16]) # ObjectName
|
|
|
|
def p_descriptionClause(self, p):
|
|
"""descriptionClause : DESCRIPTION Text
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[2])
|
|
|
|
def p_trapTypeClause(self, p):
|
|
"""trapTypeClause : fuzzy_lowercase_identifier TRAP_TYPE ENTERPRISE objectIdentifier VarPart DescrPart ReferPart COLON_COLON_EQUAL NUMBER"""
|
|
# libsmi: TODO: range of number?
|
|
p[0] = ('trapTypeClause', p[1], # fuzzy_lowercase_identifier
|
|
# p[2], # TRAP_TYPE
|
|
p[4], # objectIdentifier
|
|
p[5], # VarPart
|
|
p[6], # description
|
|
p[7], # reference
|
|
p[9]) # NUMBER
|
|
|
|
def p_VarPart(self, p):
|
|
"""VarPart : VARIABLES '{' VarTypes '}'
|
|
| empty"""
|
|
p[0] = p[1] and p[3] or []
|
|
|
|
def p_VarTypes(self, p):
|
|
"""VarTypes : VarTypes ',' VarType
|
|
| VarType"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = ('VarTypes', p[1][1] + [p[3]])
|
|
elif n == 2:
|
|
p[0] = ('VarTypes', [p[1]])
|
|
|
|
def p_VarType(self, p):
|
|
"""VarType : ObjectName"""
|
|
p[0] = p[1][1][0]
|
|
|
|
def p_DescrPart(self, p):
|
|
"""DescrPart : DESCRIPTION Text
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[2])
|
|
|
|
def p_MaxOrPIBAccessPart(self, p):
|
|
"""MaxOrPIBAccessPart : MaxAccessPart
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_MaxAccessPart(self, p):
|
|
"""MaxAccessPart : MAX_ACCESS Access
|
|
| ACCESS Access"""
|
|
p[0] = ('MaxAccessPart', p[2])
|
|
|
|
def p_notificationTypeClause(self, p):
|
|
"""notificationTypeClause : LOWERCASE_IDENTIFIER NOTIFICATION_TYPE NotificationObjectsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' NotificationName '}'"""
|
|
p[0] = ('notificationTypeClause',
|
|
p[1], # id
|
|
# p[2], # NOTIFICATION_TYPE
|
|
p[3], # NotificationObjectsPart
|
|
p[5], # status
|
|
(p[6], p[7]), # description
|
|
p[8], # reference
|
|
p[11]) # NotificationName aka objectIdentifier
|
|
|
|
def p_moduleIdentityClause(self, p):
|
|
"""moduleIdentityClause : LOWERCASE_IDENTIFIER MODULE_IDENTITY SubjectCategoriesPart LAST_UPDATED ExtUTCTime ORGANIZATION Text CONTACT_INFO Text DESCRIPTION Text RevisionPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
|
|
p[0] = ('moduleIdentityClause', p[1], # id
|
|
# p[2], # MODULE_IDENTITY
|
|
# XXX p[3], # SubjectCategoriesPart
|
|
(p[4], p[5]), # last updated
|
|
(p[6], p[7]), # organization
|
|
(p[8], p[9]), # contact info
|
|
(p[10], p[11]), # description
|
|
p[12], # RevisionPart
|
|
p[15]) # objectIdentifier
|
|
|
|
# Subject categories: RFC3159
|
|
|
|
def p_SubjectCategoriesPart(self, p):
|
|
"""SubjectCategoriesPart : SUBJECT_CATEGORIES '{' SubjectCategories '}'
|
|
| empty"""
|
|
# if p[1]:
|
|
# p[0] = (p[1], p[3])
|
|
|
|
def p_SubjectCategories(self, p):
|
|
"""SubjectCategories : CategoryIDs"""
|
|
# p[0] = p[1]
|
|
|
|
def p_CategoryIDs(self, p):
|
|
"""CategoryIDs : CategoryIDs ',' CategoryID
|
|
| CategoryID"""
|
|
# n = len(p)
|
|
# if n == 4:
|
|
# p[0] = ('CategoryIDs', p[1][1] + [p[3]])
|
|
# elif n == 2:
|
|
# p[0] = ('CategoryIDs', [p[1]])
|
|
|
|
def p_CategoryID(self, p):
|
|
"""CategoryID : LOWERCASE_IDENTIFIER '(' NUMBER ')'
|
|
| LOWERCASE_IDENTIFIER"""
|
|
# n = len(p)
|
|
# if n == 2:
|
|
# p[0] = ('CategoryID', p[1])
|
|
# elif n == 5:
|
|
# p[0] = ('CategoryID', p[3])
|
|
|
|
# ...subject categories
|
|
|
|
def p_ObjectSyntax(self, p):
|
|
"""ObjectSyntax : SimpleSyntax
|
|
| conceptualTable
|
|
| row
|
|
| entryType
|
|
| ApplicationSyntax
|
|
| typeTag SimpleSyntax"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = p[1]
|
|
elif n == 3:
|
|
p[0] = p[2]
|
|
|
|
def p_typeTag(self, p):
|
|
"""typeTag : '[' APPLICATION NUMBER ']' IMPLICIT
|
|
| '[' UNIVERSAL NUMBER ']' IMPLICIT"""
|
|
|
|
def p_sequenceObjectSyntax(self, p):
|
|
"""sequenceObjectSyntax : sequenceSimpleSyntax
|
|
| sequenceApplicationSyntax"""
|
|
# libsmi: TO DO: add to this rule conceptualTable, row, entryType
|
|
p[0] = p[1]
|
|
|
|
def p_valueofObjectSyntax(self, p):
|
|
"""valueofObjectSyntax : valueofSimpleSyntax"""
|
|
p[0] = p[1]
|
|
|
|
def p_SimpleSyntax(self, p):
|
|
"""SimpleSyntax : INTEGER
|
|
| INTEGER integerSubType
|
|
| INTEGER enumSpec
|
|
| INTEGER32
|
|
| INTEGER32 integerSubType
|
|
| UPPERCASE_IDENTIFIER enumSpec
|
|
| UPPERCASE_IDENTIFIER integerSubType
|
|
| OCTET STRING
|
|
| OCTET STRING octetStringSubType
|
|
| UPPERCASE_IDENTIFIER octetStringSubType
|
|
| OBJECT IDENTIFIER anySubType"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = ('SimpleSyntax', p[1])
|
|
|
|
elif n == 3:
|
|
if p[1] == 'OCTET':
|
|
p[0] = ('SimpleSyntax', p[1] + ' ' + p[2])
|
|
else:
|
|
p[0] = ('SimpleSyntax', p[1], p[2])
|
|
|
|
elif n == 4:
|
|
p[0] = ('SimpleSyntax', p[1] + ' ' + p[2], p[3])
|
|
|
|
def p_valueofSimpleSyntax(self, p):
|
|
"""valueofSimpleSyntax : NUMBER
|
|
| NEGATIVENUMBER
|
|
| NUMBER64
|
|
| NEGATIVENUMBER64
|
|
| HEX_STRING
|
|
| BIN_STRING
|
|
| LOWERCASE_IDENTIFIER
|
|
| QUOTED_STRING
|
|
| '{' objectIdentifier_defval '}'"""
|
|
# libsmi for objectIdentifier_defval:
|
|
# This is only for some MIBs with invalid numerical
|
|
# OID notation for DEFVALs. We DO NOT parse them
|
|
# correctly. We just don't want to produce a
|
|
# parser error.
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = p[1]
|
|
elif n == 4: # XXX
|
|
pass
|
|
|
|
def p_sequenceSimpleSyntax(self, p):
|
|
"""sequenceSimpleSyntax : INTEGER anySubType
|
|
| INTEGER32 anySubType
|
|
| OCTET STRING anySubType
|
|
| OBJECT IDENTIFIER anySubType"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = p[1] # XXX not supporting subtypes here
|
|
elif n == 4:
|
|
p[0] = p[1] + ' ' + p[2] # XXX not supporting subtypes here
|
|
|
|
def p_ApplicationSyntax(self, p):
|
|
"""ApplicationSyntax : IPADDRESS anySubType
|
|
| COUNTER32
|
|
| COUNTER32 integerSubType
|
|
| GAUGE32
|
|
| GAUGE32 integerSubType
|
|
| UNSIGNED32
|
|
| UNSIGNED32 integerSubType
|
|
| TIMETICKS anySubType
|
|
| OPAQUE
|
|
| OPAQUE octetStringSubType
|
|
| COUNTER64
|
|
| COUNTER64 integerSubType"""
|
|
# COUNTER32 and COUNTER64 was with anySubType in libsmi
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = ('ApplicationSyntax', p[1])
|
|
elif n == 3:
|
|
p[0] = ('ApplicationSyntax', p[1], p[2])
|
|
|
|
def p_sequenceApplicationSyntax(self, p):
|
|
"""sequenceApplicationSyntax : IPADDRESS anySubType
|
|
| COUNTER32 anySubType
|
|
| GAUGE32 anySubType
|
|
| UNSIGNED32 anySubType
|
|
| TIMETICKS anySubType
|
|
| OPAQUE
|
|
| COUNTER64 anySubType"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = p[1]
|
|
elif n == 3:
|
|
p[0] = p[1] # XXX not supporting subtypes here
|
|
|
|
def p_anySubType(self, p):
|
|
"""anySubType : integerSubType
|
|
| octetStringSubType
|
|
| enumSpec
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_integerSubType(self, p):
|
|
"""integerSubType : '(' ranges ')'"""
|
|
p[0] = ('integerSubType', p[2])
|
|
|
|
def p_octetStringSubType(self, p):
|
|
"""octetStringSubType : '(' SIZE '(' ranges ')' ')'"""
|
|
p[0] = ('octetStringSubType', p[4])
|
|
|
|
def p_ranges(self, p):
|
|
"""ranges : ranges '|' range
|
|
| range"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_range(self, p):
|
|
"""range : value DOT_DOT value
|
|
| value"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = (p[1],)
|
|
elif n == 4:
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_value(self, p):
|
|
"""value : NEGATIVENUMBER
|
|
| NUMBER
|
|
| NEGATIVENUMBER64
|
|
| NUMBER64
|
|
| HEX_STRING
|
|
| BIN_STRING"""
|
|
p[0] = p[1]
|
|
|
|
def p_enumSpec(self, p):
|
|
"""enumSpec : '{' enumItems '}'"""
|
|
p[0] = ('enumSpec', p[2])
|
|
|
|
def p_enumItems(self, p):
|
|
"""enumItems : enumItems ',' enumItem
|
|
| enumItem"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_enumItem(self, p):
|
|
"""enumItem : LOWERCASE_IDENTIFIER '(' enumNumber ')'"""
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_enumNumber(self, p):
|
|
"""enumNumber : NUMBER
|
|
| NEGATIVENUMBER"""
|
|
# XXX | LOWERCASE_IDENTIFIER"""
|
|
p[0] = p[1]
|
|
|
|
def p_Status(self, p):
|
|
"""Status : LOWERCASE_IDENTIFIER"""
|
|
p[0] = ('Status', p[1])
|
|
|
|
def p_DisplayPart(self, p):
|
|
"""DisplayPart : DISPLAY_HINT Text
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[2])
|
|
|
|
def p_UnitsPart(self, p):
|
|
"""UnitsPart : UNITS Text
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[2])
|
|
|
|
def p_Access(self, p):
|
|
"""Access : LOWERCASE_IDENTIFIER"""
|
|
p[0] = p[1]
|
|
|
|
def p_IndexPart(self, p):
|
|
"""IndexPart : AUGMENTS '{' Entry '}'
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[3]
|
|
|
|
def p_MibIndex(self, p):
|
|
"""MibIndex : INDEX '{' IndexTypes '}'
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_IndexTypes(self, p):
|
|
"""IndexTypes : IndexTypes ',' IndexType
|
|
| IndexType"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_IndexType(self, p):
|
|
"""IndexType : IMPLIED Index
|
|
| Index"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = (0, p[1])
|
|
elif n == 3:
|
|
p[0] = (1, p[2]) # IMPLIED
|
|
|
|
def p_Index(self, p):
|
|
"""Index : ObjectName"""
|
|
# libsmi: TODO: use the SYNTAX value of the correspondent
|
|
# OBJECT-TYPE invocation
|
|
p[0] = p[1][1][0] # XXX just name???
|
|
|
|
def p_Entry(self, p):
|
|
"""Entry : ObjectName"""
|
|
p[0] = p[1][1][0]
|
|
|
|
def p_DefValPart(self, p):
|
|
"""DefValPart : DEFVAL '{' Value '}'
|
|
| empty"""
|
|
if p[1] and p[3]:
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_Value(self, p):
|
|
"""Value : valueofObjectSyntax
|
|
| '{' BitsValue '}'"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = p[1]
|
|
elif n == 4:
|
|
p[0] = p[2]
|
|
|
|
def p_BitsValue(self, p):
|
|
"""BitsValue : BitNames
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_BitNames(self, p):
|
|
"""BitNames : BitNames ',' LOWERCASE_IDENTIFIER
|
|
| LOWERCASE_IDENTIFIER"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = ('BitNames', p[1][1] + [p[3]])
|
|
elif n == 2:
|
|
p[0] = ('BitNames', [p[1]])
|
|
|
|
def p_ObjectName(self, p):
|
|
"""ObjectName : objectIdentifier"""
|
|
p[0] = p[1]
|
|
|
|
def p_NotificationName(self, p):
|
|
"""NotificationName : objectIdentifier"""
|
|
p[0] = p[1]
|
|
|
|
def p_ReferPart(self, p):
|
|
"""ReferPart : REFERENCE Text
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[2])
|
|
|
|
def p_RevisionPart(self, p):
|
|
"""RevisionPart : Revisions
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_Revisions(self, p):
|
|
"""Revisions : Revisions Revision
|
|
| Revision"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = ('Revisions', p[1][1] + [p[2]])
|
|
elif n == 2:
|
|
p[0] = ('Revisions', [p[1]])
|
|
|
|
def p_Revision(self, p):
|
|
"""Revision : REVISION ExtUTCTime DESCRIPTION Text"""
|
|
p[0] = (p[2], # revision time
|
|
(p[3], p[4])) # description
|
|
|
|
def p_NotificationObjectsPart(self, p):
|
|
"""NotificationObjectsPart : OBJECTS '{' Objects '}'
|
|
| empty"""
|
|
p[0] = p[1] and p[3] or []
|
|
|
|
def p_ObjectGroupObjectsPart(self, p):
|
|
"""ObjectGroupObjectsPart : OBJECTS '{' Objects '}'"""
|
|
p[0] = p[3]
|
|
|
|
def p_Objects(self, p):
|
|
"""Objects : Objects ',' Object
|
|
| Object"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = ('Objects', p[1][1] + [p[3]])
|
|
elif n == 2:
|
|
p[0] = ('Objects', [p[1]])
|
|
|
|
def p_Object(self, p):
|
|
"""Object : ObjectName"""
|
|
p[0] = p[1][1][0]
|
|
|
|
def p_NotificationsPart(self, p):
|
|
"""NotificationsPart : NOTIFICATIONS '{' Notifications '}'"""
|
|
p[0] = p[3]
|
|
|
|
def p_Notifications(self, p):
|
|
"""Notifications : Notifications ',' Notification
|
|
| Notification"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = ('Notifications', p[1][1] + [p[3]])
|
|
elif n == 2:
|
|
p[0] = ('Notifications', [p[1]])
|
|
|
|
def p_Notification(self, p):
|
|
"""Notification : NotificationName"""
|
|
p[0] = p[1][1][0]
|
|
|
|
def p_Text(self, p):
|
|
"""Text : QUOTED_STRING"""
|
|
p[0] = p[1][1:-1] # getting rid of quotes
|
|
|
|
def p_ExtUTCTime(self, p):
|
|
"""ExtUTCTime : QUOTED_STRING"""
|
|
p[0] = p[1][1:-1] # getting rid of quotes
|
|
|
|
def p_objectIdentifier(self, p):
|
|
"""objectIdentifier : subidentifiers"""
|
|
p[0] = ('objectIdentifier', p[1])
|
|
|
|
def p_subidentifiers(self, p):
|
|
"""subidentifiers : subidentifiers subidentifier
|
|
| subidentifier"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = p[1] + [p[2]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
|
|
def p_subidentifier(self, p):
|
|
"""subidentifier : fuzzy_lowercase_identifier
|
|
| NUMBER
|
|
| LOWERCASE_IDENTIFIER '(' NUMBER ')'"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = p[1]
|
|
elif n == 5:
|
|
# NOTE: we are not creating new symbol p[1] because formally
|
|
# it is not defined in *this* MIB
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_objectIdentifier_defval(self, p):
|
|
"""objectIdentifier_defval : subidentifiers_defval"""
|
|
p[0] = ('objectIdentifier_defval', p[1])
|
|
|
|
def p_subidentifiers_defval(self, p):
|
|
"""subidentifiers_defval : subidentifiers_defval subidentifier_defval
|
|
| subidentifier_defval"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = ('subidentifiers_defval', p[1][1] + [p[2]])
|
|
elif n == 2:
|
|
p[0] = ('subidentifiers_defval', [p[1]])
|
|
|
|
def p_subidentifier_defval(self, p):
|
|
"""subidentifier_defval : LOWERCASE_IDENTIFIER '(' NUMBER ')'
|
|
| NUMBER"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = ('subidentifier_defval', p[1])
|
|
elif n == 5:
|
|
p[0] = ('subidentifier_defval', p[1], p[3])
|
|
|
|
def p_objectGroupClause(self, p):
|
|
"""objectGroupClause : LOWERCASE_IDENTIFIER OBJECT_GROUP ObjectGroupObjectsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
|
|
p[0] = ('objectGroupClause',
|
|
p[1], # id
|
|
p[3], # objects
|
|
p[5], # status
|
|
(p[6], p[7]), # description
|
|
p[8], # reference
|
|
p[11]) # objectIdentifier
|
|
|
|
def p_notificationGroupClause(self, p):
|
|
"""notificationGroupClause : LOWERCASE_IDENTIFIER NOTIFICATION_GROUP NotificationsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
|
|
p[0] = ('notificationGroupClause',
|
|
p[1], # id
|
|
p[3], # notifications
|
|
p[5], # status
|
|
(p[6], p[7]), # description
|
|
p[8], # reference
|
|
p[11]) # objectIdentifier
|
|
|
|
def p_moduleComplianceClause(self, p):
|
|
"""moduleComplianceClause : LOWERCASE_IDENTIFIER MODULE_COMPLIANCE STATUS Status DESCRIPTION Text ReferPart ComplianceModulePart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
|
|
p[0] = ('moduleComplianceClause',
|
|
p[1], # id
|
|
# p[2], # MODULE_COMPLIANCE
|
|
p[4], # status
|
|
(p[5], p[6]), # description
|
|
p[7], # reference
|
|
p[8], # ComplianceModules
|
|
p[11]) # objectIdentifier
|
|
|
|
def p_ComplianceModulePart(self, p):
|
|
"""ComplianceModulePart : ComplianceModules"""
|
|
p[0] = p[1]
|
|
|
|
def p_ComplianceModules(self, p):
|
|
"""ComplianceModules : ComplianceModules ComplianceModule
|
|
| ComplianceModule"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = ('ComplianceModules', p[1][1] + [p[2]])
|
|
elif n == 2:
|
|
p[0] = ('ComplianceModules', [p[1]])
|
|
|
|
def p_ComplianceModule(self, p):
|
|
"""ComplianceModule : MODULE ComplianceModuleName MandatoryPart CompliancePart"""
|
|
objects = p[3] and p[3][1] or []
|
|
objects += p[4] and p[4][1] or []
|
|
p[0] = (p[2], # ModuleName
|
|
objects) # MandatoryPart + CompliancePart
|
|
|
|
def p_ComplianceModuleName(self, p):
|
|
"""ComplianceModuleName : UPPERCASE_IDENTIFIER
|
|
| empty"""
|
|
# XXX | UPPERCASE_IDENTIFIER objectIdentifier
|
|
p[0] = p[1]
|
|
|
|
def p_MandatoryPart(self, p):
|
|
"""MandatoryPart : MANDATORY_GROUPS '{' MandatoryGroups '}'
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[3]
|
|
|
|
def p_MandatoryGroups(self, p):
|
|
"""MandatoryGroups : MandatoryGroups ',' MandatoryGroup
|
|
| MandatoryGroup"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = ('MandatoryGroups', p[1][1] + [p[3]])
|
|
elif n == 2:
|
|
p[0] = ('MandatoryGroups', [p[1]])
|
|
|
|
def p_MandatoryGroup(self, p):
|
|
"""MandatoryGroup : objectIdentifier"""
|
|
p[0] = p[1][1][0] # objectIdentifier? Maybe name?
|
|
|
|
def p_CompliancePart(self, p):
|
|
"""CompliancePart : Compliances
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_Compliances(self, p):
|
|
"""Compliances : Compliances Compliance
|
|
| Compliance"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = p[1] and p[2] and ('Compliances', p[1][1] + [p[2]]) or p[1]
|
|
elif n == 2:
|
|
p[0] = p[1] and ('Compliances', [p[1]]) or None
|
|
|
|
def p_Compliance(self, p):
|
|
"""Compliance : ComplianceGroup
|
|
| ComplianceObject"""
|
|
if p[1]:
|
|
p[0] = p[1]
|
|
|
|
def p_ComplianceGroup(self, p):
|
|
"""ComplianceGroup : GROUP objectIdentifier DESCRIPTION Text"""
|
|
p[0] = p[2][1][0] # objectIdentifier
|
|
# p[1], # GROUP
|
|
# (p[3], p[4])) # description
|
|
|
|
def p_ComplianceObject(self, p):
|
|
"""ComplianceObject : OBJECT ObjectName SyntaxPart WriteSyntaxPart AccessPart DESCRIPTION Text"""
|
|
# p[0] = (p[1], # object
|
|
# p[2], # name
|
|
# p[3], # syntax
|
|
# p[4], # write syntax
|
|
# p[5], # access
|
|
# (p[6], p[7])) # description
|
|
|
|
def p_SyntaxPart(self, p):
|
|
"""SyntaxPart : SYNTAX Syntax
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[2]
|
|
|
|
def p_WriteSyntaxPart(self, p):
|
|
"""WriteSyntaxPart : WRITE_SYNTAX WriteSyntax
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = p[2]
|
|
|
|
def p_WriteSyntax(self, p):
|
|
"""WriteSyntax : Syntax"""
|
|
p[0] = ('WriteSyntax', p[1])
|
|
|
|
def p_AccessPart(self, p):
|
|
"""AccessPart : MIN_ACCESS Access
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[2])
|
|
|
|
def p_agentCapabilitiesClause(self, p):
|
|
"""agentCapabilitiesClause : LOWERCASE_IDENTIFIER AGENT_CAPABILITIES PRODUCT_RELEASE Text STATUS Status DESCRIPTION Text ReferPart ModulePart_Capabilities COLON_COLON_EQUAL '{' objectIdentifier '}'"""
|
|
p[0] = ('agentCapabilitiesClause', p[1], # id
|
|
# p[2], # AGENT_CAPABILITIES
|
|
(p[3], p[4]), # product release
|
|
p[6], # status
|
|
(p[7], p[8]), # description
|
|
p[9], # reference
|
|
# p[10], # module capabilities
|
|
p[13]) # objectIdentifier
|
|
|
|
def p_ModulePart_Capabilities(self, p):
|
|
"""ModulePart_Capabilities : Modules_Capabilities
|
|
| empty"""
|
|
# if p[1]:
|
|
# p[0] = p[1]
|
|
|
|
def p_Modules_Capabilities(self, p):
|
|
"""Modules_Capabilities : Modules_Capabilities Module_Capabilities
|
|
| Module_Capabilities"""
|
|
# n = len(p)
|
|
# if n == 3:
|
|
# p[0] = ('Modules_Capabilities', p[1][1] + [p[2]])
|
|
# elif n == 2:
|
|
# p[0] = ('Modules_Capabilities', [p[1]])
|
|
|
|
def p_Module_Capabilities(self, p):
|
|
"""Module_Capabilities : SUPPORTS ModuleName_Capabilities INCLUDES '{' CapabilitiesGroups '}' VariationPart"""
|
|
# p[0] = ('Module_Capabilities', (p[1], p[2]), # supports
|
|
# (p[3], p[5]), # includes
|
|
# p[7]) # variations
|
|
|
|
def p_CapabilitiesGroups(self, p):
|
|
"""CapabilitiesGroups : CapabilitiesGroups ',' CapabilitiesGroup
|
|
| CapabilitiesGroup"""
|
|
# n = len(p)
|
|
# if n == 4:
|
|
# p[0] = ('CapabilitiesGroups', p[1][1] + [p[3]])
|
|
# elif n == 2:
|
|
# p[0] = ('CapabilitiesGroups', [p[1]])
|
|
|
|
def p_CapabilitiesGroup(self, p):
|
|
"""CapabilitiesGroup : objectIdentifier"""
|
|
# p[0] = ('CapabilitiesGroup', p[1])
|
|
|
|
def p_ModuleName_Capabilities(self, p):
|
|
"""ModuleName_Capabilities : UPPERCASE_IDENTIFIER objectIdentifier
|
|
| UPPERCASE_IDENTIFIER"""
|
|
# n = len(p)
|
|
# if n == 2:
|
|
# p[0] = ('ModuleName_Capabilities', p[1])
|
|
# elif n == 3:
|
|
# p[0] = ('ModuleName_Capabilities', p[1], p[2])
|
|
|
|
def p_VariationPart(self, p):
|
|
"""VariationPart : Variations
|
|
| empty"""
|
|
# if p[1]:
|
|
# p[0] = p[1]
|
|
|
|
def p_Variations(self, p):
|
|
"""Variations : Variations Variation
|
|
| Variation"""
|
|
# n = len(p)
|
|
# if n == 3:
|
|
# p[0] = ('Variations', p[1][1] + [p[2]])
|
|
# elif n == 2:
|
|
# p[0] = ('Variations', [p[1]]) pass
|
|
|
|
def p_Variation(self, p):
|
|
"""Variation : VARIATION ObjectName SyntaxPart WriteSyntaxPart VariationAccessPart CreationPart DefValPart DESCRIPTION Text"""
|
|
# p[0] = (p[1], # variation
|
|
# p[2], # name
|
|
# p[3], # syntax
|
|
# p[4], # write syntax
|
|
# p[5], # access
|
|
# p[6], # creation
|
|
# p[7], # defval
|
|
# (p[8], p[9])) # description
|
|
|
|
def p_VariationAccessPart(self, p):
|
|
"""VariationAccessPart : ACCESS VariationAccess
|
|
| empty"""
|
|
# if p[1]:
|
|
# p[0] = (p[1], p[2])
|
|
|
|
def p_VariationAccess(self, p):
|
|
"""VariationAccess : LOWERCASE_IDENTIFIER"""
|
|
# p[0] = p[1]
|
|
|
|
def p_CreationPart(self, p):
|
|
"""CreationPart : CREATION_REQUIRES '{' Cells '}'
|
|
| empty"""
|
|
if p[1]:
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_Cells(self, p):
|
|
"""Cells : Cells ',' Cell
|
|
| Cell"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = ('Cells', p[1][1] + [p[3]])
|
|
elif n == 2:
|
|
p[0] = ('Cells', [p[1]])
|
|
|
|
def p_Cell(self, p):
|
|
"""Cell : ObjectName"""
|
|
p[0] = ('Cell', p[1])
|
|
|
|
def p_empty(self, p):
|
|
"""empty :"""
|
|
|
|
# Error rule for syntax errors
|
|
def p_error(self, p):
|
|
if p:
|
|
raise error.PySmiParserError("Bad grammar near token type %s, value %s" % (p.type, p.value),
|
|
lineno=p.lineno)
|
|
|
|
|
|
#
|
|
# Parser grammar relaxation follows.
|
|
#
|
|
# The classes that follow serve a purpose of encapsulating assorted functions
|
|
# into a namespace. The namespace type is not universally supported across all
|
|
# Python versions we want to run on, thus the hack with `staticmethod` decorator
|
|
# and `self` first parameter.
|
|
#
|
|
|
|
#
|
|
# SMIv1 grammar
|
|
#
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class SupportSmiV1Keywords(object):
|
|
# NETWORKADDRESS added
|
|
@staticmethod
|
|
def p_importedKeyword(self, p):
|
|
"""importedKeyword : importedSMIKeyword
|
|
| BITS
|
|
| INTEGER32
|
|
| IPADDRESS
|
|
| NETWORKADDRESS
|
|
| MANDATORY_GROUPS
|
|
| MODULE_COMPLIANCE
|
|
| MODULE_IDENTITY
|
|
| OBJECT_GROUP
|
|
| OBJECT_IDENTITY
|
|
| OBJECT_TYPE
|
|
| OPAQUE
|
|
| TEXTUAL_CONVENTION
|
|
| TIMETICKS
|
|
| UNSIGNED32"""
|
|
p[0] = p[1]
|
|
|
|
# NETWORKADDRESS added
|
|
@staticmethod
|
|
def p_typeSMIandSPPI(self, p):
|
|
"""typeSMIandSPPI : IPADDRESS
|
|
| NETWORKADDRESS
|
|
| TIMETICKS
|
|
| OPAQUE
|
|
| INTEGER32
|
|
| UNSIGNED32"""
|
|
p[0] = p[1]
|
|
|
|
# NETWORKADDRESS added
|
|
@staticmethod
|
|
def p_ApplicationSyntax(self, p):
|
|
"""ApplicationSyntax : IPADDRESS anySubType
|
|
| NETWORKADDRESS anySubType
|
|
| COUNTER32
|
|
| COUNTER32 integerSubType
|
|
| GAUGE32
|
|
| GAUGE32 integerSubType
|
|
| UNSIGNED32
|
|
| UNSIGNED32 integerSubType
|
|
| TIMETICKS anySubType
|
|
| OPAQUE
|
|
| OPAQUE octetStringSubType
|
|
| COUNTER64
|
|
| COUNTER64 integerSubType"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = ('ApplicationSyntax', p[1])
|
|
elif n == 3:
|
|
p[0] = ('ApplicationSyntax', p[1], p[2])
|
|
|
|
# NETWORKADDRESS added for SEQUENCE syntax
|
|
@staticmethod
|
|
def p_sequenceApplicationSyntax(self, p):
|
|
"""sequenceApplicationSyntax : IPADDRESS anySubType
|
|
| NETWORKADDRESS anySubType
|
|
| COUNTER32 anySubType
|
|
| GAUGE32 anySubType
|
|
| UNSIGNED32 anySubType
|
|
| TIMETICKS anySubType
|
|
| OPAQUE
|
|
| COUNTER64 anySubType"""
|
|
n = len(p)
|
|
if n == 2:
|
|
p[0] = p[1]
|
|
elif n == 3:
|
|
p[0] = p[1] # XXX not supporting subtypes here
|
|
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class SupportIndex(object):
|
|
# SMIv1 IndexTypes added
|
|
@staticmethod
|
|
def p_Index(self, p):
|
|
"""Index : ObjectName
|
|
| typeSMIv1"""
|
|
|
|
# libsmi: TODO: use the SYNTAX value of the correspondent
|
|
# OBJECT-TYPE invocation
|
|
p[0] = isinstance(p[1], tuple) and p[1][1][0] or p[1]
|
|
|
|
# for Index rule
|
|
@staticmethod
|
|
def p_typeSMIv1(self, p):
|
|
"""typeSMIv1 : INTEGER
|
|
| OCTET STRING
|
|
| IPADDRESS
|
|
| NETWORKADDRESS"""
|
|
n = len(p)
|
|
indextype = n == 3 and p[1] + ' ' + p[2] or p[1]
|
|
p[0] = indextype
|
|
|
|
|
|
#
|
|
# Some changes in grammar to handle common mistakes in MIBs
|
|
#
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class CommaInImport(object):
|
|
# comma at the end of import list
|
|
@staticmethod
|
|
def p_importIdentifiers(self, p):
|
|
"""importIdentifiers : importIdentifiers ',' importIdentifier
|
|
| importIdentifier
|
|
| importIdentifiers ','"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
elif n == 3: # excessive comma case
|
|
p[0] = p[1]
|
|
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class CommaInSequence(object):
|
|
# comma at the end of sequence list
|
|
@staticmethod
|
|
def p_sequenceItems(self, p):
|
|
"""sequenceItems : sequenceItems ',' sequenceItem
|
|
| sequenceItem
|
|
| sequenceItems ','"""
|
|
# libsmi: TODO: might this list be emtpy?
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
elif n == 3: # excessive comma case
|
|
p[0] = p[1]
|
|
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class CommaAndSpaces(object):
|
|
# common typos handled (mix of commas and spaces)
|
|
@staticmethod
|
|
def p_enumItems(self, p):
|
|
"""enumItems : enumItems ',' enumItem
|
|
| enumItem
|
|
| enumItems enumItem
|
|
| enumItems ','"""
|
|
n = len(p)
|
|
if n == 4:
|
|
p[0] = p[1] + [p[3]]
|
|
elif n == 2:
|
|
p[0] = [p[1]]
|
|
elif n == 3: # typo case
|
|
if p[2] == ',':
|
|
p[0] = p[1]
|
|
else:
|
|
p[0] = p[1] + [p[2]]
|
|
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class UppercaseIdentifier(object):
|
|
# common mistake - using UPPERCASE_IDENTIFIER
|
|
@staticmethod
|
|
def p_enumItem(self, p):
|
|
"""enumItem : LOWERCASE_IDENTIFIER '(' enumNumber ')'
|
|
| UPPERCASE_IDENTIFIER '(' enumNumber ')'"""
|
|
p[0] = (p[1], p[3])
|
|
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class LowcaseIdentifier(object):
|
|
# common mistake - LOWERCASE_IDENTIFIER in symbol's name
|
|
@staticmethod
|
|
def p_notificationTypeClause(self, p):
|
|
"""notificationTypeClause : fuzzy_lowercase_identifier NOTIFICATION_TYPE NotificationObjectsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' NotificationName '}'""" # some MIBs have uppercase and/or lowercase id
|
|
p[0] = ('notificationTypeClause', p[1], # id
|
|
# p[2], # NOTIFICATION_TYPE
|
|
p[3], # NotificationObjectsPart
|
|
p[5], # status
|
|
(p[6], p[7]), # description
|
|
p[8], # Reference
|
|
p[11]) # NotificationName aka objectIdentifier
|
|
|
|
|
|
# noinspection PyIncorrectDocstring,PyIncorrectDocstring
|
|
class CurlyBracesInEnterprises(object):
|
|
# common mistake - curly brackets around enterprise symbol
|
|
@staticmethod
|
|
def p_trapTypeClause(self, p):
|
|
"""trapTypeClause : fuzzy_lowercase_identifier TRAP_TYPE EnterprisePart VarPart DescrPart ReferPart COLON_COLON_EQUAL NUMBER"""
|
|
# libsmi: TODO: range of number?
|
|
p[0] = ('trapTypeClause', p[1], # fuzzy_lowercase_identifier
|
|
# p[2], # TRAP_TYPE
|
|
p[3], # EnterprisePart (objectIdentifier)
|
|
p[4], # VarPart
|
|
p[5], # description
|
|
p[6], # reference
|
|
p[8]) # NUMBER
|
|
|
|
@staticmethod
|
|
def p_EnterprisePart(self, p):
|
|
"""EnterprisePart : ENTERPRISE objectIdentifier
|
|
| ENTERPRISE '{' objectIdentifier '}'"""
|
|
n = len(p)
|
|
if n == 3:
|
|
p[0] = p[2]
|
|
elif n == 5: # common mistake case
|
|
p[0] = p[3]
|
|
|
|
|
|
# noinspection PyIncorrectDocstring
|
|
class NoCells(object):
|
|
# common mistake - no Cells
|
|
@staticmethod
|
|
def p_CreationPart(self, p):
|
|
"""CreationPart : CREATION_REQUIRES '{' Cells '}'
|
|
| CREATION_REQUIRES '{' '}'
|
|
| empty"""
|
|
n = len(p)
|
|
if n == 5:
|
|
p[0] = (p[1], p[3])
|
|
|
|
|
|
relaxedGrammar = {
|
|
'supportSmiV1Keywords': [
|
|
SupportSmiV1Keywords.p_importedKeyword,
|
|
SupportSmiV1Keywords.p_typeSMIandSPPI,
|
|
SupportSmiV1Keywords.p_ApplicationSyntax,
|
|
SupportSmiV1Keywords.p_sequenceApplicationSyntax
|
|
],
|
|
'supportIndex': [
|
|
SupportIndex.p_Index,
|
|
SupportIndex.p_typeSMIv1
|
|
],
|
|
'commaAtTheEndOfImport': [CommaInImport.p_importIdentifiers],
|
|
'commaAtTheEndOfSequence': [CommaInSequence.p_sequenceItems],
|
|
'mixOfCommasAndSpaces': [CommaAndSpaces.p_enumItems],
|
|
'uppercaseIdentifier': [UppercaseIdentifier.p_enumItem],
|
|
'lowcaseIdentifier': [LowcaseIdentifier.p_notificationTypeClause],
|
|
'curlyBracesAroundEnterpriseInTrap': [
|
|
CurlyBracesInEnterprises.p_trapTypeClause,
|
|
CurlyBracesInEnterprises.p_EnterprisePart
|
|
],
|
|
'noCells': [NoCells.p_CreationPart]
|
|
}
|
|
|
|
|
|
def parserFactory(**grammarOptions):
|
|
"""Factory function producing custom specializations of base *SmiV2Parser*
|
|
class.
|
|
|
|
Keyword Args:
|
|
grammarOptions: a list of (bool) typed optional keyword parameters
|
|
enabling particular set of SMIv2 grammar relaxations.
|
|
|
|
Returns:
|
|
Specialized copy of *SmiV2Parser* class.
|
|
|
|
Notes:
|
|
The following SMIv2 grammar relaxation parameters are defined:
|
|
|
|
* supportSmiV1Keywords - parses SMIv1 grammar
|
|
* supportIndex - tolerates ASN.1 types in INDEX clause
|
|
* commaAtTheEndOfImport - tolerates stray comma at the end of IMPORT section
|
|
* commaAtTheEndOfSequence - tolerates stray comma at the end of sequence of elements in MIB
|
|
* mixOfCommasAndSpaces - tolerate a mix of comma and spaces in MIB enumerations
|
|
* uppercaseIdentifier - tolerate uppercased MIB identifiers
|
|
* lowcaseIdentifier - tolerate lowercase MIB identifiers
|
|
* curlyBracesAroundEnterpriseInTrap - tolerate curly braces around enterprise ID in TRAP MACRO
|
|
* noCells - tolerate missing cells (XXX)
|
|
|
|
Examples:
|
|
|
|
>>> from pysmi.parser import smi
|
|
>>> SmiV1Parser = smi.parserFactory(supportSmiV1Keywords=True, supportIndex=True)
|
|
|
|
"""
|
|
classAttr = {}
|
|
|
|
for option in grammarOptions:
|
|
if grammarOptions[option]:
|
|
if option not in relaxedGrammar:
|
|
raise error.PySmiError('Unknown parser relaxation option: %s' % option)
|
|
|
|
for func in relaxedGrammar[option]:
|
|
if sys.version_info[0] > 2:
|
|
classAttr[func.__name__] = func
|
|
else:
|
|
classAttr[func.func_name] = func
|
|
|
|
classAttr['defaultLexer'] = lexerFactory(**grammarOptions)
|
|
|
|
return type('SmiParser', (SmiV2Parser,), classAttr)
|