show ip interface brief in expect script - telnet

I would like to be able to setup a expect script file that would remote into multiple switches.
Do you know how to write expert script for telnet to 200 switches and run a command example: show ip interface brief then list all switch status on Linux??
#!/usr/bin/expect -f
spawn telnet 192.168.0.1
expect "Username:"
send "MyUsername\r"
expect "assword:"
send "MyPassword\r"
send "show ip int br\r"
interact timeout 5
expect {
"MORE --, next page" {send -- " "; exp_continue}
"*?2626#*" {send -- "exit \r"}
}
Thanks Dinesh, after my expect script as below:
!/usr/bin/expect -f
Slurp up the input file
set fp [open "input.txt" r]
set file_data [read $fp]
close $fp
set prompt ">"
log_file -noappend switch_port_status.txt
foreach ip [split $file_data "\n"] {
puts "Switch $ip Interface Status"
spawn telnet $ip
expect "Username:"
send "MyUsername\r"
expect "assword:"
send "MyPassword\r"
expect $prompt
# To avoid sending 'Enter' key on huge configurations
send "show ip int br\r"
expect $prompt
expect {
-ex "--More--" { send -- " "; exp_continue }
"*>" { send "exit\r" }
}
set timeout 1; # Reverting to default timeout
# Sending 'exit' at global level prompt will close the connection
expect eof
}
Thats workable. But following is the error message i got, how can i fix this? thanks
switch-hostname>Switch Interface Status
spawn telnet
usage: telnet [-l user] [-a] host-name [port]
send: spawn id exp9 not open
while executing
"send "MyUsername\r""
("foreach" body line 5)
invoked from within
"foreach ip [split $file_data "\n"] {
puts "Switch $ip Interface Status"
spawn telnet $ip
expect "Username:"
send "MyUsername\r"
expec..."
(file "./autotelnet.sh" line 8)

You can use a file which inputs the switch IP information in a line by line manner. Using terminal length 0 will save our work & finally log_file comes in handy to save the output.
#!/usr/bin/expect -f
#Slurp up the input file
set fp [open "input.txt" r]
# To avoid empty lines, 'nonewline' flag is used
set file_data [read -nonewline $fp]
close $fp
set prompt "#"
log_file -noappend switch_port_status.txt
foreach ip [split $file_data "\n"] {
puts "Switch $ip Interface Status"
spawn telnet $ip
expect "Username:"
send "MyUsername\r"
expect "assword:"
send "MyPassword\r"
expect $prompt
# To avoid sending 'Enter' key on huge configurations
send "terminal length 0\r"
expect $prompt
set timeout 120;# Increasing timeout to 2mins, as it may take more time to get the prompt
send "show ip int br\r"
expect $prompt
set timeout 10; # Reverting to default timeout
# Sending 'exit' at global level prompt will close the connection
send "exit\r"
expect eof
}
Note : The close of connection is handled on the basis of sending exit command only. So, make sure, the exit command always sent at global level of prompt. Instead of depending on exit, we can also use typical way of closing the telnet connection i.e. 'Ctrl+]' key combination.
#This will send 'Ctrl+]' to close the telnet connection gracefully
send "\x1d"
expect "telnet>"
send "quit\r"
expect "Connection"
If suppose you want to get multiple commands output, then keep them in a file and send them one by one.

Related

Using nested spawn method in expect scripts

I have written following expect script:
/usr/bin/expect<<EOF
set SERVER_HOSTNAME "$env(SERVER_HOSTNAME)"
set USERNAME "$env(USERNAME)"
set PASSWORD "$env(PASSWORD)"
set timeout -1
spawn ssh "$USERNAME#$SERVER_HOSTNAME"
expect {
"Host key verification failed." { spawn ssh-keygen -R "$SERVER_HOSTNAME"; expect "known_hosts.old"; send_user "Updated host key details."; exp_continue}
"continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue}
"assword:" { send_user "Keygen details are correctly mapped for this server\n"}
}
EOF
Here I want that if the host key of a server can't be verified while spawing "ssh" process, then the nested spawn process "ssh-keygen -R" should remove old key. Then the "ssh" process should try to connect again so that new key can be added corresponding to that server.
But here, after the execution of:
send_user "Updated host key details."
method, expect process is exiting out from this script.
I know the alternative can be to divide this expect call into two steps, as follows:
/usr/bin/expect<<EOF
set SERVER_HOSTNAME "$env(SERVER_HOSTNAME)"
set USERNAME "$env(USERNAME)"
set PASSWORD "$env(PASSWORD)"
set timeout -1
spawn ssh "$USERNAME#$SERVER_HOSTNAME"
expect {
"Host key verification failed." { spawn ssh-keygen -R "$SERVER_HOSTNAME"; expect "known_hosts.old"; send_user "Updated host key details."; exp_continue}
"continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue}
"assword:" { send_user "Keygen details are correctly mapped for this server\n"}
}
spawn ssh "$USERNAME#$SERVER_HOSTNAME"
expect {
"continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue}
"assword:" { send_user "Keygen details are correctly mapped for this server\n"}
}
EOF
But do we have a way to execute this expect call in one go. In nutshell, I want to know, can we do the nesting of spawn process?
Whitespace goes a long way toward maintainable code: you don't need to squish so many commands per line.
spawn ssh "$USERNAME#$SERVER_HOSTNAME"
expect {
"Host key verification failed." {
spawn ssh-keygen -R "$SERVER_HOSTNAME"
expect "known_hosts.old"
send_user "Updated host key details."
exp_continue
}
"continue connecting (yes/no)" {
send "yes\r"
expect "Permanently added"
exp_continue
}
"assword:" {
send_user "Keygen details are correctly mapped for this server\n"
}
}
I this case, you don't need to interact with ssh-keygen, so use exec to simply call it
"Host key verification failed." {
exec ssh-keygen -R "$SERVER_HOSTNAME"
puts "Updated host key details."
exp_continue
}
If you need to spawn something and interact with it, you need to know that there is an implicit variable, spawn_id, created by spawn, and used by expect and send. You'll need to save the spawn_id of the current process before you spawn any other process. For example:
spawn process1
set ids(1) $spawn_id
expect -i $ids(1) "some pattern"
send -i $ids(1) "some string\r"
spawn process2
set ids(2) $spawn_id
expect -i $ids(2) "some pattern from process2"
send -i $ids(2) "some string to process2\r"

Error On Expect Script

