cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
7107
Views
0
Helpful
0
Replies

Using scripting with networking equipment under Windows

Clinton Neill
Level 1
Level 1

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 ***

0 Replies 0