Expect script issue with if statement - expect

I trying different approach to update existing expect script that I have inherit...
What I trying to achieve is to check Patch Level and depending on that, execute a set of commands.
But I can't make it work. :(
I tried as to below example and also tried with 'if' statement but I am clearly to fresh to solve this task.
I would be very grateful for some feedback what I am doing wrong.
#!/usr/bin/expect -f
# Destination: user#host
set destination [lindex $argv 0]
# Destination password
set pass [lindex $argv 1]
# Expect timeout
set timeout 1000
spawn ssh $destination
expect {
"hostname nor servname provided, or not known" {
exp_close
}
"yes/no)?" {
send "yes\n"
exp_continue
}
"assword:" {
send "$pass\n"
exp_continue
}
"#" {
log_file "./tmp/$destination.log"
send "cat /etc/*release | grep PATCHLEVEL\n"
expect {
"PATCHLEVEL = 3" {
send "zypper ar http://xxx.xxx.xxx.xxx/up2date/SLES11-SP3-LTSS-Updates/sle-11-x86_64/ SLES11-SP3-LTSS-Updates && zypper mr -r SLES11-SP3-LTSS-Update;zyppe
r ref -s;zypper --non-interactive update\n"
log_file
send "exit\n"}
"PATCHLEVEL =4 " {
send "zypper --non-interactive update\n"
log_file
send "exit\n"}
exp_close
}
}
}

Related

Non-reliable results from Expect when running a remote script

Because my company insists on using sudo I now have to scrap my ssh system I've built for running remote commands.
I have a script that will connect to a server, sudo into the db2 inst owner account and then run a script (previously delivered into /tmp). Maybe 1 in 10 times it will work (outputs 'Hello world' for my test).
Here is the ksh script on the remote server (/tmp/cwow/generic.ksh):
#!/usr/bin/ksh93
echo "Hello world"
[I've also tried adding sleep 5 and wait with mixed results but it doesn't solve the problem]
The expect script I'm running locally is:
#!/usr/bin/expect
set spath /tmp/cwow/generic.ksh
set pass $env(MYEXPECTPASS)
set user $env(MYEXPECTUSER)
if { [llength $argv] != 2 } {
send_user "USAGE: $argv0 host inst\n"
exit
}
set host [lindex $argv 0]
set inst [lindex $argv 1]
set timeout 10
log_user 1
exp_internal 0
eval spawn /usr/bin/ssh -t $user#$host "sudo su - cwow"
expect {
timeout { send_user "TimedOut"; exit }
-glob "assword:" {
send "$pass\r"
expect {
-glob "assword:" {
send "$pass\r"
expect {
-glob " " {
send "/tmp/cwow/generic.ksh\r\n"
expect {
-glob "world" {
send_user "Got it\r"
}
}
}
}
}
}
}
}
I should also note that I never get the 'Got it' message but I don't really need that to work, just curious why it doesn't. What I need to work reliably is for the script to run and, most of the time, it doesn't appear to.
Any ideas for a weak expect user would be greatly appreciated.
(not an answer, just a formatted comment)
You don't need to nest all the expect commands: if you expect a pattern with no action body, the script will continue to the next command. This is more readable, IMO:
expect {
timeout { send_user "TimedOut"; exit }
"assword:"
}
send "$pass\r"
expect "assword:"
send "$pass\r"
expect " "
send "/tmp/cwow/generic.ksh\r"
expect "world"
send_user "Got it\n"
Note, you should send to the spawned process with \r as "hitting Enter". But \n is used for send_user.

Expect script not executing after ssh login

