summaryrefslogtreecommitdiff
path: root/pysnmp/smi/instrum.py
diff options
context:
space:
mode:
Diffstat (limited to 'pysnmp/smi/instrum.py')
-rw-r--r--pysnmp/smi/instrum.py221
1 files changed, 135 insertions, 86 deletions
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)