在这个教程里,我们将会逐渐学习创建并运行一些不同的SNMP命令请求和通告。我们将使用PySNMP同步的使用最简单的高级API
在PySNMP里,SNMP Engine是一个重要的综合对象。所有PySNMP操作包含SnmpEngine类实例对象。PySNMP应用可以运行多个独立的、每个由自己的SnmpEngine对象指导的SNMP引擎
>>> from pysnmp.hlapi import *
>>>
>>> SnmpEngine()
SnmpEngine(snmpEngineID=OctetString(hexValue='80004fb80567'))
SNMP引擎拥有独特的标识符,可以自动分配和管理。这个标识符是用于SNMP协议操作。
我们将发送SNMP GET命令从SNMP代理读取MIB对象。为了这个目的,我们将调用同步,高层getCmd()函数。其他SNMP命令可用于不同类似的方式通过调用相应的函数。
>>> from pysnmp.hlapi import *
>>> [ x for x in dir() if 'Cmd' in x]
['bulkCmd', 'getCmd', 'nextCmd', 'setCmd']
>>> getCmd
<function getCmd at 0x222b330>
我们需要从三个SNMP协议版本选择。采用SNMP版本1或2c,我们经过正确初始化CommunityData类的实例。对于第三个SNMP版本,我们需要传一个UsmUserData类实例对象
SNMP社区名称、以及SNMP v1和v2c之间的选择,是通过CommunityData对象传送SNMP LCD。
>>> from pysnmp.hlapi import *
>>>
>>> CommunityData('public', mpModel=0) # SNMPv1
CommunityData('public')
>>> CommunityData('public', mpModel=1) # SNMPv2c
CommunityData('public')
UsmUserData的用途是为了LCD配置使用SNMPv3。除了建立USM用户名、UsmUserData对象可以携带加密密钥和加密协议SNMP引擎LCD。
>>> from pysnmp.hlapi import *
>>>
>>> UsmUserData('testuser', authKey='myauthkey')
UsmUserData(userName='testuser', authKey=<AUTHKEY>)
>>> UsmUserData('testuser', authKey='myauthkey', privKey='myenckey')
UsmUserData(userName='testuser', authKey=<AUTHKEY>, privKey=<PRIVKEY>)
PySNMP支持MD5和SHA消息身份验证算法,DES,AES128/192/256和3 DES加密算法。
为了简单起见,我们使用SNMPv2。虽然完全不安全的,它仍然是最流行使用的SNMP版本。
PySNMP支持在IPv4和IPv6的UDP网络传输协议。在这个示例中,我们将查询通过IPv4在demo.snmplabs.com上可找到的public SNMP Simulator 。传输配置以正确的初始化后的UdpTransportTarget 或Udp6TransportTarget对象分别地传递给SNMP LCD。
>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
...
SNMP上下文是一个参数在SNMP(v3)消息头——在被管实体里,由SNMP engine 服务提供的MIBs的特定集合。SNMP引擎可以提供许多相同的MIB对象代表完全不同的硬件或软件管理的实例。这就是SNMP上下文的用处。
表明SNMP上下文在高层API使用时应该正确地始化ContextData对象。对于这个示例,我们将使用“empty”上下文(默认)。
>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
...
最后,我们必须指定我们想读的MIB对象。在协议层面上,MIB对象通过OID识别,但我们倾向于通过他们的名字访问:
$ snmpget -v2c -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
SNMPv2-MIB::sysDescr.0 = STRING: SunOS zeus.snmplabs.com
$
$ snmpget -v2c -c public demo.snmplabs.com 1.3.6.1.2.1.1.1.0
SNMPv2-MIB::sysDescr.0 = STRING: SunOS zeus.snmplabs.com
对象名称和OID都来自MIB。名称和OID连接是通过 名为OBJECT-TYPE的高层SMI结构 。这里有一个MIB对象定义的例子:sysUpTime的OID为…mgmt.mib-2.system.3 ,以及值类型TimeTicks。
sysUpTime OBJECT-TYPE
SYNTAX TimeTicks
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The time (in hundredths of a second) since
the network management portion of the system
was last re-initialized."
::= { system 3 }
在PySNMP中,我们使用ObjectIdentity类负责MIB对象标识。ObjectIdentity代表从人类角度处理MIB对象的途径。它需要查询MIB才能进入一个完全“解决”状态。在一个MIB像OID一样的启动行为来查询之后,ObjectIdentity可以通过MIB对象名称初始化。
>>> from pysnmp.hlapi import *
>>>
>>> x = ObjectIdentity('SNMPv2-MIB', 'system')
>>> # ... calling MIB lookup ...
>>> tuple(x)
(1, 3, 6, 1, 2, 1, 1, 1)
>>> x = ObjectIdentity('iso.org.dod.internet.mgmt.mib-2.system.sysDescr')
>>> # ... calling MIB lookup ...
>>> str(x)
'1.3.6.1.2.1.1.1'
MIB分解意味着MIB对象名称转换为OID的服务,反之亦然。
ObjectType类实例代表在PySNMP里构造OBJECT-TYPE SMI。ObjectType是一个引用ObjectIdentity和SNMP类型实例的容器对象。对于Python对象,它看起来像一个元组(OID,value)。
>>> from pysnmp.hlapi import *
>>> x = ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386 box'))
>>> # ... calling MIB lookup ...
>>> x[0].prettyPrint()
'SNMPv2-MIB::sysDescr.0'
>>> x[1].prettyPrint()
'Linux i386 box'
后补零是一个MIB对象实例的一个指示。在MIB中对象的描述只是声明,他们从来没有包含任何数据。数据存储在通过追加‘0’的数量指出MIB对象的位置的MIB对象实例。ObjectIdentity类以目录作为它的初始值。
>>> x = ObjectIdentity('SNMPv2-MIB', 'system', 0)
>>> # ... calling MIB lookup ...
>>> tuple(x)
(1, 3, 6, 1, 2, 1, 1, 1, 0)
在SNMPv2-MIB模块里,我们会读取sysDescr标量的MIB对象实例作为定义
>>> from pysnmp.hlapi import *
>>> g = getCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)))
默认情况下PySNMP将搜索你的本地文件系统ASN.1 MIB文件参考。也可以配置为自动下载他们从远程主机,如例子所示。在您的SNMP MIB项目里,您可以使用我们维护的ASN.1模块集合。
我们终于能够发送SNMP查询,在响应中,希望接收一些有意义的东西。
同步API的显著特征是,它是建立在Python生成器的想法。任何函数调用生成器对象结束。迭代生成器对象执行实际的SNMP通信。在每个迭代中SNMP消息被构建和发送,等待响应,接收和解析。
>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysUpTime', 0)))
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(44430646))])
SNMP定义表的概念。在一个给定的MIB对象时,表可能习惯应用于许多实例的一个属性。例如,表放入SNMP网络接口的属性。每个实例的属性是由一个后缀附加到基础MIB对象。
在MIBs中指定的表,它们的索引(或目录)通过索引子句声明。表索引是非零整数或字符串或任何基地SNMP类型。
协议级别的所有索引以OID部分形成。为了人们舒适地使用索引,SNMP管理应用程序依赖于DISPLAY-HINT子句自动索引OID和SNMP特定类型,人性化表示之间的转换。
ifEntry OBJECT-TYPE
SYNTAX IfEntry
INDEX { ifIndex }
::= { ifTable 1 }
ifIndex OBJECT-TYPE
SYNTAX InterfaceIndex
::= { ifEntry 1 }
ifDescr OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
::= { ifEntry 2 }
InterfaceIndex ::= TEXTUAL-CONVENTION
DISPLAY-HINT "d"
SYNTAX Integer32 (1..2147483647)
PySNMP表示
>>> x = ObjectIdentity('IF-MIB', 'ifDescr', 123)
>>> # ... calling MIB lookup ...
>>> str(x)
'1.3.6.1.2.1.2.2.1.2.123'
许多目录将一些SNMP表编入索引。这些目录成为OID的一部分互相连结,最终使OID寻找出MIB对象。
从语义的角度来看,每个索引反映了一个重要和独特的MIB对象的属性。
tcpConnectionEntry OBJECT-TYPE
SYNTAX TcpConnectionEntry
INDEX { tcpConnectionLocalAddressType,
tcpConnectionLocalAddress,
tcpConnectionLocalPort,
tcpConnectionRemAddressType,
tcpConnectionRemAddress,
tcpConnectionRemPort }
::= { tcpConnectionTable 1 }
tcpConnectionLocalPort OBJECT-TYPE
SYNTAX InetPortNumber
::= { tcpConnectionEntry 3 }
PySNMP的ObjectIdentity类使用任意数量的索引进行人性化的表示和转换他们为完整的OID
>>> x = ObjectIdentity('TCP-MIB', 'tcpConnectionState',
... 'ipv4', '195.218.254.105', 41511,
... 'ipv4', '194.67.1.250', 993)
>>> # ... calling MIB lookup ...
>>> str(x)
'1.3.6.1.2.1.6.19.1.7.1.4.195.218.254.105.41511.1.4.194.67.1.250.993'
让我们通过TCP连接读取TCP-MIB::tcpConnectionState对象:
>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
... ObjectType(ObjectIdentity('TCP-MIB', 'tcpConnectionState',
... 'ipv4', '195.218.254.105', 41511,
... 'ipv4', '194.67.1.250', 993)
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.6.19.1.7.1.4.195.218.254.105.41511.1.4.194.67.1.250.993')), Integer(5))])
SNMP允许你通过next去请求一个MIB对象,使用这种方式你可以预先读取一个你不知道什么的MIB对象。MIB对象的OID概念地排序它们。这个特性由nextCmd函数实现。
>>> from pysnmp.hlapi import *
>>> g = nextCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr')))
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), DisplayString('SunOS zeus.snmplabs.com'))])
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.2.0'), ObjectIdentity(ObjectIdentifier('1.3.6.1.4.1.8072.3.2.10')))])
迭代生成器对象遍历SNMP代理的MIB对象。
SNMPv2c介绍了显著的优化GETNEXT命令,修改后的版本叫做GETBULK,能够一次收集和响应一堆“下一个”MIB对象。额外的non-repeaters和max-repetitions参数可以用来影响MIB对象定量。
PySNMP在协议层隐藏了这个GETBULK优化,该bulkCmd()函数同getNext()一样方便提供了相同的生成器API 。
>>> from pysnmp.hlapi import *
>>>
>>> N, R = 0, 25
>>> g = bulkCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
... N, R,
... ObjectType(ObjectIdentity('1.3.6')))
>>>
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), DisplayString('SunOS zeus.snmplabs.com'))])
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.2.0'), ObjectIdentifier('1.3.6.1.4.1.20408'))])
Python生成器不能够产生数据,但是它能够发送数据到一个正在运行的生成器对象。高级API使用这个特性去重复同样的SNMP操作为了得到一个新的MIB对象集合
>>> from pysnmp.hlapi import *
>>>
>>> g = nextCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
... ObjectType(ObjectIdentity('IF-MIB', 'ifTable')))
>>>
>>> g.send([ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets'))])
(None, 0, 0, [(ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.10.1'), Counter32(284817787))])
你可以操作很多无关的MIB对象只是在一个PDU里为他们排列顺序。响应PDU将携带和请求信息里完全相同的顺序的MIB对象的列表和他们的值。
>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 161)),
... ContextData(),
... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
... ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysUpTime', 0))
... )
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), DisplayString('SunOS zeus.snmplabs.com')), ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(44430646))])
SNMP配置管理的部分依赖SNMP SET命令。虽然它们的实现在托管实体的方面被证明是有要求的(由于锁定和事务行为的需求)。但是厂商倾向于离开它因此呈现托管实体是只读的。
PySNMP一律通过setCmd函数支持SET命令。
>>> from pysnmp.hlapi 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('1.3.6.1.2.1.1.1.0'), DisplayString('Linux i386'))])
托管实体可以发送未经请求的消息到管理实体。这叫做SNMP通知。通知有助于减少可能成为大型网络的一个问题——轮询。
SNMP通知是可枚举的,每个都有明确的语义。通过一个特殊的称为NOTIFICATION-TYPE的高层SMI结构工作。像OBJECT-TYPE、定义MIB对象,NOTIFICATION-TYPE 都有一个独特的OID,但是代替SNMP值的是一系列其他MIB对象的引用。这些MIB对象通过OBJECTS 子句指定,并且发送通知时,他们的当前值包括通知消息。
linkUp NOTIFICATION-TYPE
OBJECTS { ifIndex, ifAdminStatus, ifOperStatus }
STATUS current
DESCRIPTION
"..."
::= { snmpTraps 4 }
在PySNMP里为了模块化NOTIFICATION-TYPE 结构,我们有NotificationType这个容器对象类。它由ObjectIdentity类确定以及引用一系列ObjectType类实例。
从行为上看,NotificationType 看起来像一系列的ObjectType类实例
>>> from pysnmp.hlapi import *
>>>
>>> x = NotificationType(ObjectIdentity('IF-MIB', 'linkUp'))
>>> # ... calling MIB lookup ...
>>> >>> [ str(y) for x in n ]
['SNMPv2-MIB::snmpTrapOID.0 = 1.3.6.1.6.3.1.1.5.3', 'IF-MIB::ifIndex = ', 'IF-MIB::ifAdminStatus = ', 'IF-MIB::ifOperStatus = ']
使用PySNMP发送通知与发送SNMP命令没有太多不同,不同的是在于PDU 变量绑定如何构建,在SNMP有两个不同类型的通知:陷阱(trap)和通知(inform)
在陷阱类型通知里,代理到管理者的通信是单向的—-无回应或者发送确认。
>>> from pysnmp.hlapi import *
>>>
>>> g = sendNotification(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 162)),
... ContextData(),
... 'trap',
... NotificationType(ObjectIdentity('IF-MIB', 'linkUp'), instanceIndex=(123,))
... )
>>> next(g)
(None, 0, 0, [])
inform通知很像一个命令,不同的在于PDU的格式。informs用于管理到管理之间的交流也用于代理到管理的交流
>>> from pysnmp.hlapi import *
>>>
>>> g = sendNotification(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 162)),
... ContextData(),
... 'inform',
... NotificationType(ObjectIdentity('IF-MIB', 'linkUp'), instanceIndex=(123,))
... )
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(0)), ObjectType(ObjectIdentity('1.3.6.1.6.3.1.1.4.1.0'), ObjectIdentity('1.3.6.1.6.3.1.1.5.4')), ObjectType(ObjectName('1.3.6.1.2.1.2.2.1.1.123'), Null()), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.7.123'), Null()), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.8.123'), Null())])
在接下来的例子里,你可以看见MIB对象(ifIndex, ifAdminStatus, ifOperStatus)从IF-MIB::linkUp通知自动的扩充。通过索引定位SNMP表中特定的行,MIB对象的索引部分通过instanceIndex参数传递给Notification.
正如你所见,扩展MIB对象实际值是NULLs,这是因为在那些例子里,我们的简单脚本没有访问那些MIB对象。我们能在传递NotificationType 的一个像映射MIB对象的OID到当前值的对象的字典前提供缺少的信息
>>> from pysnmp.hlapi import *
>>>
>>> mib = {ObjectIdentifier('1.3.6.1.2.1.2.2.1.1.123'): 123,
... ObjectIdentifier('1.3.6.1.2.1.2.2.1.7.123'): 'testing',
... ObjectIdentifier('1.3.6.1.2.1.2.2.1.8.123'): 'up'}
>>>
>>> g = sendNotification(SnmpEngine(),
... CommunityData('public'),
... UdpTransportTarget(('demo.snmplabs.com', 162)),
... ContextData(),
... 'inform',
... NotificationType(ObjectIdentity('IF-MIB', 'linkUp'), instanceIndex=(123,), objects=mib)
... )
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(0)), ObjectType(ObjectIdentity('1.3.6.1.6.3.1.1.4.1.0'), ObjectIdentity('1.3.6.1.6.3.1.1.5.4')), ObjectType(ObjectName('1.3.6.1.2.1.2.2.1.1.123'), InterfaceIndex(123)), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.7.123'), Integer(3)), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.8.123'), Integer(1))])
当在管理大型网络,阅读MIB对象 按顺序介绍了延迟。 在某种程度上的延迟 不能容忍的。 解决方案是众所周知的——你并行化查询 可以通过卸载单个操作为多个 流程或多个线程执行或构建应用程序 在异步I / O模型。
与其他解决方案相比,异步模型是最轻量级的 和可伸缩的。 这个想法很简单:永远不要等待I / O—— 只要有可能就做点什么。 后面的执行流程成为非线性会影响我们读者程序分析。
PySNMP高级API是适应工作使用三个受欢迎 异步I / O框架- asyncore , twisted 和 asyncio 。 请参考PySNMP文库和 例子 的更多信息 异步API。