summaryrefslogtreecommitdiff
path: root/pysnmp
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2018-10-26 08:20:37 +0200
committerIlya Etingof <etingof@gmail.com>2018-10-26 08:20:37 +0200
commit602c4dd30463b64f42f5ddd70d601ff672f6d0ce (patch)
tree5fdc544da0075123a59c9af1bc2512b284b98bc2 /pysnmp
parent40cfd938c4529043ed3da00b464fc71fc8fd00b3 (diff)
parent0c0d054e8e949cf7645fd3fe26640ee52893f18a (diff)
downloadpysnmp-git-602c4dd30463b64f42f5ddd70d601ff672f6d0ce.tar.gz
Merge branch 'master' of github.com:etingof/pysnmp
Diffstat (limited to 'pysnmp')
-rw-r--r--pysnmp/entity/rfc3413/cmdrsp.py255
-rw-r--r--pysnmp/entity/rfc3413/ntfrcv.py8
-rw-r--r--pysnmp/smi/instrum.py221
3 files changed, 269 insertions, 215 deletions
diff --git a/pysnmp/entity/rfc3413/cmdrsp.py b/pysnmp/entity/rfc3413/cmdrsp.py
index 8f6bbab5..e2d47f38 100644
--- a/pysnmp/entity/rfc3413/cmdrsp.py
+++ b/pysnmp/entity/rfc3413/cmdrsp.py
@@ -15,25 +15,41 @@ from pysnmp import debug
# 3.2
class CommandResponderBase(object):
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):
snmpEngine.msgAndPduDsp.registerContextEngineId(
- snmpContext.contextEngineId, self.pduTypes, self.processPdu
+ snmpContext.contextEngineId, self.SUPPORTED_PDU_TYPES, self.processPdu
)
self.snmpContext = snmpContext
self.cbCtx = cbCtx
self.__pendingReqs = {}
- def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
- pass
-
def close(self, snmpEngine):
snmpEngine.msgAndPduDsp.unregisterContextEngineId(
- self.snmpContext.contextEngineId, self.pduTypes
+ self.snmpContext.contextEngineId, self.SUPPORTED_PDU_TYPES
)
self.snmpContext = self.__pendingReqs = None
+ def releaseStateInformation(self, stateReference):
+ if stateReference in self.__pendingReqs:
+ del self.__pendingReqs[stateReference]
+
def sendVarBinds(self, snmpEngine, stateReference,
errorStatus, errorIndex, varBinds):
(messageProcessingModel,
@@ -105,10 +121,6 @@ class CommandResponderBase(object):
_setRequestType = rfc1905.SetRequestPDU.tagSet
_counter64Type = rfc1902.Counter64.tagSet
- def releaseStateInformation(self, stateReference):
- if stateReference in self.__pendingReqs:
- del self.__pendingReqs[stateReference]
-
def processPdu(self, snmpEngine, messageProcessingModel, securityModel,
securityName, securityLevel, contextEngineId, contextName,
pduVersion, PDU, maxSizeResponseScopedPDU, stateReference):
@@ -140,63 +152,11 @@ class CommandResponderBase(object):
# 3.2.5
varBinds = v2c.apiPDU.getVarBinds(PDU)
- errorStatus, errorIndex = 'noError', 0
debug.logger & debug.flagApp and debug.logger(
'processPdu: stateReference %s, varBinds %s' % (stateReference, varBinds))
- try:
- 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)
+ self.initiateMgmtOperation(snmpEngine, stateReference, contextName, PDU)
@classmethod
def verifyAccess(cls, viewType, varBind, **context):
@@ -258,45 +218,86 @@ class CommandResponderBase(object):
# This will cause MibTree to skip this OID-value
raise pysnmp.smi.error.NoAccessError(name=name, idx=context.get('idx'))
+ def _getMgmtFun(self, contextName):
+ return lambda *args, **kwargs: None
-class GetCommandResponder(CommandResponderBase):
- pduTypes = (rfc1905.GetRequestPDU.tagSet,)
+ def _checkSmiErrors(self, varBinds):
+ errorIndication = None
+ errorStatus = errorIndex = 0
- def completeMgmtOperation(self, varBinds, **context):
- self.sendVarBinds(context['snmpEngine'], context['stateReference'],
- 0, 0, varBinds)
- self.releaseStateInformation(context['stateReference'])
+ exception = None
- # rfc1905: 4.2.1
- def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
- # rfc1905: 4.2.1.1
- mgmtFun = self.snmpContext.getMibInstrum(contextName).readVars
- varBinds = v2c.apiPDU.getVarBinds(PDU)
+ 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))
- context = dict(snmpEngine=snmpEngine,
- stateReference=stateReference,
- acFun=self.verifyAccess,
- cbFun=self.completeMgmtOperation,
- cbCtx=self.cbCtx)
+ if not exception:
+ exception = value
- mgmtFun(*varBinds, **context)
+ # 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])
-class NextCommandResponder(CommandResponderBase):
- pduTypes = (rfc1905.GetNextRequestPDU.tagSet,)
+ # 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):
- self.sendVarBinds(context['snmpEngine'], context['stateReference'],
- 0, 0, varBinds)
- self.releaseStateInformation(context['stateReference'])
- # rfc1905: 4.2.2
- def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
- # rfc1905: 4.2.2.1
- mgmtFun = self.snmpContext.getMibInstrum(contextName).readNextVars
+ 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):
varBinds = v2c.apiPDU.getVarBinds(PDU)
+ mgmtFun = self._getMgmtFun(contextName)
+
context = dict(snmpEngine=snmpEngine,
stateReference=stateReference,
acFun=self.verifyAccess,
@@ -306,32 +307,54 @@ class NextCommandResponder(CommandResponderBase):
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):
+ SUPPORTED_PDU_TYPES = (rfc1905.GetNextRequestPDU.tagSet,)
+
+ # rfc1905: 4.2.2
+ def _getMgmtFun(self, contextName):
+ return self.snmpContext.getMibInstrum(contextName).readNextVars
+
+
class BulkCommandResponder(CommandResponderBase):
- pduTypes = (rfc1905.GetBulkRequestPDU.tagSet,)
+ SUPPORTED_PDU_TYPES = (rfc1905.GetBulkRequestPDU.tagSet,)
maxVarBinds = 64
+ def _getMgmtFun(self, contextName):
+ return self.snmpContext.getMibInstrum(contextName).readNextVars
+
def _completeNonRepeaters(self, varBinds, **context):
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 = self._getMgmtFun(context['contextName'])
+
+ mgmtFun(*context['reqVarBinds'], **context)
- mgmtFun(*context['varBinds'], **context)
+ else:
+ CommandResponderBase.completeMgmtOperation(self, context['rspVarBinds'], **context)
def completeMgmtOperation(self, varBinds, **context):
context['rspVarBinds'].extend(varBinds)
context['counters']['M'] -= 1
if context['counters']['M'] and context['counters']['R']:
- mgmtFun = self.snmpContext.getMibInstrum(context['contextName']).readNextVars
+ mgmtFun = self._getMgmtFun(context['contextName'])
context['cbFun'] = self.completeMgmtOperation
mgmtFun(*varBinds[-context['counters']['R']:], **context)
else:
- self.sendVarBinds(context['snmpEngine'], context['stateReference'],
- 0, 0, varBinds)
- self.releaseStateInformation(context['stateReference'])
+ CommandResponderBase.completeMgmtOperation(self, context['rspVarBinds'], **context)
# rfc1905: 4.2.3
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
@@ -355,7 +378,7 @@ class BulkCommandResponder(CommandResponderBase):
debug.logger & debug.flagApp and debug.logger(
'initiateMgmtOperation: N %d, M %d, R %d' % (N, M, R))
- mgmtFun = self.snmpContext.getMibInstrum(contextName).readNextVars
+ mgmtFun = self._getMgmtFun(contextName)
context = dict(snmpEngine=snmpEngine,
stateReference=stateReference,
@@ -363,7 +386,7 @@ class BulkCommandResponder(CommandResponderBase):
acFun=self.verifyAccess,
cbFun=self._completeNonRepeaters,
cbCtx=self.cbCtx,
- varBinds=varBinds[-R:],
+ reqVarBinds=varBinds[N:],
counters={'M': M, 'R': R},
rspVarBinds=[])
@@ -371,32 +394,14 @@ class BulkCommandResponder(CommandResponderBase):
class SetCommandResponder(CommandResponderBase):
- pduTypes = (rfc1905.SetRequestPDU.tagSet,)
-
- def completeMgmtOperation(self, varBinds, **context):
- self.sendVarBinds(context['snmpEngine'], context['stateReference'],
- 0, 0, varBinds)
- self.releaseStateInformation(context['stateReference'])
+ SUPPORTED_PDU_TYPES = (rfc1905.SetRequestPDU.tagSet,)
- # rfc1905: 4.2.5
- def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
- mgmtFun = self.snmpContext.getMibInstrum(contextName).writeVars
-
- varBinds = v2c.apiPDU.getVarBinds(PDU)
+ SMI_ERROR_MAP = CommandResponderBase.SMI_ERROR_MAP.copy()
- context = dict(snmpEngine=snmpEngine,
- stateReference=stateReference,
- acFun=self.verifyAccess,
- cbFun=self.completeMgmtOperation,
- cbCtx=self.cbCtx)
+ # turn missing OIDs into access denial
+ SMI_ERROR_MAP[pysnmp.smi.error.NoSuchObjectError] = 'notWritable'
+ SMI_ERROR_MAP[pysnmp.smi.error.NoSuchInstanceError] = 'notWritable'
- # 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
+ # rfc1905: 4.2.5.1-13
+ def _getMgmtFun(self, contextName):
+ return self.snmpContext.getMibInstrum(contextName).writeVars
diff --git a/pysnmp/entity/rfc3413/ntfrcv.py b/pysnmp/entity/rfc3413/ntfrcv.py
index 34192ad3..df394a0c 100644
--- a/pysnmp/entity/rfc3413/ntfrcv.py
+++ b/pysnmp/entity/rfc3413/ntfrcv.py
@@ -14,12 +14,12 @@ from pysnmp import debug
# 3.4
class NotificationReceiver(object):
- pduTypes = (v1.TrapPDU.tagSet, v2c.SNMPv2TrapPDU.tagSet,
- v2c.InformRequestPDU.tagSet)
+ SUPPORTED_PDU_TYPES = (v1.TrapPDU.tagSet, v2c.SNMPv2TrapPDU.tagSet,
+ v2c.InformRequestPDU.tagSet)
def __init__(self, snmpEngine, cbFun, cbCtx=None):
snmpEngine.msgAndPduDsp.registerContextEngineId(
- null, self.pduTypes, self.processPdu # '' is a wildcard
+ null, self.SUPPORTED_PDU_TYPES, self.processPdu # '' is a wildcard
)
self.__snmpTrapCommunity = ''
@@ -33,7 +33,7 @@ class NotificationReceiver(object):
def close(self, snmpEngine):
snmpEngine.msgAndPduDsp.unregisterContextEngineId(
- null, self.pduTypes
+ null, self.SUPPORTED_PDU_TYPES
)
self.__cbFun = self.__cbCtx = None
diff --git a/pysnmp/smi/instrum.py b/pysnmp/smi/instrum.py
index d84493ac..cec737d6 100644
--- a/pysnmp/smi/instrum.py
+++ b/pysnmp/smi/instrum.py
@@ -6,6 +6,8 @@
#
import sys
import traceback
+import functools
+from pysnmp import nextid
from pysnmp.smi import error
from pysnmp import debug
@@ -24,39 +26,59 @@ class AbstractMibInstrumController(object):
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 = {
# ( state, status ) -> newState
- ('start', 'ok'): 'readTest',
- ('readTest', 'ok'): 'readGet',
- ('readGet', 'ok'): 'stop',
- ('*', 'err'): 'stop'
+ (STATE_START, STATUS_OK): STATE_READ_TEST,
+ (STATE_READ_TEST, STATUS_OK): STATE_READ_GET,
+ (STATE_READ_GET, STATUS_OK): STATE_STOP,
+ (STATE_ANY, STATUS_ERROR): STATE_STOP
}
fsmReadNextVar = {
# ( state, status ) -> newState
- ('start', 'ok'): 'readTestNext',
- ('readTestNext', 'ok'): 'readGetNext',
- ('readGetNext', 'ok'): 'stop',
- ('*', 'err'): 'stop'
+ (STATE_START, STATUS_OK): STATE_READ_TEST_NEXT,
+ (STATE_READ_TEST_NEXT, STATUS_OK): STATE_READ_GET_NEXT,
+ (STATE_READ_GET_NEXT, STATUS_OK): STATE_STOP,
+ (STATE_ANY, STATUS_ERROR): STATE_STOP
}
fsmWriteVar = {
# ( state, status ) -> newState
- ('start', 'ok'): 'writeTest',
- ('writeTest', 'ok'): 'writeCommit',
- ('writeCommit', 'ok'): 'writeCleanup',
- ('writeCleanup', 'ok'): 'readTest',
+ (STATE_START, STATUS_OK): STATE_WRITE_TEST,
+ (STATE_WRITE_TEST, STATUS_OK): STATE_WRITE_COMMIT,
+ (STATE_WRITE_COMMIT, STATUS_OK): STATE_WRITE_CLEANUP,
+ (STATE_WRITE_CLEANUP, STATUS_OK): STATE_READ_TEST,
# Do read after successful write
- ('readTest', 'ok'): 'readGet',
- ('readGet', 'ok'): 'stop',
+ (STATE_READ_TEST, STATUS_OK): STATE_READ_GET,
+ (STATE_READ_GET, STATUS_OK): STATE_STOP,
# Error handling
- ('writeTest', 'err'): 'writeCleanup',
- ('writeCommit', 'err'): 'writeUndo',
- ('writeUndo', 'ok'): 'readTest',
+ (STATE_WRITE_TEST, STATUS_ERROR): STATE_WRITE_CLEANUP,
+ (STATE_WRITE_COMMIT, STATUS_ERROR): STATE_WRITE_UNDO,
+ (STATE_WRITE_UNDO, STATUS_OK): STATE_READ_TEST,
# Ignore read errors (removed columns)
- ('readTest', 'err'): 'stop',
- ('readGet', 'err'): 'stop',
- ('*', 'err'): 'stop'
+ (STATE_READ_TEST, STATUS_ERROR): STATE_STOP,
+ (STATE_READ_GET, STATUS_ERROR): STATE_STOP,
+ (STATE_ANY, STATUS_ERROR): STATE_STOP
}
+ FSM_CONTEXT = '_fsmContext'
+
+ FSM_SESSION_ID = nextid.Integer(0xffffffff)
+
def __init__(self, mibBuilder):
self.mibBuilder = mibBuilder
self.lastBuildId = -1
@@ -183,88 +205,115 @@ class MibInstrumController(AbstractMibInstrumController):
# 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:
- fsmContext = context['fsmState']
+ fsmContext = context[self.FSM_CONTEXT]
except KeyError:
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,))
mibTree, = self.mibBuilder.importSymbols('SNMPv2-SMI', 'iso')
- outputVarBinds = fsmContext['varBinds']
state = fsmContext['state']
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:
- k = '*', status
- if k in fsmTable:
- 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)
+ debug.logger & debug.flagIns and debug.logger(
+ 'flipFlopFsm: func %s initiated for %r' % (mgmtFun, varBind))
def readVars(self, *varBinds, **context):
self.flipFlopFsm(self.fsmReadVar, *varBinds, **context)