Compare commits
9 Commits
async-mib-
...
master
Author | SHA1 | Date |
---|---|---|
Ilya Etingof | acd16a65f8 | |
Ilya Etingof | e425da2a0f | |
Ilya Etingof | 4994111b2b | |
Ilya Etingof | 9ebd0c0fab | |
Ilya Etingof | 4aac8b23d5 | |
Ilya Etingof | 602c4dd304 | |
Ilya Etingof | 0c0d054e8e | |
Ilya Etingof | 40cfd938c4 | |
Ilya Etingof | 534a5bb810 |
28
.travis.yml
28
.travis.yml
|
@ -2,22 +2,10 @@ language: python
|
|||
cache: pip
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
python: '2.6'
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
python: '2.7'
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
python: '3.2'
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
python: '3.3'
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
|
@ -38,14 +26,14 @@ matrix:
|
|||
dist: xenial
|
||||
sudo: false
|
||||
python: 'nightly'
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
python: 'pypy'
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: false
|
||||
python: 'pypy3'
|
||||
# - os: linux
|
||||
# dist: xenial
|
||||
# sudo: false
|
||||
# python: 'pypy'
|
||||
# - os: linux
|
||||
# dist: xenial
|
||||
# sudo: false
|
||||
# python: 'pypy3'
|
||||
install:
|
||||
- pip install -r requirements.txt -r devel-requirements.txt -r extra-requirements.txt
|
||||
- pip install -e .
|
||||
|
|
33
CHANGES.txt
33
CHANGES.txt
|
@ -5,9 +5,11 @@ Revision 5.0.0, released 2018-10-??
|
|||
- SNMPv3 crypto operations that require external dependencies
|
||||
made dependent on the optional external
|
||||
package -- pysnmpcrypto.
|
||||
|
||||
- By switching to pysnmpcrypto, pysnmp effectively migrates from
|
||||
PyCryptodomex to pyca/cryptography whenever available on the
|
||||
platform.
|
||||
|
||||
- Many really old backward-compatibility code snippets removed.
|
||||
Most importantly:
|
||||
|
||||
|
@ -19,13 +21,22 @@ Revision 5.0.0, released 2018-10-??
|
|||
- The MIB instrumentation API overhauled in backward incompatible
|
||||
way:
|
||||
|
||||
- MIB instrumentation methods signatures simplified to accept
|
||||
just var-binds (as var-arg), the rest of the parameters packed
|
||||
into opaque kwargs
|
||||
- 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
|
||||
custom Python objects they need while being called back.
|
||||
* MIB instrumentation methods signatures simplified to accept
|
||||
just var-binds (as var-arg), the rest of the parameters packed
|
||||
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
|
||||
user-supplied `cbCtx` object throughout the MIB instrumentation
|
||||
methods. The goal is to let MIB objects access/modify whatever
|
||||
custom Python objects they need while being called back.
|
||||
|
||||
* CommandResponder refactored to facilitate asynchronous
|
||||
MIB instrumentation routines.
|
||||
|
||||
- The high-level API (`hlapi`) extended to cover lightweight SNMP v1arch
|
||||
in hope to ease the use of packet-level SNMP API.
|
||||
|
||||
|
@ -41,16 +52,22 @@ Revision 5.0.0, released 2018-10-??
|
|||
automation around building well-formed SNMP messages is and mediating
|
||||
differences between SNMP versions is not present in this new `v1arch`
|
||||
layer.
|
||||
|
||||
- The signature of the hlapi `.sendNotification()` call has changed
|
||||
to accept `*varBinds` instead of a sequence of `varBinds`. The rationale
|
||||
is to unify this method call with similar methods of CommandGenerator.
|
||||
This change should not compromise backward compatibility with pysnmp 4.
|
||||
|
||||
Revision 4.4.7, released 2018-09-XX
|
||||
Revision 4.4.7, released 2018-11-XX
|
||||
-----------------------------------
|
||||
|
||||
- Exposed ASN.1 `Null` type through `rfc1902` module for convenience.
|
||||
- Use `compile()` before `exec`'ing MIB modules to attach filename to
|
||||
the stack frames (ultimately shown in traceback/debugger)
|
||||
- Fixed hlapi/v3arch transport target caching to ensure transport targets
|
||||
are different even if just timeout/retries options differ
|
||||
- Fixed hlapi LCD configurator to include `contextName`. Prior to this fix
|
||||
sending SNMPv3 TRAP with non-default `contextName` would fail.
|
||||
|
||||
Revision 4.4.6, released 2018-09-13
|
||||
-----------------------------------
|
||||
|
|
|
@ -441,6 +441,20 @@ data description language. PySNMP types are derived from
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
.. _null:
|
||||
|
||||
Null type
|
||||
+++++++++
|
||||
|
||||
.. autoclass:: pysnmp.proto.rfc1902.Null(initializer)
|
||||
:members:
|
||||
|
||||
.. note::
|
||||
|
||||
The `NULL` type actually belongs to the base ASN.1 types. It is not defined
|
||||
in :RFC:`1902#section-2` as an SNMP type. The `Null` type is exposed through
|
||||
`rfc1902` module just for convenience.
|
||||
|
||||
.. _integer32:
|
||||
|
||||
Integer32 type
|
||||
|
|
|
@ -51,13 +51,23 @@ 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
|
||||
print('%s = %s' % ('.'.join([str(x) for x in oid]), not val.isValue and 'N/A' or val.prettyPrint()))
|
||||
|
||||
context['state']['varBinds'] = varBinds
|
||||
|
||||
context = {
|
||||
'cbFun': cbFun,
|
||||
'state': {
|
||||
'varBinds': [((1, 3, 6), None)],
|
||||
'stop': False
|
||||
}
|
||||
}
|
||||
|
||||
print('Remote manager read access to MIB instrumentation (table walk)')
|
||||
|
||||
varBinds = [((), None)]
|
||||
|
||||
while True:
|
||||
varBinds = mibInstrum.readNextVars(*varBinds)
|
||||
oid, val = varBinds[0]
|
||||
if exval.endOfMib.isSameTypeWith(val):
|
||||
break
|
||||
print(oid, val.prettyPrint())
|
||||
while not context['state']['stop']:
|
||||
mibInstrum.readNextVars(*context['state']['varBinds'], **context)
|
||||
print('done')
|
||||
|
|
|
@ -26,24 +26,40 @@ snmpCommunityEntry, = mibBuilder.importSymbols(
|
|||
instanceId = snmpCommunityEntry.getInstIdFromIndices('my-router')
|
||||
print('done')
|
||||
|
||||
|
||||
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: ')
|
||||
varBinds = mibInstrum.writeVars(
|
||||
mibInstrum.writeVars(
|
||||
(snmpCommunityEntry.name + (2,) + instanceId, 'mycomm'),
|
||||
(snmpCommunityEntry.name + (3,) + instanceId, 'mynmsname'),
|
||||
(snmpCommunityEntry.name + (7,) + instanceId, 'volatile')
|
||||
(snmpCommunityEntry.name + (7,) + instanceId, 'volatile'),
|
||||
cbFun=cbFun
|
||||
)
|
||||
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('done')
|
||||
|
||||
|
||||
def cbFun(varBinds, **context):
|
||||
for oid, val in varBinds:
|
||||
if exval.endOfMib.isSameTypeWith(val):
|
||||
context['state']['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 = {
|
||||
'cbFun': cbFun,
|
||||
'state': {
|
||||
'varBinds': [((1, 3, 6), None)],
|
||||
'stop': False
|
||||
}
|
||||
}
|
||||
|
||||
print('Read whole MIB (table walk)')
|
||||
varBinds = [((), None)]
|
||||
while True:
|
||||
varBinds = mibInstrum.readNextVars(*varBinds)
|
||||
oid, val = varBinds[0]
|
||||
if exval.endOfMib.isSameTypeWith(val):
|
||||
break
|
||||
print('%s = %s' % ('.'.join([str(x) for x in oid]), not val.isValue and 'N/A' or val.prettyPrint()))
|
||||
while not context['state']['stop']:
|
||||
mibInstrum.readNextVars(*context['state']['varBinds'], **context)
|
||||
print('done')
|
||||
|
||||
print('Unloading MIB modules...'),
|
||||
|
|
|
@ -54,7 +54,9 @@ snmpContext = context.SnmpContext(snmpEngine)
|
|||
# always echos request var-binds in response.
|
||||
class EchoMibInstrumController(instrum.AbstractMibInstrumController):
|
||||
def readVars(self, *varBinds, **context):
|
||||
return [(ov[0], v2c.OctetString('You queried OID %s' % ov[0])) for ov in varBinds]
|
||||
cbFun = context.get('cbFun')
|
||||
if cbFun:
|
||||
cbFun([(ov[0], v2c.OctetString('You queried OID %s' % ov[0])) for ov in varBinds], **context)
|
||||
|
||||
|
||||
# Create a custom Management Instrumentation Controller and register at
|
||||
|
|
|
@ -56,7 +56,7 @@ MibScalar, MibScalarInstance = mibBuilder.importSymbols(
|
|||
|
||||
class MyStaticMibScalarInstance(MibScalarInstance):
|
||||
# noinspection PyUnusedLocal,PyUnusedLocal
|
||||
def getValue(self, name, idx):
|
||||
def getValue(self, name, idx, **context):
|
||||
return self.getSyntax().clone(
|
||||
'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.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
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||
|
|
|
@ -95,7 +95,7 @@ class CommandResponder(cmdrsp.CommandResponderBase):
|
|||
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
||||
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
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||
|
|
|
@ -95,7 +95,7 @@ class CommandResponder(cmdrsp.CommandResponderBase):
|
|||
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
||||
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
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||
|
|
|
@ -98,7 +98,7 @@ class CommandResponder(cmdrsp.CommandResponderBase):
|
|||
v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
|
||||
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
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
|
||||
|
|
|
@ -99,7 +99,7 @@ class DgramSocketTransport(AbstractSocketTransport):
|
|||
except socket.error:
|
||||
raise error.CarrierError('setsockopt() for IP_TRANSPARENT failed: %s' % sys.exc_info()[1])
|
||||
except OSError:
|
||||
raise error.CarrierError('IP_TRANSPARENT socket option requires superusre previleges')
|
||||
raise error.CarrierError('IP_TRANSPARENT socket option requires superuser priveleges')
|
||||
|
||||
debug.logger & debug.flagIO and debug.logger('enableTransparent: %s option IP_TRANSPARENT on socket %s' % (flag and "enabled" or "disabled", self.socket.fileno()))
|
||||
return self
|
||||
|
|
|
@ -422,7 +422,8 @@ def delContext(snmpEngine, contextName):
|
|||
vacmContextEntry, tblIdx = __cookVacmContextInfo(snmpEngine, contextName)
|
||||
|
||||
snmpEngine.msgAndPduDsp.mibInstrumController.writeVars(
|
||||
(vacmContextEntry.name + (2,) + tblIdx, 'destroy')
|
||||
(vacmContextEntry.name + (2,) + tblIdx, 'destroy'),
|
||||
** dict(snmpEngine=snmpEngine)
|
||||
)
|
||||
|
||||
|
||||
|
@ -577,12 +578,13 @@ def addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
|||
|
||||
|
||||
def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||
readSubTree=(), writeSubTree=(), notifySubTree=()):
|
||||
readSubTree=(), writeSubTree=(), notifySubTree=(),
|
||||
contextName=null):
|
||||
(groupName, securityLevel, readView, writeView,
|
||||
notifyView) = __cookVacmUserInfo(snmpEngine, securityModel,
|
||||
securityName, securityLevel)
|
||||
delVacmGroup(snmpEngine, securityModel, securityName)
|
||||
delVacmAccess(snmpEngine, groupName, null, securityModel, securityLevel)
|
||||
delVacmAccess(snmpEngine, groupName, contextName, securityModel, securityLevel)
|
||||
if readSubTree:
|
||||
delVacmView(
|
||||
snmpEngine, readView, readSubTree
|
||||
|
@ -596,7 +598,6 @@ def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
|||
snmpEngine, notifyView, notifySubTree
|
||||
)
|
||||
|
||||
|
||||
# Notification target setup
|
||||
|
||||
def __cookNotificationTargetInfo(snmpEngine, notificationName, paramsName,
|
||||
|
|
|
@ -15,26 +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 handleMgmtOperation(self, snmpEngine, stateReference,
|
||||
contextName, PDU, acCtx):
|
||||
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,
|
||||
|
@ -106,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):
|
||||
|
@ -141,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.handleMgmtOperation(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):
|
||||
|
@ -259,57 +218,146 @@ 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
|
||||
|
||||
# rfc1905: 4.2.1
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
||||
# rfc1905: 4.2.1.1
|
||||
mgmtFun = self.snmpContext.getMibInstrum(contextName).readVars
|
||||
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):
|
||||
|
||||
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)
|
||||
|
||||
context = dict(snmpEngine=snmpEngine, acFun=self.verifyAccess, cbCtx=self.cbCtx)
|
||||
mgmtFun = self._getMgmtFun(contextName)
|
||||
|
||||
rspVarBinds = mgmtFun(*varBinds, **context)
|
||||
context = dict(snmpEngine=snmpEngine,
|
||||
stateReference=stateReference,
|
||||
acFun=self.verifyAccess,
|
||||
cbFun=self.completeMgmtOperation,
|
||||
cbCtx=self.cbCtx)
|
||||
|
||||
self.sendVarBinds(snmpEngine, stateReference, 0, 0, rspVarBinds)
|
||||
self.releaseStateInformation(stateReference)
|
||||
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):
|
||||
pduTypes = (rfc1905.GetNextRequestPDU.tagSet,)
|
||||
SUPPORTED_PDU_TYPES = (rfc1905.GetNextRequestPDU.tagSet,)
|
||||
|
||||
# rfc1905: 4.2.2
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
||||
# rfc1905: 4.2.2.1
|
||||
mgmtFun = self.snmpContext.getMibInstrum(contextName).readNextVars
|
||||
|
||||
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
||||
|
||||
context = dict(snmpEngine=snmpEngine, acFun=self.verifyAccess, cbCtx=self.cbCtx)
|
||||
|
||||
while True:
|
||||
rspVarBinds = mgmtFun(*varBinds, **context)
|
||||
|
||||
try:
|
||||
self.sendVarBinds(snmpEngine, stateReference, 0, 0, rspVarBinds)
|
||||
|
||||
except error.StatusInformation:
|
||||
idx = sys.exc_info()[1]['idx']
|
||||
varBinds[idx] = (rspVarBinds[idx][0], varBinds[idx][1])
|
||||
else:
|
||||
break
|
||||
|
||||
self.releaseStateInformation(stateReference)
|
||||
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
|
||||
|
||||
if context['counters']['M'] and context['counters']['R']:
|
||||
context['cbFun'] = self.completeMgmtOperation
|
||||
|
||||
mgmtFun = self._getMgmtFun(context['contextName'])
|
||||
|
||||
mgmtFun(*context['reqVarBinds'], **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._getMgmtFun(context['contextName'])
|
||||
|
||||
context['cbFun'] = self.completeMgmtOperation
|
||||
mgmtFun(*varBinds[-context['counters']['R']:], **context)
|
||||
|
||||
else:
|
||||
CommandResponderBase.completeMgmtOperation(self, context['rspVarBinds'], **context)
|
||||
|
||||
# rfc1905: 4.2.3
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
||||
def initiateMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
||||
nonRepeaters = v2c.apiBulkPDU.getNonRepeaters(PDU)
|
||||
if nonRepeaters < 0:
|
||||
nonRepeaters = 0
|
||||
|
@ -318,68 +366,42 @@ class BulkCommandResponder(CommandResponderBase):
|
|||
if maxRepetitions < 0:
|
||||
maxRepetitions = 0
|
||||
|
||||
reqVarBinds = v2c.apiPDU.getVarBinds(PDU)
|
||||
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
||||
|
||||
N = min(int(nonRepeaters), len(reqVarBinds))
|
||||
N = min(int(nonRepeaters), len(varBinds))
|
||||
M = int(maxRepetitions)
|
||||
R = max(len(reqVarBinds) - N, 0)
|
||||
R = max(len(varBinds) - N, 0)
|
||||
|
||||
if R:
|
||||
M = min(M, self.maxVarBinds // R)
|
||||
|
||||
debug.logger & debug.flagApp and debug.logger('handleMgmtOperation: N %d, M %d, R %d' % (N, M, R))
|
||||
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, acFun=self.verifyAccess, cbCtx=self.cbCtx)
|
||||
context = dict(snmpEngine=snmpEngine,
|
||||
stateReference=stateReference,
|
||||
contextName=contextName,
|
||||
acFun=self.verifyAccess,
|
||||
cbFun=self._completeNonRepeaters,
|
||||
cbCtx=self.cbCtx,
|
||||
reqVarBinds=varBinds[N:],
|
||||
counters={'M': M, 'R': R},
|
||||
rspVarBinds=[])
|
||||
|
||||
if N:
|
||||
# TODO(etingof): manage all PDU var-binds in a single call
|
||||
rspVarBinds = mgmtFun(*reqVarBinds[:N], **context)
|
||||
|
||||
else:
|
||||
rspVarBinds = []
|
||||
|
||||
varBinds = reqVarBinds[-R:]
|
||||
|
||||
while M and R:
|
||||
rspVarBinds.extend(mgmtFun(*varBinds, **context))
|
||||
varBinds = rspVarBinds[-R:]
|
||||
M -= 1
|
||||
|
||||
if len(rspVarBinds):
|
||||
self.sendVarBinds(snmpEngine, stateReference, 0, 0, rspVarBinds)
|
||||
self.releaseStateInformation(stateReference)
|
||||
else:
|
||||
raise pysnmp.smi.error.SmiError()
|
||||
mgmtFun(*varBinds[:N], **context)
|
||||
|
||||
|
||||
class SetCommandResponder(CommandResponderBase):
|
||||
pduTypes = (rfc1905.SetRequestPDU.tagSet,)
|
||||
SUPPORTED_PDU_TYPES = (rfc1905.SetRequestPDU.tagSet,)
|
||||
|
||||
# rfc1905: 4.2.5
|
||||
def handleMgmtOperation(self, snmpEngine, stateReference, contextName, PDU):
|
||||
mgmtFun = self.snmpContext.getMibInstrum(contextName).writeVars
|
||||
SMI_ERROR_MAP = CommandResponderBase.SMI_ERROR_MAP.copy()
|
||||
|
||||
varBinds = v2c.apiPDU.getVarBinds(PDU)
|
||||
# turn missing OIDs into access denial
|
||||
SMI_ERROR_MAP[pysnmp.smi.error.NoSuchObjectError] = 'notWritable'
|
||||
SMI_ERROR_MAP[pysnmp.smi.error.NoSuchInstanceError] = 'notWritable'
|
||||
|
||||
instrumError = None
|
||||
|
||||
context = dict(snmpEngine=snmpEngine, acFun=self.verifyAccess, cbCtx=self.cbCtx)
|
||||
|
||||
# rfc1905: 4.2.5.1-13
|
||||
try:
|
||||
rspVarBinds = mgmtFun(*varBinds, **context)
|
||||
|
||||
except (pysnmp.smi.error.NoSuchObjectError,
|
||||
pysnmp.smi.error.NoSuchInstanceError):
|
||||
instrumError = pysnmp.smi.error.NotWritableError()
|
||||
instrumError.update(sys.exc_info()[1])
|
||||
|
||||
else:
|
||||
self.sendVarBinds(snmpEngine, stateReference, 0, 0, rspVarBinds)
|
||||
|
||||
self.releaseStateInformation(stateReference)
|
||||
|
||||
if instrumError:
|
||||
raise instrumError
|
||||
# rfc1905: 4.2.5.1-13
|
||||
def _getMgmtFun(self, contextName):
|
||||
return self.snmpContext.getMibInstrum(contextName).writeVars
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -145,7 +145,8 @@ def getCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||
)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
future = asyncio.Future()
|
||||
|
||||
|
@ -250,7 +251,8 @@ def setCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||
)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
future = asyncio.Future()
|
||||
|
||||
|
@ -361,7 +363,8 @@ def nextCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||
)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
future = asyncio.Future()
|
||||
|
||||
|
@ -501,7 +504,8 @@ def bulkCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||
)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
future = asyncio.Future()
|
||||
|
||||
|
|
|
@ -152,8 +152,8 @@ def sendNotification(snmpEngine, authData, transportTarget, contextData,
|
|||
)
|
||||
|
||||
notifyName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, notifyType
|
||||
)
|
||||
snmpEngine, authData, transportTarget, notifyType,
|
||||
contextData.contextName)
|
||||
|
||||
future = asyncio.Future()
|
||||
|
||||
|
|
|
@ -121,7 +121,8 @@ def getCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
snmpEngine.cache, varBinds, lookupMib
|
||||
), cbCtx)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
return cmdgen.GetCommandGenerator().sendVarBinds(
|
||||
snmpEngine, addrName, contextData.contextEngineId,
|
||||
|
@ -232,7 +233,8 @@ def setCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
snmpEngine.cache, varBinds, lookupMib
|
||||
), cbCtx)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
return cmdgen.SetCommandGenerator().sendVarBinds(
|
||||
snmpEngine, addrName, contextData.contextEngineId,
|
||||
|
@ -343,7 +345,9 @@ def nextCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
varBindTable],
|
||||
cbCtx)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
return cmdgen.NextCommandGenerator().sendVarBinds(
|
||||
snmpEngine, addrName,
|
||||
contextData.contextEngineId, contextData.contextName,
|
||||
|
@ -483,7 +487,8 @@ def bulkCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
[vbProcessor.unmakeVarBinds(snmpEngine.cache, varBindTableRow, lookupMib) for varBindTableRow in
|
||||
varBindTable], cbCtx)
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
return cmdgen.BulkCommandGenerator().sendVarBinds(
|
||||
snmpEngine, addrName, contextData.contextEngineId,
|
||||
|
|
|
@ -162,7 +162,7 @@ def sendNotification(snmpEngine, authData, transportTarget, contextData,
|
|||
)
|
||||
|
||||
notifyName = lcd.configure(snmpEngine, authData, transportTarget,
|
||||
notifyType)
|
||||
notifyType, contextData.contextName)
|
||||
|
||||
return ntforg.NotificationOriginator().sendVarBinds(
|
||||
snmpEngine, notifyName,
|
||||
|
|
|
@ -8,6 +8,8 @@ from pysnmp.entity import config
|
|||
from pysnmp import nextid, error
|
||||
from pysnmp.hlapi.v3arch.auth import *
|
||||
|
||||
from pyasn1.compat.octets import null
|
||||
|
||||
__all__ = ['CommandGeneratorLcdConfigurator',
|
||||
'NotificationOriginatorLcdConfigurator']
|
||||
|
||||
|
@ -24,17 +26,17 @@ class AbstractLcdConfigurator(object):
|
|||
snmpEngine.setUserContext(**{cacheId: cache})
|
||||
return cache
|
||||
|
||||
def configure(self, snmpEngine, authData, transportTarget, *options):
|
||||
def configure(self, snmpEngine, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def unconfigure(self, snmpEngine, authData=None):
|
||||
def unconfigure(self, snmpEngine, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class CommandGeneratorLcdConfigurator(AbstractLcdConfigurator):
|
||||
cacheKeys = ['auth', 'parm', 'tran', 'addr']
|
||||
|
||||
def configure(self, snmpEngine, authData, transportTarget, *options):
|
||||
def configure(self, snmpEngine, authData, transportTarget, contextName, **options):
|
||||
cache = self._getCache(snmpEngine)
|
||||
if isinstance(authData, CommunityData):
|
||||
if authData.communityIndex not in cache['auth']:
|
||||
|
@ -117,7 +119,7 @@ class CommandGeneratorLcdConfigurator(AbstractLcdConfigurator):
|
|||
|
||||
return addrName, paramsName
|
||||
|
||||
def unconfigure(self, snmpEngine, authData=None):
|
||||
def unconfigure(self, snmpEngine, authData=None, contextName=null, **options):
|
||||
cache = self._getCache(snmpEngine)
|
||||
if authData:
|
||||
if isinstance(authData, CommunityData):
|
||||
|
@ -198,9 +200,9 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
|||
cacheKeys = ['auth', 'name']
|
||||
_cmdGenLcdCfg = CommandGeneratorLcdConfigurator()
|
||||
|
||||
def configure(self, snmpEngine, authData, transportTarget, *options):
|
||||
def configure(self, snmpEngine, authData, transportTarget, notifyType,
|
||||
contextName, **options):
|
||||
cache = self._getCache(snmpEngine)
|
||||
notifyType = options and options[0] or 'trap'
|
||||
notifyName = None
|
||||
|
||||
# Create matching transport tags if not given by user. Not good!
|
||||
|
@ -211,7 +213,8 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
|||
if isinstance(authData, CommunityData) and not authData.tag:
|
||||
authData.tag = transportTarget.tagList.split()[0]
|
||||
|
||||
addrName, paramsName = self._cmdGenLcdCfg.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = self._cmdGenLcdCfg.configure(
|
||||
snmpEngine, authData, transportTarget, contextName, **options)
|
||||
tagList = transportTarget.tagList.split()
|
||||
if not tagList:
|
||||
tagList = ['']
|
||||
|
@ -230,7 +233,7 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
|||
notifyType
|
||||
)
|
||||
cache['name'][notifyNameKey] = notifyName, paramsName, 1
|
||||
authDataKey = authData.securityName, authData.securityModel
|
||||
authDataKey = authData.securityName, authData.securityModel, authData.securityLevel, contextName
|
||||
if authDataKey in cache['auth']:
|
||||
authDataX, subTree, useCount = cache['auth'][authDataKey]
|
||||
cache['auth'][authDataKey] = authDataX, subTree, useCount + 1
|
||||
|
@ -240,23 +243,24 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
|||
authData.securityModel,
|
||||
authData.securityName,
|
||||
authData.securityLevel,
|
||||
(), (), subTree)
|
||||
(), (), subTree, contextName=contextName)
|
||||
cache['auth'][authDataKey] = authData, subTree, 1
|
||||
|
||||
return notifyName
|
||||
|
||||
def unconfigure(self, snmpEngine, authData=None):
|
||||
def unconfigure(self, snmpEngine, authData=None, contextName=null, **options):
|
||||
cache = self._getCache(snmpEngine)
|
||||
if authData:
|
||||
authDataKey = authData.securityName, authData.securityModel
|
||||
authDataKey = authData.securityName, authData.securityModel, authData.securityLevel, contextName
|
||||
if authDataKey in cache['auth']:
|
||||
authDataKeys = (authDataKey,)
|
||||
else:
|
||||
raise error.PySnmpError('Unknown authData %s' % (authData,))
|
||||
else:
|
||||
authDataKeys = tuple(cache['auth'].keys())
|
||||
authDataKeys = tuple(cache['auth'])
|
||||
|
||||
addrNames, paramsNames = self._cmdGenLcdCfg.unconfigure(snmpEngine, authData)
|
||||
addrNames, paramsNames = self._cmdGenLcdCfg.unconfigure(
|
||||
snmpEngine, authData, contextName, **options)
|
||||
|
||||
notifyAndParamsNames = [(cache['name'][x], x) for x in cache['name'].keys() if x[0] in paramsNames]
|
||||
|
||||
|
|
|
@ -128,7 +128,8 @@ def getCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
else:
|
||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
deferred = Deferred()
|
||||
|
||||
|
@ -244,7 +245,8 @@ def setCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
else:
|
||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
deferred = Deferred()
|
||||
|
||||
|
@ -374,7 +376,8 @@ def nextCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
else:
|
||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
deferred = Deferred()
|
||||
|
||||
|
@ -532,7 +535,8 @@ def bulkCmd(snmpEngine, authData, transportTarget, contextData,
|
|||
else:
|
||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||
|
||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
||||
addrName, paramsName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||
|
||||
deferred = Deferred()
|
||||
|
||||
|
|
|
@ -166,9 +166,8 @@ def sendNotification(snmpEngine, authData, transportTarget, contextData,
|
|||
else:
|
||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||
|
||||
notifyName = lcd.configure(
|
||||
snmpEngine, authData, transportTarget, notifyType
|
||||
)
|
||||
notifyName = lcd.configure(snmpEngine, authData, transportTarget,
|
||||
notifyType, contextData.contextName)
|
||||
|
||||
def __trapFun(deferred):
|
||||
deferred.callback((0, 0, []))
|
||||
|
|
|
@ -10,7 +10,32 @@ from pysnmp.proto import rfc1155, error
|
|||
|
||||
__all__ = ['Opaque', 'TimeTicks', 'Bits', 'Integer', 'OctetString',
|
||||
'IpAddress', 'Counter64', 'Unsigned32', 'Gauge32', 'Integer32',
|
||||
'ObjectIdentifier', 'Counter32']
|
||||
'ObjectIdentifier', 'Counter32', 'Null']
|
||||
|
||||
|
||||
class Null(univ.Null):
|
||||
"""Creates an instance of SNMP Null class.
|
||||
|
||||
:py:class:`~pysnmp.proto.rfc1902.Null` type represents the absence
|
||||
of value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
initializer: str
|
||||
Python string object. Must be an empty string.
|
||||
|
||||
Raises
|
||||
------
|
||||
PyAsn1Error :
|
||||
On constraint violation or bad initializer.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from pysnmp.proto.rfc1902 import *
|
||||
>>> Null('')
|
||||
Null('')
|
||||
>>>
|
||||
"""
|
||||
|
||||
|
||||
class Integer32(univ.Integer):
|
||||
|
|
|
@ -263,7 +263,7 @@ class MibBuilder(object):
|
|||
self.mibSymbols = {}
|
||||
self.__mibSources = []
|
||||
self.__modSeen = {}
|
||||
self.__modPathsSeen = {}
|
||||
self.__modPathsSeen = set()
|
||||
self.__mibCompiler = None
|
||||
self.setMibSources(*sources)
|
||||
|
||||
|
@ -306,19 +306,19 @@ class MibBuilder(object):
|
|||
debug.logger & debug.flagBld and debug.logger('loadModule: seen %s' % modPath)
|
||||
break
|
||||
else:
|
||||
self.__modPathsSeen[modPath] = 1
|
||||
self.__modPathsSeen.add(modPath)
|
||||
|
||||
debug.logger & debug.flagBld and debug.logger('loadModule: evaluating %s' % modPath)
|
||||
|
||||
g = {'mibBuilder': self, 'userCtx': userCtx}
|
||||
|
||||
try:
|
||||
exec (modData, g)
|
||||
exec(compile(modData, modPath, 'exec'), g)
|
||||
|
||||
except Exception:
|
||||
del self.__modPathsSeen[modPath]
|
||||
raise error.MibLoadError(
|
||||
'MIB module \"%s\" load error: %s' % (modPath, traceback.format_exception(*sys.exc_info()))
|
||||
'MIB module \'%s\' load error: %s' % (modPath, traceback.format_exception(*sys.exc_info()))
|
||||
)
|
||||
|
||||
self.__modSeen[modName] = modPath
|
||||
|
|
|
@ -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,82 +205,121 @@ class MibInstrumController(AbstractMibInstrumController):
|
|||
|
||||
# MIB instrumentation
|
||||
|
||||
def flipFlopFsm(self, fsmTable, *varBinds, **context):
|
||||
self.__indexMib()
|
||||
def _flipFlopFsmCb(self, varBind, **context):
|
||||
fsmContext = context[self.FSM_CONTEXT]
|
||||
|
||||
debug.logger & debug.flagIns and debug.logger('flipFlopFsm: input var-binds %r' % (varBinds,))
|
||||
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[self.FSM_CONTEXT]
|
||||
|
||||
except KeyError:
|
||||
self.__indexMib()
|
||||
|
||||
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 = []
|
||||
state, status = 'start', 'ok'
|
||||
origExc = origTraceback = None
|
||||
while True:
|
||||
k = state, status
|
||||
if k in fsmTable:
|
||||
fsmState = fsmTable[k]
|
||||
state = fsmContext['state']
|
||||
status = fsmContext['status']
|
||||
|
||||
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
|
||||
|
||||
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 suceeded 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
|
||||
|
||||
return outputVarBinds
|
||||
debug.logger & debug.flagIns and debug.logger(
|
||||
'flipFlopFsm: func %s initiated for %r' % (mgmtFun, varBind))
|
||||
|
||||
def readVars(self, *varBinds, **context):
|
||||
return self.flipFlopFsm(self.fsmReadVar, *varBinds, **context)
|
||||
self.flipFlopFsm(self.fsmReadVar, *varBinds, **context)
|
||||
|
||||
def readNextVars(self, *varBinds, **context):
|
||||
return self.flipFlopFsm(self.fsmReadNextVar, *varBinds, **context)
|
||||
self.flipFlopFsm(self.fsmReadNextVar, *varBinds, **context)
|
||||
|
||||
def writeVars(self, *varBinds, **context):
|
||||
return self.flipFlopFsm(self.fsmWriteVar, *varBinds, **context)
|
||||
self.flipFlopFsm(self.fsmWriteVar, *varBinds, **context)
|
||||
|
|
Loading…
Reference in New Issue