Python自動化運維實戰:使用Python管理網絡設備

程序員書屋 發佈 2020-04-21T01:02:46+00:00

print "=======SOMETHING UNKNOWN HAPPEN WITH {0}======="4.4.4 其他用例使用netmiko可以實現很多網絡自動化用例。

現在,我們已經知道如何在不同的作業系統中使用和安裝Python以及如何使用EVE-NG搭建網絡拓撲。在本章中,我們將學習如何使用目前常用的網絡自動化庫自動完成各種網絡任務。Python可以在不同的網絡層上與網絡設備進行交互。

首先,Python可以通過套接字編程和socket模塊操縱底層網絡,從而為Python所在的作業系統和網絡設備之間搭建一個低層次的網絡接口。此外,Python模塊還可以通過Telnet、SSH和API與網絡設備進行更高級別的交互。本章將深入探討如何在Python中使用Telnet與SSH模塊在遠程設備上建立連接和執行命令。

本章主要介紹以下內容:

  • 使用Python通過Telnet連接設備;
  • Python和SSH;
  • 使用netaddr處理IP位址和網絡;
  • 網絡自動化實戰示例。

4.1 技術要求

應檢查是否正確安裝了下列工具並保證它們能夠正常使用:

  • Python 2.7.1x;
  • PyCharm社區版或專業版;
  • EVE-NG,網絡仿真器的安裝和配置請參閱第3章。

本章中出現的所有腳本請參見GitHub網站。

4.1.1 Python和SSH

SSH和Telnet的不同之處在於客戶端與伺服器之間交換數據的通道不一樣。SSH使用的是安全鏈路,在客戶端和設備之間創建了一個使用不同的安全機制進行加密的隧道,通信內容很難被解密。因此在需要保證網絡安全的時候,網絡工程師會首先選擇使用SSH協議。

Paramiko庫遵循SSH2協議,支持身份驗證,密鑰處理(DSA、RSA、ECDSA和ED25519),以及其他SSH功能(如proxy命令和SFTP)。在Python中當需要使用SSH連接到網絡設備時通常使用這個庫。

4.1.2 Paramiko模塊

Paramiko是Python中應用最廣的SSH模塊。模塊本身使用Python語言編寫和開發,只有像crypto這樣的核心函數才會用到C語言。從其GitHub官方連結上能夠看到代碼的貢獻者和模塊歷史等諸多信息。

1.安裝模塊

打開Windows cmd或Linux shell,運行下面的命令,從PyPI下載最新的Paramiko模塊。如下圖所示,同時,該命令會自動下載其他依賴包(如cyrptography、ipaddress和six),並將它們安裝到計算機上。

pip install paramiko

在命令行中輸入Python,然後導入Paramiko模塊,驗證是否安裝成功。如下圖所示,正確安裝之後,能夠成功導入模塊。也就是說,命令行上不會出現任何錯誤提示。

2.用SSH連接網絡設備

如前所述,要使用Paramiko模塊,首先需要在Python腳本中導入它,然後通過繼承SSHClient()來創建SSH客戶端。然後,設置Paramiko的參數,使其能夠自動添加任意未知的主機密鑰並信任與伺服器之間的連接。接下來,將遠程主機的信息(IP位址、用戶名和密碼等)傳遞給connect函數。

#!/usr/bin/python
__author__ = "Bassim Aly"
__EMAIL__ = "basim.alyy@gmail.com"

import paramiko
import time
Channel = paramiko.SSHClient()
Channel.set_missing_host_key_policy(paramiko.AutoAddPolicy())
Channel.connect(hostname="10.10.88.112", username='admin',
password='access123', look_for_keys=False,allow_agent=False)

shell = Channel.invoke_shell()

 

AutoAddPolicy()是一種策略,可以作為函數set_missing_host_key_policy()的輸入參數。在虛擬實驗室環境中推薦使用這種策略,但在生產環境中應當使用更加嚴格的策略,如WarningPolicy()或RejectPolicy()。

最後,invoke_shell()將啟動一個連接到SSH伺服器的交互式shell會話。在調用該函數時可以傳入一些其他參數(如終端類型、寬度、高度等)。

Paramiko的連接參數如下。

  • Look_For_Keys:默認為True,強制Paramiko使用密鑰進行身份驗證。也就是說,用戶需要使用私鑰和公鑰對網絡設備進行身份驗證。在這裡使用密碼驗證,因此將該參數設置為False。
  • allow_agent:表示是否允許連接到SSH代理,默認為True。在用密鑰驗證時可能需要使用這個選項。由於這裡使用的是用戶名/密碼,因此禁用它。

