Class: MultiSSH
- Inherits:
-
Object
- Object
- MultiSSH
- Defined in:
- lib/multi_ssh.rb
Instance Attribute Summary collapse
-
#amazon ⇒ Object
readonly
Returns the value of attribute amazon.
-
#concurrent_max ⇒ Object
Returns the value of attribute concurrent_max.
-
#deploy_group ⇒ Object
readonly
Returns the value of attribute deploy_group.
-
#group_config ⇒ Object
readonly
Returns the value of attribute group_config.
-
#group_name ⇒ Object
readonly
Returns the value of attribute group_name.
-
#instances ⇒ Object
Returns the value of attribute instances.
-
#longest ⇒ Object
Returns the value of attribute longest.
-
#ui ⇒ Object
Returns the value of attribute ui.
Instance Method Summary collapse
- #add_output(host, msg) ⇒ Object
- #clear_session ⇒ Object
-
#find_instance_by_host(host) ⇒ Object
find a matching instance for the specified host.
-
#initialize(amazon, group_name, deploy_group, concurrent_max = 1000) ⇒ MultiSSH
constructor
A new instance of MultiSSH.
-
#output_tracked_data_to_files(prefix, result_path) ⇒ Object
dump the result data into a file for each host.
- #output_tracker ⇒ Object
-
#print_data(host, data, color = :cyan) ⇒ Object
print and group data by host.
- #run(remote_cmd) ⇒ Object
- #run_instances(instances, remote_cmd) ⇒ Object
- #session ⇒ Object
- #ssh_command(command, subsession) ⇒ Object
Constructor Details
#initialize(amazon, group_name, deploy_group, concurrent_max = 1000) ⇒ MultiSSH
Returns a new instance of MultiSSH.
8 9 10 11 12 13 14 15 16 |
# File 'lib/multi_ssh.rb', line 8 def initialize(amazon, group_name, deploy_group, concurrent_max = 1000) @amazon = amazon @group_name = group_name @deploy_group = deploy_group @group_config = deploy_group.config @concurrent_max = concurrent_max @session = nil @ui = Printer.new(STDOUT, STDERR, STDIN) end |
Instance Attribute Details
#amazon ⇒ Object (readonly)
Returns the value of attribute amazon.
5 6 7 |
# File 'lib/multi_ssh.rb', line 5 def amazon @amazon end |
#concurrent_max ⇒ Object
Returns the value of attribute concurrent_max.
6 7 8 |
# File 'lib/multi_ssh.rb', line 6 def concurrent_max @concurrent_max end |
#deploy_group ⇒ Object (readonly)
Returns the value of attribute deploy_group.
5 6 7 |
# File 'lib/multi_ssh.rb', line 5 def deploy_group @deploy_group end |
#group_config ⇒ Object (readonly)
Returns the value of attribute group_config.
5 6 7 |
# File 'lib/multi_ssh.rb', line 5 def group_config @group_config end |
#group_name ⇒ Object (readonly)
Returns the value of attribute group_name.
5 6 7 |
# File 'lib/multi_ssh.rb', line 5 def group_name @group_name end |
#instances ⇒ Object
Returns the value of attribute instances.
6 7 8 |
# File 'lib/multi_ssh.rb', line 6 def instances @instances end |
#longest ⇒ Object
Returns the value of attribute longest.
6 7 8 |
# File 'lib/multi_ssh.rb', line 6 def longest @longest end |
#ui ⇒ Object
Returns the value of attribute ui.
6 7 8 |
# File 'lib/multi_ssh.rb', line 6 def ui @ui end |
Instance Method Details
#add_output(host, msg) ⇒ Object
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/multi_ssh.rb', line 94 def add_output(host, msg) # see if we already have an entry for this host data_array = output_tracker[host] if data_array.nil? data_array = [] output_tracker[host] = data_array end # now add the msg to the array data_array << msg end |
#clear_session ⇒ Object
70 71 72 73 74 75 |
# File 'lib/multi_ssh.rb', line 70 def clear_session @session = nil # a map that tracks all output data in an array # keyed by the host name @output_tracker = {} end |
#find_instance_by_host(host) ⇒ Object
find a matching instance for the specified host
61 62 63 64 65 66 67 68 |
# File 'lib/multi_ssh.rb', line 61 def find_instance_by_host(host) self.instances.each do |instance| if instance[:public_hostname] == host return instance end end return nil end |
#output_tracked_data_to_files(prefix, result_path) ⇒ Object
dump the result data into a file for each host
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/multi_ssh.rb', line 82 def output_tracked_data_to_files(prefix, result_path) return if result_path.nil? output_tracker.each do |key, data| file_path = File.('.', "#{result_path}/results_#{prefix}_#{key}.txt") File.open(file_path, 'w') do |f| data.each do |line| f.puts(line) end end end end |
#output_tracker ⇒ Object
77 78 79 |
# File 'lib/multi_ssh.rb', line 77 def output_tracker @output_tracker end |
#print_data(host, data, color = :cyan) ⇒ Object
print and group data by host
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/multi_ssh.rb', line 120 def print_data(host, data, color = :cyan) if data =~ /\n/ data.split(/\n/).each { |d| print_data(host, d) } else add_output(host, data) padding = self.longest - host.length str = ui.color(host, color) + (" " * (padding + 1)) + data ui.msg(str) end end |
#run(remote_cmd) ⇒ Object
18 19 20 21 |
# File 'lib/multi_ssh.rb', line 18 def run(remote_cmd) @instances ||= amazon.find_and_sort_named_instances(group_name) run_instances(@instances, remote_cmd) end |
#run_instances(instances, remote_cmd) ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/multi_ssh.rb', line 23 def run_instances(instances, remote_cmd) clear_session @longest = 0 @instances = instances @instances.each do |instance| session_opts = {} host = instance[:public_hostname] session_opts[:keys] = File.("~/.ssh/#{group_config[:amazon_security_key]}.pem") session_opts[:paranoid] = false session_opts[:user_known_hosts_file] = "/dev/null" session_opts[:timeout] = 30 hostspec = "ec2-user@#{host}" session.use(hostspec, session_opts) @longest = host.length if host.length > @longest end ssh_command(remote_cmd, session) result = 0 failed_count = 0 @instances.each do |instance| ssh_result = instance[:ssh_exit_status] if ssh_result != 0 failed_count += 1 result = ssh_result if result == 0 end end if failed_count > 0 raise "Failed SSH command. Out of #{@instances.count} servers, #{failed_count} failed. First error was: #{result}" else ui.msg(ui.color("All #{@instances.count} ssh requests completed without error.", :green, :bold)) end end |
#session ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/multi_ssh.rb', line 105 def session return @session unless @session.nil? ssh_error_handler = Proc.new do |server| host = server.host msg = "Failed to connect to #{host} -- #{$!.class.name}: #{$!.}" print_data(host, msg, :red) instance = find_instance_by_host(host) instance[:ssh_exit_status] = 99 end @session ||= Net::SSH::Multi.start(:concurrent_connections => concurrent_max, :on_error => ssh_error_handler) end |
#ssh_command(command, subsession) ⇒ Object
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/multi_ssh.rb', line 131 def ssh_command(command, subsession) subsession.open_channel do |ch| ch.request_pty ch.exec command do |ch, success| raise ArgumentError, "Cannot execute #{command}" unless success ch.on_data do |ichannel, data| print_data(ichannel[:host], data) end ch.on_extended_data do |ichannel, type, data| print_data(ichannel[:host], data, :red) end ch.on_request("exit-status") do |ichannel, data| status = data.read_long ichannel[:exit_status] = status host = ichannel[:host] instance = find_instance_by_host(host) instance[:ssh_exit_status] = status print_data(host, "Exit status is: #{status}", status == 0 ? :blue : :red) end end end session.loop end |