银狐NetDevOps-网络运维Python初篇(六)协程gevent+netmiko,10秒并发备份上百台华为网络配置

科技银狐

银狐DevNet系列会持续将网络运维工作中python的应用进行场景化的分享,因为每个单独的模块网上都有详细的教学,这里就不深入讲解模块基础了,内容主要以思路和示例为主,并将碰到的问题汇总提出注意事项。

主要是因为网络工程师和网络运维工作者编程基础不强,加上网上对于这个领域的python资料又少,传统的分享方式(每个章节仅单纯分享一个知识点)对于很多网工来说各个知识点相对独立且割裂的,很难进行一个知识的融合,现实工作中也很难直接应用,大家学习的难度就会很大,也会导致大部分人刚入门就放弃。所以我将这些内容进行场景化,根据特定场景由浅入深不断优化,从而带出更多知识点,希望对大家有所帮助。

这些分享都是我本人真实的学习路径,一方面是帮助自己梳理网络自动化相关知识,另一方面也希望可以通过分享我微不足道的学习过程和实战经验,帮助更多想要了解NetDevOps而苦于没有资料和环境被劝退的人,进而找到同行之人互相交流、互相提升。


1、使用场景:

前面5个小节由浅入深分享了调用excel内容,通过netmiko批量抓取华为网络设备配置,并存入本地。目前来看已可以正常在企业内进行配置备份工作,但现实情况下有个不可避免的问题,如果要批量备份大量设备,加入1台设备需要10秒,那100台设备岂不是需要1000秒,那如果有上千、上万台设备怎么办呢?虽然自动化方式比人为备份强很多,但效率还是太低了,所以在现实网络运维场景中,批量操作一般需要结合并发操作,这也是本小节的核心内容。

2、知识讲解

高并发操作网上有很多内容,但都说的很官方,看完并不知道再说些什么。所以我尽可能用大白话简单解释一下核心概念,什么是进程、线程、协程,同步、异步、并发、并行?有什么区别?

模式一:试想工厂里有一条生产线做手机,第一个人负责装电池,第二个人负责装芯片,第三个人负责装屏幕。每个人只负责自己的一项任务,这样分工明确的方式肯定比一个人干的效率高,这样的模式就是单进程、多线程方式。

模式二:这时工厂增加了一条生产线,还是同样的配置,第一个人负责装电池,第二个人负责装芯片,第三个人负责装屏幕。两条线同时工作生产手机,这就叫多进程、多线程方式。

模式三:让我们把视角重新拉回单条生产线,这里会发现一个问题,如果第一个人工作没完成,第二个人是没法工作的,他必须处于等待状态,就算第一个人工作速度足够快,第二个人也会有等待时间,只是很短暂而已,这就叫同步。也就是处理任务必须一个一个来,单个任务处理的再快,也是有等待时间的。如果我们不让工人闲着,第一个人处理任务的时候,第二个人不闲着,而是处理其他事情(工人只要进入等待状态就干其他任务),最后会发现同样的时间有更多的产出,这种方式就叫协程,也就是我们说的异步

进程就是我们CPU的核数,有多少核理论上就可以有多少进程,2个进程同时工作就是2条生产线,相互之间没有依赖关系,你干你的,我干我的,这种模式就是并行,2个CPU可以实现真正的同时处理任务

当我们启动py脚本的时候,就会启动一个进程,进程会启动线程,线程是依赖进程的,单进程多线程工作是有前后依赖关系的,第一个工作做完工作,第二个工作才能继续,同一个任务项无论他们操作的速度有多快,他们永远无法并行工作的,而是有个前后等待关系,这就叫并发。协程只是线程的优化,把等待时间利用上,但也无法实现真正意义上同时任务,所以也是并发。

总结:

进程是种资源的分配单位,实际上做事情的是线程,线程进行优化就变成了协程。多进程可实现真正意义上的并行,但成本很高,换句话说就是需要资源很多,所以网络运维一般使用协程就好。经过测试抓取100台华为设备配置并进行本地测试需要的时间12秒,几百台的时间也不会超过20秒,所以简单的批量操作使用协程就好,如果是比较复杂的跑批操作可以使用进程+协程的组合方式,大部分企业用不上的。

3、实验环境:

操作系统:Linux CentOS 7.4

python版本:python 3.8

网络设备:华为CE 6865

编辑器:vscode(pycharm、sublime均可,推荐vscode)

excel格式:初次使用简单一些,excel中只加入IP地址

image.png

4、思路分析

批量操作直接使用协程就好,不用考虑进程和线程,线程有很多库asyncio 、gevent之类的,个人不推荐什么模块都研究,很浪费时间,不是开发人员不用研究太深,哪个方便好用就用哪个,个人推荐gevent,方便好用而且不用改源代码。有些研发人员会将gevent的一些缺陷,但其实运维场景中没什么影响,不用在意。

gevent也有很多使用方式,可以查看文档,这里就推荐我常用的方式,gevent.map,这种方式可以控制并发量,比较灵活,防止CPU动不动干到100%。

gevent批量备份配置的思路,比如我们要备份100台设备,其实是登录100台设备,然后输出命令抓取配置,并将回显内容写入本地文件。当我们SSH第一台设备时,其实会有一个等待时间,这时不要让程序等待,而是直接SSH第二台设备,以此类推。可能SSH第10台网络设备时,第一台设备配置回显了,这些任务流虽然有前后顺序,但切换的速度非常非常块,所以会给我们一种同时连接100台设备并写入文件的错觉,所以我们操作1台设备的时间和操作100台设备的时间,其实差距不会很大。这就是为什么我说100台设备12秒,500台也不会超过20秒。

5、整体代码

#!/usr/bin/env python
#coding: utf-8

import os
from time import time
from datetime import datetime
from netmiko import ConnectHandler
from openpyxl import Workbook
from openpyxl import load_workbook
import gevent
from gevent import spawn
from gevent import monkey;monkey.patch_all()
from gevent.pool import Pool
from netmiko.ssh_exception import NetMikoTimeoutException
from netmiko.ssh_exception import AuthenticationException
from paramiko.ssh_exception import SSHException

def read_device_excel( ):

    ip_list = []

    wb1 = load_workbook('/home/netops/venv/cs_lab.xlsx')
    ws1 = wb1.get_sheet_by_name("Sheet1")

    for cow_num in range(2,ws1.max_row+1):

        ipaddr = ws1["a"+str(cow_num)].value
        ip_list.append(ipaddr)

    return ip_list

def get_config(ipaddr):

    session = ConnectHandler(device_type="huawei",
                            ip=ipaddr,
                            username="root",
                            password="root",
                            banner_timeout=300)

    print("connecting to "+ ipaddr)
    print ("---- Getting HUAWEI configuration from {}-----------".format(ipaddr))

    config_data = session.send_command("dis cu")

    session.disconnect()

    return config_data

def write_config_to_file(config_data,ipaddr):
    now = datetime.now()
    date= "%s-%s-%s"%(now.year,now.month,now.day)
    time_now = "%s-%s"%(now.hour,now.minute)

    #---- Write out configuration information to file
    config_path = '/home/netops/linsy_env/devconfig/' +date
    verify_path = os.path.exists(config_path)
    if not verify_path:
        os.makedirs(config_path)

    config_filename = config_path+"/"+'config_' + ipaddr +"_"+date+"_" + time_now # Important - create unique configuration file name

    print ('---- Writing configuration: ', config_filename)
    with open( config_filename, "w",encoding='utf-8' ) as config_out:  
        config_out.write( config_data )

    return

def write_issue_device(issue_device):
    now = datetime.now()
    date= "%s-%s-%s"%(now.year,now.month,now.day)
    time_now = "%s-%s"%(now.hour,now.minute)

    config_path = '/home/netops/venv/' + "issue_" + date
    verify_path = os.path.exists(config_path)
    if not verify_path:
        os.makedirs(config_path)

    config_filename = config_path+"/"+'issue_'+date+"_" + time_now
    print ('---- Writing issue: ', config_filename)
    with open (config_filename, "w", encoding='utf-8') as issue_facts:
        issue_facts.write('\n'.join(issue_device))

def run_gevent(ipaddr):

    issue_device = []

    try:
        hwconfig = get_config(ipaddr)
        write_config_to_file(hwconfig,ipaddr)

    except (AuthenticationException):
        issue_message = (ipaddr + ': 认证错误 ')
        issue_device.append(issue_message)

    except NetMikoTimeoutException:
        issue_message = (ipaddr + ': 网络不可达 ')
        issue_device.append(issue_message)

    except (SSHException):
        issue_message = (ipaddr +': SSH端口异常 ')
        issue_device.append(issue_message)

    except Exception as unknown_error:
        issue_message = (ipaddr +': 发生未知错误: ')
        issue_device.append(issue_message+str(unknown_error))

    finally:
        write_issue_device(issue_device)                  #异常处理信息写入文件

def main():

    starting_time = time()   
    ip_list = read_device_excel()

    pool = Pool(100)
    pool.map(run_gevent,ip_list)                                 #map(func, iterable)
    pool.join()
    print ('\n---- End get config threading, elapsed time=', time() - starting_time)

#========================================
# Get config of HUAWEI
#========================================
if __name__ == '__main__':
    main()

6、代码详解

大部分内容都是迭代上一小节的内容,只讲解新增的协程部分。

from gevent import spawn
from gevent import monkey;monkey.patch_all()
from gevent.pool import Pool

导入模块复制粘贴就好,monkey.patch_all()是为了不修改源代码直接使用协程功能的,这么理解比较简单。

def main():

    starting_time = time()   
    ip_list = read_device_excel()

    pool = Pool(100)
    pool.map(run_gevent,ip_list)                                 #map(func, iterable)
    pool.join()
    print ('\n---- End get config threading, elapsed time=', time() - starting_time)

首先定义一个pool,我一般使用50或者100,主要是为了控制下并发数,别给本地机器带来太大压力。

pool.map(第一个参数是执行的函数,第二个参数是可迭代的数据)

run_gevent函数也是上一个小节的内容,只是我把需要执行的函数从main函数中剥离,重新定义了一个函数。可迭代的数据就是能被for循环的,list和tuple都属于可迭代。

这里我们用pool.map调用run_gevent函数(真正的主程序任务),并把IP地址列表传递进去,就会自动迭代每个IP地址到run_gevent函数的任务里。


到此为止,抓取设备配置这个小场景就可以在现实环境中应用了,但还远远不止于此,还有很多内容可以优化和使用,我也会在后续持续分享。比如如何加入类和方法,什么是封装和继承,如何一次性批量抓取Juniper、华为、H3C、Cisco不同厂商的配置,有的厂商不支持netmiko怎么办?跑批1000台设备靠协程,复杂任务跑批1万台设备时如何使用进程+协程?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容