从今天开始,陆续把我所写的一些自动测试脚本贴上来,希望对初学者有所帮助。由于目前没有找好合适的 服务器存放代码,所以代码先贴在blog的正文中,以后再提供完整的代码 下载。
我的自动化测试脚本运行在debian Linux下,使用/usr/bin/expect进行解释执行。为了简化处理,把一些常用的功能编写成函数,放在commonLib.exp文件中,其它脚本文件可以使用source commonLib.exp命令引用这些函数。
下面的函数完成telenet到目标机器并login。从其实现上大家可以看到tcl/expect编写测试脚本的简洁。
这个函数带有三个参数,分别是目标机器的IP地址ipaddr,登录用户名user和登录密码,telenet的端口号采用默认的23端口。
函数中使用了三个全局变量,g_prompt,g_usrPrompt和g_pwdPrompt,分别表示登录后的命令提示符,提示用户名输入的提示符,以及提示密码输入的提示符,这三个全局变量定义在global.exp中。之所以采用全局变量,是因为这些值使用比较广泛,但在不同设备中都不相同。使用全局变量可以方便修改。
代码如下:
# telnet login routine
#
# @PARAMS
# ipaddr - remote device ip address
# user - user name to login in
# passwd - login passWord
#
# @RETURN
# spawn_id if login success, otherwise 0
#************************************************
proc login {ipaddr user passwd} {
global g_prompt g_usrPrompt g_pwdPrompt
spawn telnet $ipaddr
expect {
"$g_usrPrompt" {
exp_send "$user/r/n"
exp_continue
}
"$g_pwdPrompt" {
exp_send "$passwd/r/n"
exp_continue
}
-ex "$g_prompt" {
dbgLog "Login Successful/n"
return $spawn_id
}
timeout {
send_user "timeout"
return 0
}
}
}
现在介绍一下测试主程序: test.exp。
为了方便加入新的测试项目,主程序采用了一种灵活的机制,它根据需要通过source命令调用相应的子测试程序。这样一来,每个测试点都可以单独放到一个文件中,然后被主程序引用。
先看一下代码:
# $Id$
# Usage:
# ./test [-uuser] [-ppassward] [-iip_address] test_001 ...
# or ./test [-uuser] [-ppassward] [-iip_address] [-ccommand_file] cmd
# or ./test [-uuser] [-ppassward] [-iip_address] [-sscript_file] script
source global.exp
source commonLib.exp
# initialize variables
set cmdFile ""
set tList $argv
set execScript ""
# process options
set endOptIndex -1
foreach arg $argv {
if {![string match "-/[a-zA-Z]*" $arg]} {
break
}
# inc end option index
incr endOptIndex
# get option flag and option value
set optFlg [string range $arg 1 1]
set optVal [string range $arg 2 end]
dbgLog "$optFlg $optVal"
if {$optVal == ""} {
dbgLog "option value is null: -$optFlg"
return -1
}
switch $optFlg {
"u" {
set g_user $optVal
dbgLog "user: $g_user"
}
"p" {
set g_passwd $optVal
dbgLog "pass Word: $g_passwd"
}
"i" {
set g_devip $optVal
dbgLog "devip: $g_devip"
}
"c" {
set cmdFile $optVal
dbgLog "cmdFile: $cmdFile"
}
"s" {
set execScript $optVal
dbgLog "execScript: $execScript"
}
default {
puts "unknown option: -$optFlg"
return -1
}
} ;# end switch
} ;# end foreach
# remove options from list
if {$endOptIndex != -1} {
set tList [lreplace $argv 0 $endOptIndex]
}
dbgLog "tList is: $tList"
# create log dir
if { ![file exist "log"] || ![file isdirectory "log"] } {
puts "please create directory /"log/""
return -1
}
# read current time
set clicks [clock clicks]
set tstr [clock format $clicks -format "%y%m%d%I%M%S"]
# open log file
log_file "log/vLog$tstr.log"
# open brief log file
set g_bLogFd [open "log/bLog.log" w]
# start testing
foreach tItem $tList {
switch $tItem {
"sys_001" { ;# test group sys_001
source snmp.exp
}
"cmd" { ;# exec cmd file
source tCmd.exp
}
"script" { ;# exec script file
if {$execScript == ""} {
puts "Please specify script name using -s option"
return -1
}
source $execScript
}
default {
puts "do you want to test /"$tItem/"/?"
}
}
}
close $g_bLogFd
在程序开始,通过source导入两个文件,其中global.exp中主要存放了一些全局变量的定义,因为这些全局变量对每台测试设备可能各不相同,所以把它们提取出来。commonLib.exp文件中存放着一些通用子程序,可供各测试程序调用。我们前面介绍过的login子程序,就放在此文件中。
接下来,分析命令行参数,首先提取出所有的选项参数,目前支持的命令行选项包括:
-u :此选项用来更改登录的用户名
-p :此选项用来更改登录的密码
-i :此选项用来更改telnet的IP地址
-c :此选项用来指明批处理文件的文件名,用法在后面描述
-s :此选项用来指明脚本文件的文件名,用法在后面描述
最后,命令行参数中所有非选项的部分,都被做为测试项,分别对这些测试项进行测试。
例如测试项test_001,会使用source命令调用snmp.exp脚本,进行snmp community方面的测试。
可以根据需要自行添加测试项目。
有两个特别的测试项名称,分别为cmd和script。
cmd测试项,会调用cmd.exp脚本,这个脚本在后面介绍,它的主要功能是执行一个文本文件里的所有命令。文本文件名由-c选项提供。
script测试项,它会调用source命令,执行$execScript脚本。可以使用-s选项为$execScript变量赋值。
这个测试脚本提供了两种日志,一种是详细的日志(vLog*),包括了telnet的所有交互过程;另外一种是简单的日志,只包含程序中使用errLog输出的信息。日志文件被放在子目录log中,其文件名中包含了脚本执行的时间,方便查找。
本脚本中使用dbgLog,以及以后将用到的errLog,都是定义在commonLib.exp文件中的子函数,代码如下:
# debug output routine
#
# @PARAMS
# arg - variable length arguments
#************************************************
proc dbgLog arg {
global g_dbgFlag
if {$g_dbgFlag} {
puts $arg
}
}
#************************************************
# error output routine
#
# @PARAMS
# arg - variable length arguments
#************************************************
proc errLog arg {
global g_bLogFd
global g_dbgFlag
if {$g_dbgFlag} {
puts $arg
}
if { $g_bLogFd != 0 } {
puts $g_bLogFd $arg
}
}
下面是global.exp文件的内容,只是定义一些全局变量,供其它文件使用。
# global variables
set g_dbgFlag 1 ;# Debug flag
set g_bLogFd 0 ;# Error Log FD
set g_devip "192.168.1.222" ;# Default device IP address
set g_prompt "$" ;# CLI prompt
set g_user "root" ;# login account name
set g_passwd "root" ;# login passWord
set g_usrPrompt "*ogin:" ;# login prompt
set g_pwdPrompt "*assWord:" ;# login password prompt
在测试过程中,在具体测试某一个功能点时,往往需要为此进行大量的配置。为了简化测试过程,我们可以把所有的配置命令放在一个文本文件中,然后使用测试脚本来执行这些命令。这样就不需要再手工进行配置了,费时费力。
基于如上考虑,编写了下面的脚本tCmd.exp。这个脚本被我们前面介绍过的test.exp脚本调用。
# This file is used to execute specific commands list in a file
proc execCmdFile {cmdFile} {
global g_dbgFlag g_prompt
# enable debug
set g_dbgFlag 1
# login
set spawn_id [login $g_devip $g_user $g_passwd]
if {$spawn_id == 0} {
errLog "login $g_devip failed"
return 0
}
# open cmdFile
set cmdFd [open $cmdFile r]
while true {
# get a line
if {![getLine $cmdFd line]} {
dbgLog "reached eof"
break
}
# split the line
set ln [split $line ","]
set cmd [string trim [lindex $ln 0]]
set out [string trim [lindex $ln 1]]
if {$cmd == ""} continue
if {$out == ""} set out $g_prompt
# send cmd line
exp_send "$cmd/n"
dbgLog "send $cmd"
# expect output
dbgLog "expect $out"
expect {
timeout {
errLog "TIMEOUT: while exec /"$cmd/""
continue
}
-ex "$out" {
continue
}
} ;# end expect
}
# close cmdFile
close $cmdFd
}
# if no cmdFile, use default
if {$cmdFile == ""} {
set cmdFile "cmdFile.txt"
}
execCmdFile $cmdFile
有了这个脚本,我们可以使用"./test.exp -cinterface.txt cmd"来执行interface.txt中的命令。
代码见下,比较简单,就不再分析了。调用实例见前面的文章。
# get a line from file, skip blank lines and
# comment lines, return the reading line in
# parameter 'line'.
#
# @PARAMS
# fd - file fd
# line - var used to return the line
#
# @RETURN
# return 1 if read successfully, otherwise 0
#************************************************
proc getLine {fd line} {
upvar $line ln
# read a line from fd
while {[set lineLen [gets $fd ln]] >= 0} {
# blank line
if { $lineLen == 0 } continue
# trim whitespace
set ln [string trim $ln]
if { [string length $ln] == 0 } continue
# skip comment
if { [string index $ln 0] == "#" } continue
# success
return 1
}
return 0
}
下面通过一个测试SNMP community最大长度的脚本,介绍一下net-snmp工具。
net-snmp是一组基于命令行的snmp manager工具,可以在命令行下进行snmp get, snmp set, snmp walk等操作,支持snmp v1/v2c/v3。原来的名字叫做ucd-snmp,也已经被移植到Windows NT上。
它的主页在http://net-snmp.sourceforge.net/
由于它可以在命令行下进行SNMP操作,所以可以和TCL/expect很好的结合,完成自动化测试的功能。
下面的脚本(snmp.exp),不断的增加SNMP community,长度从1到256,每增加一个community,就调用snmp-get来进行SNMP get操作,如果get成功,说明此community有效;反之,就说明community已经超出了设备支持的最大长度。
这个脚本使用前面讲到的test.exp调用,调用方法是:
./test.exp -ssnmp.exp script
对它稍加修改,也可以直接在命令行中调用,此处不再赘述。
代码如下:
proc snmpCommTest {comm} {
global g_devip
spawn snmpget -c $comm -v 2c -r 2 $g_devip system.sysUpTime.0
expect {
"system.sysUpTime.0*" {
return 1
}
"*Timeout*" {
return 0
}
}
return 1
}
set spawn_id [login $g_devip $g_user $g_passwd]
if {$spawn_id == 0} {
errLog "login error/n"
return 0
}
set cmdCommAdd "create snmp community %s rw/n"
set cmdCommDel "delete snmp community %s/n"
set cmdHostAdd "create snmp host ip 192.168.1.2 community %s/n"
set cmdHostDel "delete snmp host ip 192.168.1.2 community %s/n"
set comm ""
for {set i 1} {$i < 256} {incr i} {
set comm "a$comm"
set cmd [format $cmdCommAdd $comm]
exp_send $cmd
expect {
"Error*" {
errLog "create comm len $i error"
continue
}
timeout {
errLog "create comm len $i timeout"
continue
}
"Entry Created"
}
set cmd [format $cmdHostAdd $comm]
exp_send $cmd
expect {
"Error*" {
errLog "create host error"
continue
}
timeout {
errLog "create host timeout"
continue
}
"Entry Created"
}
set rc [snmpCommTest $comm]
if {$rc == 0} {
errLog "community len $i failed"
}
set cmd [format $cmdHostDel $comm]
exp_send $cmd
expect "Entry Deleted"
set cmd [format $cmdCommDel $comm]
exp_send $cmd
expect "Entry Deleted"
}
前面提到过使用nemesis构造报文进行测试的思路,今天介绍一个这样的脚本。
这个脚本的功能是构造并发送不同源MAC地址的报文,通过这样一个脚本,我们就可以测试交换机每端口最大能学习到的MAC地址的数目。
简单说一下nemesis,它运行在Linux上,也可以在Windows上运行。在Linux上,需要拥有root权限才能构造报文。
它可以用来构造arp, enternet, ip, icmp, igmp, dns, tcp, ospf, rip等类型的报文。实际上,用户可以使用一个文件做为它所构造的报文的内容,从这个角度上讲,它可以用来构造任何类型的报文。
另外,由于它是基于命令行的一组工具,所以能够非常好的和TCL/EXPECT结合使用,完成自动化测试。
这个脚本仍旧由前面介绍的test.exp脚本调用,调用方式是:
./test.exp -ssrc_mac_attack.exp script
此脚本文件的内容(src_mac_attack.exp)如下:
# $Id$
# Construct different source MAC address packets, and send them to switch.proc src_mac_attack {mac} {
set rc [exec echo "src MAC attack packet $mac" /
| nemesis ethernet -M 00:01:02:03:04:05 -H $mac -T 0x0800 -P -]
return $rc
}
for {set i 1} {$i < 256} {incr i} {
set mac [constructMac $i]
src_mac_attack $mac
}
脚本很简单,只有一个循环,不断生成新MAC,然后构造报文发送。
简单介绍一下proc src_mac_attack,这个函数中,使用TCL的exec命令来执行linux下的命令。在这里执行的linux命令就是:
echo "src MAC attack packet $mac" /
| nemesis ethernet -M 00:01:02:03:04:05 -H $mac -T 0x0800 -P -
其中,echo命令的输出通过管道,被送给nemesis命令做输入,echo命令显示的内容将做为所构造的以太报文的内容;
nemesis ethernet命令表明所构造的是以太报文,-M、-H、-T分别指明报文的目的地址、源地址,报文类型。“-P -”则指明报文的内容由标准输入获得,在此例中就是echo命令的输出。
脚本中调用了另外一个自定义函数,这个函数放在commonLib.exp中,用来生成MAC地址(最多可生成65535个不重复的MAC地址),函数内容如下,比较简单,不再赘述:
#************************************************
# Construct MAC address
#
# @PARAMS
# rawMac --- raw MAC address, integer
#
# @RETURN
# the MAC address string
#************************************************
proc constructMac {rawMac} {
set mac "00:00:00:00"
set j [expr "($rawMac >> 8) & 0xFF"]
set k [format "%x" $j]
set mac "$mac:$k"
set j [expr "$rawMac & 0xFF"]
set k [format "%x" $j]
set mac "$mac:$k"dbgLog "rawMac = $rawMac, mac = $mac"
return $mac
}
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow