Mini Shell
#
# 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)
Zerion Mini Shell 1.0