Python教程-SNMP Python模块

SNMP,即Simple Network Management Protocol,是SDN的必要工具,也是控制软件中的设备的最佳选项。即使不考虑这一点,SNMP的主要目的是应用程序访问。毫无疑问,所有监控系统都使用SNMP来监视和控制服务器和网络设备。在脚本中具备SNMP的强大功能将会非常有用。因此,在本教程中,我们将讨论如何在Python编程语言中使用SNMP。
但在开始之前,让我们先了解一下SNMP。
了解SNMP
SNMP(Simple Network Management Protocol),也称为简单网络管理协议,是管理服务器和远程设备(代理)之间通信的标准方式。SNMP的目标是使管理者能够了解(甚至更改)代理上的数据。例如,管理者可以检查哪些接口处于活动状态,哪些接口已停用,或更改远程设备的主机名。
我们的脚本能够在管理站上运行Python程序,以控制执行SNMP代理的远程设备。
SNMP代理准备了管理者可以在特殊表(MIB)中读取或更改的大部分信息。MIB是一种树状结构,其中树中的每个节点都有一个编号。例如,1.3.6.1.2.1.1代表系统的描述。如果您想知道这些数字是从哪里来的,那就是整个树结构!每个数字都关联一个名称。因此,我们可以将其翻译成更具说明性的iso.org.dod.internet.mgmt.mib-2.system.sysDescr。
现在,让我们使用Python的PySNMP模块来了解如何在Python编程语言中使用SNMP。
了解PySNMP模块
PySNMP是Python的一个开源模块。与telnet或HTTP不同,Python本身不原生实现SNMP。毕竟,只有网络和系统工程师才需要在工厂中雇佣Python开发人员。PySNMP在覆盖Python本机功能的不足方面做得很好。总的来说,PySNMP模块允许我们使用任何版本的SNMP,无论是作为代理还是管理者。创建代理意味着我们正在构建一个应用程序或设备。然而,我们只会讨论如何使用PySNMP来管理远程设备。
此外,我们将了解PySNMP在Python中的不同功能。本教程的主要目标是创建一个可以为我们简化事情的快速Python程序。在程序中,我们将包含我们需要的所有SNMP操作。
那么,让我们开始吧。
准备环境
首先,我们需要安装PySNMP模块。我们可以使用pip安装程序来安装所需的模块,使用以下命令:
命令:
$ pip install pysnmp
该模块将根据Python和pip的版本安装在系统中。
验证安装
为了验证模块是否已正确安装在系统中,我们可以尝试导入模块并执行程序。
安装完成后,创建一个新的Python文件,然后在文件中键入以下语法。
示例:
# importing the required module
import pysnmp
现在,保存文件并在命令提示符中使用以下命令运行文件。
命令:
$ python <file-name>.py
如果程序运行而没有引发任何导入错误,那么模块已正确安装。否则,建议重新安装模块并查看其官方文档。
了解Python SNMP Get操作
SNMP的Get操作允许我们检索MIB中单个对象的值。我们还可以使用它来获取单个对象的列表。我们可以编写如下的get()函数:
示例:
# importing the required module
from pysnmp import hlapi
# defining the get() function
def get(
target,
oids,
credentials,
port = 161,
engine = hlapi.SnmpEngine(),
context = hlapi.ContextData()
):
handler = hlapi.getCmd(
engine,
credentials,
hlapi.UdpTransportTarget((target, port)),
context,
*construct_object_types(oids)
)
return fetch(handler, 1)[0]
解释:
在上面的代码片段中,我们可以看到PySNMP的高级API的使用。我们定义了一个名为get()的简单函数,首先需要目标(IP或远程设备名称)。然后,它需要我们需要获取的对象ID(oids)的列表以及会话身份验证的一组凭据。如果需要,我们还可以指定不同的UDP端口,使用现有的SNMP引擎或自定义上下文。我们可能需要在相同设备上的所有操作上使用相同的引擎,这可以节省资源。但是,对于简单的代码段,这不是必需的,所以我们可以忽略引擎和上下文。
该函数生成了SNMP会话的处理程序,并从中获取数据。为了执行此操作,它依赖于我们必须创建的两种方法:construct_object_types和fetch。
构建对象类型
正如我们之前讨论过的,更多的功能意味着更多的复杂性。因此,hlapi.getCmd()函数需要一些特殊的hlapi.ObjectType对象,而不是简单的字符串OID列表。因此,construct_object_type函数是根据PySNMP的需求创建的。如果没有时间,我们可以将其简单地复制并粘贴到代码中。但这应该是一个非常简单的函数,让我们来看看:
示例:
def construct_object_types(listOfOids):
objectTypes = []
for oid in listOfOids:
objectTypes.append(hlapi.ObjectType(hlapi.ObjectIdentity(oid)))
return objectTypes
解释:
在上面的代码片段中,我们定义了一个函数,该函数返回一个列表,可以通过在get()方法中使用与操作数一样的方式来扩展它。
获取数据
fetch()函数是Python SMP教程的杰作。总的来说,我们编写它以便在其他基于PySNMP的函数上可以重复使用它,例如get-bulk。它只是在处理程序上多次循环,次数取决于计数变量。如果出现任何错误,流程将停止,并返回RuntimeError消息。在其他情况下,它会将数据存储在字典列表中。
示例:
def fetch(handler, count):
res = []
for i in range(count):
try:
error_indication, error_status, error_index, var_binds = next(handler)
if not error_indication and not error_status:
items = {}
for var_bind in var_binds:
items[str(var_bind[0])] = cast(var_bind[1])
res.append(items)
else:
raise RuntimeError('Got SNMP error: {0}'.format(error_indication))
except StopIteration:
break
return res
解释:
在上面的代码片段中,我们构建了try-except方法以停止特定原因的迭代。如果用户指定的计数高于我们实际拥有的对象数量,我们只需停止并返回到目前为止获取的内容。这是构建的目的。
为什么需要返回一个字典列表呢?在每个get操作中,我们可以获取多个对象ID。因此,每个字典将包含对象ID作为键,MIB中OID的值作为该键的值。在需要一次获取多个OID的情况下,我们将返回包含多个键的字典。但是列表有什么用呢?使用get操作,我们只能一次检索数据。但是,正如我们将在get-bulk中看到的那样,我们可能需要在不同实例上多次获取相同的数据。为了更好地理解,让我们考虑一个示例。假设我们需要检查所有接口上的错误:信息总是错误,但我们有不同的实例(每个接口一个)。我们可以使用一个列表来表示,其中每个数据元素都是表示一个实例的字典。
注意: fetch()函数依赖于另一个我们必须创建的函数:cast()。此函数将从PySNMP接收的数据转换为整数、浮点数或字符串。
让我们看一下以下示例来了解:
示例:
def cast(val):
try:
return int(val)
except (ValueError, TypeError):
try:
return float(val)
except (ValueError, TypeError):
try:
return str(val)
except (ValueError, TypeError):
pass
return val
解释:
在上面的代码片段中,我们定义了一个函数,该函数使用try-except方法检查是否引发任何错误。
提供凭据
PySNMP 库的身份验证系统强大且非常直观。没有必要在其上再写一层额外的代码,所以我们可以直接使用它。get() 函数以及其他函数都在凭据变量中有一个特殊的身份验证对象。如果使用 SNMPv2c 或 SNMPv3,这个对象会有所不同。
对于 SNMPv2c(或更低版本),我们需要指定社区。我们可以使用以下的 CommunityData 对象来实现:
语法:
hlapi.CommunityData('javatiku')
而 SNMPv3 较为复杂。这是因为它使用一个用户带有两个密码和两个协议:一个用于身份验证,另一个用于加密。因此,我们需要指定用户名、身份验证密码、身份验证协议、加密密码和加密协议。我们可以使用 UsmUserData 类来实现。
让我们通过以下示例更好地理解。
示例:
hlapi.UsmUserData(
'testuser',
authKey = 'authenticationkey',
privKey = 'encryptionkey',
authProtocol = hlapi.usmHMACSHAAuthProtocol,
privProtocol = hlapi.usmAesCfb128Protocol
)
解释:
在上面的代码片段中,我们使用了 UsmUserData 类并输入了所需的详细信息。这比它看起来简单,我们只需了解远程设备的协议。不过,一些人可能需要参考完整的 UsmUserData 官方文档。
获取主机名
现在,让我们测试 get() 函数。我们将使用它来检索设备的主机名,其对象为 1.3.6.1.2.1.1.5.0。我们可以简单地编写以下代码片段:
示例:
print(get('10.0.0.1', ['1.3.6.1.2.1.1.5.0'], hlapi.CommunityData('javatiku')))
输出:
{'1.3.6.1.2.1.1.5.0': 'R1.sdn.local'}
解释:
在上面的代码片段中,我们打印了 get() 函数获取设备主机名的结果。
注意:这里我们不会得到一个字典列表,而只是一个字典。这是有意的,get() 函数在所有情况下都会表现得像这样。通常情况下,我们知道该函数只会执行一次,它无法创建多个实例。因此,我们返回了从 fetch() 获取的第一个元素。
理解Python SNMP Get Bulk
get_bulk() 方法用于检索相同对象标识的多个实例,比如每个接口的一个实例。当我们处理表格时,如接口一的路由表时,这个函数变得非常有用。它非常直观,与 get() 方法类似。但是它需要一些额外的详细信息:从哪个对象开始以及我们需要获取多少个实例。我们在 start_from 和 count 中提供这些信息。
让我们考虑以下代码片段以了解 get_bulk() 函数的工作原理。
示例:
def get_bulk(
target,
oids,
credentials,
count,
start_from = 0,
port = 161,
engine = hlapi.SnmpEngine(),
context = hlapi.ContextData()):
handler = hlapi.bulkCmd(
engine,
credentials,
hlapi.UdpTransportTarget(( target, port )),
context,
start_from, count,
*construct_object_types( oids )
)
return fetch(handler, count)
解释:
在上面的代码片段中,我们定义了 get_bulk() 函数,其中指定了处理程序。最后,我们返回了 fetch() 函数。因此,我们期望得到一个字典列表,因此我们不需要像使用 get() 函数那样提取第一个字典。
理解Python SNMP Get Bulk Auto
get_bulk_auto() 函数是 get_bulk() 函数的改进版本。假设我们想使用 get_bulk() 遍历设备接口。我们如何知道接口的数量?这变得必要,因为 SNMP 需要知道要迭代多少次。我们无法提前知道这一点,但我们可以使用 SNMP 作为选项来查找这些信息。
我们可以使用 get_bulk_auto() 函数告诉代码从另一个对象标识中检索计数变量。因此,我们可以指定一个对象标识,而不是指定一个数字。函数将获取该对象并将其用作 count。
让我们考虑以下示例以更好地理解。
示例:
def get_bulk_auto(
target,
oids,
credentials,
count_oid,
start_from = 0,
port = 161,
engine = hlapi.SnmpEngine(),
context = hlapi.ContextData()):
count = get(
target,
[count_oid],
credentials,
port,
engine,
context
)[count_oid]
return get_bulk(target, oids, credentials, count, start_from, port, engine, context)
解释:
在上面的代码片段中,我们定义了 get_bulk_auto() 函数,并使用 get() 指定了 count 变量,然后返回了 get_bulk() 函数。
使用Python SNMP Get Bulk和Get Bulk Auto
让我们考虑执行以下代码片段。
示例:
ele = get_bulk_auto(
'10.0.0.1',
['1.3.6.1.2.1.2.2.1.2 ', '1.3.6.1.2.1.31.1.1.1.18'],
hlapi.CommunityData('javatiku'),
'1.3.6.1.2.1.2.1.0')
for i in ele:
for x, y in i.items():
print("{0} = {1}".format(x, y))
print('')
输出:
1.3.6.1.2.1.2.2.1.2.1=FastEthernet1/0
1.3.6.1.2.1.31.1.1.1.18.1=
1.3.6.1.2.1.2.2.1.2.2=FastEthernet0/0
1.3.6.1.2.1.31.1.1.1.18.2=
1.3.6.1.2.1.2.2.1.2.3=FastEthernet0/1
1.3.6.1.2.1.31.1.1.1.18.3=Test Desc
1.3.6.1.2.1.2.2.1.2.4=Serial2/0
1.3.6.1.2.1.31.1.1.1.18.4=
1.3.6.1.2.1.2.2.1.2.5=Serial2/1
1.3.6.1.2.1.31.1.1.1.18.5=
1.3.6.1.2.1.2.2.1.2.6=Serial2/2
1.3.6.1.2.1.31.1.1.1.18.6=
1.3.6.1.2.1.2.2.1.2.7=Serial2/3
1.3.6.1.2.1.31.1.1.1.18.7=
1.3.6.1.2.1.2.2.1.2.9=Null0
1.3.6.1.2.1.31.1.1.1.18.9=