Ruby array.each and RestClient skipping array elements - ruby

I've a array of elements and for each element, I've to make a get call using RestClient. However, I see a few of the array elements are being skipped.
total_hosts = []
puts "total_hosts initially = " + total_hosts.size.to_s
require 'rest-client'
total_hosts = %w{10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4 10.0.0.5 10.0.0.6 10.0.0.7 10.0.0.8 10.0.0.9 10.0.0.10 10.0.0.11 10.0.0.12 10.0.0.13 10.0.0.14 10.0.0.15}
puts "total_hosts after adding = " + total_hosts.size.to_s
total_hosts.each { |host|
begin
sleep 1
RestClient.get("http://#{host}/get_my_build") { |response|
case response.code
when 200
if response.body.include?("Your build number is 10")
puts "Latest build on #{host}"
else
puts "Older build on #{host}"
end
when 404
puts "Exception on #{host} not reachable"
end
}
rescue => e
puts "Exception on #{host}"
end
}
puts "job completed"
Output :
Latest build on 10.0.0.3
Latest build on 10.0.0.4
Exception on 10.0.0.5
Latest build on 10.0.0.6
Latest build on 10.0.0.7
Latest build on 10.0.0.8
Latest build on 10.0.0.9
Exception on 10.0.0.10
Latest build on 10.0.0.11
Latest build on 10.0.0.12
Latest build on 10.0.0.13
Latest build on 10.0.0.14
Latest build on 10.0.0.15
I could see hosts 1 and 2 are missing in the output. If I run the same script after some time they might be listed in the output but some other hosts are missing.

Whenever your script encounters an HTTP Response that is not 200 or 404, your script does nothing. So, if a host has a temporary error, or a redirection, or tells you to try again, or simply gives you some information, or any of the other ~60 possible responses, you simply ignore that host completely.

Related

How to capture the output of a command without it displaying in stdout

How can I capture the output of a command and check for what it says without the command displaying in stdout? For example:
def update!
`git pull origin master`
if $?.exitstatus > 0
puts 'Failed to update'
elsif $?.success?
puts 'Upgraded successfully'
else
puts 'Already up to date'
end
end
How can I capture the output of this in order to check whether the command says up-to date, an error occurs, or successfully updates? Is there a way to write the output to a file instead of to the console?
Update for answer:
def update!
update_status = `git pull origin master 2>&1`
if $?.exitstatus > 0
puts 'error'
elsif update_status =~ /Already up-to date/
puts 'same version as origin master'
else
puts 'updated'
end
end
The output for this will always be:
[06:44:29 INFO] Updating to newest version..
updated
Even if the version is the same as the origin. What I would like to do, if possible, is save the stdout of the command to a file and read from that file to discover if the program was updated or not. I think that would be the easiest way to go about doing this.
You can assign the output of the command to a string.
Use 2>&1 to redirect stderr to stdout and thus capture all the output.
str = `git pull origin master 2>&1`
if $?.exitstatus > 0
...
elsif str =~ /up-to-date/
...
else
...
end

removing quotations and brackets from an array when printing (cannot use puts)

I have this:
[1] pry(main)> ip = ["123.456.789.666"]
=> ["123.456.789.666"]
[2] pry(main)> p ip
["123.456.789.666"]
=> ["123.456.789.666"]
How do I get it so that it comes out 123.456.789.666? The end goal is to take whatever IP is in my array to ping with. The ping fails because it's doing ping "123.456.789.666".
You can pass the IP only as a string or IPAddr object (but in general it will be a string for net/ping gem). Looks like there are some problems with this gem when host specified as an IP.
If you need to check the availability of a server - use system ping:
#5 - number of retry, ip[0] - your ip from the array
`ping -q -c 5 #{ip[0]}`
'All is OK' if $?.exitstatus == 0

How to print the output on the console?

I am trying to show the "curl's" output on the console when I execute it. Below is what I wrote. It prints the statement that I declared in puts for all the hostnames, which is as per designed.
desc "check_app_version <environment> <project>", "checks the /dev/info page of the app"
def check_app_version(environment, project)
check_environment(environment)
machines = `knife node list`.split
machines.each do |hostname|
puts "checking the app-version for #{hostname}"
system("curl --silent -m 5 #{hostname}:8080/dev/info |grep 'Application\|Build'")
end
end
But I don't see anything for the next line which instructs to perform a curl on my servers.
Use the backtick notation to return a string, then return to puts
puts `curl --silent -m 5 #{hostname}:8080/dev/info |grep 'Application\|Build'`
puts `curl --silent -m 5 #{hostname}:8080/dev/info |grep 'Application\\|Build'`