I'm trying to write an expect script to:
Connect to a remote server providing the user and password
Loop through a local file reading each line
Execute a specific command on the remote server for each of those lines
I could successfully achieve the step #1 and was testing the #3 with a simple scenario, but couldn't make it work yet. Unfortunately in the line 8 of the script, after sending the password, I just get logged into the server as I would have been logged manually (I can interact with the console) and the rest is not executed.
How can I circumvent this problem?
This is the script:
#!/usr/bin/expect
set timeout 20
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh -t -t "$user\#$ip"
expect "Password:"
send "$password\r";
expect "NYXOOBPN402(config)$"
send "delete decoders:ASX-Trade24-FIX.mdp\r"
expect "Are you sure you want to delete 'decoders:ASX-Trade24-FIX.mdp' (y/n)?"
send "y\r";
And this is how I'm executing it:
./test_expect.sh 172.18.250.20 admin admin
The problem is that my expect was incorrect in the line 17. Instead of "NYXOOBPN402(config)$", I should just put "*(config)*", since there's a lot of text before this part that was not being matched.
This is my final script for someone that runs into this same issue:
#!/usr/bin/expect
set timeout 9
# Check if the parameters are correct
if {[llength $argv] == 0} {
send_user "Usage: ./test_expect.sh ip username password\n"
exit 1
}
# Read the file with all the decoders names to be deleted
set f [open "decoders.txt"]
set decoders [split [read $f] "\n"]
close $f
# debug mode - very useful:
#exp_internal 1
# Connect to the server
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh -t "$user\#$ip"
expect "Password: "
send "$password\r";
sleep 3
# send ctrl+c since this terminal shows a lot of decoders
#send \x03
expect {
default { send_user "\nCould not find the expected value.\n"; exit 1 }
"*(config)*" {
# Loop through all the decoders
foreach decoder $decoders {
#send_user "Removing $decoder\n"
send "delete decoders:$decoder\r"
expect {
"Are you sure you want to delete*" { send "y\r" }
"*decoder will still be active*" { send_user "\nRemoved $decoder successfully\n" }
"*no such file or directory" { send_user "\nDecoder $decoder already deleted.\n" }
default { send_user "\nNot expected value with $decoder, please debug.\n"; exit 1 }
}
}
}
}

Run command on cisco switch

I made the below script to login to a switch and execute a command. as soon as i execute the script it logins to switch and exits without running the command.
#!/usr/bin/expect -f
set timeout 3000
spawn bash -c "ssh ananair#10.60.255.100"
expect "*assword:"
send "pass#123\n"
expect "*#"
send "show interfaces status"
I suspect the problem is the missing \n in your final send. If you simply send "string", then expect never hits Enter at the end of the command, And since there's no final expect, it figures its work is done, and exits, probably even before it has a chance to echo the command it sent but never executed.
Consider the following:
#!/usr/bin/expect -f
set timeout 3000
spawn ssh switchhostname # no need to run this inside a shell
expect "ssword:"
send "1ns3cur3\n" # it would be better to use key based auth...
expect "#"
send "term len 0\n" # tell the switch to avoid "More" prompts
expect "#"
send "show interfaces status\n" # note the final "\n"
expect "#" # don't quit until the "show" command finishes
You might also consider getting access to this information via SNMP.
try \r instead of \n
I have my expect script that I use to login to my cisco switches for me. and then I use interact from the expect script that leaves me at the cisco prompt.
I would need to rework it to not show passwords but I can definitely help you out.
I didn't need timeouts in mine.
#!/usr/bin/expect
proc enable {} {
expect "assword:" {
send "<enable password>\r"
expect "#" {
}
}
}
proc login {} {
send "<login password>\r"
expect {
"failed" {
send "<username>\r"
enable
}
">" {
send "en\r"
enable
}
"#" {
}}
}
set user_computer_attached [lindex $argv 0]
set user_computer [split $user_computer_attached "#"]
spawn ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 \
-oGlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null \
-oStrictHostKeyChecking=no $user_computer_attached
expect_after eof {
wait
spawn telnet [lindex $user_computer 1]
expect "name:"
send [lindex $user_computer 0]
send "\r"
expect "assword:" {
login
}
}
expect "assword:" {
login
}
interact

Expect Script - If SSH fails, use telnet

I am iterating over a list of IP addresses and loging into them with SSH. But, some of them do not have SSH, but rather, only telnet. How do I tell Expect that: if SSH fails, try again but using telnet?
I know that a message "connection refused" is outputted when SSH fails, but when I try to use that as a condition, expect doesn't work with it...after the SSH attempt is refused, the script breaks.
Here is a code snippet - this is not working:
foreach line $MACHINES {
spawn ssh -q $USER#$line
expect {
-ex "ssh: connect to host $line port 22: Connection refused" {
spawn telnet -l $USER#$line; continue
}
}
}
Thank you so much in advance,
D
Have timeout handle the failed connection after your expect. Some systems may not even be kind enough to spit out connection refused. The following was tested on a system that does not inform the user it's spawn ssh timed out.
#!/usr/bin/env expect
# invoke example ./telnet.exp 127.0.0.1 username password
set IP [lindex $argv 0]
set User [lindex $argv 1]
set Password [lindex $argv 2]
# Set timeout from default 10 seconds
set timeout 5
# connect without asking yes/no for known_hosts
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $User#$IP
# I'm not being cheeky, I left out the p/P because I avoid regex where possible
expect {
-nocase "refused" { spawn telnet $IP }
timeout { spawn telnet $IP }
"assword:" { send "$Password\r" }
}
interact
Here is my suggested change to your snippet (this is untested)
foreach line $MACHINES {
spawn ssh -q $USER#$line
expect {
-ex "ssh: connect to host $line port 22: Connection refused" {
spawn telnet -l $USER#$line; continue
}
timeout {
spawn telnet -l $USER#$line; continue
}
}
}
Please pardon me for my belated response; I appreciate everyone's contribution.
I ended up using this little block successfully. I believe the "wait" command functions to remove the SSH process from the process list.
expect {
eof {wait;spawn telnet -l $USER $line}
}
Had to clean up some users on cisco routers (mixed bag ssh/telnet). Found this thread. Thanks for pointing me in a direction.
#!/usr/bin/expect
set arg1 [lindex $argv 0]
set arg2 [lindex $argv 1]
set timeout 120
set username "router_username"
set password "router_password"
spawn telnet $arg1
expect {
-re "Connection refused" { spawn ssh -o StrictHostKeyChecking=no $arg1 -l $username }
"Username" { send "$username\n"; }
}
expect "Password" { send "$password\n"; }
expect "#$" { send "conf t\n" ; }
expect "config)#$" { send "no username $arg2\n"; }
expect {
-re "This operation will remove all username*" { send "\n"; exp_continue }
"config)#$" { send "exit\n"; }
}
expect "#$" { send "wr\n" ; }
expect "#$" { send "end\n" ; }
Don't do that. If an attacker is able to bring down the ssh service on that box, or man in the middle that box, he could force unencrypted telnet and get credentials to log into that server.
Don't try to use telnet as fallback for ssh. A script which is doing so can be easily exploited from remote.

How to use su command from within ssh command in unix shell script

My Requirement is to login into the remote machine and make some folders for this I am planning to take username and pwd as input from the user and try to make automate shell script.
1.)I used the following code to ssh into the machine and give the expected pwd to login into this machine.
DSADMINPWD=dsadminpwd
PWD=pwd
/usr/bin/expect<<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no <username>#<remotemachineurl>
expect "password"
send "$PWD\n"
EOD
The above works fine. But while doing su dsadmin after this. I am unable to go inside that
user from the previously taken password.
2.) I have to change user like su dsadmin from inside this machine. Also there is a password for dsadmin. But it is not working properly.
DSADMINPWD=dsadminpwd
PWD=pwd
/usr/bin/expect<<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no <username>#<remotemachineurl>
expect "password"
send "$PWD\n"
EOD
su dsadmin
<like to make some folders in dsadmin directory>
exit...
After doing su dsadmin it will come as
bash-3.00$
No sign of password or anything here.
From the above it is not working
Could you please suggest if anything is possible to make su after ssh with password in automated script. Any suggestions would be highly appreciated.
Thanks!
I used the expect command long time ago and it worked smoothly with the su command even if it was launched from a ssh console.
Here some examples that maybe you can find useful.
First of all the bash script:
#!/bin/bash
exec 1> stdoutfile
exec 2> stderrfile
./yourexpectscript YOUR_REMOTEIP_HERE userexample passwordexample
Secondly the expect script:
#!/usr/bin/expect --
send_user "connecting to remote server\n"
set server [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
set dsadminpass "dsadminpwd"
spawn ssh $user#$server
expect "password: "
send "$pass\n"
expect {
"> " { }
"$ " { }
"# " { }
}
#example command in ssh shell
send "ls -lah\n"
expect {
"> " { }
"$ " { }
"# " { }
default { }
}
#su command
send "su - dsadmin\n"
expect {
"Password: " { }
}
send "$dsadminpass\n"
expect {
"> " { }
"$ " { }
"# " { }
default { }
}
#example command in dsadmin shell
send "ls -lah\n"
#login out dsdamin shell
send "exit\n"
expect {
"> " { }
"$ " { }
"# " { }
default { }
}
#login out ssh connection
send "exit\n"
send_user "connection to remote server finished\n"

Resources