#include in Expect script - linux

So I am having trouble making TestCases with expect scripts, I have like 10 TestCases which all starts and ends with the same "functions" like login and logout or turning some flags off, is there a possibility to include them or execute them remotely from my script, like spawn login.exp or even better to put them in functions ?
TC01.exp
#!/usr/bin/expect -f
set timeout 5
#example of getting arguments passed from command line..
#not necessarily the best practice for passwords though...
set server [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
set no [lindex $argv 3]
set counter 0
# connect to server via ssh, login, and su to root
send_user "connecting to $server\n"
spawn ssh $user#$server
#login handles cases:
# login with keys (no user/pass)
# user/pass
# login with keys (first time verification)
expect {
"> " { }
"$ " { }
"assword: " {
send "$pass\n"
expect {
"> " { }
"$ " { }
"assword: " {
send_user "\nLogin failed\n"
incr counter 1
exit 5
}
}
}
"(yes/no)? " {
send "yes\n"
expect {
"> " { }
"$ " { }
}
}
default {
send_user "Login failed\n"
incr counter 1
exit
}
}
#TEST CASE HERE
#login out
send "exit\n"
expect {
"> " {}
default {}
}
if { $counter > 0 } {
send_user "\nTestCase finished with some errors!\nFAILED!!!\nERRORS $counter\n";
exit 4;
}
send_user "\nTestCase finished with SUCCESS!\nERRORS: $counter\n";
So i would like to have login and count_error as functions, so I would be able to create my test cases just like this:
TC01.exp
#!/usr/bin/expect -f
set timeout 5
set server [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
set no [lindex $argv 3]
set counter 0
login($server, $user, $pass)
#TestCase
Errors($counter)
exit

Expect is actually Tcl with some bindings to pty's and fork() thrown in. All very well described on http://tcl.tk
Functions in Tcl are done with proc (look here) eg:
lib.tcl
proc login {server user pass} {
# Your expect code goes here
return $errorCount
}
proc errors {errorCount} {
if {$errorCount > 0} {
# Your error handling code here
}
}
test:
#!/usr/bin/env expect
source lib.tcl
set errors [login $server $user $pass]
# your test case here
errors $errors

Related

Expect ignoring pattern matching and not exiting

I'm new using expect and is puzzling me big time. It works perfectly with one pattern but when the second case comes up it just ignores the exit completely. First, this is my code.
#!/usr/bin/expect
#Usage migration_test.xpct <ssh_password> <vmname> <no_migraciones>
set timest [ timestamp -format %Y-%m-%d_%H-%M ]
set vmname [lindex $argv 1]
log_file migtest_${vmname}_${timest}.log ;
set password [lindex $argv 0]
set num [lindex $argv 2]
set failureMsg "Status: Failure\n\r"
set timeout 60
spawn ssh admin#localhost -p 10000
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "$password\r" }
} "*?assword" { send "$password\r" }
for {set i 0} {$i < $num} {incr i 1} {
expect "OVM> " {
send "show Vm name=$vmname\r"
expect {
$failureMsg { }
-re "Status = Running\n\r" {
exp_continue
}
-re "Server = .*? \\\[(.*?)(1|2)?\\\]\n\r" {
set destserver $expect_out(2,string);
if { $destserver == 1 } {
send_user "\n\nMIGRATION [ expr $i+1 ] of $num\n\n"
send "migrate Vm name=$vmname destServer=serv_prod02\r"
expect {
-re "JobId: (.*?)\n\r" {
set jobid $expect_out(1,string);
send "show Job id=$jobid\r";
expect {
-re "Command:(.*?)\n\r" { send_user "\n\nWaiting 30secs before next migration\n\n";
sleep 30; }
}
}
-re "Status: Failure\n\r" { send_user "\n\nExiting\n"; exit 1 }
}
} else {
send_user "\n\nMIGRATION [expr $i+1] of $num\n\n"
send "migrate Vm name=$vmname destServer=serv_prod01\r"
expect {
-re "JobId: (.*?)\n\r" {
set jobid $expect_out(1,string);
send "show Job id=$jobid\r";
expect {
-re "Command:(.*?)\n\r" { send_user "\n\nWaiting 30secs before next migration\n\n";
sleep 30; }
}
}
-re "Status: Failure\n\r" { send_user "\n\nExiting\n"; exit 1 }
}
}
}
}
}
}
send "exit\r"
expect eof
The problem comes when it reaches the "migrate vm" section. That's a job I'm sending to a CLI (oracle ovm cli to be precise) and the job can either fail or success. I want to print the job details when it success but finish the entire execution if the job fails (since it already shows the reason and I don't have to expand the job details).
Here is how the output of a successful job looks:
MIGRATION 5 of 12
migrate Vm name=slestest_temp_share_vm destServer=serv_prod01
Command: migrate Vm name=slestest_temp_share_vm
destServer=serv_prod01
Status: Success
Time: 2016-04-13 10:45:24,174
JobId: 12345678978
OVM> show Job id=12345678978
Command: show Job id=12345678978
Status: Success Time: 2016-04-13 10:45:24,188
Data:
Run State = Success
Summary State = Success
Done = Yes
Summary Done = Yes
Job Group = No
Username = admin
Creation Time = Apr 13, 2016 10:44:45 am
Start Time = Apr 13, 201 10:44:45 am
End Time = Apr 13, 2016 10:45:23 am
Duration = 37s
Id = 12345678978 [Migrate Vm: slestest_temp_share_vm to Server: serv_prod01]
Name = Migrate Vm: slestest_temp_share_vm to Server:serv_prod01
Description = Migrate Vm: slestest_temp_share_vm to
Server: serv_prod01 Locked = false
OVM>
Waiting 30secs before next migration
And here is how a failured job looks like:
MIGRATION 4 of 12
migrate Vm name=slestest_temp_share_vm destServer=serv_prod01
Command: migrate Vm name=slestest_temp_share_vm destServer=serv_prod01
Status: Failure
Time: 2016-04-13 11:31:08,819
JobId: 1460564963372
Error Msg: Job failed on Core: OVMAPI_5001E Job: 1460564963372/Migrate Vm: slestest_temp_share_vm to Server: serv_prod01/Migrate Vm: slestest_temp_share_vm serv_prod01, failed. Job Failure Event: 1460565064570/Server Async Command Failed/OVMEVT_00C014D_001 Async command failed serv_prod02. Object: slestest_temp_share_vm, PID: 1724,
Server error: Command: ['xm', 'migrate', '--live', '0004fb00000600009f354416bab38df6', '8.8.8.1'] failed (1): stderr: Error: ti
stdout: Usage: xm migrate
Migrate a domain to another machine.
Options:
-h, --help Print this help.
-l, --live Use live migration.
-p=portnum, --port=portnum
Use specified port for migration.
-n=nodenum, --node=nodenum
Use specified NUMA node on target.
-s, --ssl Use ssl connection for migration.
-c, --change_home_server
Change home server for managed domains.
, on server: serv_prod02, associated with object: 0004fb00000600009f354416bab38df6 [Wed Apr 13 11:31:04 2016]
Why does the Status: Failure is ignored? Also, when that happens it seems it jumps an iteration of the loop, if it was in the 5th it then shows "Migration 7 of 12" for example.
Thanks everyone
I can suggest two things, one you can rewrite code to avoid duplicacy. Second, I think you are matching for both \n\r at the end of pattern. Try with \n alone or use \n?\r? which will match zero, one, or both line endings.
-re "Server = .*? \\\[(.*?)(1|2)?\\\]\n" {
set destserver $expect_out(2,string);
send_user "\n\nMIGRATION [ expr $i+1 ] of $num\n\n"
if { $destserver == 1 } {
send "migrate Vm name=$vmname destServer=serv_prod02\r"
} else {
send "migrate Vm name=$vmname destServer=serv_prod01\r"
}
expect {
-re "JobId: (.*?)\n" {
set jobid $expect_out(1,string);
send "show Job id=$jobid\r";
expect {
-re "Command:(.*?)$" {
send_user "\n\nWaiting 30secs before next migration\n\n";
sleep 30;
}
}
}
-re "Status: Failure\n" { send_user "\n\nExiting\n"; exit 1 }
}
}
Well, after some tests I found the problem. It seems I didn't understand how the timeout worked in expect. Every time a failured migration was performed it exceeded the timeout.
This wasn't evident for me because, although the timeout was exceeded, the script still kept waiting for the answer and printed it anyways, just none of the patterns I was expecting to get were being checked.
The solution was either use the "timeout" command or set it higher. I did the later and everything is running fine now.

How test SSH connectivity with expect script

I have Linux red-hat machine - version 5.X
I want to create expect script that verify if ssh login to remote machine is successfully
my test is only to get the password prompt on remote login ( I not want to enter password )
its like to test ssh connectivity
what I created until now is the following script
so if I get password prompt then script need to return 0
if not then script need to return 1
the problem with my script is that - the script return 0 even ssh login is failed ( no password prompt )
#!/usr/bin/expect
set LOGIN [lindex $argv 0]
set PASSWORD [lindex $argv 1]
set IP [lindex $argv 2]
set timeout 10
spawn ssh -o StrictHostKeyChecking=no $LOGIN#$IP
expect -re "(Password:|word:)"
exit 0
expect {
timeout {error "incorrect password"; exit 1}
eof
}
please advice how to update my script ?
You have to enclose the condition in one expect as,
set timeout 10
spawn ssh -o StrictHostKeyChecking=no $LOGIN#$IP
expect {
timeout {puts "Timeout happened";exit 0}
eof {puts "EOF received"; exit 0}
-nocase "password:" {puts "SSH connection is alive for the host $IP"; exit 1}
}
Have a look at the here which resembles your question.
sshlogin.expect :
set timeout 60
spawn ssh [lindex $argv 1]#[lindex $argv 0]
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "[lindex $argv 2]\r" }
} "*?assword" { send "[lindex $argv 2]\r" }
interact
Usage :
sshlogin.expect <host> <ssh user> <ssh password>