I keep getting an error on this after sending the new password. It will change the password, but not do the rest of the script. Any ideas why it keeps erroring out. I wrote a very similar script for a different device and it works prefect, but the stuff after changing the password is not needed on that device. This device will not save the password after reboot if the rest is not completed. Doing it manually through ssh works just fine so its not the cmds that is the problem, it's something with the script.
#!/usr/bin/expect
set timeout -1
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn ssh $user#$line
expect "assword:"
send "$old\r"
sleep 10
send "passwd $user\r"
expect "assword:"
send "$new\r"
expect "assword:"
send "$new\r"
sleep 10
send "grep -v users.1.password= /tmp/system.cfg > /tmp/system.cfg.new\r"
sleep 10
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print \$2}'` >> /tmp/system.cfg.new\r"
sleep 10
send "cp /tmp/system.cfg.new /tmp/system.cfg\r"
sleep 10
send "save && reboot\r"
close
expect eof
}
The full script is alot bigger with with fail-safes if the device does not respond to ssh or the original password is wrong. That one won't work though until I figure out what is wrong with this portion. I just slimmed it down to figure out where the problem is happening. Also this line seems to be the issue as it does create the system.config.new on the line before it:
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print \$2}'` >> /tmp/system.cfg.new\r"
It was and works in ssh as this:
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print $2}'` >> /tmp/system.cfg.new\r"
But sends an error because of the $2 then is view-able by expect. I was told that putting a \$2 would make it only view-able to the remote shell. Any help would be great.
Originally it had expect "star" instead of the sleep cmds. I have been trying tons of stuff out on this script and once I get it to run incorporate it in my full script. The reason I am using sleep is because "star" doesn't seem to match output and fails on the second send $new/r. With sleep it has made it alot farther. I do have a correction though. It is actually making it right up to send "save && reboot\r". I am going to eventually use your $prompt suggestion from my other question in place of sleep or expect "star". With debug on this is where it throws the error:
send: sending "save && reboot\r" to { exp4 }
expect: spawn id exp4 not open
while executing
"expect eof"
("foreach" body line 21)
invoked from within
"foreach line [split $data \n] {
if {$line eq {}} continue
spawn ssh $user#$line
expect "assword:"
send "$old\r"
sleep 3
send "passwd $user\r"
e..."
(file "./ubnt.sh" line 15)
The "save && reboot\r" will kick out the ssh connection after it saves the settings, but it doesn't seem to be getting that far. Is there an issue with &&, maybe I need to /&&.
Interact instead of close and expect eof is the answer I will update with full script when it's done. Here is the working UBNT password change script:
#!/usr/bin/expect
set timeout 30
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn ssh $user#$line
expect {
"assword:" {
send "$old\r"
expect {
"assword:" {
close
continue
}}
expect {
"*" {
send "passwd $user\r"
expect "*assword:*"
send "$new\r"
expect "*assword:*"
send "$new\r"
expect "*"
send "grep -v users.1.password= /tmp/system.cfg > /tmp/system.cfg.new\r"
expect "*"
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print \$2}'` >> /tmp/system.cfg.new\r"
expect "*"
send "cp /tmp/system.cfg.new /tmp/system.cfg\r"
expect "*"
send "save && reboot\r"
interact
continue
}}}}
expect {
"*" {
close
continue
}}
expect eof
}
Here is the script for Mikrotik passwords:
#!/usr/bin/expect
set timeout 30
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn -noecho ssh -q -o "StrictHostKeyChecking=no" $user#$line
expect {
"assword:" {
send "$old\r"
expect {
"assword:" {
close
continue
}}
expect {
"*" {
send "user set $user password=$new\r"
expect ">"
send "quit\r"
close
continue
}}}}
expect {
"*" {
close
continue
}}
expect eof
}
Dinesh mentioned that I should not use "star" in my expect commands and said that instead use:
set prompt "#|%|>|\\\$ $"
And:
expect -re $prompt
Which I will probably change. The scripts also move onto next IP in list if the device cannot be reached via ssh or if the oldpassword does not work for the device. The only other change I plan to make on both is to have it create 3 log files listing successful ips, wrong password ips, and cant reach ips. This should only be one more command before each continue, but haven't yet researched how to output a variable to a local log file.
Thanks for your help on this Dinesh.

Expect Scripting Issue Reading From File

I am trying to change the password for a user on a bunch of linux based routers pulling IPs from a list. The iplist.txt is just a list of ips (one per line). This is the code I am trying to use:
#!/usr/bin/expect
set timeout 20
#Edit for User
set user username
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set hosts [split [read $f] "\n"]
close $f
foreach host $hosts {
spawn -noecho ssh -q -o "StrictHostKeyChecking=no" $user#$host
expect "assword:"
send "$old\r"
expect ">"
send "user set $user password=$new\r"
expect ">"
send "quit\r"
expect eof
close
}
Which works for the first ip on the list but send this error on the second:
spawn_id: spawn id exp4 not open
I got it to work this way as I wanted:
#!/usr/bin/expect
set timeout 30
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn -noecho ssh -q -o "StrictHostKeyChecking=no" $user#$line
expect "assword:"
send "$old\r"
expect ">"
send "user set $user password=$new\r"
expect ">"
send "\r"
expect ">"
send "quit\r"
send "\r"
expect eof
}
The next issue I run into is if the device doesn't have the old password or if the linux box cannot reach the device via ssh, it will error out with the same spawn id exp* not open when it gets to that IP and will not continue onto the next IP in the list. Is there anyway I can a statement that says if "assword:" comes up a second time to move onto next IP and if ">" comes up like its supposed to keep going with the script, then add a line after the spawn command that will move to the next IP in list if it doesn't receive the first expect "assword:"?
Any help would be appreciated. I am new to expect, but seems to be a really good tool for mass ssh processes in a script. Just having trouble tweaking it to not error out on 1 job instead of moving to next job upon error.
#!/usr/bin/expect
set timeout 30
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn -noecho ssh -q -o "StrictHostKeyChecking=no" $user#$line
expect {
"assword:" {
send "$old\r"
expect {
"assword:" {
close
continue
}}
expect {
"*" {
send "user set $user password=$new\r"
expect ">"
send "quit\r"
close
continue
}}}}
expect {
"*" {
close
continue
}}
expect eof
}
Probably a bit dirty scripting, but it works. Now if I can figure out how to export successful, wrong password, and timeout logs so I know if any error-ed out.

Sending commands via SSH with expect script

In a another question I was kindly provided with a solution to a dilemma I was having with expect and if/else statements (basically, my lack of knowledge in how they're written). The script I was given works with one exception: If I cleanly connect to a remote host the exit command is not sent which will close that connection and move onto the next host in the list.
This is the functional part of the script:
while {[gets $file1 host] != -1} {
puts $host
spawn -noecho ssh $host
expect {
"continue connecting" {
send "yes\r"
expect {
"current" {
send -- $pw2\r
exp_continue
}
"New password" {
send -- $pw1\r
exp_continue
}
"Retype new password" {
send -- $pw1\r
exp_continue
}
msnyder
}
send "exit\r"
}
}
interact
}
When I'm connected to the host I expect to see the standard Linux prompt: [username#host ~]$. At least, that's our standard prompt. The prompt is preceded by a banner. Might that be throwing a kink into this? I wouldn't think so since I'm only looking for "continue connecting" from the RSA fingerprint prompt and that is within text spread across multiple lines. I would think that expect is intelligent enough to ignore any scrolling text and only look at text in prompts that are looking for input.
I considered passing the exit command as an argument for the ssh $host command, but expect doesn't seem to like that.
Can someone now assist me in getting the exit command to be sent properly?
UPDATE: I've added the exp_internal 1 option to the script to see what it is doing and it seems to only ever be matching "continue connecting" even at the user prompt once an SSH connection is established. It doesn't appear to be executing the next comparison for "msnyder". http://pastebin.com/kEGH3yUY
UPDATE2: I'm making progress. Based on Glenn Jackman's script below, I was able to get this working until the password prompt appears:
set prompt {\$ $}
set file1 [open [lindex $argv 0] r]
set pw1 [exec cat /home/msnyder/bin/.pw1.txt]
set pw2 [exec cat /home/msnyder/bin/.pw2.txt]
while {[gets $file1 host] != -1} {
puts $host
spawn -noecho ssh -q $host
expect {
"continue connecting" {
send "yes\r"
expect {
"current" {
send -- $pw2\r
exp_continue
}
"New password" {
send -- $pw1\r
exp_continue
}
"Retype new password" {
send -- $pw1\r
exp_continue
}
-re $prompt {
send -- exit\r
expect eof
}
}
} -re $prompt {
send -- exit\r
expect eof
}
}
}
I have explicitly told it to send the exit command twice rather than trying the short-circuit method that Glenn used (it didn't seem to work per my comment under his answer).
What is now happening, is that it will loop through the list of hosts, send 'yes' to the RSA fingerprint prompt and then send 'exit' to the command prompt. However, when it hits a server that needs to have the password set it stops. It seems to eventually time out and then restarts with the next host. The current password is not being sent.
# a regular expression that matches a dollar sign followed by a space
# anchored at the end of the string
set prompt {\$ $}
while {[gets $file1 host] != -1} {
puts $host
spawn -noecho ssh $host
expect {
"continue connecting" {
send "yes\r"
expect {
# ...snipped...
-re $prompt
}
}
-re $prompt
}
send -- exit\r
expect eof
}
We've moved the "exit" to a place where it will be executed if you match either "comtinue connecting" or if you log in directly to your prompt.
Nice answer but I hate seeing code cut and pasted. One comment and a bit of refactoring: When you hit the "current" block, you change the password but never exit. It occurs to be that each scenario should end with you seeing the prompt and exiting:
while {[gets $file1 host] != -1} {
puts $host
spawn -noecho ssh -q $host
expect {
"continue connecting" {
send "yes\r"
exp_continue
}
"current" {
send -- $pw2\r
expect "New password"
send -- $pw1\r
expect "Retype new password"
send -- $pw1\r
exp_continue
}
-re $prompt
}
send -- exit\r
expect eof
}
If you see "continue connecting", you send yes and then wait to see "current" or the prompt.
If you see "current" you change the password and then wait to see the prompt.
If you see the prompt, the expect comment ends and the next thing you do is exit.
I've sorted out the answer. After looking at Glenn's script below it occurred to me that each block is a separate if/else when instead I need the password section to act as a single block and the exit to act as the else. This is what I now have:
set prompt {\$ $}
set file1 [open [lindex $argv 0] r]
set pw1 [exec cat /home/msnyder/bin/.pw1.txt]
set pw2 [exec cat /home/msnyder/bin/.pw2.txt]
while {[gets $file1 host] != -1} {
puts $host
spawn -noecho ssh -q $host
expect {
-re $prompt {
send -- exit\r
expect eof
}
"current" {
send -- $pw2\r
expect "New password"
send -- $pw1\r
expect "Retype new password"
send -- $pw1\r
}
"continue connecting" {
send "yes\r"
expect {
"current" {
send -- $pw2\r
expect "New password"
send -- $pw1\r
expect "Retype new password"
send -- $pw1\r
}
-re $prompt {
send -- exit\r
expect eof
}
}
}
}
}
It addresses four situations:
I do NOT have to accept the RSA fingerprint OR change my password.
I do NOT have to accept the RSA fingerprint but DO have to change my password.
I DO have to accept the RSA fingerprint AND change my password.
I DO have to accept the RSA fingerprint but NOT change my password.

how to hide ssh expect user/password

My Expect script shows password/user in clear text and I want to hide it.
#!/usr/local/bin/expect
########################################################################################### ############
# Input: It will handle two arguments -> a device and a show command.
########################################################################################### ############
# ######### Start of Script ######################
# #### Set up Timeouts - Debugging Variables
log_user 0
set timeout 10
set userid "USER"
set password "PASS"
# ############## Get two arguments - (1) Device (2) Command to be executed
set device [lindex $argv 0]
set command [lindex $argv 1]
spawn /usr/local/bin/ssh -l $userid $device
match_max [expr 32 * 1024]
expect {
-re "RSA key fingerprint" {send "yes\r"}
timeout {puts "Host is known"}
}
expect {
-re "username: " {send "$userid\r"}
-re "(P|p)assword: " {send "$password\r"}
-re "Warning:" {send "$password\r"}
-re "Connection refused" {puts "Host error -> $expect_out(buffer)";exit}
-re "Connection closed" {puts "Host error -> $expect_out(buffer)";exit}
-re "no address.*" {puts "Host error -> $expect_out(buffer)";exit}
timeout {puts "Timeout error. Is device down or unreachable?? ssh_expect";exit}
}
expect {
-re "\[#>]$" {send "term len 0\r"}
timeout {puts "Error reading prompt -> $expect_out(buffer)";exit}
}
expect {
-re "\[#>]$" {send "$command\r"}
timeout {puts "Error reading prompt -> $expect_out(buffer)";exit}
}
expect -re "\[#>]$"
set output $expect_out(buffer)
send "exit\r"
puts "$output\r\n"
... and add -OUseBatchMode=Yes so that if there is a problem with your keys, ssh will fail immediately (you can verify exit code) rather than just falling back to password mode and hanging (as you are running interactively)
The same issue exists in any scripting language. The script can't type in your password if it doesn't know it... the easiest solution is to use passwordless ssh, using keys.
Have a look at Glenn's answer here: How can I make an expect script prompt for a password?
I was trying to do the same sort of thing and found this useful.
The following is expect code that prompts for the username and password:
send_user "Username?\ "
expect_user -re "(.*)\n"
set user $expect_out(1,string)
send_user "password?\ "
stty -echo
expect_user -re "(.*)\n"
stty echo
set password $expect_out(1,string)
Alternatively if you need multiple runs and don't want to re-enter your password each time then you could store the password in a file in your home directory with restricted permissions (0400) and read in the password from there. Then delete the password file when no longer needed.
When you send password through expect, if you do a 'ps', you can view the entire command with the password. To avoid this, you can call expect script from another bash script sending at about 500 character random string and then the password. Then on expect script you can call password as $1 variable.
Bash script example:
#!/bin/bash
string="6e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d820"
./expectscript $string "MySuperSecretPassword"
And expect script example:
#!/usr/bin/expect -f
set timeout 20
set string [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh "user#192.168.1.1"
expect "assword"
send "$password\r"
Hope this solve your problem.