Merge branch 'master' of github.com:etingof/pysnmp
commit
602c4dd304
|
@ -25,15 +25,17 @@ Revision 5.0.0, released 2018-10-??
|
||||||
just var-binds (as var-arg), the rest of the parameters packed
|
just var-binds (as var-arg), the rest of the parameters packed
|
||||||
into opaque kwargs
|
into opaque kwargs
|
||||||
|
|
||||||
|
* The `readVars`, `readNextVars` and `writeVars` methods of MIB
|
||||||
|
instrumentation controller return immediately and deliver their
|
||||||
|
results via a call back.
|
||||||
|
|
||||||
* CommandResponder application passes `snmpEngine` and optionally
|
* CommandResponder application passes `snmpEngine` and optionally
|
||||||
user-supplied `cbCtx` object throughout the MIB instrumentation
|
user-supplied `cbCtx` object throughout the MIB instrumentation
|
||||||
methods. The goal is to let MIB objects access/modify whatever
|
methods. The goal is to let MIB objects access/modify whatever
|
||||||
custom Python objects they need while being called back.
|
custom Python objects they need while being called back.
|
||||||
|
|
||||||
* CommandResponder refactored to facilitate asynchronous
|
* CommandResponder refactored to facilitate asynchronous
|
||||||
MIB instrumentation routines. The `readVars`, `readNextVars` and
|
MIB instrumentation routines.
|
||||||
`writeVars` MIB controller methods return immediately and
|
|
||||||
deliver their results via a call back.
|
|
||||||
|
|
||||||
- The high-level API (`hlapi`) extended to cover lightweight SNMP v1arch
|
- The high-level API (`hlapi`) extended to cover lightweight SNMP v1arch
|
||||||
in hope to ease the use of packet-level SNMP API.
|
in hope to ease the use of packet-level SNMP API.
|
||||||
|
|
|
@ -56,7 +56,7 @@ MibScalar, MibScalarInstance = mibBuilder.importSymbols(
|
||||||
|
|
||||||
class MyStaticMibScalarInstance(MibScalarInstance):
|
class MyStaticMibScalarInstance(MibScalarInstance):
|
||||||
# noinspection PyUnusedLocal,PyUnusedLocal
|
# noinspection PyUnusedLocal,PyUnusedLocal
|
||||||
def getValue(self, name, idx):
|
def getValue(self, name, idx, **context):
|
||||||
return self.getSyntax().clone(
|
return self.getSyntax().clone(
|
||||||
'Python %s running on a %s platform' % (sys.version, sys.platform)
|
'Python %s running on a %s platform' % (sys.version, sys.platform)
|
||||||
)
|
)
|
||||||
|
|
|
@ -96,7 +96,7 @@ class CommandResponder(cmdrsp.CommandResponderBase):
|
||||||
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
||||||
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
||||||
}
|
}
|
||||||
pduTypes = cmdGenMap.keys() # This app will handle these PDUs
|
SUPPORTED_PDU_TYPES = cmdGenMap.keys() # This app will handle these PDUs
|
||||||
|
|
||||||
# SNMP request relay
|
# SNMP request relay
|
||||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||||
|
|
|
@ -95,7 +95,7 @@ class CommandResponder(cmdrsp.CommandResponderBase):
|
||||||
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
||||||
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
||||||
}
|
}
|
||||||
pduTypes = cmdGenMap.keys() # This app will handle these PDUs
|
SUPPORTED_PDU_TYPES = cmdGenMap.keys() # This app will handle these PDUs
|
||||||
|
|
||||||
# SNMP request relay
|
# SNMP request relay
|
||||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||||
|
|
|
@ -95,7 +95,7 @@ class CommandResponder(cmdrsp.CommandResponderBase):
|
||||||
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
||||||
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
||||||
}
|
}
|
||||||
pduTypes = cmdGenMap.keys() # This app will handle these PDUs
|
SUPPORTED_PDU_TYPES = cmdGenMap.keys() # This app will handle these PDUs
|
||||||
|
|
||||||
# SNMP request relay
|
# SNMP request relay
|
||||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||||
|
|
|
@ -98,7 +98,7 @@ class CommandResponder(cmdrsp.CommandResponderBase):
|
||||||
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
||||||
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
|
||||||
}
|
}
|
||||||
pduTypes = cmdGenMap.keys() # This app will handle these PDUs
|
SUPPORTED_PDU_TYPES = cmdGenMap.keys() # This app will handle these PDUs
|
||||||
|
|
||||||
# SNMP request relay
|
# SNMP request relay
|
||||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||||
|
|
|
@ -15,25 +15,41 @@ from pysnmp import debug
|
||||||
# 3.2
|
# 3.2
|
||||||
class CommandResponderBase(object):
|
class CommandResponderBase(object):
|
||||||
acmID = 3 # default MIB access control method to use
|
acmID = 3 # default MIB access control method to use
|
||||||
pduTypes = ()
|
SUPPORTED_PDU_TYPES = ()
|
||||||
|
SMI_ERROR_MAP = {
|
||||||
|
pysnmp.smi.error.NoAccessError: 'noAccess',
|
||||||
|
pysnmp.smi.error.WrongTypeError: 'wrongType',
|
||||||
|
pysnmp.smi.error.WrongLengthError: 'wrongLength',
|
||||||
|
pysnmp.smi.error.WrongEncodingError: 'wrongEncoding',
|
||||||
|
pysnmp.smi.error.WrongValueError: 'wrongValue',
|
||||||
|
pysnmp.smi.error.NoCreationError: 'noCreation',
|
||||||
|
pysnmp.smi.error.InconsistentValueError: 'inconsistentValue',
|
||||||
|
pysnmp.smi.error.ResourceUnavailableError: 'resourceUnavailable',
|
||||||
|
pysnmp.smi.error.CommitFailedError: 'commitFailed',
|
||||||
|
pysnmp.smi.error.UndoFailedError: 'undoFailed',
|
||||||
|
pysnmp.smi.error.AuthorizationError: 'authorizationError',
|
||||||
|
pysnmp.smi.error.NotWritableError: 'notWritable',
|
||||||
|
pysnmp.smi.error.InconsistentNameError: 'inconsistentName'
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, snmpEngine, snmpContext, cbCtx=None):
|
def __init__(self, snmpEngine, snmpContext, cbCtx=None):
|
||||||
snmpEngine.msgAndPduDsp.registerContextEngineId(
|
snmpEngine.msgAndPduDsp.registerContextEngineId(
|
||||||
snmpContext.contextEngineId, self.pduTypes, self.processPdu
|
snmpContext.contextEngineId, self.SUPPORTED_PDU_TYPES, self.processPdu
|
||||||
)
|
)
|
||||||
self.snmpContext = snmpContext
|
self.snmpContext = snmpContext
|
||||||
self.cbCtx = cbCtx
|
self.cbCtx = cbCtx
|
||||||
self.__pendingReqs = {}
|
self.__pendingReqs = {}
|
||||||
|
|
||||||
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def close(self, snmpEngine):
|
def close(self, snmpEngine):
|
||||||
snmpEngine.msgAndPduDsp.unregisterContextEngineId(
|
snmpEngine.msgAndPduDsp.unregisterContextEngineId(
|
||||||
self.snmpContext.contextEngineId, self.pduTypes
|
self.snmpContext.contextEngineId, self.SUPPORTED_PDU_TYPES
|
||||||
)
|
)
|
||||||
self.snmpContext = self.__pendingReqs = None
|
self.snmpContext = self.__pendingReqs = None
|
||||||
|
|
||||||
|
def releaseStateInformation(self, stateReference):
|
||||||
|
if stateReference in self.__pendingReqs:
|
||||||
|
del self.__pendingReqs[stateReference]
|
||||||
|
|
||||||
def sendVarBinds(self, snmpEngine, stateReference,
|
def sendVarBinds(self, snmpEngine, stateReference,
|
||||||
errorStatus, errorIndex, varBinds):
|
errorStatus, errorIndex, varBinds):
|
||||||
(messageProcessingModel,
|
(messageProcessingModel,
|
||||||
|
@ -105,10 +121,6 @@ class CommandResponderBase(object):
|
||||||
_setRequestType = rfc1905.SetRequestPDU.tagSet
|
_setRequestType = rfc1905.SetRequestPDU.tagSet
|
||||||
_counter64Type = rfc1902.Counter64.tagSet
|
_counter64Type = rfc1902.Counter64.tagSet
|
||||||
|
|
||||||
def releaseStateInformation(self, stateReference):
|
|
||||||
if stateReference in self.__pendingReqs:
|
|
||||||
del self.__pendingReqs[stateReference]
|
|
||||||
|
|
||||||
def processPdu(self, snmpEngine, messageProcessingModel, securityModel,
|
def processPdu(self, snmpEngine, messageProcessingModel, securityModel,
|
||||||
securityName, securityLevel, contextEngineId, contextName,
|
securityName, securityLevel, contextEngineId, contextName,
|
||||||
pduVersion, PDU, maxSizeResponseScopedPDU, stateReference):
|
pduVersion, PDU, maxSizeResponseScopedPDU, stateReference):
|
||||||
|
@ -140,63 +152,11 @@ class CommandResponderBase(object):
|
||||||
|
|
||||||
# 3.2.5
|
# 3.2.5
|
||||||
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
||||||
errorStatus, errorIndex = 'noError', 0
|
|
||||||
|
|
||||||
debug.logger & debug.flagApp and debug.logger(
|
debug.logger & debug.flagApp and debug.logger(
|
||||||
'processPdu: stateReference %s, varBinds %s' % (stateReference, varBinds))
|
'processPdu: stateReference %s, varBinds %s' % (stateReference, varBinds))
|
||||||
|
|
||||||
try:
|
self.initiateMgmtOperation(snmpEngine, stateReference, contextName, PDU)
|
||||||
self.initiateMgmtOperation(snmpEngine, stateReference, contextName, PDU)
|
|
||||||
|
|
||||||
# SNMPv2 SMI exceptions
|
|
||||||
except pysnmp.smi.error.GenError:
|
|
||||||
errorIndication = sys.exc_info()[1]
|
|
||||||
debug.logger & debug.flagApp and debug.logger(
|
|
||||||
'processPdu: stateReference %s, errorIndication %s' % (stateReference, errorIndication))
|
|
||||||
if 'oid' in errorIndication:
|
|
||||||
# Request REPORT generation
|
|
||||||
statusInformation['oid'] = errorIndication['oid']
|
|
||||||
statusInformation['val'] = errorIndication['val']
|
|
||||||
|
|
||||||
# PDU-level SMI errors
|
|
||||||
except pysnmp.smi.error.NoAccessError:
|
|
||||||
errorStatus, errorIndex = 'noAccess', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.WrongTypeError:
|
|
||||||
errorStatus, errorIndex = 'wrongType', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.WrongLengthError:
|
|
||||||
errorStatus, errorIndex = 'wrongLength', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.WrongEncodingError:
|
|
||||||
errorStatus, errorIndex = 'wrongEncoding', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.WrongValueError:
|
|
||||||
errorStatus, errorIndex = 'wrongValue', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.NoCreationError:
|
|
||||||
errorStatus, errorIndex = 'noCreation', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.InconsistentValueError:
|
|
||||||
errorStatus, errorIndex = 'inconsistentValue', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.ResourceUnavailableError:
|
|
||||||
errorStatus, errorIndex = 'resourceUnavailable', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.CommitFailedError:
|
|
||||||
errorStatus, errorIndex = 'commitFailed', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.UndoFailedError:
|
|
||||||
errorStatus, errorIndex = 'undoFailed', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.AuthorizationError:
|
|
||||||
errorStatus, errorIndex = 'authorizationError', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.NotWritableError:
|
|
||||||
errorStatus, errorIndex = 'notWritable', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.InconsistentNameError:
|
|
||||||
errorStatus, errorIndex = 'inconsistentName', sys.exc_info()[1]['idx'] + 1
|
|
||||||
except pysnmp.smi.error.SmiError:
|
|
||||||
errorStatus, errorIndex = 'genErr', len(varBinds) and 1 or 0
|
|
||||||
except pysnmp.error.PySnmpError:
|
|
||||||
self.releaseStateInformation(stateReference)
|
|
||||||
return
|
|
||||||
else: # successful request processor must release state info
|
|
||||||
return
|
|
||||||
|
|
||||||
self.sendVarBinds(snmpEngine, stateReference, errorStatus,
|
|
||||||
errorIndex, varBinds)
|
|
||||||
|
|
||||||
self.releaseStateInformation(stateReference)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def verifyAccess(cls, viewType, varBind, **context):
|
def verifyAccess(cls, viewType, varBind, **context):
|
||||||
|
@ -258,21 +218,86 @@ class CommandResponderBase(object):
|
||||||
# This will cause MibTree to skip this OID-value
|
# This will cause MibTree to skip this OID-value
|
||||||
raise pysnmp.smi.error.NoAccessError(name=name, idx=context.get('idx'))
|
raise pysnmp.smi.error.NoAccessError(name=name, idx=context.get('idx'))
|
||||||
|
|
||||||
|
def _getMgmtFun(self, contextName):
|
||||||
|
return lambda *args, **kwargs: None
|
||||||
|
|
||||||
class GetCommandResponder(CommandResponderBase):
|
def _checkSmiErrors(self, varBinds):
|
||||||
pduTypes = (rfc1905.GetRequestPDU.tagSet,)
|
errorIndication = None
|
||||||
|
errorStatus = errorIndex = 0
|
||||||
|
|
||||||
|
exception = None
|
||||||
|
|
||||||
|
for idx, varBind in enumerate(varBinds):
|
||||||
|
name, value = varBind
|
||||||
|
if isinstance(value, tuple): # expect exception tuple
|
||||||
|
debug.logger & debug.flagApp and debug.logger(
|
||||||
|
'_checkSmiErrors: exception reported for OID %s exception %s' % (name, value))
|
||||||
|
|
||||||
|
if not exception:
|
||||||
|
exception = value
|
||||||
|
|
||||||
|
# reset exception object
|
||||||
|
varBinds[idx] = name, v2c.null
|
||||||
|
|
||||||
|
try:
|
||||||
|
# TODO: perhaps chain exceptions
|
||||||
|
if exception:
|
||||||
|
debug.logger & debug.flagApp and debug.logger(
|
||||||
|
'_checkSmiErrors: re-raising exception %s' % (exception,))
|
||||||
|
raise exception[1].with_traceback(exception[2])
|
||||||
|
|
||||||
|
# SNMPv2 SMI exceptions
|
||||||
|
except pysnmp.smi.error.GenError:
|
||||||
|
errorIndication = sys.exc_info()[1]
|
||||||
|
debug.logger & debug.flagApp and debug.logger(
|
||||||
|
'_checkSmiErrors: errorIndication %s' % (errorIndication,))
|
||||||
|
|
||||||
|
except pysnmp.smi.error.SmiError:
|
||||||
|
exc_type, exc_obj, trb = sys.exc_info()
|
||||||
|
|
||||||
|
errorStatus = self.SMI_ERROR_MAP.get(exc_type, 'genErr')
|
||||||
|
|
||||||
|
try:
|
||||||
|
errorIndex = exc_obj['idx'] + 1
|
||||||
|
|
||||||
|
except IndexError:
|
||||||
|
errorIndex = len(varBinds) and 1 or 0
|
||||||
|
|
||||||
|
return errorIndication, errorStatus, errorIndex
|
||||||
|
|
||||||
def completeMgmtOperation(self, varBinds, **context):
|
def completeMgmtOperation(self, varBinds, **context):
|
||||||
self.sendVarBinds(context['snmpEngine'], context['stateReference'],
|
|
||||||
0, 0, varBinds)
|
|
||||||
self.releaseStateInformation(context['stateReference'])
|
|
||||||
|
|
||||||
# rfc1905: 4.2.1
|
try:
|
||||||
|
(errorIndication,
|
||||||
|
errorStatus, errorIndex) = self._checkSmiErrors(varBinds)
|
||||||
|
|
||||||
|
except pysnmp.error.PySnmpError:
|
||||||
|
self.releaseStateInformation(context['stateReference'])
|
||||||
|
return
|
||||||
|
|
||||||
|
stateReference = context['stateReference']
|
||||||
|
|
||||||
|
if errorIndication:
|
||||||
|
statusInformation = self.__pendingReqs[stateReference]['statusInformation']
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Request REPORT generation
|
||||||
|
statusInformation['oid'] = errorIndication['oid']
|
||||||
|
statusInformation['val'] = errorIndication['val']
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.sendVarBinds(context['snmpEngine'], stateReference,
|
||||||
|
errorStatus, errorIndex, varBinds)
|
||||||
|
|
||||||
|
self.releaseStateInformation(stateReference)
|
||||||
|
|
||||||
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
||||||
# rfc1905: 4.2.1.1
|
|
||||||
mgmtFun = self.snmpContext.getMibInstrum(contextName).readVars
|
|
||||||
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
||||||
|
|
||||||
|
mgmtFun = self._getMgmtFun(contextName)
|
||||||
|
|
||||||
context = dict(snmpEngine=snmpEngine,
|
context = dict(snmpEngine=snmpEngine,
|
||||||
stateReference=stateReference,
|
stateReference=stateReference,
|
||||||
acFun=self.verifyAccess,
|
acFun=self.verifyAccess,
|
||||||
|
@ -282,56 +307,54 @@ class GetCommandResponder(CommandResponderBase):
|
||||||
mgmtFun(*varBinds, **context)
|
mgmtFun(*varBinds, **context)
|
||||||
|
|
||||||
|
|
||||||
|
class GetCommandResponder(CommandResponderBase):
|
||||||
|
SUPPORTED_PDU_TYPES = (rfc1905.GetRequestPDU.tagSet,)
|
||||||
|
|
||||||
|
# rfc1905: 4.2.1
|
||||||
|
def _getMgmtFun(self, contextName):
|
||||||
|
return self.snmpContext.getMibInstrum(contextName).readVars
|
||||||
|
|
||||||
|
|
||||||
class NextCommandResponder(CommandResponderBase):
|
class NextCommandResponder(CommandResponderBase):
|
||||||
pduTypes = (rfc1905.GetNextRequestPDU.tagSet,)
|
SUPPORTED_PDU_TYPES = (rfc1905.GetNextRequestPDU.tagSet,)
|
||||||
|
|
||||||
def completeMgmtOperation(self, varBinds, **context):
|
|
||||||
self.sendVarBinds(context['snmpEngine'], context['stateReference'],
|
|
||||||
0, 0, varBinds)
|
|
||||||
self.releaseStateInformation(context['stateReference'])
|
|
||||||
|
|
||||||
# rfc1905: 4.2.2
|
# rfc1905: 4.2.2
|
||||||
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
def _getMgmtFun(self, contextName):
|
||||||
# rfc1905: 4.2.2.1
|
return self.snmpContext.getMibInstrum(contextName).readNextVars
|
||||||
mgmtFun = self.snmpContext.getMibInstrum(contextName).readNextVars
|
|
||||||
|
|
||||||
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
|
||||||
|
|
||||||
context = dict(snmpEngine=snmpEngine,
|
|
||||||
stateReference=stateReference,
|
|
||||||
acFun=self.verifyAccess,
|
|
||||||
cbFun=self.completeMgmtOperation,
|
|
||||||
cbCtx=self.cbCtx)
|
|
||||||
|
|
||||||
mgmtFun(*varBinds, **context)
|
|
||||||
|
|
||||||
|
|
||||||
class BulkCommandResponder(CommandResponderBase):
|
class BulkCommandResponder(CommandResponderBase):
|
||||||
pduTypes = (rfc1905.GetBulkRequestPDU.tagSet,)
|
SUPPORTED_PDU_TYPES = (rfc1905.GetBulkRequestPDU.tagSet,)
|
||||||
maxVarBinds = 64
|
maxVarBinds = 64
|
||||||
|
|
||||||
|
def _getMgmtFun(self, contextName):
|
||||||
|
return self.snmpContext.getMibInstrum(contextName).readNextVars
|
||||||
|
|
||||||
def _completeNonRepeaters(self, varBinds, **context):
|
def _completeNonRepeaters(self, varBinds, **context):
|
||||||
context['rspVarBinds'][:] = varBinds
|
context['rspVarBinds'][:] = varBinds
|
||||||
context['cbFun'] = self.completeMgmtOperation
|
|
||||||
|
|
||||||
mgmtFun = self.snmpContext.getMibInstrum(context['contextName']).readNextVars
|
if context['counters']['M'] and context['counters']['R']:
|
||||||
|
context['cbFun'] = self.completeMgmtOperation
|
||||||
|
|
||||||
mgmtFun(*context['varBinds'], **context)
|
mgmtFun = self._getMgmtFun(context['contextName'])
|
||||||
|
|
||||||
|
mgmtFun(*context['reqVarBinds'], **context)
|
||||||
|
|
||||||
|
else:
|
||||||
|
CommandResponderBase.completeMgmtOperation(self, context['rspVarBinds'], **context)
|
||||||
|
|
||||||
def completeMgmtOperation(self, varBinds, **context):
|
def completeMgmtOperation(self, varBinds, **context):
|
||||||
context['rspVarBinds'].extend(varBinds)
|
context['rspVarBinds'].extend(varBinds)
|
||||||
context['counters']['M'] -= 1
|
context['counters']['M'] -= 1
|
||||||
|
|
||||||
if context['counters']['M'] and context['counters']['R']:
|
if context['counters']['M'] and context['counters']['R']:
|
||||||
mgmtFun = self.snmpContext.getMibInstrum(context['contextName']).readNextVars
|
mgmtFun = self._getMgmtFun(context['contextName'])
|
||||||
|
|
||||||
context['cbFun'] = self.completeMgmtOperation
|
context['cbFun'] = self.completeMgmtOperation
|
||||||
mgmtFun(*varBinds[-context['counters']['R']:], **context)
|
mgmtFun(*varBinds[-context['counters']['R']:], **context)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.sendVarBinds(context['snmpEngine'], context['stateReference'],
|
CommandResponderBase.completeMgmtOperation(self, context['rspVarBinds'], **context)
|
||||||
0, 0, varBinds)
|
|
||||||
self.releaseStateInformation(context['stateReference'])
|
|
||||||
|
|
||||||
# rfc1905: 4.2.3
|
# rfc1905: 4.2.3
|
||||||
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
||||||
|
@ -355,7 +378,7 @@ class BulkCommandResponder(CommandResponderBase):
|
||||||
debug.logger & debug.flagApp and debug.logger(
|
debug.logger & debug.flagApp and debug.logger(
|
||||||
'initiateMgmtOperation: N %d, M %d, R %d' % (N, M, R))
|
'initiateMgmtOperation: N %d, M %d, R %d' % (N, M, R))
|
||||||
|
|
||||||
mgmtFun = self.snmpContext.getMibInstrum(contextName).readNextVars
|
mgmtFun = self._getMgmtFun(contextName)
|
||||||
|
|
||||||
context = dict(snmpEngine=snmpEngine,
|
context = dict(snmpEngine=snmpEngine,
|
||||||
stateReference=stateReference,
|
stateReference=stateReference,
|
||||||
|
@ -363,7 +386,7 @@ class BulkCommandResponder(CommandResponderBase):
|
||||||
acFun=self.verifyAccess,
|
acFun=self.verifyAccess,
|
||||||
cbFun=self._completeNonRepeaters,
|
cbFun=self._completeNonRepeaters,
|
||||||
cbCtx=self.cbCtx,
|
cbCtx=self.cbCtx,
|
||||||
varBinds=varBinds[-R:],
|
reqVarBinds=varBinds[N:],
|
||||||
counters={'M': M, 'R': R},
|
counters={'M': M, 'R': R},
|
||||||
rspVarBinds=[])
|
rspVarBinds=[])
|
||||||
|
|
||||||
|
@ -371,32 +394,14 @@ class BulkCommandResponder(CommandResponderBase):
|
||||||
|
|
||||||
|
|
||||||
class SetCommandResponder(CommandResponderBase):
|
class SetCommandResponder(CommandResponderBase):
|
||||||
pduTypes = (rfc1905.SetRequestPDU.tagSet,)
|
SUPPORTED_PDU_TYPES = (rfc1905.SetRequestPDU.tagSet,)
|
||||||
|
|
||||||
def completeMgmtOperation(self, varBinds, **context):
|
SMI_ERROR_MAP = CommandResponderBase.SMI_ERROR_MAP.copy()
|
||||||
self.sendVarBinds(context['snmpEngine'], context['stateReference'],
|
|
||||||
0, 0, varBinds)
|
|
||||||
self.releaseStateInformation(context['stateReference'])
|
|
||||||
|
|
||||||
# rfc1905: 4.2.5
|
# turn missing OIDs into access denial
|
||||||
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
SMI_ERROR_MAP[pysnmp.smi.error.NoSuchObjectError] = 'notWritable'
|
||||||
mgmtFun = self.snmpContext.getMibInstrum(contextName).writeVars
|
SMI_ERROR_MAP[pysnmp.smi.error.NoSuchInstanceError] = 'notWritable'
|
||||||
|
|
||||||
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
# rfc1905: 4.2.5.1-13
|
||||||
|
def _getMgmtFun(self, contextName):
|
||||||
context = dict(snmpEngine=snmpEngine,
|
return self.snmpContext.getMibInstrum(contextName).writeVars
|
||||||
stateReference=stateReference,
|
|
||||||
acFun=self.verifyAccess,
|
|
||||||
cbFun=self.completeMgmtOperation,
|
|
||||||
cbCtx=self.cbCtx)
|
|
||||||
|
|
||||||
# rfc1905: 4.2.5.1-13
|
|
||||||
try:
|
|
||||||
mgmtFun(*varBinds, **context)
|
|
||||||
|
|
||||||
except (pysnmp.smi.error.NoSuchObjectError,
|
|
||||||
pysnmp.smi.error.NoSuchInstanceError):
|
|
||||||
instrumError = pysnmp.smi.error.NotWritableError()
|
|
||||||
instrumError.update(sys.exc_info()[1])
|
|
||||||
self.releaseStateInformation(stateReference)
|
|
||||||
raise instrumError
|
|
||||||
|
|
|
@ -14,12 +14,12 @@ from pysnmp import debug
|
||||||
|
|
||||||
# 3.4
|
# 3.4
|
||||||
class NotificationReceiver(object):
|
class NotificationReceiver(object):
|
||||||
pduTypes = (v1.TrapPDU.tagSet, v2c.SNMPv2TrapPDU.tagSet,
|
SUPPORTED_PDU_TYPES = (v1.TrapPDU.tagSet, v2c.SNMPv2TrapPDU.tagSet,
|
||||||
v2c.InformRequestPDU.tagSet)
|
v2c.InformRequestPDU.tagSet)
|
||||||
|
|
||||||
def __init__(self, snmpEngine, cbFun, cbCtx=None):
|
def __init__(self, snmpEngine, cbFun, cbCtx=None):
|
||||||
snmpEngine.msgAndPduDsp.registerContextEngineId(
|
snmpEngine.msgAndPduDsp.registerContextEngineId(
|
||||||
null, self.pduTypes, self.processPdu # '' is a wildcard
|
null, self.SUPPORTED_PDU_TYPES, self.processPdu # '' is a wildcard
|
||||||
)
|
)
|
||||||
|
|
||||||
self.__snmpTrapCommunity = ''
|
self.__snmpTrapCommunity = ''
|
||||||
|
@ -33,7 +33,7 @@ class NotificationReceiver(object):
|
||||||
|
|
||||||
def close(self, snmpEngine):
|
def close(self, snmpEngine):
|
||||||
snmpEngine.msgAndPduDsp.unregisterContextEngineId(
|
snmpEngine.msgAndPduDsp.unregisterContextEngineId(
|
||||||
null, self.pduTypes
|
null, self.SUPPORTED_PDU_TYPES
|
||||||
)
|
)
|
||||||
self.__cbFun = self.__cbCtx = None
|
self.__cbFun = self.__cbCtx = None
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#
|
#
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import functools
|
||||||
|
from pysnmp import nextid
|
||||||
from pysnmp.smi import error
|
from pysnmp.smi import error
|
||||||
from pysnmp import debug
|
from pysnmp import debug
|
||||||
|
|
||||||
|
@ -24,39 +26,59 @@ class AbstractMibInstrumController(object):
|
||||||
|
|
||||||
|
|
||||||
class MibInstrumController(AbstractMibInstrumController):
|
class MibInstrumController(AbstractMibInstrumController):
|
||||||
|
STATUS_OK = 'ok'
|
||||||
|
STATUS_ERROR = 'err'
|
||||||
|
|
||||||
|
STATE_START = 'start'
|
||||||
|
STATE_STOP = 'stop'
|
||||||
|
STATE_ANY = '*'
|
||||||
|
# These states are actually methods of the MIB objects
|
||||||
|
STATE_READ_TEST = 'readTest'
|
||||||
|
STATE_READ_GET = 'readGet'
|
||||||
|
STATE_READ_TEST_NEXT = 'readTestNext'
|
||||||
|
STATE_READ_GET_NEXT = 'readGetNext'
|
||||||
|
STATE_WRITE_TEST = 'writeTest'
|
||||||
|
STATE_WRITE_COMMIT = 'writeCommit'
|
||||||
|
STATE_WRITE_CLEANUP = 'writeCleanup'
|
||||||
|
STATE_WRITE_UNDO = 'writeUndo'
|
||||||
|
|
||||||
fsmReadVar = {
|
fsmReadVar = {
|
||||||
# ( state, status ) -> newState
|
# ( state, status ) -> newState
|
||||||
('start', 'ok'): 'readTest',
|
(STATE_START, STATUS_OK): STATE_READ_TEST,
|
||||||
('readTest', 'ok'): 'readGet',
|
(STATE_READ_TEST, STATUS_OK): STATE_READ_GET,
|
||||||
('readGet', 'ok'): 'stop',
|
(STATE_READ_GET, STATUS_OK): STATE_STOP,
|
||||||
('*', 'err'): 'stop'
|
(STATE_ANY, STATUS_ERROR): STATE_STOP
|
||||||
}
|
}
|
||||||
fsmReadNextVar = {
|
fsmReadNextVar = {
|
||||||
# ( state, status ) -> newState
|
# ( state, status ) -> newState
|
||||||
('start', 'ok'): 'readTestNext',
|
(STATE_START, STATUS_OK): STATE_READ_TEST_NEXT,
|
||||||
('readTestNext', 'ok'): 'readGetNext',
|
(STATE_READ_TEST_NEXT, STATUS_OK): STATE_READ_GET_NEXT,
|
||||||
('readGetNext', 'ok'): 'stop',
|
(STATE_READ_GET_NEXT, STATUS_OK): STATE_STOP,
|
||||||
('*', 'err'): 'stop'
|
(STATE_ANY, STATUS_ERROR): STATE_STOP
|
||||||
}
|
}
|
||||||
fsmWriteVar = {
|
fsmWriteVar = {
|
||||||
# ( state, status ) -> newState
|
# ( state, status ) -> newState
|
||||||
('start', 'ok'): 'writeTest',
|
(STATE_START, STATUS_OK): STATE_WRITE_TEST,
|
||||||
('writeTest', 'ok'): 'writeCommit',
|
(STATE_WRITE_TEST, STATUS_OK): STATE_WRITE_COMMIT,
|
||||||
('writeCommit', 'ok'): 'writeCleanup',
|
(STATE_WRITE_COMMIT, STATUS_OK): STATE_WRITE_CLEANUP,
|
||||||
('writeCleanup', 'ok'): 'readTest',
|
(STATE_WRITE_CLEANUP, STATUS_OK): STATE_READ_TEST,
|
||||||
# Do read after successful write
|
# Do read after successful write
|
||||||
('readTest', 'ok'): 'readGet',
|
(STATE_READ_TEST, STATUS_OK): STATE_READ_GET,
|
||||||
('readGet', 'ok'): 'stop',
|
(STATE_READ_GET, STATUS_OK): STATE_STOP,
|
||||||
# Error handling
|
# Error handling
|
||||||
('writeTest', 'err'): 'writeCleanup',
|
(STATE_WRITE_TEST, STATUS_ERROR): STATE_WRITE_CLEANUP,
|
||||||
('writeCommit', 'err'): 'writeUndo',
|
(STATE_WRITE_COMMIT, STATUS_ERROR): STATE_WRITE_UNDO,
|
||||||
('writeUndo', 'ok'): 'readTest',
|
(STATE_WRITE_UNDO, STATUS_OK): STATE_READ_TEST,
|
||||||
# Ignore read errors (removed columns)
|
# Ignore read errors (removed columns)
|
||||||
('readTest', 'err'): 'stop',
|
(STATE_READ_TEST, STATUS_ERROR): STATE_STOP,
|
||||||
('readGet', 'err'): 'stop',
|
(STATE_READ_GET, STATUS_ERROR): STATE_STOP,
|
||||||
('*', 'err'): 'stop'
|
(STATE_ANY, STATUS_ERROR): STATE_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FSM_CONTEXT = '_fsmContext'
|
||||||
|
|
||||||
|
FSM_SESSION_ID = nextid.Integer(0xffffffff)
|
||||||
|
|
||||||
def __init__(self, mibBuilder):
|
def __init__(self, mibBuilder):
|
||||||
self.mibBuilder = mibBuilder
|
self.mibBuilder = mibBuilder
|
||||||
self.lastBuildId = -1
|
self.lastBuildId = -1
|
||||||
|
@ -183,88 +205,115 @@ class MibInstrumController(AbstractMibInstrumController):
|
||||||
|
|
||||||
# MIB instrumentation
|
# MIB instrumentation
|
||||||
|
|
||||||
def flipFlopFsm(self, fsmTable, *varBinds, **context):
|
def _flipFlopFsmCb(self, varBind, **context):
|
||||||
|
fsmContext = context[self.FSM_CONTEXT]
|
||||||
|
|
||||||
|
varBinds = fsmContext['varBinds']
|
||||||
|
|
||||||
|
idx = context.pop('idx')
|
||||||
|
|
||||||
|
if idx >= 0:
|
||||||
|
fsmContext['count'] += 1
|
||||||
|
|
||||||
|
varBinds[idx] = varBind
|
||||||
|
|
||||||
|
debug.logger & debug.flagIns and debug.logger(
|
||||||
|
'_flipFlopFsmCb: var-bind %d, processed %d, expected %d' % (idx, fsmContext['count'], len(varBinds)))
|
||||||
|
|
||||||
|
if fsmContext['count'] < len(varBinds):
|
||||||
|
return
|
||||||
|
|
||||||
|
debug.logger & debug.flagIns and debug.logger(
|
||||||
|
'_flipFlopFsmCb: finished, output %r' % (varBinds,))
|
||||||
|
|
||||||
|
fsmCallable = fsmContext['fsmCallable']
|
||||||
|
|
||||||
|
fsmCallable(**context)
|
||||||
|
|
||||||
|
def flipFlopFsm(self, fsmTable, *varBinds, **context):
|
||||||
try:
|
try:
|
||||||
fsmContext = context['fsmState']
|
fsmContext = context[self.FSM_CONTEXT]
|
||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.__indexMib()
|
self.__indexMib()
|
||||||
|
|
||||||
fsmContext = context['fsmState'] = dict(varBinds=[], state='start', status='ok')
|
fsmContext = context[self.FSM_CONTEXT] = dict(
|
||||||
|
sessionId=self.FSM_SESSION_ID(),
|
||||||
|
varBinds=list(varBinds[:]),
|
||||||
|
fsmCallable=functools.partial(self.flipFlopFsm, fsmTable, *varBinds),
|
||||||
|
state=self.STATE_START, status=self.STATUS_OK
|
||||||
|
)
|
||||||
|
|
||||||
debug.logger & debug.flagIns and debug.logger('flipFlopFsm: input var-binds %r' % (varBinds,))
|
debug.logger & debug.flagIns and debug.logger('flipFlopFsm: input var-binds %r' % (varBinds,))
|
||||||
|
|
||||||
mibTree, = self.mibBuilder.importSymbols('SNMPv2-SMI', 'iso')
|
mibTree, = self.mibBuilder.importSymbols('SNMPv2-SMI', 'iso')
|
||||||
|
|
||||||
outputVarBinds = fsmContext['varBinds']
|
|
||||||
state = fsmContext['state']
|
state = fsmContext['state']
|
||||||
status = fsmContext['status']
|
status = fsmContext['status']
|
||||||
|
|
||||||
origExc = origTraceback = None
|
debug.logger & debug.flagIns and debug.logger(
|
||||||
|
'flipFlopFsm: current state %s, status %s' % (state, status))
|
||||||
|
|
||||||
|
try:
|
||||||
|
newState = fsmTable[(state, status)]
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
newState = fsmTable[(self.STATE_ANY, status)]
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
raise error.SmiError('Unresolved FSM state %s, %s' % (state, status))
|
||||||
|
|
||||||
|
debug.logger & debug.flagIns and debug.logger(
|
||||||
|
'flipFlopFsm: state %s status %s -> new state %s' % (state, status, newState))
|
||||||
|
|
||||||
|
state = newState
|
||||||
|
|
||||||
|
if state == self.STATE_STOP:
|
||||||
|
context.pop(self.FSM_CONTEXT, None)
|
||||||
|
|
||||||
|
cbFun = context.get('cbFun')
|
||||||
|
if cbFun:
|
||||||
|
varBinds = fsmContext['varBinds']
|
||||||
|
cbFun(varBinds, **context)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
fsmContext.update(state=state, count=0)
|
||||||
|
|
||||||
|
# the case of no var-binds
|
||||||
|
if not varBinds:
|
||||||
|
return self._flipFlopFsmCb(None, idx=-1, **context)
|
||||||
|
|
||||||
|
mgmtFun = getattr(mibTree, state, None)
|
||||||
|
if not mgmtFun:
|
||||||
|
raise error.SmiError(
|
||||||
|
'Unsupported state handler %s at %s' % (state, self)
|
||||||
|
)
|
||||||
|
|
||||||
|
for idx, varBind in enumerate(varBinds):
|
||||||
|
try:
|
||||||
|
# TODO: managed objects to run asynchronously
|
||||||
|
#mgmtFun(varBind, idx=idx, **context)
|
||||||
|
self._flipFlopFsmCb(mgmtFun(varBind, idx=idx, **context), idx=idx, **context)
|
||||||
|
|
||||||
|
except error.SmiError:
|
||||||
|
exc = sys.exc_info()
|
||||||
|
debug.logger & debug.flagIns and debug.logger(
|
||||||
|
'flipFlopFsm: fun %s exception %s for %r with traceback: %s' % (
|
||||||
|
mgmtFun, exc[0], varBind, traceback.format_exception(*exc)))
|
||||||
|
|
||||||
|
varBind = varBind[0], exc
|
||||||
|
|
||||||
|
fsmContext['status'] = self.STATUS_ERROR
|
||||||
|
|
||||||
|
self._flipFlopFsmCb(varBind, idx=idx, **context)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
while True:
|
|
||||||
k = state, status
|
|
||||||
if k in fsmTable:
|
|
||||||
fsmState = fsmTable[k]
|
|
||||||
else:
|
else:
|
||||||
k = '*', status
|
debug.logger & debug.flagIns and debug.logger(
|
||||||
if k in fsmTable:
|
'flipFlopFsm: func %s initiated for %r' % (mgmtFun, varBind))
|
||||||
fsmState = fsmTable[k]
|
|
||||||
else:
|
|
||||||
raise error.SmiError(
|
|
||||||
'Unresolved FSM state %s, %s' % (state, status)
|
|
||||||
)
|
|
||||||
debug.logger & debug.flagIns and debug.logger(
|
|
||||||
'flipFlopFsm: state %s status %s -> fsmState %s' % (state, status, fsmState))
|
|
||||||
state = fsmState
|
|
||||||
status = 'ok'
|
|
||||||
if state == 'stop':
|
|
||||||
break
|
|
||||||
|
|
||||||
for idx, (name, val) in enumerate(varBinds):
|
|
||||||
mgmtFun = getattr(mibTree, state, None)
|
|
||||||
if not mgmtFun:
|
|
||||||
raise error.SmiError(
|
|
||||||
'Unsupported state handler %s at %s' % (state, self)
|
|
||||||
)
|
|
||||||
|
|
||||||
context['idx'] = idx
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Convert to tuple to avoid ObjectName instantiation
|
|
||||||
# on subscription
|
|
||||||
rval = mgmtFun((tuple(name), val), **context)
|
|
||||||
|
|
||||||
except error.SmiError:
|
|
||||||
exc_t, exc_v, exc_tb = sys.exc_info()
|
|
||||||
debug.logger & debug.flagIns and debug.logger(
|
|
||||||
'flipFlopFsm: fun %s exception %s for %s=%r with traceback: %s' % (
|
|
||||||
mgmtFun, exc_t, name, val, traceback.format_exception(exc_t, exc_v, exc_tb)))
|
|
||||||
if origExc is None: # Take the first exception
|
|
||||||
origExc, origTraceback = exc_v, exc_tb
|
|
||||||
status = 'err'
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
debug.logger & debug.flagIns and debug.logger(
|
|
||||||
'flipFlopFsm: fun %s succeeded for %s=%r' % (mgmtFun, name, val))
|
|
||||||
if rval is not None:
|
|
||||||
outputVarBinds.append((rval[0], rval[1]))
|
|
||||||
|
|
||||||
if origExc:
|
|
||||||
if sys.version_info[0] <= 2:
|
|
||||||
raise origExc
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
raise origExc.with_traceback(origTraceback)
|
|
||||||
finally:
|
|
||||||
# Break cycle between locals and traceback object
|
|
||||||
# (seems to be irrelevant on Py3 but just in case)
|
|
||||||
del origTraceback
|
|
||||||
|
|
||||||
cbFun = context.get('cbFun')
|
|
||||||
if cbFun:
|
|
||||||
cbFun(outputVarBinds, **context)
|
|
||||||
|
|
||||||
def readVars(self, *varBinds, **context):
|
def readVars(self, *varBinds, **context):
|
||||||
self.flipFlopFsm(self.fsmReadVar, *varBinds, **context)
|
self.flipFlopFsm(self.fsmReadVar, *varBinds, **context)
|
||||||
|
|
Loading…
Reference in New Issue