百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

网络工程师的Python之路——netdev(异步并行)

off999 2024-10-18 08:07 46 浏览 0 评论

弈心:从事计算机网络工作11年(新加坡7年,沙特4年),2013年考取CCIE,在新加坡先后任职于AT&T,新加坡交通部,苹果,Equinix,苏格兰皇家银行等大型企业、银行和政府部门。 目前供职于“世界第一土豪大学“沙特阿卜杜拉国王科技大学(KAUST),担任Senior Network Engineer,为KAUST校史上第一位也是唯一一位华人IT部门高级职员。2019年6月在知乎发布了华语圈第一本专门为编程零基础的网络工程师量身打造的Python教程《网络工程师的Python之路》。


对网工来说,我们通常必须借助paramiko,netmiko,NAPALM或者pyntc等这些第三方开源模块才能通过SSH或者各种API来登录、操作、管理各种网络设备。很遗憾的是,由于异步在Python中引入较晚(Python 3.4过后才支持异步),上述所有这些模块都不支持异步。也就意味着在不使用多线程的情况下,运行Python脚本的主机必须一台一台地登录设备执行代码。假设登录一台交换机执行配置需耗时5秒的话,那么在拥有1000台交换机的大型企业网里就要耗时5000秒才能执行完脚本,效率太低。

2019年4月,受netmiko项目的启发,俄罗斯网络运维开发工程师Sergey Yakovlev在netmiko的基础上开发了一个叫做netdev的开源模块,该模块依赖于netmiko,并且需要至少Python3.5以上才能运行,最大的特点是支持对网络设备进行异步登录和操作。

在讲解netdev的用法前,首先我们需要知道什么是“同步”(Synchronous),什么是“异步”(Asynchronous),以及为什么使用异步能够帮助我们提升我们创建的Python脚本的工作效率。


1. 同步vs异步

所谓同步,可以理解为每当系统执行完一段代码或者函数后,系统将一直等待该段代码或函数返回的值或消息,直到系统接收到返回的值或消息后才继续往下执行下一段代码或者函数,在等待返回值或消息的期间,程序处于阻塞状态,系统将不做任何事情

本文前面所有涉及到管理多个设备的实验中,我们都是将设备的IP地址预先写入一个名为“ip_list.txt”的文本文件,然后在脚本里使用open()函数将其打开,然后调用readlines()函数并配合for循环读取每个设备的IP地址,然后通过paramiko或者netmiko一台设备接一台地完成SSH登录。像这样Python一次只能登录一台设备,只有在完成一台设备的配置后才能继续登录下一台设备继续配置的方式就是一种典型的“同步”。

而异步则恰恰相反,系统在执行完一段代码或者函数后,不用阻塞性地等待返回的值或消息,而是继续执行下一段代码或函数,在同一时间段里执行多个任务(而不是傻傻的等着一件事情做完并且直到结果出来了以后才去做下件事情),将多个任务并行,从而提高程序的执行效率。如果你有读过数学家华罗庚的《统筹方法》,一定不会对其中所举的例子感到陌生:同样是沏茶的步骤,因为烧水需要一段时间,你不用等水煮沸了过后才来洗茶杯、倒茶叶(类似“同步”),而是在等待烧水的过程中就把茶杯洗好,把茶叶倒好,等水烧开了就能直接泡茶喝了,这里烧水、洗茶杯、倒茶叶三个任务是在同一个时间段内并行完成的,这就是一种典型的“异步”。


2. 单线程vs多线程

过去的Python并不支持异步,因为不管是同步还是异步,它们都是在单线程下完成的。而之前在Python中已经有了多线程(Multithreading)的存在,程序可以启动多个线程同时完成多个任务,如果一个线程阻塞,其他线程并不受影响,程序并不会卡死。后来Python在3.4.x版本中开始加入了异步,为什么要加入呢?因为多线程虽然效率很高,但是程序在切换线程的时候会占用系统资源,产生额外的开销。另外因为异步只用了一个线程,不用担心多线程复杂的锁机制(Lock Mechanism),这也是异步被加入进Python的原因之一。关于多线程及其锁机制的话题超出了本文的讨论范围,读者可以自己参阅其他的材料深入学习。


