网络工程师的python之路


  最近有个需求,需要重复的对某几台防火墙进行某些操作,为了让不懂网络设备的同事也能一键操作,尝试了用python开发脚本,TODO:后续可以考虑弄个web界面,更友好。

  一开始使用netmiko模块,1台设备执行1条命令需要20-30秒,N台设备*N,况且命令条数也是动态的,这样操作怕不是还没有手动快。同步不行就异步吧,后来研究了下netdev模块,功能实现后发现一个致命问题,截止目前为止netdev还不支持huawei的操作系统(已经向作者提交issue),没办法还是换回netmiko。

  9.14更新,为netdev库增加了华为设备的支持,服务端用aiohttp,改为异步并发,速度快到不知哪去了(已经向作者提交PR)。后面有空再深入了解下asyncio~~

  没法异步,要提高处理速度只有多线程,能异步当然还是异步快 为了避免线程间数据同步以及复杂情况下线程锁的问题,最后用1个线程操作1台设备的方法,代码如下:

from netmiko import ConnectHandler
import time
from queue import Queue
import threading

def fengdu_hw(device,output_q):
    try:
        net_connect = ConnectHandler(**device)
        file = open('./ip_list.txt')
        for ip in file.readlines():
            ip = ip.strip()
            #要配置的命令
            config_commands = ['ip address-set fengdu_20200821_1',
                               'address '+ip+' 0']
            #提交要配置的命令,input为提交的真实内容
            input = net_connect.send_config_set(config_commands)
    except:
        print(f"{ip} 操作失败,请检查!")
    else:
        print(f"{ip} 操作成功!")
    return

def fengdu_h3c(device,output_q):
    try:
        net_connect = ConnectHandler(**device)
        file = open('./ip_list.txt')
        for ip in file.readlines():
            ip = ip.strip()
            #要配置的命令
            config_commands = ['object-group ip address fengdu_20200821_1',
                               'network host address '+ip]
            #提交要配置的命令,input为提交的真实内容
            input = net_connect.send_config_set(config_commands)
    except:
        print(f"{ip} 操作失败,请检查!")
    else:
        print(f"{ip} 操作成功!")
    return

hw1 = {
    'device_type':'huawei',
    'host':'X.X.X.X',
    'username':'xxxxx',
    'password':'xxxxx'
}
hw2 = {
    'device_type':'huawei',
    'host':'X.X.X.X',
    'username':'xxxxx',
    'password':'xxxxx'
}
hw3 = {
    'device_type':'huawei',
    'host':'X.X.X.X',
    'username':'xxxxx',
    'password':'xxxxx'
}
h3c = {
    'device_type':'hp_comware',
    'host':'X.X.X.X',
    'username':'xxxxx',
    'password':'xxxxx'
}


print(f"操作于 {time.strftime('%X')} 开始执行\n")

t1 = threading.Thread(target=fengdu_hw, args=(hw1,Queue()))
t2 = threading.Thread(target=fengdu_hw, args=(hw2,Queue()))
t3 = threading.Thread(target=fengdu_hw, args=(hw3,Queue()))
t4 = threading.Thread(target=fengdu_h3c, args=(h3c,Queue()))

threads = [t1,t2,t3,t4]

for t in threads:
    t.start()

for t in threads:
    t.join()

print(f"\n操作于 {time.strftime('%X')} 执行结束")

  join方法是个很tricky的东西,至今还不是很清楚地懂这是个什么玩意儿。join([timeout])方法阻塞了主线程,直到调用此方法的子线程完成之后主线程才继续往下运行。(之前我糊里糊涂地把join就紧紧接在start后面写了,如果这么写了的话那么多线程在速度上就毫无优势,和单线程一样了= =)。而像上面这个示例一样,先一个遍历把所有线程都启动起来,再用一个遍历把所有线程都join一遍似乎是比较通行的做法。