diff options
author | elie <elie> | 2015-09-20 18:40:58 +0000 |
---|---|---|
committer | elie <elie> | 2015-09-20 18:40:58 +0000 |
commit | 8ede39948b4f6037012552bab20da2ff8376d715 (patch) | |
tree | de0b67c276ef98e347fbbe3381c5820cc5ad5446 /pysnmp/hlapi/asyncore | |
parent | 75905be006cc82d0880d5c01c35f8457726ac4ee (diff) | |
download | pysnmp-8ede39948b4f6037012552bab20da2ff8376d715.tar.gz |
major revamp:
* oneliner API moved to pysnmp.hlapi.asyncore location
* all hlapi components now pulled from inner modules to into
pysnmp.hlapi.asyncore.
* LCD configuration moved from SNMP apps classes to be stand-alone
* var-binds MIB<->protocol representation code moved from SNMP apps
classes to be stand-alone
Diffstat (limited to 'pysnmp/hlapi/asyncore')
-rw-r--r-- | pysnmp/hlapi/asyncore/__init__.py | 16 | ||||
-rw-r--r-- | pysnmp/hlapi/asyncore/_sync/cmdgen.py | 613 | ||||
-rw-r--r-- | pysnmp/hlapi/asyncore/_sync/compat/cmdgen.py | 266 | ||||
-rw-r--r-- | pysnmp/hlapi/asyncore/_sync/compat/ntforg.py | 52 | ||||
-rw-r--r-- | pysnmp/hlapi/asyncore/_sync/ntforg.py | 124 | ||||
-rw-r--r-- | pysnmp/hlapi/asyncore/cmdgen.py | 529 | ||||
-rw-r--r-- | pysnmp/hlapi/asyncore/ntforg.py | 169 | ||||
-rw-r--r-- | pysnmp/hlapi/asyncore/transport.py | 150 |
8 files changed, 1919 insertions, 0 deletions
diff --git a/pysnmp/hlapi/asyncore/__init__.py b/pysnmp/hlapi/asyncore/__init__.py new file mode 100644 index 0000000..2f815b6 --- /dev/null +++ b/pysnmp/hlapi/asyncore/__init__.py @@ -0,0 +1,16 @@ +# This file is necessary to make this directory a package. +from pysnmp.proto.rfc1902 import * +from pysnmp.smi.rfc1902 import * +from pysnmp.hlapi.auth import * +from pysnmp.hlapi.context import * +from pysnmp.hlapi.asyncore.transport import * +from pysnmp.hlapi.asyncore.cmdgen import * +from pysnmp.hlapi.asyncore.ntforg import * +from pysnmp.entity.engine import SnmpEngine + +try: + from pysnmp.hlapi.asyncore._sync.cmdgen import * + from pysnmp.hlapi.asyncore._sync.ntforg import * +except SyntaxError: + from pysnmp.hlapi.asyncore._sync.compat.cmdgen import * + from pysnmp.hlapi.asyncore._sync.compat.ntforg import * diff --git a/pysnmp/hlapi/asyncore/_sync/cmdgen.py b/pysnmp/hlapi/asyncore/_sync/cmdgen.py new file mode 100644 index 0000000..357cc52 --- /dev/null +++ b/pysnmp/hlapi/asyncore/_sync/cmdgen.py @@ -0,0 +1,613 @@ +from pysnmp.hlapi.asyncore.cmdgen import * +from pysnmp.hlapi.varbinds import * +from pysnmp.proto.rfc1905 import endOfMibView +from pysnmp.proto.errind import * +from pyasn1.type.univ import Null + +__all__ = ['getCmd', 'nextCmd', 'setCmd', 'bulkCmd'] + +def getCmd(snmpEngine, authData, transportTarget, contextData, + *varBinds, **options): + """Creates a generator to perform one or more SNMP GET queries. + + On each iteration, new SNMP GET request is send (:RFC:`1905#section-4.2.1`). + The iterator blocks waiting for response to arrive or error to occur. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName values. + + \*varBinds : :py:class:`~pysnmp.smi.rfc1902.ObjectType` + One or more class instances representing MIB variables to place + into SNMP request. + + Other Parameters + ---------------- + \*\*options : + Request options: + + * `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + + Yields + ------ + errorIndication : str + True value indicates SNMP engine error. + errorStatus : str + True value indicates SNMP PDU error. + errorIndex : int + Non-zero value refers to `varBinds[errorIndex-1]` + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables returned in SNMP response. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Notes + ----- + The `getCmd` generator will be exhausted immidiately unless + a new sequence of `varBinds` are send back into running generator + (supported since Python 2.6). + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> g = getCmd(SnmpEngine(), + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))) + >>> next(g) + (None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.1.0')), DisplayString('SunOS zeus.snmplabs.com 4.1.3_U1 1 sun4m'))]) + >>> + + """ + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBinds'] = varBinds + + cbCtx = {} + + cmdGen = AsyncCommandGenerator() + + while True: + if varBinds: + cmdGen.getCmd( + snmpEngine, + authData, + transportTarget, + contextData, + varBinds, + (cbFun, cbCtx), + options.get('lookupMib', True) + ) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx['errorIndication'] + errorStatus = cbCtx['errorStatus'] + errorIndex = cbCtx['errorIndex'] + varBinds = cbCtx['varBinds'] + else: + errorIndication = errorStatus = errorIndex = None + varBinds = [] + + varBinds = ( yield errorIndication, errorStatus, errorIndex, varBinds ) + + if not varBinds: + break + +def setCmd(snmpEngine, authData, transportTarget, contextData, + *varBinds, **options): + """Creates a generator to perform one or more SNMP SET queries. + + On each iteration, new SNMP SET request is send (:RFC:`1905#section-4.2.5`). + The iterator blocks waiting for response to arrive or error to occur. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName values. + + \*varBinds : :py:class:`~pysnmp.smi.rfc1902.ObjectType` + One or more class instances representing MIB variables to place + into SNMP request. + + Other Parameters + ---------------- + \*\*options : + Request options: + + * `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + Default is `True`. + + Yields + ------ + errorIndication : str + True value indicates SNMP engine error. + errorStatus : str + True value indicates SNMP PDU error. + errorIndex : int + Non-zero value refers to `varBinds[errorIndex-1]` + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables returned in SNMP response. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Notes + ----- + The `setCmd` generator will be exhausted immidiately unless + a new sequence of `varBinds` are send back into running generator + (supported since Python 2.6). + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> g = setCmd(SnmpEngine(), + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386')) + >>> next(g) + (None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.1.0')), DisplayString('Linux i386'))]) + >>> + + """ + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBinds'] = varBinds + + cbCtx = {} + + cmdGen = AsyncCommandGenerator() + + while True: + if varBinds: + cmdGen.setCmd( + snmpEngine, + authData, + transportTarget, + contextData, + varBinds, + (cbFun, cbCtx), + options.get('lookupMib', True) + ) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx['errorIndication'] + errorStatus = cbCtx['errorStatus'] + errorIndex = cbCtx['errorIndex'] + varBinds = cbCtx['varBinds'] + else: + errorIndication = errorStatus = errorIndex = None + varBinds = [] + + varBinds = ( yield errorIndication, errorStatus, errorIndex, varBinds ) + + if not varBinds: + break + +def nextCmd(snmpEngine, authData, transportTarget, contextData, + *varBinds, **options): + """Creates a generator to perform one or more SNMP GETNEXT queries. + + On each iteration, new SNMP GETNEXT request is send + (:RFC:`1905#section-4.2.2`). The iterator blocks waiting for response + to arrive or error to occur. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName values. + + \*varBinds : :py:class:`~pysnmp.smi.rfc1902.ObjectType` + One or more class instances representing MIB variables to place + into SNMP request. + + Other Parameters + ---------------- + \*\*options : + Request options: + + * `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + Default is `True`. + * `lexicographicMode` - stop iteration when all response MIB + variables leave the scope of initial MIB variables in + `varBinds`. Default is `True`. + * `ignoreNonIncreasingOid` - continue iteration even if response + MIB variables (OIDs) are not greater then request MIB variables. + Default is `False`. + * `maxRows` - stop iteration once this generator instance processed + `maxRows` of SNMP conceptual table. Default is `0` (no limit). + * `maxCalls` - stop iteration once this generator instance processed + `maxCalls` responses. Default is 0 (no limit). + + Yields + ------ + errorIndication : str + True value indicates SNMP engine error. + errorStatus : str + True value indicates SNMP PDU error. + errorIndex : int + Non-zero value refers to `varBinds[errorIndex-1]` + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables returned in SNMP response. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Notes + ----- + The `nextCmd` generator will be exhausted on any of the following + conditions: + + * SNMP engine error occurs thus `errorIndication` is `True` + * SNMP PDU `errorStatus` is reported as `True` + * SNMP :py:class:`~pysnmp.proto.rfc1905.EndOfMibView` values + (also known as *SNMP exception values*) are reported for all + MIB variables in `varBinds` + * *lexicographicMode* option is set to `False` and all + response MIB variables leave the scope of `varBinds` + + At any moment a new sequence of `varBinds` could be send back into + running generator (supported since Python 2.6). + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> g = nextCmd(SnmpEngine(), + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))) + >>> next(g) + (None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.1.0')), DisplayString('SunOS zeus.snmplabs.com 4.1.3_U1 1 sun4m'))]) + >>> g.send( [ ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets')) ] ) + (None, 0, 0, [(ObjectName('1.3.6.1.2.1.2.2.1.10.1'), Counter32(284817787))]) + """ + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBindTable, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBindTable'] = varBindTable + + lookupMib = options.get('lookupMib', True) + lexicographicMode = options.get('lexicographicMode', True) + ignoreNonIncreasingOid = options.get('ignoreNonIncreasingOid', False) + maxRows = options.get('maxRows', 0) + maxCalls = options.get('maxCalls', 0) + + cbCtx = {} + + vbProcessor = CommandGeneratorVarBinds() + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in vbProcessor.makeVarBinds(snmpEngine, varBinds) ] + + totalRows = totalCalls = 0 + + while True: + if varBinds: + cmdGen.nextCmd(snmpEngine, + authData, + transportTarget, + contextData, + [ (x[0], Null()) for x in varBinds ], + (cbFun, cbCtx), + lookupMib) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx['errorIndication'] + errorStatus = cbCtx['errorStatus'] + errorIndex = cbCtx['errorIndex'] + + if ignoreNonIncreasingOid and errorIndication and \ + isinstance(errorIndication, errind.OidNotIncreasing): + errorIndication = None + + if errorIndication: + yield errorIndication, errorStatus, errorIndex, varBinds + return + elif errorStatus: + if errorStatus == 2: + # Hide SNMPv1 noSuchName error which leaks in here + # from SNMPv1 Agent through internal pysnmp proxy. + errorStatus = errorStatus.clone(0) + errorIndex = errorIndex.clone(0) + yield errorIndication, errorStatus, errorIndex, varBinds + return + else: + varBinds = cbCtx['varBindTable'] and cbCtx['varBindTable'][0] + for idx, varBind in enumerate(varBinds): + name, val = varBind + if not isinstance(val, Null): + if lexicographicMode or initialVars[idx].isPrefixOf(name): + break + else: + return + + totalRows += 1 + totalCalls += 1 + else: + errorIndication = errorStatus = errorIndex = None + varBinds = [] + + initialVarBinds = ( yield errorIndication, errorStatus, + errorIndex, varBinds ) + + if initialVarBinds: + varBinds = initialVarBinds + initialVars = [ x[0] for x in vbProcessor.makeVarBinds(snmpEngine, + varBinds) ] + + if maxRows and totalRows >= maxRows or \ + maxCalls and totalCalls >= maxCalls: + return + +def bulkCmd(snmpEngine, authData, transportTarget, contextData, + nonRepeaters, maxRepetitions, *varBinds, **options): + """Creates a generator to perform one or more SNMP GETBULK queries. + + On each iteration, new SNMP GETBULK request is send + (:RFC:`1905#section-4.2.3`). The iterator blocks waiting for response + to arrive or error to occur. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName values. + + nonRepeaters : int + One MIB variable is requested in response for the first + `nonRepeaters` MIB variables in request. + + maxRepetitions : int + `maxRepetitions` MIB variables are requested in response for each + of the remaining MIB variables in the request (e.g. excluding + `nonRepeaters`). Remote SNMP engine may choose lesser value than + requested. + + \*varBinds : :py:class:`~pysnmp.smi.rfc1902.ObjectType` + One or more class instances representing MIB variables to place + into SNMP request. + + Other Parameters + ---------------- + \*\*options : + Request options: + + * `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + Default is `True`. + * `lexicographicMode` - stop iteration when all response MIB + variables leave the scope of initial MIB variables in + `varBinds`. Default is `True`. + * `ignoreNonIncreasingOid` - continue iteration even if response + MIB variables (OIDs) are not greater then request MIB variables. + Default is `False`. + * `maxRows` - stop iteration once this generator instance processed + `maxRows` of SNMP conceptual table. Default is `0` (no limit). + * `maxCalls` - stop iteration once this generator instance processed + `maxCalls` responses. Default is 0 (no limit). + + Yields + ------ + errorIndication : str + True value indicates SNMP engine error. + errorStatus : str + True value indicates SNMP PDU error. + errorIndex : int + Non-zero value refers to *varBinds[errorIndex-1] + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables returned in SNMP response. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Notes + ----- + The `bulkCmd` generator will be exhausted on any of the following + conditions: + + * SNMP engine error occurs thus `errorIndication` is `True` + * SNMP PDU `errorStatus` is reported as `True` + * SNMP :py:class:`~pysnmp.proto.rfc1905.EndOfMibView` values + (also known as *SNMP exception values*) are reported for all + MIB variables in `varBinds` + * *lexicographicMode* option is set to `False` and all + response MIB variables leave the scope of `varBinds` + + At any moment a new sequence of `varBinds` could be send back into + running generator (supported since Python 2.6). + + Setting `maxRepetitions` value to 15..50 might significantly improve + system performance, as many MIB variables get packed into a single + response message at once. + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> g = bulkCmd(SnmpEngine(), + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... 0, 25, + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))) + >>> next(g) + (None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.1.0')), DisplayString('SunOS zeus.snmplabs.com 4.1.3_U1 1 sun4m'))]) + >>> g.send( [ ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets')) ] ) + (None, 0, 0, [(ObjectName('1.3.6.1.2.1.2.2.1.10.1'), Counter32(284817787))]) + """ + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBindTable, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBindTable'] = varBindTable + + lookupMib = options.get('lookupMib', True) + lexicographicMode = options.get('lexicographicMode', True) + ignoreNonIncreasingOid = options.get('ignoreNonIncreasingOid', False) + maxRows = options.get('maxRows', 0) + maxCalls = options.get('maxCalls', 0) + + cbCtx = {} + + vbProcessor = CommandGeneratorVarBinds() + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in vbProcessor.makeVarBinds(snmpEngine, varBinds) ] + nullVarBinds = [ False ] * len(initialVars) + + totalRows = totalCalls = 0 + stopFlag = False + + while not stopFlag: + if maxRows and totalRows < maxRows: + maxRepetitions = min(maxRepetitions, maxRows-totalRows) + + cmdGen.bulkCmd(snmpEngine, + authData, + transportTarget, + contextData, + nonRepeaters, maxRepetitions, + [ (x[0], Null()) for x in varBinds ], + (cbFun, cbCtx), + lookupMib) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx['errorIndication'] + errorStatus = cbCtx['errorStatus'] + errorIndex = cbCtx['errorIndex'] + varBindTable = cbCtx['varBindTable'] + + if ignoreNonIncreasingOid and errorIndication and \ + isinstance(errorIndication, errind.OidNotIncreasing): + errorIndication = None + + if errorIndication: + yield errorIndication, errorStatus, errorIndex, \ + varBindTable and varBindTable[0] or [] + if errorIndication != errind.requestTimedOut: + return + elif errorStatus: + if errorStatus == 2: + # Hide SNMPv1 noSuchName error which leaks in here + # from SNMPv1 Agent through internal pysnmp proxy. + errorStatus = errorStatus.clone(0) + errorIndex = errorIndex.clone(0) + yield errorIndication, errorStatus, errorIndex, \ + varBindTable and varBindTable[0] or [] + return + else: + for i in range(len(varBindTable)): + stopFlag = True + if len(varBindTable[i]) != len(initialVars): + varBindTable = i and varBindTable[:i-1] or [] + break + for j in range(len(varBindTable[i])): + name, val = varBindTable[i][j] + if nullVarBinds[j]: + varBindTable[i][j] = name, endOfMibView + continue + stopFlag = False + if isinstance(val, Null): + nullVarBinds[j] = True + elif not lexicographicMode and \ + not initialVars[j].isPrefixOf(name): + varBindTable[i][j] = name, endOfMibView + nullVarBinds[j] = True + if stopFlag: + varBindTable = i and varBindTable[:i-1] or [] + break + + totalRows += len(varBindTable) + totalCalls += 1 + + if maxRows and totalRows >= maxRows: + if totalRows > maxRows: + varBindTable = varBindTable[:-(totalRows-maxRows)] + stopFlag = True + + if maxCalls and totalCalls >= maxCalls: + stopFlag = True + + for varBinds in varBindTable: + initialVarBinds = ( yield errorIndication, errorStatus, + errorIndex, varBinds ) + + if initialVarBinds: + varBinds = initialVarBinds + initialVars = [ x[0] for x in vbProcessor.makeVarBinds(snmpEngine, varBinds) ] diff --git a/pysnmp/hlapi/asyncore/_sync/compat/cmdgen.py b/pysnmp/hlapi/asyncore/_sync/compat/cmdgen.py new file mode 100644 index 0000000..530c376 --- /dev/null +++ b/pysnmp/hlapi/asyncore/_sync/compat/cmdgen.py @@ -0,0 +1,266 @@ +# +# This is a Python 2.6- version of the same file at level up +# +from pysnmp.hlapi.asyncore.cmdgen import * +from pysnmp.hlapi.varbinds import * +from pysnmp.proto.rfc1905 import endOfMibView +from pysnmp.proto.errind import * +from pyasn1.type.univ import Null + +__all__ = ['getCmd', 'nextCmd', 'setCmd', 'bulkCmd', 'next'] + +def next(iter): + return iter.next() + +def getCmd(snmpEngine, authData, transportTarget, contextData, + *varBinds, **options): + + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBinds'] = varBinds + + cbCtx = {} + + cmdGen = AsyncCommandGenerator() + + if varBinds: + cmdGen.getCmd( + snmpEngine, + authData, + transportTarget, + contextData, + varBinds, + (cbFun, cbCtx), + options.get('lookupMib', True) + ) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx['errorIndication'] + errorStatus = cbCtx['errorStatus'] + errorIndex = cbCtx['errorIndex'] + varBinds = cbCtx['varBinds'] + else: + errorIndication = errorStatus = errorIndex = None + varBinds = [] + + yield errorIndication, errorStatus, errorIndex, varBinds + +def setCmd(snmpEngine, authData, transportTarget, contextData, + *varBinds, **options): + + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBinds'] = varBinds + + cbCtx = {} + + cmdGen = AsyncCommandGenerator() + + while True: + cmdGen.setCmd( + snmpEngine, + authData, + transportTarget, + contextData, + varBinds, + (cbFun, cbCtx), + options.get('lookupMib', True) + ) + + snmpEngine.transportDispatcher.runDispatcher() + + yield cbCtx['errorIndication'], \ + cbCtx['errorStatus'], cbCtx['errorIndex'], \ + cbCtx['varBinds'] + + if cbCtx['errorIndication'] != errind.requestTimedOut: + break + +def nextCmd(snmpEngine, authData, transportTarget, contextData, + *varBinds, **options): + + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBindTable, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBindTable'] = varBindTable + + lookupMib = options.get('lookupMib', True) + lexicographicMode = options.get('lexicographicMode', True) + ignoreNonIncreasingOid = options.get('ignoreNonIncreasingOid', False) + maxRows = options.get('maxRows', 0) + maxCalls = options.get('maxCalls', 0) + + cbCtx = {} + + vbProcessor = CommandGeneratorVarBinds() + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in vbProcessor.makeVarBinds(snmpEngine, varBinds) ] + + totalRows = totalCalls = 0 + + while True: + cmdGen.nextCmd(snmpEngine, + authData, + transportTarget, + contextData, + [ (x[0], Null()) for x in varBinds ], + (cbFun, cbCtx), + lookupMib) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx['errorIndication'] + errorStatus = cbCtx['errorStatus'] + errorIndex = cbCtx['errorIndex'] + + if ignoreNonIncreasingOid and errorIndication and \ + isinstance(errorIndication, errind.OidNotIncreasing): + errorIndication = None + + if errorIndication: + yield errorIndication, errorStatus, errorIndex, varBinds + if errorIndication != errind.requestTimedOut: + return + elif errorStatus: + if errorStatus == 2: + # Hide SNMPv1 noSuchName error which leaks in here + # from SNMPv1 Agent through internal pysnmp proxy. + errorStatus = errorStatus.clone(0) + errorIndex = errorIndex.clone(0) + yield errorIndication, errorStatus, errorIndex, varBinds + return + else: + varBinds = cbCtx['varBindTable'] and cbCtx['varBindTable'][0] + for idx, varBind in enumerate(varBinds): + name, val = varBind + if not isinstance(val, Null): + if lexicographicMode or initialVars[idx].isPrefixOf(name): + break + else: + return + + totalRows += 1 + totalCalls += 1 + + yield errorIndication, errorStatus, errorIndex, varBinds + + if maxRows and totalRows >= maxRows or \ + maxCalls and totalCalls >= maxCalls: + return + +def bulkCmd(snmpEngine, authData, transportTarget, contextData, + nonRepeaters, maxRepetitions, *varBinds, **options): + + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBindTable, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBindTable'] = varBindTable + + lookupMib = options.get('lookupMib', True) + lexicographicMode = options.get('lexicographicMode', True) + ignoreNonIncreasingOid = options.get('ignoreNonIncreasingOid', False) + maxRows = options.get('maxRows', 0) + maxCalls = options.get('maxCalls', 0) + + cbCtx = {} + + vbProcessor = CommandGeneratorVarBinds() + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in vbProcessor.makeVarBinds(snmpEngine, varBinds) ] + nullVarBinds = [ False ] * len(initialVars) + + totalRows = totalCalls = 0 + stopFlag = False + + while not stopFlag: + if maxRows and totalRows < maxRows: + maxRepetitions = min(maxRepetitions, maxRows-totalRows) + + cmdGen.bulkCmd(snmpEngine, + authData, + transportTarget, + contextData, + nonRepeaters, maxRepetitions, + [ (x[0], Null()) for x in varBinds ], + (cbFun, cbCtx), + lookupMib) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx['errorIndication'] + errorStatus = cbCtx['errorStatus'] + errorIndex = cbCtx['errorIndex'] + varBindTable = cbCtx['varBindTable'] + + if ignoreNonIncreasingOid and errorIndication and \ + isinstance(errorIndication, errind.OidNotIncreasing): + errorIndication = None + + if errorIndication: + yield errorIndication, errorStatus, errorIndex, \ + varBindTable and varBindTable[0] or [] + if errorIndication != errind.requestTimedOut: + return + elif errorStatus: + if errorStatus == 2: + # Hide SNMPv1 noSuchName error which leaks in here + # from SNMPv1 Agent through internal pysnmp proxy. + errorStatus = errorStatus.clone(0) + errorIndex = errorIndex.clone(0) + yield errorIndication, errorStatus, errorIndex, \ + varBindTable and varBindTable[0] or [] + return + else: + for i in range(len(varBindTable)): + stopFlag = True + if len(varBindTable[i]) != len(initialVars): + varBindTable = i and varBindTable[:i-1] or [] + break + for j in range(len(varBindTable[i])): + name, val = varBindTable[i][j] + if nullVarBinds[j]: + varBindTable[i][j] = name, endOfMibView + continue + stopFlag = False + if isinstance(val, Null): + nullVarBinds[j] = True + elif not lexicographicMode and \ + not initialVars[j].isPrefixOf(name): + varBindTable[i][j] = name, endOfMibView + nullVarBinds[j] = True + if stopFlag: + varBindTable = i and varBindTable[:i-1] or [] + break + + totalRows += len(varBindTable) + totalCalls += 1 + + if maxRows and totalRows >= maxRows: + if totalRows > maxRows: + varBindTable = varBindTable[:-(totalRows-maxRows)] + stopFlag = True + + if maxCalls and totalCalls >= maxCalls: + stopFlag = True + + for varBinds in varBindTable: + yield errorIndication, errorStatus, errorIndex, varBinds diff --git a/pysnmp/hlapi/asyncore/_sync/compat/ntforg.py b/pysnmp/hlapi/asyncore/_sync/compat/ntforg.py new file mode 100644 index 0000000..420b3b6 --- /dev/null +++ b/pysnmp/hlapi/asyncore/_sync/compat/ntforg.py @@ -0,0 +1,52 @@ +# +# This is a Python 2.6- version of the same file at level up +# +from pysnmp.hlapi.asyncore.ntforg import * + +__all__ = ['sendNotification', 'next'] + +def next(iter): + return iter.next() + +# +# Synchronous one-liner SNMP Notification Originator application +# + +def sendNotification(snmpEngine, authData, transportTarget, contextData, + notifyType, varBinds, **options): + + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBinds'] = varBinds + + cbCtx = {} + + ntfOrg = AsyncNotificationOriginator() + + if varBinds: + ntfOrg.sendNotification( + snmpEngine, + authData, + transportTarget, + contextData, + notifyType, + varBinds, + (cbFun, cbCtx), + options.get('lookupMib', True) + ) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx.get('errorIndication') + errorStatus = cbCtx.get('errorStatus') + errorIndex = cbCtx.get('errorIndex') + varBinds = cbCtx.get('varBinds', []) + else: + errorIndication = errorStatus = errorIndex = None + varBinds = [] + + yield errorIndication, errorStatus, errorIndex, varBinds diff --git a/pysnmp/hlapi/asyncore/_sync/ntforg.py b/pysnmp/hlapi/asyncore/_sync/ntforg.py new file mode 100644 index 0000000..bec88cf --- /dev/null +++ b/pysnmp/hlapi/asyncore/_sync/ntforg.py @@ -0,0 +1,124 @@ +from pysnmp.hlapi.asyncore.ntforg import * + +__all__ = ['sendNotification'] + +def sendNotification(snmpEngine, authData, transportTarget, contextData, + notifyType, varBinds, **options): + """Creates a generator to send one or more SNMP notifications. + + On each iteration, new SNMP TRAP or INFORM notification is send + (:RFC:`1905#section-4,2,6`). The iterator blocks waiting for + INFORM acknowlegement to arrive or error to occur. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName values. + + notifyType : str + Indicates type of notification to be sent. Recognized literal + values are *trap* or *inform*. + + varBinds: tuple + Single :py:class:`~pysnmp.smi.rfc1902.NotificationType` class instance + representing a minimum sequence of MIB variables required for + particular notification type. + Alternatively, a sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` + objects could be passed instead. In the latter case it is up to + the user to ensure proper Notification PDU contents. + + Other Parameters + ---------------- + \*\*options : + Request options: + + * `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + + Yields + ------ + errorIndication : str + True value indicates SNMP engine error. + errorStatus : str + True value indicates SNMP PDU error. + errorIndex : int + Non-zero value refers to `varBinds[errorIndex-1]` + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables returned in SNMP response. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Notes + ----- + The `sendNotification` generator will be exhausted immidiately unless + an instance of :py:class:`~pysnmp.smi.rfc1902.NotificationType` class + or a sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` `varBinds` + are send back into running generator (supported since Python 2.6). + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.ntforg import * + >>> g = sendNotification(SnmpEngine(), + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 162)), + ... ContextData(), + ... 'trap', + ... NotificationType(ObjectIdentity('IF-MIB', 'linkDown'))) + >>> next(g) + (None, 0, 0, []) + >>> + + """ + def cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + cbCtx['errorIndication'] = errorIndication + cbCtx['errorStatus'] = errorStatus + cbCtx['errorIndex'] = errorIndex + cbCtx['varBinds'] = varBinds + + cbCtx = {} + + ntfOrg = AsyncNotificationOriginator() + + while True: + if varBinds: + ntfOrg.sendNotification( + snmpEngine, + authData, + transportTarget, + contextData, + notifyType, + varBinds, + cbInfo=(cbFun, cbCtx), + lookupMib=options.get('lookupMib', True) + ) + + snmpEngine.transportDispatcher.runDispatcher() + + errorIndication = cbCtx.get('errorIndication') + errorStatus = cbCtx.get('errorStatus') + errorIndex = cbCtx.get('errorIndex') + varBinds = cbCtx.get('varBinds', []) + else: + errorIndication = errorStatus = errorIndex = None + varBinds = [] + + varBinds = ( yield errorIndication, errorStatus, errorIndex, varBinds ) + + if not varBinds: + break diff --git a/pysnmp/hlapi/asyncore/cmdgen.py b/pysnmp/hlapi/asyncore/cmdgen.py new file mode 100644 index 0000000..850dade --- /dev/null +++ b/pysnmp/hlapi/asyncore/cmdgen.py @@ -0,0 +1,529 @@ +from sys import version_info +from pysnmp.entity import config +from pysnmp.entity.rfc3413 import cmdgen +from pysnmp.smi.rfc1902 import * +from pysnmp.hlapi.auth import * +from pysnmp.hlapi.context import * +from pysnmp.hlapi.lcd import * +from pysnmp.hlapi.varbinds import * +from pysnmp.hlapi.asyncore.transport import * +from pysnmp.proto import rfc1905, errind +from pyasn1.type import univ, base + +__all__ = ['AsyncCommandGenerator'] + +class AsyncCommandGenerator: + """Creates asyncore-based SNMP Command Generator object. + + This is a high-level wrapper around pure Command Generator + impementation that aims at simplyfing + :py:class:`pysnmp.entity.engine.SnmpEngine`'s Local Configuration + Datastore (:RFC:`2271#section-3.4.2`) management. Typically, + users instantiate `AsyncCommandGenerator` and call its + commmand-specific methods passing them canned Security, + Transport and SNMP Context parameters along with + :py:class:`~pysnmp.smi.rfc1902.ObjectType` object carrying + MIB variables to include with SNMP request. `AsyncCommandGenerator` + will manage LCD by applying user-supplied configuratoin parameters + and running requested operation. + + See :RFC:`3413#section-3.1` for more information on SNMP + Command Generator purpose, design and supported operations. + + """ + vbProcessor = CommandGeneratorVarBinds() + lcd = CommandGeneratorLcdConfigurator() + + def getCmd(self, snmpEngine, authData, transportTarget, contextData, + varBinds, cbInfo, lookupMib=True): + """Performs SNMP GET query. + + Based on passed parameters, prepares SNMP GET packet + (:RFC:`1905#section-4.2.1`) and schedules its transmission by + I/O framework at a later point of time. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer + address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName + values. + + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables to place into SNMP request. + + cbInfo : tuple + + * `cbFun` - user-supplied callable that is invoked to pass + SNMP response data or error to user at a later point of time. + * `cbCtx` - user-supplied object passing additional parameters + to/from `cbFun`. Default is `None`. + + Other Parameters + ---------------- + lookupMib : bool + `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + + Notes + ----- + User-supplied `cbFun` callable must have the following call + signature: + + * snmpEngine (:py:class:`~pysnmp.entity.engine.SnmpEngine`): + Class instance representing SNMP engine. + * sendRequestHandle (int): Unique request identifier. Can be used + for matching multiple ongoing requests with received responses. + * errorIndication (str): True value indicates SNMP engine error. + * errorStatus (str): True value indicates SNMP PDU error. + * errorIndex (int): Non-zero value refers to `varBinds[errorIndex-1]` + * varBinds (tuple): A sequence of + :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances + representing MIB variables returned in SNMP response in exactly + the same order as `varBinds` in request. + * `cbCtx` : Original user-supplied object. + + Returns + ------- + sendRequestHandle : int + Unique request identifier. Can be used for matching received + responses with ongoing requests. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> def cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBinds, cbCtx): + ... print(errorIndication, errorStatus, errorIndex, varBinds) + >>> + >>> snmpEngine = SnmpEngine() + >>> g = AsyncCommandGenerator() + >>> g.getCmd(snmpEngine, + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)), + ... (cbFun, None)) + >>> snmpEngine.transportDispatcher.runDispatcher() + (None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.1.0')), DisplayString('SunOS zeus.snmplabs.com 4.1.3_U1 1 sun4m'))]) + >>> + + """ + def __cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + lookupMib, cbFun, cbCtx = cbCtx + return cbFun( + snmpEngine, + sendRequestHandle, + errorIndication, + errorStatus, + errorIndex, + self.vbProcessor.unmakeVarBinds( + snmpEngine, varBinds, lookupMib + ), + cbCtx + ) + + (cbFun, cbCtx) = cbInfo + addrName, paramsName = self.lcd.configure( + snmpEngine, authData, transportTarget + ) + + return cmdgen.GetCommandGenerator().sendVarBinds( + snmpEngine, + addrName, + contextData.contextEngineId, + contextData.contextName, + self.vbProcessor.makeVarBinds(snmpEngine, varBinds), + __cbFun, + (lookupMib, cbFun, cbCtx) + ) + + def setCmd(self, snmpEngine, authData, transportTarget, contextData, + varBinds, cbInfo, lookupMib=True): + """Performs SNMP SET query. + + Based on passed parameters, prepares SNMP SET packet + (:RFC:`1905#section-4.2.5`) and schedules its transmission by + I/O framework at a later point of time. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer + address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName + values. + + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables to place into SNMP request. + + cbInfo : tuple + + * `cbFun` - user-supplied callable that is invoked to pass + SNMP response data or error to user at a later point of time. + * `cbCtx` - user-supplied object passing additional parameters + to/from `cbFun`. Default is `None`. + + Other Parameters + ---------------- + lookupMib : bool + `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + + Notes + ----- + User-supplied `cbFun` callable must have the following call + signature: + + * snmpEngine (:py:class:`~pysnmp.entity.engine.SnmpEngine`): + Class instance representing SNMP engine. + * sendRequestHandle (int): Unique request identifier. Can be used + for matching multiple ongoing requests with received responses. + * errorIndication (str): True value indicates SNMP engine error. + * errorStatus (str): True value indicates SNMP PDU error. + * errorIndex (int): Non-zero value refers to `varBinds[errorIndex-1]` + * varBinds (tuple): A sequence of + :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances + representing MIB variables returned in SNMP response in exactly + the same order as `varBinds` in request. + * `cbCtx` : Original user-supplied object. + + Returns + ------- + sendRequestHandle : int + Unique request identifier. Can be used for matching received + responses with ongoing requests. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> def cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBinds, cbCtx): + ... print(errorIndication, errorStatus, errorIndex, varBinds) + >>> + >>> snmpEngine = SnmpEngine() + >>> g = AsyncCommandGenerator() + >>> g.setCmd(snmpEngine, + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysContact', 0), 'info@snmplabs.com'), + ... (cbFun, None)) + >>> snmpEngine.transportDispatcher.runDispatcher() + (None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.4.0')), DisplayString('info@snmplabs.com'))]) + >>> + + """ + def __cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBinds, cbCtx): + lookupMib, cbFun, cbCtx = cbCtx + return cbFun( + snmpEngine, + sendRequestHandle, + errorIndication, + errorStatus, + errorIndex, + self.vbProcessor.unmakeVarBinds( + snmpEngine, varBinds, lookupMib + ), + cbCtx + ) + + (cbFun, cbCtx) = cbInfo + addrName, paramsName = self.lcd.configure( + snmpEngine, authData, transportTarget + ) + + return cmdgen.SetCommandGenerator().sendVarBinds( + snmpEngine, + addrName, + contextData.contextEngineId, + contextData.contextName, + self.vbProcessor.makeVarBinds(snmpEngine, varBinds), + __cbFun, + (lookupMib, cbFun, cbCtx) + ) + + def nextCmd(self, snmpEngine, authData, transportTarget, contextData, + varBinds, cbInfo, lookupMib=True): + """Performs SNMP GETNEXT query. + + Based on passed parameters, prepares SNMP GETNEXT packet + (:RFC:`1905#section-4.2.2`) and schedules its transmission by + I/O framework at a later point of time. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer + address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName + values. + + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables to place into SNMP request. + + cbInfo : tuple + + * `cbFun` - user-supplied callable that is invoked to pass + SNMP response data or error to user at a later point of time. + * `cbCtx` - user-supplied object passing additional parameters + to/from `cbFun`. Default is `None`. + + Other Parameters + ---------------- + lookupMib : bool + `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + + Notes + ----- + User-supplied `cbFun` callable must have the following call + signature: + + * snmpEngine (:py:class:`~pysnmp.entity.engine.SnmpEngine`): + Class instance representing SNMP engine. + * sendRequestHandle (int): Unique request identifier. Can be used + for matching multiple ongoing requests with received responses. + * errorIndication (str): True value indicates SNMP engine error. + * errorStatus (str): True value indicates SNMP PDU error. + * errorIndex (int): Non-zero value refers to `varBinds[errorIndex-1]` + * varBinds (tuple): A sequence of sequences (e.g. 2-D array) of + :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances + representing a table of MIB variables returned in SNMP response. + Inner sequences represent table rows and ordered exactly the same + as `varBinds` in request. Response to GETNEXT always contain a + single row. + * `cbCtx` : Original user-supplied object. + + Returns + ------- + sendRequestHandle : int + Unique request identifier. Can be used for matching received + responses with ongoing requests. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> def cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBinds, cbCtx): + ... print(errorIndication, errorStatus, errorIndex, varBinds) + >>> + >>> snmpEngine = SnmpEngine() + >>> g = AsyncCommandGenerator() + >>> g.nextCmd(snmpEngine, + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')), + ... (cbFun, None)) + >>> snmpEngine.transportDispatcher.runDispatcher() + (None, 0, 0, [ [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.1.0')), DisplayString('SunOS zeus.snmplabs.com 4.1.3_U1 1 sun4m'))] ]) + >>> + + """ + def __cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBindTable, cbCtx): + lookupMib, cbFun, cbCtx = cbCtx + return cbFun( + snmpEngine, + sendRequestHandle, + errorIndication, + errorStatus, + errorIndex, + [ self.vbProcessor.unmakeVarBinds(snmpEngine, varBindTableRow, lookupMib) for varBindTableRow in varBindTable ], + cbCtx + ) + + (cbFun, cbCtx) = cbInfo + addrName, paramsName = self.lcd.configure( + snmpEngine, authData, transportTarget + ) + return cmdgen.NextCommandGenerator().sendVarBinds( + snmpEngine, + addrName, + contextData.contextEngineId, contextData.contextName, + self.vbProcessor.makeVarBinds(snmpEngine, varBinds), + __cbFun, + (lookupMib, cbFun, cbCtx) + ) + + def bulkCmd(self, snmpEngine, authData, transportTarget, contextData, + nonRepeaters, maxRepetitions, varBinds, cbInfo, + lookupMib=True): + """Performs SNMP GETBULK query. + + Based on passed parameters, prepares SNMP GETBULK packet + (:RFC:`1905#section-4.2.3`) and schedules its transmission by + I/O framework at a later point of time. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer + address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName + values. + + nonRepeaters : int + One MIB variable is requested in response for the first + `nonRepeaters` MIB variables in request. + + maxRepetitions : int + `maxRepetitions` MIB variables are requested in response for each + of the remaining MIB variables in the request (e.g. excluding + `nonRepeaters`). Remote SNMP engine may choose lesser value than + requested. + + varBinds : tuple + A sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances representing MIB variables to place into SNMP request. + + cbInfo : tuple + + * `cbFun` - user-supplied callable that is invoked to pass + SNMP response data or error to user at a later point of time. + * `cbCtx` - user-supplied object passing additional parameters + to/from `cbFun`. Default is `None`. + + Other Parameters + ---------------- + lookupMib : bool + `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + + Notes + ----- + User-supplied `cbFun` callable must have the following call + signature: + + * snmpEngine (:py:class:`~pysnmp.entity.engine.SnmpEngine`): + Class instance representing SNMP engine. + * sendRequestHandle (int): Unique request identifier. Can be used + for matching multiple ongoing requests with received responses. + * errorIndication (str): True value indicates SNMP engine error. + * errorStatus (str): True value indicates SNMP PDU error. + * errorIndex (int): Non-zero value refers to `varBinds[errorIndex-1]` + * varBinds (tuple): A sequence of sequences (e.g. 2-D array) of + :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances + representing a table of MIB variables returned in SNMP response. + Inner sequences represent table rows and ordered exactly the same + as `varBinds` in request. Number of rows might be less or equal + to `maxRepetitions` value in request. + * `cbCtx` : Original user-supplied object. + + Returns + ------- + sendRequestHandle : int + Unique request identifier. Can be used for matching received + responses with ongoing requests. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.cmdgen import * + >>> def cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBinds, cbCtx): + ... print(errorIndication, errorStatus, errorIndex, varBinds) + >>> + >>> snmpEngine = SnmpEngine() + >>> g = AsyncCommandGenerator() + >>> g.bulkCmd(snmpEngine, + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 161)), + ... ContextData(), + ... 0, 2, + ... ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')), + ... (cbFun, None)) + >>> snmpEngine.transportDispatcher.runDispatcher() + (None, 0, 0, [ [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.1.0')), DisplayString('SunOS zeus.snmplabs.com 4.1.3_U1 1 sun4m')), ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.1.2.0')), ObjectIdentifier('1.3.6.1.4.1.424242.1.1')] ]) + >>> + + """ + def __cbFun(snmpEngine, sendRequestHandle, + errorIndication, errorStatus, errorIndex, + varBindTable, cbCtx): + lookupMib, cbFun, cbCtx = cbCtx + return cbFun( + snmpEngine, + sendRequestHandle, + errorIndication, + errorStatus, + errorIndex, + [ self.vbProcessor.unmakeVarBinds(snmpEngine, varBindTableRow, lookupMib) for varBindTableRow in varBindTable ], + cbCtx + ) + + (cbFun, cbCtx) = cbInfo + addrName, paramsName = self.lcd.configure( + snmpEngine, authData, transportTarget + ) + return cmdgen.BulkCommandGenerator().sendVarBinds( + snmpEngine, + addrName, + contextData.contextEngineId, + contextData.contextName, + nonRepeaters, maxRepetitions, + self.vbProcessor.makeVarBinds(snmpEngine, varBinds), + __cbFun, + (lookupMib, cbFun, cbCtx) + ) diff --git a/pysnmp/hlapi/asyncore/ntforg.py b/pysnmp/hlapi/asyncore/ntforg.py new file mode 100644 index 0000000..6536b9c --- /dev/null +++ b/pysnmp/hlapi/asyncore/ntforg.py @@ -0,0 +1,169 @@ +from pysnmp.entity import engine, config +from pysnmp.smi.rfc1902 import * +from pysnmp.entity.rfc3413 import ntforg, context +from pysnmp.hlapi.auth import * +from pysnmp.hlapi.context import * +from pysnmp.hlapi.lcd import * +from pysnmp.hlapi.varbinds import * +from pysnmp.hlapi.asyncore.transport import * +from pysnmp.hlapi.asyncore import cmdgen +from pysnmp import error + +__all__ = ['AsyncNotificationOriginator'] + +class AsyncNotificationOriginator: + """Creates asyncore-based SNMP Notification Originator object. + + This is a high-level wrapper around pure Notification Originator + impementation that aims at simplyfing + :py:class:`pysnmp.entity.engine.SnmpEngine`'s Local Configuration + Datastore (:RFC:`2271#section-3.4.2`) management. Typically, + users instantiate `AsyncNotificationOriginator` and call its + commmand-specific methods passing them canned Security, + Transport and SNMP Context parameters along with + :py:class:`~pysnmp.smi.rfc1902.NotificationType` object carrying + MIB variables to include with Notification. `AsyncNotificationOriginator` + will manage LCD by applying user-supplied configuratoin parameters + and running requested operation. + + See :RFC:`3413#section-3.2` for more information on SNMP + Notification Originator purpose, design and supported operations. + + """ + vbProcessor = NotificationOriginatorVarBinds() + lcd = NotificationOriginatorLcdConfigurator() + + def sendNotification(self, snmpEngine, + authData, transportTarget, contextData, + notifyType, + varBinds, + cbInfo=(None, None), + lookupMib=False): + """Send SNMP notification. + + Based on passed parameters, prepares SNMP TRAP or INFORM + notification (:RFC:`1905#section-4.2.6`) and schedules its + transmission by I/O framework at a later point of time. + + Parameters + ---------- + snmpEngine : :py:class:`~pysnmp.entity.engine.SnmpEngine` + Class instance representing SNMP engine. + + authData : :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.CommunityData` or :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.UsmUserData` + Class instance representing SNMP credentials. + + transportTarget : :py:class:`~pysnmp.entity.rfc3413.oneliner.target.UdpTransportTarget` or :py:class:`~pysnmp.entity.rfc3413.oneliner.target.Udp6TransportTarget` + Class instance representing transport type along with SNMP peer + address. + + contextData : :py:class:`~pysnmp.entity.rfc3413.oneliner.ctx.ContextData` + Class instance representing SNMP ContextEngineId and ContextName + values. + + notifyType : str + Indicates type of notification to be sent. Recognized literal + values are *trap* or *inform*. + + varBinds: tuple + Single :py:class:`~pysnmp.smi.rfc1902.NotificationType` class + instance representing a minimum sequence of MIB variables + required for particular notification type. Alternatively, + a sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` + objects could be passed instead. In the latter case it is up to + the user to ensure proper Notification PDU contents. + + cbInfo : tuple + + * `cbFun` - user-supplied callable that is invoked to pass + SNMP response to *INFORM* notification or error to user at + a later point of time. The `cbFun` callable is never invoked + for *TRAP* notifications. + * `cbCtx` - user-supplied object passing additional parameters + to/from `cbFun`. Default is `None`. + + Other Parameters + ---------------- + lookupMib : bool + `lookupMib` - load MIB and resolve response MIB variables at + the cost of slightly reduced performance. Default is `True`. + + Notes + ----- + User-supplied `cbFun` callable must have the following call + signature: + + * snmpEngine (:py:class:`~pysnmp.entity.engine.SnmpEngine`): + Class instance representing SNMP engine. + * sendRequestHandle (int): Unique request identifier. Can be used + for matching multiple ongoing *INFORM* notifications with received + responses. + * errorIndication (str): True value indicates SNMP engine error. + * errorStatus (str): True value indicates SNMP PDU error. + * errorIndex (int): Non-zero value refers to `varBinds[errorIndex-1]` + * varBinds (tuple): A sequence of + :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances + representing MIB variables returned in SNMP response in exactly + the same order as `varBinds` in request. + * `cbCtx` : Original user-supplied object. + + Returns + ------- + sendRequestHandle : int + Unique request identifier. Can be used for matching received + responses with ongoing *INFORM* requests. Returns `None` for + *TRAP* notifications. + + Raises + ------ + PySnmpError + Or its derivative indicating that an error occurred while + performing SNMP operation. + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.ntforg import * + >>> + >>> snmpEngine = SnmpEngine() + >>> n = AsyncNotificationOriginator() + >>> n.sendNotification( + ... snmpEngine, + ... CommunityData('public'), + ... UdpTransportTarget(('demo.snmplabs.com', 162)), + ... ContextData(), + ... 'trap', + ... NotificationType(ObjectIdentity('SNMPv2-MIB', 'coldStart')), + ... ) + >>> snmpEngine.transportDispatcher.runDispatcher() + >>> + + """ + def __cbFun(snmpEngine, sendRequestHandle, errorIndication, + errorStatus, errorIndex, varBinds, cbCtx): + lookupMib, cbFun, cbCtx = cbCtx + return cbFun and cbFun( + snmpEngine, + sendRequestHandle, + errorIndication, + errorStatus, errorIndex, + self.vbProcessor.unmakeVarBinds( + snmpEngine, varBinds, lookupMib + ), + cbCtx + ) + + cbFun, cbCtx = cbInfo + + # Create matching transport tags if not given by user + if not transportTarget.tagList: + transportTarget.tagList = str( + hash((authData.securityName, transportTarget.transportAddr)) + ) + if isinstance(authData, CommunityData) and not authData.tag: + authData.tag = transportTarget.tagList.split()[0] + + notifyName = self.lcd.configure( + snmpEngine, authData, transportTarget, notifyType + ) + + return ntforg.NotificationOriginator().sendVarBinds(snmpEngine, notifyName, contextData.contextEngineId, contextData.contextName, self.vbProcessor.makeVarBinds(snmpEngine, varBinds), __cbFun, (lookupMib, cbFun, cbCtx)) diff --git a/pysnmp/hlapi/asyncore/transport.py b/pysnmp/hlapi/asyncore/transport.py new file mode 100644 index 0000000..f77f575 --- /dev/null +++ b/pysnmp/hlapi/asyncore/transport.py @@ -0,0 +1,150 @@ +import socket, sys +from pysnmp.carrier.asyncore.dgram import udp, udp6, unix +from pysnmp import error +from pyasn1.compat.octets import null + +__all__ = ['UnixTransportTarget', 'Udp6TransportTarget', 'UdpTransportTarget'] + +class _AbstractTransportTarget: + transportDomain = None + protoTransport = NotImplementedError + def __init__(self, transportAddr, timeout=1, retries=5, tagList=null): + self.transportAddr = self._resolveAddr(transportAddr) + self.timeout = timeout + self.retries = retries + self.tagList = tagList + self.iface = None + + def __repr__(self): + return '%s(%r, timeout=%r, retries=%r, tagList=%r)' % ( + self.__class__.__name__, self.transportAddr, + self.timeout, self.retries, self.tagList + ) + + def getTransportInfo(self): + return self.transportDomain, self.transportAddr + + def setLocalAddress(self, iface): + self.iface = iface + return self + + def openClientMode(self): + self.transport = self.protoTransport().openClientMode(self.iface) + return self.transport + + def verifyDispatcherCompatibility(self, snmpEngine): + if not self.protoTransport.isCompatibleWithDispatcher(snmpEngine.transportDispatcher): + raise error.PySnmpError('Transport %r is not compatible with dispatcher %r' % (self.protoTransport, snmpEngine.transportDispatcher)) + + def _resolveAddr(self, transportAddr): raise NotImplementedError() + +class UdpTransportTarget(_AbstractTransportTarget): + """Creates UDP/IPv4 configuration entry and initialize socket API if needed. + + This object can be used by + :py:class:`~pysnmp.entity.rfc3413.oneliner.cmdgen.AsyncCommandGenerator` or + :py:class:`~pysnmp.entity.rfc3413.oneliner.ntforg.AsyncNotificationOriginator` + and their derevatives for adding new entries to Local Configuration + Datastore (LCD) managed by :py:class:`~pysnmp.entity.engine.SnmpEngine` + class instance. + + See :RFC:`1906#section-3` for more information on the UDP transport mapping. + + Parameters + ---------- + transportAddr : tuple + Indicates remote address in Python :py:mod:`socket` module format + which is a tuple of FQDN, port where FQDN is a string representing + either hostname or IPv4 address in quad-dotted form, port is an + integer. + timeout : int + Response timeout in seconds. + retries : int + Maximum number of request retries, 0 retries means just a single + request. + tagList : str + Arbitrary string that contains a list of tag values which are used + to select target addresses for a particular operation + (:RFC:`3413#section-4.1.4`). + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.target import UdpTransportTarget + >>> UdpTransportTarget(('demo.snmplabs.com', 161)) + UdpTransportTarget(('195.218.195.228', 161), timeout=1, retries=5, tagList='') + >>> + + """ + transportDomain = udp.domainName + protoTransport = udp.UdpSocketTransport + def _resolveAddr(self, transportAddr): + try: + return socket.getaddrinfo(transportAddr[0], + transportAddr[1], + socket.AF_INET, + socket.SOCK_DGRAM, + socket.IPPROTO_UDP)[0][4][:2] + except socket.gaierror: + raise error.PySnmpError('Bad IPv4/UDP transport address %s: %s' % ('@'.join([ str(x) for x in transportAddr ]), sys.exc_info()[1])) + +class Udp6TransportTarget(_AbstractTransportTarget): + """Creates UDP/IPv6 configuration entry and initialize socket API if needed. + + This object can be used by + :py:class:`~pysnmp.entity.rfc3413.oneliner.cmdgen.AsyncCommandGenerator` or + :py:class:`~pysnmp.entity.rfc3413.oneliner.ntforg.AsyncNotificationOriginator` + and their derevatives for adding new entries to Local Configuration + Datastore (LCD) managed by :py:class:`~pysnmp.entity.engine.SnmpEngine` + class instance. + + See :RFC:`1906#section-3`, :RFC:`2851#section-4` for more information + on the UDP and IPv6 transport mapping. + + Parameters + ---------- + transportAddr : tuple + Indicates remote address in Python :py:mod:`socket` module format + which is a tuple of FQDN, port where FQDN is a string representing + either hostname or IPv6 address in one of three conventional forms + (:RFC:`1924#section-3`), port is an integer. + timeout : int + Response timeout in seconds. + retries : int + Maximum number of request retries, 0 retries means just a single + request. + tagList : str + Arbitrary string that contains a list of tag values which are used + to select target addresses for a particular operation + (:RFC:`3413#section-4.1.4`). + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.target import Udp6TransportTarget + >>> Udp6TransportTarget(('google.com', 161)) + Udp6TransportTarget(('2a00:1450:4014:80a::100e', 161), timeout=1, retries=5, tagList='') + >>> Udp6TransportTarget(('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', 161)) + Udp6TransportTarget(('fedc:ba98:7654:3210:fedc:ba98:7654:3210', 161), timeout=1, retries=5, tagList='') + >>> Udp6TransportTarget(('1080:0:0:0:8:800:200C:417A', 161)) + Udp6TransportTarget(('1080::8:800:200c:417a', 161), timeout=1, retries=5, tagList='') + >>> Udp6TransportTarget(('::0', 161)) + Udp6TransportTarget(('::', 161), timeout=1, retries=5, tagList='') + >>> Udp6TransportTarget(('::', 161)) + Udp6TransportTarget(('::', 161), timeout=1, retries=5, tagList='') + >>> + + """ + transportDomain = udp6.domainName + protoTransport = udp6.Udp6SocketTransport + def _resolveAddr(self, transportAddr): + try: + return socket.getaddrinfo(transportAddr[0], + transportAddr[1], + socket.AF_INET6, + socket.SOCK_DGRAM, + socket.IPPROTO_UDP)[0][4][:2] + except socket.gaierror: + raise error.PySnmpError('Bad IPv6/UDP transport address %s: %s' % ('@'.join([ str(x) for x in transportAddr ]), sys.exc_info()[1])) + +class UnixTransportTarget(_AbstractTransportTarget): + transportDomain = unix.domainName + protoTransport = unix.UnixSocketTransport |