问题由来
做测试开发,经常需要通过ssh或者telnet访问设备,来获取信息或者执行命令。目前tcl没有专门的api实现这个功能,以前windows版本的expect能做到。但是近期发现expect不能用了,于是就有开发一个相关api的打算。
解决方案
写代码,越简单越好,这里采用最简单的方案,不容易出错。该方案基于tcl调用putty来实现。核心代码是下面的两行。
set file_id [open "|$program -telnet -x -t -sanitise-stderr -sanitise-stdout $ip" r+]
fconfigure $file_id -blocking 0 -buffering none
就是通过管道打开一个进程,然后通过这个管道跟进程通讯,记住必须配置 -blocking 0 -buffering none,否则会有卡死现象。
目前我已经在github上创建了一个lib,可以拿来就用。github的地址是:
https://github.com/smshi/TclPutty
也可以在我的百度网盘下载:
链接:https://pan.baidu.com/s/133yljc4ZH4Nds_NIrW18DA?pwd=oiqh
提取码:oiqh
下面是我在github创建的使用说明。
TclPutty
TclPutty is tcl lib to connect server by ssh or telnet via putty. It has tested on windows system. I think it could work on Linux too after replace the putty binary with Linux version.
Exmaple
Telnet
package require TclPutty
set DutIp 10.0.11.123
set DutUsername admin
set DutPassword admin123
set log_file_id [open log.txt w]
#Create object named telnet_obj using telnet protocol, the log is recorded into log.txt
TclPutty telnet_obj $DutIp $DutUsername $DutPassword telnet $log_file_id
#Connect to the server
telnet_obj login
#Input command in the connection
telnet_obj exec_cmd "sys"
#Get response from the connection
set output [telnet_obj get_response]
puts $output
telnet_obj exec_cmd "display cur config"
#Get all the response until regexp match the pattern, if not match, it will timeout in 20 seconds in default.
#Following example is get the output from session until match regexp .*[HUAWEI].*
set output [telnet_obj read_until {.*\[HUAWEI\].*}]
puts $output
#Input command in the connection and get all the response until regexp match.
set output [telnet_obj exec_until "display cur config" {.*\[HUAWEI\].*}]
puts $output
#Input command in the connection and get all the response until first regexp match,
#check if all the response match another regexp, match return 1, not match return 0
if { [telnet_obj exec_until_match "display cur config" .+ ".*interface 8.*"] } {
puts "Interface 8 is in the running config."
} else {
puts "Not match"
}
delete object telnet_obj
close $log_file_id
SSH
package require TclPutty
set DutIp 10.0.11.123
set DutUsername admin
set DutPassword admin123
set log_file_id [open log.txt w]
#Create object named telnet_obj using telnet protocol, the log is recorded into log.txt
TclPutty telnet_obj $DutIp $DutUsername $DutPassword ssh $log_file_id
#Connect to the server
telnet_obj login
#Input command in the connection
telnet_obj exec_cmd "sys"
#Get response from the connection
set output [telnet_obj get_response]
puts $output
telnet_obj exec_cmd "display cur config"
#Get all the response until regexp match the pattern, if not match, it will timeout in 20 seconds in default.
#Following example is get the output from session until match regexp .*[HUAWEI].*
set output [telnet_obj read_until {.*\[HUAWEI\].*}]
puts $output
#Input command in the connection and get all the response until regexp match.
set output [telnet_obj exec_until "display cur config" {.*\[HUAWEI\].*}]
puts $output
#Input command in the connection and get all the response until first regexp match,
#check if all the response match another regexp, match return 1, not match return 0
if { [telnet_obj exec_until_match "display cur config" .+ ".*interface 8.*"] } {
puts "Interface 8 is in the running config."
} else {
puts "Not match"
}
delete object telnet_obj
close $log_file_id
API Induction
Constructor
constructor { mngip usr pwd {prtcl telnet} {fileid ""}}
You can construct object like this, default using telnet protocol, and the log is only output to the stdout:
TclPutty objectname 192.168.1.1 root mypass
You can construct object like this too, using ssh protocol, and the log is output to the stdout and the file specified:
TclPutty objectname 192.168.1.1 root mypass ssh fileid
Object attributes
Attribute Default Induction
usrprompt Username: When login by telnet, when the putty output is usrprompt, it will input username automatically.
pwdprompt Password: When login by telnet, when the putty output is pwdprompt, it will input password automatically.
login_flag #|> It is a regexpr pattern. When the output is matched with the pattern, it will think login successfully.
command_end \r The paramter will be appended to command when exec_cmd is called.
reponse_delay 100 The interval to read data from the putty proccess channel
timeout 20000 When the api with pattern parameter is called, if not match, it will wait until the timeout is reached.
We can use the basic itcl grammar to change attribute into what you want.
For example:
TclPutty objectname 192.168.1.1 root mypass
#This change the login_flag into >
objectname configure -login_flag >
Member functions
login
After construct object, you should login.
objectname login
exec_cmd cmd
It will input cmd to the putty channel.
Following example is to input dir command to the putty channel.
objectname exec_cmd "dir"
read_until { {expr_pat .+} {delay default}}
It will read the data from the putty channel until match expr_pat. In default, it will match any character.
Usage examples.
objectname read_until ".*localhost.*" --------- read all the data from putty channel until localhost appears
objectname read_until --------------------------read all the data from putty channel in condition that there is response data unread
objectname read_until ".*localhost.*" 30000 ----read all the data from putty channel until localhost appears, but the timeout is changed into 30000 milliseconds.
exec_until {cmd {expr_pat .+} }
It is combination of exec_cmd and read_until.
Usage exmaple:
objectname exec_until "show running config" ".*localhost.*"
objectname exec_until "show running config"
exec_until_match {cmd {expr_pat .+} {match .*} }
It is combination of exec_cmd and read_until. Additionally, it will match the return data to check if it is matched with regexp in match variable.
Usage exmaple:
objectname exec_until_match "show running config" ".+" ".*interface 8.*"
upload_via_scp filename
It will upload the local file into the remote server by ssh.
Usage exmaple:
objectname upload_via_scp filename