Skip to content

Latest commit

 

History

History
738 lines (519 loc) · 38.2 KB

07.md

File metadata and controls

738 lines (519 loc) · 38.2 KB

七、使用 Python 进行网络监控——第一部分

假设你在凌晨 2 点接到一个电话。另一端的人说:“您好,我们正面临一个影响生产服务的难题*。*我们怀疑这可能与网络有关。您能帮我们检查一下吗?对于这种紧急的开放式问题,您首先要做的是什么?”大多数时候,我想到的是:在网络运行到出现问题的这段时间里,发生了什么变化?你可能会检查你的监控工具,看看在过去的几个小时里是否有任何关键指标发生了变化。更好的是,如果您收到来自任何度量基准偏差的任何监控警报

在本书中,我们一直在讨论各种方法,以便系统地对我们的网络进行可预测的更改,目的是保持网络尽可能平稳地运行。然而,网络不是静态的——远远不是静态的——它们可能是整个基础设施中最具流动性的部分之一。根据定义,网络将基础设施的不同部分连接在一起,不断地来回传递流量。有很多活动部件可能会导致您的网络按预期停止工作:硬件故障、软件有漏洞、人为错误(尽管它们是出于好意)等等。这不是一个事情是否会出错的问题,而是一个发生错误的时间和原因的问题。我们需要一些方法来监控我们的网络,以确保它按预期工作,并希望在它不工作时得到通知。

在接下来的两章中,我们将研究执行网络监视任务的各种方法。到目前为止,我们看到的许多工具都可以绑定在一起,或者直接由 Python 管理。与我们所看到的许多工具一样,网络监控需要两个部分。首先,我们需要知道设备能够传输什么信息。其次,我们需要确定我们可以从中解读哪些有用的信息。

我们将介绍一些能够有效监控网络的工具:

  • 简单网络管理协议(SNMP
  • Matplotlib 和 Pygal 可视化
  • MRTG 与仙人掌

该列表并非详尽无遗,网络监控领域当然也不乏商业供应商。然而,我们将要介绍的网络监控的基础知识对于开源和商业工具都很有用。

实验室设置

本章的实验室类似于第 6 章、Python 网络安全中的实验室,但有一个区别:两个网络设备都是 IOSv 设备。下面是一个例子:

这两台 Ubuntu 主机将用于在网络上生成流量,以便我们可以查看一些非零计数器。

SNMP

SNMP 是用于收集和管理设备的标准化协议。尽管该标准允许您使用 SNMP 进行设备管理,但根据我的经验,大多数网络管理员更喜欢将 SNMP 仅作为一种信息收集机制。由于 SNMP 在 UDP(无连接)上运行,并且考虑到版本 1 和版本 2 中相对较弱的安全机制,因此通过 SNMP 进行设备更改往往会让网络运营商感到不安。SNMP 版本 3 在协议中添加了加密安全性和新的概念和术语,但该技术的适应方式因网络设备供应商而异。

SNMP 广泛应用于网络监控,自 1988 年作为 RFC1065 的一部分开始出现。操作非常简单,网络管理器向设备发送GET和SET请求,SNMP 代理使用每个请求的信息响应设备。最广泛采用的标准是 SNMPv2c,其定义见 RFC 1901–RFC 1908。它使用一个简单的基于社区的安全方案来实现安全性。它还引入了一些新功能,例如获取海量信息的能力。下图显示了 SNMP 的高级操作:

SNMP operations 

驻留在设备中的信息在管理信息库(MIB中结构化。MIB 使用包含对象标识符(OID)的分层名称空间,该名称空间表示可以读取并反馈给请求者的信息。当我们谈论使用 SNMP 来查询设备信息时,我们实际上是在谈论使用管理站来查询表示我们要查找的信息的特定 OID。有一个通用的 OID 结构,如系统和接口 OID,在供应商之间共享。除了通用 OID 之外,每个供应商还可以提供特定于他们的企业级 OID。

作为一名操作员,您需要花费一些精力将信息整合到环境中的 OID 结构中,以检索有用的信息。有时一次查找一个 OID 是一个繁琐的过程。例如,您可能向设备 OID 发出请求,并收到 10000 的值。这个值是多少?这是接口流量吗?它是字节还是位?或者可能是一些数据包?我们怎么知道?我们需要查阅标准或供应商文件以了解情况。有一些工具可以帮助完成此过程,例如可以为值提供更多元数据的 MIB 浏览器。但是,至少在我的经验中,为您的网络构建基于 SNMP 的监控工具有时会感觉像是一场猫捉老鼠的游戏,试图找到丢失的值

操作中需要注意的一些要点如下:

  • 实现在很大程度上依赖于设备代理可以提供的信息量。这又取决于供应商如何将 SNMP 视为核心功能或附加功能。
  • SNMP 代理通常需要控制平面的 CPU 周期来返回值。这不仅对于具有大型 BGP 表的设备效率低下,而且使用 SNMP 以较小的间隔查询数据也是不可行的。
  • 用户需要知道 OID 才能查询数据。

由于 SNMP 已经存在了一段时间,我的假设是您已经有了一些使用它的经验。让我们直接跳到包安装和我们的第一个 SNMP 示例。

安装程序

首先,让我们确保 SNMP 管理设备和代理在我们的设置中工作。SNMP 捆绑包可以安装在我们实验室的主机(客户端或服务器)或管理网络上的管理设备上。只要 SNMP 管理器对设备具有 IP 可访问性,并且受管设备允许入站连接,SNMP 就应该工作。在生产中,您应该只在管理主机上安装软件,并且只允许控制平面中的 SNMP 通信

在本实验室中,我们在管理网络上的 Ubuntu 主机和实验室中的客户端主机上安装了 SNMP,以测试安全性:

$ sudo apt-get install snmp

下一步是在网络设备iosv-1和iosv-2上打开并配置 SNMP 选项。您可以在网络设备上配置许多可选参数,例如联系人、位置、机箱 ID 和 SNMP 数据包大小。这些选项是特定于设备的,您应该查看设备上的文档。对于 IOSv 设备,我们将配置访问列表,以仅限制查询设备所需的主机,并将访问列表与 SNMP 社区字符串绑定。在本例中,我们将使用单词secret作为只读社区字符串,permit_snmp作为访问列表名称:

!
ip access-list standard permit_snmp
 permit 172.16.1.173 log
 deny any log
!
!
snmp-server community secret RO permit_snmp
!

SNMP 团体字符串充当管理器和代理之间的共享密码;因此,在您想要查询设备的任何时候都需要包含它。

正如本章前面提到的,在使用 SNMP 时,找到正确的 OID 通常是任务的一半。我们可以使用 Cisco IOS MIB 定位器(等工具 http://tools.cisco.com/ITDIT/MIBS/servlet/index 用于查找要查询的特定 OID。或者,我们可以从 Cisco 的企业树顶部的.1.3.6.1.4.1.9开始遍历 SNMP 树。我们将执行漫游以确保 SNMP 代理和访问列表正常工作:

$ snmpwalk -v2c -c secret 172.16.1.189 .1.3.6.1.4.1.9
iso.3.6.1.4.1.9.2.1.1.0 = STRING: "
Bootstrap program is IOSv
"
iso.3.6.1.4.1.9.2.1.2.0 = STRING: "reload" iso.3.6.1.4.1.9.2.1.3.0 = STRING: "iosv-1"
iso.3.6.1.4.1.9.2.1.4.0 = STRING: "virl.info"
...

我们还可以更具体地了解需要查询的 OID:

$ snmpwalk -v2c -c secret 172.16.1.189 .1.3.6.1.4.1.9.2.1.61.0
iso.3.6.1.4.1.9.2.1.61.0 = STRING: "cisco Systems, Inc.
170 West Tasman Dr.
San Jose, CA 95134-1706
U.S.A.
Ph +1-408-526-4000
Customer service 1-800-553-6387 or +1-408-526-7208
24HR Emergency 1-800-553-2447 or +1-408-526-7209
Email Address tac@cisco.com
World Wide Web http://www.cisco.com"

作为演示,如果我们在最后一个 OID 的末尾输入了从0到1的1数字的错误值,该怎么办?我们将看到:

$ snmpwalk -v2c -c secret 172.16.1.189 .1.3.6.1.4.1.9.2.1.61.1
iso.3.6.1.4.1.9.2.1.61.1 = No Such Instance currently exists at this OID

与 API 调用不同,没有有用的错误代码或消息;它只是说 OID 不存在。这有时会让人非常沮丧

最后要检查的是我们配置的访问列表,它将拒绝不需要的 SNMP 查询。由于访问列表中的许可和拒绝条目都有log关键字,所以只允许172.16.1.173查询设备:

*Mar 3 20:30:32.179: %SEC-6-IPACCESSLOGNP: list permit_snmp permitted 0 172.16.1.173 -> 0.0.0.0, 1 packet
*Mar 3 20:30:33.991: %SEC-6-IPACCESSLOGNP: list permit_snmp denied 0 172.16.1.187 -> 0.0.0.0, 1 packet

如您所见,设置 SNMP 的最大挑战是找到正确的 OID。一些 OID 在标准化 MIB-2 中定义;其他的在树的企业部分下。不过,供应商文档是最好的选择。有许多工具可以提供帮助,例如 MIB 浏览器;您可以将 MIB(同样由供应商提供)添加到浏览器中,并查看基于企业的 OID 的描述。Cisco 的 SNMP 对象导航器等工具(http://snmp.cloudapps.cisco.com/Support/SNMP/do/BrowseOID.do?local=en 当你需要找到你正在寻找的对象的正确 OID 时,被证明是非常有价值的。

PySNMP

PySNMP 是由 Ilya Etingof(开发的跨平台纯 Python SNMP 引擎实现 https://github.com/etingof )。它为您抽象了许多 SNMP 细节,就像伟大的库一样,并且支持 Python 2 和 Python 3。

PySNMP 需要 PyASN1 包。以下内容摘自维基百科:

"ASN.1 is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunication and computer networking."

PyASN1 方便地提供了围绕 ASN.1 的 Python 包装器。让我们先安装软件包:

cd /tmp
git clone https://github.com/etingof/pyasn1.git
cd pyasn1/
git checkout 0.2.3
sudo python3 setup.py install

接下来,安装 PySNMP 包:

git clone https://github.com/etingof/pysnmp
cd pysnmp/
git checkout v4.3.10
sudo python3 setup.py install

We are using an older version of PySNMP due to the fact that pysnmp.entity.rfc3413.oneliner was removed starting with version 5.0.0 (https://github.com/etingof/pysnmp/blob/a93241007b970c458a0233c16ae2ef82dc107290/CHANGES.txt). If you use pip to install the packages, the examples will likely break. 

让我们看看如何使用 PySNMP 来查询前面示例中使用的相同 Cisco 联系人信息。我们将采取的步骤是从的 PySNMP 示例中稍微修改的版本 http://pysnmp.sourceforge.net/faq/response-values-mib-resolution.html 。我们将首先导入必要的模块并创建一个CommandGenerator对象:

>>> from pysnmp.entity.rfc3413.oneliner import cmdgen
>>> cmdGen = cmdgen.CommandGenerator()
>>> cisco_contact_info_oid = "1.3.6.1.4.1.9.2.1.61.0"

我们可以使用getCmd方法执行 SNMP。结果被分解成各种变量;其中我们最关心的是varBinds,它包含查询结果:

>>> errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
...     cmdgen.CommunityData('secret'),
...     cmdgen.UdpTransportTarget(('172.16.1.189', 161)),
...     cisco_contact_info_oid
... )
>>> for name, val in varBinds:
...     print('%s = %s' % (name.prettyPrint(), str(val)))
...
SNMPv2-SMI::enterprises.9.2.1.61.0 = cisco Systems, Inc.
170 West Tasman Dr.
San Jose, CA 95134-1706
U.S.A.
Ph +1-408-526-4000
Customer service 1-800-553-6387 or +1-408-526-7208
24HR Emergency 1-800-553-2447 or +1-408-526-7209
Email Address tac@cisco.com
World Wide Web http://www.cisco.com
>>>

请注意,响应值是 PyASN1 对象。prettyPrint()方法将这些值中的一些转换为人类可读的格式,但由于我们案例中的结果未转换,因此我们将手动将其转换为字符串。

我们可以根据前面的交互示例编写脚本。我们将使用错误检查将其命名为pysnmp_1.py。我们还可以在getCmd()方法中包括多个 OID:

#!/usr/bin/env/python3

from pysnmp.entity.rfc3413.oneliner import cmdgen

cmdGen = cmdgen.CommandGenerator()

system_up_time_oid = "1.3.6.1.2.1.1.3.0"
cisco_contact_info_oid = "1.3.6.1.4.1.9.2.1.61.0"

errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
    cmdgen.CommunityData('secret'),
    cmdgen.UdpTransportTarget(('172.16.1.189', 161)),
    system_up_time_oid,
    cisco_contact_info_oid
)

# Check for errors and print out results
if errorIndication:
    print(errorIndication)
else:
    if errorStatus:
        print('%s at %s' % (
            errorStatus.prettyPrint(),
            errorIndex and varBinds[int(errorIndex)-1] or '?'
            )
        )
    else:
        for name, val in varBinds:
            print('%s = %s' % (name.prettyPrint(), str(val)))

结果将被解包并列出两个 OID 的值:

$ python3 pysnmp_1.py
SNMPv2-MIB::sysUpTime.0 = 660959
SNMPv2-SMI::enterprises.9.2.1.61.0 = cisco Systems, Inc.
170 West Tasman Dr.
San Jose, CA 95134-1706
U.S.A.
Ph +1-408-526-4000
Customer service 1-800-553-6387 or +1-408-526-7208
24HR Emergency 1-800-553-2447 or +1-408-526-7209
Email Address tac@cisco.com
World Wide Web http://www.cisco.com 

在下面的示例中,我们将持久化从查询中接收到的值,以便可以对数据执行其他功能,例如可视化。对于我们的示例,我们将在 MIB-2 树中使用ifEntry来绘制与接口相关的值。您可以找到许多绘制ifEntry树的资源;以下是我们之前为ifEntry访问的 Cisco SNMP 对象导航器站点的屏幕截图:

SNMP ifEntry OID tree

快速测试将说明设备上接口的 OID 映射:

$ snmpwalk -v2c -c secret 172.16.1.189 .1.3.6.1.2.1.2.2.1.2
iso.3.6.1.2.1.2.2.1.2.1 = STRING: "GigabitEthernet0/0"
iso.3.6.1.2.1.2.2.1.2.2 = STRING: "GigabitEthernet0/1"
iso.3.6.1.2.1.2.2.1.2.3 = STRING: "GigabitEthernet0/2"
iso.3.6.1.2.1.2.2.1.2.4 = STRING: "Null0"
iso.3.6.1.2.1.2.2.1.2.5 = STRING: "Loopback0"

从文档中,我们可以将ifInOctets(10)、ifInUcastPkts(11)、ifOutOctets(16)和ifOutUcastPkts(17)的值映射到各自的 OID 值中。通过快速查看 CLI 和 MIB 文档,我们可以看到,GigabitEthernet0/0数据包输出的值映射到 OID1.3.6.1.2.1.2.2.1.17.1。我们将遵循相同过程的其余部分,为接口统计数据绘制其余 OID。在 CLI 和 SNMP 之间进行检查时,请记住,这些值应接近,但不完全相同,因为在 CLI 输出时间和 SNMP 查询时间之间可能会有一些通信量:

# Command Line Output
iosv-1#sh int gig 0/0 | i packets
 5 minute input rate 0 bits/sec, 0 packets/sec
 5 minute output rate 0 bits/sec, 0 packets/sec
 38532 packets input, 3635282 bytes, 0 no buffer
 53965 packets output, 4723884 bytes, 0 underruns

# SNMP Output
$ snmpwalk -v2c -c secret 172.16.1.189 .1.3.6.1.2.1.2.2.1.17.1
iso.3.6.1.2.1.2.2.1.17.1 = Counter32: 54070

如果我们在生产环境中,我们可能会将结果写入数据库。但由于这只是一个示例,我们将把查询值写入一个平面文件。我们将编写用于信息查询的pysnmp_3.py脚本,并将结果写入文件。在脚本中,我们定义了需要查询的各种 OID:

  # Hostname OID
  system_name = '1.3.6.1.2.1.1.5.0'

  # Interface OID
  gig0_0_in_oct = '1.3.6.1.2.1.2.2.1.10.1'
  gig0_0_in_uPackets = '1.3.6.1.2.1.2.2.1.11.1'
  gig0_0_out_oct = '1.3.6.1.2.1.2.2.1.16.1'
  gig0_0_out_uPackets = '1.3.6.1.2.1.2.2.1.17.1'

这些值在snmp_query()函数中消耗,输入为host、community和oid:

  def snmp_query(host, community, oid):
      errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
      cmdgen.CommunityData(community),
      cmdgen.UdpTransportTarget((host, 161)),
      oid
      )

所有值都放在一个带有各种键的字典中,并写入一个名为results.txt的文件:

  result = {}
  result['Time'] = datetime.datetime.utcnow().isoformat()
  result['hostname'] = snmp_query(host, community, system_name)
  result['Gig0-0_In_Octet'] = snmp_query(host, community, gig0_0_in_oct)
  result['Gig0-0_In_uPackets'] = snmp_query(host, community, gig0_0_in_uPackets)
  result['Gig0-0_Out_Octet'] = snmp_query(host, community, gig0_0_out_oct)
  result['Gig0-0_Out_uPackets'] = snmp_query(host, community, gig0_0_out_uPackets)

  with open('/home/echou/Master_Python_Networking/Chapter7/results.txt', 'a') as f:
      f.write(str(result))
      f.write('n')

结果将是一个文件,其结果显示查询时表示的接口数据包:

# Sample output
$ cat results.txt
{'Gig0-0_In_Octet': '3990616', 'Gig0-0_Out_uPackets': '60077', 'Gig0-0_In_uPackets': '42229', 'Gig0-0_Out_Octet': '5228254', 'Time': '2017-03-06T02:34:02.146245', 'hostname': 'iosv-1.virl.info'}
{'Gig0-0_Out_uPackets': '60095', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5229721', 'Time': '2017-03-06T02:35:02.072340', 'Gig0-0_In_Octet': '3991754', 'Gig0-0_In_uPackets': '42242'}
{'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5231484', 'Gig0-0_In_Octet': '3993129', 'Time': '2017-03-06T02:36:02.753134', 'Gig0-0_In_uPackets': '42257', 'Gig0-0_Out_uPackets': '60116'}
{'Gig0-0_In_Octet': '3994504', 'Time': '2017-03-06T02:37:02.146894', 'Gig0-0_In_uPackets': '42272', 'Gig0-0_Out_uPackets': '60136', 'Gig0-0_Out_Octet': '5233187', 'hostname': 'iosv-1.virl.info'}
{'Gig0-0_In_uPackets': '42284', 'Time': '2017-03-06T02:38:01.915432', 'Gig0-0_In_Octet': '3995585', 'Gig0-0_Out_Octet': '5234656', 'Gig0-0_Out_uPackets': '60154', 'hostname': 'iosv-1.virl.info'}
...

我们可以使此脚本可执行,并计划每五分钟执行一次cron作业:

$ chmod +x pysnmp_3.py

# Crontab configuration
*/5 * * * * /home/echou/Master_Python_Networking/Chapter7/pysnmp_3.py

如前所述,在生产环境中,我们将把信息放在数据库中。对于 SQL 数据库,可以使用唯一 ID 作为主键。在 NoSQL 数据库中,我们可以使用时间作为主索引(或键),因为它总是唯一的,后面跟着各种键值对。

我们将等待脚本执行几次以填充值。如果你是不耐烦型,你可以将cron作业间隔缩短为一分钟。当您在results.txt文件中看到足够多的值以生成一个有趣的图形后,我们可以进入下一节,看看如何使用 Python 可视化数据。

用于数据可视化的 Python

我们收集网络数据是为了深入了解我们的网络。了解数据含义的最佳方法之一是用图形将其可视化。这几乎适用于所有数据,尤其适用于网络监控环境中的时间序列数据。上周通过网络传输了多少数据?TCP 协议在所有流量中所占的百分比是多少?这些是我们可以使用数据收集机制(如 SNMP)收集到的值,我们可以使用一些流行的 Python 库生成可视化图形。

在本节中,我们将使用上一节中使用 SNMP 收集的数据,并使用两个流行的 Python 库 Matplotlib 和 Pygal 对它们进行图形化。

Matplotlib

Matplotlibhttp://matplotlib.org/ 是 Python 语言及其 NumPy 数学扩展的 Python 2D 绘图库。它可以用几行代码生成出版物质量的数字,如绘图、直方图和条形图。

NumPy is an extension of the Python programming language. It is open source and widely used in various data science projects. You can learn more about it at https://en.wikipedia.org/wiki/NumPy.

安装

可以使用 Linux 软件包管理系统完成安装,具体取决于您的发行版:

$ sudo apt-get install python-matplotlib # for Python2
$ sudo apt-get install python3-matplotlib

Matplotlib–第一个示例

对于以下示例,默认情况下,输出图形显示为标准输出。在开发过程中,在使用脚本完成代码之前,首先尝试代码并在标准输出上生成图形通常比较容易。如果您一直通过虚拟机阅读本书,建议您使用 VM 窗口而不是 SSH,以便查看图表。如果您没有访问标准输出的权限,则可以保存图形并在下载后查看它(您很快就会看到)。请注意,您需要在我们将在本节中生成的一些图中设置$DISPLAY变量。

下面是本章可视化示例中使用的 Ubuntu 桌面的屏幕截图。终端窗口发出plt.show()命令后,Figure 1将出现在屏幕上。关闭图形后,将返回 Python shell:

Matplotlib visualization with the Ubuntu desktop 

让我们先看看折线图。直线图简单地给出了两个对应于x轴和y轴值的数字列表:

>>> import matplotlib.pyplot as plt
>>> plt.plot([0,1,2,3,4], [0,10,20,30,40])
[<matplotlib.lines.Line2D object at 0x7f932510df98>]
>>> plt.ylabel('Something on Y')
<matplotlib.text.Text object at 0x7f93251546a0>
>>> plt.xlabel('Something on X')
<matplotlib.text.Text object at 0x7f9325fdb9e8>
>>> plt.show()

该图将显示为线形图:

Matplotlib line graph

或者,如果您无法访问标准输出或首先保存了图形,则可以使用savefig()方法:

>>> plt.savefig('figure1.png')
or
>>> plt.savefig('figure1.pdf')

有了这些绘制图的基本知识,我们现在可以绘制从 SNMP 查询中收到的结果。

SNMP 结果的 Matplotlib

在我们的第一个 Matplotlib 示例中,即matplotlib_1.py,我们将导入pyplot之外的日期模块。我们将使用matplotlib.dates模块,而不是 Python 标准库dates模块。与 Pythondates模块不同,mapplotlib.dates库将在内部将日期值转换为浮点类型,这是 Matplotlib 所必需的:

  import matplotlib.pyplot as plt
  import matplotlib.dates as dates

Matplotlib provides sophisticated date plotting capabilities; you can find more information on this at http://matplotlib.org/api/dates_api.html.

在脚本中,我们将创建两个空列表,每个列表表示*x-轴和y-*轴的值。请注意,在第 12 行,我们使用内置的eval()Python 函数将输入读取为字典,而不是默认字符串:

   x_time = []
   y_value = []

   with open('results.txt', 'r') as f:
       for line in f.readlines():
           line = eval(line)
           x_time.append(dates.datestr2num(line['Time']))
           y_value.append(line['Gig0-0_Out_uPackets'])

为了以人类可读的日期格式读取*x-轴值,我们需要使用plot_date()函数而不是plot()。我们还将稍微调整图形的大小,并旋转x-*轴上的值,以便我们可以完整读取该值:

  plt.subplots_adjust(bottom=0.3)
  plt.xticks(rotation=80)

  plt.plot_date(x_time, y_value)
  plt.title('Router1 G0/0')
  plt.xlabel('Time in UTC')
  plt.ylabel('Output Unicast Packets')
  plt.savefig('matplotlib_1_result.png')
  plt.show()

最终结果将显示 Router1 Gig0/0 并输出单播数据包,如下所示:

Router1 Matplotlib graph

请注意,如果您喜欢直线而不是点,您可以在plot_date()函数中使用第三个可选参数:

     plt.plot_date(x_time, y_value, "-")

我们可以对输出八位字节、输入单播数据包和作为单个图形输入的其余值重复这些步骤。但是,在下面的示例中,即matplotlib_2.py,我们将向您展示如何在同一时间范围内绘制多个值,以及其他 Matplotlib 选项。

在这种情况下,我们将创建其他列表并相应地填充值:

   x_time = []
   out_octets = []
   out_packets = []
   in_octets = []
   in_packets = []

   with open('results.txt', 'r') as f:
       for line in f.readlines():
   ...
           out_packets.append(line['Gig0-0_Out_uPackets'])
           out_octets.append(line['Gig0-0_Out_Octet'])
           in_packets.append(line['Gig0-0_In_uPackets'])
           in_octets.append(line['Gig0-0_In_Octet'])

由于我们有相同的*x-轴值,我们可以将不同的y-*轴值添加到同一个图形中:

  # Use plot_date to display x-axis back in date format
  plt.plot_date(x_time, out_packets, '-', label='Out Packets')
  plt.plot_date(x_time, out_octets, '-', label='Out Octets')
  plt.plot_date(x_time, in_packets, '-', label='In Packets')
  plt.plot_date(x_time, in_octets, '-', label='In Octets')

另外,将网格和图例添加到图形中:

  plt.legend(loc='upper left')
  plt.grid(True)

最终结果将把所有值合并到一个图表中。请注意,左上角的一些值被图例阻止。您可以调整图形大小和/或使用“平移/缩放”选项在图形周围移动以查看值:

Router 1 – Matplotlib multiline graph

Matplotlib 中提供了更多的图形选项;我们当然不局限于绘制图形。例如,我们可以使用以下模拟数据来绘制我们可以在线路上看到的不同流量类型的百分比:

#!/usr/bin/env python3
# Example from http://matplotlib.org/2.0.0/examples/pie_and_polar_charts/pie_demo_features.html
import matplotlib.pyplot as plt

# Pie chart, where the slices will be ordered and plotted counter-clockwise:
labels = 'TCP', 'UDP', 'ICMP', 'Others'
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0) # Make UDP stand out

fig1, ax1 = plt.subplots()
ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
 shadow=True, startangle=90)
ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.

plt.show()

前面的代码从plt.show()引出此饼图:

Matplotlib pie chart

其他 Matplotlib 资源

Matplotlib 是最好的 Python 绘图库之一,能够生成出版物质量的图形。与 Python 一样,它的目标是简化复杂任务。GitHub 上有超过 7550 颗星星(正在计算),它也是最受欢迎的开源项目之一。它的流行直接转化为更快的 bug 修复、友好的用户社区和通用性。学习软件包需要一点时间,但这是非常值得的。

In this section, we barely scratched the surface of Matplotlib. You'll find additional resources at http://matplotlib.org/2.0.0/index.html (the Matplotlib project page) and https://github.com/matplotlib/matplotlib (the Matplotlib GitHub repository).

在下一节中,我们将了解另一个流行的 Python 图形库:Pygal。

皮格尔

Pygal(http://www.pygal.org/ 是一个用 Python 编写的动态 SVG 图表库。在我看来,Pygal 最大的优势在于它可以轻松地生成可缩放向量图形(SVG格式)图形,并且可以本地生成。与其他图形格式相比,SVG 有许多优点,但其中两个主要优点是它对 web 浏览器友好,并且在不牺牲图像质量的情况下提供可伸缩性。换句话说,您可以在任何现代 web 浏览器中显示生成的图像,并在不丢失图形细节的情况下放大和缩小图像。我提到过我们可以在几行 Python 代码中完成这项工作吗?那有多酷

安装

通过pip完成安装:

$ sudo pip install pygal #Python 2
$ sudo pip3 install pygal

Pygal——第一个例子

让我们看看 Pygal 文档中演示的折线图示例,可在上找到 http://pygal.org/en/stable/documentation/types/line.html :

>>> import pygal
>>> line_chart = pygal.Line()
>>> line_chart.title = 'Browser usage evolution (in %)'
>>> line_chart.x_labels = map(str, range(2002, 2013))
>>> line_chart.add('Firefox', [None, None, 0, 16.6, 25, 31, 36.4, 45.5, 46.3, 42.8, 37.1])
<pygal.graph.line.Line object at 0x7fa0bb009c50>
>>> line_chart.add('Chrome', [None, None, None, None, None, None, 0, 3.9, 10.8, 23.8, 35.3])
<pygal.graph.line.Line object at 0x7fa0bb009c50>
>>> line_chart.add('IE', [85.8, 84.6, 84.7, 74.5, 66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1])
<pygal.graph.line.Line object at 0x7fa0bb009c50>
>>> line_chart.add('Others', [14.2, 15.4, 15.3, 8.9, 9, 10.4, 8.9, 5.8, 6.7, 6.8, 7.5])
<pygal.graph.line.Line object at 0x7fa0bb009c50>
>>> line_chart.render_to_file('pygal_example_1.svg')

In this example, we created a line object with the x_labels automatically rendered as strings for 11 units. Each of the objects can be added with the label and the value in a list format, such as Firefox, Chrome, and IE.

以下是在 Firefox 中查看的结果图:

Pygal sample graph

现在我们可以看到 Pygal 的一般用法,我们可以使用相同的方法来绘制我们手头上的 SNMP 结果。我们将在下一节中这样做。

用于 SNMP 结果的 Pygal

对于 Pygal 线图,我们大体上可以遵循与 Matplotlib 示例相同的模式,在 Matplotlib 示例中,我们通过读取文件来创建值列表。我们不再需要像对待 Matplotlib 那样,将*x-*轴值转换为内部浮点;但是,我们确实需要转换我们在浮点中收到的每个值中的数字:

  #!/usr/bin/env python3

  import pygal

  x_time = []
  out_octets = []
  out_packets = []
  in_octets = []
  in_packets = []

  with open('results.txt', 'r') as f:
      for line in f.readlines():
          line = eval(line)
          x_time.append(line['Time'])
          out_packets.append(float(line['Gig0-0_Out_uPackets']))
          out_octets.append(float(line['Gig0-0_Out_Octet']))
          in_packets.append(float(line['Gig0-0_In_uPackets']))
          in_octets.append(float(line['Gig0-0_In_Octet']))

我们可以使用我们所看到的构造线图的相同机制:

  line_chart = pygal.Line()
  line_chart.title = "Router 1 Gig0/0"
  line_chart.x_labels = x_time
  line_chart.add('out_octets', out_octets)
  line_chart.add('out_packets', out_packets)
  line_chart.add('in_octets', in_octets)
  line_chart.add('in_packets', in_packets)
  line_chart.render_to_file('pygal_example_2.svg')

结果与我们已经看到的类似,但图形现在是 SVG 格式,可以轻松地显示在网页上。可以从现代 web 浏览器中查看:

Router 1—Pygal multiline graph

与 Matplotlib 一样,Pygal 为图形提供了更多选项。例如,要绘制之前在 Pygal 中看到的饼图,我们可以使用pygal.Pie()对象:

#!/usr/bin/env python3

import pygal

line_chart = pygal.Pie()
line_chart.title = "Protocol Breakdown"
line_chart.add('TCP', 15)
line_chart.add('UDP', 30)
line_chart.add('ICMP', 45)
line_chart.add('Others', 10)
line_chart.render_to_file('pygal_example_3.svg')

生成的 SVG 文件类似于 Matplotlib 生成的 PNG:

Pygal pie chart

额外 Pygal 资源

Pygal 为从基本网络监控工具(如 SNMP)收集的数据提供了更多可定制的功能和图形功能。在本节中,我们演示了一个简单的线图和饼图。您可以在此处找到有关该项目的更多信息:

在下一节中,我们将继续介绍网络监控的 SNMP 主题,但将介绍一个功能齐全的网络监控系统,名为Cacti。

仙人掌巨蟒

我在一家地区 ISP 担任初级网络工程师的早期,我们使用了开源跨平台多路由器流量记录仪(MRTG)()https://en.wikipedia.org/wiki/Multi_Router_Traffic_Grapher 用于检查网络链路上的流量负载的工具。我们几乎完全依靠该工具进行交通监控。我真的很惊讶一个开源项目是多么的好和有用。它是第一个开源高级网络监控系统之一,为网络工程师抽象了 SNMP、数据库和 HTML 的细节。然后是循环数据库工具(RRDtool)(https://en.wikipedia.org/wiki/RRDtool 。在 1999 年的第一个版本中,它被称为“MRTGDoneRight”。它极大地提高了后端数据库和轮询器的性能。

2001 年发布,仙人掌(https://en.wikipedia.org/wiki/Cacti_(软件)是一款开源的基于 web 的网络监控和绘图工具,旨在改进 RRDtool 的前端。由于 MRTG 和 RRDtool 的传统,您会注意到熟悉的图形布局、模板和 SNMP 轮询器。作为一个打包工具,安装和使用将需要停留在工具本身的边界内。但是,Cacti 提供了自定义数据查询功能,我们可以使用 Python 来实现这一功能。在本节中,我们将了解如何使用 Python 作为 Cacti 的输入方法。

安装

在 Ubuntu 管理 VM 上使用 APT 时,Ubuntu 上的安装非常简单:

$ sudo apt-get install cacti

它将触发一系列安装和设置步骤,包括 MySQL 数据库、web 服务器(Apache 或 lighttpd)和各种配置任务。安装完成后,导航至http://<ip>/cacti开始。最后一步是使用默认用户名和密码(admin/admin登录);系统将提示您更改密码。

登录后,您可以按照文档添加设备并将其与模板关联。有一个思科路由器预制模板,你可以去。仙人掌在上有很好的文档 http://docs.cacti.net/ 用于添加设备和创建第一个图形,因此我们将快速查看一些您希望看到的屏幕截图:

当您可以看到设备正常运行时间时,会出现指示 SNMP 通信正在工作的标志:

您可以向设备添加用于接口流量和其他统计信息的图形:

一段时间后,您将开始看到流量,如下所示:

现在,我们准备看看如何使用 Python 脚本来扩展 Cacti 的数据收集功能。

Python 脚本作为输入源

在尝试使用 Python 脚本作为输入源之前,我们应该阅读两个文档:

有人可能想知道使用 Python 脚本作为数据输入扩展的用例是什么。其中一个用例是为没有相应 OID 的资源提供监控,例如,如果我们想知道如何绘制访问列表permit_snmp允许主机172.16.1.173执行 SNMP 查询的次数。我们知道可以通过 CLI 查看匹配的数量:

iosv-1#sh ip access-lists permit_snmp | i 172.16.1.173
 10 permit 172.16.1.173 log (6362 matches)

但是,很可能没有与此值关联的 OID(或者我们可以假装没有)。在这里,我们可以使用外部脚本生成 Cacti 主机可以使用的输出。

我们可以重用我们在第 2 章、底层网络设备交互、chapter1_1.py中讨论过的 Pexpect 脚本。我们将其重命名为cacti_1.py。除了我们将执行 CLI 命令并保存输出之外,所有内容都应该熟悉原始脚本:

for device in devices.keys():
...
    child.sendline('sh ip access-lists permit_snmp | i 172.16.1.173')
    child.expect(device_prompt)
    output = child.before
...

原始形式的输出将如下所示:

b'sh ip access-lists permit_snmp | i 172.16.1.173rn 10 permit 172.16.1.173 log (6428 matches)rn'

我们将对字符串使用split()函数,只留下匹配数,并在脚本的标准输出上打印出来:

print(str(output).split('(')[1].split()[0])

为了测试这一点,我们可以通过多次执行脚本来查看增量的数量:

$ ./cacti_1.py
6428
$ ./cacti_1.py
6560
$ ./cacti_1.py
6758

我们可以使脚本可执行,并将其放入默认的 Cacti 脚本位置:

$ chmod a+x cacti_1.py
$ sudo cp cacti_1.py /usr/share/cacti/site/scripts/

仙人掌文档,可在获取 http://www.cacti.net/downloads/docs/html/how_to.html 提供了如何将脚本结果添加到输出图的详细步骤。这些步骤包括将脚本添加为数据输入法,将输入法添加到数据源,然后创建要查看的图形:

SNMP 是向设备提供网络监控服务的常用方式。以 Cacti 为前端的 RRDtool 提供了一个良好的平台,可通过 SNMP 用于所有网络设备。

总结

在本章中,我们探讨了通过 SNMP 执行网络监控的方法。我们在网络设备上配置了与 SNMP 相关的命令,并使用带有 SNMP 轮询器的网络管理虚拟机来查询设备。我们使用 PySNMP 模块简化并自动化 SNMP 查询。我们还学习了如何将查询结果保存在平面文件或数据库中,以供将来的示例使用。

在本章后面,我们使用两个不同的 Python 可视化包,即 Matplotlib 和 Pygal,来绘制 SNMP 结果。每个包都有其独特的优点。Matplotlib 是一个成熟、功能丰富的库,广泛应用于数据科学项目中。Pygal 可以生成灵活且对 web 友好的 SVG 格式的图形。我们了解了如何生成与网络监控相关的线图和饼图。

在本章末尾,我们介绍了一个名为 Cacti 的全包式网络监控工具。它主要使用 SNMP 进行网络监控,但我们看到了当远程主机上没有 SNMP OID 时,如何使用 Python 脚本作为输入源来扩展平台的监控功能。

在第 8 章、Python 网络监控–第 2 部分中,我们将继续讨论我们可以用来监控网络的工具,并深入了解网络是否按预期运行。我们将研究使用 NetFlow、sFlow 和 IPFIX 的基于流的监控。我们还将使用 Graphviz 等工具来可视化我们的网络拓扑并检测任何拓扑变化。最后,我们将使用 Elasticsearch、Logstash 和 Kibana(通常称为 ELK 堆栈)来监视网络日志数据以及其他与网络相关的输入。