Is it possible to set exit code of 'expect'

The following bash script doesn't work because command 'expect' always return 0 regardless which exit code of the remote script /tmp/my.sh returns.
any idea to make it work? thanks.
#!/usr/bash
user=root
passwd=123456abcd
host=10.58.33.21
expect -c "
spawn ssh -o StrictHostKeyChecking=no -l $user $host bash -x /tmp/my.sh
expect {
\"assword:\" {send \"$passwd\r\"}
eof {exit $?}
}
"
case "$?" in
0) echo "Password successfully changed on $host by $user" ;;
1) echo "Failure, password unchanged" ;;
2) echo "Failure, new and old passwords are too similar" ;;
3) echo "Failure, password must be longer" ;;
*) echo "Password failed to change on $host" ;;
esac
Edited at 10:23 AM 11/27/2013
Thanks for the comments. Let me emphasis the problem once again,
The main script is supposed to run on linux server A silently, during which it invokes another script my.sh on server B unattended. The question is how to get exit code of my.sh?
That's why I cannot leverage ssl_key approach in my case, which requires at least one time configuration.
#!/usr/bin/expect
set user root
set passwd 123456abcd
set host 10.58.33.21
set result_code 255
# exp_internal 1 to see internal processing
exp_internal 0
spawn ssh -o StrictHostKeyChecking=no -l $user $host bash -x /tmp/my.sh && echo aaa0bbb || echo aaa$?bbb
expect {
"assword:" {send "$passwd\r"; exp_continue}
-re "aaa(.*)bbb" {set result_code $expect_out(1,string)}
eof {}
timeout {set result_code -1}
}
switch $result_code {
0 { puts "Password successfully changed on $host by $user" }
1 { puts "Failure, password unchanged" }
2 { puts "Failure, new and old passwords are too similar" }
3 { puts "Failure, password must be longer" }
-1 { puts "Failure, timeout" }
default { puts "Password failed to change on $host" }
}
exit $result_code

SSH session exit in TCL/EXPECT

I ran into the problem that it keeps exit and I just couldn't figure it out why it couldn't put in anymore expect any send statement to keep the SSH session going???
#!/usr/bin/expect
set timeout 15
set hostname [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set enable [lindex $argv 3]
set send_slow {10 .001}
log_user 1
expect -f snmp2.exp 172.28.135.24 localadmin Gr0upM540 z540Mxy3
set configuration /tmp/ntp
set routerlist [open "/tmp/routerlist" "r"]
set logfile [open "/tmp/ntp_config" "w"]
spawn ssh $username#$hostname
expect {
timeout { send_user "\nFailed to get password prompt\n"; exit 1 }
eof { send_user "\nSSH failure for $hostname\n"; exit 1 }
"*#" {}
"*assword:" {
send "$password\r"
}
}
send "/r"
expect {
"*#" {}
"*>" {
send "enable\r"
expect "*assword"
send "$enable\r"
expect "*#"
}
}
send "exit\r"
send_user "\nSuccessfully changed SNMP password on $hostname\n"
close

Transfer environment variables with expect

I am writing an expect script and need to transfer environment variables over a telnet session (which the man page proudly touts as a feature but provides no other mention).
So something like this:
#!/usr/bin/expect -c
spawn telnet 1.2.3.4
set rpath ""
expect "#" { set rpath $PATH }
where $PATH is in the environment of the remote system..any ideas?
You could easily do this by spawning bash and then issue telnet to the remote system.
I presume you want to set the path variable from the local machine to the remote machine.
#!/bin/sh
# the next line restarts using tclsh \
exec expect "$0" "$#"
set prompt "~$"
set hostname "anyhost"
spawn bash
send "echo $PATH\r"
expect {
$prompt {
set pathVariable $expect_out(buffer)
}
timeout {
send_user "path hasn't been set - exiting\n"
close
exit 1
}
}
send "telnet $hostname\r"
expect {
"Login:" {}
"telnet: " {
send_error "$argv0 couldn't login to $hostname\n"
exit 1;
}
timeout {
send_error "$argv0 couldn't login to $hostname, timeout of $timeout passed\n"
exit 1;
}
}
send "$username\r"
expect "Password:"
send "$password\r"
expect $remotePrompt
send "bash\r"
send "export PATH=$pathVariable\r"
# continue with whatever you want.

Resources