Timeout Command under Expect not Working as Expected - timeout

I'm new to this community as well as programming. I'm currently working on a an simple Expect script that that reads a file with a list of DNS names, SSH into a Cisco router, and does a simple "show ip int brief".
This list contains some hosts that are not reachable at the moment, so I'm trying to get the script to timeout that unreachable device but to continue with the rest of devices.
When I run the script, it is able to SSH to the first device and execute the "show" command. However, when it reaches the second device (which is unreachable), it hangs there for about 30 seconds and then terminates the script. I'm not sure what I'm doing wrong. Any assistance would be greatly appreciated.
#!/usr/bin/expect
#
#
set workingdir cisco/rtr
puts stdout "Enter TACACS Username:"
gets stdin tacuserid
system stty -echo
puts stdout "Enter TACACS password:"
gets stdin tacpswd
puts stdout "\nEnter enable password:"
gets stdin enabpswd
system stty echo
#
set RTR [open "$workingdir/IP-List.txt" r]
set timestamp [timestamp -format %Y-%m-%d_%H:%M]
#
while {[gets $RTR dnsname] != -1} {
if {[ string range $dnsname 0 0 ] != "#"} {
send_user "The value of the router name is $dnsname\n"
set timeout 10
set count 0
log_file -a -noappend $workingdir/session_$dnsname\_$timestamp.log
send_log "### /START-SSH-SESSION/ IP: $dnsname # [exec date] ###\n"
spawn ssh -o StrictHostKeyChecking=no -l $tacuserid $dnsname
expect {
"TACACS Password: " {send "$tacpswd\r"}
timeout {puts "$dnsname - failed to login"; wait;close;exp_continue}
}
expect {
{>} {send "enable\r"; send_user "on the second expect\n"}
}
expect {
{assword: } {send "$enabpswd\r"}
}
#
expect {
"#" {send "show ip int brief\r"}
}
#expect "#"
send "exit\r"
send_log "\n"
send_log "### /END-SSH-SESSION/ IP: $dnsname # [exec date] ###\n"
log_file
}
}
exit

Your first expect is doing
expect {...
timeout {puts "..."; wait; close; exp_continue}
}
This will match when the ssh takes over 10 seconds to connect to a host.
When this matches it inevitably exits with an error (spawn id ... not open). This is because you wait for the command to end, close the spawn connection, then restart the same expect command.
You probably meant to use continue rather than exp_continue, in order to continue with the enclosing while loop.

Related

Stop Logstash process automatically after imported all data

Situation:
I'm importing data to Elasticsearch via Logstash at 12 pm manually every day.
I understand there is no "close" on Logstash because ideally, you would want to continuously send data to the server.
I am using elk-docker as my ELK stack.
I wrote a shell script that sends a command to a docker container to execute the following:
dailyImport.sh
docker exec -it $DOCKER_CONTAINER_NAME opt/logstash/bin/logstash --path.data /tmp/logstash/data -e \
'input {
file {
path => "'$OUTPUT_PATH'"
start_position => "beginning"
sincedb_path => "/dev/null"
mode => "read"
file_completed_action => "delete"
}
}
filter {
csv {
separator => ","
columns => ["foo", "bar", "foo2", "bar2"]
}
}
output {
elasticsearch{
hosts => "localhost:9200"
index => "foo"
document_type => "foo"
}
stdout {}
}'
What I have tried and understood:
I have read that adding read mode and file_completed_action to delete would stop the operation, I tried it but it didn't work.
I would still need to send Ctrl + C manually to stop the pipeline. e.g:
^C[2019-02-21T15:49:07,787][WARN ][logstash.runner ] SIGINT received. Shutting down.
[2019-02-21T15:49:07,899][INFO ][filewatch.observingread ] QUIT - closing all files and shutting down.
[2019-02-21T15:49:09,764][INFO ][logstash.pipeline ] Pipeline has terminated {:pipeline_id=>"main", :thread=>"#<Thread:0x6bdb92ea run>"}
Done
I have read that I could do the following, but don't know how:
Monitor the sincedb file to check when Logstash has reached EOF, then kill Logstash.
Use the stdin input instead. Logstash will shut down by itself when stdin has been closed and all inputs has been processed. On the flip side, it Logstash dies for whatever reason you don't know how much it has processed.
Reference: https://discuss.elastic.co/t/stop-logstash-after-processing-the-file/84959
What I want:
I don't need a fancy progress bar to tell me how much data I have imported (against the input file).
I only want to end the operation when "it's done" and maybe send a Ctrl + C when it reaches the EOF or "finished importing".
for file input in read mode there's recently a way to exit the process upon reading all files, just set:
input { file { exit_after_read => true } }
https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html#plugins-inputs-file-exit_after_read

Tcl script started from jenkins turns the command to lowercase

I'm having a strange issue when running a tcl command in jenkins.
The tcl script has the following lines (pay attention to the uppercase I in Id):
foreach name $docker_names {
set name "TestName"
puts $name
set command "docker inspect --format='{{.Id}}' ${name} > /home/temp/id.txt"
send -- "$command\n"
expect "$"
}
In the jeknins log I see that the job fails because what is sent in the second iteration of the loop is the command above but in lowercase. I need the I in Id to be uppercase.
This is what is sent in the second iteration of the loop:
docker inspect --format='{{.id}}' testname > /home/temp/id.txt
NOTE: In the first iteration, everything is sent correctly.
Anyone has an idea of why this is happening?
Thanks!
It can't happen from pure TCL perspective. It's a side effect of TCL interpreter integration in Jenkins: I guess there's a hidden layer that parses the script and modify it according to non-TCL syntax.
I would do 2 trials:
First is to minimize the TCL parsings f the string:
foreach name $docker_names {
set name "TestName"
puts $name
set command [concat {docker inspect --format='{{.Id}}'} $name { > /home/temp/id.txt} "\n"]
send -- $command
expect "$"
}
If it doesn't help, try backquoting the curlybraces that may be interpreted by another layer:
foreach name $docker_names {
set name "TestName"
puts $name
set command "docker inspect --format='\{\{.Id\}\}' ${name} > /home/temp/id.txt"
send -- "$command\n"
expect "$"
}

expect output only stdout of the command and nothing else

How to write expect script which executes command and prints just the command's output?
I've tried various things but none works, e.g.
#!/usr/bin/expect
log_user 0
spawn bash
send "echo 1\r"
log_user 1
expect "1"
log_user 0
send "exit\r"
expect eof
Gives in output:
echo 1
While I need just "1" . I hope somebody knows simple solution how to fix my example
Capturing the output from sent commands is a bit of a pain in expect.
Here's a more general case that does not rely on the log_user setting, it captures the output with a regular expression:
#!/usr/bin/expect
log_user 0
spawn bash
# set the prompt to a known value
send "PS1='>'\r"
expect -re {>$}
# send a command: we don't know what the output is going to be
send "echo \$RANDOM\r"
# capture the portion of the output that occurs just before the prompt
expect -re "\r\n(.*?)\r\n>$"
puts "output is: $expect_out(1,string)"
send "exit\r"
expect eof
A thought just occurred to me: if the command does not require any interaction, then expect is overkill: just use exec
set output [exec bash -c {echo $RANDOM}]
Ok, it looks following script does (at least similar to) what I need:
log_user 0
spawn bash
expect "#" {} "\\\$" {}
send -- "echo AA\r"
expect -- "echo AA\r" {}
log_user 1
expect -- "AA"
log_user 0
send -- "exit\r"
expect eof

Redirect Output to stderr

I want to redirect my output to stderr. I have a cron job
function auth_tester {
cd /data/$1/current && bundle exec rake 'authentication:tester' 1> /dev/null
}
which calls a rake task
namespace :authentication do
desc "Automatically runs authentication tester and notifies in case of failure"
task :tester => :environment do
auth_tester_results = AuthenticationTester.perform
exit(auth_tester_results.success? ? 0 : 1)
end
end
If the 'auth_tester_results' boolean is a false I want to redirect the output to stderr. How can it be done?
Since you are already dealing with shell, do it in shell:
function auth_tester {
cd /data/$1/current && \
bundle exec rake 'authentication:tester' >/dev/null 2>&1
# HERE: ⇑⇑⇑⇑⇑⇑⇑⇑⇑⇑⇑⇑⇑⇑⇑
}
stderr has an id = 2, and we are redirecting stdout to /dev/null, and stderr to stdout, eventually redirecting it to /dev/null.
To redirect stdout to stderr, use the opposite:
1>&2
To redirect the output from ruby, one uses proper receiver with IO#puts:
$stderr.puts "Goes to stderr"
Sending output to STDERR can be done via print or puts. Here I opt to send to STDERR rather than $stderr, but this will work for either:
STDERR.puts 'my message here'
If you'd like to change the exit status of your Ruby script (to show the script did not finish successfully, for example) you can use exit with a parameter of false:
exit(false)
To both send output to STDERR and return an unsuccessful exit status, you can use abort:
abort 'my message here'
For additional information, ref this article from honeybadger.io.

Using an expect Script to send the output of a command and store in a file

Hi I am trying to store the output of a command run through a spawn ssh remote window into my local host, I am new to expect and am not able to figure out where I am wrong.
My Code:
#!/bin/bash
while read line
do
/usr/bin/expect <<EOD
spawn ssh mininet#$line
expect "assword:"
send -- "mininet\r"
set output [open "outputfile.txt" "a+"]
expect "mininet#mininet-vm:*"
send -- "ls\r"
set outcome $expect_out(buffer)
send "\r"
puts $output "$outcome"
close $output
expect "mininet#mininet-vm:*"
send -- "exit\r"
interact
expect eof
EOD
done <read_ip.txt
I am getting the error
expect: spawn id exp6 not open
while executing
"expect "mininet#mininet-vm:*""
Please can any body help me on this code.
You have your expect program in a shell heredoc. The shell will expand variables in the heredoc before launching expect. You have to protect expect's variables from the shell.
One way is to use a 'quoted' heredoc, and pass the shell variable to expect through the environment:
#!/bin/bash
export host ## an environment variable
while read host
do
/usr/bin/expect <<'EOD' ## note the quotes here
spawn ssh mininet#$env(host) ## get the value from the environment
expect "assword:"
send -- "mininet\r"
set output [open "outputfile.txt" "a+"]
expect "mininet#mininet-vm:*"
send -- "ls\r"
set outcome $expect_out(buffer)
send "\r"
puts $output "$outcome"
close $output
expect "mininet#mininet-vm:*"
send -- "exit\r"
expect eof ## don't want both "interact" and "expect eof"
EOD
done <read_ip.txt
Putting single quotes around the heredoc terminator means the whole heredoc acts like a single quoted string, and expect's variables are left for expect to handle.
You might also investigate the expect log_file command: you can enable and disable logging at will, much as you are doing manually here.

Resources