我们之前玩转网络自动化之路系列文章中介绍了通过Netmiko模块批量操作设备,有尝试过的读者是不是发现了如果设备非常多(2000台以上),发现批量执行要花费很多时间,小编之前对500台设备批量增加syslog服务器配置并保存配置居然花了200多秒(该下发任务还加入了多线程的优化),如果有5000台设备那么至少需要1000多秒。这样长的执行时间大多数人是无法忍受的。 为了解决该问题,小编今天为大家带来一个可以大大提高并发性能与执行效率的Python异步模块netdev,下面我们就一起来学习吧! netdev模块是通过AsyncSSH模块来建立SSH连接,模拟命令行登录来操作网络设备,该模块内部的逻辑是仿造Netmiko来开发的。该模块必须在Python3.5之后的版本才可以运行,目前该模块支持世界上主流的网络设备厂家,具体如下: (1)异步模块介绍 在使用netdev模块之前我们先来了解一下什么是异步(Asynchronous),什么是同步(Synchronous)。异步是指程序在执行一段代码或者函数后,不要等待该段代码的返回值会继续执行下段代码与模块。同步(Synchronous)需要等待执行结果才能继续下一段代码或者函数。 asyncio是目前python最常用的异步模块(netdev模块也是通过该异步模块框架实现了网络设备的异步操作),该模块内置于python3.4之后的版本中不需要安装,asynico模块常用于高并发的IO密集型场景。asynico模块通过并发运行python协程(协程就是在一个线程上并发的微线程,英文名Coroutine,可以通过程序来自己控制调度多任务的切换要线程一般都是由系统来控制)并对其执行过程完全控制来实现程序的并发。 下面我们来看看asynico模块使用的代码示例: 如上协程函数main()是不能够直接运行的,如下所示: 协程函数需要通过asyncio.run()或者asyncio.create_task()方法来执行,asyncio.create_task()方法用来并发运行作为 asyncio任务的多个协程。我们先来看看asyncio.run()方法使用示例: 执行结果如下,程序是执行了3秒: 接着我们来看看通过asyncio.create_task()方法来执行的代码示例: 通过asyncio.create_task()方法来执行的结果如下,确实程序一共执行了2秒。 我们异步模块先讲解到这,如果有感兴趣的读者欢迎给我们留言或私信,如果人比较多的话我们可以出一篇专门讲解异步模块的文章。 (2)netdev模块具体使用 现在我们来看看netdev的具体使用吧,我们通过asyncio.create_task()方法调用netdev模块来对两台H3C设备进行并发的接口内容获取,这边需要注意一下netdev的H3C操作类的代码中查找prompt标识的正则变量存在bug,读者们可以跟代码示例中一样进行修改,也可以通过自己编写后补丁进行修复,代码示例如下: 执行结果如下: 直接从上面数据,大家是不是看不出来较netmiko模块有多大的效率提升,现在我们就通过多线程调用netmiko来进行相同的操作看看大概要多少时间,代码示例如下: 运行结果如下: 多线程池配合netmiko我们一共花费了10.3秒,而通过netdev来执行相同操作我们才花费了2.5秒,这还是在两台设备的情况下,随着设备的数量越来越多,netdev模块会体现出更高的执行效率,看到这是不是有不少读者心动了,心动不如行动,赶快用起来吧! 好了,今天我们netdev模块就讲到这,接下来我们玩转系列将讲ncclient模块,该模块是python操作netconf的利器,请大家持续关注我们的后续内容哦!最后如果喜欢本章内容请不要忘了进行点赞、关注与转发哦! --喜欢的同学请点击@IT管理局关注哦-- 本局精彩文章:01 模块介绍
1. Cisco IOS
2. Cisco IOS XE
3. Cisco IOS XR
4. Cisco ASA
5. Cisco NX-OS
6. Cisco SG3XX
7. HP Comware (like V1910 too) #就是H3C设备
8. Fujitsu Blade Switches
9. Mikrotik RouterOS
10. Arista EOS
11. Juniper JunOS
12. Aruba AOS 6.X
13. Aruba AOS 8.X
14. Terminal
15. Alcatel AOS
02 模块安装
01 #netdev模块安装比较简单,具体安装命令如下:
02 pip install netdev
03 #如果出现'Read timed out'可以用阿里云镜像安装,具体命令如下:
04 pip install -i https://mirrors.aliyun.com/pypi/simple/ netdev
03 模块使用
1. >>> import asyncio
2.
3. >>> async def main(): #以async/await语法开头的声明我们一般称之为协程或者协程函数,是编写异步应用的推荐方式
4. ... print('hello')
5. ... await asyncio.sleep(1)
6. ... print('world')
7.
8. >>> asyncio.run(main())
9. hello
10. world
1. >>> main() #不能直接运行返回的协程对象不是运行结果
2. <coroutine object main at 0x1053bb7c8>
1. #!/usr/bin/env/ python
2. # -*- coding:utf-8 -*-
3.
4. import asyncio
5. import time
6.
7. async def say_after(delay, what):
8. await asyncio.sleep(delay)
9. print(what)
10.
11. async def main():
12. print(f"started at {time.strftime('%X')}")
13.
14. #这边await语法后面是协程对象,这边是按顺序执行,所以我们看到结果程序应该是执行了3秒
15. await say_after(1, 'hello')
16. await say_after(2, 'world')
17.
18. print(f"finished at {time.strftime('%X')}")
19.
20. if __name__ == "__main__":
21. asyncio.run(main())
1. D:\env\python3_env\Scripts\python3.exe E:/it_code/aysnc_task_test.py
2. started at 17:32:01
3. hello
4. world
5. finished at 17:32:04
6.
7. Process finished with exit code 0
1. #!/usr/bin/env/ python
2. # -*- coding:utf-8 -*-
3.
4. import asyncio
5. import time
6.
7. async def say_after(delay, what):
8. await asyncio.sleep(delay)
9. print(what)
10.
11. async def main():
12. task1 = asyncio.create_task(
13. say_after(1, 'hello'))
14.
15. task2 = asyncio.create_task(
16. say_after(2, 'world'))
17.
18. print(f"started at {time.strftime('%X')}")
19.
20. #这边await后面跟的是task对象,task1与task2是并行执行的,所以总执行时间应该为单个执行时间最长的那个任务
21. #这边我们给task2传的时间为2秒,所以总执行时间为2秒
22. await task1
23. await task2
24.
25. print(f"finished at {time.strftime('%X')}")
26.
27. if __name__ == "__main__":
28. asyncio.run(main())
1. D:\env\python3_env\Scripts\python3.exe E:/it_code/aysnc_task_test.py
2. started at 18:04:50
3. hello
4. world
5. finished at 18:04:52
6.
7. Process finished with exit code 0
1. #!/usr/bin/env/ python
2. # -*- coding:utf-8 -*-
3.
4. import asyncio
5. import netdev
6. import time
7.
8. from netdev.vendors.comware_like import ComwareLikeDevice
9.
10. #h3c设备的源代码中找prompt的正则变量_pattern有BUG, 需要改成如下
11. #源代码中正则变量_pattern值为"[{delimiter_left}]{prompt}[\-\w]*[{delimiter_right}]"
12. ComwareLikeDevice._pattern = r"{delimiter_left}{prompt}[\-\w]*{delimiter_right}"
13.
14. async def get_interface(**kwargs):
15. #通过netdev.create()创建ssh连接对象
16. async with netdev.create(**kwargs) as con:
17. #发送方法跟netmiko是一样的都是send_command()方法, 注意这边等待执行结果要通过await来声明
18. #await后面跟的一般叫等待对象,一般可以是协程/任务/Feature这三个类型
19. result = await con.send_command("dis ip int br")
20. print("设备({})完成执行,输出内容如下: ".format(kwargs["host"]))
21. print(result)
22. return result
23.
24.
25. async def main(args_list):
26. #通过asyncio.create_task()方法来创建并行任务
27. task_list = [asyncio.create_task(get_interface(**args)) for args in args_list]
28. for task in task_list:
29. await task
30.
31.
32. if __name__ == "__main__":
33. start = time.time()
34.
35. kwargs_list = [
36. {
37. "host": "10.1.1.1",
38. "username": "admin",
39. "password": "admin",
40. "device_type": "hp_comware"
41. },
42. {
43. "host": "10.1.1.2",
44. "username": "admin",
45. "password": "admin",
46. "device_type": "hp_comware"
47. },
48. ]
49.
50. #通过asyncio.run()来执行main()方法
51. asyncio.run(main(kwargs_list))
52.
53. end = time.time()
54. print(f"一共花费时间为: {end - start}")
1. D:\env\python3_env\Scripts\python3.exe E:/it_code/netdev_test.py
2.
3. 设备(10.1.1.1)完成执行,输出内容如下:
4. *down: administratively down
5. (s): spoofing (l): loopback
6. Interface Physical Protocol IP address VPN instance Description
7. Loop0 up up(s) 2.2.2.2 -- --
8. MGE0/0/0 up up 10.1.1.1 -- --
9. MGE0/0/1 down down -- -- --
10. XGE1/0/1 up up 12.1.1.2 -- --
11. Tun0 up up -- -- --
12. Vsi2010030 up up 192.168.8.6 a --
13. Vsi3023000 up up -- a --
14.
15. 设备(10.1.1.2)完成执行,输出内容如下:
16. *down: administratively down
17. (s): spoofing (l): loopback
18. Interface Physical Protocol IP address VPN instance Description
19. Loop0 up up(s) 1.1.1.1 -- --
20. MGE0/0/0 up up 10.1.1.2 -- --
21. MGE0/0/1 down down -- -- --
22. XGE2/0/1 up up 12.1.1.1 -- --
23. Tun0 up up -- -- --
24. Vsi2010030 up up 192.168.8.6 a --
25. Vsi2010031 down down 192.168.11.254 a --
26. Vsi2020010 down down 192.168.100.254 a --
27. Vsi3023000 up up -- a --
28.
29. 一共花费时间为: 2.521251916885376
1. #!/usr/bin/env/ python
2. # -*- coding:utf-8 -*-
3.
4. import time
5. from netmiko import ConnectHandler
6. from concurrent.futures import ThreadPoolExecutor
7.
8.
9. def task(kwargs):
10. #初始化SSH连接
11. with ConnectHandler(**kwargs) as c:
12. #下发查询命令
13. result = c.send_command("dis ip int br")
14. print("设备({})完成执行,输出内容如下: ".format(kwargs["host"]))
15. print(result)
16. return result
17.
18.
19. if __name__ == "__main__":
20.
21. kwargs_list = [
22. {
23. "host": "10.1.1.1",
24. "username": "admin",
25. "password": "admin",
26. "device_type": "hp_comware"
27. },
28. {
29. "host": "10.1.1.2",
30. "username": "admin",
31. "password": "admin",
32. "device_type": "hp_comware"
33. },
34. ]
35.
36. start = time.time()
37.
38. #通过线程池来先实现程序并发调用
39. with ThreadPoolExecutor(max_workers=60) as pool:
40. for args in kwargs_list:
41. t = pool.submit(task, args)
42.
43. end = time.time()
44.
45. print(f"一共花费时间为: {end - start}")
1. D:\env\python3_env\Scripts\python3.exe E:/it_code/netmiko_test.py
2.
3. 设备(10.1.1.1)完成执行,输出内容如下:
4. *down: administratively down
5. (s): spoofing (l): loopback
6. Interface Physical Protocol IP address VPN instance Description
7. Loop0 up up(s) 2.2.2.2 -- --
8. MGE0/0/0 up up 10.1.1.1 -- --
9. MGE0/0/1 down down -- -- --
10. XGE1/0/1 up up 12.1.1.2 -- --
11. Tun0 up up -- -- --
12. Vsi2010030 up up 192.168.8.6 a --
13. Vsi3023000 up up -- a --
14.
15. 设备(10.1.1.2)完成执行,输出内容如下:
16. *down: administratively down
17. (s): spoofing (l): loopback
18. Interface Physical Protocol IP address VPN instance Description
19. Loop0 up up(s) 1.1.1.1 -- --
20. MGE0/0/0 up up 10.1.1.2 -- --
21. MGE0/0/1 down down -- -- --
22. XGE2/0/1 up up 12.1.1.1 -- --
23. Tun0 up up -- -- --
24. Vsi2010030 up up 192.168.8.6 a --
25. Vsi2010031 down down 192.168.11.254 a --
26. Vsi2020010 down down 192.168.100.254 a --
27. Vsi3023000 up up -- a --
28. 一共花费时间为: 10.32203197479248
29.
30. Process finished with exit code 0