当前位置: 首页 > 工具软件 > pySNMP > 使用案例 >

PySNMP中文6、常用操作

叶裕
2023-12-01

安装

安装pysnmp

pip install pysnmp -i https://pypi.douban.com/simple

安装pysnmp-mibs

pip install pysnmp-mibs

常用操作

在本教程中,我们将逐步创建和运行几个不同的SNMP命令请求和通知。我们将使用PySNMP同步高级API hlapi,这是最简单的使用。

创建SNMP Engine

SNMP引擎是PySNMP中的一个中心伞形对象。所有的PySNMP操作都涉及 SnmpEngine类实例。PySNMP应用程序可以运行多个独立的SNMP引擎,每个引擎由它自己的SnmpEngine对象引导。

>>> from pysnmp.hlapi import *
>>>
>>> SnmpEngine()
SnmpEngine(snmpEngineId=OctetString(hexValue='80004fb80567'))

SNMP引擎具有可自动或者手动分配的唯一标识符。此标识符用于SNMP协议操作。

查询SNMP

我们发送SNMP GET命令去SNMP agent读取一个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协议版本。为了使用SNMP的版本1或者2c,我们需要传递正确初始化的CommunityData类的实例。对于第三个版本,我们需要传递 UsmUserData 类的实例

SNMP共同体名以及SNMP v1和v2c之间的选择通过 CommunityData 对象传递给SNMP LCD。

>>> from pysnmp.hlapi import *
>>>
>>> CommunityData('public', mpModel=0) # SNMPv1
CommunityData('public')
>>> CommunityData('public', mpModdel=1) # SNMPv2
CommunityData('public')

使用 UsmUserData 对象进行LCD配置意味着使用SNMPv3。除了设置USM用户名之外,UsmUserData 对象还可以将密码密钥和密码协议传输到SNMP配置LCD。

>>> from pysnmp.hlapi import *
>>>
>>> UsmUserData('testuser', authKey='myauthkey')
UsmUserData(userName='testuser', authKey=<AUTHKEY>)
>>> UsmUserDdata('testuser', authKey='myauthkey', privKey='myencky')
UsmUserData(userName='testuser', authKey=<AUTHKEY>, privKey=<PRIVKEY>)

PySNMP支持MD5和SHA消息授权算法,DES,AES128/192/256 和 3DES加密算法。
为了简单起见,我们使用SNMPv2。尽管不太安全,但它仍然是最流行的版本。

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(), CommunityData('public'),
...

设置传输和目标target

PySNMP支持UDP-over-IPv4和UDP-over-IPv6网络传输。在本例中,我们将通过Internet上调度IPv4在 demo.snmplabs.com 上查询可用的公告SNMP模拟器。传输配置分别以正确初始化的 UdpTrasportTarget 或者 Udp6TransportTarget 对象的形式传递给SNMP LCD。

>>> from pysnmp.hlapi imort *
>>>
>>> g = getCmd(SnmpEngine(),
· · ·                    CommunityData('public'),
· · ·                    UdpTransportTarget(('demo.snmplabs.com', 161)),
· · ·

addressing SNMP context:调用SNMP上下文

SNMP上下文是SNMP(v3)消息头中的一个参数,它处理SNMP引擎在托管实体上提供的MIB的特定集合。SNMP引擎可以为许多相同的MIB对象提供服务,这些对象表示被管理的硬件或软件的完全不同的实例。这里可以用SNMP上下文。
为了在高级API hlapi中指示SNMP上下文,应该使用正确初始化的ContextData对象。对于本例,我们将使用“empty”上下文(默认)

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
· · ·                    CommunityData('public'),
· · ·                    UdpTransportTarget(('demo.snmplabs.com', 161)),
· · ·                    ContextData(),
· · ·

Specifying MIB object:指定MIB对象

最后,我们必须指定要读取的MIB对象。在协议层上,MIB对象由OIDs标识,但人们倾向于通过名称来调用它们,哈哈哈!

$ 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链接由称为对象类型的高级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中,我们使用负责MIB对象标识的 ObjectIdentity 类。ObjectIdentity标识从人的角度处理MIB对象的方法。它需要咨询MIB才能进入完全“解决”的状态。ObjectIdentity可以使用MIB对象名进行初始化,在进行MIB查找之后,它将开始执行类似于OID的操作。

>>> 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.sysDdescr')
>>> # . . . calling MIB lookup . . .
>>> str(x)
'1.3.6.1.2.1.1.1'

calling MIB lookup类似如下

from pysnmp.smi import builder, view

mib_builder = builder.MibBuilder()
mib_view = view.MibViewController(mib_builder)
mib_var = ObjectIdentity('SNMPv2-MIB', 'system', '0')
mib_var.resolveWithMib(mib_view)

print tuple(mib_var)
print str(mib_var)

MIB解析是指将MIB对象名称到OID的转换,反之亦然。

ObjectType 类的实例标识PySNMP中的对象类型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中描述的对象指示声明,它们从来不包含任何数据。数据存储在MIB对象的实例中,通过向MIB对象标识符附加额外信息(称为索引)来寻址。
对于标量MIB对象,按照惯例索引是’0’。ObjectIdentity类将索引作为其初始化器。

>>> x = ObjectIdentity('SNMPv2-MIB', 'system', 0)
>>> # ... calling MIB lookup ...
>>> tuple(x)
(1, 3, 6, 1, 2, 1, 1, 1, 0)

我们将读取在SNMPv2-MIB模块中定义的 sysDesc 标量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文件。还可以将其配置为从远程主机自动下载,如示例所示。我们维护了一个ASN.1 MIB模块的集合,你可以在你的SNMP项目中使用它。

note:
一个“ASN.1 MIB”是一个iddentifier和types的纯文本描述。它是制造商用来描述其SNMP服务的通用格式,与Perl的Net::SNMP和几乎所有SNMP工具使用的格式相同。

Reading scalar value

我们终于可以发送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))])

Working with SNMP tables

SNMP定义了表的概念,当一个给定的MIB对象将应用于一个属性的多个实例时,将使用表。例如,将网络接口的属性放到SNMP表中。属性的每个实例都由附加到MIB对象的后缀来寻址。
表再MIBs中指定,它们的索引通过INDEX语句声明。表索引是非零整数、字符串或任何基本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的一部分,彼此相连,并最终连接到MIB对象的OID。
从语义的角度看,每个索引都反映了一个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-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 command operations:SNMP命令操作

SNMP允许你请求一个MIB对象,通过next获取下一个值。通过这种方式,你可以提前读取你不知道的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 agent的MIB对象s。

SNMPv2为GETNEXT命令引入了重要的优化——修改后叫GETBULK,能立即收集和响应一组“下一个”MIB对象。附加的非重复器和大量重复参数可用于影响MIB对象的批处理。

PySNMP在协议级别隐藏了GETBULK的优化,为了方便,bulkCmd()函数公开了和getNext()相同的生成器API。

>>> from pysnmp.hlapi imort *
>>>
>>> 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 hlapi使用该特性对一组新的MIB对象重复相同的SNMP操作。

>>> 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))])

你可以通过在一个PDU中列出许多不相关的MIB对象来操作它们。响应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() 函数统一支持设置。

>>> 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'))])

sending SNMP notifications:发送SNMP通知

托管实体可以向管理实体发送未经请求的消息。在SNMP中称为通知notification。通知有助于减少轮询,轮询在大型网络中是一个麻烦的问题。
SNMP通知是枚举的,每个通知都有明确的语义。这是通过一种称为NOTIFICATION-TYPE的特殊高级SMI构造实现的。与定义MIB对象的 OBJECT-TYPE一样,NOTIFICATION-TYPE也有唯一的OID,但是它引用的是其他的MIB对象序列,而不是SNMP值。这些MIB对象由OBJECTS子句指定,当发送通知时,它们的当前值包含在通知消息中。

linkUp NOTIFICATION-TYPE
    OBJECTS { ifIndex, ifAdminStatus, ifOperStatus }
    STATUS  current
    DESCRIPTION
        "..."
::= { snmpTraps 4 }

为了在PySNMP中建模NOTIFICATION-TYPE结构,我们有一个Notification Type类的容器对象。它由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。
在trap中,agent-to-manager 是单向的,不发生响应或确认消息。

>>> 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格式。Inform用于manager-to-manager通信以及agent-to-manager通信。

>>> 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表对象的特定行,可以通过instanceIndex参数将MIB对象的索引部分传递给NotificationType。

如你所见,扩展的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))])

High-volume messaging

在管理大型网络时,按顺序读取MIB对象会导致延迟。在某个时间点,延迟变得无法忍受。并行化查询的解决方案是众所周知的——您可以通过将单个操作转移到多个进程或多个执行线程中,或者围绕异步I/O模型构建应用程序来实现这一点。

与其他解决方案相比,异步模型是最轻量级和可伸缩的。这个想法很简单:永远不要等待I/O——只要可能,就做其他事情。这背后的问题是,执行流变成了非线性的,这影响了人类读者对程序的分析。

PySNMP高级API采用了三种流行的异步I/O框架——asyncore、twisted和asyncio。有关异步API的更多信息,请参阅PySNMPlibrary referenceexample

 类似资料: