SNMP ,简称简单网络管理协议,是 SDN 的必备工具,是软件控制设备的最佳选择。即使除此之外,应用内访问也是 SNMP 的主要目的。毫无疑问,所有监控系统都利用 SNMP 来监控服务器和网络设备。在脚本中拥有来自 SNMP 的巨大力量将是惊人的。因此,我们将在本教程中讨论 SNMP 在 Python 编程语言中的使用。
但是在我们开始之前,让我们讨论一下 SNMP。
了解简单网络管理协议
SNMP ,也称为简单网络管理协议,是管理服务器和远程设备(代理)之间进行通信的标准方式。SNMP 的目标是让管理人员了解(甚至更改)代理上的数据。例如,管理器可以检查哪些接口是打开的,哪些是关闭的,或者更改远程设备的主机名。
我们的脚本使管理站上的 Python 程序能够控制执行 SNMP 代理的远程设备。
SNMP 代理在一个特殊的表 MIB 中准备了管理器可以读取或更改的大部分细节。MIB 是一个树状结构,其中一个数字表示树中的每个节点。例如 1.3.6.1.2.1.1 表示系统的描述。如果我们中的一些人想知道这个数字链是从哪里来的,这就是整个树的结构!每个数字都与一个名字相关联。因此,我们可以把它翻译成更明确的iso . org . DOD . internet . mgmt . MIB-2 . system . SysDerse。
现在,让我们借助 Python PySNMP 模块来讨论 SNMP 在 Python 编程语言中的使用。
了解 PySNMP 模块
PySNMP 是 Python 的开源模块。与 telnet 或 HTTP 不同,Python 本身并不实现 SNMP。毕竟,仅仅是网络和系统工程师就需要工厂里有一个 Python 开发人员。 PySNMP 很好地弥补了原生 Python 的不足。总的来说,SNMP模块允许我们使用任何版本的 SNMP,既可以作为代理,也可以作为管理器。创建代理意味着我们正在构建应用或设备。然而,我们将只讨论 PySNMP 在管理远程设备中的使用。
而且,我们会了解 Python 中 PySNMP 的不同功能。本教程的主要目标是创建一个快速的 python 程序,使我们的工作变得简单。在程序中,我们将拥有所需的所有 SNMP 操作。
所以,让我们开始吧。
准备环境
首先我们要安装 PySNMP 模块。我们可以使用 pip 安装程序,在以下命令的帮助下安装所需的模块。
语法:
$ pip install pysnmp
该模块将作为 Python 和 pip 版本安装在系统中。
验证安装
为了检查模块是否已经正确安装在系统中,我们可以尝试导入模块并执行程序。
安装完成后,创建一个新的 Python 文件,并在其中键入以下语法。
示例:
# importing the required module
import pysnmp
现在,保存该文件并在命令提示符下使用以下命令运行该文件。
语法:
$ python .py
如果程序运行时没有出现任何导入错误,则模块安装正确。否则,建议重新安装该模块,并参考其官方文档。
理解 Python SNMP 获取操作
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 或远程设备名)。然后,它需要我们需要获取的对象标识(oid)列表,然后是一组用于会话身份验证的凭据。如果需要,我们还可以指定一个不同的 UDP 端口,并利用流行的 SNMP 引擎或自定义上下文。我们可能需要对同一设备上的所有操作使用相同的引擎,这样可以节省资源。但是,对于一个简单的代码片段来说,这不是必需的,这样我们就可以忽略两个引擎以及上下文。
该函数为 SNMP 会话生成一个处理程序,并从中获取详细信息。为了实现这一点,它依赖于我们必须创建的两种方法:构造 对象 类型和获取。
构造对象类型
正如我们之前讨论的,拥有更多的力量意味着更多的复杂性。因此, 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。它只是根据计数变量在处理程序上循环多次。如果出现任何错误,将停止该过程,并返回运行时间错误消息。在其他情况下,它将数据存储在字典列表中。
示例:
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-除了方法,为了特定的原因停止迭代。在用户指定的计数高于我们实际拥有的对象数量的情况下,我们只需停止并返回到目前为止得到的结果。这就是构造的目的。
返回字典列表的需求是什么?在每个 get 操作中,我们可以得到各种各样的对象 id。因此,每个字典都将由作为关键字的对象标识和作为该关键字值的管理信息库中的 OID 值组成。在一次获取中需要多个 oid 的场景中,我们将返回一个带有多个键的字典。但是,有什么必要列出清单呢?使用 get 操作,我们只能检索一次数据。然而,正如我们将在 get bulk 中看到的,我们可能需要在不同的实例上多次获得类似的细节。让我们考虑一个更好理解的例子。假设我们需要检查所有接口上的错误:信息总是错误;然而,我们有不同的实例(每个接口一个)。我们可以使用列表来可视化它,其中每个数据元素都是描述一个实例的字典。
注意:fetch()函数依赖于我们必须创建的另一个函数:cast()。该函数将从 PySNMP 接收的数据转换为 int、float 或 string。
让我们考虑下面的代码片段:
示例:
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-除了方法来检查任何引发的错误。
提供凭据
PySNMP 库的认证系统很强大,也很简单。没有理由在上面额外写一层,这样我们就可以直接利用它。 get() 函数与其他函数一样,在凭证变量中有一个特殊的身份验证对象。如果我们使用 SNMPv2c 或 SNMPv3,这个对象是变化的。
在 SNMPv2c(或更低)的情况下,我们必须指定社区。我们可以使用社区数据对象来实现,如下所示:
语法:
hlapi.CommunityData('JAVATPOINT')
相反,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('JAVATPOINT')))
输出:
{'1.3.6.1.2.1.1.5.0': 'R1.sdn.local'}
说明:
在上面的代码片段中,我们打印了 get() 函数获取设备主机名的结果。
注意:在这里,我们没有得到字典的列表,而只是一个字典。这是经过深思熟虑的,get()函数在任何时候都是这样的。一般我们知道函数只执行一次;它不能创建多个实例。因此,我们返回从 fetch()获得的第一个元素。
理解 Python SNMP 批量获取
get_bulk() 方法检索同一个对象标识的多个实例,假设每个接口一个。当我们使用表时,这个函数变得可支持,比如接口一的路由表。它相当简单,功能类似于 get() 方法。但是它需要一些额外的细节:从哪个对象开始,以及我们需要获得的实例数量。我们在从开始发货,从算起。
让我们考虑以下代码片段来理解 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() 功能的改进版。假设我们想使用 get_bulk() 循环访问设备接口。我们如何知道接口的数量?这是必要的,因为 SNMP 需要知道迭代多少次。我们不能提前知道;但是,我们可以使用 SNMP 作为一个选项来查找这些信息。
我们可以使用 get_bulk_auto() 函数告诉代码从另一个 OID 中检索计数变量。因此,我们可以指定一个对象标识,而不是指定一个数字。该函数将获取该对象,并将其用作计数。
为了更好地理解,让我们考虑下面的例子。
示例:
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() 指定了计数变量,并返回 get_bulk() 函数。
使用 Python SNMP 批量获取和批量自动获取
让我们考虑执行下面的代码片段。
示例:
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('JAVATPOINT'),
'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=