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.
Related
I am using the firewalld cookbook and having difficulty rewriting the provider code to exclude subnets missing from an array. Below is what the current firewalld provider code looks like. Can some assist with this?
use_inline_resources
action :add do
e = execute "add port #{new_resource.name} to zone" do
not_if "firewall-cmd #{zone} --query-rich-rule=\"#{rich_rule}\"
command(<<-EOC)
firewall-cmd #{zone} --add-rich-rule="#{rich_rule}"
firewall-cmd --permanent #{zone} --add-rich-rule="#{rich_rule}"
EOC
end
new_resource.updated_by_last_action(e.updated_by_last_action?)
end
action :remove do
e = execute "remove port #{new_resource.name} from zone" do
only_if "firewall-cmd #{zone} --query-rich-rule=\"#{rich_rule}\""
command(<<-EOC)
firewall-cmd #{zone} --remove-rich-rule="#{rich_rule}"
firewall-cmd --permanent #{zone} --remove-rich-rule="#{rich_rule}"
EOC
end
new_resource.updated_by_last_action(e.updated_by_last_action?)
end
def zone
new_resource.zone ? "--zone=#{new_resource.zone}" : ''
end
def rich_rule
cmd = "rule "
cmd += "family='#{new_resource.family}' " if new_resource.family
cmd += "source address='#{new_resource.source_address}' " if new_resource.source_address
cmd += "destination address='#{new_resource.destination_address}' " if new_resource.destination_address
cmd += "service name='#{new_resource.service_name}' " if new_resource.service_name
cmd += "port port='#{new_resource.port_number}' protocol='#{new_resource.port_protocol}' " if new_resource.port_number
cmd += "log " if new_resource.log_prefix || new_resource.log_level || new_resource.limit_value
cmd += "prefix='#{new_resource.log_prefix}' " if new_resource.log_prefix
cmd += "level='#{new_resource.log_level}' " if new_resource.log_level
cmd += "limit value='#{new_resource.limit_value}' " if new_resource.limit_value
cmd += new_resource.firewall_action if new_resource.firewall_action
cmd end
My recipe currently looks like this
node['cookbook']['iptables']['subnets'].each do |firewall|
firewalld_rich_rule firewall do
zone firewall["public"]
family firewall["ipv4"]
source_address firewall
firewall_action firewall["accept"]
action :add
end
end
My attributes currently look like this
default["cookbook"]["iptables"]["subnets"] = ["172.16.2.0/24","192.168.1.1/24","10.10.10.0/24"]
The code is currently working as expected. In other words, when i run the cookbook, it populates firewalld with subnets in the attribute array. However when i delete one of the subnets and run the cookbook, it doesn't delete the excluded subnet. Is there a way to write this whereby it automatically deletes any rich rule subnet not in the array?
I'm trying to write a Clockwork recipe for Capistrano 3. After having a look at Sidekiq's recipe I've come up with this:
namespace :load do
task :defaults do
set :clockwork_default_hooks, -> { true }
set :clockwork_pid, -> { 'tmp/pids/clockwork.pid' }
set :clockwork_log, -> { "#{current_path}/log/clockwork.log" }
set :clockwork_roles, -> { :app }
set :clockwork_config, -> { 'clock.rb' }
end
end
namespace :deploy do
before :starting, :check_clockwork_hooks do
invoke 'clockwork:add_default_hooks' if fetch(:clockwork_default_hooks)
end
end
namespace :clockwork do
def clockwork_pid_full_path
if fetch(:clockwork_pid).start_with?('/')
fetch(:clockwork_pid)
else
"#{current_path}/#{fetch(:clockwork_pid)}"
end
end
task :add_default_hooks do
after 'deploy:updated', 'clockwork:stop'
after 'deploy:reverted', 'clockwork:stop'
after 'deploy:published', 'clockwork:start'
end
desc 'Stop clockwork'
task :stop do
on roles fetch(:clockwork_roles) do
if test "[ -f #{clockwork_pid_full_path} ]"
within current_path do
execute "kill -int $(cat #{clockwork_pid_full_path}) 2>/dev/null"
end
else
execute "echo 'clockwork was not running'"
end
end
end
desc 'Start clockwork'
task :start do
on roles fetch(:clockwork_roles) do
within current_path do
with rails_env: fetch(:rails_env, 'production') do
execute "export RAILS_ENV=$RAILS_ENV"
execute :bundle, :exec, :clockwork, "#{fetch(:clockwork_config)} >> #{fetch(:clockwork_log)} 2>&1 &"
end
execute "ps -eo pid,command | grep clockwork | grep -v grep | awk '{print $1}' > #{clockwork_pid_full_path}"
end
end
end
desc 'Restart clockwork'
task :restart do
invoke 'clockwork:stop'
invoke 'clockwork:start'
end
end
However, the clockwork:start task only works if I remove the 2>&1 & part at the end. If I try to start the process in the background, nothing seems to happen.
What am I doing wrong?
This is what I ended up with. I adapted it from a Gist I found.
namespace :clockwork do
desc "Stop clockwork"
task :stop do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log stop"
end
end
end
end
desc "Clockwork status"
task :status do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log status"
end
end
end
end
desc "Start clockwork"
task :start do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log start"
end
end
end
end
desc "Restart clockwork"
task :restart do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log restart"
end
end
end
end
def cw_log_dir
"#{shared_path}/log"
end
def cw_pid_dir
"#{shared_path}/tmp/pids"
end
def rails_env
fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : ''
end
end
Try this gem capistrano-clockwork.
It's the most easy way that worked very well to me. I tried several options of 'gists' but no success.
You just need update this files Gemfile, Capifile and deploy.rb.
Gemfile:
gem 'daemons'
gem 'capistrano-clockwork', group: :development
Capfile:
require 'capistrano/clockwork'
deploy.rb:
set :clockwork_file, "lib/name_of_your_clockwork_config.rb"
Remember to execute bundle install before deploy ;)
I have code that requires me to connect to one server, rsync to a different server, then connect to the second server and run a bunch of commands on it. But without fail, the second SSH connection throws a 'do_open_failed': open failed (1) (Net::SSH::ChannelOpenFailed) error. Am I doing something wrong here, is there a way to close the first connection properly that makes the second one connect?
Net::SSH.start(self.from_creds['host'], self.from_creds['user'], :password => self.from_creds['password']) do |ssh|
channel = ssh.open_channel do |ch|
ch.exec "/usr/bin/rsync -e ssh -varuzP --exclude=sys-export --delete #{self.from_creds['filepath']}/#{self.client_id}/ #{self.scp_to}/#{new_client_id}" do |ch, success|
raise "could not execute command" unless success
# "on_data" is called when the process writes something to stdout
ch.on_data do |c, data|
$stdout.print data
end
# "on_extended_data" is called when the process writes something to stderr
ch.on_extended_data do |c, type, data|
$stderr.print data
end
ch.on_close { puts "done!" }
end
end
channel.wait
end
Net::SSH.start(self.to_creds['host'], self.to_creds['user'], :password => self.to_creds['password']) do |ssh1|
# Do some other stuff here
tmp_path = "#{self.to_creds['filepath']}/tmp/#{Time.now.to_i}"
ssh1.exec "mkdir -p #{tmp_path}"
ssh1.exec "cd #{self.to_creds['filepath']}/#{new_client_id}"
end
According to the documentation, exec doesn't block. Trying using exec! instead.
Net::SSH.start(self.to_creds['host'], self.to_creds['user'], :password => self.to_creds['password']) do |ssh1|
# Do some other stuff here
tmp_path = "#{self.to_creds['filepath']}/tmp/#{Time.now.to_i}"
ssh1.exec! "mkdir -p #{tmp_path}"
ssh1.exec! "cd #{self.to_creds['filepath']}/#{new_client_id}"
end
Alternatively,
Net::SSH.start(self.to_creds['host'], self.to_creds['user'], :password => self.to_creds['password']) do |ssh1|
# Do some other stuff here
tmp_path = "#{self.to_creds['filepath']}/tmp/#{Time.now.to_i}"
ssh1.exec "mkdir -p #{tmp_path}"
ssh1.exec "cd #{self.to_creds['filepath']}/#{new_client_id}"
ssh1.loop
end
For a specific bash command when I execute it locally after completion I got the shell free but when I execute remotely the shell hangs like that:
[user#host ~]$ ruby bin/remote_control.rb server start_server1
Running /home/server_manager.sh start_server ... wait
[]
When I use ruby and NET::SSH to call remotely this command and it's necessary to press ctrl+C to to get the shell prompt available again and press enter doesn't work.
Again the remote script/command /home/server_manager.sh doesn't has this behavior when
called locally
For get free the terminal the script has this syntax:
I'm trying to execute in backgroud
`commmand &` 2>&1 | echo "\n"
And the ruby code bellow is used for calling the script above:
Net::SSH.start(#hostname, #username, :password => #password) do |ssh|
channel = ssh.open_channel do |ch|
ch.exec #cmd do |ch, success|
raise "Could not execute command: #{cmd}" unless success
ch.on_data do |c, data|
begin
if !data.nil? then
print data
else
exit
end
rescue SystemExit
puts "Rescued a SystemExit exception"
end
end
ch.on_extended_data do |c, type, data|
begin
if !data.nil? then
print data
else
exit
end
rescue SystemExit
puts "Rescued a SystemExit exception"
end
end
ch.on_eof do |ch|
puts "Cmd finished with success: #{#cmd}"
$LOG.info("Cmd finished with success: #{#cmd}")
end
ch.on_close { puts "Done!" }
end
end
channel.wait
ssh.loop
end
But I haven't success until now. What I need to add to this code to always have the shell free.
In a previous question I figured out how to start a password-authenticated ssh sessions on multiple servers to run a single command. Now I need to be able to execute a "sudo" command. The problem is, that net-ssh-multi does not allocate a pseudo terminal (pty), which sudo needs to run, resulting in the following error:
[127.0.0.1 : stderr] sudo: sorry, you must have a tty to run sudo
According to the documentation, a pseudo-terminal can be allocated with a method call to a channel object, however, the following code does not work: it generates the "no tty" error above:
require 'net/ssh'
require 'net/ssh/multi'
Net::SSH::Multi.start do |session|
# define the servers we want to use
my_ticket.servers.each do |session_server|
session.use session_server , :user => user_name , \
:password => user_pass
end
# execute commands on all servers
session.exec 'sudo ls /root' do |channel, stream, data|
if data =~ /^\[sudo\] password for user:/
channel.request_pty # <- problem must be here.
channel.send_data user_pass
end
end
# run the aggregated event loop
session.loop
end
$ ruby --version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
Can you try something like this:
channel.request_pty do |c, success|
if success
command = "sudo YOUR_COMMAND"
c.exec(command) do |c, success|
# Some processing
end
end
end
In this case 'sudo' is inside.
You need to request a pty before running the command.
session.open_channel do |ch|
ch.request_pty
ch.exec "sudo ls /root"
end
Also you may remove the tty requeriment from /etc/sudoers. To do it run visudo and comment Defaults requiretty
This is what I wound up doing, thanks to #Christian and this wonderful Pastie:
Net::SSH::Multi.start do |session|
# define the servers we want to use
my_ticket.servers.each do |session_server|
session.use session_server , :user => my_ticket.user_name , \
:password => my_ticket.user_pass
end
session.open_channel do |channel|
channel.request_pty do |c, success|
raise "could not request pty" unless success
channel.exec "sudo YOUR_COMMAND"
channel.on_data do |c_, data|
if data = /\[sudo\]/
channel.send_data(#password + "\n")
end
puts data
end
end
end
# run the aggregated event loop
session.loop
end