205 lines
8.0 KiB
Python
205 lines
8.0 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 os
|
|
import sys
|
|
import tempfile
|
|
from pyasn1.compat.octets import str2octs
|
|
from pysnmp.proto.rfc3412 import MsgAndPduDispatcher
|
|
from pysnmp.proto.mpmod.rfc2576 import SnmpV1MessageProcessingModel, \
|
|
SnmpV2cMessageProcessingModel
|
|
from pysnmp.proto.mpmod.rfc3412 import SnmpV3MessageProcessingModel
|
|
from pysnmp.proto.secmod.rfc2576 import SnmpV1SecurityModel, \
|
|
SnmpV2cSecurityModel
|
|
from pysnmp.proto.secmod.rfc3414 import SnmpUSMSecurityModel
|
|
from pysnmp.proto.acmod import rfc3415, void
|
|
from pysnmp.entity import observer
|
|
from pysnmp import debug
|
|
from pysnmp import error
|
|
|
|
__all__ = ['SnmpEngine']
|
|
|
|
|
|
class SnmpEngine(object):
|
|
"""Creates SNMP engine object.
|
|
|
|
SNMP engine object is central in SNMP v3 architecture. It is an umbrella
|
|
object that coordinates interactions between all parts of SNMP v3 system.
|
|
See :RFC:`3412#section-2.1` (where it is termed *The Dispatcher*).
|
|
|
|
With PySNMP design, `SnmpEngine` is the only stateful object, all SNMP
|
|
v3 operations require an instance of SNMP engine. Users do not normally
|
|
request services directly from `SnmpEngine`, but pass it around to
|
|
other PySNMP interfaces.
|
|
|
|
It is possible to run multiple instances of `SnmpEngine` in the
|
|
application. In a multithreaded environment, each thread that
|
|
works with SNMP must have its own `SnmpEngine` instance.
|
|
|
|
Parameters
|
|
----------
|
|
snmpEngineID : :py:class:`~pysnmp.proto.rfc1902.OctetString`
|
|
Unique and unambiguous identifier of an SNMP engine.
|
|
If not given, `snmpEngineID` is autogenerated and stored on
|
|
the filesystem. See :RFC:`3411#section-3.1.1` for details.
|
|
|
|
Examples
|
|
--------
|
|
>>> SnmpEngine()
|
|
SnmpEngine(snmpEngineID=OctetString(hexValue='0x80004fb80567726f6d6d69742'))
|
|
>>>
|
|
|
|
"""
|
|
|
|
def __init__(self, snmpEngineID=None, maxMessageSize=65507,
|
|
msgAndPduDsp=None):
|
|
self.cache = {}
|
|
|
|
self.observer = observer.MetaObserver()
|
|
|
|
if msgAndPduDsp is None:
|
|
self.msgAndPduDsp = MsgAndPduDispatcher()
|
|
else:
|
|
self.msgAndPduDsp = msgAndPduDsp
|
|
self.messageProcessingSubsystems = {
|
|
SnmpV1MessageProcessingModel.messageProcessingModelID:
|
|
SnmpV1MessageProcessingModel(),
|
|
SnmpV2cMessageProcessingModel.messageProcessingModelID:
|
|
SnmpV2cMessageProcessingModel(),
|
|
SnmpV3MessageProcessingModel.messageProcessingModelID:
|
|
SnmpV3MessageProcessingModel()
|
|
}
|
|
self.securityModels = {
|
|
SnmpV1SecurityModel.securityModelID: SnmpV1SecurityModel(),
|
|
SnmpV2cSecurityModel.securityModelID: SnmpV2cSecurityModel(),
|
|
SnmpUSMSecurityModel.securityModelID: SnmpUSMSecurityModel()
|
|
}
|
|
self.accessControlModel = {
|
|
void.Vacm.accessModelID: void.Vacm(),
|
|
rfc3415.Vacm.accessModelID: rfc3415.Vacm()
|
|
}
|
|
|
|
self.transportDispatcher = None
|
|
|
|
if self.msgAndPduDsp.mibInstrumController is None:
|
|
raise error.PySnmpError(
|
|
'MIB instrumentation does not yet exist'
|
|
)
|
|
snmpEngineMaxMessageSize, = self.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'__SNMP-FRAMEWORK-MIB', 'snmpEngineMaxMessageSize')
|
|
snmpEngineMaxMessageSize.syntax = snmpEngineMaxMessageSize.syntax.clone(maxMessageSize)
|
|
snmpEngineBoots, = self.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB',
|
|
'snmpEngineBoots')
|
|
snmpEngineBoots.syntax += 1
|
|
origSnmpEngineID, = self.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB',
|
|
'snmpEngineID')
|
|
|
|
if snmpEngineID is None:
|
|
self.snmpEngineID = origSnmpEngineID.syntax
|
|
else:
|
|
origSnmpEngineID.syntax = origSnmpEngineID.syntax.clone(snmpEngineID)
|
|
self.snmpEngineID = origSnmpEngineID.syntax
|
|
|
|
debug.logger & debug.flagApp and debug.logger(
|
|
'SnmpEngine: using custom SNMP Engine ID: %s' % self.snmpEngineID.prettyPrint())
|
|
|
|
# Attempt to make some of snmp Engine settings persistent.
|
|
# This should probably be generalized as a non-volatile MIB store.
|
|
|
|
persistentPath = os.path.join(tempfile.gettempdir(), '__pysnmp', self.snmpEngineID.prettyPrint())
|
|
|
|
debug.logger & debug.flagApp and debug.logger('SnmpEngine: using persistent directory: %s' % persistentPath)
|
|
|
|
if not os.path.exists(persistentPath):
|
|
try:
|
|
os.makedirs(persistentPath)
|
|
except OSError:
|
|
return
|
|
|
|
f = os.path.join(persistentPath, 'boots')
|
|
try:
|
|
snmpEngineBoots.syntax = snmpEngineBoots.syntax.clone(open(f).read())
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
snmpEngineBoots.syntax += 1
|
|
except Exception:
|
|
snmpEngineBoots.syntax = snmpEngineBoots.syntax.clone(1)
|
|
|
|
try:
|
|
fd, fn = tempfile.mkstemp(dir=persistentPath)
|
|
os.write(fd, str2octs(snmpEngineBoots.syntax.prettyPrint()))
|
|
os.close(fd)
|
|
os.rename(fn, f)
|
|
except Exception:
|
|
debug.logger & debug.flagApp and debug.logger(
|
|
'SnmpEngine: could not stored SNMP Engine Boots: %s' % sys.exc_info()[1])
|
|
else:
|
|
debug.logger & debug.flagApp and debug.logger(
|
|
'SnmpEngine: stored SNMP Engine Boots: %s' % snmpEngineBoots.syntax.prettyPrint())
|
|
|
|
def __repr__(self):
|
|
return '%s(snmpEngineID=%r)' % \
|
|
(self.__class__.__name__, self.snmpEngineID)
|
|
|
|
# Transport dispatcher bindings
|
|
|
|
def __receiveMessageCbFun(self, transportDispatcher, transportDomain,
|
|
transportAddress, wholeMsg):
|
|
self.msgAndPduDsp.receiveMessage(
|
|
self, transportDomain, transportAddress, wholeMsg
|
|
)
|
|
|
|
def __receiveTimerTickCbFun(self, timeNow):
|
|
self.msgAndPduDsp.receiveTimerTick(self, timeNow)
|
|
for mpHandler in self.messageProcessingSubsystems.values():
|
|
mpHandler.receiveTimerTick(self, timeNow)
|
|
for smHandler in self.securityModels.values():
|
|
smHandler.receiveTimerTick(self, timeNow)
|
|
|
|
def registerTransportDispatcher(self, transportDispatcher, recvId=None):
|
|
if self.transportDispatcher is not None and \
|
|
self.transportDispatcher is not transportDispatcher:
|
|
raise error.PySnmpError(
|
|
'Transport dispatcher already registered'
|
|
)
|
|
transportDispatcher.registerRecvCbFun(
|
|
self.__receiveMessageCbFun, recvId
|
|
)
|
|
if self.transportDispatcher is None:
|
|
transportDispatcher.registerTimerCbFun(
|
|
self.__receiveTimerTickCbFun
|
|
)
|
|
self.transportDispatcher = transportDispatcher
|
|
|
|
def unregisterTransportDispatcher(self, recvId=None):
|
|
if self.transportDispatcher is None:
|
|
raise error.PySnmpError(
|
|
'Transport dispatcher not registered'
|
|
)
|
|
self.transportDispatcher.unregisterRecvCbFun(recvId)
|
|
self.transportDispatcher.unregisterTimerCbFun()
|
|
self.transportDispatcher = None
|
|
|
|
def getMibBuilder(self):
|
|
return self.msgAndPduDsp.mibInstrumController.mibBuilder
|
|
|
|
# User app may attach opaque objects to SNMP Engine
|
|
def setUserContext(self, **kwargs):
|
|
self.cache.update(
|
|
dict([('__%s' % k, kwargs[k]) for k in kwargs])
|
|
)
|
|
|
|
def getUserContext(self, arg):
|
|
return self.cache.get('__%s' % arg)
|
|
|
|
def delUserContext(self, arg):
|
|
try:
|
|
del self.cache['__%s' % arg]
|
|
except KeyError:
|
|
pass
|