第2章
低级网络设备交互
在第1章中,我们研究了网络通信协议背后的理论和规范,粗略介绍了Python语言。在本章中,我们将开始深入探讨如何使用Python管理网络设备,尤其是使用Python与传统网络路由器和交换机进行程序化通信的不同方式。
传统网络路由器和交换机是指什么?虽然如今出现的所有的网络设备几乎都配有用于程序化通信的应用程序编程接口(API),但众所周知的事实是前几年部署的许多网络设备都不包含API。这些设备的预期管理方法是使用终端程序的命令行界面(CLI),这些程序最初是为人类工程师开发的。管理层依赖工程师对设备返回的数据进行解释,以采取适当的措施。但随着网络设备数量和网络复杂性的增加,逐个手动管理它们变得越来越困难。
Python有两个很好的库可以辅助完成这些任务—Pexpect库和Paramiko库以及从它们派生的其他库。本章将首先介绍Pexpect库,然后介绍Paramiko示例。一旦了解了Paramiko的基础知识,就可以比较轻松地掌握Netmiko等扩展库。值得一提的是,Ansible(将在第4章和第5章中介绍)在很大程度上依赖于Paramiko的网络模块。在本章中,我们按以下主题展开:
CLI面临的挑战。
搭建虚拟实验。
Python Pexpect库。
Python Paramiko库。
Pexpect和Paramiko的缺点。
让我们开始吧!
2.1 CLI面临的挑战
在2014年拉斯维加斯的Interop展会上,BigSwitch Networks的首席执行官Douglas Murray展示了以下幻灯片,内容是1993—2013的20年间数据中心网络(DCN)的变化:
他的意思很明显:我们管理网络设备的方式在这20年中没有太大变化。虽然他在展示这张幻灯片时可能对现有供应商有些负面偏见,但他的观点是有价值的。在他看来,20年来管理路由器和交换机的唯一变化是协议从安全性较低的Telnet转变为更安全的SSH。
大概在2014年的同一时间,我们开始看到业界达成了一个共识,需要从手动的、人为驱动的CLI转向以计算机为中心的自动化API。毫无疑问,在进行网络设计、概念的初步验证和首次部署拓扑时,我们仍然需要直接与设备进行通信。但是,一旦我们经过了初始部署,就需要对全部部署进行相同的更改,以保证它们的正确性,并在工程师没有分心或感到疲倦的情况下反复重复这些更改。这听起来像是计算机和我们最喜欢的语言Python的理想工作。
根据幻灯片中的信息,主要挑战是路由器和管理员之间的交互。路由器将发送一系列信息,并期望管理员根据工程师对输出的解释输入一系列手动命令。例如,你必须输入enable才能进入特权模式,并在收到带#符号的返回提示后,输入configure terminal以进入配置模式。相同的过程可以进一步扩展到接口配置模式和路由协议配置模式。这与计算机驱动的程序化思维形成鲜明对比。当计算机想要完成一项任务(例如,在接口上放置一个IP地址)时,它期望一次性地将所有信息提供给路由器,并且期望路由器回复单一信号yes或no以指示任务成功与否。
由Pexpect和Paramiko实施的解决方案是将交互式流程视为子流程,并监视流程与目标设备之间的交互。父进程根据返回的值决定后续操作(如果有)。
2.2 搭建虚拟实验
在深入研究库之前,让我们来看看为便于学习而组建实验环境的方案。正如老话所说,熟能生巧:我们需要一个孤立的沙箱来安全地试错,尝试新的操作方式,并重复一些步骤来强化第一次尝试中不明确的概念。安装Python和管理主机所需的软件包很容易,但我们想要模拟的那些路由器和交换机呢?
为了搭建网络实验,我们主要有两种方案,每种方案都有其优点和缺点:
物理设备:此方案由可以查看和触摸的物理设备组成。如果足够幸运,可以搭建一个完全复制生产环境的实验:
优点:这是一个从实验到生产的简单过渡,更容易被管理人员以及操作设备的工程师理解。简而言之,由于较为熟悉,所以物理设备的满意度较高。
缺点:用于实验设备的费用相对较高。设备需要工时来搭建,并且一旦构造好了就不能灵活改变。
虚拟设备:虚拟设备是实际网络设备的仿真,由供应商或开源社区提供:
优点:虚拟设备更易于设置、相对便宜,并且可以快速更改拓扑。
缺点:它们通常是物理对应物的缩小版。有时虚拟设备和物理设备之间存在功能差距。
当然,个人可根据成本因素、是否易于实施及实验与生产之间存在差距的风险来决定使用虚拟实验还是物理实验。在我所研究的一些环境中,虚拟实验在进行初始概念验证时使用,而当我们接近最终设计时再使用物理实验。
在我看来,随着越来越多的供应商生产虚拟设备,虚拟实验也成为提供学习环境的一种方式。虚拟设备的功能差距相对较小且有具体记录,尤其是在供应商还提供虚拟实例时。与购买物理设备相比,虚拟设备的成本相对较低,因为它们通常只是软件程序,所以使用虚拟设备搭建实验用的时间更少。
本书将使用物理和虚拟设备的组合来进行概念演示,但更多使用的是虚拟设备。对于我们将看到的示例,使用哪种设备的差异应该是透明的。如果与我们的目标有关的虚拟和物理设备之间存在任何已知差异,本书中都会列出它们。
在虚拟实验方面,除了来自不同供应商的图像之外,我还使用了Cisco的一个称为虚拟互联网路由实验(VIRL)的程序,位于https://learningnetworkstore.cisco.com/virtual-internet-routing-lab-virl/cisco-personal-edition-pe-20-nodes-virl-20 。
警告:对于读者来说,并不是必须使用这个程序。但强烈建议读者使用一些实验设备来实践本书中的示例。
2.2.1 Cisco VIRL
我第一次开始学习Cisco VIRL时,是为了Cisco认证互联网专家(CCIE)实验考试。我从eBay购买了一些二手的Cisco设备。即使打折,每个路由器和交换机也要花费数百美元,所以为了省钱,我购买了一些过时的20世纪80年代的Cisco路由器(在你最喜欢的搜索引擎中搜索Cisco AGS路由器就能看到它),即使按实验标准,这款路由器也明显达不到需要的功能和功率。启动时发出的声音非常响,这件事很多次成为我们的笑料。连接物理设备并不好玩,它们非常沉而且很笨重,连接所有电缆也很痛苦,并且很容易引起链路故障,每次有故障时又需要拔掉电缆。
几年后,Dynamip出现了,创建不同的网络场景变得非常容易。只需要来自Cisco的IOS镜像和一些精心构建的拓扑文件,就可以轻松构建一个可以检验所学知识的虚拟网络。如场景所要求的那样,有一个完整的网络拓扑文件夹、预先保存的配置和不同版本的图像。GNS3前端的添加为整个设置提供了漂亮的GUI。使用GNS3,只需点击就可以删除链接和设备,你甚至可以直接从GNS3设计面板中打印出管理的网络拓扑。该工具唯一不好的地方就是没有被供应商正式接受,并且因为这个原因被认为缺乏可信度。
2015年,Cisco社区决定通过发布Cisco VIRL来满足这一需求。如果服务器满足要求并且你愿意支付所需的年度许可证费用,那么无论对于本书还是生产用途,这是开发和尝试大部分Python代码的首选方法。
警告:自2017年1月1日起,只有个人版20节点许可证可以购买,每年199.99美元。
虽然有一定的成本,但在我看来VIRL平台提供了一些超过其他选择的优势:
便于使用:IOSv、IOS-XRv、CSR100v、NX-OSv和ASAv的所有映像都包含在一次下载中。
官方:虽然由社区提供支持,但它是Cisco内部广泛使用的工具。鉴于其受欢迎程度,错误能得到快速修复,新功能会被仔细记录,并且有用的知识在其用户之间广泛共享。
云迁移路径:当仿真功能超出硬件功能时,该项目提供了一个逻辑迁移路径,例如Cisco dCloud(https://dcloud.cisco.com/ )、包上的VIRL(http://virl.cisco.com/cloud/ )和Cisco DevNet(https://developer.cisco.com/ )。这是一个有时被忽视的重要特点。
链路和控制平面模拟:该工具可以基于每个链路模拟实际链路特性的延迟、波数据包丢失。还有一个用于外部路由注入的控制平面流量发生器。
其他:该工具提供了一些不错的功能,例如VM Maestro拓扑设计和模拟控制、用于生成自动配置的AutoNetkit以及用户工作区管理(如果服务器是共享的)。还有开源项目,如virlutils(https://github.com/CiscoDevNet/virlutils ),这个项目的社区积极致力于提高工具的可用性。
在本书中没有使用VIRL中的所有功能。但由于这是一个值得考虑的相对较新的工具,如果你想要使用该工具,我可以提供一些我使用的设置。
警告:同样,我想强调实验的重要性,但并不一定是Cisco VIRL实验。本书中提供的代码示例应该适用于任何实验设备,只要它们运行相同的软件类型和版本即可。
VIRL的建议
VIRL网站(http://virl.cisco.com/ )提供了大量的指南、准备和文档。我还发现VIRL用户社区通常提供快速准确的帮助。我不会重复这两个地方已经提供的信息,但是,这里有一些我在本书中用于实验的设置:
VIRL使用两个虚拟以太网接口进行连接。第一个接口设置为主机连接的NAT,第二个接口用于本地管理接口连接(以下示例中为VMnet2)。我使用具有类似网络设置的单独虚拟机来运行Python代码,第一个主以太网用于互联网连接,第二个以太网连接到VMnet2用于实验设备管理网络:
VMnet2是为了将Ubuntu主机与VIRL虚拟机连接而创建的自定义网络:
在Topology Design选项中,我将Management Network选项设置为Shared flat network,以便将VMnet2用作虚拟路由器上的管理网络:
在节点配置下,可以选择静态配置管理IP。我用的是静态设置管理IP地址,而不是让它们由软件动态分配。这会有更确定的可访问性:
2.2.2 Cisco DevNet和Cisco dCloud
在撰写本书时,Cisco免费提供了另外两个非常好的网络自动化的工具。这两个工具都需要Cisco Connection Online(CCO)登录。
第一个工具是Cisco DevNet(https://developer.cisco.com/ )沙箱,其中包括指导学习的教程、完整文档和沙箱远程实验等。有些实验室一直处于打开状态,而其他实验室则需要预定。实验室的可用性取决于使用情况。如果你还没有自己的实验室,这是一个很好的选择。根据我使用DevNet的经验,一些文档和链接已经过时,但可以轻松检索它们以获得最新版本。在软件开发等快速变化的领域,这是可以预料到的。无论是否拥有本地运行的VIRL主机,DevNet无疑是应该充分利用的工具:
Cisco的另一个在线实验是https://dcloud.cisco.com/ 。你可以将dCloud视为在其他人的服务器上运行VIRL,而无须管理或支付这些资源。似乎Cisco将dCloud视为独立产品以及VIRL的扩展。例如,当遇到无法在本地运行多个IOX-XR或NX-OS实例的情况时,可以使用dCloud扩展本地实验。这是一个相对较新的工具,但绝对值得一看:
2.2.3 GNS3
在本书中我还会使用其他一些虚拟实验工具。GNS3工具就是其中之一:
正如本章前面提到的,很多人使用CNS3来认证测试和进行实践。这个工具已经从早期的Dynamips前端发展成为一个可行的商业产品。Cisco制造的工具,如VIRL、DevNet和dCloud,只包含Cisco技术。尽管它们为虚拟实验设备提供了与外部世界通信的方式,但它们并不像直接在模拟环境中使用多供应商虚拟化设备那么容易。但GNS3与供应商无关,可以直接在实验中包含多供应商虚拟化平台。这通常通过制作图像的克隆(例如Arista vEOS)或通过其他管理程序(例如Juniper Olive仿真)直接启动网络设备映像来完成。有些人可能认为GNS3没有Cisco VIRL项目的广度和深度,但因为它们可以运行不同的Cisco技术,所以我经常在需要将其他供应商技术融入实验时使用它。
另一个获得了大量好评的多供应商网络仿真环境是下一代仿真虚拟环境(EVE-NG),位于http://www.eve-ng.net/ 。
还有其他虚拟化平台,如Arista vEOS(https://eos.arista.com/tag/veos/ )、Juniper vMX(http://www.juniper.net/us/en/products-services/routing/mx-series/vmx/ )和vSRX(http://www.juniper.net/us/en/products-services/security /srx-series/vsrx/ ),你可以在测试期间将其用作独立的虚拟设备。它们是用于测试特定于平台的功能(例如平台上API版本之间的差异)的强大补充工具。其中许多是在公共云提供商市场上作为付费产品提供的。为了便于访问,它们通常具有与其物理对应物相同的特征。
2.3 Python Pexpect库
Pexpect是一个纯Python模块,用于生成子应用程序,控制它们并响应其输出中的预期模式。Pexpect就像Don Libe的Expect一样。Pexpect允许你的脚本生成子应用程序并控制它,就像人类正在键入命令一样。Pexpect文档位于https://pexpect.readthedocs.io/en/stable/index.html 。
现在我们来看看Python Pexpect库。与Don Libe的原始Tcl Expect模块类似,Pexpect启动或生成另一个流程并监视它以控制交互。Expect工具最初是为了自动化FTP、Telnet和rlogin等交互式流程而开发的,后来扩展到网络自动化。与最初的Expect不同,Pexpect完全用Python编写,不需要编译TCL或C扩展。这允许我们在代码中使用熟悉的Python语法及其丰富的标准库。
2.3.1 Pexpect安装
由于这是我们安装的第一个软件包,我们将安装pip工具和pexpect软件包。这个过程非常简单:
警告:我使用pip3安装Python 3软件包,同时使用pip在Python 2环境中安装软件包。
快速测试以确保包可用:
2.3.2 Pexpect概述
对于第一个实验,我们将构建一个简单的网络,其中两个IOSv设备直接连接:
每个设备都具有192.16.0.x/24范围内的环回地址,管理IP将在172.16.1.x/24范围内。VIRL拓扑文件包含在可下载文件中。你可以将拓扑导入自己的VIRL软件中。如果你没有VIRL,还可以通过使用文本编辑器打开拓扑文件来查看必要的信息。该文件只是一个XML文件,node元素下包含每个节点的信息:
准备好设备后,让我们来看看如果要远程登录到设备中如何与路由器进行交互:
我使用VIRL AutoNetkit自动生成路由器的初始配置,生成默认用户名cisco和密码cisco。请注意,由于配置中分配了特权,用户已处于特权模式:
auto-config还为Telnet和SSH生成了vty访问:
让我们看一下使用Python交互式shell的Pexpect示例:
警告:从Pexpect 4.0版本开始,你可以在Windows平台上运行Pexpect。但是,正如Pexpect文档中所述,在Windows上运行Pexpect应该被认为是实验性的。
在之前的交互式示例中,Pexpect生成了一个子进程,并以交互方式监视它。示例中展示了两个重要的方法expect()和sendline()。expect()行指示Pexpect进程查找的字符串,这些字符串被视为“已完成”的指示符。这是预期的模式。在示例中,我们知道路由器在返回主机名提示符(iosv-1#)时向我们发送了所有信息。sendline()方法指示应将哪些单词作为命令发送到远程设备。还有一个名为send()的方法,但不同的是sendline()包含换行符,类似于在前一个telnet会话中发送的单词末尾按Enter键。从路由器的角度来看,就好像有人从终端输入了文本。换句话说,当路由器实际上与计算机通信时,我们“欺骗”路由器让它们以为正在与人类连接。
before和after属性将设置为子应用程序打印的文本。before属性将设置为子应用程序打印的文本,直到达到预期的模式。after字符串将包含与预期模式匹配的文本。在我们的例子中,before文本将被设置为两个预期匹配(iosv-1#)之间的输出,包括show version命令。after文本是路由器主机名提示符:
当出现错误时会发生什么?例如,如果在生成子应用程序后键入username而不是Username,则Pexpect进程将从子进程中查找username字符串。在这种情况下,Pexpect进程只会挂起,因为路由器永远不会返回username单词。会话最终会超时,或者你通过Ctrl + C手动退出。
expect()方法需等待子应用程序返回给定的字符串,因此在前面的示例中,如果要同时容纳小写和大写u,则可以使用以下命令:
方括号用作or操作,告诉子应用程序期望小写或大写u后跟sername作为字符串。我们告诉流程的是将接受Username或username作为预期字符串。
警告:有关Python正则表达式的更多信息,请参考:
https: //docs.python.org/3.5/library/re.html 。
expect()方法还可以包含选项列表,而不是仅包含单个字符串,这些选项还可以是正则表达式。回到上一个示例,你可以使用以下选项列表来容纳两个不同的字符串:
一般来说,当要在正则表达式中使用不同的主机名时,将正则表达式用于单个expect字符串,而如果需要从路由器捕获完全不同的响应(例如密码拒绝),请加入相应的选项。例如,如果登录时使用多个不同的密码,则需要捕获% Login invalid以及设备提示。
Pexpect正则表达式和Python正则表达式之间的一个重要区别是:Pexpect是非贪婪匹配模式,这意味着在使用特殊字符时将尽可能少匹配。因为Pexpect在一个流上实施正则表达式匹配时,生成这个流的子进程可能还没有结束,所以在流中很难预测。这意味着通常与行尾匹配的特殊美元符号$是无效的,因为字符.+将始终不返回任何字符,而字符.*模式又是尽可能少匹配。请记住,一般情况下expect匹配字符串越具体越好。
我们考虑以下场景:
这里不太对劲。将它与之前的终端输出进行比较,你期望的输出将是hostname iosv-1:
仔细看看预期的字符串就可发现错误。在这种情况下,我们漏掉了iosv-1主机名后面的哈希(#)符号。因此,子应用程序将返回字符串的第二部分视为预期的字符串:
在几个例子之后,你可以看到使用Pexpect时出现的模式。用户映射Pexpect进程和子应用程序之间的交互顺序。通过一些Python变量和循环,我们可以开始构建一个有用的程序,帮助我们收集信息并对网络设备进行更改。
2.3.3 第一个Pexpect程序
第一个程序chapter2_1.py通过一些额外的代码扩展了我们在上一节中所做的工作:
在第5行使用嵌套字典:
嵌套字典允许我们使用适当的IP地址和提示符号来引用同一设备(例如iosv-1)。然后我们可以在循环中将这些值用于expect()方法。
将show version | i V输出打印到每个设备的屏幕上:
2.3.4 Pexpect的更多功能
在本小节中,我们将介绍更多Pexpect功能,这些功能在某些情况下可能会派上用场。
如果你的远程设备有慢速或快速链接,则expect()方法默认的超时为30秒,可以通过设置timeout参数增加或减少超时时间:
你可以选择使用interact()方法将命令传回用户。当只想自动执行初始任务的某些部分时,这非常有用:
可以以字符串的格式打印有关child.spawn对象的大量信息:
Pexpect最有用的调试工具是将输出记录在一个文件中:
警告:对Python 2使用child.logfile = open('debug','w')。默认情况下,Python 3使用字节字符串。有关Pexpect功能的更多信息。
2.3.5 Pexpect和SSH
如果你尝试使用以前的Telnet示例并将其插入SSH会话中,可能会发现自己对这种体验感到非常沮丧。你总是必须在会话中包含用户名,回答ssh新键问题等。幸运的是,有许多方法可以进行SSH会话工作,Pexpect有一个名为pxssh的子类,它专门用于建立SSH连接。该类添加了登录、注销和各种复杂的方法来处理ssh登录过程中的不同情况。除了login()和logout()之外,其余方法大致相同:
请注意login()方法中的auto_prompt_reset = False参数。默认情况下,pxssh使用Shell提示符同步输出。但由于它在大多数bash或CSH中使用PS1选项,因此在Cisco或其他网络设备上会出错。
2.3.6 将Pexpect的所有内容都放到脚本中
最后一步,我们将迄今为止学到的关于Pexpect的所有内容都放到脚本中。将代码放入脚本可以更轻松地在开发环境中使用,也可以更轻松地与同事共享。我们将编写第二个脚本chapter2_2.py。
警告:你可以从GitHub仓库https://github.com/PacktPublishing/Mastering-Python-Networking- second-edition 下载该脚本,并查看命令行输出。
请参阅以下代码:
该脚本进一步扩展了第一个Pexpect程序,具有以下附加功能:
使用SSH代替Telnet。
通过将命令放入列表(第8行)并循环执行命令(从第20行开始)来达到支持多个命令而不是仅支持一个命令的目的。
提示用户输入用户名和密码,而不是在脚本中对其进行硬编码。
将输出写入两个文件iosv-1_output.txt和ios-2_output.txt中进行进一步分析。
提示:对于Python 2,使用raw_input()而不是input()作为用户名提示。另外,使用w而不是wb作为文件模式。
2.4 Python Paramiko库
Paramiko是SSHv2协议的Python实现。就像Pexpect的pxssh子类一样,Paramiko简化了主机和远程设备之间的SSHv2交互。与pxssh不同,Paramiko只关注没有Telnet支持的SSHv2。它还提供客户端和服务器操作。
Paramiko是其网络模块的高级自动化框架Ansible背后的低级SSH客户端。我们将在后面的章节中介绍Ansible。下面我们来看看Paramiko库。
2.4.1 Paramiko安装
使用Python pip安装Paramiko非常简单。但是,密码库存在严重依赖性。该库为SSH协议提供了基于C的低级加密算法。
有关Windows、Mac和其他Linux版本的安装说明,请访问:
https://cryptography.io/en/latest/installation/ 。
我们将在以下输出中说明Ubuntu 16.04虚拟机中的Paramiko安装。以下输出说明了安装步骤以及在Python交互式环境中导入Paramiko的步骤。
如果使用的是Python 2,请按照以下步骤操作。我们将尝试在交互式提示中导入库以确保可以使用库:
如果使用的是Python 3,请参阅以下命令行安装依赖项。安装后,我们将导入库以确保它已正确安装:
2.4.2 Paramiko概述
让我们看一下使用Python 3交互式shell的Paramiko例子:
提示:time.sleep()函数插入时间延迟以确保捕获所有输出。这在较慢的网络连接或繁忙的设备上特别有用。此命令不是必需的,建议根据具体情况而定。
即使你是第一次看到Paramiko操作,Python美妙清晰的语法也可以使你直观地推断出程序的功能:
前4行创建了Paramiko的SSHClient类的实例。下一行设置用SSH服务器的主机名连接时客户端应使用的策略,在这种情况下,iosv-1不出现在系统主机密钥或应用程序密钥中。在我们的场景中,会自动将密钥添加到应用程序的HostKeys对象。此时,如果你登录路由器,将看到Paramiko的其他登录会话:
接下来的几行从连接中调用一个新的交互式shell,以及一个发送命令和检索输出的可重复模式。最后,我们关闭连接。
一些之前使用过Paramiko的读者可能会使用exec_command()方法而不是调用shell。为什么我们需要调用交互式shell而不是直接使用exec_command()呢?因为不幸的是,Cisco IOS上的exec_command()只允许一个命令。考虑以下带有exec_command()的连接示例:
一切都很好,但是,如果查看Cisco设备上的会话数,你会注意到Cisco设备在没有关闭连接的情况下断开连接:
由于SSH会话不再处于活动状态,因此如果要向远程设备发送更多命令,exec_command()将返回错误:
提示:Kirk Byers的Netmiko库是一个开源Python库,简化了对网络设备的SSH管理。
如果你没有清除接收的缓冲区会发生什么?输出将继续填充缓冲区并覆盖它:
为了确定输出的一致性,我们将在每次执行命令时从缓冲区中检索输出。
2.4.3 第一个Paramiko程序
第一个程序将使用与之前整理的Pexpect程序相同的通用结构。我们将使用Paramiko而不是Pexpect循环遍历设备和命令列表。这里可以很好地将Paramiko和Pexpect进行对比。
如果你还没有这样做,可以从本书的GitHub仓库下载代码chapter2_3.py,位于https://github.com/PacktPublishing/Mastering-Python- Networking-second-edition。我将列出这里的明显差异:
我们不再需要使用Paramiko来匹配设备提示,因此,设备字典可以简化:
Paramiko没有与sendline相似的命令,相反,在每个命令中需要手动输入newline:
因为不需要这些命令的输出,我们提供了一种新方法来清除缓冲区以发送命令(例如terminal length 0或enable)。我们只想清除缓冲区并进入执行提示。稍后将在循环中使用此函数,例如脚本的第25行:
程序的其余部分应该是非常清晰的,类似于我们在本章中已看到的。我要指出的最后一件事是,由于这是一个交互式程序,需放置一些缓冲区并等待命令在远程设备上完成,然后再检索输出:
清除缓冲区后,在执行命令之间的时间内,我们将等待两秒。如果设备繁忙,这将为设备提供足够的响应时间。
2.4.4 Paramiko的更多功能
讨论Ansible时,一般会同时讨论Paramiko,因为Paramiko是许多网络模块的基础传输。在本节中,我们将介绍Paramiko的一些其他功能。
Paramiko服务器
Paramiko也可通过SSHv2管理服务器。我们看一下如何使用Paramiko来管理服务器。我们将对SSHv2会话使用基于密钥的身份验证。
提示:在此示例中,我在与目标服务器相同的虚拟机管理程序上使用了另一个Ubuntu虚拟机。你还可以使用VIRL模拟器上的服务器或其中一个公共云提供程序(例如Amazon AWS EC2)中的实例。
我们将为Paramiko主机生成公钥–私钥对:
默认情况下,此命令将生成名为id_rsa.pub的公钥以及名为id_rsa的私钥放在用户主目录~/.ssh下。可以将私钥看作不想与其他人共享的私人密码,将公钥视为可识别身份的名片。使用私钥和公钥,邮件将在本地由你的私钥加密,并由远程主机使用公钥解密。我们应该将公钥复制到远程主机。在实际应用中,可以通过使用一个外置的USB驱动器来实现;在我们的实验中,可以简单地将公钥复制到远程主机的~/.ssh/authorized_keys文件中。但需要打开远程服务器的终端窗口,以便粘贴公钥。
使用Paramiko复制管理主机上~/.ssh/id_rsa的内容:
然后,将其粘贴到用户目录下的远程主机上,这种情况下,双方应使用echou:
现在可以使用Paramiko来管理远程主机。请注意,在此示例中,我们将使用私钥进行身份验证,并使用exec_command()方法发送命令:
请注意,在服务器示例中,不需要创建交互式会话来执行多个命令。现在可以在远程主机的SSHv2配置中关闭基于密码的身份验证,以便在启用自动化的情况下实现更安全的基于密钥的身份验证。某些网络设备(如Cumulus和Vyatta交换机)也支持基于密钥的身份验证。
2.4.5 Paramiko可重用性
在本章的最后,让Paramiko程序更具可重用性。现在的脚本有一个缺点:每次我们想要添加/删除主机或者更改想要在远程主机上执行的命令时,都需要打开脚本。这是因为主机和命令信息都是静态输入到脚本中的。硬编码主机和命令有更高的错误率。此外,如果要将脚本传给同事,他们可能会觉得在Python、Paramiko或Linux中工作不舒服。
通过将主机和命令文件作为脚本的参数读入,可以消除其中的一些问题。当需要进行主机或命令更改时,用户可以简单地修改这些文本文件。
我们已将更改合并到名为chapter2_4.py的脚本中。
我们将命令分解为单独的commands.txt文件,而不是对命令进行硬编码。到目前为止,我们一直在使用show命令,在这个例子中,我们将更改配置。特别是,将日志记录缓冲区大小更改为30000字节:
设备的信息将写入devices.json文件。我们选择JSON格式来记录设备的信息,因为JSON数据类型可以很容易地转换为Python字典数据类型:
在脚本中,我们进行了以下更改:
这是脚本执行的缩略输出:
快速检查以确保在running-config和startup-config中都有更改:
2.5 展望
就使用Python来自动化网络而言,我们在本章中有了相当大的飞跃。但是,我们使用的方法感觉有点像自动化的变通方案。我们试图欺骗远程设备使其认为正在与另一端的人进行交互。
与其他工具相比Pexpect和Paramiko的缺点
到目前为止,我们的方法的最大缺点是远程设备不返回结构化数据,它们返回的数据非常适合在终端上由人类而不是计算机程序来解释。人类可以轻松地解释空间,而计算机只能看到返回的字符。
我们将在接下来的章节中看到更好的方法。作为第3章的前奏,让我们先来讨论幂等性的概念。
幂等网络设备交互
幂等性一词具有不同的含义,取决于其背景。但在本书的上下文中,该术语意味着当客户端对远程设备进行相同的调用时,结果应始终相同。我相信我们都同意这是必要的。想象一下每次执行相同的脚本时得到不同的结果,这种情况是多么可怕。如果是这样的话,你怎么能相信你的脚本?它会使自动化工作变得无用,因为我们需要准备好处理不同的回调结果。
由于Pexpect和Paramiko以交互方式拼出一系列命令,因此进行非幂等交互的可能性更大。对有用元素进行屏幕截取才能返回结果这一事实使得产生差异的风险要高很多。远程端的某些内容可能在我们编写脚本和脚本第100次执行之间发生了变化。例如,如果供应商在不更新脚本的情况下在版本之间更改屏幕输出,则脚本可能会中断。
如果我们需要依赖脚本进行实验,则需要脚本尽可能具有幂等性。
糟糕的自动化使错误加剧
糟糕的自动化会使你大跌眼镜。计算机执行任务的速度比人类工程师快得多。如果我们使人类与脚本执行相同的操作程序集,则脚本将比人类快很多,有时甚至没有在程序之间建立可靠的反馈循环。当有人按下回车键并立即后悔时,互联网上就会充满恐怖的事情。
我们需要确保自动化脚本错误的可能性尽量小。我们都会犯错,在任何实验工作开始之前仔细测试你的脚本和确定错误范围是确保你在错误发生之前发现错误的关键。
2.6 小结
在本章中,我们介绍了与网络设备直接通信的低级方法。如果没有办法以编程方式进行通信并对网络设备进行更改,则无法实现自动化。我们介绍了Python中的两个库,它们允许我们管理要由CLI管理的设备。虽然很有用,但很容易看出这个过程有多么脆弱。这主要是因为有问题的网络设备是由人类而不是计算机管理的。
在第3章中,我们将介绍支持API和意图驱动网络的网络设备。