銀狐NetDevOps-網(wǎng)絡(luò)運維Python初篇(六)協(xié)程gevent+netmiko,10秒并發(fā)備份上百臺華為網(wǎng)絡(luò)配置

科技銀狐

銀狐DevNet系列會持續(xù)將網(wǎng)絡(luò)運維工作中python的應(yīng)用進行場景化的分享胯杭,因為每個單獨的模塊網(wǎng)上都有詳細的教學(xué),這里就不深入講解模塊基礎(chǔ)了受啥,內(nèi)容主要以思路和示例為主做个,并將碰到的問題匯總提出注意事項。

主要是因為網(wǎng)絡(luò)工程師和網(wǎng)絡(luò)運維工作者編程基礎(chǔ)不強滚局,加上網(wǎng)上對于這個領(lǐng)域的python資料又少居暖,傳統(tǒng)的分享方式(每個章節(jié)僅單純分享一個知識點)對于很多網(wǎng)工來說各個知識點相對獨立且割裂的,很難進行一個知識的融合核畴,現(xiàn)實工作中也很難直接應(yīng)用膝但,大家學(xué)習(xí)的難度就會很大,也會導(dǎo)致大部分人剛?cè)腴T就放棄谤草。所以我將這些內(nèi)容進行場景化跟束,根據(jù)特定場景由淺入深不斷優(yōu)化莺奸,從而帶出更多知識點,希望對大家有所幫助冀宴。

這些分享都是我本人真實的學(xué)習(xí)路徑灭贷,一方面是幫助自己梳理網(wǎng)絡(luò)自動化相關(guān)知識,另一方面也希望可以通過分享我微不足道的學(xué)習(xí)過程和實戰(zhàn)經(jīng)驗略贮,幫助更多想要了解NetDevOps而苦于沒有資料和環(huán)境被勸退的人甚疟,進而找到同行之人互相交流、互相提升逃延。


1览妖、使用場景:

前面5個小節(jié)由淺入深分享了調(diào)用excel內(nèi)容,通過netmiko批量抓取華為網(wǎng)絡(luò)設(shè)備配置揽祥,并存入本地讽膏。目前來看已可以正常在企業(yè)內(nèi)進行配置備份工作,但現(xiàn)實情況下有個不可避免的問題拄丰,如果要批量備份大量設(shè)備府树,加入1臺設(shè)備需要10秒,那100臺設(shè)備豈不是需要1000秒料按,那如果有上千奄侠、上萬臺設(shè)備怎么辦呢?雖然自動化方式比人為備份強很多载矿,但效率還是太低了垄潮,所以在現(xiàn)實網(wǎng)絡(luò)運維場景中,批量操作一般需要結(jié)合并發(fā)操作恢准,這也是本小節(jié)的核心內(nèi)容魂挂。

2、知識講解

高并發(fā)操作網(wǎng)上有很多內(nèi)容馁筐,但都說的很官方涂召,看完并不知道再說些什么。所以我盡可能用大白話簡單解釋一下核心概念敏沉,什么是進程果正、線程、協(xié)程盟迟,同步秋泳、異步、并發(fā)攒菠、并行迫皱?有什么區(qū)別?

模式一:試想工廠里有一條生產(chǎn)線做手機,第一個人負責(zé)裝電池卓起,第二個人負責(zé)裝芯片和敬,第三個人負責(zé)裝屏幕。每個人只負責(zé)自己的一項任務(wù)戏阅,這樣分工明確的方式肯定比一個人干的效率高昼弟,這樣的模式就是單進程、多線程方式奕筐。

模式二:這時工廠增加了一條生產(chǎn)線舱痘,還是同樣的配置,第一個人負責(zé)裝電池离赫,第二個人負責(zé)裝芯片芭逝,第三個人負責(zé)裝屏幕。兩條線同時工作生產(chǎn)手機笆怠,這就叫多進程铝耻、多線程方式。

模式三:讓我們把視角重新拉回單條生產(chǎn)線蹬刷,這里會發(fā)現(xiàn)一個問題,如果第一個人工作沒完成频丘,第二個人是沒法工作的办成,他必須處于等待狀態(tài),就算第一個人工作速度足夠快搂漠,第二個人也會有等待時間迂卢,只是很短暫而已,這就叫同步桐汤。也就是處理任務(wù)必須一個一個來而克,單個任務(wù)處理的再快,也是有等待時間的怔毛。如果我們不讓工人閑著员萍,第一個人處理任務(wù)的時候,第二個人不閑著拣度,而是處理其他事情(工人只要進入等待狀態(tài)就干其他任務(wù))碎绎,最后會發(fā)現(xiàn)同樣的時間有更多的產(chǎn)出,這種方式就叫協(xié)程抗果,也就是我們說的異步筋帖。

進程就是我們CPU的核數(shù),有多少核理論上就可以有多少進程冤馏,2個進程同時工作就是2條生產(chǎn)線日麸,相互之間沒有依賴關(guān)系,你干你的逮光,我干我的代箭,這種模式就是并行墩划,2個CPU可以實現(xiàn)真正的同時處理任務(wù)

當(dāng)我們啟動py腳本的時候梢卸,就會啟動一個進程走诞,進程會啟動線程,線程是依賴進程的蛤高,單進程多線程工作是有前后依賴關(guān)系的蚣旱,第一個工作做完工作,第二個工作才能繼續(xù)戴陡,同一個任務(wù)項無論他們操作的速度有多快塞绿,他們永遠無法并行工作的,而是有個前后等待關(guān)系恤批,這就叫并發(fā)异吻。協(xié)程只是線程的優(yōu)化,把等待時間利用上喜庞,但也無法實現(xiàn)真正意義上同時任務(wù)诀浪,所以也是并發(fā)。

總結(jié):

進程是種資源的分配單位延都,實際上做事情的是線程雷猪,線程進行優(yōu)化就變成了協(xié)程。多進程可實現(xiàn)真正意義上的并行晰房,但成本很高求摇,換句話說就是需要資源很多,所以網(wǎng)絡(luò)運維一般使用協(xié)程就好殊者。經(jīng)過測試抓取100臺華為設(shè)備配置并進行本地測試需要的時間12秒与境,幾百臺的時間也不會超過20秒,所以簡單的批量操作使用協(xié)程就好猖吴,如果是比較復(fù)雜的跑批操作可以使用進程+協(xié)程的組合方式摔刁,大部分企業(yè)用不上的。

3距误、實驗環(huán)境:

操作系統(tǒng):Linux CentOS 7.4

python版本:python 3.8

網(wǎng)絡(luò)設(shè)備:華為CE 6865

編輯器:vscode(pycharm簸搞、sublime均可,推薦vscode)

excel格式:初次使用簡單一些准潭,excel中只加入IP地址

image.png

4趁俊、思路分析

批量操作直接使用協(xié)程就好,不用考慮進程和線程刑然,線程有很多庫asyncio 寺擂、gevent之類的,個人不推薦什么模塊都研究,很浪費時間怔软,不是開發(fā)人員不用研究太深垦细,哪個方便好用就用哪個,個人推薦gevent挡逼,方便好用而且不用改源代碼括改。有些研發(fā)人員會將gevent的一些缺陷,但其實運維場景中沒什么影響家坎,不用在意嘱能。

gevent也有很多使用方式,可以查看文檔虱疏,這里就推薦我常用的方式惹骂,gevent.map,這種方式可以控制并發(fā)量做瞪,比較靈活对粪,防止CPU動不動干到100%。

gevent批量備份配置的思路装蓬,比如我們要備份100臺設(shè)備著拭,其實是登錄100臺設(shè)備,然后輸出命令抓取配置牍帚,并將回顯內(nèi)容寫入本地文件茫死。當(dāng)我們SSH第一臺設(shè)備時,其實會有一個等待時間履羞,這時不要讓程序等待,而是直接SSH第二臺設(shè)備屡久,以此類推忆首。可能SSH第10臺網(wǎng)絡(luò)設(shè)備時被环,第一臺設(shè)備配置回顯了糙及,這些任務(wù)流雖然有前后順序,但切換的速度非常非常塊筛欢,所以會給我們一種同時連接100臺設(shè)備并寫入文件的錯覺浸锨,所以我們操作1臺設(shè)備的時間和操作100臺設(shè)備的時間,其實差距不會很大版姑。這就是為什么我說100臺設(shè)備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 + ': 網(wǎng)絡(luò)不可達 ')
        issue_device.append(issue_message)

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

    except Exception as unknown_error:
        issue_message = (ipaddr +': 發(fā)生未知錯誤: ')
        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聪蘸、代碼詳解

大部分內(nèi)容都是迭代上一小節(jié)的內(nèi)容,只講解新增的協(xié)程部分。

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

導(dǎo)入模塊復(fù)制粘貼就好健爬,monkey.patch_all()是為了不修改源代碼直接使用協(xié)程功能的,這么理解比較簡單。

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礁蔗,主要是為了控制下并發(fā)數(shù),別給本地機器帶來太大壓力设拟。

pool.map(第一個參數(shù)是執(zhí)行的函數(shù)慨仿,第二個參數(shù)是可迭代的數(shù)據(jù))

run_gevent函數(shù)也是上一個小節(jié)的內(nèi)容,只是我把需要執(zhí)行的函數(shù)從main函數(shù)中剝離蒜绽,重新定義了一個函數(shù)镶骗。可迭代的數(shù)據(jù)就是能被for循環(huán)的躲雅,list和tuple都屬于可迭代鼎姊。

這里我們用pool.map調(diào)用run_gevent函數(shù)(真正的主程序任務(wù)),并把IP地址列表傳遞進去相赁,就會自動迭代每個IP地址到run_gevent函數(shù)的任務(wù)里相寇。


到此為止,抓取設(shè)備配置這個小場景就可以在現(xiàn)實環(huán)境中應(yīng)用了钮科,但還遠遠不止于此唤衫,還有很多內(nèi)容可以優(yōu)化和使用,我也會在后續(xù)持續(xù)分享绵脯。比如如何加入類和方法佳励,什么是封裝和繼承,如何一次性批量抓取Juniper蛆挫、華為赃承、H3C、Cisco不同廠商的配置悴侵,有的廠商不支持netmiko怎么辦瞧剖?跑批1000臺設(shè)備靠協(xié)程,復(fù)雜任務(wù)跑批1萬臺設(shè)備時如何使用進程+協(xié)程可免?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抓于,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子浇借,更是在濱河造成了極大的恐慌捉撮,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逮刨,死亡現(xiàn)場離奇詭異呕缭,居然都是意外死亡堵泽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門恢总,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迎罗,“玉大人,你說我怎么就攤上這事片仿∥瓢玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵砂豌,是天一觀的道長厢岂。 經(jīng)常有香客問我,道長阳距,這世上最難降的妖魔是什么塔粒? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮筐摘,結(jié)果婚禮上卒茬,老公的妹妹穿的比我還像新娘。我一直安慰自己咖熟,他們只是感情好圃酵,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著馍管,像睡著了一般郭赐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上确沸,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天捌锭,我揣著相機與錄音,去河邊找鬼罗捎。 笑死舀锨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宛逗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼盾剩,長吁一口氣:“原來是場噩夢啊……” “哼雷激!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起告私,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屎暇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驻粟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體根悼,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡凶异,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挤巡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剩彬。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖矿卑,靈堂內(nèi)的尸體忽然破棺而出喉恋,到底是詐尸還是另有隱情,我是刑警寧澤母廷,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布轻黑,位于F島的核電站,受9級特大地震影響琴昆,放射性物質(zhì)發(fā)生泄漏氓鄙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一业舍、第九天 我趴在偏房一處隱蔽的房頂上張望抖拦。 院中可真熱鬧,春花似錦勤讽、人聲如沸蟋座。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽向臀。三九已至,卻和暖如春诸狭,著一層夾襖步出監(jiān)牢的瞬間券膀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工驯遇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芹彬,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓叉庐,卻偏偏與公主長得像舒帮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子陡叠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容