05-15-2013 06:43 AM
It can be a challenge to use scripting to automate working with Cisco devices. The Cisco IOS does not seem to directly provide a command line interface. You are forced to find a way to automate interaction with a telnet or ssh session.
The PERL language provides a number of object-oriented methods to help manage an interactive session, most notably Net::SSH::Expect and Net::Appliance::Session. These options can work well in a Unix environment, but not under MS Windows.
There are PERL for Windows options, the best probably being Strawberry PERL. There is also a Unix under Windows option known as CYGWIN that is freely available. Unfortunately none of these will work well with the way Windows manages low-level terminal I/O. The curious can google "windows pseudo terminal" to see all the technical details.
One way that does work under Windows is Tcl. It was initially named Tool Command Language. It is sometimes shown as Tcl/Tk.
Interestingly enough, Tcl is included within Cisco IOS as tclsh. There is no interaction with the tclsh and this example. It is just a bit of a curious coincidence.
A Tcl port to Windows can be downloaded from http://www.activestate.com/activetcl/downloads. Select Download ActiveTCL for Windows. A direct link to the download that worked at the time of writing is Download ActiveTcl 8.5.14 for Windows (x86)
Once base Tcl has been downloaded and installed there is one other component that will need to be installed from the Tcl Extension Archive, the expect package.
The teacup program that is installed with the base Tcl package makes this easy. The teacup program will work with a proxy.
You can set these Windows environment variables to specify proxy details:
set http_proxy=
set http_proxy_user=
set http_proxy_pass=
Then run teacup install expect
The plink tool from the PuTTY download is also needed. It can be obtained from http://www.putty.org/.
The sample that follows assumes that the data files, script and plink.exe executable all reside in the same directory.
A sample Tcl script follows that reads a file of devices and a file of commands. It will run the list of commands against each device in the device file. It has some basic error checking, but should best be considered a ‘beta’ version. You could do more complex interactions in the Tcl script by adding exp_send and expect command statements. In short, if you can type it you could script it!
Change directory to where your script, plink.exe and data is stored and run with tclsh <script_name>
devices.list
# Comment lines are allowed if they start with a hash mark
# <IP_Addr> <userid> <password> <ssh|telnet> <timeout_in_secs>
#
nnn.nnn.nnn.nnn <userid> <password> ssh <timeout_in_secs>
#
nnn.nnn.nnn.nnn <userid> <password> telnet 30
commands.list
# term length 0 needed or else IOS will wait for an enter to be pressed at the --More-- prompts
term length 0
show run
exit
Script:
#
# Run batch commands against one or more devices
#
package require Expect
exp_log_user 0
set exp_internal 0
set exp::nt_debug 0
set prompt "(#\s*$|>\s*$)"
set env(TERM) dumb
set file_channel [open "devices.list" r]
set DEVICES [read $file_channel]
close $file_channel
set file_channel [open "commands.list" r]
set COMMANDS [read $file_channel]
close $file_channel
set command_entries [split $COMMANDS "\n"]
set device_entries [split $DEVICES "\n"]
proc timedout {{msg {none}}} {
send_user "Timed out (reason: $msg)\n"
if {[info exists ::expect_out]} { parray ::expect_out }
exit 1
}
foreach device_entry $device_entries {
if {[string length $device_entry] == 0 || [regexp {[ \t]*#} $device_entry]} { continue }
set device [lindex $device_entry 0]
set user [lindex $device_entry 1]
set pass [lindex $device_entry 2]
set mode [lindex $device_entry 3]
set wait [lindex $device_entry 4]
set serial [lindex $device_entry 5]
# puts "Device=$device"
# puts "User=$user"
# puts "Mode=$mode"
# puts "Wait=$wait"
set timeout $wait
#
# Spawning the Session
#
# If you are logging on to the remote machine using "ssh", "slogin" or "rlogin", the information
# gets processed in a slightly different manner. With any of these methods, it is necessary to
# include an additional -l option to specify a username.
#
# Next, the $spawn_id variable is captured, storing information about this spawn session in
# memory for future reference.
#
# If you are logging in via Telnet, the final code block in this section is required to pass the
# username to Telnet. If the login is completed before the script times out, the exp_send command
# passes the username.
switch -exact $mode {
"telnet" { set pid [spawn plink -telnet -l $user $device] }
"ssh" { set pid [spawn plink -ssh -l $user -pw $pass $device] }
"serial" { set pid [spawn plink -serial $serial -l $user -pw $pass $device] }
}
set id $spawn_id
if {$mode == "telnet"} {
expect -i $id timeout {
timedout "in user login"
} eof {
timedout "spawn failed with eof on login"
} -re "(login|Username):.*" {
exp_send -i $id -- "$user\r"
}
}
#
# Handling Errors
#
# The error-handling section of the script is a while loop that anticipates a number of problems
# that could occur during login. This section is not exhaustive. For example, you could also add
# provisions for invalid usernames and passwords.
#
# If the login is not completed during the allotted time frame, which is set from the devices.list file
# and specified with expect -i $id timeout, the program displays an appropriate error message.
#
# The remainder of this loop makes use of the exp_send command to allow for other scenarios, such
# as the user typing "yes" when prompted to proceed with the connection, entering a password, or
# resetting the terminal mode.
#
set logged_in 0
while {!$logged_in} {
expect -i $id timeout {
timedout "in while loop"
break
} eof {
timedout "spawn failed with eof"
break
} "Store key in cache? (y/n)" {
exp_send -i $id -- "y\r"
} -re "\[Pp\]assword:.*" {
exp_send -i $id -- "$pass\r"
} "TERM = (*) " {
exp_send -i $id -- "$env(TERM)\r"
} -re $prompt {
set logged_in 1
}
}
foreach command $command_entries {
if {[string length $command] == 0 || [regexp {[ \t]*#} $command]} { continue }
#
# Sending the Request
#
# If the login is successful, the code in the if statement below is used to send the "cmd" request
# to display files and directories. After the request is sent with exp_send, the resulting output
# is captured in the dir variable, which is set on the fourth line of the code shown below.
#
if {$logged_in} {
exp_send -i $id -- "$command\r"
expect -i $id timeout {timedout "on prompt"} -re $prompt
puts "$expect_out(buffer)"
}
#
# Closing the Spawned Session
#
# The exp_close command ends the session spawned earlier. Just to be sure that session
# does indeed close, the exp_wait command causes the script to continue running until a result is
# obtained from the system processes. If the system hangs, it is likely because exp_close was not
# able to close the spawned process, and you may need to kill it manually.
#
}
catch { exp_close -i $id }
exp_wait -i $id
set logged_in 0
}
*** End of Document ***
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide