From 2f24177c361ba4d9deba2d7378f84aa96111db80 Mon Sep 17 00:00:00 2001 From: elie Date: Mon, 14 Sep 2015 05:20:24 +0000 Subject: * synchronous oneliner apps redesigned to offer Python generator-based API along with a more comprehensive set of accepted parameters. * massively documented (in Sphinx, NumPy style) --- pysnmp/entity/rfc3413/oneliner/auth.py | 126 ++++- pysnmp/entity/rfc3413/oneliner/cmdgen.py | 313 ++--------- pysnmp/entity/rfc3413/oneliner/ctx.py | 35 ++ pysnmp/entity/rfc3413/oneliner/ntforg.py | 91 +--- pysnmp/entity/rfc3413/oneliner/sync/__init__.py | 1 + pysnmp/entity/rfc3413/oneliner/sync/cmdgen.py | 602 +++++++++++++++++++++ .../rfc3413/oneliner/sync/compat/__init__.py | 1 + .../entity/rfc3413/oneliner/sync/compat/cmdgen.py | 256 +++++++++ .../entity/rfc3413/oneliner/sync/compat/ntforg.py | 48 ++ pysnmp/entity/rfc3413/oneliner/sync/ntforg.py | 126 +++++ pysnmp/entity/rfc3413/oneliner/target.py | 81 +++ pysnmp/smi/rfc1902.py | 457 +++++++++++++++- 12 files changed, 1781 insertions(+), 356 deletions(-) create mode 100644 pysnmp/entity/rfc3413/oneliner/sync/__init__.py create mode 100644 pysnmp/entity/rfc3413/oneliner/sync/cmdgen.py create mode 100644 pysnmp/entity/rfc3413/oneliner/sync/compat/__init__.py create mode 100644 pysnmp/entity/rfc3413/oneliner/sync/compat/cmdgen.py create mode 100644 pysnmp/entity/rfc3413/oneliner/sync/compat/ntforg.py create mode 100644 pysnmp/entity/rfc3413/oneliner/sync/ntforg.py (limited to 'pysnmp') diff --git a/pysnmp/entity/rfc3413/oneliner/auth.py b/pysnmp/entity/rfc3413/oneliner/auth.py index 8c7e5f6..df1eafe 100644 --- a/pysnmp/entity/rfc3413/oneliner/auth.py +++ b/pysnmp/entity/rfc3413/oneliner/auth.py @@ -3,6 +3,49 @@ from pysnmp import error from pyasn1.compat.octets import null class CommunityData: + """Creates SNMP v1/v2c configuration entry. + + 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 derivatives for adding new entries to Local Configuration + Datastore (LCD) managed by :py:class:`~pysnmp.entity.engine.SnmpEngine` + class instance. + + See :RFC:`2576#section-5.3` for more information on the + *SNMP-COMMUNITY-MIB::snmpCommunityTable*. + + Parameters + ---------- + communityIndex : str + Unique index value of a row in snmpCommunityTable. If it is the + only positional parameter, it is taken as *communityName*. + communityName : str + SNMP v1/v2c community string. + mpModel : int + SNMP version - 0 for SNMPv1 and 1 for SNMPv2c. + contextEngineId : str + Indicates the location of the context in which management + information is accessed when using the community string + specified by the above communityName. + contextName : str + The context in which management information is accessed when + using the above communityName. + tag : str + Arbitrary string that specifies a set of transport endpoints + to which a notification may be sent using communityName above + (see also :RFC:`3413#section-4.1.4`). + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.auth import CommunityData + >>> CommunityData('public') + CommunityData(communityIndex='s1410706889', communityName=, mpModel=1, contextEngineId=None, contextName='', tag='') + >>> CommunityData('public', 'public') + CommunityData(communityIndex='public', communityName=, mpModel=1, contextEngineId=None, contextName='', tag='') + >>> + + """ mpModel = 1 # Default is SMIv2 securityModel = mpModel + 1 securityLevel = 'noAuthNoPriv' @@ -65,8 +108,89 @@ class CommunityData: tag is None and self.tag or tag, securityName is None and self.securityName or securityName ) - + +#: No Authentication Protocol. +usmNoAuthProtocol = config.usmNoAuthProtocol +#: The HMAC-MD5-96 Digest Authentication Protocol (:RFC:`3414#section-6`) +usmHMACMD5AuthProtocol = config.usmHMACMD5AuthProtocol +#: The HMAC-SHA-96 Digest Authentication Protocol (:RFC:`3414#section-7`) +usmHMACSHAAuthProtocol = config.usmHMACSHAAuthProtocol + +#: No Privacy Protocol. +usmNoPrivProtocol = config.usmNoPrivProtocol +#: The CBC-DES Symmetric Encryption Protocol (:RFC:`3414#section-8`) +usmDESPrivProtocol = config.usmDESPrivProtocol +#: The 3DES-EDE Symmetric Encryption Protocol (`draft-reeder-snmpv3-usm-3desede-00 `_) +usm3DESEDEPrivProtocol = config.usm3DESEDEPrivProtocol +#: The CFB128-AES-128 Symmetric Encryption Protocol (:RFC:`3826#section-3`) +usmAesCfb128Protocol = config.usmAesCfb128Protocol +#: The CFB128-AES-192 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) +usmAesCfb192Protocol = config.usmAesCfb192Protocol +#: The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) +usmAesCfb256Protocol = config.usmAesCfb256Protocol + class UsmUserData: + """Creates SNMP v3 User Security Model (USM) configuration entry. + + 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 derivatives for adding new entries to Local Configuration + Datastore (LCD) managed by :py:class:`~pysnmp.entity.engine.SnmpEngine` + class instance. + + See :RFC:`3414#section-5` for more information on the + *SNMP-USER-BASED-SM-MIB::usmUserTable*. + + Parameters + ---------- + userName : str + A human readable string representing the name of the SNMP USM user. + authKey : str + Initial value of the secret authentication key. If not set, + :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmNoAuthProtocol` + is implied. If set and no *authProtocol* is specified, + :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmHMACMD5AuthProtocol` + takes effect. + privKey : str + Initial value of the secret encryption key. If not set, + :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmNoPrivProtocol` + is implied. If set and no *privProtocol* is specified, + :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmDESPrivProtocol` + takes effect. + authProtocol : tuple + An indication of whether messages sent on behalf of this USM user + can be authenticated, and if so, the type of authentication protocol + which is used. + + Supported authentication protocol identifiers are: + + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmNoAuthProtocol` (default is *authKey* not given) + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmHMACMD5AuthProtocol` (default if *authKey* is given) + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmHMACSHAAuthProtocol` + privProtocol : tuple + An indication of whether messages sent on behalf of this USM user + be encrypted, and if so, the type of encryption protocol which is used. + + Supported encryption protocol identifiers are: + + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmNoPrivProtocol` (default is *authKey* not given) + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmDESPrivProtocol` (default if *authKey* is given) + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usm3DESEDEPrivProtocol` + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmAesCfb128Protocol` + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmAesCfb192Protocol` + * :py:class:`~pysnmp.entity.rfc3413.oneliner.auth.usmAesCfb256Protocol` + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.auth import UsmUserData + >>> UsmUserData('testuser', authKey='authenticationkey') + UsmUserData(userName='testuser', authKey=, privKey=, authProtocol=(1,3,6,1,6,3,10,1,1,2), privProtocol=(1,3,6,1,6,3,10,1,2,1)) + >>> UsmUserData('testuser', authKey='authenticationkey', privKey='encryptionkey') + UsmUserData(userName='testuser', authKey=, privKey=, authProtocol=(1,3,6,1,6,3,10,1,1,2), privProtocol=(1,3,6,1,6,3,10,1,2,2)) + >>> + + """ authKey = privKey = None authProtocol = config.usmNoAuthProtocol privProtocol = config.usmNoPrivProtocol diff --git a/pysnmp/entity/rfc3413/oneliner/cmdgen.py b/pysnmp/entity/rfc3413/oneliner/cmdgen.py index d646cf6..4f38d51 100644 --- a/pysnmp/entity/rfc3413/oneliner/cmdgen.py +++ b/pysnmp/entity/rfc3413/oneliner/cmdgen.py @@ -1,31 +1,19 @@ +from sys import version_info from pysnmp.entity import engine, config from pysnmp.entity.rfc3413 import cmdgen -from pysnmp.smi.rfc1902 import ObjectIdentity, ObjectType -from pysnmp.entity.rfc3413.oneliner.auth import CommunityData, UsmUserData -from pysnmp.entity.rfc3413.oneliner.target import UdpTransportTarget, \ - Udp6TransportTarget, UnixTransportTarget -from pysnmp.entity.rfc3413.oneliner.ctx import ContextData +from pysnmp.smi.rfc1902 import * +from pysnmp.entity.rfc3413.oneliner.auth import * +from pysnmp.entity.rfc3413.oneliner.target import * +from pysnmp.entity.rfc3413.oneliner.ctx import * from pysnmp.proto import rfc1905, errind from pysnmp.smi import view from pysnmp import nextid, error from pyasn1.type import univ, base from pyasn1.compat.octets import null + # obsolete, compatibility symbols from pysnmp.entity.rfc3413.oneliner.mibvar import MibVariable -# Auth protocol -usmHMACMD5AuthProtocol = config.usmHMACMD5AuthProtocol -usmHMACSHAAuthProtocol = config.usmHMACSHAAuthProtocol -usmNoAuthProtocol = config.usmNoAuthProtocol - -# Privacy protocol -usmDESPrivProtocol = config.usmDESPrivProtocol -usm3DESEDEPrivProtocol = config.usm3DESEDEPrivProtocol -usmAesCfb128Protocol = config.usmAesCfb128Protocol -usmAesCfb192Protocol = config.usmAesCfb192Protocol -usmAesCfb256Protocol = config.usmAesCfb256Protocol -usmNoPrivProtocol = config.usmNoPrivProtocol - # SNMP engine SnmpEngine = engine.SnmpEngine @@ -226,29 +214,21 @@ class AsyncCommandGenerator: return __varBinds - def unmakeVarBinds(self, snmpEngine, varBinds, lookupNames, lookupValues): - if lookupNames or lookupValues: + def unmakeVarBinds(self, snmpEngine, varBinds, lookupMib=False): + if lookupMib: mibViewController = self.getMibViewController(snmpEngine) varBinds = [ ObjectType(ObjectIdentity(x[0]), x[1]).resolveWithMib(mibViewController) for x in varBinds ] return varBinds - def makeVarBindsHead(self, snmpEngine, varNames): - return [ - x[0] for x in self.makeVarBinds( - snmpEngine, - [ (x, univ.Null('')) for x in varNames ] - ) - ] - # Async SNMP apps def getCmd(self, snmpEngine, authData, transportTarget, contextData, - varBinds, cbInfo, lookupNames=False, lookupValues=False): + varBinds, cbInfo, lookupMib=False): def __cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBinds, cbCtx): - lookupNames, lookupValues, cbFun, cbCtx = cbCtx + lookupMib, cbFun, cbCtx = cbCtx return cbFun( snmpEngine, sendRequestHandle, @@ -256,7 +236,7 @@ class AsyncCommandGenerator: errorStatus, errorIndex, self.unmakeVarBinds( - snmpEngine, varBinds, lookupNames, lookupValues + snmpEngine, varBinds, lookupMib ), cbCtx ) @@ -273,15 +253,15 @@ class AsyncCommandGenerator: contextData.contextName, self.makeVarBinds(snmpEngine, varBinds), __cbFun, - (lookupNames, lookupValues, cbFun, cbCtx) + (lookupMib, cbFun, cbCtx) ) def setCmd(self, snmpEngine, authData, transportTarget, contextData, - varBinds, cbInfo, lookupNames=False, lookupValues=False): + varBinds, cbInfo, lookupMib=False): def __cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBinds, cbCtx): - lookupNames, lookupValues, cbFun, cbCtx = cbCtx + lookupMib, cbFun, cbCtx = cbCtx return cbFun( snmpEngine, sendRequestHandle, @@ -289,7 +269,7 @@ class AsyncCommandGenerator: errorStatus, errorIndex, self.unmakeVarBinds( - snmpEngine, varBinds, lookupNames, lookupValues + snmpEngine, varBinds, lookupMib ), cbCtx ) @@ -306,22 +286,22 @@ class AsyncCommandGenerator: contextData.contextName, self.makeVarBinds(snmpEngine, varBinds), __cbFun, - (lookupNames, lookupValues, cbFun, cbCtx) + (lookupMib, cbFun, cbCtx) ) def nextCmd(self, snmpEngine, authData, transportTarget, contextData, - varBinds, cbInfo, lookupNames=False, lookupValues=False): + varBinds, cbInfo, lookupMib=False): def __cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBindTable, cbCtx): - lookupNames, lookupValues, cbFun, cbCtx = cbCtx + lookupMib, cbFun, cbCtx = cbCtx return cbFun( snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, - [ self.unmakeVarBinds(snmpEngine, varBindTableRow, lookupNames, lookupValues) for varBindTableRow in varBindTable ], + [ self.unmakeVarBinds(snmpEngine, varBindTableRow, lookupMib) for varBindTableRow in varBindTable ], cbCtx ) @@ -335,23 +315,23 @@ class AsyncCommandGenerator: contextData.contextEngineId, contextData.contextName, self.makeVarBinds(snmpEngine, varBinds), __cbFun, - (lookupNames, lookupValues, cbFun, cbCtx) + (lookupMib, cbFun, cbCtx) ) def bulkCmd(self, snmpEngine, authData, transportTarget, contextData, nonRepeaters, maxRepetitions, varBinds, cbInfo, - lookupNames=False, lookupValues=False): + lookupMib=False): def __cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBindTable, cbCtx): - lookupNames, lookupValues, cbFun, cbCtx = cbCtx + lookupMib, cbFun, cbCtx = cbCtx return cbFun( snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, - [ self.unmakeVarBinds(snmpEngine, varBindTableRow, lookupNames, lookupValues) for varBindTableRow in varBindTable ], + [ self.unmakeVarBinds(snmpEngine, varBindTableRow, lookupMib) for varBindTableRow in varBindTable ], cbCtx ) @@ -367,239 +347,9 @@ class AsyncCommandGenerator: nonRepeaters, maxRepetitions, self.makeVarBinds(snmpEngine, varBinds), __cbFun, - (lookupNames, lookupValues, cbFun, cbCtx) + (lookupMib, cbFun, cbCtx) ) -# Synchronous one-liner SNMP apps - -def getCmd(snmpEngine, authData, transportTarget, contextData, - *varBinds, **kwargs): - - def cbFun(snmpEngine, sendRequestHandle, - errorIndication, errorStatus, errorIndex, - varBinds, cbCtx): - cbCtx['errorIndication'] = errorIndication - cbCtx['errorStatus'] = errorStatus - cbCtx['errorIndex'] = errorIndex - cbCtx['varBinds'] = varBinds - - cbCtx = {} - - AsyncCommandGenerator().getCmd( - snmpEngine, - authData, - transportTarget, - contextData, - varBinds, - (cbFun, cbCtx), - kwargs.get('lookupNames'), - kwargs.get('lookupValues') - ) - - snmpEngine.transportDispatcher.runDispatcher() - - yield cbCtx['errorIndication'], \ - cbCtx['errorStatus'], cbCtx['errorIndex'], \ - cbCtx['varBinds'] - -def setCmd(snmpEngine, authData, transportTarget, contextData, - *varBinds, **kwargs): - - def cbFun(snmpEngine, sendRequestHandle, - errorIndication, errorStatus, errorIndex, - varBinds, cbCtx): - cbCtx['errorIndication'] = errorIndication - cbCtx['errorStatus'] = errorStatus - cbCtx['errorIndex'] = errorIndex - cbCtx['varBinds'] = varBinds - - cbCtx = {} - - AsyncCommandGenerator().setCmd( - snmpEngine, - authData, - transportTarget, - contextData, - varBinds, - (cbFun, cbCtx), - kwargs.get('lookupNames'), - kwargs.get('lookupValues') - ) - - snmpEngine.transportDispatcher.runDispatcher() - - yield cbCtx['errorIndication'], \ - cbCtx['errorStatus'], cbCtx['errorIndex'], \ - cbCtx['varBinds'] - -def nextCmd(snmpEngine, authData, transportTarget, contextData, - *varBinds, **kwargs): - - def cbFun(snmpEngine, sendRequestHandle, - errorIndication, errorStatus, errorIndex, - varBindTable, cbCtx): - cbCtx['errorIndication'] = errorIndication - cbCtx['errorStatus'] = errorStatus - cbCtx['errorIndex'] = errorIndex - cbCtx['varBindTable'] = varBindTable - - lookupNames = kwargs.get('lookupNames', False) - lookupValues = kwargs.get('lookupValues', False) - lexicographicMode = kwargs.get('lexicographicMode', False) - ignoreNonIncreasingOid = kwargs.get('ignoreNonIncreasingOid', False) - maxRows = kwargs.get('maxRows', 0) - maxCalls = kwargs.get('maxCalls', 0) - - cbCtx = {} - - cmdGen = AsyncCommandGenerator() - - initialVars = [ x[0] for x in cmdGen.makeVarBinds(snmpEngine, varBinds) ] - - totalRows = totalCalls = 0 - - while True: - cmdGen.nextCmd(snmpEngine, - authData, - transportTarget, - contextData, - [ (x[0], univ.Null()) for x in varBinds ], - (cbFun, cbCtx), - kwargs.get('lookupNames'), - kwargs.get('lookupValues')) - - snmpEngine.transportDispatcher.runDispatcher() - - errorIndication = cbCtx['errorIndication'] - errorStatus = cbCtx['errorStatus'] - errorIndex = cbCtx['errorIndex'] - - if ignoreNonIncreasingOid and errorIndication and \ - isinstance(errorIndication, errind.OidNotIncreasing): - errorIndication = None - if errorStatus or errorIndication: - 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 - continue - else: - varBinds = cbCtx['varBindTable'] and cbCtx['varBindTable'][0] - for idx, varBind in enumerate(varBinds): - name, val = varBind - if not isinstance(val, univ.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, **kwargs): - - def cbFun(snmpEngine, sendRequestHandle, - errorIndication, errorStatus, errorIndex, - varBindTable, cbCtx): - cbCtx['errorIndication'] = errorIndication - cbCtx['errorStatus'] = errorStatus - cbCtx['errorIndex'] = errorIndex - cbCtx['varBindTable'] = varBindTable - - lookupNames = kwargs.get('lookupNames', False) - lookupValues = kwargs.get('lookupValues', False) - lexicographicMode = kwargs.get('lexicographicMode', False) - ignoreNonIncreasingOid = kwargs.get('ignoreNonIncreasingOid', False) - maxRows = kwargs.get('maxRows', 0) - maxCalls = kwargs.get('maxCalls', 0) - - cbCtx = {} - - cmdGen = AsyncCommandGenerator() - - initialVars = [ x[0] for x in cmdGen.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], univ.Null()) for x in varBinds ], - (cbFun, cbCtx), - kwargs.get('lookupNames'), - kwargs.get('lookupValues')) - - 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 errorStatus or errorIndication: - 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 [] - continue - 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, rfc1905.endOfMibView - continue - stopFlag = False - if isinstance(val, univ.Null): - nullVarBinds[j] = True - elif not lexicographicMode and \ - not initialVars[j].isPrefixOf(name): - varBindTable[i][j] = name, rfc1905.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 - # # The rest of code in this file belongs to obsolete, compatibility wrappers. # Never use interfaces below for new applications! @@ -641,7 +391,7 @@ class AsynCommandGenerator: def unmakeVarBinds(self, varBinds, lookupNames, lookupValues): return self.__asyncCmdGen.unmakeVarBinds( - self.snmpEngine, varBinds, lookupNames, lookupValues + self.snmpEngine, varBinds, lookupNames or lookupValues ) def getCmd(self, authData, transportTarget, varNames, cbInfo, @@ -668,7 +418,7 @@ class AsynCommandGenerator: ContextData(contextEngineId, contextName), [(x, self._null) for x in varNames], cbInfo, - lookupNames, lookupValues + lookupNames or lookupValues ) asyncGetCmd = getCmd @@ -695,7 +445,7 @@ class AsynCommandGenerator: self.snmpEngine, authData, transportTarget, ContextData(contextEngineId, contextName), varBinds, cbInfo, - lookupNames, lookupValues + lookupNames or lookupValues ) asyncSetCmd = setCmd @@ -724,7 +474,7 @@ class AsynCommandGenerator: ContextData(contextEngineId, contextName), [(x, self._null) for x in varNames], cbInfo, - lookupNames, lookupValues + lookupNames or lookupValues ) asyncNextCmd = nextCmd @@ -755,7 +505,7 @@ class AsynCommandGenerator: nonRepeaters, maxRepetitions, [(x, self._null) for x in varNames], cbInfo, - lookupNames, lookupValues + lookupNames or lookupValues ) asyncBulkCmd = bulkCmd @@ -816,3 +566,8 @@ class CommandGenerator: return errorIndication, errorStatus, errorIndex, varBindTable +# circular module import dependency +if version_info[:2] < (2, 6): + from pysnmp.entity.rfc3413.oneliner.sync.compat.cmdgen import * +else: + from pysnmp.entity.rfc3413.oneliner.sync.cmdgen import * diff --git a/pysnmp/entity/rfc3413/oneliner/ctx.py b/pysnmp/entity/rfc3413/oneliner/ctx.py index 3449912..528a2b0 100644 --- a/pysnmp/entity/rfc3413/oneliner/ctx.py +++ b/pysnmp/entity/rfc3413/oneliner/ctx.py @@ -1,6 +1,41 @@ from pyasn1.compat.octets import null class ContextData: + """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 forming SNMP PDU and also adding new entries to + Local Configuration Datastore (LCD) in order to support SNMPv1/v2c with + SNMPv3 interoperability. + + See :RFC:`3411#section-4.1` for SNMP Context details. + + Parameters + ---------- + contextEngineId : str + Uniquely identifies an SNMP entity that may realize an instance of + a MIB with a particular contextName (:RFC:`3411#section-3.3.2`). + More frequently than not, ContextEngineID is the same as + authoritative SnmpEngineID, however if SNMP Engine serves multiple + SNMP Entities, their ContextEngineIDs would be distinct. + Default is authoritative SNMP Engine ID. + contextName : str + Used to name an instance of MIB (:RFC:`3411#section-3.3.3`). + Default is empty string. + + Examples + -------- + >>> from pysnmp.entity.rfc3413.oneliner.ctx import ContextData + >>> ContextData() + ContextData(contextEngineId=None, contextName='') + >>> ContextData(OctetString(hexValue='01020ABBA0')) + ContextData(contextEngineId=OctetString(hexValue='01020abba0'), contextName='') + >>> ContextData(contextName='mycontext') + ContextData(contextEngineId=None, contextName='mycontext') + + """ def __init__(self, contextEngineId=None, contextName=null): self.contextEngineId = contextEngineId self.contextName = contextName diff --git a/pysnmp/entity/rfc3413/oneliner/ntforg.py b/pysnmp/entity/rfc3413/oneliner/ntforg.py index 6ff9215..96a8456 100644 --- a/pysnmp/entity/rfc3413/oneliner/ntforg.py +++ b/pysnmp/entity/rfc3413/oneliner/ntforg.py @@ -1,30 +1,16 @@ from pyasn1.compat.octets import null from pysnmp import nextid, error from pysnmp.entity import engine, config -from pysnmp.smi.rfc1902 import ObjectIdentity, ObjectType, NotificationType +from pysnmp.smi.rfc1902 import * from pysnmp.entity.rfc3413 import ntforg, context -from pysnmp.entity.rfc3413.oneliner.auth import CommunityData, UsmUserData -from pysnmp.entity.rfc3413.oneliner.target import UdpTransportTarget, \ - Udp6TransportTarget, UnixTransportTarget +from pysnmp.entity.rfc3413.oneliner.auth import * +from pysnmp.entity.rfc3413.oneliner.target import * +from pysnmp.entity.rfc3413.oneliner.ctx import * from pysnmp.entity.rfc3413.oneliner import cmdgen # obsolete, compatibility symbols from pysnmp.entity.rfc3413.oneliner.mibvar import MibVariable -# Auth protocol -usmHMACMD5AuthProtocol = config.usmHMACMD5AuthProtocol -usmHMACSHAAuthProtocol = config.usmHMACSHAAuthProtocol -usmNoAuthProtocol = config.usmNoAuthProtocol - -# Privacy protocol -usmDESPrivProtocol = config.usmDESPrivProtocol -usm3DESEDEPrivProtocol = config.usm3DESEDEPrivProtocol -usmAesCfb128Protocol = config.usmAesCfb128Protocol -usmAesCfb192Protocol = config.usmAesCfb192Protocol -usmAesCfb256Protocol = config.usmAesCfb256Protocol -usmNoPrivProtocol = config.usmNoPrivProtocol - SnmpEngine = engine.SnmpEngine -ContextData = cmdgen.ContextData nextID = nextid.Integer(0xffffffff) @@ -135,8 +121,8 @@ class AsyncNotificationOriginator: __varBinds.append(varBind.resolveWithMib(mibViewController)) return __varBinds - def unmakeVarBinds(self, snmpEngine, varBinds, lookupNames, lookupValues): - if lookupNames or lookupValues: + def unmakeVarBinds(self, snmpEngine, varBinds, lookupMib=False): + if lookupMib: mibViewController = self.getMibViewController(snmpEngine) varBinds = [ ObjectType(ObjectIdentity(x[0]), x[1]).resolveWithMib(mibViewController) for x in varBinds ] return varBinds @@ -144,20 +130,20 @@ class AsyncNotificationOriginator: def sendNotification(self, snmpEngine, authData, transportTarget, contextData, notifyType, - varBinds=(), + varBinds, cbInfo=(None, None), - lookupNames=False, lookupValues=False): + lookupMib=False): def __cbFun(snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, varBinds, cbCtx): - lookupNames, lookupValues, cbFun, cbCtx = cbCtx + lookupMib, cbFun, cbCtx = cbCtx return cbFun and cbFun( snmpEngine, sendRequestHandle, errorIndication, errorStatus, errorIndex, self.unmakeVarBinds( - snmpEngine, varBinds, lookupNames, lookupValues + snmpEngine, varBinds, lookupMib ), cbCtx ) @@ -176,43 +162,7 @@ class AsyncNotificationOriginator: snmpEngine, authData, transportTarget, notifyType ) - return ntforg.NotificationOriginator().sendVarBinds(snmpEngine, notifyName, contextData.contextEngineId, contextData.contextName, self.makeVarBinds(snmpEngine, varBinds), __cbFun, (lookupNames, lookupValues, cbFun, cbCtx)) - -# -# Synchronous one-liner Notification Originator application -# - -def sendNotification(snmpEngine, authData, transportTarget, contextData, - notifyType, notificationType, **kwargs): - - def cbFun(snmpEngine, sendRequestHandle, - errorIndication, errorStatus, errorIndex, - varBinds, cbCtx): - cbCtx['errorIndication'] = errorIndication - cbCtx['errorStatus'] = errorStatus - cbCtx['errorIndex'] = errorIndex - cbCtx['varBinds'] = varBinds - - cbCtx = {} - - AsyncNotificationOriginator().sendNotification( - snmpEngine, - authData, - transportTarget, - contextData, - notifyType, - notificationType, - (cbFun, cbCtx), - kwargs.get('lookupNames'), - kwargs.get('lookupValues') - ) - - snmpEngine.transportDispatcher.runDispatcher() - - if cbCtx: - yield cbCtx['errorIndication'], \ - cbCtx['errorStatus'], cbCtx['errorIndex'], \ - cbCtx['varBinds'] + return ntforg.NotificationOriginator().sendVarBinds(snmpEngine, notifyName, contextData.contextEngineId, contextData.contextName, self.makeVarBinds(snmpEngine, varBinds), __cbFun, (lookupMib, cbFun, cbCtx)) # # The rest of code in this file belongs to obsolete, compatibility wrappers. @@ -256,6 +206,16 @@ class AsynNotificationOriginator: def uncfgNtfOrg(self, authData=None): return self.__asyncNtfOrg.uncfgNtfOrg(self.snmpEngine, authData) + def makeVarBinds(self, varBinds): + return self.__asyncNtfOrg.makeVarBinds( + self.snmpEngine, varBinds + ) + + def unmakeVarBinds(self, varBinds, lookupNames, lookupValues): + return self.__asyncNtfOrg.unmakeVarBinds( + self.snmpEngine, varBinds, lookupNames or lookupValues + ) + def sendNotification(self, authData, transportTarget, notifyType, notificationType, varBinds=(), # legacy, use NotificationType instead @@ -306,7 +266,7 @@ class AsynNotificationOriginator: contextName), notifyType, notificationType.addVarBinds(*varBinds), (__cbFun, cbInfo), - lookupNames, lookupValues + lookupNames or lookupValues ) asyncSendNotification = sendNotification @@ -328,3 +288,10 @@ class NotificationOriginator: notificationType.addVarBinds(*varBinds), **kwargs): return x + +# circular module import dependency +from sys import version_info +if version_info[:2] < (2, 6): + from pysnmp.entity.rfc3413.oneliner.sync.compat.ntforg import * +else: + from pysnmp.entity.rfc3413.oneliner.sync.ntforg import * diff --git a/pysnmp/entity/rfc3413/oneliner/sync/__init__.py b/pysnmp/entity/rfc3413/oneliner/sync/__init__.py new file mode 100644 index 0000000..8c3066b --- /dev/null +++ b/pysnmp/entity/rfc3413/oneliner/sync/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/pysnmp/entity/rfc3413/oneliner/sync/cmdgen.py b/pysnmp/entity/rfc3413/oneliner/sync/cmdgen.py new file mode 100644 index 0000000..b3a42d7 --- /dev/null +++ b/pysnmp/entity/rfc3413/oneliner/sync/cmdgen.py @@ -0,0 +1,602 @@ +from pysnmp.entity.rfc3413.oneliner.cmdgen import * + +if version_info[:2] < (2, 6): + def next(iter): + return iter.next() + +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 + Non-zero 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 + Non-zero 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 + Non-zero 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 = {} + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in cmdGen.makeVarBinds(snmpEngine, varBinds) ] + + totalRows = totalCalls = 0 + + while True: + if varBinds: + cmdGen.nextCmd(snmpEngine, + authData, + transportTarget, + contextData, + [ (x[0], univ.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, univ.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 cmdGen.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 + Non-zero 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 = {} + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in cmdGen.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], univ.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, rfc1905.endOfMibView + continue + stopFlag = False + if isinstance(val, univ.Null): + nullVarBinds[j] = True + elif not lexicographicMode and \ + not initialVars[j].isPrefixOf(name): + varBindTable[i][j] = name, rfc1905.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/entity/rfc3413/oneliner/sync/compat/__init__.py b/pysnmp/entity/rfc3413/oneliner/sync/compat/__init__.py new file mode 100644 index 0000000..8c3066b --- /dev/null +++ b/pysnmp/entity/rfc3413/oneliner/sync/compat/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/pysnmp/entity/rfc3413/oneliner/sync/compat/cmdgen.py b/pysnmp/entity/rfc3413/oneliner/sync/compat/cmdgen.py new file mode 100644 index 0000000..ebe3296 --- /dev/null +++ b/pysnmp/entity/rfc3413/oneliner/sync/compat/cmdgen.py @@ -0,0 +1,256 @@ +from pysnmp.entity.rfc3413.oneliner.cmdgen import * + +# Synchronous one-liner SNMP Command Generator apps + +if version_info[:2] < (2, 6): + 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 = {} + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in cmdGen.makeVarBinds(snmpEngine, varBinds) ] + + totalRows = totalCalls = 0 + + while True: + cmdGen.nextCmd(snmpEngine, + authData, + transportTarget, + contextData, + [ (x[0], univ.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, univ.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 = {} + + cmdGen = AsyncCommandGenerator() + + initialVars = [ x[0] for x in cmdGen.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], univ.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, rfc1905.endOfMibView + continue + stopFlag = False + if isinstance(val, univ.Null): + nullVarBinds[j] = True + elif not lexicographicMode and \ + not initialVars[j].isPrefixOf(name): + varBindTable[i][j] = name, rfc1905.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/entity/rfc3413/oneliner/sync/compat/ntforg.py b/pysnmp/entity/rfc3413/oneliner/sync/compat/ntforg.py new file mode 100644 index 0000000..7a9291c --- /dev/null +++ b/pysnmp/entity/rfc3413/oneliner/sync/compat/ntforg.py @@ -0,0 +1,48 @@ +from pysnmp.entity.rfc3413.oneliner.ntforg import * + +if version_info[:2] < (2, 6): + 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/entity/rfc3413/oneliner/sync/ntforg.py b/pysnmp/entity/rfc3413/oneliner/sync/ntforg.py new file mode 100644 index 0000000..16f3930 --- /dev/null +++ b/pysnmp/entity/rfc3413/oneliner/sync/ntforg.py @@ -0,0 +1,126 @@ +from pysnmp.entity.rfc3413.oneliner.ntforg import * + +if version_info[:2] < (2, 6): + def next(iter): + return iter.next() + +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:`3413#section-3.3`). 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 + Non-zero 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/entity/rfc3413/oneliner/target.py b/pysnmp/entity/rfc3413/oneliner/target.py index 04ecb5f..ecb7882 100644 --- a/pysnmp/entity/rfc3413/oneliner/target.py +++ b/pysnmp/entity/rfc3413/oneliner/target.py @@ -37,6 +37,42 @@ class _AbstractTransportTarget: 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): @@ -50,6 +86,51 @@ class UdpTransportTarget(_AbstractTransportTarget): 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): diff --git a/pysnmp/smi/rfc1902.py b/pysnmp/smi/rfc1902.py index 8d66f25..b61f5a0 100644 --- a/pysnmp/smi/rfc1902.py +++ b/pysnmp/smi/rfc1902.py @@ -8,17 +8,83 @@ from pyasn1.type.base import AbstractSimpleAsn1Item from pyasn1.error import PyAsn1Error from pysnmp import debug -# -# An OID-like object that embeds MIB resolution. -# -# Valid initializers include: -# ObjectIdentity('1.3.6.1.2.1.1.1.0') -# ObjectIdentity('iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0') -# ObjectIdentity('SNMPv2-MIB', 'system') -# ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0) -# ObjectIdentity('IP-MIB', 'ipAdEntAddr', '127.0.0.1', 123) -# +# expose SNMP types in this namespace for convenience +Integer = rfc1902.Integer +Integer32 = rfc1902.Integer32 +OctetString = rfc1902.OctetString +ObjectIdentifier = rfc1902.ObjectIdentifier +IpAddress = rfc1902.IpAddress +Counter32 = rfc1902.Counter32 +Gauge32 = rfc1902.Gauge32 +Unsigned32 = rfc1902.Unsigned32 +TimeTicks = rfc1902.TimeTicks +Opaque = rfc1902.Opaque +Counter64 = rfc1902.Counter64 +Bits = rfc1902.Bits +ObjectName = rfc1902.ObjectName + class ObjectIdentity: + """Create an object representing MIB variable ID. + + At the protocol level, MIB variable is only identified by an OID. + However, when interacting with humans, MIB variable can also be referred + to by its MIB name. The *ObjectIdentity* class supports various forms + of MIB variable identification, providing automatic conversion from + one to others. At the same time *ObjectIdentity* objects behave like + :py:obj:`tuples` of py:obj:`int` sub-OIDs. + + See :RFC:`1902#section-2` for more information on OBJECT-IDENTITY + SMI definitions. + + Parameters + ---------- + args + initial MIB variable identity. Recognized variants: + + * single :py:obj:`tuple` or integers representing OID + * single :py:obj:`str` representing OID in dot-separated + integers form + * single :py:obj:`str` representing MIB variable in + dot-separated labels form + * single :py:obj:`str` representing MIB name. First variable + defined in MIB is assumed. + * pair of :py:obj:`str` representing MIB name and variable name + * pair of :py:obj:`str` representing MIB name and variable name + followed by an arbitrary number of :py:obj:`str` and/or + :py:obj:`int` values representing MIB variable instance + identification. + + Other parameters + ---------------- + kwargs + MIB resolution options: + + * whenever only MIB name is given, resolve into last variable defined + in MIB if last=True. Otherwise resolves to first variable (default). + + Notes + ----- + Actual conversion between MIB variable representation formats occurs + upon :py:meth:`~pysnmp.smi.rfc1902.ObjectIdentity.resolveWithMib` + invocation. + + Examples + -------- + >>> from pysnmp.smi.rfc1902 import ObjectIdentity + >>> ObjectIdentity((1, 3, 6, 1, 2, 1, 1, 1, 0)) + ObjectIdentity((1, 3, 6, 1, 2, 1, 1, 1, 0)) + >>> ObjectIdentity('1.3.6.1.2.1.1.1.0') + ObjectIdentity('1.3.6.1.2.1.1.1.0') + >>> ObjectIdentity('iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0') + ObjectIdentity('iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0') + >>> ObjectIdentity('SNMPv2-MIB', 'system') + ObjectIdentity('SNMPv2-MIB', 'system') + >>> ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0) + ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0) + >>> ObjectIdentity('IP-MIB', 'ipAdEntAddr', '127.0.0.1', 123) + ObjectIdentity('IP-MIB', 'ipAdEntAddr', '127.0.0.1', 123) + + """ stDirty, stClean = 1, 2 def __init__(self, *args, **kwargs): @@ -28,22 +94,95 @@ class ObjectIdentity: self.__asn1SourcesToAdd = self.__asn1SourcesOptions = None self.__state = self.stDirty - # - # public API - # def getMibSymbol(self): + """Returns MIB variable symbolic identification. + + Returns + ------- + str + MIB module name + str + MIB variable symbolic name + : :py:class:`~pysnmp.proto.rfc1902.ObjectName` + class instance representing MIB variable instance index. + + Raises + ------ + SmiError + If MIB variable conversion has not been performed. + + Examples + -------- + >>> objectIdentity = ObjectIdentity('1.3.6.1.2.1.1.1.0') + >>> objectIdentity.resolveWithMib(mibViewController) + >>> objectIdentity.getMibSymbol() + ('SNMPv2-MIB', 'sysDescr', (0,)) + >>> + + """ if self.__state & self.stClean: return self.__modName, self.__symName, self.__indices else: raise SmiError('%s object not fully initialized' % self.__class__.__name__) def getOid(self): + """Returns OID identifying MIB variable. + + Returns + ------- + : :py:class:`~pysnmp.proto.rfc1902.ObjectName` + full OID identifying MIB variable including possible index part. + + Raises + ------ + SmiError + If MIB variable conversion has not been performed. + + Examples + -------- + >>> objectIdentity = ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0) + >>> objectIdentity.resolveWithMib(mibViewController) + >>> objectIdentity.getOid() + ObjectName('1.3.6.1.2.1.1.1.0') + >>> + + """ if self.__state & self.stClean: return self.__oid else: raise SmiError('%s object not fully initialized' % self.__class__.__name__) def getLabel(self): + """Returns symbolic path to this MIB variable. + + Meaning a sequence of symbolic identifications for each of parent + MIB objects in MIB tree. + + Returns + ------- + tuple + sequence of names of nodes in a MIB tree from the top of the tree + towards this MIB variable. + + Raises + ------ + SmiError + If MIB variable conversion has not been performed. + + Notes + ----- + Returned sequence may not contain full path to this MIB variable + if some symbols are now known at the moment of MIB look up. + + Examples + -------- + >>> objectIdentity = ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0) + >>> objectIdentity.resolveWithMib(mibViewController) + >>> objectIdentity.getOid() + ('iso', 'org', 'dod', 'internet', 'mgmt', 'mib-2', 'system', 'sysDescr') + >>> + + """ if self.__state & self.stClean: return self.__label else: @@ -63,6 +202,35 @@ class ObjectIdentity: # def addAsn1MibSource(self, *asn1Sources, **kwargs): + """Adds path to a repository to search ASN.1 MIB files. + + Parameters + ---------- + *asn1Sources : + one or more URL in form of :py:obj:`str` identifying local or + remote ASN.1 MIB repositories. Path must include the *@mib@* + component which will be replaced with MIB module name at the + time of search. + + Returns + ------- + : :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` + reference to itself + + Notes + ----- + Please refer to :py:class:`~pysmi.reader.localfile.FileReader`, + :py:class:`~pysmi.reader.httpclient.HttpReader` and + :py:class:`~pysmi.reader.ftpclient.FtpReader` classes for + in-depth information on ASN.1 MIB lookup. + + Examples + -------- + >>> ObjectIdentity('SNMPv2-MIB', 'sysDescr').addAsn1Source('http://mibs.snmplabs.com/asn1/@mib@') + ObjectIdentity('SNMPv2-MIB', 'sysDescr') + >>> + + """ if self.__asn1SourcesToAdd is None: self.__asn1SourcesToAdd = asn1Sources else: @@ -91,6 +259,47 @@ class ObjectIdentity: # this would eventually be called by an entity which posses a # reference to MibViewController def resolveWithMib(self, mibViewController): + """Perform MIB variable ID conversion. + + Parameters + ---------- + mibViewController : :py:class:`~pysnmp.smi.view.MibViewController` + class instance representing MIB browsing functionality. + + Returns + ------- + : :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` + reference to itself + + Raises + ------ + SmiError + In case of fatal MIB hanling errora + + Notes + ----- + Calling this method might cause the following sequence of + events (exact details depends on many factors): + + * ASN.1 MIB file downloaded and handed over to + :py:class:`~pysmi.compiler.MibCompiler` for conversion into + Python MIB module (based on pysnmp classes) + * Python MIB module is imported by SNMP engine, internal indices + created + * :py:class:`~pysnmp.smi.view.MibViewController` looks up the + rest of MIB identification information based on whatever information + is already available, :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` + class instance + gets updated and ready for further use. + + Examples + -------- + >>> objectIdentity = ObjectIdentity('SNMPv2-MIB', 'sysDescr') + >>> objectIdentity.resolveWithMib(mibViewController) + ObjectIdentity('SNMPv2-MIB', 'sysDescr') + >>> + + """ if self.__mibSourcesToAdd is not None: debug.logger & debug.flagMIB and debug.logger('adding MIB sources %s' % ', '.join(self.__mibSourcesToAdd)) mibViewController.mibBuilder.addMibSources( @@ -374,6 +583,63 @@ class ObjectIdentity: # A two-element sequence of ObjectIdentity and SNMP data type object class ObjectType: + """Create an object representing MIB variable. + + Instances of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class are + containters incorporating :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` + class instance (identifying MIB variable) and optional value belonging + to one of SNMP types (:RFC:`1902`). + + Typical MIB variable is defined like this (from *SNMPv2-MIB.txt*): + + .. code-block:: bash + + sysDescr OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A textual description of the entity. This value should..." + ::= { system 1 } + + Corresponding ObjectType instantiation would look like this: + + .. code-block:: python + + ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'), 'Linux i386 box') + + In order to behave like SNMP variable-binding (:RFC:`1157#section-4.1.1`), + :py:class:`~pysnmp.smi.rfc1902.ObjectType` objects also support + sequence protocol addressing `objectIdentity` as its 0-th element + and `objectSyntax` as 1-st. + + See :RFC:`1902#section-2` for more information on OBJECT-TYPE SMI + definitions. + + Parameters + ---------- + objectIdentity : :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` + Class instance representing MIB variable identification. + objectSyntax : + Represents a value associated with this MIB variable. Values of + built-in Python types will be automatically converted into SNMP + object as specified in OBJECT-TYPE->SYNTAX field. + + Notes + ----- + Actual conversion between MIB variable representation formats occurs + upon :py:meth:`~pysnmp.smi.rfc1902.ObjectType.resolveWithMib` + invocation. + + Examples + -------- + >>> from pysnmp.smi.rfc1902 import * + >>> ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0')) + ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), Null()) + >>> ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386') + ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386') + + """ stDirty, stClean = 1, 2 def __init__(self, objectIdentity, objectSyntax=rfc1905.unSpecified): if not isinstance(objectIdentity, ObjectIdentity): @@ -387,6 +653,9 @@ class ObjectType: else: raise SmiError('%s object not fully initialized' % self.__class__.__name__) + def __str__(self): + return self.prettyPrint() + def __repr__(self): return '%s(%s)' % (self.__class__.__name__, ', '.join([ repr(x) for x in self.__args])) @@ -394,6 +663,39 @@ class ObjectType: return self.__state & self.stClean def resolveWithMib(self, mibViewController): + """Perform MIB variable ID and associated value conversion. + + Parameters + ---------- + mibViewController : :py:class:`~pysnmp.smi.view.MibViewController` + class instance representing MIB browsing functionality. + + Returns + ------- + : :py:class:`~pysnmp.smi.rfc1902.ObjectType` + reference to itself + + Raises + ------ + SmiError + In case of fatal MIB hanling errora + + Notes + ----- + Calling this method involves + :py:meth:`~pysnmp.smi.rfc1902.ObjectIdentity.resolveWithMib` + method invocation. + + Examples + -------- + >>> objectType = ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'), 'Linux i386') + >>> objectType.resolveWithMib(mibViewController) + ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'), DisplayString('Linux i386')) + >>> str(objectType) + 'SNMPv2-MIB::sysDescr."0" = Linux i386' + >>> + + """ if self.__state & self.stClean: return self @@ -433,8 +735,71 @@ class ObjectType: else: raise SmiError('%s object not fully initialized' % self.__class__.__name__) -# A sequence of ObjectType's class NotificationType: + """Create an object representing SNMP Notification. + + Instances of :py:class:`~pysnmp.smi.rfc1902.NotificationType` class are + containters incorporating :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` + class instance (identifying particular notification) and a collection + of MIB variables IDs that + :py:class:`~pysnmp.entity.rfc3413.oneliner.cmdgen.NotificationOriginator` + should gather and put into notification message. + + Typical notification is defined like this (from *IF-MIB.txt*): + + .. code-block:: bash + + linkDown NOTIFICATION-TYPE + OBJECTS { ifIndex, ifAdminStatus, ifOperStatus } + STATUS current + DESCRIPTION + "A linkDown trap signifies that the SNMP entity..." + ::= { snmpTraps 3 } + + Corresponding NotificationType instantiation would look like this: + + .. code-block:: python + + NotificationType(ObjectIdentity('IF-MIB', 'linkDown')) + + To retain similarity with SNMP variable-bindings, + :py:class:`~pysnmp.smi.rfc1902.NotificationType` objects behave like + a sequence of :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances. + + See :RFC:`1902#section-2` for more information on NOTIFICATION-TYPE SMI + definitions. + + Parameters + ---------- + objectIdentity : :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` + Class instance representing MIB notification type identification. + instanceIndex : :py:class:`~pysnmp.proto.rfc1902.ObjectName` + Trailing part of MIB variables OID identification that represents + concrete instance of a MIB variable. When notification is prepared, + `instanceIndex` is appended to each MIB variable identification + listed in NOTIFICATION-TYPE->OBJECTS clause. + objects : dict + Dictionary-like object that may return values by OID key. The + `objects` dictionary is consulted when notification is being + prepared. OIDs are taken from MIB variables listed in + NOTIFICATION-TYPE->OBJECTS with `instanceIndex` part appended. + + Notes + ----- + Actual notification type and MIB variables look up occurs + upon :py:meth:`~pysnmp.smi.rfc1902.NotificationType.resolveWithMib` + invocation. + + Examples + -------- + >>> from pysnmp.smi.rfc1902 import * + >>> NotificationType(ObjectIdentity('1.3.6.1.6.3.1.1.5.3')) + NotificationType(ObjectIdentity('1.3.6.1.6.3.1.1.5.3'), (), {}) + >>> NotificationType(ObjectIdentity('IP-MIB', 'linkDown'), ObjectName('3.5)) + NotificationType(ObjectIdentity('1.3.6.1.6.3.1.1.5.3'), ObjectName('3.5'), {}) + + """ stDirty, stClean = 1, 2 def __init__(self, objectIdentity, instanceIndex=(), objects={}): if not isinstance(objectIdentity, ObjectIdentity): @@ -456,6 +821,33 @@ class NotificationType: return '%s(%r, %r, %r)' % (self.__class__.__name__, self.__objectIdentity, self.__instanceIndex, self.__objects) def addVarBinds(self, *varBinds): + """Appends variable-binding to notification. + + Parameters + ---------- + *varBinds : :py:class:`~pysnmp.smi.rfc1902.ObjectType` + One or more :py:class:`~pysnmp.smi.rfc1902.ObjectType` class + instances. + + Returns + ------- + : :py:class:`~pysnmp.smi.rfc1902.NotificationType` + reference to itself + + Notes + ----- + This method can be used to add custom variable-bindings to + notification message in addition to MIB variables specified + in NOTIFICATION-TYPE->OBJECTS clause. + + Examples + -------- + >>> nt = NotificationType(ObjectIdentity('IP-MIB', 'linkDown')) + >>> nt.addVarBinds(ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))) + NotificationType(ObjectIdentity('IP-MIB', 'linkDown'), (), {}) + >>> + + """ debug.logger & debug.flagMIB and debug.logger('additional var-binds: %r' % (varBinds,)) if self.__state & self.stClean: self.__varBinds.extend(varBinds) @@ -467,6 +859,43 @@ class NotificationType: return self.__state & self.stClean def resolveWithMib(self, mibViewController): + """Perform MIB variable ID conversion and notification objects expansion. + + Parameters + ---------- + mibViewController : :py:class:`~pysnmp.smi.view.MibViewController` + class instance representing MIB browsing functionality. + + Returns + ------- + : :py:class:`~pysnmp.smi.rfc1902.NotificationType` + reference to itself + + Raises + ------ + SmiError + In case of fatal MIB hanling errora + + Notes + ----- + Calling this method might cause the following sequence of + events (exact details depends on many factors): + + * :py:meth:`pysnmp.smi.rfc1902.ObjectIdentity.resolveWithMib` is called + * MIB variables names are read from NOTIFICATION-TYPE->OBJECTS clause, + :py:class:`~pysnmp.smi.rfc1902.ObjectType` instances are created + from MIB variable OID and `indexInstance` suffix. + * `objects` dictionary is queried for each MIB variable OID, + acquired values are added to corresponding MIB variable + + Examples + -------- + >>> notificationType = NotificationType(ObjectIdentity('IF-MIB', 'linkDown')) + >>> notificationType.resolveWithMib(mibViewController) + NotificationType(ObjectIdentity('IF-MIB', 'linkDown'), (), {}) + >>> + + """ if self.__state & self.stClean: return self -- cgit v1.2.1