01 前言
前段時間分享了華爲NETCONF結合協程gevent,在大規模數據中心自動化批量操作的方法,不過我忽略了兩個問題,一個是協程gevent的加入會讓排錯難度大幅提升,導致很多小夥伴自己應用時無限掉坑被逼瘋,第二是gevent在windows環境下總會出現莫名其妙的問題,而很多使用者對linux又不算熟悉,最終導致很多人使用gevent並不順利。
秉着簡單易用的原則,我重新研究了下nornir這個專爲網絡而生的模塊,個人感覺3000臺以下網絡設備的簡單跑批操作使用nr就可以了。相比於ansible來說這個模塊在效率和靈活性上都有質的飛躍,相比協程+api的方式又簡單了許多,相比單獨的netmiko模塊又有更多的優化。所以中小規模企業推薦直接使用nornir。nornir的基礎知識王印老師(知乎@弈心)已經做過介紹,華爲安裝的一些坑朱嘉盛老師也介紹過,這些內容直接在知乎搜索即可,我就不重複介紹了。
雖然nr模塊確實非常好用,但從實踐的角度我發現一個問題,就是從現有的資料來看並不適合大規模跑批操作,因爲網上分享的內容(包括官方案例)都有一個問題,就是nr模塊強依賴hosts.yaml這個文件,但沒人強調如何大批量導入資產信息到這個yaml文件內,如果你有1000臺設備,按照yaml格式一個一個輸入到yaml文件裏手都要斷了,所以我手寫了一個自動生成hosts.yaml+nr跑批的py腳本,這樣在大規模場景下更加實用。
02 實現邏輯
- 簡化hosts.yaml內部配置,只保存設備名稱和IP地址(yaml內的hostname),用戶名、密碼、platform全放在defaults.yaml中,不使用group.yaml(想使用也可以,因爲我這個內容主要是HUAWEI,沒有第二個廠商,所以不需要)。
config.yaml
---
inventory:
plugin: SimpleInventory
options:
host_file: "hosts.yaml"
defaults_file: "defaults.yaml"
runner:
plugin: threaded
options:
num_workers: 20
defaults.yaml
---
username: user
password: pass
platform: huawei_vrpv8
- 讀取excel內容(excel內容如下:),形成dict,在用dict專爲yaml文件,生成hosts.yaml,再使用nornir進行跑批操作。
03 代碼示例
#!/usr/bin/env python
#coding: utf-8
import os
from pprint import pprint
from ruamel import yaml
#ruamel.yaml用來讀寫yaml文件
from openpyxl import Workbook
from openpyxl import load_workbook
from collections import defaultdict
#使用defaultdict必須導入
from nornir import InitNornir
from nornir_netmiko import netmiko_send_command
from nornir_utils.plugins.functions import print_result,print_title
def read_device_excel():
current_path = os.path.abspath(".")
devices_filename = current_path + "/info02.xlsx"
wb1 = load_workbook(devices_filename)
ws1 = wb1.get_sheet_by_name("device list")
device_dict = defaultdict(dict)
for cow_num in range(2,ws1.max_row+1):
device_dict[ws1["a"+str(cow_num)].value.strip()]["hostname"]=ws1["b"+str(cow_num)].value.strip()
return (dict(device_dict))
def dict_to_yaml(yaml_file):
device_dict = read_device_excel()
with open(yaml_file, 'w', encoding='utf-8') as file:
file.write("---\\n")
yaml.dump(device_dict, file, Dumper=yaml.RoundTripDumper)
def main():
current_path = os.path.abspath(".")
yaml_path = os.path.join(current_path, "hosts.yaml")
dict_to_yaml(yaml_path)
nr = InitNornir(config_file="config.yaml", dry_run=True)
results = nr.run(netmiko_send_command, command_string='disp ver')
print_result(results)
#===========================================================
# 讀取excel內容自動生成hosts.yaml文件,再使用nr進行跑批操作
#===========================================================
if __name__ == '__main__':
main()
執行結果(示例我就執行了1臺)
netmiko_send_command************************************************************
* SHM-RO4-POD2-1 ** changed : False ********************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
Huawei Versatile Routing Platform Software
VRP (R) software, Version 8.150 (CE6851HI V200R002C50SPC018T)
Copyright (C) 2012-2017 Huawei Technologies Co., Ltd.
HUAWEI CE6851-48S6Q-HI uptime is 1476 days, 2 hours, 26 minutes
CE6851-48S6Q-HI(Master) 1 : uptime is 1476 days, 2 hours, 25 minutes
StartupTime 2017/06/09 14:44:20
Memory Size : 2048 M bytes
Flash Size : 1024 M bytes
CE6851-48S6Q-HI version information
1\. PCB Version : CEM48S6QP04 VER B
2\. MAB Version : 1
3\. Board Type : CE6851-48S6Q-HI
4\. CPLD1 Version : 103
5\. CPLD2 Version : 103
6\. BIOS Version : 383
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
04 代碼詳解
def read_device_excel():
current_path = os.path.abspath(".")
devices_filename = current_path + "/info02.xlsx"
wb1 = load_workbook(devices_filename)
ws1 = wb1.get_sheet_by_name("device list")
device_dict = defaultdict(dict)
for cow_num in range(2,ws1.max_row+1):
device_dict[ws1["a"+str(cow_num)].value.strip()]["hostname"]=ws1["b"+str(cow_num)].value.strip()
return (dict(device_dict))
看過我分享的都比較熟悉上面的代碼了,就不做介紹了,主要說下defaultdict(dict),這個可以用作嵌套dict,比如要根據已知內容生成{'SHM-RO4-POD2-1': {'hostname': '10.13.0.224'}},那我們就用for循環遍歷excel內容,然後以A列爲key,hostname:B列內容作爲value,生成嵌套dict。
def dict_to_yaml(yaml_file):
device_dict = read_device_excel()
with open(yaml_file, 'w', encoding='utf-8') as file:
file.write("---\\n")
yaml.dump(device_dict, file, Dumper=yaml.RoundTripDumper)
使用yaml.dump()方法將dict內容以標準yaml格式寫入hosts.yaml文件,注意需要加參數Dumper=yaml.RoundTripDumper。
生成的hosts.yaml
---
SHM-RO4-POD2-1:
hostname: 10.13.0.224
SHM-RO4-POD2-2:
hostname: 10.13.0.225
SHM-RO4-POD2-3:
hostname: 10.13.0.226
SHM-RO4-POD2-4:
hostname: 10.13.0.227
def main():
current_path = os.path.abspath(".")
yaml_path = os.path.join(current_path, "hosts.yaml")
dict_to_yaml(yaml_path)
nr = InitNornir(config_file="config.yaml", dry_run=True)
results = nr.run(netmiko_send_command, command_string='disp ver')
# results = nr.run(task=napalm_get, getters=["facts"]) #napalm API不好用
print_result(results)
主函數很簡單,調用生成yaml文件的方法,然後使用Nornir進行跑批操作。