现在,您已经了解了如何在各种操作系统上使用Python进行安装,以及如何使用EVE-NG构建网络拓扑。

Python自动化运维实战:使用Python管理网络设备

本章学习如何使用当前常用的网络自动化库来自动化各种网络任务。

Python可以逐网络层与网络设备进行交互。

首先,Python可以在套接字编程和套接字模块中操作底层网络,从而可以在Python所在的操作系统和网络设备之间构建低级网络接口。

Python模块还可以使用Telnet、SSH和API与网络设备进行更高级的交互。

本章详细介绍如何在Python中使用Telnet和SSH模块连接到远程设备并执行命令。

1根据技术要求,必须验证以下工具是否已正确安装和正确使用:

Python 2.7.1x; PyCharm社区版或专业版; 请参考第3章安装和配置EVE-NG、网络仿真器。

本章中出现的所有脚本请参考GitHub网站。

1.1 Python、SSHSSH和Telnet的区别在于客户端和服务器之间交换数据的通道不同。

SSH使用安全链路,很难在客户端和设备之间创建使用不同安全机制加密的隧道并解密通信内容。

因此,在需要确保网络安全的情况下,网络工程师首先选择使用SSH协议。

Paramiko库符合SSH2协议,并支持身份验证、密钥处理和其他SSH功能。

Python在需要使用SSH连接到网络设备时通常使用此库。

1.2 Paramiko模块Paramiko是Python中使用最广泛的SSH模块。

模块本身是用Python语言编写和开发的,只有像crypto这样的核心函数使用c语言。

从GitHub官方链接可以看到代码的贡献者和模块历史等很多信息。

1 .安装模块并打开Windows cmd或Linux shell,然后运行以下命令从PyPI下载最新的Paramiko模块:

此命令会自动下载其他依赖软件包并将其安装在计算机上,如下图所示。

pip install Paramiko在命令行中键入Python,然后将其导入paramiko模块以验证是否成功安装。

如下图正确安装后,模块导入成功。

这意味着命令行中不会显示错误消息。

2 .通过ssh连接网络设备如上所述,要使用Paramiko模块,必须先将其导入Python脚本中,然后通过继承SSHClient ( )创建ssh客户端。

然后设置Paramiko的参数以自动添加未知的主机密钥,并信任与服务器的连接。

然后将远程主机的信息传递给connect函数。

#! /usr/bin/python _ author _=' bassi maly ' _ email _=' basim.alyy @ Gmail.com ' importparamikoimporration channel.set _ mise connect ) Hostname='10.10.)。look_for_keys=False,allow _ agent=false ( shell=channel.invoke _ shell )

在虚拟实验室环境中建议使用此策略,但在生产环境中必须使用更严格的策略,如WarningPolicy (和RejectPolicy )。

最后,invoke _ shell ( )将启动连接到SSH服务器的交互式shell会话。

调用函数时,可以传递一些附加参数。

Paramiko的连接参数如下。

Look_For_Keys :默认值为True,强制Paramiko使用密钥进行身份验证。

这意味着用户必须使用私钥和公钥对网络设备进行身份验证。

由于此处使用密码验证,因此将此参数设置为False。

allow _ agent :指示是否允许连接到ssh代理,默认值为True。

用密钥进行身份验证时,可能需要此选项。

此处使用的是用户名/密码,因此将其禁用。

最后,将各种命令发送到设备终端,并在Python窗口中输出结果。

壳. send(enable(n ) )壳. send ) access123(n ) )壳. send ( terminal length0( n ) )壳. send )。

如果需要在远程设备上运行耗时的命令,则建议使用time.sleep ( ),因为在设备生成输出并将结果返回给Python之前,必须强制Python等待一段时间。

否则,Python可能无法获得正确的输出结果。

1.3 netmiko模块netmiko是Paramiko的扩展版,专门用于网络设备。

帕拉米科可以处理与设备的SSH连接,并确定设备类型是服务器、打印机还是网络设备,而netmiko针对网络设备进行了优化,以便更有效地处理SSH连接

netmiko还支持各种设备制造商和平台。

netmiko也是Paramiko的封装,包括使用有效密码直接访问支持的设备、从文件中读取配置并将其推送到设备,以及在登录期间禁用分页显示

