Calling scp from net:ssh - ruby

Here's the setup:
I have server A, which is running my ruby scripts and servers B & C. I need to be able to transfer files from B to C.
I've experimented with using Net:SCP, but I haven't found a way to set up a transfer between the two remote servers. The best I've been able to do was go from B to A and then from A to C.
Based on some example code I've seen elsewhere, I'm trying to ssh onto B and call scp from that server:
Net::SSH.start(host, user, :password => pword) do |ssh|
ssh.exec! "scp /filename user#serverC:/filename" do |channel, stream, data|
channel.send_data "#{pword}\n"
end
end
This is not working for me. Is this even possible?

Related

create a background ssh tunnel in ruby

My oracle db is only accessable via a jumpoff server and is load balanced. As a result I run the following background tunnel command in bash:
ssh ${jumpoffUser}#${jumpoffIp} -L1521:ont-db01-vip:1521 -L1522:ont-db02-vip:1521 -fN
Before I run my commands on the db using sqlplus like so:
sqlplus #{#sqlUsername}/#{#sqlPassword}#'#{#sqlUrl}' #scripts/populateASDB.sql
This all works fine.
Now I want to rubisize this procedure.
In looking up the documentation on ruby I could not find how to put the tunnel in the background (which would be my preference) but I found documentation on local port forwarding which I thought would emulate the above tunnel and subsequent sqlplus command.
Here is my code:
Net::SSH.start( #jumpoffIp, #jumpoffUser ) do |session|
session.forward.local( 1521, 'ont-db01-vip', 1521 )
session.forward.local( 1522, 'ont-db02-vip', 1521 )
puts "About to populateDB"
res = %x[sqlplus #{#sqlUsername}/#{#sqlPassword}#'#{#sqlUrl}' #scripts/populateASDB.sql > output.txt]
puts "populateDb output #{res}"
session.loop
end
When I run the above I get the line "About to populateDB" but it hangs on the actual running of the sqlplus command. Is there something wrong with my port forwarding code or how do I put the following:
ssh ${jumpoffUser}#${jumpoffIp} -L1521:ont-db01-vip:1521 -L1522:ont-db02-vip:1521 -fN
into ruby code?
A
Try to use this gem: https://github.com/net-ssh/net-ssh-gateway/
require 'net/ssh/gateway'
gateway = Net::SSH::Gateway.new(#jumpoffIp, #jumpoffUser)
gateway.open('ont-db01-vip', 1521, 1521)
gateway.open('ont-db02-vip', 1521, 1521)
res = %x[sqlplus #{#sqlUsername}/#{#sqlPassword}#'#{#sqlUrl}' #scripts/populateASDB.sql > output.txt]
puts "populateDb output #{res}"
gateway.shutdown!
You have two problems.
1) You need to use 'session.loop { true }' so that the session actually loops
2) You don't start looping the session until your sqlplus command is done, but the sqlplus needs the session looping (the forwarding to be up).
So I suggest creating a background thread using Thread.new and then killing the thread once sqlplus is done.
Thanks to David's answer, I came up with the following:
Net::SSH.start(ip_addr, 'user') do |session|
session.forward.local( 9090, 'localhost', 9090 )
# Need to run the event loop in the background for SSH callbacks to work
t = Thread.new {
session.loop { true }
}
commands.each do | command |
command.call(9090)
end
Thread.kill(t)
end

How to mock a 3rd-party library [duplicate]

an important part of my project is to log in into remote server with ssh and do something with files on it:
Net::SSH.start(#host, #username, :password => #password) do |ssh|
ssh.exec!(rename_files_on_remote_server)
end
How to test it?
I think I can have local ssh server on and check file names on it (maybe it could be in my test/spec directory).
Or maybe someone could point me better solution?
I think it's enough to test that you're sending the correct commands to the ssh server. You're application presumably doesn't implement the server - so you have to trust that the server is correctly working and tested.
If you do implement the server then you'd need to test that, but as far as the SSH stuff goes, i'd do some mocking like this (RSpec 2 syntax):
describe "SSH Access" do
let (:ssh_connection) { mock("SSH Connection") }
before (:each) do
Net::SSH.stub(:start) { ssh_connection }
end
it "should send rename commands to the connection" do
ssh_connection.should_receive(:exec!).ordered.with("expected command")
ssh_connection.should_receive(:exec!).ordered.with("next expected command")
SSHAccessClass.rename_files!
end
end
Your suggested solution is similar to how I've done it before:
Log into the local machine. For convenience you could use 'localhost' or '127.0.0.1', but for a better simulation of network activity you might want to use the full hostname. On Mac OS and Linux you can grab the host easily by using:
`hostname`
or
require 'socket'
hostname = Socket.gethostname
which should be universal.
From there create or touch a file on the local machine after logging in, so you can test for the change with your test code.

Ruby - checking ping status featback with ssh, backtick via ssh?

In my project I want to write a script to check if every device in my network is online/reachable. I have a method called pingtest and it works for now..
def pingtest(destination)
system("ping -n 2 #{destination}")
if $? == 0 #checking status of the backtick
puts "\n Ping was successful!"
else
close("Device is unreachable. Check the config.txt for the correct IPs.")
#close() is just print & exit..
end
end
Now I wanted to ping via a ssh session with an other device in my network:
#--------------------------------
require 'net/ssh'
Net::SSH.start(#ip, #user, :password => #password)
#--------------------------------
#ssh = Ssh.new(#config)
#ssh.cmd("ping -c 3 #{#IP}")
The ping works fine but how can I use my backtrack idea now to determine if it was succesful or not?
I thought about using a sftp connection..
"ping -c 3 #{#IP} => tmpfile.txt" => download => check/compare => delete
(or something like that) to check if it was correct, but im not statusfied with that. Is there a possibility to check the success status like I did before?
I also tried something like this..
result = #ssh.cmd("ping -c 3 #{#IP}")
if result.success? == 0 # and so on..
I startet learning ruby some days ago, so im a newbie looking forward for your ideas to help me with this problem.
You can use Net::SSH to run the command remotely, similarly to what you've already got there.
The result returned from running the command will be whatever is written to both stdout and stderr.
You can use the contents of that returned value to check if it was successful or not.
Net::SSH.start(#ip, #user. password: #password) do |ssh|
response = ssh.exec! "ping -c 3 #{#other_ip}"
if response.include? 'Destination Host Unreachable'
close("Host unreachable. Result was: #{result}")
else
puts "\n Ping was successful"
end
end

Unable to connect remote host through net/ssh

This is pretty weird. I have my public key added at host machine. I can simply run
ssh -p <port> -l <username> hostt.com
which simply opens the remote shell. I can even run my capistrano scripts for the deployments on the same machine. But when i was trying connect with this following simple ruby script
require 'rubygems'
require 'net/ssh'
Net::SSH.start("hostt.com",
:port => <port>,
:username => <username>
) do |session|
puts session.pwd
end
it refuses immediately with the following exception:
`initialize': Connection refused - connect(2) (Errno::ECONNREFUSED)
Is there anything I'm missing here?
Appreciate your help.
Okay, now after a few days when I look back to the problem, I got a quick success with the following tweak:
Net::SSH.start("<host>", "<user>", :port => "<port>") { |ssh|
puts "logged in"
puts ssh.exec!("ls -l")
} rescue puts "failed to connect."
So the difference with the previous one is the username, which in this case is passed as the second argument rather than like an option key.
you probably need to provide the location of your SSH key, or a password to use with the username you provide in the SSH.start parameters. for the keys, you need to pass the map value as an array :keys => ["path_to_key"]. I'm not sure why the api is set up that way, but it is.

Testing ssh connection

an important part of my project is to log in into remote server with ssh and do something with files on it:
Net::SSH.start(#host, #username, :password => #password) do |ssh|
ssh.exec!(rename_files_on_remote_server)
end
How to test it?
I think I can have local ssh server on and check file names on it (maybe it could be in my test/spec directory).
Or maybe someone could point me better solution?
I think it's enough to test that you're sending the correct commands to the ssh server. You're application presumably doesn't implement the server - so you have to trust that the server is correctly working and tested.
If you do implement the server then you'd need to test that, but as far as the SSH stuff goes, i'd do some mocking like this (RSpec 2 syntax):
describe "SSH Access" do
let (:ssh_connection) { mock("SSH Connection") }
before (:each) do
Net::SSH.stub(:start) { ssh_connection }
end
it "should send rename commands to the connection" do
ssh_connection.should_receive(:exec!).ordered.with("expected command")
ssh_connection.should_receive(:exec!).ordered.with("next expected command")
SSHAccessClass.rename_files!
end
end
Your suggested solution is similar to how I've done it before:
Log into the local machine. For convenience you could use 'localhost' or '127.0.0.1', but for a better simulation of network activity you might want to use the full hostname. On Mac OS and Linux you can grab the host easily by using:
`hostname`
or
require 'socket'
hostname = Socket.gethostname
which should be universal.
From there create or touch a file on the local machine after logging in, so you can test for the change with your test code.

Resources