
銀狐DevNet系列會持續(xù)將網(wǎng)絡(luò)運維工作中python的應(yīng)用進行場景化的分享,因為每個單獨的模塊網(wǎng)上都有詳細(xì)的教學(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í)路徑和記錄,因為不是專業(yè)編程人員,難免會出現(xiàn)問題,歡迎大家隨時指正。最后,希望可以通過分享我微不足道的學(xué)習(xí)過程和實戰(zhàn)經(jīng)驗,幫助更多想要學(xué)習(xí)python提升工作效率的人。另一方面也是為了可以找到更多同行之人,互相交流互相提升,祝愿DEVNET行業(yè)發(fā)展的越來越好。
1、訓(xùn)練場景:
讀取excel中設(shè)備IP地址,通過Netmiko抓取設(shè)備配置,并存入本地。代碼加入異常處理,出現(xiàn)異常不影響正常執(zhí)行,并將異常內(nèi)容輸出到本地文件內(nèi),方便后續(xù)查看。
2、實驗環(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地址

3、思路分析
沿用上一章節(jié)代碼,加入異常處理try.....except,這里需要注意,異常處理是有傳遞性的,就是當(dāng)我們執(zhí)行代碼并報錯時,這個異常信息會持續(xù)跟隨,所以當(dāng)我們有多個函數(shù)或者方法時,不需要在每個函數(shù)內(nèi)部使用異常處理try.....except,這樣會很麻煩,最推薦的方式就是在主函數(shù)上統(tǒng)一使用異常處理。
4、整體代碼
#!/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="libb112",
password="3333labcs",
banner_timeout=300)
print("connecting to "+ ipaddr)
print ("---- Getting HUAWEI configuration from {}-----------".format(ipaddr))
# config_data = session.send_command('screen-length 0 temporary')
# config_data = session.send_command('dis cu | no-more ')
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/linsy_env/' + "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 main():
starting_time = time()
issue_device = []
ip_list = read_device_excel()
for ipaddr in ip_list:
try:
hwconfig = get_config(ipaddr)
write_config_to_file(hwconfig,ipaddr)
print ('\n---- End get config threading, elapsed time=', time() - starting_time)
except (AuthenticationException):
issue_message = (ipaddr + ': 認(rèn)證錯誤 ')
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) #異常處理信息寫入文件
#========================================
# Get config of HUAWEI
#========================================
if __name__ == '__main__':
main()
執(zhí)行結(jié)果

5、代碼詳解
read_device_excel、get_config、write_config_to_file三個函數(shù)的講解請直接參考上一個小節(jié),都有詳細(xì)說明。本小節(jié)主要講解新增的write_issue_device函數(shù),和對主函數(shù)main的修改。
銀狐NetDevOps-網(wǎng)絡(luò)運維Python初篇(四)netmiko抓取華為網(wǎng)絡(luò)配置并存入本地
def main():
starting_time = time()
issue_device = [] #定義一個list,收集異常處理信息
ip_list = read_device_excel()
for ipaddr in ip_list:
try: #try后面跟我們的代碼
hwconfig = get_config(ipaddr)
write_config_to_file(hwconfig,ipaddr)
print ('\n---- End get config threading, elapsed time=', time() - starting_time)
except (AuthenticationException): #except為異常處理
issue_message = (ipaddr + ': 認(rèn)證錯誤 ')
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: #無論是否有異常,都執(zhí)行
write_issue_device(issue_device) #異常處理信息寫入文件
1、try后面跟正常的代碼,因為異常是有傳遞性的,就算代碼中有多個函數(shù),也不需要每個函數(shù)里面使用異常處理,而是直接在主函數(shù)統(tǒng)一使用。
2、如果主函數(shù)有for循環(huán),注意異常處理要放在for循環(huán)里面,因為try....except異常處理模塊的邏輯是,當(dāng)我們執(zhí)行代碼報錯時直接跳向except進行異常處理,處理完except之后會去執(zhí)行finally后面的代碼,也就是說不會返回try繼續(xù)執(zhí)行我們后續(xù)代碼。
比如我們for循環(huán)4臺設(shè)備,第一臺設(shè)備SSH登錄就報錯了,直接進入except異常處理,后面3臺相當(dāng)于直接跳過了。
except (AuthenticationException): #捕捉認(rèn)證錯誤異常
issue_message = (ipaddr + ': 認(rèn)證錯誤 ') #捕捉到異常后輸出提示信息,XXX設(shè)備認(rèn)證錯誤
issue_device.append(issue_message) #將提示信息加入前面定義好的list中
except NetMikoTimeoutException:
issue_message = (ipaddr + ': 網(wǎng)絡(luò)不可達 ')
issue_device.append(issue_message)
except (SSHException):
issue_message = (ipaddr +': SSH端口異常 ')
issue_device.append(issue_message)
使用netmiko連接網(wǎng)絡(luò)設(shè)備異常經(jīng)常出現(xiàn)三種情況,設(shè)備不可達,認(rèn)證錯誤,SSH端口異常,所以我把這些異常處理進行分類并寫入文件,這樣批量操作以后我就知道哪些設(shè)備有哪些問題進行批量處理。
那么如何捕捉異常信息呢?比如我netmiko登錄設(shè)備時密碼錯誤,會提示我認(rèn)證錯誤。

最后一行會提示我們AuthenticationException:Authentication Failed.我們捕捉冒號前的AuthenticationException就好。
except Exception as unknown_error:
issue_message = (ipaddr +': 發(fā)生未知錯誤: ')
issue_device.append(issue_message+str(unknown_error))
finally:
write_issue_device(issue_device) #異常處理信息寫入文件
我們很難捕捉到所有已知的異常,所以需要一個兜底策略,就是 except Exception as unknown_error,定義一個提示信息(發(fā)生未知錯誤),并把報錯的原內(nèi)容加入上面定義的異常list中,需要注意第三行我把原錯誤信息轉(zhuǎn)化為str字符串,因為后面我們會把所有異常提示信息list的內(nèi)容用換行符\n進行拼接,便于我們觀看文本,所以要確保所有信息是字符串,否則無法拼接。
最后,無論是否有錯誤信息,都要把異常處理列表內(nèi)的信息寫入文件,函數(shù)write_issue_device和上一小節(jié)內(nèi)容一致,相信大家都能看懂。只需要注意,我們要使用換行符\n拼接list內(nèi)的內(nèi)容,確保每個設(shè)備的錯誤信息為一行,否則跑批1000臺設(shè)備,100臺設(shè)備報錯,而且所有錯誤信息串在一起根本無法查看。
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/linsy_env/' + "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)) #使用換行符\n拼接list內(nèi)