Compare commits

...

1 Commits

Author SHA1 Message Date
Ilya Etingof f7c62633ab [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.
2018-12-09 10:36:04 +01:00
6 changed files with 302 additions and 156 deletions

View File

@ -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

View File

@ -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')

View File

@ -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...'),

View File

@ -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)

View File

@ -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

View File

@ -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