和缺省情况下,每个命令后面都会加上回车“\n”。

1 .支持的设备公司netmiko支持许多供应商的设备,并定期在支持列表中添加新供应商。

netmiko支持的供应商列表分为定期测试类、有限测试类和实验班。

在该模块的GitHub页面上可以找到这个列表。

定期测试类中支持的供应商如下图所示。

有限测试类中支持的供应商如下图所示。

实验类中支持的供应商如下图所示。

2安装和验证安装netmiko非常简单。

打开Windows命令行窗口或Linux shell,执行下面的命令就可以从PyPI获取最新版本的netmiko包。

pip install netmiko然后,在Python shell中导入netmiko,验证模块是否正确地安装到Python site-packages中。

$python import netmiko3使用netmiko建立SSH连接现在该开始使用netmiko了,让我们来看看它强大的SSH功能。

首先连接到网络设备并在上面执行命令。

默认情况下,netmiko在建立会话的过程中会在后台处理许多操作,在需要的时候还可以进入特权模式,然后通过运行供应商提供的命令来禁用分页。

首先,以字典格式定义设备并提供下列5个必需的关键信息。

R1=('device type ': 'cisco ios','ip': '10.10.88.110','username': 'admin','password': 'access123','secret': 'access123',}第一个参数是device_type,

然后,需要SSH的IP地址。

如果已经使用DNS解析了IP地址,该参数可能是主机名;否则,该参数是IP地址。

接下来,提供username、password以及以secret参数传递的特权模式的密码。

注意,可以使用getpass()模块隐藏密码,并且只在脚本执行期间提示它们。

虽然变量中的密钥序列不重要,但是为了使netmiko能够正确解析字典并开始和设备建立连接,密钥的名称应该和之前示例中提供的密钥完全一样。

接下来,从netmiko模块导入ConnectHandler函数,并提供定义的字典来开始建立连接。

因为所有的设备是通过特权模式的密码配置的,所以需要为创建的连接提供.enable(),以在特权模式下访问。

使用.send_command()在路由器终端上执行命令,send_command()将会执行命令并通过变量的值显示设备的输出。

from netmiko import ConnectHandlerconnection=ConnectHandler(**R1)connection.enable()output=connection.send_command('show ip int b')print output脚本输出结果如下。

注意,这里看到的输出结果去掉了命令行中的命令回显和设备提示符。

默认情况下,netmiko会替换设备的返回结果,使输出更加整洁,替换过程通过正则表达式完成,这部分会在下一章中介绍。

如果不想使用这种方式,而是希望看到命令提示符,并在返回结果的后面执行命令,可以在.send_command()函数中加上以下参数。

output=connection.send_command('show ip int b',strip_command=False,strip_prompt=False)strip_command=False和strip_prompt=False告诉netmiko保留而不是替换命令行回显和提示符。

默认情况下它为True,可以根据需要进行设置。

4使用netmiko配置设备netmiko可以通过SSH配置远程设备,通过.config方法进入设备的配置模式,然后按照list格式中的信息配置设备。

配置列表可以直接写在Python脚本中,也可以从文件中读取,然后用readlines()方法转换为列表。

from netmiko import ConnectHandlerSW2={'device_type': 'cisco_ios','ip': '10.10.88.112','username': 'admin','password': 'access123','secret': 'access123',}core_sw_config=['int range gig0/1 - 2','switchport trunk encapsulationdot1q','switchport mode trunk','switchport trunk allowed vlan1,2']print '########## Connecting to Device {0} ############'.format(SW2['ip'])net_connect=ConnectHandler(**SW2)net_connect.enable()print '***** Sending Configuration to Device *****'net_connect.send_config_set(core_sw_config)上面的脚本以另外一种形式连接到SW2并进入特权模式。

但这次使用的是另一个netmiko方法——send_config_set(),该方法需要使用列表形式的配置文件,同时进入设备的配置模式并根据列表对设备进行配置。

这里测试了一个简单的配置,即修改gig0/1和gig0/2,并将这两个端口配成trunk模式。

在设备上执行show run命令时,如果命令执行成功,会出现类似下面的输出。

5netmiko中的异常处理在设计Python脚本时,我们可能会假设设备已启动并运行,并且用户已提供了正确的登录信息,但实际情况并非总是如此。

有时Python和远程设备之间的网络连接可能存在问题,或者用户输入了错误的登录信息。

如果发生这种情况,Python通常会抛出异常并退出,但这种解决方案显然不够完美。

netmiko中的异常处理模块netmiko.ssh_exception提供的一些异常处理类可以处理上面所说的那些情况。

第一个类AuthenticationException能够捕获远程设备中的身份验证错误。

第二个类NetMikoTimeoutException能够捕获netmiko和设备之间的超时或任何连接问题。

下面的例子中使用try-except子句包含了ConnectHandler()方法,用来捕获超时和身份验证异常。

from netmiko import ConnectHandlerfrom netmiko.ssh_exception import AuthenticationException,NetMikoTimeoutExceptiondevice={'device_type': 'cisco_ios','ip': '10.10.88.112','username': 'admin','password': 'access123','secret': 'access123',}print '########## Connecting to Device {0}############'.format(device['ip'])try:net_connect=ConnectHandler(**device)net_connect.enable()print '***** show ip configuration of Device *****'output=net_connect.send_command('show ip int b')print outputnet_connect.disconnect()except NetMikoTimeoutException:print '===========SOMETHING WRONG HAPPEN WITH {0}============'.format(device['ip'])except AuthenticationException:print '=========Authentication Failed with {0}============'.format(device['ip'])except Exception as unknown_error:print '============SOMETHING UNKNOWN HAPPEN WITH {0}============'6设备自动发现

netmiko提供了一种可以“猜测”设备类型和发现设备的机制。

通过组合使用SNMP发现OIDS和在远程控制台上执行多个show命令这两种方式,根据输出字符串检测路由器的操作系统和类型。

然后,netmiko将相应的驱动程序加载到ConnectHandler()类中。

#!/usr/local/bin/python__author__='Bassim Aly'__EMAIL__='basim.alyy@gmail.com'from netmiko import SSHDetect, Netmikodevice={'device_type': 'autodetect','host': '10.10.88.110','username': 'admin','password': 'access123',}detect_device=SSHDetect(**device)device_type=detect_device.autodetect()print(device_type)print(detect_device.potential_matches)device['device_type']=device_typeconnection=Netmiko(**device)在上面的脚本中,

首先,设备字典中的device_type等于autodetect,也就是告诉netmiko在检测到设备类型之前不要加载驱动程序。

然后,使用netmiko的SSHDetect()类发现设备。

它使用SSH连接到设备,并执行一些命令以找出操作系统的类型,结果以字典形式返回。

接着,使用autodetect()函数将匹配度最高的结果赋给device_type变量。

接下来,输出potential_matches,查看字典内的全部返回结果。

最后,可以更新设备字典并为其分配新的device_type。

2 在Python中使用Telnet协议Telnet是TCP/IP协议栈中最早可用的协议之一,主要用来在服务器和客户端之间建立连接、交换数据。

服务器端监听TCP端口23,等待客户端的连接请求。

在下面的例子中,我们将创建一个Python脚本作为Telnet客户端,拓扑中的其他路由器和交换机则作为Telnet服务器。

Python原生的telnetlib库已经支持Telnet,所以不需要另外安装。

客户端对象可以通过telnetlib模块中的Telnet()类实例化创建。

通过这个对象,我们能够使用telnetlib中的两个重要函数——read_until()和write()。

这两个函数用来和Telnet连接交互,从Telnet连接读取或向Telnet连接写入数据。

还有一点非常关键,使用read_until()读取Telnet连接的内容之后缓冲区会被清空,无法再次读取。

因此,如果在后面的处理中还会用到之前读取的重要数据,需要在脚本里将其另存为变量。

Telnet数据以明文形式发送,因此通过“中间人攻击”可以捕获并查看到Telnet数据,如用户信息和密码。

即便如此,一些服务提供商和企业仍然在使用它,只是他们会集成VPN和radius/tacacs协议,以提供轻量级和安全的访问方式。

让我们一步步分析这个脚本。

在Python脚本中导入telnetlib模块,在变量中定义用户名和密码。

代码如下。

import telnetlibusername='admin'password='access123'enable_password='access123'定义一个变量,

用来和远程主机建立连接。

注意,只需要提供远程主机的IP地址,不用在连接建立过程中提供用户名或密码。

cnx=telnetlib.Telnet(host='10.10.88.110') #here we're telnet toGateway通过读取Telnet连接返回的输出并搜索“Username:”关键字来提供Telnet连接的用户名。

然后写入管理员用户名。

如果需要,用同样的方法输入Telnet密码。

cnx.read_until('Username:')cnx.write(username + '\n')cnx.read_until('Password:')cnx.write(password + '\n')cnx.read_until('')cnx.write('en' + '\n')cnx.read_until('Password:')cnx.write(enable_password + '\n')Telnet连接建好之后,

接着Python脚本就会超时并出现错误。

向Telnet连接写入show ip interface brief命令并开始读取返回内容,直到出现路由器提示符为止。

通过以下命令可以得到路由器的接口配置。

cnx.read_until('#')cnx.write('show ip int b' + '\n')output=cnx.read_until('#')print output完整的脚本如下所示。

脚本运行结果如下所示。

注意,在输出中包含了执行的命令show ip int b,并且在stdout中输出和返回了路由器提示符”R1#”。

可以使用内置的字符串函数从输出中清除它们。

cleaned_output=output.replace('show ip int b','').replace('R1#','')print cleaned_output你可能已经注意到脚本中使用了密码并将密码以明文形式写下来,

同时,在Python脚本中使用硬编码也不是好习惯。

在下一节中,我们将学习如何隐藏密码并设计一种机制,从而在脚本运行时要求用户输入密码。

此外,如果要执行那些输出结果可能跨越多个页面的命令,则需要在连接到设备之后和发送命令之前,先通过发送termindl length 0来禁用分页。

使用telnetlib推送配置在上一节中,我们通过执行show ip int brief简单介绍了telnetlib的操作过程。

现在我们要用telnetlib将VLAN配置推送到实验室网络拓扑中的4台交换机。

使用Python的range()函数创建一个VLAN列表,遍历列表将VLAN ID推送到当前交换机。

注意,我们将交换机的IP地址放到了另一个列表中,使用外部for循环来遍历这个列表。

同时使用内置模块getpass隐藏控制台中的密码,在脚本运行时提示用户输入密码。

#!/usr/bin/pythonimport telnetlibimport getpassimport timeswitch_ips=['10.10.88.111', '10.10.88.112', '10.10.88.113','10.10.88.114']username=raw_input('Please Enter your username:')password=getpass.getpass('Please Enter your Password:')enable_password=getpass.getpass('Please Enter your Enable Password:')for sw_ip in switch_ips:print '\n#################### Working on Device ' + sw_ip + '####################'connection=telnetlib.Telnet(host=sw_ip.strip())connection.read_until('Username:')connection.write(username + '\n')connection.read_until('Password:')connection.write(password + '\n')connection.read_until('')connection.write('enable' + '\n')connection.read_until('Password:')connection.write(enable_password + '\n')connection.read_until('#')connection.write('config terminal' + '\n') # now i'm in config modevlans=range(300,400)for vlan_id in vlans:print '\n********* Adding VLAN ' + str(vlan_id) + '**********'connection.read_until('#')connection.write('vlan ' + str(vlan_id) + '\n')time.sleep(1)connection.write('exit' + '\n')connection.read_until('#')connection.close()最外层的for循环用来遍历设备列表,

脚本运行结果如下。

当然,也可以通过交换机控制台检查运行结果。

3 使用netaddr处理IP地址和网络管理和操作IP地址是网络工程师最重要的任务之一。

Python开发人员提供了一个令人惊叹的库—— netaddr,它可以识别IP地址并对其进行处理。

假设你开发了一个应用程序,其中需要获取129.183.1.55/21的网络地址和广播地址,通过模块内的内置方法network和broadcast可以轻松地获取到相应的地址。

net.network129.183.0.net.broadcast129.183.0.0netaddr支持很多功能。

在第3层的地址中,netaddr支持下列功能。

识别IPv4和IPv6地址、子网、掩码和前缀。

对IP网络进行迭代、切片、排序、汇总和分类。

处理各种格式。

对IP地址和子网进行集合操作。

解析各种不同的格式和符号。

查找IANA IP块信息。

生成DNS反向查找结果。

检索超网和生成子网。

在第2层的地址中,netaddr支持下列功能。

展示和操作Mac地址与EUI-64标识符。

查找IEEE组织信息。

生成链路本地的IPv6地址。

3.1 安装netaddr使用pip安装netaddr模块,命令如下。

pip install netaddr安装完成之后打开PyCharm或Python控制台并导入模块,验证模块是否安装成功。

如果没有出现错误信息,说明模块安装成功。

python import netaddr3.2 使用netaddr的方法netaddr模块提供了两种重要的方法来定义IP地址并对其进行处理。

第一种方法是IPAddress(),它用来定义具有默认子网掩码的单个有类IP地址。

第二种方法是IPNetwork(),它使用CIDR定义无类IP地址。

两种方法都将IP地址作为字符串来处理,根据字符串返回IP地址或IP网络对象。

返回的对象还可以继续执行许多方法,比如判断IP地址是单播地址、多播地址、环回地址、私有地址还是公有地址,以及地址有效还是无效地址。

这些操作的结果是True或False。

在Python的if条件中可以直接使用这些方法。

另外,该模块支持使用==、和等比较运算符比较两个IP 地址,从而生成子网。

它还可以检索一个给定IP地址或者子网术语的超网列表。

最终,netaddr模块可以生成有效主机的一个完整列表。

#!/usr/bin/python__author__='Bassim Aly'__EMAIL__='basim.alyy@gmail.com'from netaddr import IPNetwork,IPAddressdef check_ip_address(ipaddr):ip_attributes=[]ipaddress=IPAddress(ipaddr)if ipaddress.is_private():ip_attributes.append('IP Address is Private')else:ip_attributes.append('IP Address is public')if ipaddress.is_unicast():ip_attributes.append('IP Address is unicast')elif ipaddress.is_multicast():ip_attributes.append('IP Address is multicast')if ipaddress.is_loopback():ip_attributes.append('IP Address is loopback')return '\n'.join(ip_attributes)def operate_on_ip_network(ipnet):net_attributes=[]net=IPNetwork(ipnet)net_attributes.append('Network IP Address is ' + str(net.network) + 'and Netowrk Mask is ' + str(net.netmask))net_attributes.append('The Broadcast is ' + str(net.broadcast) )net_attributes.append('IP Version is ' + str(net.version) )net_attributes.append('Information known about this network is ' +str(net.info) )net_attributes.append('The IPv6 representation is ' + str(net.ipv6()))net_attributes.append('The Network size is ' + str(net.size))net_attributes.append('Generating a list of ip addresses inside thesubnet')for ip in net:net_attributes.append('\t' + str(ip))return '\n'.join(net_attributes)ipaddr=raw_input('Please Enter the IP Address: ')print check_ip_address(ipaddr)ipnet=raw_input('Please Enter the IP Network: ')print operate_on_ip_network(ipnet)在上面的脚本中,

第一个函数check_ip_address()会检查输入的IP地址,同时尝试生成有关IP地址属性的报告,并将输出返回给用户。

第二个函数operate_on_ip_network()用来完成和网络相关的操作,即生成网络ID、掩码、广播、版本、网络上的已知信息、IPv6地址的显示方式,最后生成该子网内的所有IP地址。

注意,net.info只能对公共IP地址生成可用信息,对私有IP地址不起作用。

同样,在使用之前需要先从netaddr模块导入IP Network和IP Address。

脚本运行结果如下所示。

4 简单的用例随着网络变得越来越大,其中包含更多来自各种不同供应商的设备,这就需要创建模块化的Python脚本来自动执行各种任务。

接下来的几节将分析3个用例,这些用例可以从网络中收集不同信息,缩短解决问题所需的时间,或者至少将网络配置恢复到其上次已知的良好状态。

使用自动化工作流来处理网络故障、修复网络环境,网络工程师能够更关心工作完成情况,提高业务水平。

