Expect script for checking ssh connection for a list of ips

2.8k views Asked by At

Can anyone help me in creating an expect script to just do an SSH on a list of servers and check if it was fine. I do not need to interact, do not need to fire any command on each server, I just want to do an ssh and come out, with a return code mentioning whether it was successful or not.

Expect is important here, as I do not have an option to setup a passwordless connection. Also there is a possibility that passwordless connection is setup on some of those servers.

I tried something like:

#!/usr/local/bin/expect
set timeout 10
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set prompt "(>|%|\\\\\\\$|#|]|) \$"
spawn ssh "$user\@$ip"
expect "Password:"
send "$password\r"
send "echo hello\r"
expect "hello"
send "exit\r"

But this gets stuck on the first server, and does nothing after that.

Thanks, Piyush

1

There are 1 answers

0
Dinesh On BEST ANSWER

A generalized idea can be having a procedure to spawn the ssh and close the connection which will maintain the connections local to the scope, so that global spawn_id won't get affected at all.

#!/usr/bin/expect
proc isHostAlive {host user pwd} {
 # We escaped the `$` symbol with backslash to match literal '$'
 # Commonly used prompt types
 set prompt "#|%|>|\\\$"
 set timeout 60
 spawn ssh $user@$host
 expect {
    timeout  {return FAIL}
    "(yes/no)" {send "yes\r";exp_continue}
    "password:" {send "$pwd\r";exp_continue}
    -re $prompt 
 }
 set msg "Hello World"
 send "echo $msg\r"
 expect { 
    timeout {return FAIL}
    "\r\n$msg"
 }
 send "exit\r"
 expect {
    timeout {return FAIL}
    eof
 }
 return PASS
}

# Lists to maintain the each host's information 
set serverList {server1 server2 server3}
set userList {user1 user2 user3}
set pwdList {pwd1 pwd2 pwd3}

# Looping through all the servers and checking it's availability
foreach server $serverList user $userList pwd $pwdList { 
    puts "\n==>Status of $server : [isHostAlive $server $user $pwd]\n"
}

With exp_continue, we can handle even if any host does not have password. Basically exp_continue will cause the expect to run again. So, among the mentioned phrase whichever comes, it will be handled. i.e. if expect sees (yes/no), it will send yes, if expect sees password, it will send the password value and so on. Then expect will continue to wait for the whole set of phrases again.

The reason why I have added yes/no is because if suppose the host's RSA fingerprint needs to be saved.

After successful login, I am echoing Hello World and expecting for the echoed message. If you have noticed, I have used \r\n$msg in the expect statement. Why do we need \r\n here ?

Here is why. Whenever we send command that will be seen by the expect also and it will try to match against that too. If it matched, it will proceed as such.

Sample echo command output

dinesh@dinesh-VirtualBox:~/stackoverflow$ echo Hello World
Hello World
dinesh@dinesh-VirtualBox:~/stackoverflow$ 

The string we want to expect is already available in the send command. So, to make sure the matching expect string is only from the actual echoed response, I have added \r\n which will help us in matching what is necessary.

Then at last of the proc, I am sending exit command which will close the ssh connection and to match the same eof (End Of File) is used. In all sort of failure cases, the procedure will return FAIL.