引言
对于简单网络管理协议 (SNMP),大多数系统管理员都具有一定的使用经验,或者至少听说过它。如果您正在一个数据中心工作,那么您每天都可能采用某种方式与 SNMP 进行交互。有许多给人印象深刻的、同等规模的网络管理系统 (NMS) 或者网络监视系统使用了 SNMP 监视,但本文并不打算介绍这些系统。本文主要涉及的是通过 Python? 语言来研究 SNMP,并亲自编写相关的代码。
一位朋友最近告诉我,有时候遇到的情况就像:只是希望沿着街道一路走到奶奶家,而不需要乘坐像 Saturn V 火箭一样飞快。有许多任务,如果利用或者配置大规模的 NMS,就好像是一个 Saturn V 火箭,在填满液氧罐之前,先尝试一下使用 Python,那么您将得到更好的服务。了解如何编写灵活的 Python 代码与 SNMP 进行交互,这可能是系统管理员可以获得的、最有趣且最高效的技能之一。尽管 SNMP 的设置和使用非常复杂,但本文中所讨论的内容将使它变得非常有趣。
安装和配置 Net-SNMP
要学习本文中的内容,您需要在您的 *nix 计算机中安装最新的 Python(即 Python 2.3 或者更高版本)。在撰写本文时,Python 2.5.1 是 Python 的最新版本。您还需要 IPython,以便以交互的方式使用带 Python 绑定的 Net-SNMP 库。Net-SNMP 团队对各种操作系统中的支持进行了详细测试,具体包括 AIX?、HP-UX?、GNU/Linux? 分发版(如 Red Hat)、Windows?,甚至 OS X?。
安装 IPython 是一项非常简单的工作。一个很好的选择是使用 Easy Install 来管理 Python 包。通过运行 ez_setup.py 脚本,您可以很容易地安装任何 Python 包。例如,您只需要键入以下命令:
1
|
easy_install ipython
|
其他可选的安装方式包括,使用您最喜欢的包管理系统、或者只需下载 IPython 并键入以下命令:
1
|
python setup.py install
|
请注意,干线 (trunk) 指的是版本控制系统中的根路径,其中保存了最近的代码副本。此外,干线还常常表示一个子版本和版本控制系统。有关更详细的内容,请参见参考资料部分中的子版本链接。
要学习本文中的内容,您需要确保您的客户端计算机、或者运行所有代码的计算机都安装了 NET-SNMP Version 5.4.x 或者更高版本,因为从这个源代码版本开始,包括了 Python 绑定。在大多数情况下,绑定的安装需要对源文件进行编译;然而,也可以使用 Red Hat Package Managers (RPM)。如果你有兴趣和时间,那么可以从 Net-SNMP Web 站点查看一下它的最新版本。
有许多编译选项可供使用,但主要的任务是对 NET-SNMP 进行正确编译,然后运行 Python 目录中的、独立的 Python 安装程序。另一个需要注意的问题是,当您进行编译并运行 ./configure 的时候,它将运行本地计算机(正在编译代理的计算机)的配置脚本。您不应该使用该配置脚本,对于本文而言,您只需要创建一个简单的配置脚本。
对 /etc/snmp/snmpd.conf 中所存储的配置文件进行备份,并构建下面这个非常基本的配置:
1
2
3
|
syslocation "My Local Machine"
syscontact me@localhost.com
rocommunity public
|
保存它,并重新启动 snmpd 守护进程。在大多数 *nix 系统中,可以使用 /etc/init.d/snmpd restart 来完成这项工作。
除非在不得已的情况下,否则最好不要对处于活动开发阶段的版本进行编译,因为这样做,您将需要自己动手修复有问题的代码。在 CentOS 5 或者 Red Hat Enterprise Linux 5 (RHEL 5) 中,您可以下载最新的、稳定的源文件 RPM(在撰写本文之时,是 5.4.1 版本)。请注意,您还需要使用 Python 源文件来构建绑定。因此,如果您正在使用一台基于 Red Hat 的计算机,那么请确保为特定的 *nix 操作系统安装了 python-dev(或者其等价物)和 Python Header 文件。如果您在从源文件构建 RPM 的过程中,需要任何帮助的话,请参考官方的 Red Hat 文档。从源文件构建包可能是一个非常复杂的主题,并且已经超出了本文的范围。如果您在使用 Python 绑定的时候遇到了麻烦,那么您应该在 Net-SNMP 邮件列表中请求帮助。
对代码进行分析
还等什么呢?假设您已经安装了 Python 绑定和 IPython。现在,您已经做好了使用 IPython 的准备,并开始相关的工作。尽管在某些时候,您可能还需要浏览 IPython 文档。Jeff Rush 是当前的 Python Advocacy Coordinator,他为 IPython 提供了一些非常好的屏幕录像内容。好的,让我们开始进行编码。
让我们进行一次简单的查询,以便通过使用计算机的对象标识符 (OID) 值 sysDescr 来标识一台计算机。通过键入 ipython 启动 IPython,然后执行这个交互式会话:
清单 1. IPython 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
In [ 1 ]: import netsnmp
In [ 2 ]: oid = netsnmp.Varbind( 'sysDescr' )
In [ 3 ]: result = netsnmp.snmpwalk(oid,
...: Version = 2 ,
...: DestHost = "localhost" ,
...: Community = "public" )
In [ 4 ]: result = netsnmp.snmpwalk(oid,
Version = 2 ,
DestHost = "localhost" ,
Community = "public" )
In [ 16 ]: result
Out[ 16 ]: ('Linux localhost 2.6 . 18 - 8.1 . 14.el5 #1 SMP Thu Sep 27
18 : 58 : 54 EDT 2007 i686',)
|
请注意,您所得到的 result 值与这里所显示的 result 值是不同的。如果您已经遵循了上面清单 1 中所显示的配置,那么所有其他的内容都应该是可用的。如果您对 SNMP 比较熟悉,那么您可能马上就能够明白这些内容的实际作用。
使用 IPython 来测试 SNMP 代码片段的好处之一是,它就好像是一个普通的 Shell,并且许多基本的、交互式 Shell 的概念“都可以起作用”,但需要说明的是,它们是以 python 方式进行工作的。编写 SNMP 代码可能是一项非常单调乏味的活动,但是通过 IPython 来使用 Net-SNMP 库将使其变得非常有趣。
正如您所看到的,要将结果转换为 Python 数据类型,这项任务是非常简单的。这就是为什么 IPython 和 Net-SNMP 可以很好地在一起工作的原因。现在,对于编写自定义脚本来说,只需要以交互方式分析 OID 的组合以进行查询。在理想的情况下,需要运行一个大规模的、易于配置的 NMS 设置脚本,自动地将新的计算机集成到网络中。
当然,并不存在这种理想的情况,您需要了解如何将一些好的 SNMP 代码组合到一起,对于一名系统管理员来说,这是非常有用的。下面给出一个示例情况,假设您刚刚将一个高速 DDR 转换为正在运行 Ubuntu Linux 的 2 TB RAID 0 服务器,因为您必须在一个小时的时间内解决问题,所以您不得不这样做。
现在,您遇到了很大的麻烦,并且您只有几分钟的时间来监视具体的问题,以了解是否需要开始发送简历、或者是否应该开始准备讲话并请求升职。让我们使用 IPython 中的编辑功能来编写脚本,并将它保存到文件中,然后在不离开 IPython 的情况下,在一个会话中运行它: ed snmpinput.py
清单 2. IPython 模块的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import netsnmp
class snmpSessionBaseClass( object ):
"""A Base Class For a SNMP Session"""
def __init__( self ,
oid = "sysDescr" ,
Version = 2 ,
DestHost = "localhost" ,
Community = "public" ):
self .oid = oid
self .Version = Version
self .DestHost = DestHost
self .Community = Community
def query( self ):
"""Creates SNMP query session"""
try :
result = netsnmp.snmpwalk( self .oid,
Version = self .Version,
DestHost = self .DestHost,
Community = self .Community)
except :
import sys
print sys.exc_info()
result = None
return result
|
对 IPython 进行自定义以使用正确的编辑器
通过编辑 $HOME/.ipython/ipythonrc,您可以对 Ipython 进行自定义。首先需要进行自定义的内容之一是 %edit 命令,可以通过键入 ed 来调用该命令。在缺省情况下,它被设置为使用 vi,但是您可以更改它,以便使用其他编辑器,包括 Emacs。关于如何更改您的环境,本文给出了相应的指导。您还可以使用下面的命令来启动 Ipython,以便以硬编码的形式指定使用某个特定的编辑器: ipython -editor=vim。
继续执行,将下面的代码剪切并粘贴到您刚刚创建的文件中。当保存这个文件时,IPython 将自动地运行它,并将该类放置到您环境中相应的模块中。如果您键入 who,那么您将看到与以下所示类似的内容:
1
2
|
In [ 2 ]: who
netsnmp snmpSessionBaseClass
|
这项操作的功能非常强大,因为您可以获得使用您最喜欢的文本编辑器(也许是 Vim 或者 Emacs)的所有优点,然后在交互的 IPython Shell 会话中立即使用这些代码。请注意,如果您已经编写了一个模块,那么您还可以简单地键入并运行它,以获得相同的结果。执行和运行 IPython 中的模块,这就相当于运行其中的代码,并将其放入到 IPython 环境中。
以迭代的方式进行编码
现在通过使用 IPython,可以将 Python Shell、UNIX Shell 和您最喜欢的文本编辑器的最佳特性组合到一起。在与像 SNMP 库这样非常复杂的对象进行交互时,您需要使用可以获得的所有帮助,而在这个示例中,真正地展示了 IPython 的强大功能。
您可以动态地编写一些模块,并且您可以在稍后对其进行测试和使用。IPython 可以与任何编程风格很好地融合在一起,包括测试驱动的开发 (TDD) 或者测试增强的开发 (TED)。为了证明这种便利性,让我们转到您刚刚编写的模块。
既然已经有一个面向对象的 SNMP 接口,那么您就可以开始向本地计算机进行询问:
清单 3. IPython 迭代式编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
In [ 1 ]: run snmpinput
In [ 2 ]: who
netsnmp snmpSessionBaseClass
In [ 3 ]: s = snmpSessionBaseClass()
In [ 4 ]: s.query()
Out[ 4 ]: ( 'Linux localhost 2.6.18-8.1.14.el5 #1 SMP Thu Sep 27 18:58:54 EDT 2007 i686' ,)
In [ 5 ]: result = s.query()
In [ 6 ]: len (result)
Out[ 6 ]: 1
|
通过使用这个模块获得相关的结果,这是非常容易的,但是您基本上只是在运行一个硬编码脚本,因此需要更改 OID 对象的值,以遍历系统子树:
清单 4. 更改 OID 对象的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
In [ 7 ]: s.oid
Out[ 7 ]: 'sysDescr'
In [ 8 ]: s.oid = ".1.3.6.1.2.1.1"
In [ 9 ]: result = s.query()
In [ 10 ]: print result
( 'Linux localhost 2.6.18-8.1.14.el5 #1 SMP Thu Sep 27 18:58:54 EDT 2007 i686' ,
'.1.3.6.1.4.1.8072.3.2.10' , '121219' , 'me@localhost.com' ,
'localhost' , '"My Local
Machine" ', ' 0 ', ' . 1.3 . 6.1 . 6.3 . 10.3 . 1.1 ', ' . 1.3 . 6.1 . 6.3 . 11.3 . 1.1 ',
'.1.3.6.1.6.3.15.2.1.1' ,
'.1.3.6.1.6.3.1' , '.1.3.6.1.2.1.49' , '.1.3.6.1.2.1.4' , '.1.3.6.1.2.1.50' ,
'.1.3.6.1.6.3.16.2.2.1' ,
'The SNMP Management Architecture MIB.' , 'The MIB for Message Processing and
Dispatching. ', ' The management information definitions for the SNMP
User - based Security Model. ', ' The MIB module for SNMPv2 entities ', ' The
MIB module for managing TCP implementations ', ' The MIB module for
managing IP and ICMP implementations ', ' The MIB module for managing
UDP implementations ', ' View - based Access
Control Model for SNMP. ', ' 0 ', ' 0 ', ' 0 ', ' 0 ', ' 0 ', ' 0 ', ' 0 ', ' 0 ')
|
正如您可以看到的,要使用这个模块并开始分析整个网络(一次一台计算机),这项任务是非常容易的。请仔细地进行分析,并确定需要在网络中进行查询的内容。这是 IPython 的另一个有趣的特性,有必要对其进行深入研究。IPython 具有一个令人难以置信的特性,即允许您将 Python 代码片段作为后台进程来运行。幸运的是,进行这项操作非常简单。让我们再次运行相同的查询,但这一次将其作为后台进程运行(请参见清单 5)。
清单 5. IPython 迭代式编码示例——后台进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
In [ 11 ]: bg s.query()
Starting job # 0 in a separate thread.
In [ 12 ]: jobs[ 0 ].status
Out[ 12 ]: 'Completed'
In [ 16 ]: jobs[ 0 ].result
Out[ 16 ]:
( 'Linux localhost 2.6.18-8.1.14.el5 #1 SMP Thu Sep 27 18:58:54 EDT 2007 i686' ,
'.1.3.6.1.4.1.8072.3.2.10' , '121219' , 'me@localhost.com' , 'localhost' , '"My Local
Machine" ', ' 0 ', ' . 1.3 . 6.1 . 6.3 . 10.3 . 1.1 ', ' . 1.3 . 6.1 . 6.3 . 11.3 . 1.1 ',
'.1.3.6.1.6.3.15.2.1.1' , '.1.3.6.1.6.3.1' , '.1.3.6.1.2.1.49' ,
'.1.3.6.1.2.1.4' , '.1.3.6.1.2.1.50' , '.1.3.6.1.6.3.16.2.2.1' ,
'The SNMP Management Architecture MIB.' , 'The MIB for Message Processing and
Dispatching. ', ' The management information definitions for the SNMP User - based
Security Model. ', ' The MIB module for SNMPv2 entities ', ' The MIB module for
managing TCP implementations ', ' The MIB module for managing IP and ICMP
implementations ', ' The MIB module for managing UDP implementations',
'View-based Access Control Model for SNMP.' , '0' , '0' , '0' , '0' , '0' , '0' , '0' , '0' )
|
令人激动的是,IPython 中的后台线程是一种非常有价值的功能,但它只能与支持异步线程的库一起工作。不幸的是,Net-SNMP 是同步的。如果您对这一点感兴趣,那么可以通过将 s.oid 值更改为 .iso 来进行测试。在查询完成之前,您应该可以注意到,IPython 解释器发生了“阻塞”或者“挂起”。尽管只是一个警告,但对整个 .iso 树的 SNMP 遍历可能会花费较长的时间,因此您可能会接受我的观点。
当然,还有另一种解决方案。您可以使用 Python 所提供的众多处理库中的其中一个,为这个阻塞的进程派生新的进程。Python Cheese Shop 提供了一些第三方库。如果您正在使用 easy_install,那么要安装一个包(如 Parallel Python)并使用 Net-SNMP 来测试这个库,将是相当简单的,因此这将由您来决定。
1
|
easy_install http: //www .parallelpython.com /downloads/pp/pp-1 .5.zip
|
这里要展示的最后一个特性是,在 IPython Shell 中运行单元测试。在对模块进行更改时,需要频繁地运行单元测试,而这也是非常容易的。您需要添加一个标记以运行 run -e,这样一来,您就可以在 IPython Shell 中避免回溯到单元测试模块。您可以在本文所附带的源文件中下载这个单元测试。
请注意,IPython 0.8.2 还有一个新的文档测试特性,它允许您在 IPython 中生成文档测试。文档测试是 Python 的一个很好的特性,因为与其他特性一起,它提供了一种为 API 创建可测试文档的方法。下面给出了一个示例,以说明如何在 IPython 中为我们的模块运行 doctest:
清单 6.以 doctest 模式运行 IPython
1
2
3
4
5
6
7
8
9
10
|
In [ 5 ]: % doctest_mode
* * * Pasting of code with ">>>" or "..." has been enabled.
Exception reporting mode: Plain
Doctest mode is : ON
>>> from snmpinput import snmpSessionBaseClass
>>> s = snmpSessionBaseClass()
>>> s.query()
( 'Linux devmws2.racemi.com 2.6.9-55.0.2.EL #1 Tue Jun 26 14:08:18 EDT 2007 i686' ,)
|
因为 doctest 模式将不加分析地执行 Python 语句,所以您必须小心,不要在 doctest 中使用可能更改的值,就像上面所给出的值。如果您在模块的 docstring 中粘贴了数行代码,那么您可以通过使用下面的方法来测试您的 API 文档:
1
2
3
4
5
6
|
def _test():
import doctest
doctest.testmod()
if __name__ = = "__main__" :
_test()
|
总结
在本文中,您已经了解了协同使用 Net-SNMP 和 IPython 将成为一种功能强大的组合。本文介绍了下面几个主要概念:
- Python 绑定:Net-SNMP 现在提供了 Python 绑定,这使得我们能够充分地利用 Python 的功能来处理 SNMP 协议。
- 处理库:目前,Python 绑定是同步的,但是使用处理库可以为每个请求派生新的进程,从而解决这个问题。
- 灵活的技术:对于系统管理员和软件工程师来说,IPython 是一种非常成熟、并且功能非常强大的工具。尽管本文只是简要地介绍了几种灵活的技术,如文档测试和单元测试,但是您可以应用这些技术,以进行任何以测试为中心的开发、或者以交互的方式编写和分析代码。
- SNMP 和 IPython:对于 SNMP 和 IPython 在单独或者共同使用时能够实现的功能,本文只是进行了简要介绍。
SNMP 非常复杂,它几乎使人们不敢想象编写任何有意义的代码,但是,我们希望本文所介绍的技术能够激发某些新的观点。如果您很好奇,想知道 SNMP 的 Python 实现究竟到了什么程度,那么可以研究一下 Zenoss,下载一个虚拟机,并对它进行测试。还有一个 API,您可以利用它来编写脚本,因此您可以将这里所学到的内容和全面的 Python NMS 相结合。当然,也适用于任何其他 NMS。