4.1 备份设备配置备份设备配置对于任何一名网络工程师来说都是最重要的任务之一。

在这个用例中,我们将使用netmiko库设计一个示例Python脚本。

该脚本用来备份设备配置,它适用于不同的供应商和平台。

为方便日后访问或引用,我们将根据设备IP地址格式化输出文件名,例如,SW1备份操作的输出文件保存在dev_10.10.88.111_.cfg中。

创建Python脚本从定义交换机开始,我们希望将其配置备份为文本文件,用逗号分隔访问设备的用户名、密码等详细信息。

这样就可以在Python脚本中使用split()函数来获取这些数据,方便在ConnectHandler函数中使用这些数据。

此外,还可以从Microsoft Excel工作表或任何数据库中轻松导出该文件以及把该文件导入其中。

文件结构如下。

device_ipaddress,username,password,enable_password,vendor创建Python脚本,使用with open子句在脚本中导入该文件。

在导入的文件对象上使用readlines()方法将文件中的每一行组成列表,然后用for循环逐行遍历文件,用split()函数获取每一行中用逗号隔开的设备信息,并将它们赋予相应的变量。

from netmiko import ConnectHandlerfrom datetime import datetimewithopen('/media/bassim/DATA/GoogleDrive/Packt/EnterpriseAutomationProject/Chapter5_Using_Python_to_manage_network_devices/UC1_devices.txt') asdevices_file:devices=devices_file.readlines()for line in devices:line=line.strip('\n')ipaddr=line.split(',')[0]username=line.split(',')[1]password=line.split(',')[2]enable_password=line.split(',')[3]vendor=line.split(',')[4]if vendor.lower()=='cisco':device_type='cisco_ios'backup_command='show running-config'elif vendor.lower()=='juniper':device_type='juniper'backup_command='show configuration | display set'由于我们的目标是创建模块化的、支持多种设备供应商的脚本,

接下来,建立与设备的SSH连接,使用netmiko模块中的.send_command()方法执行备份命令。

print str(datetime.now()) + ' Connecting to device {}' .format(ipaddr)net_connect=ConnectHandler(device_type=device_type,ip=ipaddr,username=username,password=password,secret=enable_password)net_connect.enable()running_config=net_connect.send_command(backup_command)print str(datetime.now()) + ' Saving config from device {}' .format(ipaddr)f=open( 'dev_' + ipaddr + '_.cfg', 'w')f.write(running_config)f.close()print '=============================================='在最后的几行中,

脚本运行结果如下。

需要注意的是,备份的配置文件存储在项目的主目录中,文件名称包含每个设备的IP地址。

使用Linux服务器上的cron任务,或Windows服务器上的计划任务,可让服务器在指定时间运行上面的Python脚本。

例如,每天凌晨运行一次,将配置信息存储在latest目录中,以方便运维团队使用。

4.2 创建访问终端在Python或其他编程活动中,你就是自己的设备供应商。

为了满足自己的需求,你可以创建任何喜欢的代码组合和程序。

在第二个例子中我们创建自己的终端,通过telnetlib访问路由器。

只需要在终端写几个单词,就会在网络设备中执行很多命令并返回输出结果。

输出结果将会显示在标准输出或保存在文件中。

#!/usr/bin/python__author__='Bassim Aly'__EMAIL__='basim.alyy@gmail.com'import telnetlibconnection=telnetlib.Telnet(host='10.10.88.110')connection.read_until('Username:')connection.write('admin' + '\n')connection.read_until('Password:')connection.write('access123' + '\n')connection.read_until('')connection.write('en' + '\n')connection.read_until('Password:')connection.write('access123' + '\n')connection.read_until('#')connection.write('terminal length 0' + '\n')connection.read_until('#')while True:command=raw_input('#:')if 'health' in command.lower():commands=['show ip int b','show ip route','show clock','show banner motd']elif 'discover' in command.lower():commands=['show arp','show version | i uptime','show inventory',]else:commands=[command]for cmd in commands:connection.write(cmd + '\n')output=connection.read_until('#')print outputprint '==================='首先,

然后,创建一个始终为true的无限while循环,使用内置的raw_input()函数捕捉用户输入的命令。

脚本捕获到用户输入之后,在网络设备上执行这些命令。

如果用户输入关键字health或discover,终端将自动执行一系列命令以反映期望的操作。

这些关键字在排除网络故障时非常有用,可以根据常用的操作自由扩展它们。

想象一下,在需要解决两个路由器之间的开放式最短路径优先邻居问题时,只要打开自己的Python终端脚本,并将这些命令打包到诸如tshoot_ospf之类的if条件之后。

一旦脚本看到这个关键字,它就会执行这些命令,输出OSPF邻居状态、MTU的接口、OSPF的广播网络等,简化定位问题的过程。

通过在提示符中输入health尝试脚本中的第一条命令。

脚本输出结果如下。

可以看到,脚本将返回在设备上执行多条命令后的结果。

接着试一下第二个命令discover。

脚本输出结果如下。

这次脚本返回discover命令的输出。

在后面的章节中,我们将会解析返回的输出结果并从中提取有用的信息。

4.3 从Excel工作表中读取数据网络和IT工程师始终使用Excel工作表来存储基础设施的相关信息,如IP地址、设备供应商和登录凭证。

Python支持从Excel工作表中读取数据并对其进行处理,以便我们在脚本中使用数据。

在这个用例中,我们将使用Excel Read模块读取UC3_devices.xlsx文件。

该文件保存了基础设施的主机名、IP地址、用户名、普通密码、特权模式下的密码和供应商名称。

然后将读到的数据用作netmiko模块的输入。

Excel工作表中的内容如下图所示。

首先,用pip安装xlrd模块,因为需要用它来读取Microsoft Excel工作表。

pip install xlrdxlrd模块能够读取Excel工作表并将行和列转换为矩阵。

比如,row[0][0]代表第一行第一列的单元格,右边紧接着它的单元格是row[0][1],以此类推。

xlrd在读取工作表时,每次读取一行,同时特殊计数器nrows自动加1。

同样,每次读取一列,ncols自动加1,这样我们就能够通过这两个参数知道矩阵的大小。

然后,在xlrd的open_workbook()函数中输入文件路径,并用sheet_by_index()或sheet_by_name()函数访问工作表。

在本例中,数据存储在第一个工作表中,工作表文件存储在以章为名的文件夹下。

接着,遍历工作表的每一行,或者使用row()函数来访问指定行。

返回的输出结果是一个列表,使用索引可以访问列表中的元素。

Python脚本如下。

__author__='Bassim Aly'__EMAIL__='basim.alyy@gmail.com'from netmiko import ConnectHandlerfrom netmiko.ssh_exception import AuthenticationException,NetMikoTimeoutExceptionimport xlrdfrom pprint import pprintworkbook=xlrd.open_workbook(r'/media/bassim/DATA/GoogleDrive/Packt/EnterpriseAutomationProject/Chapter4_Using_Python_to_manage_network_devices/UC3_devices.xlsx')sheet=workbook.sheet_by_index(0)for index in range(1, sheet.nrows):hostname=sheet.row(index)[0].valueipaddr=sheet.row(index)[1].valueusername=sheet.row(index)[2].valuepassword=sheet.row(index)[3].valueenable_password=sheet.row(index)[4].valuevendor=sheet.row(index)[5].valuedevice={'device_type': vendor,'ip': ipaddr,'username': username,'password': password,'secret': enable_password,}# pprint(device)print '########## Connecting to Device {0}############'.format(device['ip'])try:net_connect=ConnectHandler(**device)net_connect.enable()print '***** show ip configuration of Device *****'output=net_connect.send_command('show ip int b')print outputnet_connect.disconnect()except NetMikoTimeoutException:print '=======SOMETHING WRONG HAPPEN WITH{0}======='.format(device['ip'])except AuthenticationException:print '=======Authentication Failed with{0}======='.format(device['ip'])except Exception as unknown_error:print '=======SOMETHING UNKNOWN HAPPEN WITH {0}======='4.4 其他用例

使用netmiko可以实现很多网络自动化用例。

例如,在升级期间从远程设备上传/下载文件,利用Jinja2模板加载配置,访问终端服务器,访问终端设备等。