We have a server which we can connect by SSH and we want to run commands there.
We will use SSHKit gem for it.
Gems:
gem 'net-ssh'
gem 'sshkit'
gem 'sshkit-sudo'
require libs in Ruby code:
require 'net/ssh'
require 'sshkit'
require 'sshkit/dsl'
include SSHKit::DSL
require 'sshkit/sudo'
def self.run_ssh_cmd(server_ip, username, pwd, cmd)
require 'sshkit'
require 'sshkit/dsl'
include SSHKit::DSL
host = SSHKit::Host.new({hostname: server_ip, user: username})
host.password = pwd
output = ''
SSHKit::Coordinator.new(host).each in: :sequence do
output = capture cmd
end
#
return {res: 1, output: output}
rescue => e
{
res: 0,
output: output,
error: e.message
}
end
- run command
res = run_ssh_cmd('127.0.0.1', 'myuser', 'mypwd', 'touch /tmp/1.txt')
# see output
puts res[:output]
def run_ssh_cmd_sudo(hostname, ssh_user, ssh_pass, cmd, handler=nil)
host = SSHKit::Host.new("#{ssh_user}@#{hostname}")
host.password = ssh_pass
on host do |host|
execute("#{cmd}", interaction_handler: handler)
end
#
return {res: 1, output: ""}
rescue => e
{
res: 0,
error: e.message
}
end
- run command
to run command we need to define a method which represents interaction with SSH session and provides passwords for input.
def interaction_handler_pwd(user, pwd, host='')
{
"#{user}@#{host}'s password:" => "#{pwd}\n",
/#{user}@#{host}'s password: */ => "#{pwd}\n",
"password: " => "#{pwd}\n",
"password:" => "#{pwd}\n",
"Password: " => "#{pwd}\n",
}
end
# run command
res = run_ssh_cmd_sudo(
'192.168.1.1', 'myuser', 'pwd',
'sudo touch /var/www/1.txt',
interaction_handler_pwd('myuser', 'pwd', 'hostname')
)
puts "output: #{res[:output]}"
-
if you don't want to enter password each time
-
edit visudo
myuser ALL=(ALL) NOPASSWD: ALL
- execute command
all_servers = ['myserver.com']
myuser = 'myuser'
pwd = 'mypwd'
on all_servers do |srv|
as(user: myuser) do
execute('sudo apt-get update', interaction_handler: {
"password: " => "#{pwd}\n",
})
end
end
end
- use upload! inside ssh session to upload file
def ssh_upload_file(hostname, ssh_user, ssh_pass, source_file, dest_file, handler=nil)
host = SSHKit::Host.new("#{ssh_user}@#{hostname}")
host.password = ssh_pass
f_temp = "/tmp/#{SecureRandom.uuid}"
# sshkit
SSHKit::Coordinator.new(host).each in: :sequence do
# upload to temp file
upload! source_file, f_temp
# upload to dest
execute("cp #{f_temp} #{dest_file}", interaction_handler: handler)
end
#
return {res: 1, output: ""}
rescue => e
{
res: 0,
error: e.message
}
end
- using DSL
- this variant doesn't work if server has no sudo
def ssh_upload_file(hostname, ssh_user, ssh_pass, source_file, dest_file, handler=nil)
host = SSHKit::Host.new("#{ssh_user}@#{hostname}")
host.password = ssh_pass
f_temp = "/tmp/#{SecureRandom.uuid}"
# sshkit
on host do |host|
as(user: ssh_user) do
end
# upload to temp file
upload! source_file, f_temp
# upload to dest
execute("cp #{f_temp} #{dest_file}", interaction_handler: handler)
end
#
return {res: 1, output: ""}
rescue => e
{
res: 0,
error: e.message
}
end
in case when your command runs capistrano. We have to use interaction handler.
cmd = "cap production task:name"
user = "myuser"
pwd = "mypwd"
host = "myhost"
ih = {
"#{user}@#{host}'s password:" => "#{pwd}\n",
/#{user}@#{host}'s password: */ => "#{pwd}\n",
"password: " => "#{pwd}\n",
"password:" => "#{pwd}\n",
"Password: " => "#{pwd}\n",
}
SSHKit::Coordinator.new(host).each in: :sequence do
execute(cmd, interaction_handler: ih)
end