2
votes

I am trying to get configuration details from a switch via expect script.
I have below piece working fine as expected when i call via shell manually.
But when the same called via, php ( to build a web interface to call this) somehow expect_out(buffer) doesn't have all the lines filled and my match fails.

I am trying to match configuration bit set or not.
Again code works when called manually, just that via php this somehow fails to fill expect_out(buffer) :( :(


Working code.

#!/usr/bin/expect
set ip ROUTER_IP
set uname USER_NAME
set psd PASSWORD
set log_file -a "/tmp/script.log"
spawn /usr/bin/ssh $uname@$ip
expect {
    "ord:" {
        send "$psd\r"
        expect {
            "> " {
                send "\r"
                set cmd "show configuration | display set | match class | match rancid"
                set values $expect_out(buffer)
                send_log "\nbefore trim n match -$values-\n"
                set values [string trim $values]
                send_log "\nbefore match -$values-\n"
                set found [regexp {match rancid\s+(.*)\s+\r\n\{.*} $values match px]
                if { $found == 1  && $px != "" } {
                    puts "Rancid Exists... !!!"
                    send "exit\r"
                    exit 1
                } else { 
                    puts "Coundnt find Rancid ... !!!"
                    send "exit\r"
                    exit 2
                }
            }
            "denied: " {
                puts "ERROR: Access Denied"
                exit 2
            }
        }
    }
}

I am calling this via php like this,

$res=shell_exec("/usr/bin/expect $scriptPath ");

I could see, expect_out(buffer) which is set to $values, is being filled with half commands output from log_file.

Succesfull expect_out(buffer),

before trim match -show configuration | display set | match class | match rancid ^M
set system login user rancid class remote-backup^M
^M
{master:1}^M
USER_NAME@SWITCH> -

before match -show configuration | display set | match class | match rancid ^M
set system login user rancid class remote-backup^M
^M
{master:1}^M
USER_NAME@SWITCH>-

when called via php, expect_out(buffer) looks like this,

before trim match -show configuration | display set | m^MUSER_NAME@SWITCH> -

before match -show configuration | display set | m^MUSER_NAME@SWITCH>-

Somehow, when expect script called via php, buffer doesn't have all the commands output and match fails.
Can someone guide me what could be the mistake.Thanks.

1

1 Answers

0
votes

Why do the things differ? Well, there's all sorts of possible reasons. Different timing can be one of them; that's very possible and very difficult to prevent. You simply have to write your code to adapt to the variation; fortunately that's fairly easy.

In general, using $expect_out(buffer) itself is not recommended as it is not defined just how much data is accumulated in it. It'll probably have as much as it needed to match the expected pattern, but really isn't guaranteed to match much more than that. I'd expect the script to be more like this:

#!/usr/bin/expect
set ip ROUTER_IP
set uname USER_NAME
set psd PASSWORD
set log_file -a "/tmp/script.log"
spawn /usr/bin/ssh $uname@$ip

# Less nesting here! Makes the code much simpler to read
expect "ord:"
send "$psd\r"
expect {
    "> " {
        send_log "logged in\n"
    }
    "denied: " {
        puts "ERROR: Access Denied"
        exit 2
    }
}

# Classic code to read a multi-line result from a remote site, ONE LINE AT A TIME
set cmd "show configuration | display set | match class | match rancid"
send "$cmd\r"; # Had you forgotten this in pasting?
set values ""
expect {
    -re {^(.*\S|)\r?\n} {
        append values $expect_out(1,string) "\n"
        exp_continue
    }
    "> " {
        # Got the prompt again
    }
}

# This is your code again
send_log "\nbefore trim n match -$values-\n"
set values [string trim $values]
send_log "\nbefore match -$values-\n"
set found [regexp {match rancid\s+(.*)\s+\r\n\{.*} $values match px]
if { $found == 1  && $px != "" } {
    puts "Rancid Exists... !!!"
    send "exit\r"
    exit 1
} else { 
    puts "Coundnt find Rancid ... !!!"
    send "exit\r"
    exit 2
}

In general, you should try to structure your code so that you are interleaving your expects and sends, though a sequence of several expects in a row can be OK. However, you can think of it as if the send isn't really processed until the next expect.

Note the use of exp_continue. That's a very powerful feature of Expect, which lets you keep the current expect statement doing more matching. It lets you build matcher loops, such as the one I did, and greatly simplifies many interaction problems. And for goodness' sake, try to avoid nesting the code as much as you can! Programs are hard enough without making them extra complicated!