3. 异步在Python中的应用

自从Python在3.4.x版本起开始支持异步后,关于异步的Python语法几经更改,在Python3.4、Python3.5、Python3.7中异步的实现方式有很大的不同,本文后面的例子中都将基于Python 3.8.2来讲解异步的使用。

要了解异步在Python中的应用,必须知道什么是协程(Corountine),什么是任务(Task),什么是可等待对象(Awaitable Object)

协程可以理解为线程的优化,我们可以把协程看成一种微线程。它是一种比线程更节省资源、效率更高的系统调度机制。而异步就是基于协程实现的。在Python中实现协程的模块主要有asyncio,gevent和tornado,使用较多的是asyncio。首先来看下面的例子:

 #coding=utf-8
 import asyncio
 import time
  
 async def main():
  print('hello')
  await asyncio.sleep(1)
  print('world')
  
 print (f"程序于 {time.strftime('%X')} 开始执行")
 asyncio.run(main())
 print (f"程序于 {time.strftime('%X')} 执行结束")  
  • 在Python中,我们通过在def语句前加上async语句来将一个函数定义为协程函数,在上面的例子中,main()现在被定义为了协程函数
  • 这里的“await asyncio.sleep(1)”表示临时中断当前的函数一秒钟,如果程序里还有其他函数的话,继续执行下一个函数,直到下一个函数执行完毕后,再返回来执行这个main()程序,因为这里我们除了一个main()函数之外没有其他的函数了,所以在print('hello')后,main()函数休眠了1秒钟,然后继续print(‘world’)。
  • 协程函数不是普通的函数,这里我们不能直接用“main()”来调用它,我们需要使用“asyncio.run(main())”才能执行该协程函数。
  • 这里我们配合time模块的strftime()函数来记录程序开始前的时间和程序结束后的时间,可以看到总共耗时确实是1秒钟。

这里需要注意的是不要把 “await asyncio.sleep(1)”和“time.sleep(1)”弄混,后者是在“同步”中使用的休眠操作,前者是在“异步”中使用的,因为这里我们只有main()一个函数需要执行,所以你暂时感受不到这两者有什么区别,不用着急,继续来看下面的两个例子你就知道了:

 #coding=utf-8
 import asyncio
 import time
  
 async def say_after(what, delay):
   print(what)
  await asyncio.sleep(delay)
  
 async def main():
  print (f"程序于 {time.strftime('%X')} 执行结束")
  await say_after('hello',1)
  await say_after('world',2)
  print (f"程序于 {time.strftime('%X')} 执行结束")
  
 asyncio.run(main())
  • 这里我们在协程函数main()的基础上加入了另外一个函数say_after(),同样的,我们用async将它定义为了协程函数。
  • 我们在main()函数中两次调用say_after()函数,因为say_after()是一个协程函数,因此在调用它时,前面必须加上await。
  • 当main()第一次调用say_after()函数时,我们首先打印出hello,然后休眠1秒,第二次调用say_after()函数时,我们打印出world,然后再休眠两秒,两次调用完毕后总共花费了3秒钟来运行完整个程序, 如下:

这里你会说,第一次花了1秒,第二次花了2秒,总共3秒时间,这没节省时间啊,两此调用的say_after()函数并没有并行啊,这和同步有什么区别?别急,继续往下看:

 #coding=utf-8
 import asyncio
 import time
  
 async def say_after(what, delay):
  await asyncio.sleep(delay)
  print(what)
  
 async def main():
 task1 = asyncio.create_task(say_after('hello',1))
  task2 = asyncio.create_task(say_after('world',2))
  print (f"程序于 {time.strftime('%X')} 开始执行")
  await task1
  await task2
  print (f"程序于 {time.strftime('%X')} 执行结束")
  
 asyncio.run(main())
  • 要实现异步并行,需要将协程函数打包成一个任务(Task),这里我们使用asyncio的create_task()函数将say_after()打包了两次,并分别赋值给task1和task2两个变量。然后使用await来调用task1和task2两个任务。
  • 运行脚本后可以看到,因为task1和task2是并行执行的,所以程序总共只耗时两秒钟即告完成(06:57:04到06:57:06)!

最后来说说什么是可等待对象(Awaitable Object),可等待对象的定义很简单:如果一个对象可以在await 语句中使用,那么它就是可等待对象。可等待对象主要有三种类型:协程、任务以及Future。协程和任务前面已经讲到了,Future不在本文的讨论范围内,读者可以自己参阅其他的材料深入学习。

4. netdev的安装和应用

截至2020年5月,netdev支持7家厂商的12种操作系统:

  • Cisco IOS
  • Cisco IOS XE
  • Cisco IOS XR
  • Cisco ASA
  • Cisco NX-OS
  • HP Comware (like V1910 too)
  • Fujitsu Blade Switches
  • Mikrotik RouterOS
  • Arista EOS
  • Juniper JunOS
  • Aruba AOS 6.X
  • Aruba AOS 8.X
  • Terminal

Netdev可以通过pip直接下载安装(因为netdev依赖于netmiko,下载netdev前请确认你的Python主机已经安装了netmiko):

下载完成后进入Python编辑器,如果import netdev没有报错则说明安装成功:

在使用netdev进行异步操作之前,我们先来做个“同步”和“异步”的对比试验,首先我们用传统的“同步”方式,通过netmiko对5台交换机(192.168.2.11--192.168.2.15)下的“line vty 5 15”配置“login local”,并统计从脚本开始运行到脚本执行完成所耗费的时间,然后我们再使用netdev,通过“异步”的方式对同样的5台交换机做同样的配置,并计时,最后将两次耗时相比较,看看孰优孰劣。

传统的“同步”方案的脚本如下(交换机192.168.2.11 – 192.168.2.15的IP的地址被保存在一个名叫ip_list.txt的文件里):

 from netmiko import ConnectHandler
 import time
  
 f = open('ip_list.txt')
 start_time = time.time()
  
 for ips in f.readlines():
  ip = ips.strip()
  SW = {
   'device_type': 'cisco_ios',
   'ip': ip,
   'username': 'python',
   'password': '123',
  }
  connect = ConnectHandler(**SW)
  print ("Sucessfully connected to " + SW['ip'])
  config_commands = ['line vty 5 15','login local','exit']
  output = connect.send_config_set(config_commands)
  print (output)
  
 print ('Time elapsed: %.2f'%(time.time()-start_time))

执行脚本看效果:

可以看到,通过“同步”方式一台一台登录5台交换机完成配置总共耗时了45.02秒。

接下来我们再来看netdev的表现,代码如下:

 import asyncio
 import netdev
 import time
  
 async def task(dev):
  async with netdev.create(**dev) as ios:
  commands = ["line vty 5 15", "login local","exit"]
  out = await ios.send_config_set(commands)
  print(out)
  
 async def run():
  devices = []
  f = open('ip_list.txt')
  for ips in f.readlines():
  ip = ips.strip()
  dev = { 'username' : 'python',
  'password' : '123',
  'device_type': 'cisco_ios',
  'host': ip
  }
  devices.append(dev)
  tasks = [task(dev) for dev in devices]
  await asyncio.wait(tasks)
  
 start_time = time.time()
 asyncio.run(run())
 print ('Time elapsed: %.2f'%(time.time()-start_time))

执行代码看效果:

可以看到通过netdev提供的“异步”方式,我们仅仅耗时5.15秒便跑完了脚本,对5台交换机完成了同样的配置!

另外,除了netdev外,pexpect模块也支持异步并行,有兴趣的读者可以自行参阅其他资料了解。

相关推荐

大文件传不动?WinRAR/7-Zip 入门到高手,这 5 个技巧让你效率翻倍

“这200张照片怎么传给女儿?微信发不了,邮箱附件又超限……”62岁的张阿姨对着电脑犯愁时,儿子只用了3分钟就把照片压缩成一个文件,还教她:“以后用压缩软件,比打包行李还方便!”职场人更懂这...

电脑解压缩软件推荐——7-Zip:免费、高效、简洁的文件管理神器

在日常工作中,我们经常需要处理压缩文件。无论是下载软件包、接收文件,还是存储大量数据,压缩和解压缩文件都成为了我们日常操作的一部分。而说到压缩解压软件,7-Zip绝对是一个不可忽视的名字。今天,我就来...

设置了加密密码zip文件要如何打开?这几个方法可以试试~

Zip是一种常见的压缩格式文件,文件还可以设置密码保护。那设置了密码的Zip文件要如何打开呢?不清楚的小伙伴一起来看看吧。当我们知道密码想要打开带密码的Zip文件,我们需要用到适用于Zip格式的解压缩...

大文件想要传输成功,怎么把ZIP文件分卷压缩

不知道各位小伙伴有没有这样的烦恼,发送很大很大的压缩包会受到限制,为此,想要在压缩过程中将文件拆分为几个压缩包并且同时为所有压缩包设置加密应该如何设置?方法一:使用7-Zip免费且强大的文件管理工具7...

高效处理 RAR 分卷压缩包:合并解压操作全攻略

在文件传输和存储过程中,当遇到大文件时,我们常常会使用分卷压缩的方式将其拆分成多个较小的压缩包,方便存储和传输。RAR作为一种常见的压缩格式,分卷压缩包的使用频率也很高。但很多人在拿到RAR分卷...

2个方法教你如何删除ZIP压缩包密码

zip压缩包设置了加密密码,每次解压文件都需要输入密码才能够顺利解压出文件,当压缩包文件不再需要加密的时候,大家肯定想删除压缩包密码,或是忘记了压缩包密码,想要通过删除操作将压缩包密码删除,就能够顺利...

速转!漏洞预警丨压缩软件Winrar目录穿越漏洞

WinRAR是一款功能强大的压缩包管理器,它是档案工具RAR在Windows环境下的图形界面。该软件可用于备份数据,缩减电子邮件附件的大小,解压缩从Internet上下载的RAR、ZIP及其它类...

文件解压方法和工具分享_文件解压工具下载

压缩文件减少文件大小,降低文件失效的概率,总得来说好处很多。所以很多文件我们下载下来都是压缩软件,很多小伙伴不知道怎么解压,或者不知道什么工具更好,所以今天做了文件解压方法和工具的分享给大家。一、解压...

[python]《Python编程快速上手:让繁琐工作自动化》学习笔记3

1.组织文件笔记(第9章)(代码下载)1.1文件与文件路径通过importshutil调用shutil模块操作目录,shutil模块能够在Python程序中实现文件复制、移动、改名和删除;同时...

Python内置tarfile模块:读写 tar 归档文件详解

一、学习目标1.1学习目标掌握Python内置模块tarfile的核心功能,包括:理解tar归档文件的原理与常见压缩格式(gzip/bz2/lzma)掌握tar文件的读写操作(创建、解压、查看、过滤...

使用python展开tar包_python拓展

类Unix的系统,打包文件经常使用的就是tar包,结合zip工具,可以方便的打包并解压。在python的标准库里面有tarfile库,可以方便实现生成了展开tar包。使用这个库最大的好处,可能就在于不...

银狐钓鱼再升级:白文件脚本化实现GO语言后门持久驻留

近期,火绒威胁情报中心监测到一批相对更为活跃的“银狐”系列变种木马。火绒安全工程师第一时间获取样本并进行分析。分析发现,该样本通过阿里云存储桶下发恶意文件,采用AppDomainManager进行白利...

ZIP文件怎么打开?2个简单方法教你轻松搞定!

在日常工作和生活中,我们经常会遇到各种压缩文件,其中最常见的格式之一就是ZIP。ZIP文件通过压缩数据来减少文件大小,方便我们进行存储和传输。然而,对于初学者来说,如何打开ZIP文件可能会成为一个小小...

Ubuntu—解压多个zip压缩文件.zip .z01 .z02

方法将所有zip文件放在同一目录中:zip_file.z01,zip_file.z02,zip_file.z03,...,zip_file.zip。在Zip3.0版本及以上,使用下列命令:将所有zi...

如何使用7-Zip对文件进行加密压缩

7-Zip是一款开源的文件归档工具,支持多种压缩格式,并提供了对压缩文件进行加密的功能。使用7-Zip可以轻松创建和解压.7z、.zip等格式的压缩文件,并且可以通过设置密码来保护压缩包中的...

取消回复欢迎 发表评论: