[WIP] Change managed objects to run asynchronously
The `read*`, `write*`, `create*` and `destroy*` methods of the `SNMPv2-SMI` MIB objects redesigned to return immediately and deliver their results via a call back. This change makes it possible to work with the data that managed objects represent through SNMP in an asynchronous manner. This change breaks backward compatibility.async-managed-objects
parent
9ebd0c0fab
commit
f7c62633ab
|
@ -29,6 +29,10 @@ Revision 5.0.0, released 2018-10-??
|
|||
instrumentation controller return immediately and deliver their
|
||||
results via a call back.
|
||||
|
||||
* The `read*`, `write*`, `create*` and `destroy*` methods of
|
||||
the `SNMPv2-SMI` MIB objects redesigned to return immediately
|
||||
and deliver their results via a call back.
|
||||
|
||||
* CommandResponder application passes `snmpEngine` and optionally
|
||||
user-supplied `cbCtx` object throughout the MIB instrumentation
|
||||
methods. The goal is to let MIB objects access/modify whatever
|
||||
|
|
|
@ -29,8 +29,10 @@ sysLocation, = mibBuilder.importSymbols('SNMPv2-MIB', 'sysLocation')
|
|||
class MySysLocationInstance(MibScalarInstance):
|
||||
# noinspection PyUnusedLocal
|
||||
def readGet(self, varBind, **context):
|
||||
cbFun = context['cbFun']
|
||||
|
||||
# Just return a custom value
|
||||
return varBind[0], self.syntax.clone('The Leaky Cauldron')
|
||||
cbFun((varBind[0], self.syntax.clone('The Leaky Cauldron')), **context)
|
||||
|
||||
|
||||
sysLocationInstance = MySysLocationInstance(
|
||||
|
@ -51,23 +53,22 @@ if __name__ == '__main__':
|
|||
|
||||
mibInstrum = instrum.MibInstrumController(mibBuilder)
|
||||
|
||||
|
||||
def cbFun(varBinds, **context):
|
||||
for oid, val in varBinds:
|
||||
if exval.endOfMib.isSameTypeWith(val):
|
||||
context['state']['stop'] = True
|
||||
context['app']['stop'] = True
|
||||
print('%s = %s' % ('.'.join([str(x) for x in oid]), not val.isValue and 'N/A' or val.prettyPrint()))
|
||||
|
||||
context['state']['varBinds'] = varBinds
|
||||
context['app']['varBinds'] = varBinds
|
||||
|
||||
context = {
|
||||
'cbFun': cbFun,
|
||||
'state': {
|
||||
'varBinds': [((1, 3, 6), None)],
|
||||
'stop': False
|
||||
}
|
||||
|
||||
app_context = {
|
||||
'varBinds': [((1, 3, 6), None)],
|
||||
'stop': False
|
||||
}
|
||||
|
||||
print('Remote manager read access to MIB instrumentation (table walk)')
|
||||
while not context['state']['stop']:
|
||||
mibInstrum.readNextVars(*context['state']['varBinds'], **context)
|
||||
while not app_context['stop']:
|
||||
mibInstrum.readNextVars(*app_context['varBinds'], cbFun=cbFun, app=app_context)
|
||||
print('done')
|
||||
|
|
|
@ -8,6 +8,9 @@ its MIB possibly triggered by SNMP Manager's commands.
|
|||
"""#
|
||||
# SNMP agent backend e.g. Agent access to Managed Objects
|
||||
from pysnmp.smi import builder, instrum, exval
|
||||
from pysnmp import debug
|
||||
|
||||
#debug.setLogger(debug.Debug('all'))
|
||||
|
||||
print('Loading MIB modules...'),
|
||||
mibBuilder = builder.MibBuilder().loadModules(
|
||||
|
@ -31,6 +34,7 @@ def cbFun(varBinds, **context):
|
|||
for oid, val in varBinds:
|
||||
print('%s = %s' % ('.'.join([str(x) for x in oid]), not val.isValue and 'N/A' or val.prettyPrint()))
|
||||
|
||||
|
||||
print('Create/update SNMP-COMMUNITY-MIB::snmpCommunityEntry table row: ')
|
||||
mibInstrum.writeVars(
|
||||
(snmpCommunityEntry.name + (2,) + instanceId, 'mycomm'),
|
||||
|
@ -44,22 +48,20 @@ print('done')
|
|||
def cbFun(varBinds, **context):
|
||||
for oid, val in varBinds:
|
||||
if exval.endOfMib.isSameTypeWith(val):
|
||||
context['state']['stop'] = True
|
||||
context['app']['stop'] = True
|
||||
print('%s = %s' % ('.'.join([str(x) for x in oid]), not val.isValue and 'N/A' or val.prettyPrint()))
|
||||
|
||||
context['state']['varBinds'] = varBinds
|
||||
context['app']['varBinds'] = varBinds
|
||||
|
||||
context = {
|
||||
'cbFun': cbFun,
|
||||
'state': {
|
||||
'varBinds': [((1, 3, 6), None)],
|
||||
'stop': False
|
||||
}
|
||||
|
||||
app_context = {
|
||||
'varBinds': [((1, 3, 6), None)],
|
||||
'stop': False
|
||||
}
|
||||
|
||||
print('Read whole MIB (table walk)')
|
||||
while not context['state']['stop']:
|
||||
mibInstrum.readNextVars(*context['state']['varBinds'], **context)
|
||||
while not app_context['stop']:
|
||||
mibInstrum.readNextVars(*app_context['varBinds'], cbFun=cbFun, app=app_context)
|
||||
print('done')
|
||||
|
||||
print('Unloading MIB modules...'),
|
||||
|
|
|
@ -263,7 +263,7 @@ class MibBuilder(object):
|
|||
self.mibSymbols = {}
|
||||
self.__mibSources = []
|
||||
self.__modSeen = {}
|
||||
self.__modPathsSeen = set()
|
||||
self.__modPathsSeen = {}
|
||||
self.__mibCompiler = None
|
||||
self.setMibSources(*sources)
|
||||
|
||||
|
@ -306,7 +306,7 @@ class MibBuilder(object):
|
|||
debug.logger & debug.flagBld and debug.logger('loadModule: seen %s' % modPath)
|
||||
break
|
||||
else:
|
||||
self.__modPathsSeen.add(modPath)
|
||||
self.__modPathsSeen[modPath] = True
|
||||
|
||||
debug.logger & debug.flagBld and debug.logger('loadModule: evaluating %s' % modPath)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import sys
|
|||
import traceback
|
||||
import functools
|
||||
from pysnmp import nextid
|
||||
from pysnmp.proto import rfc1905
|
||||
from pysnmp.smi import error
|
||||
from pysnmp import debug
|
||||
|
||||
|
@ -205,51 +206,70 @@ class MibInstrumController(AbstractMibInstrumController):
|
|||
|
||||
# MIB instrumentation
|
||||
|
||||
def _flipFlopFsmCb(self, varBind, **context):
|
||||
fsmContext = context[self.FSM_CONTEXT]
|
||||
def flipFlopFsm(self, fsmTable, *varBinds, **context):
|
||||
|
||||
varBinds = fsmContext['varBinds']
|
||||
count = [0]
|
||||
|
||||
idx = context.pop('idx')
|
||||
cbFun = context['cbFun']
|
||||
|
||||
if idx >= 0:
|
||||
fsmContext['count'] += 1
|
||||
def _cbFun(varBind, **context):
|
||||
idx = context.pop('idx', None)
|
||||
|
||||
varBinds[idx] = varBind
|
||||
_varBinds = context['varBinds']
|
||||
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'_flipFlopFsmCb: var-bind %d, processed %d, expected %d' % (idx, fsmContext['count'], len(varBinds)))
|
||||
name, value = varBind
|
||||
|
||||
if fsmContext['count'] < len(varBinds):
|
||||
# Watch for possible exception tuple
|
||||
if isinstance(value, tuple):
|
||||
|
||||
exc_type, exc_value, traceback = value
|
||||
|
||||
if isinstance(exc_value, error.NoSuchObjectError):
|
||||
value = rfc1905.noSuchObject
|
||||
|
||||
elif isinstance(exc_value, error.NoSuchInstanceError):
|
||||
value = rfc1905.noSuchOInstance
|
||||
|
||||
elif isinstance(exc_value, error.EndOfMibViewError):
|
||||
value = rfc1905.endOfMibView
|
||||
|
||||
elif isinstance(exc_value, Exception):
|
||||
raise value
|
||||
|
||||
_varBinds[idx] = name, value
|
||||
|
||||
if idx is None:
|
||||
cbFun(_varBinds, **context)
|
||||
return
|
||||
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'_flipFlopFsmCb: finished, output %r' % (varBinds,))
|
||||
count[0] += 1
|
||||
|
||||
fsmCallable = fsmContext['fsmCallable']
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'_cbFun: var-bind %d, processed %d, expected %d' % (
|
||||
idx, count[0], len(varBinds)))
|
||||
|
||||
fsmCallable(**context)
|
||||
if count[0] < len(varBinds):
|
||||
return
|
||||
|
||||
def flipFlopFsm(self, fsmTable, *varBinds, **context):
|
||||
try:
|
||||
fsmContext = context[self.FSM_CONTEXT]
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'_cbFun: finished, output var-binds %r' % (_varBinds,))
|
||||
|
||||
except KeyError:
|
||||
self.__indexMib()
|
||||
self.flipFlopFsm(fsmTable, *varBinds, **dict(context, cbFun=cbFun))
|
||||
|
||||
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')
|
||||
|
||||
state = fsmContext['state']
|
||||
status = fsmContext['status']
|
||||
try:
|
||||
state = context['state']
|
||||
status = context['status']
|
||||
_varBinds = context['varBinds']
|
||||
|
||||
except KeyError:
|
||||
state, status = self.STATE_START, self.STATUS_OK
|
||||
_varBinds = list(varBinds)
|
||||
|
||||
self.__indexMib()
|
||||
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'flipFlopFsm: current state %s, status %s' % (state, status))
|
||||
|
@ -271,19 +291,18 @@ class MibInstrumController(AbstractMibInstrumController):
|
|||
|
||||
if state == self.STATE_STOP:
|
||||
context.pop(self.FSM_CONTEXT, None)
|
||||
|
||||
cbFun = context.get('cbFun')
|
||||
context.pop('state', None)
|
||||
context.pop('status', None)
|
||||
context.pop('varBinds', None)
|
||||
context.pop('oName', None)
|
||||
if cbFun:
|
||||
varBinds = fsmContext['varBinds']
|
||||
cbFun(varBinds, **context)
|
||||
|
||||
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)
|
||||
if cbFun and not varBinds:
|
||||
_cbFun(None, **context)
|
||||
return
|
||||
|
||||
mgmtFun = getattr(mibTree, state, None)
|
||||
if not mgmtFun:
|
||||
|
@ -292,22 +311,20 @@ class MibInstrumController(AbstractMibInstrumController):
|
|||
)
|
||||
|
||||
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)
|
||||
mgmtFun(varBind, idx=idx, **dict(context, cbFun=_cbFun, state=state, status=status, varBinds=_varBinds, oName=None))
|
||||
|
||||
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)
|
||||
_cbFun(varBind, idx=idx, **dict(context, status=self.STATUS_ERROR))
|
||||
|
||||
return
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ Unsigned32 = rfc1902.Unsigned32
|
|||
TimeTicks = rfc1902.TimeTicks
|
||||
Opaque = rfc1902.Opaque
|
||||
Counter64 = rfc1902.Counter64
|
||||
Null = rfc1902.Null
|
||||
|
||||
|
||||
class ExtUTCTime(OctetString):
|
||||
|
@ -337,6 +338,8 @@ class MibTree(ObjectType):
|
|||
branchVersionId = 0 # cnanges on tree structure change
|
||||
maxAccess = 'not-accessible'
|
||||
|
||||
_null = Null()
|
||||
|
||||
def __init__(self, name, syntax=None):
|
||||
ObjectType.__init__(self, name, syntax)
|
||||
self._vars = OidOrderedDict()
|
||||
|
@ -423,18 +426,23 @@ class MibTree(ObjectType):
|
|||
def readTest(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
if name == self.name:
|
||||
acFun = context.get('acFun')
|
||||
if acFun:
|
||||
if (self.maxAccess not in ('readonly', 'readwrite', 'readcreate') or
|
||||
acFun('read', (name, self.syntax), **context)):
|
||||
raise error.NoAccessError(name=name, idx=context.get('idx'))
|
||||
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
try:
|
||||
node = self.getBranch(name, **context)
|
||||
|
||||
except (error.NoSuchInstanceError, error.NoSuchObjectError):
|
||||
return # missing object is not an error here
|
||||
cbFun((name, self._null), **context)
|
||||
|
||||
else:
|
||||
node.readTest(varBind, **context)
|
||||
|
@ -442,14 +450,16 @@ class MibTree(ObjectType):
|
|||
def readGet(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
try:
|
||||
node = self.getBranch(name, **context)
|
||||
|
||||
except (error.NoSuchInstanceError, error.NoSuchObjectError):
|
||||
return name, exval.noSuchObject
|
||||
cbFun((name, exval.noSuchObject), **context)
|
||||
|
||||
else:
|
||||
return node.readGet(varBind, **context)
|
||||
node.readGet(varBind, **context)
|
||||
|
||||
# Read next operation is subtree-specific
|
||||
|
||||
|
@ -465,6 +475,8 @@ class MibTree(ObjectType):
|
|||
nextName = name
|
||||
direction = self.depthFirst
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
while True: # NOTE(etingof): linear search here
|
||||
if direction == self.depthFirst:
|
||||
direction = self.breadthFirst
|
||||
|
@ -480,14 +492,17 @@ class MibTree(ObjectType):
|
|||
|
||||
except (error.NoSuchInstanceError, error.NoSuchObjectError):
|
||||
if topOfTheMib:
|
||||
cbFun((name, exval.endOfMib), **context)
|
||||
return
|
||||
|
||||
raise
|
||||
|
||||
direction = self.depthFirst
|
||||
nextName = node.name
|
||||
|
||||
try:
|
||||
return node.readTestNext(varBind, **context)
|
||||
node.readTestNext(varBind, **context)
|
||||
return
|
||||
|
||||
except (error.NoAccessError, error.NoSuchInstanceError, error.NoSuchObjectError):
|
||||
pass
|
||||
|
@ -502,6 +517,8 @@ class MibTree(ObjectType):
|
|||
nextName = name
|
||||
direction = self.depthFirst
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
while True: # NOTE(etingof): linear search ahead!
|
||||
if direction == self.depthFirst:
|
||||
direction = self.breadthFirst
|
||||
|
@ -517,14 +534,17 @@ class MibTree(ObjectType):
|
|||
|
||||
except (error.NoSuchInstanceError, error.NoSuchObjectError):
|
||||
if topOfTheMib:
|
||||
return name, exval.endOfMib
|
||||
cbFun((name, exval.endOfMib), **context)
|
||||
return
|
||||
|
||||
raise
|
||||
|
||||
direction = self.depthFirst
|
||||
nextName = node.name
|
||||
|
||||
try:
|
||||
return node.readGetNext((nextName, val), **context)
|
||||
node.readGetNext((nextName, val), **context)
|
||||
return
|
||||
|
||||
except (error.NoAccessError, error.NoSuchInstanceError, error.NoSuchObjectError):
|
||||
pass
|
||||
|
@ -541,6 +561,10 @@ class MibTree(ObjectType):
|
|||
if (self.maxAccess not in ('readwrite', 'readcreate') or
|
||||
acFun('write', (name, self.syntax), **context)):
|
||||
raise error.NotWritableError(name=name, idx=context.get('idx'))
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
node = self.getBranch(name, **context)
|
||||
node.writeTest(varBind, **context)
|
||||
|
@ -629,10 +653,11 @@ class MibScalar(MibTree):
|
|||
node = self.getBranch(name, **context)
|
||||
|
||||
except error.NoSuchInstanceError:
|
||||
return name, exval.noSuchInstance
|
||||
cbFun = context['cbFun']
|
||||
cbFun((name, exval.noSuchInstance), **context)
|
||||
|
||||
else:
|
||||
return node.readGet(varBind, **context)
|
||||
node.readGet(varBind, **context)
|
||||
|
||||
def readTestNext(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
@ -657,7 +682,7 @@ class MibScalar(MibTree):
|
|||
acFun('read', (name, self.syntax), **context)):
|
||||
raise error.NoAccessError(name=name, idx=context.get('idx'))
|
||||
|
||||
return MibTree.readGetNext(self, varBind, **context)
|
||||
MibTree.readGetNext(self, varBind, **context)
|
||||
|
||||
# Two-phase commit implementation
|
||||
|
||||
|
@ -752,13 +777,19 @@ class MibScalarInstance(MibTree):
|
|||
if name != self.name:
|
||||
raise error.NoSuchInstanceError(name=name, idx=context.get('idx'))
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
def readGet(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
# Return current variable (name, value)
|
||||
if name == self.name:
|
||||
debug.logger & debug.flagIns and debug.logger('readGet: %s=%r' % (self.name, self.syntax))
|
||||
return self.name, self.getValue(name, **context)
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.getValue(name, **context)), **context)
|
||||
|
||||
else:
|
||||
raise error.NoSuchInstanceError(name=name, idx=context.get('idx'))
|
||||
|
||||
|
@ -770,6 +801,9 @@ class MibScalarInstance(MibTree):
|
|||
if name != self.name or name <= oName:
|
||||
raise error.NoSuchInstanceError(name=name, idx=context.get('idx'))
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
def readGetNext(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
|
@ -777,7 +811,7 @@ class MibScalarInstance(MibTree):
|
|||
|
||||
if name == self.name and name > oName:
|
||||
debug.logger & debug.flagIns and debug.logger('readGetNext: %s=%r' % (self.name, self.syntax))
|
||||
return self.readGet(varBind, **context)
|
||||
self.readGet(varBind, **context)
|
||||
|
||||
else:
|
||||
raise error.NoSuchInstanceError(name=name, idx=context.get('idx'))
|
||||
|
@ -801,6 +835,10 @@ class MibScalarInstance(MibTree):
|
|||
raise why
|
||||
else:
|
||||
raise error.WrongValueError(name=name, idx=context.get('idx'), msg=sys.exc_info()[1])
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
raise error.NoSuchInstanceError(name=name, idx=context.get('idx'))
|
||||
|
||||
|
@ -811,19 +849,28 @@ class MibScalarInstance(MibTree):
|
|||
# Commit new value
|
||||
self.syntax = self.__newSyntax
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def writeCleanup(self, varBind, **context):
|
||||
self.branchVersionId += 1
|
||||
debug.logger & debug.flagIns and debug.logger('writeCleanup: %s=%r' % (name, val))
|
||||
debug.logger & debug.flagIns and debug.logger('writeCleanup: %s=%r' % varBind)
|
||||
# Drop previous value
|
||||
self.__newSyntax = self.__oldSyntax = None
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def writeUndo(self, varBind, **context):
|
||||
# Revive previous value
|
||||
self.syntax = self.__oldSyntax
|
||||
self.__newSyntax = self.__oldSyntax = None
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
# Table column instance specifics
|
||||
|
||||
# Create operation
|
||||
|
@ -841,15 +888,24 @@ class MibScalarInstance(MibTree):
|
|||
why = sys.exc_info()[1]
|
||||
if 'syntax' in why:
|
||||
self.__newSyntax = why['syntax']
|
||||
|
||||
else:
|
||||
raise error.WrongValueError(name=name, idx=context.get('idx'), msg=sys.exc_info()[1])
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
raise error.NoSuchInstanceError(name=name, idx=context.get('idx'))
|
||||
|
||||
def createCommit(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
if val is not None:
|
||||
if val is None:
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
self.writeCommit(varBind, **context)
|
||||
|
||||
def createCleanup(self, varBind, **context):
|
||||
|
@ -858,13 +914,21 @@ class MibScalarInstance(MibTree):
|
|||
|
||||
debug.logger & debug.flagIns and debug.logger('createCleanup: %s=%r' % (name, val))
|
||||
|
||||
if val is not None:
|
||||
if val is None:
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
self.writeCleanup(varBind, **context)
|
||||
|
||||
def createUndo(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
if val is not None:
|
||||
if val is None:
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
self.writeUndo(varBind, **context)
|
||||
|
||||
# Destroy operation
|
||||
|
@ -882,18 +946,27 @@ class MibScalarInstance(MibTree):
|
|||
why = sys.exc_info()[1]
|
||||
if 'syntax' in why:
|
||||
self.__newSyntax = why['syntax']
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
else:
|
||||
raise error.NoSuchInstanceError(name=name, idx=context.get('idx'))
|
||||
|
||||
def destroyCommit(self, varBind, **context):
|
||||
pass
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def destroyCleanup(self, varBind, **context):
|
||||
self.branchVersionId += 1
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
def destroyUndo(self, varBind, **context):
|
||||
pass
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
|
||||
# Conceptual table classes
|
||||
|
@ -923,7 +996,7 @@ class MibTableColumn(MibScalar):
|
|||
self.protoInstance = protoInstance
|
||||
|
||||
# Column creation (this should probably be converted into some state
|
||||
# machine for clarity). Also, it might be a good idea to inidicate
|
||||
# machine for clarity). Also, it might be a good idea to indicate
|
||||
# defaulted cols creation in a clearer way than just a val == None.
|
||||
|
||||
def createTest(self, varBind, **context):
|
||||
|
@ -945,6 +1018,8 @@ class MibTableColumn(MibScalar):
|
|||
# Create instances if either it does not yet exist (row creation)
|
||||
# or a value is passed (multiple OIDs in SET PDU)
|
||||
if val is None and name in self.__createdInstances:
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
return
|
||||
|
||||
self.__createdInstances[name] = self.protoInstance(
|
||||
|
@ -956,16 +1031,25 @@ class MibTableColumn(MibScalar):
|
|||
def createCommit(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
# Commit new instance value
|
||||
if name in self._vars: # XXX
|
||||
if name in self.__createdInstances:
|
||||
self._vars[name].createCommit(varBind, **context)
|
||||
return
|
||||
return
|
||||
|
||||
self.__createdInstances[name].createCommit(varBind, **context)
|
||||
else:
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
return
|
||||
|
||||
# ...commit new column instance
|
||||
self._vars[name], self.__createdInstances[name] = self.__createdInstances[name], self._vars.get(name)
|
||||
def _cbFun(varBind, **context):
|
||||
# ...commit new column instance
|
||||
self._vars[name], self.__createdInstances[name] = self.__createdInstances[name], self._vars.get(name)
|
||||
|
||||
cbFun(varBind, **context)
|
||||
|
||||
self.__createdInstances[name].createCommit(varBind, **dict(context, cbFun=_cbFun))
|
||||
|
||||
def createCleanup(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
@ -979,6 +1063,10 @@ class MibTableColumn(MibScalar):
|
|||
|
||||
elif name in self._vars:
|
||||
self._vars[name].createCleanup(varBind, **context)
|
||||
return
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
def createUndo(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
@ -1001,6 +1089,10 @@ class MibTableColumn(MibScalar):
|
|||
|
||||
else:
|
||||
self._vars[name].createUndo(varBind, **context)
|
||||
return
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
# Column destruction
|
||||
|
||||
|
@ -1012,6 +1104,8 @@ class MibTableColumn(MibScalar):
|
|||
raise error.NoAccessError(name=name, idx=context.get('idx'))
|
||||
|
||||
if name not in self._vars:
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
return
|
||||
|
||||
acFun = context.get('acFun')
|
||||
|
@ -1031,6 +1125,9 @@ class MibTableColumn(MibScalar):
|
|||
self.__destroyedInstances[name] = self._vars[name]
|
||||
del self._vars[name]
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
def destroyCleanup(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
|
@ -1042,6 +1139,9 @@ class MibTableColumn(MibScalar):
|
|||
debug.logger & debug.flagIns and debug.logger('destroyCleanup: %s=%r' % (name, val))
|
||||
del self.__destroyedInstances[name]
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
def destroyUndo(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
|
@ -1051,11 +1151,24 @@ class MibTableColumn(MibScalar):
|
|||
self._vars[name].destroyUndo(varBind, **context)
|
||||
del self.__destroyedInstances[name]
|
||||
|
||||
cbFun = context['cbFun']
|
||||
cbFun((self.name, self.syntax), **context)
|
||||
|
||||
# Set/modify column
|
||||
|
||||
def writeTest(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
def _cbFun(varBind, **context):
|
||||
if name in self.__rowOpWanted:
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'%s flagged by %s=%r, exception %s' % (self.__rowOpWanted[name], name, val, sys.exc_info()[1]))
|
||||
raise self.__rowOpWanted[name]
|
||||
|
||||
cbFun(varBind, **context)
|
||||
|
||||
# Besides common checks, request row creation on no-instance
|
||||
try:
|
||||
# First try the instance
|
||||
|
@ -1068,65 +1181,55 @@ class MibTableColumn(MibScalar):
|
|||
self.__rowOpWanted[name] = excValue
|
||||
else:
|
||||
self.__rowOpWanted[name] = error.RowCreationWanted()
|
||||
self.createTest(varBind, **context)
|
||||
|
||||
self.createTest(varBind, **dict(context, cbFun=_cbFun))
|
||||
|
||||
except error.RowDestructionWanted:
|
||||
self.__rowOpWanted[name] = error.RowDestructionWanted()
|
||||
self.destroyTest(varBind, **context)
|
||||
|
||||
if name in self.__rowOpWanted:
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'%s flagged by %s=%r, exception %s' % (self.__rowOpWanted[name], name, val, sys.exc_info()[1]))
|
||||
raise self.__rowOpWanted[name]
|
||||
self.destroyTest(varBind, **dict(context, cbFun=_cbFun))
|
||||
|
||||
def __delegateWrite(self, subAction, varBind, **context):
|
||||
def __delegateWrite(self, subAction, varBind, dropException, **context):
|
||||
name, val = varBind
|
||||
|
||||
if name not in self.__rowOpWanted:
|
||||
actionFun = getattr(MibScalar, 'write' + subAction)
|
||||
return actionFun(self, varBind, **context)
|
||||
cbFun = context['cbFun']
|
||||
|
||||
def _cbFun(varBind, **context):
|
||||
if name in self.__rowOpWanted:
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'%s flagged by %s=%r, exception %s' % (self.__rowOpWanted[name], name, val, sys.exc_info()[1]))
|
||||
exc = self.__rowOpWanted[name]
|
||||
if dropException:
|
||||
debug.logger & debug.flagIns and debug.logger('%s dropped by %s=%r' % (exc, name, val))
|
||||
del self.__rowOpWanted[name]
|
||||
raise exc
|
||||
|
||||
cbFun(varBind, **context)
|
||||
|
||||
if isinstance(self.__rowOpWanted[name], error.RowCreationWanted):
|
||||
actionFun = getattr(self, 'create' + subAction)
|
||||
return actionFun(varBind, **context)
|
||||
actionFun(varBind, **dict(context, cbFun=_cbFun))
|
||||
|
||||
if isinstance(self.__rowOpWanted[name], error.RowDestructionWanted):
|
||||
elif isinstance(self.__rowOpWanted[name], error.RowDestructionWanted):
|
||||
actionFun = getattr(self, 'destroy' + subAction)
|
||||
return actionFun(varBind, **context)
|
||||
actionFun(varBind, **dict(context, cbFun=_cbFun))
|
||||
|
||||
else:
|
||||
actionFun = getattr(MibScalar, 'write' + subAction)
|
||||
actionFun(self, varBind, **dict(context, cbFun=_cbFun))
|
||||
|
||||
def writeCommit(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
self.__delegateWrite('Commit', varBind, **context)
|
||||
if name in self.__rowOpWanted:
|
||||
raise self.__rowOpWanted[name]
|
||||
self.__delegateWrite('Commit', varBind, False, **context)
|
||||
|
||||
def writeCleanup(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
self.branchVersionId += 1
|
||||
|
||||
self.__delegateWrite('Cleanup', varBind, **context)
|
||||
|
||||
if name in self.__rowOpWanted:
|
||||
e = self.__rowOpWanted[name]
|
||||
del self.__rowOpWanted[name]
|
||||
debug.logger & debug.flagIns and debug.logger('%s dropped by %s=%r' % (e, name, val))
|
||||
raise e
|
||||
self.__delegateWrite('Cleanup', varBind, True, **context)
|
||||
|
||||
def writeUndo(self, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
if name in self.__rowOpWanted:
|
||||
self.__rowOpWanted[name] = error.RowDestructionWanted()
|
||||
|
||||
self.__delegateWrite('Undo', varBind, **context)
|
||||
|
||||
if name in self.__rowOpWanted:
|
||||
e = self.__rowOpWanted[name]
|
||||
del self.__rowOpWanted[name]
|
||||
debug.logger & debug.flagIns and debug.logger('%s dropped by %s=%r' % (e, name, val))
|
||||
raise e
|
||||
self.__delegateWrite('Undo', varBind, True, **context)
|
||||
|
||||
|
||||
class MibTableRow(MibTree):
|
||||
|
@ -1288,20 +1391,28 @@ class MibTableRow(MibTree):
|
|||
indexVals[mibObj.name] = syntax
|
||||
indices.append(syntax)
|
||||
|
||||
count = [len(self._vars)]
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
def _cbFun(varBind, **context):
|
||||
count[0] -= 1
|
||||
|
||||
if not count[0]:
|
||||
cbFun(varBind, **context)
|
||||
|
||||
for name, var in self._vars.items():
|
||||
if name == excludeName:
|
||||
count[0] -= 1
|
||||
continue
|
||||
|
||||
actionFun = getattr(var, action)
|
||||
|
||||
if name in indexVals:
|
||||
# NOTE(etingof): disable VACM call
|
||||
_context = context.copy()
|
||||
_context.pop('acFun', None)
|
||||
|
||||
actionFun((name + nameSuffix, indexVals[name]), **_context)
|
||||
actionFun((name + nameSuffix, indexVals[name]), **dict(context, cbFun=_cbFun, acFun=None))
|
||||
else:
|
||||
actionFun((name + nameSuffix, val), **context)
|
||||
actionFun((name + nameSuffix, val), **dict(context, cbFun=_cbFun))
|
||||
|
||||
debug.logger & debug.flagIns and debug.logger('__manageColumns: action %s name %s suffix %s %svalue %r' % (
|
||||
action, name, nameSuffix, name in indexVals and "index " or "", indexVals.get(name, val)))
|
||||
|
@ -1309,47 +1420,58 @@ class MibTableRow(MibTree):
|
|||
def __delegate(self, subAction, varBind, **context):
|
||||
name, val = varBind
|
||||
|
||||
# Relay operation request to column, expect row operation request.
|
||||
rowIsActive = False
|
||||
|
||||
try:
|
||||
writeFun = getattr(self.getBranch(name, **context), 'write' + subAction)
|
||||
writeFun(varBind, **context)
|
||||
|
||||
except error.RowCreationWanted:
|
||||
createAction = 'create' + subAction
|
||||
except (error.RowCreationWanted, error.RowDestructionWanted):
|
||||
if isinstance(sys.exc_info()[1], error.RowCreationWanted):
|
||||
action = 'create' + subAction
|
||||
|
||||
else:
|
||||
action = 'destroy' + subAction
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
def _cbFun(varBind, **context):
|
||||
self.announceManagementEvent(action, (name, None), **context)
|
||||
cbFun(varBind, **context)
|
||||
|
||||
self.__manageColumns(
|
||||
createAction, name[:len(self.name) + 1], (name[len(self.name) + 1:], None), **context
|
||||
action, name[:len(self.name) + 1], (name[len(self.name) + 1:], None), **dict(context, cbFun=_cbFun)
|
||||
)
|
||||
|
||||
self.announceManagementEvent(createAction, (name, None), **context)
|
||||
|
||||
# watch for RowStatus == 'stActive'
|
||||
rowIsActive = sys.exc_info()[1].get('syntax', 0) == 1
|
||||
|
||||
except error.RowDestructionWanted:
|
||||
destroyAction = 'destroy' + subAction
|
||||
|
||||
self.__manageColumns(
|
||||
destroyAction, name[:len(self.name) + 1], (name[len(self.name) + 1:], None), **context
|
||||
)
|
||||
|
||||
self.announceManagementEvent(destroyAction, (name, None), **context)
|
||||
|
||||
return rowIsActive
|
||||
|
||||
def writeTest(self, varBind, **context):
|
||||
self.__delegate('Test', varBind, **context)
|
||||
|
||||
def writeCommit(self, varBind, **context):
|
||||
name, val = varBind
|
||||
rowIsActive = self.__delegate('Commit', varBind, **context)
|
||||
if rowIsActive:
|
||||
|
||||
# TODO: this is bad
|
||||
RowStatus = mibBuilder.importSymbols("SNMPv2-TC", "RowStatus")
|
||||
|
||||
cbFun = context['cbFun']
|
||||
|
||||
def _cbFun(varBind, **context):
|
||||
rowIsActive = False
|
||||
inconsistentColumn = None
|
||||
|
||||
for mibNode in self._vars.values():
|
||||
colNode = mibNode.getNode(mibNode.name + name[len(self.name) + 1:], **context)
|
||||
|
||||
if not colNode.syntax.hasValue():
|
||||
raise error.InconsistentValueError(msg='Row consistency check failed for %r' % colNode)
|
||||
inconsistentColumn = colNode
|
||||
|
||||
# TODO: avoid importing RowStatus?
|
||||
elif isinstance(colNode.syntax, RowStatus):
|
||||
rowIsActive = colNode.syntax == 1
|
||||
|
||||
if rowIsActive and inconsistentColumn is not None:
|
||||
raise error.InconsistentValueError(msg='Row consistency check failed for %r' % inconsistentColumn)
|
||||
|
||||
cbFun(varBind, **context)
|
||||
|
||||
self.__delegate('Commit', varBind, **dict(context, cbFun=_cbFun))
|
||||
|
||||
def writeCleanup(self, varBind, **context):
|
||||
self.branchVersionId += 1
|
||||
|
|
Loading…
Reference in New Issue