在上一篇文案中「网络自动化运维」华为交换机配置自动化备份实践(基本篇),我实践了经过Python的paramiko模块来SSH登录到华为交换机上批量备份配置文件到FTP服务器上。最后总结存在两个问题,一个是在脚本运行过程中,倘若中间某台设备因用户名秘码错误或网络不达到的状况,会引起脚本中断运行没法继续执行下去,后面设备的备份亦没法完成。第二个便是因为python默认是单线程串行执行的,在设备量大的时候效率可能不高,脚本需要运行的时间比较长。
本篇文案将会经过try...except来实现反常处理,以及经过threading模块来处理多线程问题,并发执行多台设备同期进行备份操作。
本实验经过学习@弈心大佬的《网络工程师的Python之路》后结合自己的理解和思路形成。
实验环境及拓扑仍然跟上次同样,python实验平台与5台交换机桥接在一块。
反常处理try...except
反常是一个事件,在程序执行过程中python没法正常处理的程序出现的话,就会影响程序的正常执行。Python能够经过try...except语句来检测try语句块中的错误,并且让except语句来捕捉反常信息并进行处理。
首要基于上一次的实验的基本上,加入反常处理的机制,以下是加入反常处理机制后完整的代码。
下面咱们来对代码进行分析。
第1部分 for line in f.readlines():
try:
line_s = line.split( )
device_ip = line_s[0]
device_name = line_s[1]
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=device_ip, username=username, password=password)
print(成功连接上 , device_ip)
command = ssh_client.invoke_shell()
command.send(save\n)
command.send(y\n)
time.sleep(1)
command.send(ftp 192.168.134.1\n)
command.send(ftpuser\n)
command.send(ftpuser\n)
command.send(put vrpcfg.zip + date + _ + device_name + _vrpcfg.zip\n)
command.send(bye\n)
time.sleep(1)
output = command.recv(65535)
print(output.decode(UTF-8))
except paramiko.ssh_exception.AuthenticationException:
print(device_ip + 用户认证失败..)
device_authentication_failed_list.append(device_ip)
except socket.error:
print(device_ip + 网络不达到..)
device_not_reachable_list.append(device_ip)主代码跟上一次实验是同样的,只是在主代码前面加入try,并在主代码后加入except捕捉详细的反常信息。倘若try后的语句执行时出现反常,python就会跳到except来判断反常信息,并执行响应动作。倘若try后的语句执行正常,则会跳过except,就跟if...else要求判断语句是一个意思。主代码跟上一次实验是同样的,只是在主代码前面加入try,并在主代码后加入except捕捉详细的反常信息。倘若try后的语句执行时出现反常,python就会跳到except来判断反常信息,并执行相应的动作。倘若try后的语句执行正常,则会跳过except,就跟if...else要求判断语句是一个意思。
网络设备登录反常一般有2个场景,一个便是用户名秘码错误,即认证失败没法登录上设备,第二个便是这台网络设备断网了没法连接上。
经过paramiko登录设备认证失败的话,python会抛出反常信息
paramiko.ssh_exception.AuthenticationException,因此呢咱们经过except来捕捉这个反常报错信息来执行下面的动作,这儿咱们会先打印一个信息提示这个IP认证失败的原由,并且将该反常设备的IP位置添加进先前定义好的空列表 device_authentication_failed_list里。网络不达到的反常需要引入socket模块来处理,当设备连接不上时会报socket.error,一样的把这个反常状况打印出来并放到定义好的空列表device_not_reachable_list里。
第二部分 print(\n以下设备认证失败没法登录: )
if device_authentication_failed_list == []: #判断是不是为空列表
print(无)
else:
for i in device_authentication_failed_list:
print(i)
print(\n以下设备网络不达到: )
if device_not_reachable_list == []:
print(无)
else:
for i in device_not_reachable_list:
print(i)最后,当程序执行完把所有显现登录反常的设备的打印出来,后期人工再做后续处理。判断倘若列表为空,则打印“无”,否则非空则分别打印出列表里的反常设备IP。
反常处理实验验证
把SW2的登录秘码修改为Cisco@123(模拟认证失败),把SW4连HUB的接口G0/0/1 shutdown掉(模拟网络不达到),再运行脚本。
能够看到,在脚本执行过程中针对没法登录的设备会打印出反常信息,并继续执行下一台设备的备份操作而不会半途中断。在所有设备备份执行完之后,会统计出所有反常设备的IP并打印出来。
多线程 threading
Python默认是单线程的,在执行备份脚本的时候经过输出能够看到是在备份完一台设备后再备份另一一台这般串行执行的。因为这次实验总共就5台设备,因此全部备份时间都能够接受,那倘若现网中有几十上百台设备的话,单线程执行的过程会是比较慢的,下面就介绍经过python自带的threading模块来实现多线程并发执行多台设备同期备份,加快程序执行效率。据述有更好用的多线程模块,后面我亦会继续科研学习一下。
这次实验我会对代码稍微改造一下,将SSH登录及命令执行封装成函数ssh_f,而后用后面代码调用ssh_f函数。全部代码如下。
代码解析
第1部分 def ssh_f(ip, username, password, device_name, date): #封装成函数ssh_f
try:
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=ip, username=username, password=password)
print(成功连接上: , ip)
time.sleep(2)
command = ssh_client.invoke_shell()
command.send(save\n)
command.send(y\n)
time.sleep(1)
command.send(ftp 192.168.134.1\n)
command.send(ftpuser\n)
command.send(ftpuser\n)
command.send(put vrpcfg.zip + date + _ + device_name + _vrpcfg.zip\n)
command.send(bye\n)
time.sleep(1)
print(已完成备份... , ip)
except paramiko.ssh_exception.AuthenticationException:
print(用户认证失败 + ip + .)
device_authentication_failed_list.append(ip)
except socket.error:
print(ip + 网络不达到)
device_not_reachable_list.append(ip)
ssh_client.close()此部分只是把先前的代码封装成为了函数,定义了函数名为ssh_f,函数内的代码跟先前是同样的,只是把ssh登录跟下发命令的代码单独抽取了出来放到函数里,这般能够方便后面代码的调用。并对ssh_f函数定义了这几个参数,ip, username, password, device_name, date,后面调用时会传参进去。
第二部分 username = python
password = Python@123
date = time.strftime(%Y-%m-%d)
threads = [10]
device_authentication_failed_list = []
device_not_reachable_list = []
print(起始执行备份操作...)
f = open(devices_list.txt, r)
for line in f.readlines():
line_s = line.split( )
ip_address = line_s[0]
device_name = line_s[1]
#运用threading的Thread()函数为ssh_2函数创建一个线程并将它赋值给变量a,重视Thread()函数的target参数对应的是函数名叫作(即ssh_2)
#args对应的是该ssh_f函数的参数
a = threading.Thread(target=ssh_f, args=(ip_address, username, password, device_name, date))
a.start()
f.close()
time.sleep(5) #先等待所有操作执行完,再打印反常列表
print(\n以下设备认证失败没法登录: )
if device_authentication_failed_list == []: # 判断是不是为空列表
print(无)
else:
for i in device_authentication_failed_list:
print(i)
print(\n以下设备网络不达到: )
if device_not_reachable_list == []:
print(无)
else:
for i in device_not_reachable_list:
print(i)接下来的代码其实跟之前的主代码基本差不多,再也不一一解释。增多的点便是在for循环里增多了threading的代码,并且在threading里调用刚才定义的ssh_f函数用于ssh连接及命令执行。
运用threading的Thread()函数为ssh_2函数创建一个线程并将它赋值给变量a,Thread()函数的target参数对应的是函数名叫作ssh_2,args对应的是该ssh_f函数的参数,关联参数都已然定义并传参进来。
而后a.start() 起步线程。运用threading多线程,这般下一个操作就不需要等上一个操作执行完后再起始执行,实现了并行执行。
实验验证
下面咱们就对这个脚本运行一下,再来瞧瞧这5台设备的备份效果。特意制作了以下的GIF动图,能够看到5台设备的备份过程基本上是同期连接上,并且是几乎同期完成的,再也不是前面实验同样先完成一台后再执行下一台,这般,这个脚本的执行效率就会大大的提高,倘若设备多的话将会脚本运行的效率就会有量级的提高。
能够看到服务器文件目录里已然能够正常获取到5台交换机的配置文件。
总结
经过以上两个实验案例,经过反常处理及多线程,就能够优化上一个实验遗留下来的2个问题了。以上的实践尤其是多线程并发处理,肯定还有非常多方式能够实现,接下来我亦会继续探索更加多Netdevops的内容。因为日前还处在小白的周期,以上编写的代码和诠释肯定还有非常多不足和理解不足深入之处,如有任何错误的地区还请各位伴侣指出,一起学习!
|