pysnmp-sky/pysnmp/entity/rfc3413/ntforg.py

389 lines
17 KiB
Python

#
# This file is part of pysnmp software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pysnmp.sf.net/license.html
#
import sys
from pyasn1.compat.octets import null
from pysnmp.entity.rfc3413 import config
from pysnmp.proto.proxy import rfc2576
from pysnmp.proto import rfc3411
from pysnmp.proto.api import v2c
from pysnmp.proto import error
from pysnmp.smi import view, rfc1902
from pysnmp import nextid
from pysnmp import debug
getNextHandle = nextid.Integer(0x7fffffff)
class NotificationOriginator(object):
acmID = 3 # default MIB access control method to use
def __init__(self, snmpContext=None):
self.__pendingReqs = {}
self.__pendingNotifications = {}
self.snmpContext = snmpContext # this is deprecated
def processResponsePdu(self, snmpEngine, messageProcessingModel,
securityModel, securityName, securityLevel,
contextEngineId, contextName, pduVersion,
PDU, statusInformation, sendPduHandle, cbInfo):
sendRequestHandle, cbFun, cbCtx = cbInfo
# 3.3.6d
if sendPduHandle not in self.__pendingReqs:
raise error.ProtocolError('Missing sendPduHandle %s' % sendPduHandle)
(origTransportDomain, origTransportAddress,
origMessageProcessingModel, origSecurityModel,
origSecurityName, origSecurityLevel, origContextEngineId,
origContextName, origPdu, origTimeout,
origRetryCount, origRetries) = self.__pendingReqs.pop(sendPduHandle)
snmpEngine.transportDispatcher.jobFinished(id(self))
if statusInformation:
debug.logger & debug.flagApp and debug.logger(
'processResponsePdu: sendRequestHandle %s, sendPduHandle %s statusInformation %s' % (
sendRequestHandle, sendPduHandle, statusInformation))
if origRetries == origRetryCount:
debug.logger & debug.flagApp and debug.logger(
'processResponsePdu: sendRequestHandle %s, sendPduHandle %s retry count %d exceeded' % (
sendRequestHandle, sendPduHandle, origRetries))
cbFun(snmpEngine, sendRequestHandle,
statusInformation['errorIndication'], None, cbCtx)
return
# Convert timeout in seconds into timeout in timer ticks
timeoutInTicks = float(origTimeout) / 100 / snmpEngine.transportDispatcher.getTimerResolution()
# User-side API assumes SMIv2
if messageProcessingModel == 0:
reqPDU = rfc2576.v2ToV1(origPdu)
pduVersion = 0
else:
reqPDU = origPdu
pduVersion = 1
# 3.3.6a
try:
sendPduHandle = snmpEngine.msgAndPduDsp.sendPdu(
snmpEngine, origTransportDomain, origTransportAddress,
origMessageProcessingModel, origSecurityModel,
origSecurityName, origSecurityLevel,
origContextEngineId, origContextName, pduVersion,
reqPDU, True, timeoutInTicks, self.processResponsePdu,
(sendRequestHandle, cbFun, cbCtx)
)
except error.StatusInformation:
statusInformation = sys.exc_info()[1]
debug.logger & debug.flagApp and debug.logger(
'processResponsePdu: sendRequestHandle %s: sendPdu() failed with %r ' % (
sendRequestHandle, statusInformation))
cbFun(snmpEngine, sendRequestHandle,
statusInformation['errorIndication'], None, cbCtx)
return
snmpEngine.transportDispatcher.jobStarted(id(self))
debug.logger & debug.flagApp and debug.logger(
'processResponsePdu: sendRequestHandle %s, sendPduHandle %s, timeout %d, retry %d of %d' % (
sendRequestHandle, sendPduHandle, origTimeout, origRetries, origRetryCount))
# 3.3.6b
self.__pendingReqs[sendPduHandle] = (
origTransportDomain, origTransportAddress,
origMessageProcessingModel, origSecurityModel,
origSecurityName, origSecurityLevel,
origContextEngineId, origContextName, origPdu,
origTimeout, origRetryCount, origRetries + 1
)
return
# 3.3.6c
# User-side API assumes SMIv2
if messageProcessingModel == 0:
PDU = rfc2576.v1ToV2(PDU, origPdu)
cbFun(snmpEngine, sendRequestHandle, None, PDU, cbCtx)
def sendPdu(self, snmpEngine, targetName, contextEngineId,
contextName, pdu, cbFun=None, cbCtx=None):
(transportDomain, transportAddress, timeout,
retryCount, params) = config.getTargetAddr(snmpEngine, targetName)
(messageProcessingModel, securityModel, securityName,
securityLevel) = config.getTargetParams(snmpEngine, params)
# User-side API assumes SMIv2
if messageProcessingModel == 0:
reqPDU = rfc2576.v2ToV1(pdu)
pduVersion = 0
else:
reqPDU = pdu
pduVersion = 1
# 3.3.5
if reqPDU.tagSet in rfc3411.confirmedClassPDUs:
# Convert timeout in seconds into timeout in timer ticks
timeoutInTicks = float(timeout) / 100 / snmpEngine.transportDispatcher.getTimerResolution()
sendRequestHandle = getNextHandle()
# 3.3.6a
sendPduHandle = snmpEngine.msgAndPduDsp.sendPdu(
snmpEngine, transportDomain, transportAddress,
messageProcessingModel, securityModel, securityName,
securityLevel, contextEngineId, contextName,
pduVersion, reqPDU, True, timeoutInTicks,
self.processResponsePdu, (sendRequestHandle, cbFun, cbCtx)
)
debug.logger & debug.flagApp and debug.logger(
'sendPdu: sendPduHandle %s, timeout %d' % (sendPduHandle, timeout))
# 3.3.6b
self.__pendingReqs[sendPduHandle] = (
transportDomain, transportAddress, messageProcessingModel,
securityModel, securityName, securityLevel, contextEngineId,
contextName, pdu, timeout, retryCount, True
)
snmpEngine.transportDispatcher.jobStarted(id(self))
else:
snmpEngine.msgAndPduDsp.sendPdu(
snmpEngine, transportDomain, transportAddress,
messageProcessingModel, securityModel,
securityName, securityLevel, contextEngineId,
contextName, pduVersion, reqPDU, False
)
sendRequestHandle = None
debug.logger & debug.flagApp and debug.logger('sendPdu: message sent')
return sendRequestHandle
def processResponseVarBinds(self, snmpEngine, sendRequestHandle,
errorIndication, pdu, cbCtx):
notificationHandle, cbFun, cbCtx = cbCtx
self.__pendingNotifications[notificationHandle].remove(sendRequestHandle)
debug.logger & debug.flagApp and debug.logger(
'processResponseVarBinds: notificationHandle %s, sendRequestHandle %s, errorIndication %s, pending requests %s' % (
notificationHandle, sendRequestHandle, errorIndication, self.__pendingNotifications[notificationHandle]))
if not self.__pendingNotifications[notificationHandle]:
debug.logger & debug.flagApp and debug.logger(
'processResponseVarBinds: notificationHandle %s, sendRequestHandle %s -- completed' % (
notificationHandle, sendRequestHandle))
del self.__pendingNotifications[notificationHandle]
cbFun(snmpEngine, sendRequestHandle, errorIndication,
pdu and v2c.apiPDU.getErrorStatus(pdu) or 0,
pdu and v2c.apiPDU.getErrorIndex(pdu, muteErrors=True) or 0,
pdu and v2c.apiPDU.getVarBinds(pdu) or (),
cbCtx)
#
# Higher-level API to Notification Originator. Supports multiple
# targets, automatic var-binding formation and is fully LCD-driven.
#
def sendVarBinds(self, snmpEngine, notificationTarget, contextEngineId,
contextName, varBinds=(), cbFun=None, cbCtx=None):
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: notificationTarget %s, contextEngineId %s, contextName "%s", varBinds %s' % (
notificationTarget, contextEngineId or '<default>', contextName, varBinds))
if contextName:
__SnmpAdminString, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
'SNMP-FRAMEWORK-MIB', 'SnmpAdminString')
contextName = __SnmpAdminString(contextName)
# 3.3
(notifyTag, notifyType) = config.getNotificationInfo(snmpEngine, notificationTarget)
notificationHandle = getNextHandle()
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: notificationHandle %s, notifyTag %s, notifyType %s' % (
notificationHandle, notifyTag, notifyType))
varBinds = [(v2c.ObjectIdentifier(x), y) for x, y in varBinds]
# 3.3.2 & 3.3.3
snmpTrapOID, sysUpTime = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMPv2-MIB',
'snmpTrapOID',
'sysUpTime')
for idx in range(len(varBinds)):
if idx and varBinds[idx][0] == sysUpTime.getName():
if varBinds[0][0] == sysUpTime.getName():
varBinds[0] = varBinds[idx]
else:
varBinds.insert(0, varBinds[idx])
del varBinds[idx]
if varBinds[0][0] != sysUpTime.getName():
varBinds.insert(0, (v2c.ObjectIdentifier(sysUpTime.getName()),
sysUpTime.getSyntax().clone()))
if len(varBinds) < 2 or varBinds[1][0] != snmpTrapOID.getName():
varBinds.insert(1, (v2c.ObjectIdentifier(snmpTrapOID.getName()),
snmpTrapOID.getSyntax()))
sendRequestHandle = -1
debug.logger & debug.flagApp and debug.logger('sendVarBinds: final varBinds %s' % (varBinds,))
for targetAddrName in config.getTargetNames(snmpEngine, notifyTag):
(transportDomain, transportAddress, timeout,
retryCount, params) = config.getTargetAddr(snmpEngine,
targetAddrName)
(messageProcessingModel, securityModel, securityName,
securityLevel) = config.getTargetParams(snmpEngine, params)
# 3.3.1 XXX
# XXX filtering's yet to be implemented
# filterProfileName = config.getNotifyFilterProfile(params)
# (filterSubtree, filterMask,
# filterType) = config.getNotifyFilter(filterProfileName)
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: notificationHandle %s, notifyTag %s yields: transportDomain %s, transportAddress %r, securityModel %s, securityName %s, securityLevel %s' % (
notificationHandle, notifyTag, transportDomain, transportAddress, securityModel,
securityName, securityLevel))
for varName, varVal in varBinds:
if varName in (sysUpTime.name, snmpTrapOID.name):
continue
try:
snmpEngine.accessControlModel[self.acmID].isAccessAllowed(
snmpEngine, securityModel, securityName,
securityLevel, 'notify', contextName, varName
)
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: ACL succeeded for OID %s securityName %s' % (varName, securityName))
except error.StatusInformation:
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: ACL denied access for OID %s securityName %s, droppping notification' % (
varName, securityName))
return
# 3.3.4
if notifyType == 1:
pdu = v2c.SNMPv2TrapPDU()
elif notifyType == 2:
pdu = v2c.InformRequestPDU()
else:
raise error.ProtocolError('Unknown notify-type %r', notifyType)
v2c.apiPDU.setDefaults(pdu)
v2c.apiPDU.setVarBinds(pdu, varBinds)
# 3.3.5
try:
sendRequestHandle = self.sendPdu(
snmpEngine, targetAddrName, contextEngineId,
contextName, pdu, self.processResponseVarBinds,
(notificationHandle, cbFun, cbCtx)
)
except error.StatusInformation:
statusInformation = sys.exc_info()[1]
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: sendRequestHandle %s: sendPdu() failed with %r' % (
sendRequestHandle, statusInformation))
if notificationHandle not in self.__pendingNotifications or \
not self.__pendingNotifications[notificationHandle]:
if notificationHandle in self.__pendingNotifications:
del self.__pendingNotifications[notificationHandle]
if cbFun:
cbFun(snmpEngine, notificationHandle,
statusInformation['errorIndication'], 0, 0, (),
cbCtx)
return notificationHandle
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: notificationHandle %s, sendRequestHandle %s, timeout %d' % (
notificationHandle, sendRequestHandle, timeout))
if notifyType == 2:
if notificationHandle not in self.__pendingNotifications:
self.__pendingNotifications[notificationHandle] = set()
self.__pendingNotifications[notificationHandle].add(sendRequestHandle)
debug.logger & debug.flagApp and debug.logger(
'sendVarBinds: notificationHandle %s, sendRequestHandle %s, notification(s) sent' % (
notificationHandle, sendRequestHandle))
return notificationHandle
#
# Obsolete, compatibility interfaces.
#
def _sendNotificationCbFun(snmpEngine, sendRequestHandle, errorIndication,
errorStatus, errorIndex, varBinds, cbCtx):
cbFun, cbCtx = cbCtx
try:
# we need to pass response PDU information to user for INFORMs
cbFun(sendRequestHandle, errorIndication,
errorStatus, errorIndex, varBinds, cbCtx)
except TypeError:
# a backward compatible way of calling user function
cbFun(sendRequestHandle, errorIndication, cbCtx)
def _sendNotification(self, snmpEngine, notificationTarget, notificationName,
additionalVarBinds=(), cbFun=None, cbCtx=None,
contextName=null, instanceIndex=None):
if self.snmpContext is None:
raise error.ProtocolError('SNMP context not specified')
#
# Here we first expand trap OID into associated OBJECTS
# and then look them up at context-specific MIB
#
mibViewController = snmpEngine.getUserContext('mibViewController')
if not mibViewController:
mibViewController = view.MibViewController(snmpEngine.getMibBuilder())
snmpEngine.setUserContext(mibViewController=mibViewController)
# Support the following syntax:
# '1.2.3.4'
# (1,2,3,4)
# ('MIB', 'symbol')
if isinstance(notificationName, (tuple, list)) and \
notificationName and isinstance(notificationName[0], str):
notificationName = rfc1902.ObjectIdentity(*notificationName)
else:
notificationName = rfc1902.ObjectIdentity(notificationName)
varBinds = rfc1902.NotificationType(notificationName,
instanceIndex=instanceIndex)
varBinds.resolveWithMib(mibViewController)
mibInstrumController = self.snmpContext.getMibInstrum(contextName)
varBinds = varBinds[:1] + mibInstrumController.readVars(varBinds[1:])
return self.sendVarBinds(snmpEngine, notificationTarget,
self.snmpContext.contextEngineId,
contextName, varBinds + list(additionalVarBinds),
_sendNotificationCbFun, (cbFun, cbCtx))
# install compatibility wrapper
NotificationOriginator.sendNotification = _sendNotification
# XXX
# move/group/implement config setting/retrieval at a stand-alone module