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

Expect scripting issue

pantelis1
Level 1
Level 1

Hi All

I am in the process of creating an expect script to capture the output of show commands from various devices and output them as <name of the device>.<command>.txt

However, I am having the following issues:

  1. The "show commands" on the script are not always parsed correctly on the routers (for example the "show ip route" might be parsed as "how ip route" and therefore generate an error)
  2. The output file has the correct format but there is no output within the file

Could you please review and assist in the issues above?

#!/usr/bin/expect 
# Here, we specify all our commands in a list, that we will issue one
# by one at a later time.
set commands {
"show ip route"
"show ip int brief"
"show arp"
}
# Set the date.
set date [timestamp -format %C%y%m%d]
# This variable is for a file called *.txt that has the IP
# of all of the routers you are collecting information from.
set device_list [read [open "routers.txt"]]
# Specify password, as well as what we expect the routers'
# prompt to be.
set pass "*********"
set prompt "#"
# This command tells expect not to echo the output to the console.
exp_log_user 0
# We loop through each device in our list, one by one...
foreach device $device_list {
# Set each device's log file to be the name of the device...
set file_name "$device.txt"

# we initiate the SSH connection
spawn ssh $device -l *********


#If we see a message asking about the device's host key, accept it.
expect -re "Password:" {
exp_send "$pass\r"
}

#exp_send "terminal length 0\n"
foreach cmd $commands {
exp_send "terminal length 0\n"
expect -re $prompt {
exp_log_file -a $file_name.$cmd
exp_send "$cmd\r"
#exp_sleep 2
exp_log_file
}


}
#expect -re $prompt {
#send "term len 0\r"
#}


expect -re $prompt {
exp_send "logout\r"
}


# Turn off logging.
exp_log_file
}
close

Thanks

Pantelis      

2 Accepted Solutions

Accepted Solutions

Maybe it's a latency issue.  Try adding:

expect -re $prompt

Right before term len 0 is sent.

View solution in original post

Hi,

I tried your code and it appears like the host #1 is used to connect to the host #2. BUT it only appears like it because:

#

In reality the connection to host #2 does not originate from the host #1 but from the host you are running the script from.

To clarify, IOS doesn't have a command named "spawn"... so the command would simply fail if you were trying to connect to the host #2 from host #1 using that command.

The code you supplied works for me, all I did was to change the username and password, the code successfully creates files for both the devices I had in the txt-file for the supplied show-commands.

If your experiences differ from mine, I'd next check that you are running the latest versions of expect etc.

View solution in original post

22 Replies 22

Joe Clarke
Cisco Employee
Cisco Employee

By the time you start logging, you've already seen the output you want.  What about:

send "term len 0\r"

expect -re $prompt

foreach cmd $commands {

    exp_log_file -a $file_name.$cmd

    send "$cmd\r"

    expect -re $prompt

    exp_log_file

}

Thanks Joseph

It is now logging per file. However it seems that the name of the file and the actual output are not the same. For example, the output ".show in int brief"  ill display the "show ip route" instead

show arp file has the "show ip int brief" output

show ip int brief has the "show ip route" output

show ip route has a "term len 0" entry

Also again once of the commands was not properly parsed to the router

how ip int brief

               ^

% Invalid input detected at '^' marker.

Try this instead:

match_max [expr 32 * 1024]

foreach cmd $commands {

    send "$cmd\r"

    expect -re $prompt

    set output $expect_out(buffer)

    set fd [open $filename.$cmd w]

    puts $fd $output

    close $fd

}

exactly the same result

I am testing it locally, and it works fine for me.  Maybe there's an issue with your prompt pattern.  Try changing your prompt to:

set prompt {([#>]) ?$}

Did that but still no luck. The device I am quering is a Cisco CISCO2911/K9 with Version 15.1(3)T. No high cpu on the device either

The latest script with the amendement is:

#!/usr/bin/expect

# Here, we specify all our commands in a list, that we will issue one

# by one at a later time.

set commands {

"show ip route"

"show ip int brief"

"show arp"

}

# Set the date.

set date [timestamp -format %C%y%m%d]

# This variable is for a file called routers.txt that has the hostname/IP

# of all of the routers you are collecting information from.tepad

set device_list [read [open "routers.txt"]]

# Specify password, as well as what we expect the routers'

# prompt to be.

set pass "******"

#set prompt "#"

set prompt {([#>]) ?$}

# This command tells expect not to echo the output to the console.

exp_log_user 0

# We loop through each device in our list, one by one...

foreach device $device_list {

# Set each device's log file to be the name of the device...

set file_name "$device"

# we initiate the SSH connection

spawn ssh $device -l *****

#If we see a message asking about the device's host key, accept it.

expect -re "Password:" {

exp_send "$pass\r"

}

# We log our output from each router to its specified file.

#exp_log_file -a $file_name.$date

# Loop through each command that we specified earlier.

send "term len 0\r"

expect -re $prompt

#foreach cmd $commands {

#   exp_log_file -a $file_name.$date.$cmd.txt

#   send "$cmd\r"

#   expect -re $prompt

#   exp_sleep 2

#   exp_log_file

#}

match_max [expr 32 * 1024]

foreach cmd $commands {

    send "$cmd\r"

    expect -re $prompt

    set output $expect_out(buffer)

    set fd [open $file_name.$cmd w]

    puts $fd $output

    close $fd

}

expect -re $prompt {

exp_send "logout\r"

}

# Turn off logging.

exp_log_file

}


one of the output file is:

show arp

how ip int brief

               ^

% Invalid input detected at '^' marker.

#


Here's what I'm using.  I have log_user on just to make sure things are going okay.

#!/usr/bin/expect

# Here, we specify all our commands in a list, that we will issue one

# by one at a later time.

set commands [list "show ip route" "show ip int brief" "show arp"]

# Set the date.

set date [timestamp -format %C%y%m%d]

# This variable is for a file called routers.txt that has the hostname/IP

# of all of the routers you are collecting information from.tepad

set device_list [read [open "routers.txt"]]

# Specify the username and password, as well as what we expect the routers'

# prompt to be.

set pass "******"

#set prompt "#"

set prompt {([#>]) ?$}

# This command tells expect not to echo the output to the console.

log_user 1

# We loop through each device in our list, one by one...

foreach device $device_list {

# Set each device's log file to be the name of the device...

    set file_name $device

# we initiate the SSH connection

    eval spawn ssh $device -l *****

    match_max [expr 32 * 1024]

#If we see a message asking about the device's host key, accept it.

    interact -o -nobuffer -re "assword: $" return

    send "$pass\r"

# We log our output from each router to its specified file.

#exp_log_file -a $file_name.$date

# Loop through each command that we specified earlier.

    send "term len 0\r"

    expect -re $prompt

    foreach cmd $commands {

        send "$cmd\r"

        expect -re $prompt

        set output $expect_out(buffer)

        set fd [open $file_name.$cmd w]

        puts $fd $output

        close $fd

    }

    expect -re $prompt

    send "exit\r"

}

I am afraid this didn't work for me as well. The output from the putty session to the server is:

[root@*****tmp]# expect test.tcl

spawn ssh -l ****

Password:


hostname#show ip route

Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP

       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area

       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2

       E1 - OSPF external type 1, E2 - OSPF external type 2

       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2

       ia - IS-IS inter area, * - candidate default, U - per-user static route

       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP

       + - replicated route, % - next hop override

hostname#how ip int brief

               ^

% Invalid input detected at '^' marker.

hostname#show arp

Protocol  Address          Age (min)  Hardware Addr   Type   Interface

As a result the output is not saved correctly to the files as mentioned above. for example the "hostname.show ip route" give the "exec motd". Strange that it works fine for you

Maybe it's a latency issue.  Try adding:

expect -re $prompt

Right before term len 0 is sent.

Hi Joseph

Thank you very much. This did the trick. At the moment if I add two or more devices under the router.txt the script uses the previous device to hop to the next. For example if my list has the following devices: 1.1.1.1 and 2.2.2.2 it will try to ssh to 2.2.2.2 from 1.1.1.1 as per below:

1.1.1.1#spawn ssh 2.2.2.2 -l

Is this possible to change this in order to ssh from the server I am running the program from everytime rather that the previous device?

Thanks

Pantelis

The spawn looks like it is running from the device, but it is running from the expect host.  The log_user output is deceiving because the spawn is not fully closed.  In your main foreach loop, add the following at the end:

close

Hi Joseph

There is already a "close" file within the foreeach loop.Code below:

  foreach cmd $commands {

        send "$cmd\r"

        expect -re $prompt

        set output $expect_out(buffer)

        set fd [open $file_name.$cmd w]

        puts $fd $output

        close $fd

    }

I've tried to add "close" as per below but I am getting errors

foreach cmd $commands {

send "$cmd\r"

expect -re $prompt

#remove zeros from the cmd filename

regsub -all {[ \r\t\n]+} $cmd "" correct_cmd

set output $expect_out(buffer)

set fd [open $file_name-$date-$correct_cmd.txt w]

puts $fd $output

close $fd

}

close

1.1.1.1#send: spawn id exp7 not open

    while executing

"send "$cmd\r""

    ("foreach" body line 2)

    invoked from within

"foreach cmd $commands {

        send "$cmd\r"

        expect -re $prompt

        #remove zeros from the cmd filename

        regsub -all {[ \r\t\n]+} ..."

    ("foreach" body line 20)

    invoked from within

"foreach device $device_list {

# Set each device's log file to be the name of the device...

    set file_name $device

# we initiate the SSH connection

..."

    (file "store_show_commands_switches.tcl" line 21)

It looks like you've put the close in the wrong place.  Post your current script.

Hi Joseph

Late code is:

#!/usr/bin/expect

# Here, we specify all our commands in a list, that we will issue one

# by one at a later time.

set commands [list "sh ip route summary" "show interfaces" "show arp" "sh ip ospf neighbor" "sh ip eigrp neighbor" "sh cdp neighbors"]

# Set the date.

set date [timestamp -format %C%y%m%d]

# This variable is for a file called switches.txt that has the hostname/IP

# of all of the routers you are collecting information from.tepad

set device_list [read [open "switches.txt"]]

# Specify password, as well as what we expect the switch

# prompt to be.

set pass "********"

#set prompt "#"

set prompt {([#>]) ?$}

# This command tells expect not to echo the output to the console.

#exp_log_user 0

#log_user 1

# We loop through each device in our list, one by one...

foreach device $device_list {

# Set each device's log file to be the name of the device...

set file_name $device

# we initiate the SSH connection

eval spawn ssh $device -l ******

match_max [expr 32 * 1024]

#If we see a message asking about the device's host key, accept it.

interact -o -nobuffer -re "assword: $" return

send "$pass\r"

# We log our output from each router to its specified file.

#exp_log_file -a $file_name.$date

# Loop through each command that we specified earlier.

expect -re $prompt

send "term len 0\r"

expect -re $prompt

foreach cmd $commands {

send "$cmd\r"

expect -re $prompt

#remove zeros from the cmd filename

regsub -all {[ \r\t\n]+} $cmd "" correct_cmd

set output $expect_out(buffer)

set fd [open $file_name-$date-$correct_cmd.txt w]

puts $fd $output

close $fd

}

expect -re $prompt

send "exit\r"

}

Thanks

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: