新浪微網誌: @wandering
部落格位址: dayong.info
在深入進行網絡工作一段時間後,開始着手解決AAA、NTP、SYSLOG基礎服務(簡稱:基礎服務)的可用性和線上裝置相關配置的正确性問題。
前期,一邊優化、重構基礎服務,一邊修正線上配置。但這個項目是個系統工程,無法在短期内完成,是以即要保持戰果讓已修正的配置不再出錯,還希望新增加的裝置可以直接進行正确配置。另外,如何保證其他工程師也正确配置裝置,也是需要解決的問題,畢竟工作最終是需要多個團隊共同協作的。
是以,自動檢查線上所有重要交換機和路由器的網管服務、Spanning-Tree、VTP Mode等重要配置是否正确,成了必須優先解決的問題。
最直接的方法就是直接檢查相應配置,這符合網絡管理人員的思維和操作習慣。是以,決定優先解決多台裝置的批量執行指令需求。
本人一直對程式設計有這樣的觀點,不能解決問題的程式不是好程式,是以程式首先要實作功能,其次才是程式的效能。隻有在規模、需求達到相當程度後才有必要對效率、性能追求極緻。對非專業開發人員來說更是要注意精力、時間的配置設定,20%的投入獲得80%的回報其投入産出比是相當可觀的,再多花80%的精力去提升最多20%的性能是必須慎重對待的。是以,決定采用模拟人機互動方式實作網絡裝置的批量化操作。
程式實作基本邏輯是:
1)自動登入交換機、路由器批量執行指令,将結果輸出。
2)對輸出結果進行二次處理,實作不同目标。
這樣,基本可以解決大部分網絡管理需要,其主要優點是簡單,會操作交換機/路由器的人就可以使用。但是,此方法最大的問題是效率,因為本質上隻是由程式模仿手工操作,需要考慮cli可以接受的操作頻率等問題。曾考慮過SNMP、TCL-Script、NET-CONF等方法,但考慮到自己的能力及精力配置設定、跨廠商平台相容性問題最終放棄。
網上可以查到的模拟人工指令互動操作的方法有2個:perl、expect
首先考慮的是perl,因為有perl程式設計基礎,有其他同僚寫好的相似功能腳本,但最終放棄。因為perl的switch子產品不支援Cisco的Nexus平台内容輸出,其解決方法非常複雜,要修改switch子產品的源代碼,這樣會産生自己的分支,管理維護成本太高,不利于程式的持續開發和推廣 。
最後,選擇expect,其原理是執行指令,根據不同輸出回報采取不同操作,重複這個過程。
關于expect的學習使用,不在本文的關注範圍。
以下最新版本的代碼:
#!/usr/local/bin/expect
#
# Statement: sw-telnet.exp <ip> <cmd-prefix> <uid> <pwd>
#
# <ip> ip for telnet
# <cmd-prefix> For example, sw-backup is cmd-prefix of sw-backup.cmd.h3c and
# sw-backup.cmd.cisco
# <uid> uid for telnet
# <pwd> pwd for telnet
#
#
# Depends: <cmd-prefix>.cmd.h3c
# <cmd-prefix>.cmd.cisco
#
#
# Last modified: 2012/05/24
#
#
set path_cmd "/aaa/bin"
set cmd_telnet "telnet"
set timeout_default 10
set timeout $timeout_default
set vendor "cisco"
# Arg 1
set ip [lindex $argv 0]
if { $ip == "" } {
puts ""
puts "Statement: command <ip> <cmd-prefix> <uid> <pwd>"
puts " ^^"
puts " <ip> ip for telnet"
puts " <cmd-prefix> For example, sw-backup is cmd-prefix of sw-backup.cmd.h3c and"
puts " sw-backup.cmd.cisco"
puts " <uid> uid for telnet"
puts " <pwd> pwd for telnet"
puts ""
exit 1
}
# Arg 2
set cmd_prefix [lindex $argv 1]
if { $cmd_prefix == "" } {
puts ""
puts "Statement: command <ip> <cmd-prefix> <uid> <pwd>"
puts " ^^^^^^^^^^"
puts " <ip> ip for telnet"
puts " <cmd-prefix> For example, sw-backup is cmd-prefix of sw-backup.cmd.h3c and"
puts " sw-backup.cmd.cisco"
puts " <uid> uid for telnet"
puts " <pwd> pwd for telnet"
puts ""
exit 1
}
# Arg 3
set uid [lindex $argv 2]
if { $uid == "" } {
#set uid "backup"
puts ""
puts "Statement: command <ip> <cmd-prefix> <uid> <pwd>"
puts " ^^^"
puts " <ip> ip for telnet"
puts " <cmd-prefix> For example, sw-backup is cmd-prefix of sw-backup.cmd.h3c and"
puts " sw-backup.cmd.cisco"
puts " <uid> uid for telnet"
puts " <pwd> pwd for telnet"
puts ""
exit 1
}
# Arg 4
set pwd [lindex $argv 3]
if { $pwd == "" } {
#set pwd "M2dpSF6rSU"
puts ""
puts "Statement: command <ip> <cmd-prefix> <uid> <pwd>"
puts " ^^^"
puts " <ip> ip for telnet"
puts " <cmd-prefix> For example, sw-backup is cmd-prefix of sw-backup.cmd.h3c and"
puts " sw-backup.cmd.cisco"
puts " <uid> uid for telnet"
puts " <pwd> pwd for telnet"
puts ""
exit 1
}
#___ start telnet ___
spawn $cmd_telnet "$ip"
sleep 1
expect "H3C" { set vendor "h3c" }
expect -re "Username:|Login:|login:" {
send "$uid\r"
sleep 1
}
expect "Password:" {
send "$pwd\r"
sleep 1
}
#_____ login failed _____
expect {
"Access denied" { exit }
"Connection refused" { exit }
"Login failed" { exit }
"Login incorrect" { exit }
"Login invalid" { exit }
"Password incorrect." { exit }
"timeout expired!" { exit }
}
#_____ Command sets selection by vendor (cisco, h3c) _____
switch -- $vendor cisco { # vendor: cisco
set timeout_cisco 60
set timeout $timeout_cisco
#___ get commands __
set file [ open "$path_cmd/$cmd_prefix.cmd.$vendor" "r" ]
set cmd_count 0
while 1 {
if { [gets $file line] == -1 } break
incr cmd_count
set cmd_list($cmd_count) $line
}
close $file
expect -re ".*# *$"
send "term len 0\r\n\n\n"
set i 1
while { $i <= $cmd_count } {
expect -re ".*# *$"
send "$cmd_list($i)\r\n\n\n"
incr i
sleep 1
}
expect -re ".*# *$"
send "exit\r"
} h3c { # vendor: h3c
set timeout_h3c 10
set timeout $timeout_h3c
#___ get commands __
set file [ open "$path_cmd/$cmd_prefix.cmd.$vendor" "r" ]
set cmd_count 0
while 1 {
if { [gets $file line] == -1 } break
incr cmd_count
set cmd_list($cmd_count) $line
}
close $file
set i 1
while { $i <= $cmd_count } {
expect -re "<.*>$"
send "$cmd_list($i)\r\r\r\r"
expect -re "\- More \-+$" {
set timeout 3
set more "yes"
while {$more == "yes"} {
#puts "___ more ___\r"
send " "
expect -re "<.*>$" {
#puts "___ there's no more ___"
set more "no"
}
}
set timeout $timeout_h3c
}
incr i
sleep 1
}
expect -re "<.*>$"
send "quit\r"
} default { # vendor: unkown
puts "\nError: Unkown Vendor!\n"
exit
}
expect eof
puts "\nVendor: $vendor"
puts "Command list:"
set i 1
while { $i <= $cmd_count } {
puts "$i) $cmd_list($i)"
incr i
}
puts ""
exit
*注:腳本目前隻支援Cisco和H3C兩個主流平台。
*注:注意設定程式運作路徑變量 path_cmd 。
舉例,假設需要對裝置1.2.3.4做以下操作:
1)備份running-config
2)檢視cpu狀态
首先,需要建立4個檔案,腳本會自動判斷Cisco或H3C裝置類型執行相應指令集:
1)backup.cmd.cisco
dir show ver show inv show run
2)backup.cmd.h3c
dir disp verion disp device manuinfo disp curr
3)version.cmd.cisco
show process cpu sort | exclude 0.00% show process cpu history
4)version.cmd.h3c
display cpu-usage
其次,寫crontab:
0 3 * * * /aaa/bin/sw-telnet.exp 1.2.3.4 backup test_uid test_pwd > /bak/1.2.3.4_show-run_$(date +"%Y%m%d")
*/10 * * * * /aaa/bin/sw-telnet.exp 1.2.3.4 version test_uid test_pwd >> /bak/1.2.3.4_show-ver_$(date +"%Y%m%d")
OK,這樣就實作了對1.2.3.4的自動抓取running-config和記錄cpu狀态。
在此代碼基礎上,完成了以下工作:
- 對全網重要裝置抓取running-config,并實作關鍵配置檢查報警
- 對某産品相關伺服器接入交換機端口進行流量監控、報警(公司監控不能檢視port-channel屬性)
- 對某IDC核心交換機的mac位址表監控,增減幅度超過5%報警
自動批量執行指令腳本是核心代碼,可以通過其它程式調用實作更複雜的功能,例如對多個IP批量操作,具體實作本文不再贅述。
希望本文能夠對有需要的朋友有所幫助,程式代碼可以任意使用。