Mixlib::ShellOut - timeout

I am trying to use Mixlib::ShellOut to execute commands under ruby_block inside a chef recipe.
In Some situations, we cannot complete the task in 600 seconds, and I would like extend further. I have added command in below way,
ruby_block "#{host_short_name}_reg_chef_node" do
block do
puts "Registering Chef Node #{host_full_name}"
_command = "cd #{node['nodeManager']['app']['base_dir']}; #{node['nodeManager']['knife']['binary']} bootstrap --sudo #{host_full_name}"
_command += " --ssh-user #{node['nodeManager']['admin']['user']} --no-host-key-verify --identity-file #{node['nodeManager']['admin']['keyfile']}"
_command +=" --environment #{params[:environment]} --run-list 'role[#{params[:role_hash]['role']}]'"
puts _command
vsphere_output = Mixlib::ShellOut.new(_command, :timeout => 10000)
vsphere_output.run_command
puts "Output: #{vsphere_output.stdout}"
puts "Error : #{vsphere_output.stderr}"
end
action :nothing
end
and I suspect it is not respecting timeout value. Please advise.

Multiple SSH hops with Net::SSH (Ruby)

So here's my setup:
Laptop -> Host 1 -> Host 2 -> Host 3
Laptop can reach Host 1, but not Host 2 or Host 3
Host 1 can reach Host 2, but not Host 3
Host 3 can reach Host 2, but not Host 1
What I'm trying to do is set up remote forwards so that a process running on Host 3 will be routed to running service on Laptop. I've successfully done this using the following code:
Run from Laptop:
require 'rubygems'
require 'net/ssh'
threads = []
config = {:user => "user", :remote_port => 3333, :service_port => 2222}
threads << Thread.new{
Net::SSH.start("host1", config[:user]) do |ssh|
puts "Forwarding port #{config[:remote_port]} on host1 to #{config[:service_port]} on localhost"
ssh.forward.remote(config[:service_port], "localhost", config[:remote_port], "127.0.0.1")
ssh.exec! "ssh #{config[:user]}#host2 -R #{config[:remote_port]}:localhost:#{config[:remote_port]}"
ssh.loop {true}
end
}
threads << Thread.new{
Net::SSH.start("host3", config[:user]) do |ssh|
puts "Creating local forward for port #{config[:service_port]} on host3 to port #{config[:remote_port]} on host2"
ssh.exec! "ssh #{config[:user]}#host2 -L #{config[:service_port]}:localhost:#{config[:remote_port]}"
ssh.loop {true}
end
}
threads.each {|t| t.join}
In one thread, I'm setting up a remote forward from Laptop to Host 1 and then another remote forward from Host 1 to Host 2. In a separate thread, I'm starting another connection from Laptop to Host 3, then running a local forward from Host 3 to Host 2.
The only way I can connect from Laptop to Host 3 is because of my .ssh/config file, which automatically routes me through Host 1 and Host 2 when I try to connect to Host 3 from Laptop.
What I want to do is cut out the second thread where I'm connecting from Laptop to Host 3 so that I can remove the dependency on the .ssh/config file. I want to do all of my connections from within the first thread.
So basically I need to do multiple hops that originate from Laptop. I can initiate the first connection from Laptop to Host 1 and then execute a command on Host 1, but after that I can't get any further. What I need to do is initiate the connection from Laptop to Host 1, set up the forward on Host 1, connect to Host 2 from Host 1 and then set up the second forward on Host 2.
Is this possible to do with net/ssh?
Thanks for your help!
I fixed this by writing out an SSH config file, then specifying the config file when initiating the connection. The config file contains proxy commands that will automatically route through the necessary hosts to reach my destination.
Example:
def write_config_file
File.open('confi_file', 'w') { |f|
f << "host host1\n"
f << "HostName host1.something.net\n"
f << "user user_name\n"
f << "\n"
f << "host host2\n"
f << "HostName host2.something.net\n"
f << "ProxyCommand ssh host1 nc %h %p 2> /dev/null\n"
f << "user user_name\n"
f << "\n"
f << "host host3\n"
f << "HostName host3.something.net\n"
f << "ProxyCommand ssh host2 nc %h %p 2> /dev/null\n"
f << "user user_name\n"
}
end
write_config_file
Net::SSH.start("host3", "user_name", :config => './config_file') do |ssh|
#whatever you need to do...
end
I wrapped the connection in a begin/rescue blocked and trapped ctrl+c input, and in the trap block I delete the config file and shut down the connection.

Resources