最後一步,把各種命令(如show ip int b和show arp)發送到設備終端,並將返回結果輸出到Python窗口中。

shell.send("enable\n")
shell.send("access123\n")
shell.send("terminal length 0\n")
shell.send("show ip int b\n")
shell.send("show arp \n")
time.sleep(2)
print shell.recv(5000)
Channel.close()

腳本運行結果如下圖所示。

 

如果需要在遠程設備上執行耗時很長的命令,就要強制Python等待一段時間,直到設備生成輸出並將結果返回給Python,因此最好使用time.sleep()。否則,Python可能得不到正確的輸出結果。

4.1.3 netmiko模塊

netmiko是Paramiko的增強版本,專門面向網絡設備。雖然Paramiko能夠處理與設備的SSH連接並判斷設備類型是伺服器、印表機還是網絡設備,但netmiko在設計時針對網絡設備做了優化,能夠更有效地處理SSH連接。netmiko還支持各種不同的設備廠商和平台。

netmiko也是對Paramiko的封裝,它使用許多其他增強功能擴展了Paramiko,比如使用啟用的密碼直接訪問所支持的設備,從文件讀取配置並將推送到設備,在登錄期間禁用分頁顯示,以及默認在每條命令後面加上回車符"\n"。

1.支持的設備商

netmiko支持許多供應商的設備,並定期在支持列表中添加新的供應商。netmiko支持的供應商列表分為定期測試類、有限測試類和實驗類。在該模塊的GitHub頁面上可以找到這個列表。

定期測試類中支持的供應商如下圖所示。

有限測試類中支持的供應商如下圖所示。

實驗類中支持的供應商如下圖所示。

2.安裝和驗證

安裝netmiko非常簡單。打開Windows命令行窗口或Linux shell,執行下面的命令就可以從PyPI獲取最新版本的netmiko包(見下圖)。

pip install netmiko

然後,在Python shell中導入netmiko,驗證模塊是否正確地安裝到Python site-packages中。

$python
>>>import netmiko

3.使用netmiko建立SSH連接

現在該開始使用netmiko了,讓我們來看看它強大的SSH功能。首先連接到網絡設備並在上面執行命令。默認情況下,netmiko在建立會話(session)的過程中會在後台處理許多操作(如添加未知的SSH密鑰主機,設置終端類型、寬度和高度),在需要的時候還可以進入特權(enable)模式,然後通過運行供應商提供的命令來禁用分頁。

首先,以字典格式定義設備並提供下列5個必需的關鍵信息。

R1 = (
    'device type ': 'cisco ios',
    'ip': '10.10.88.110',
    'username': 'admin',
    'password': 'access123',
    'secret': 'access123',
}

第一個參數是device_type,為了執行正確的命令,需要使用這個參數來定義平台供應商。然後,需要SSH的IP位址。如果已經使用DNS解析了IP位址,該參數可能是主機名;否則,該參數是IP位址。接下來,提供username、password以及以secret參數傳遞的特權模式的密碼。注意,可以使用getpass()模塊隱藏密碼,並且只在腳本執行期間提示它們。

 

雖然變量中的密鑰序列不重要,但是為了使netmiko能夠正確解析字典並開始和設備建立連接,密鑰的名稱應該和之前示例中提供的密鑰完全一樣。

接下來,從netmiko模塊導入ConnectHandler函數,並提供定義的字典來開始建立連接。因為所有的設備是通過特權模式的密碼配置的,所以需要為創建的連接提供.enable(),以在特權模式下訪問。使用.send_command()在路由器終端上執行命令,.send_command()將會執行命令並通過變量的值顯示設備的輸出。

from netmiko import ConnectHandler
connection = ConnectHandler(**R1)
connection.enable()
output = connection.send_command("show ip int b")
print output

腳本輸出結果如下。

注意,這裡看到的輸出結果去掉了命令行中的命令回顯和設備提示符。默認情況下,netmiko會替換設備的返回結果,使輸出更加整潔,替換過程通過正則表達式完成,這部分會在下一章中介紹。

如果不想使用這種方式,而是希望看到命令提示符,並在返回結果的後面執行命令,可以在.send_command()函數中加上以下參數。

output = connection.send_command("show ip int
b",strip_command=False,strip_prompt=False)

strip_command=False和strip_prompt=False告訴netmiko保留而不是替換命令行回顯和提示符。默認情況下它為True,可以根據需要進行設置。

4.使用netmiko配置設備

netmiko可以通過SSH配置遠程設備,通過.config方法進入設備的配置模式,然後按照list格式中的信息(配置列表)配置設備。配置列表可以直接寫在Python腳本中,也可以從文件中讀取,然後用readlines()方法轉換為列表。

from netmiko import ConnectHandler

SW2 = {
     'device_type': 'cisco_ios',
     'ip': '10.10.88.112',
     'username': 'admin',
     'password': 'access123',
     'secret': 'access123',
}

core_sw_config = ["int range gig0/1 - 2","switchport trunk encapsulation
dot1q",
                  "switchport mode trunk","switchport trunk allowed vlan
1,2"]

print "########## Connecting to Device {0} ############".format(SW2['ip'])
net_connect = ConnectHandler(**SW2)
net_connect.enable()
print "***** Sending Configuration to Device *****"
net_connect.send_config_set(core_sw_config)

上面的腳本以另外一種形式連接到SW2並進入特權模式。但這次使用的是另一個netmiko方法——send_config_set(),該方法需要使用列表形式的配置文件,同時進入設備的配置模式並根據列表對設備進行配置。這裡測試了一個簡單的配置,即修改gig0/1和gig0/2,並將這兩個埠配成trunk模式。在設備上執行show run命令時,如果命令執行成功,會出現類似下面的輸出。

5.netmiko中的異常處理

在設計Python腳本時,我們可能會假設設備已啟動並運行,並且用戶已提供了正確的登錄信息,但實際情況並非總是如此。有時Python和遠程設備之間的網絡連接可能存在問題,或者用戶輸入了錯誤的登錄信息。如果發生這種情況,Python通常會拋出異常並退出,但這種解決方案顯然不夠完美。

netmiko中的異常處理模塊netmiko.ssh_exception提供的一些異常處理類可以處理上面所說的那些情況。第一個類AuthenticationException能夠捕獲遠程設備中的身份驗證錯誤。第二個類NetMikoTimeoutException能夠捕獲netmiko和設備之間的超時或任何連接問題。下面的例子中使用try-except子句包含了ConnectHandler()方法,用來捕獲超時和身份驗證異常。

from netmiko import ConnectHandler
from netmiko.ssh_exception import AuthenticationException,
NetMikoTimeoutException
device = {
    'device_type': 'cisco_ios',
    'ip': '10.10.88.112',
    'username': 'admin',
    'password': 'access123',
    'secret': 'access123',
}

print "########## Connecting to Device {0}
############".format(device['ip'])
try:
    net_connect = ConnectHandler(**device)
    net_connect.enable()

    print "***** show ip configuration of Device *****"
    output = net_connect.send_command("show ip int b")
    print output

    net_connect.disconnect()

except NetMikoTimeoutException:
    print "=========== SOMETHING WRONG HAPPEN WITH {0}
============".format(device['ip'])

except AuthenticationException:
    print "========= Authentication Failed with {0}
============".format(device['ip'])

except Exception as unknown_error:
    print "============ SOMETHING UNKNOWN HAPPEN WITH {0} ============"

6.設備自動發現

netmiko提供了一種可以「猜測」設備類型和發現設備的機制。通過組合使用SNMP發現OIDS和在遠程控制台上執行多個show命令這兩種方式,根據輸出字符串檢測路由器的作業系統和類型。然後,netmiko將相應的驅動程序加載到ConnectHandler()類中。

#!/usr/local/bin/python
__author__ = "Bassim Aly"
__EMAIL__ = "basim.alyy@gmail.com"

from netmiko import SSHDetect, Netmiko
device = {
'device_type': 'autodetect',
'host': '10.10.88.110',
'username': 'admin',
'password': "access123",
}

detect_device = SSHDetect(**device)
device_type = detect_device.autodetect()
print(device_type)
print(detect_device.potential_matches)

device['device_type'] = device_type
connection = Netmiko(**device)

在上面的腳本中,應注意以下幾點。

首先,設備字典中的device_type等於autodetect,也就是告訴netmiko在檢測到設備類型之前不要加載驅動程序。

然後,使用netmiko的SSHDetect()類發現設備。它使用SSH連接到設備,並執行一些命令以找出作業系統的類型,結果以字典形式返回。

接著,使用autodetect()函數將匹配度最高的結果賦給device_type變量。

接下來,輸出potential_matches,查看字典內的全部返回結果。

最後,可以更新設備字典並為其分配新的device_type。

4.2 在Python中使用Telnet協議

Telnet是TCP/IP協議棧中最早可用的協議之一,主要用來在伺服器和客戶端之間建立連接、交換數據。伺服器端監聽TCP埠23,等待客戶端的連接請求。

在下面的例子中,我們將創建一個Python腳本作為Telnet客戶端,拓撲中的其他路由器和交換機則作為Telnet伺服器。Python原生的telnetlib庫已經支持Telnet,所以不需要另外安裝。

客戶端對象可以通過telnetlib模塊中的Telnet()類實例化創建。通過這個對象,我們能夠使用telnetlib中的兩個重要函數——read_until()(用於讀取輸出結果)和write()(用於向遠程設備寫入內容)。這兩個函數用來和Telnet連接交互,從Telnet連接讀取或向Telnet連接寫入數據。

還有一點非常關鍵,使用read_until()讀取Telnet連接的內容之後緩衝區會被清空,無法再次讀取。因此,如果在後面的處理中還會用到之前讀取的重要數據,需要在腳本里將其另存為變量。

 

Telnet數據以明文形式發送,因此通過「中間人攻擊」可以捕獲並查看到Telnet數據,如用戶信息和密碼。即便如此,一些服務提供商和企業仍然在使用它,只是他們會集成VPN和radius/tacacs協議,以提供輕量級和安全的訪問方式。

讓我們一步步分析這個腳本。

(1)在Python腳本中導入telnetlib模塊,在變量中定義用戶名和密碼。代碼如下。

import telnetlib
username = "admin"
password = "access123"
enable_password = "access123"

(2)定義一個變量,用來和遠程主機建立連接。注意,只需要提供遠程主機的IP位址,不用在連接建立過程中提供用戶名或密碼。

cnx = telnetlib.Telnet(host="10.10.88.110") #here we're telnet to
Gateway

(3)通過讀取Telnet連接返回的輸出並搜索「Username:」關鍵字來提供Telnet連接的用戶名。然後寫入管理員用戶名。如果需要,用同樣的方法輸入Telnet密碼。

cnx.read_until("Username:")
cnx.write(username + "\n")
cnx.read_until("Password:")
cnx.write(password + "\n")
cnx.read_until(">")
cnx.write("en" + "\n")
cnx.read_until("Password:")
cnx.write(enable_password + "\n")

 

Telnet連接建好之後,在腳本中加上控制台提示符非常重要;否則,連接將陷入死循環。接著Python腳本就會超時並出現錯誤。

(4)向Telnet連接寫入show ip interface brief命令並開始讀取返回內容,直到出現路由器提示符(#)為止。通過以下命令可以得到路由器的接口配置。

cnx.read_until("#")
cnx.write("show ip int b" + "\n")
output = cnx.read_until("#")
print output

完整的腳本如下所示。

腳本運行結果如下所示。

注意,在輸出中包含了執行的命令show ip int b,並且在stdout中輸出和返回了路由器提示符"R1#"。可以使用內置的字符串函數(如replace())從輸出中清除它們。

cleaned_output = output.replace("show ip int b","").replace("R1#","")
print cleaned_output

你可能已經注意到腳本中使用了密碼並將密碼以明文形式寫下來,這樣做顯然是不安全的。同時,在Python腳本中使用硬編碼也不是好習慣。在下一節中,我們將學習如何隱藏密碼並設計一種機制,從而在腳本運行時要求用戶輸入密碼。

此外,如果要執行那些輸出結果可能跨越多個頁面的命令(如show running config),則需要在連接到設備之後和發送命令之前,先通過發送termindl length 0來禁用分頁。

使用telnetlib推送配置

在上一節中,我們通過執行show ip int brief簡單介紹了telnetlib的操作過程。現在我們要用telnetlib將VLAN配置推送到實驗室網絡拓撲中的4台交換機。使用Python的range()函數創建一個VLAN列表,遍歷列表將VLAN ID推送到當前交換機。注意,我們將交換機的IP位址放到了另一個列表中,使用外部for循環來遍歷這個列表。同時使用內置模塊getpass隱藏控制台中的密碼,在腳本運行時提示用戶輸入密碼。

#!/usr/bin/python
import telnetlib
import getpass
import time

switch_ips = ["10.10.88.111", "10.10.88.112", "10.10.88.113",
"10.10.88.114"]
username = raw_input("Please Enter your username:")
password = getpass.getpass("Please Enter your Password:")
enable_password = getpass.getpass("Please Enter your Enable Password:")

for sw_ip in switch_ips:
    print "\n#################### Working on Device " + sw_ip + "
####################"
    connection = telnetlib.Telnet(host=sw_ip.strip())
    connection.read_until("Username:")
    connection.write(username + "\n")
    connection.read_until("Password:")
    connection.write(password + "\n")
    connection.read_until(">")
    connection.write("enable" + "\n")
    connection.read_until("Password:")
    connection.write(enable_password + "\n")
    connection.read_until("#")
    connection.write("config terminal" + "\n") # now i'm in config mode
    vlans = range(300,400)
    for vlan_id in vlans:
        print "\n********* Adding VLAN " + str(vlan_id) + "**********"
        connection.read_until("#")
        connection.write("vlan " + str(vlan_id) + "\n")
        time.sleep(1)
        connection.write("exit" + "\n")
        connection.read_until("#")
    connection.close()

最外層的for循環用來遍歷設備列表,然後在每次循環(每個設備)中生成範圍為300~400的VLAN ID並將它們推送到當前設備。

腳本運行結果如下。

當然,也可以通過交換機控制台檢查運行結果(僅顯示部分結果)。

4.3 使用netaddr處理IP位址和網絡

管理和操作IP位址是網絡工程師最重要的任務之一。Python開發人員提供了一個令人驚嘆的庫—— netaddr,它可以識別IP位址並對其進行處理。假設你開發了一個應用程式,其中需要獲取129.183.1.55/21的網絡地址和廣播地址,通過模塊內的內置方法network和broadcast可以輕鬆地獲取到相應的地址。

net.network
129.183.0.
net.broadcast
129.183.0.0

netaddr支持很多功能。

在第3層的地址中,netaddr支持下列功能。

  • 識別IPv4和IPv6地址、子網、掩碼和前綴。
  • 對IP網絡進行疊代、切片、排序、匯總和分類。
  • 處理各種格式(CIDR、任意子網長度、nmap)。
  • 對IP位址和子網進行集合操作(聯合、交叉等)。
  • 解析各種不同的格式和符號。
  • 查找IANA IP塊信息。
  • 生成DNS反向查找結果。
  • 檢索超網和生成子網。

在第2層的地址中,netaddr支持下列功能。

  • 展示和操作Mac地址與EUI-64標識符。
  • 查找IEEE組織信息(OUI、IAB)。
  • 生成鏈路本地的IPv6地址。

4.3.1 安裝netaddr

使用pip安裝netaddr模塊,命令如下。

pip install netaddr

安裝完成之後打開PyCharm或Python控制台並導入模塊,驗證模塊是否安裝成功。如果沒有出現錯誤信息,說明模塊安裝成功。

python
>>>import netaddr

4.3.2 使用netaddr的方法

netaddr模塊提供了兩種重要的方法來定義IP位址並對其進行處理。第一種方法是IPAddress(),它用來定義具有默認子網掩碼的單個有類IP位址。第二種方法是IPNetwork(),它使用CIDR定義無類IP位址。

兩種方法都將IP位址作為字符串來處理,根據字符串返回IP位址或IP網絡對象。返回的對象還可以繼續執行許多方法,比如判斷IP位址是單播地址、多播地址、環回地址、私有地址還是公有地址,以及地址有效還是無效地址。這些操作的結果是True或False。在Python的if條件中可以直接使用這些方法。

另外,該模塊支持使用==、<和>等比較運算符比較兩個 IP 地址,從而生成子網。它還可以檢索一個給定IP位址或者子網術語的超網列表。最終,netaddr模塊可以生成有效主機的一個完整列表(不包括網絡IP位址和網絡廣播地址)。

#!/usr/bin/python
__author__ = "Bassim Aly"
__EMAIL__ = "basim.alyy@gmail.com"
from netaddr import IPNetwork,IPAddress
def check_ip_address(ipaddr):
    ip_attributes = []
    ipaddress = IPAddress(ipaddr)

    if ipaddress.is_private():
        ip_attributes.append("IP Address is Private")
    else:
        ip_attributes.append("IP Address is public")
    if ipaddress.is_unicast():
        ip_attributes.append("IP Address is unicast")
    elif ipaddress.is_multicast():
        ip_attributes.append("IP Address is multicast")
    if ipaddress.is_loopback():
        ip_attributes.append("IP Address is loopback")

    return "\n".join(ip_attributes)

def operate_on_ip_network(ipnet):

    net_attributes = []
    net = IPNetwork(ipnet)
    net_attributes.append("Network IP Address is " + str(net.network) + "
    and Netowrk Mask is " + str(net.netmask))

    net_attributes.append("The Broadcast is " + str(net.broadcast) )
    net_attributes.append("IP Version is " + str(net.version) )
    net_attributes.append("Information known about this network is " +
    str(net.info) )
    net_attributes.append("The IPv6 representation is " + str(net.ipv6()))
    net_attributes.append("The Network size is " + str(net.size))
    net_attributes.append("Generating a list of ip addresses inside the
    subnet")

    for ip in net:
        net_attributes.append("\t" + str(ip))
    return "\n".join(net_attributes)

ipaddr = raw_input("Please Enter the IP Address: ")
print check_ip_address(ipaddr)

ipnet = raw_input("Please Enter the IP Network: ")
print operate_on_ip_network(ipnet)

在上面的腳本中,首先使用raw_input()函數請求用戶輸入IP位址和IP網絡,然後將輸入的值作為參數傳遞給兩個用戶方法check_ip_address()和operate_on_ip_network()並調用它們。第一個函數check_ip_address()會檢查輸入的IP位址,同時嘗試生成有關IP位址屬性的報告(例如,IP位址是單播、多播、私有還是環回地址),並將輸出返回給用戶。

第二個函數operate_on_ip_network()用來完成和網絡相關的操作,即生成網絡ID、掩碼、廣播、版本、網絡上的已知信息、IPv6地址的顯示方式,最後生成該子網內的所有IP位址。

注意,net.info只能對公共IP位址生成可用信息,對私有IP位址不起作用。

同樣,在使用之前需要先從netaddr模塊導入IP Network和IP Address。

腳本運行結果如下所示。

4.4 簡單的用例

隨著網絡變得越來越大,其中包含更多來自各種不同供應商的設備,這就需要創建模塊化的Python腳本來自動執行各種任務。接下來的幾節將分析3個用例,這些用例可以從網絡中收集不同信息,縮短解決問題所需的時間,或者至少將網絡配置恢復到其上次已知的良好狀態。使用自動化工作流來處理網絡故障、修復網絡環境,網絡工程師能夠更關心工作完成情況,提高業務水平。

4.4.1 備份設備配置

備份設備配置對於任何一名網絡工程師來說都是最重要的任務之一。在這個用例中,我們將使用netmiko庫設計一個示例Python腳本。該腳本用來備份設備配置,它適用於不同的供應商和平台。

為方便日後訪問或引用,我們將根據設備IP位址格式化輸出文件名,例如,SW1備份操作的輸出文件保存在dev_10.10.88.111_.cfg中。

創建Python腳本

從定義交換機開始,我們希望將其配置備份為文本文件(設備文件),用逗號分隔訪問設備的用戶名、密碼等詳細信息。這樣就可以在Python腳本中使用split()函數來獲取這些數據,方便在ConnectHandler函數中使用這些數據。此外,還可以從Microsoft Excel工作表或任何資料庫中輕鬆導出該文件以及把該文件導入其中。

文件結構如下。

<device_ipaddress>,<username>,<password>,<enable_password>,<vendor>

創建Python腳本,使用with open子句在腳本中導入該文件。在導入的文件對象上使用readlines()方法將文件中的每一行組成列表,然後用for循環逐行遍歷文件,用split()函數獲取每一行中用逗號隔開的設備信息,並將它們賦予相應的變量。

from netmiko import ConnectHandler
from datetime import datetime

with
open("/media/bassim/DATA/GoogleDrive/Packt/EnterpriseAutomationProject/Chap
ter5_Using_Python_to_manage_network_devices/UC1_devices.txt") as
devices_file:
    devices = devices_file.readlines()
for line in devices:
    line = line.strip("\n")
    ipaddr = line.split(",")[0]
    username = line.split(",")[1]
    password = line.split(",")[2]
    enable_password = line.split(",")[3]

    vendor = line.split(",")[4]

    if vendor.lower() == "cisco":
        device_type = "cisco_ios"
        backup_command = "show running-config"

    elif vendor.lower() == "juniper":
        device_type = "juniper"
        backup_command = "show configuration | display set"

由於我們的目標是創建模塊化的、支持多種設備供應商的腳本,因此在if子句中需要檢查設備供應商,並為該設備分配正確的device_type和backup_command。

接下來,建立與設備的SSH連接,使用netmiko模塊中的.send_command()方法執行備份命令。

print str(datetime.now()) + " Connecting to device {}" .format(ipaddr)

net_connect = ConnectHandler(device_type=device_type,
                             ip=ipaddr,
                             username=username,
                             password=password,
                             secret=enable_password)
net_connect.enable()
running_config = net_connect.send_command(backup_command)

print str(datetime.now()) + " Saving config from device {}" .format(ipaddr)

f = open( "dev_" + ipaddr + "_.cfg", "w")
f.write(running_config)
f.close()
print "=============================================="

在最後的幾行中,以寫入方式打開一個文件(文件不存在時將自動創建),文件名中包含了前面從設備文件中讀取的ipaddr變量。

腳本運行結果如下。

需要注意的是,備份的配置文件存儲在項目的主目錄中,文件名稱包含每個設備的IP位址(見下圖)。

 

使用Linux伺服器上的cron任務,或Windows伺服器上的計劃任務,可讓伺服器在指定時間運行上面的Python腳本。例如,每天凌晨運行一次,將配置信息存儲在latest目錄中,以方便運維團隊使用。

4.4.2 創建訪問終端

在Python或其他編程活動中,你就是自己的設備供應商。為了滿足自己的需求,你可以創建任何喜歡的代碼組合和程序。在第二個例子中我們創建自己的終端(terminal),通過telnetlib訪問路由器。只需要在終端寫幾個單詞,就會在網絡設備中執行很多命令並返回輸出結果。輸出結果將會顯示在標準輸出或保存在文件中。

#!/usr/bin/python
__author__ = "Bassim Aly"
__EMAIL__ = "basim.alyy@gmail.com"

import telnetlib

connection = telnetlib.Telnet(host="10.10.88.110")
connection.read_until("Username:")
connection.write("admin" + "\n")
connection.read_until("Password:")
connection.write("access123" + "\n")
connection.read_until(">")
connection.write("en" + "\n")
connection.read_until("Password:")
connection.write("access123" + "\n")
connection.read_until("#")
connection.write("terminal length 0" + "\n")
connection.read_until("#")
while True:
    command = raw_input("#:")
    if "health" in command.lower():
        commands = ["show ip int b",
                    "show ip route",
                    "show clock",
                    "show banner motd"
                    ]

    elif "discover" in command.lower():
        commands = ["show arp",
                    "show version | i uptime",
                    "show inventory",
                    ]
else:
    commands = [command]
for cmd in commands:
    connection.write(cmd + "\n")
    output = connection.read_until("#")
    print output
    print "==================="

首先,建立到路由器的Telnet連接並輸入相應的用戶信息,一直到打開特權(enable)模式。然後,創建一個始終為true的無限while循環,使用內置的raw_input()函數捕捉用戶輸入的命令。腳本捕獲到用戶輸入之後,在網絡設備上執行這些命令。

如果用戶輸入關鍵字health或discover,終端將自動執行一系列命令以反映期望的操作。這些關鍵字在排除網絡故障時非常有用,可以根據常用的操作自由擴展它們。想像一下,在需要解決兩個路由器之間的開放式最短路徑優先(Open Shortest Path First,OSPF)鄰居問題時,只要打開自己的Python終端腳本(這個腳本中已經寫好了幾個排除故障常用的命令),並將這些命令打包到諸如tshoot_ospf之類的if條件之後。一旦腳本看到這個關鍵字,它就會執行這些命令,輸出OSPF鄰居狀態、MTU的接口、OSPF的廣播網絡等,簡化定位問題的過程。

通過在提示符中輸入health嘗試腳本中的第一條命令。腳本輸出結果如下。

可以看到,腳本將返回在設備上執行多條命令後的結果。

接著試一下第二個命令discover。腳本輸出結果如下。

這次腳本返回discover命令的輸出。在後面的章節中,我們將會解析返回的輸出結果並從中提取有用的信息。

4.4.3 從Excel工作表中讀取數據

網絡和IT工程師始終使用Excel工作表來存儲基礎設施的相關信息,如IP位址、設備供應商和登錄憑證。Python支持從Excel工作表中讀取數據並對其進行處理,以便我們在腳本中使用數據。

在這個用例中,我們將使用Excel Read(xlrd)模塊讀取UC3_devices.xlsx文件。該文件保存了基礎設施的主機名、IP位址、用戶名、普通密碼、特權模式下的密碼和供應商名稱。然後將讀到的數據用作netmiko模塊的輸入。

Excel工作表中的內容如下圖所示。

首先,用pip安裝xlrd模塊,因為需要用它來讀取Microsoft Excel工作表。

pip install xlrd

xlrd模塊能夠讀取Excel工作表並將行和列轉換為矩陣。比如,row[0][0]代表第一行第一列的單元格,右邊緊接著它的單元格是row[0][1](見下圖),以此類推。

xlrd在讀取工作表時,每次讀取一行,同時特殊計數器nrows(行數)自動加1。同樣,每次讀取一列,ncols(列數)自動加1,這樣我們就能夠通過這兩個參數知道矩陣的大小。

然後,在xlrd的open_workbook()函數中輸入文件路徑,並用sheet_by_index()或sheet_by_name()函數訪問工作表。在本例中,數據存儲在第一個工作表(index = 0)中,工作表文件存儲在以章為名的文件夾下。接著,遍歷工作表的每一行,或者使用row()函數來訪問指定行。返回的輸出結果是一個列表,使用索引可以訪問列表中的元素。

Python腳本如下。

__author__ = "Bassim Aly"
__EMAIL__ = "basim.alyy@gmail.com"

from netmiko import ConnectHandler
from netmiko.ssh_exception import AuthenticationException,
NetMikoTimeoutException
import xlrd
from pprint import pprint

workbook =
xlrd.open_workbook(r"/media/bassim/DATA/GoogleDrive/Packt/EnterpriseAutomat
ionProject/Chapter4_Using_Python_to_manage_network_devices/UC3_devices.xlsx
")

sheet = workbook.sheet_by_index(0)

for index in range(1, sheet.nrows):
    hostname = sheet.row(index)[0].value
    ipaddr = sheet.row(index)[1].value
    username = sheet.row(index)[2].value
    password = sheet.row(index)[3].value
    enable_password = sheet.row(index)[4].value
    vendor = sheet.row(index)[5].value

    device = {
        'device_type': vendor,
        'ip': ipaddr,
        'username': username,
        'password': password,
        'secret': enable_password,

    }
    # pprint(device)

    print "########## Connecting to Device {0}
############".format(device['ip'])
    try:
        net_connect = ConnectHandler(**device)
        net_connect.enable()

        print "***** show ip configuration of Device *****"
        output = net_connect.send_command("show ip int b")
        print output

        net_connect.disconnect()

    except NetMikoTimeoutException:
        print "=======SOMETHING WRONG HAPPEN WITH
{0}=======".format(device['ip'])

    except AuthenticationException:
        print "=======Authentication Failed with
{0}=======".format(device['ip'])
except Exception as unknown_error:
    print "=======SOMETHING UNKNOWN HAPPEN WITH {0}======="

4.4.4 其他用例

使用netmiko可以實現很多網絡自動化用例。例如,在升級期間從遠程設備上傳/下載文件,利用Jinja2模板加載配置,訪問終端伺服器,訪問終端設備等。要了解更多用例,請參見GitHub官網(見下圖)。

4.5 小結

在本章中,我們開始使用Python進入網絡自動化世界。本章討論了Python中的一些工具,通過Telnet和SSH建立到遠程節點的連接,並在遠程設備上執行命令。此外,本章還講述了如何在netaddr模塊的幫助下處理IP位址和網絡子網。最後通過兩個實際用例鞏固了這些知識。

本文摘自:《Python自動化運維實戰》

  • 運維工程師教程書籍,自動化運維實踐
  • 通過Python模塊、庫與工具自動配置和管理大量伺服器的講解,提高運維的效率

《Python自動化運維實戰》介紹了如何通過Python來自動完成伺服器的配置與管理,自動完成系統的管理任務(如用戶管理、資料庫管理和進程管理),以及完成這些工作所需的模塊、庫和工具。此外,本書還講述了如何使用Python腳本自動執行測試,如何通過Python在雲基礎設施和虛擬機上自動執行任務,如何使用基於Python的安全工具自動完成與安全相關的任務。

本書適合運維人員和開發人員閱讀,也可作為相關專業人士的參考書。

關鍵字: