class Chef::Knife::CloudSsh

Attributes

password[W]

Public Instance Methods

configure_attribute() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 266
def configure_attribute
  config[:override_attribute] = config[:attribute] || Chef::Config[:knife][:ssh_attribute]
  config[:attribute] = (Chef::Config[:knife][:ssh_attribute] ||
                        config[:attribute] ||
                        "private_ipaddress").strip
end
configure_gateway() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 114
def configure_gateway
  config[:ssh_gateway] ||= Chef::Config[:knife][:ssh_gateway]
  if config[:ssh_gateway]
    gw_host, gw_user = config[:ssh_gateway].split('@').reverse
    gw_host, gw_port = gw_host.split(':')
    gw_opts = gw_port ? { :port => gw_port } : {}

    session.via(gw_host, gw_user || config[:ssh_user], gw_opts)
  end
rescue Net::SSH::AuthenticationFailed
  user = gw_user || config[:ssh_user]
  prompt = "Enter the password for #{user}@#{gw_host}: "
  gw_opts.merge!(:password => prompt_for_password(prompt))
  session.via(gw_host, user, gw_opts)
end
configure_identity_file() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 333
def configure_identity_file
  config[:identity_file] = get_stripped_unfrozen_value(config[:identity_file] ||
                       Chef::Config[:knife][:ssh_identity_file])
end
configure_session() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 130
def configure_session
  list = case config[:manual]
         when true
           @all_nodes += @name_args[0].split(" ")
           @name_args[0].split(" ")
         when false
           r = Array.new
           q = Chef::Search::Query.new
           @action_nodes = q.search(:node, @name_args[0])[0]
           @action_nodes.each do |item|
             # we should skip the loop to next iteration if the item returned by the search is nil
             next if item.nil?
             # if a command line attribute was not passed, and we have a cloud public_hostname, use that.
             # see #configure_attribute for the source of config[:attribute] and config[:override_attribute]
             if !config[:override_attribute] && item[:cloud] and item[:cloud][:public_hostname]
               i = item[:cloud][:public_hostname]
             elsif config[:override_attribute]
               i = extract_nested_value(item, config[:override_attribute])
             else
               i = extract_nested_value(item, config[:attribute])
             end
             # next if we couldn't find the specified attribute in the returned node object
             next if i.nil?
             r.push(i)
             @all_nodes << item[:fqdn]
           end
           r
         end
  if list.length == 0
    if @action_nodes.length == 0
      ui.fatal("No nodes returned from search!")
    else
      ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
               "but does not have the required attribute to establish the connection. " +
               "Try setting another attribute to open the connection using --attribute.")
    end
    exit 10
  end
  session_from_list(list)
end
configure_user() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 328
def configure_user
  config[:ssh_user] = get_stripped_unfrozen_value(config[:ssh_user] ||
                       Chef::Config[:knife][:ssh_user])
end
extract_nested_value(data_structure, path_spec) click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 338
def extract_nested_value(data_structure, path_spec)
  ui.presenter.extract_nested_value(data_structure, path_spec)
end
fixup_sudo(command) click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 201
def fixup_sudo(command)
  command.sub(/^sudo/, 'sudo -p \'knife sudo password: \'')
end
get_password() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 258
def get_password
  @password ||= prompt_for_password
end
get_stripped_unfrozen_value(value) click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 323
def get_stripped_unfrozen_value(value)
  return nil if value.nil?
  value.strip
end
interactive() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 294
def interactive
  puts "Connected to #{ui.list(session.servers_for.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}"
  puts
  puts "To run a command on a list of servers, do:"
  puts "  on SERVER1 SERVER2 SERVER3; COMMAND"
  puts "  Example: on latte foamy; echo foobar"
  puts
  puts "To exit interactive mode, use 'quit!'"
  puts
  while 1
    command = read_line
    case command
    when 'quit!'
      puts 'Bye!'
      break
    when /^on (.+?); (.+)$/
      raw_list = $1.split(" ")
      server_list = Array.new
      session.servers.each do |session_server|
        server_list << session_server if raw_list.include?(session_server.host)
      end
      command = $2
      ssh_command(command, session.on(*server_list))
    else
      ssh_command(command)
    end
  end
end
print_all_status() click to toggle source
print_data(host, data) click to toggle source
print_line(host, data) click to toggle source
prompt_for_password(prompt = "Enter your password: ") click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 262
def prompt_for_password(prompt = "Enter your password: ")
  ui.ask(prompt) { |q| q.echo = false }
end
read_line() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 273
def read_line
  loop do
    command = reader.readline("#{ui.color('knife-ssh>', :bold)} ", true)

    if command.nil?
      command = "exit"
      puts(command)
    else
      command.strip!
    end

    unless command.empty?
      return command
    end
  end
end
reader() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 290
def reader
  Readline
end
run() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 354
def run
  extend Chef::Mixin::Command

  @longest = 0

  @all_nodes = []
  @failed_connect_nodes = []
  @not_zerro_nodes = []
  @zerro_nodes = []

  configure_attribute
  configure_user
  configure_identity_file
  configure_gateway
  configure_session

  exit_status =
  case @name_args[1]
  when "interactive"
    interactive
  else
    ssh_command(@name_args[1..-1].join(" "))
  end


  session.close

  print_all_status

  if (@not_zerro_nodes + @failed_connect_nodes).compact.empty?
    exit_status
  else
    my_status = exit_status == 0 ? 1 : exit_status
    exit my_status
  end
end
session() click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 89
def session
  config[:on_error] ||= :skip
  ssh_error_handler = Proc.new do |server|

    @failed_connect_nodes << { "node" => server.host , "message" => "#{$!.class.name}: #{$!.message}" }
    if config[:manual]
      node_name = server.host
    else
      @action_nodes.each do |n|
        node_name = n if format_for_display(n)[config[:attribute]] == server.host
      end
    end
    case config[:on_error]
    when :skip
      ui.warn "Failed to connect to #{node_name} -- #{$!.class.name}: #{$!.message}"
      $!.backtrace.each { |l| Chef::Log.error(l) }
    when :raise
      #Net::SSH::Multi magic to force exception to be re-raised.
      throw :go, :raise
    end
  end

  @session ||= Net::SSH::Multi.start(:concurrent_connections => config[:concurrency], :on_error => ssh_error_handler)
end
session_from_list(list) click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 171
def session_from_list(list)
  list.each do |item|
    Chef::Log.debug("Adding #{item}")
    session_opts = {}

    ssh_config = Net::SSH.configuration_for(item)

    # Chef::Config[:knife][:ssh_user] is parsed in #configure_user and written to config[:ssh_user]
    user = config[:ssh_user] || ssh_config[:user]
    hostspec = user ? "#{user}@#{item}" : item
    session_opts[:keys] = File.expand_path(config[:identity_file]) if config[:identity_file]
    session_opts[:keys_only] = true if config[:identity_file]
    session_opts[:password] = config[:ssh_password] if config[:ssh_password]
    session_opts[:forward_agent] = config[:forward_agent]
    session_opts[:port] = config[:ssh_port] || Chef::Config[:knife][:ssh_port] || ssh_config[:port]
    session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug

    if !config[:host_key_verify]
      session_opts[:paranoid] = false
      session_opts[:user_known_hosts_file] = "/dev/null"
    end

    session.use(hostspec, session_opts)

    @longest = item.length if item.length > @longest
  end

  session
end
ssh_command(command, subsession=nil) click to toggle source
# File lib/knife/undev/plugins/cloud_ssh.rb, line 228
def ssh_command(command, subsession=nil)
  exit_status = 0
  subsession ||= session
  command = fixup_sudo(command)
  command.force_encoding('binary') if command.respond_to?(:force_encoding)
  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)
        if data =~ /^knife sudo password: /
          print_data(ichannel[:host], "\n")
          ichannel.send_data("#{get_password}\n")
        end
      end
      ch.on_request "exit-status" do |ichannel, data|
        exit_status = [exit_status, data.read_long].max
        if exit_status != 0
          @not_zerro_nodes << { 'node' => ichannel[:host], "message" => "exit status: #{exit_status}" }
        else
          @zerro_nodes << {'node' => ichannel[:host]}
        end
      end
    end
  end
  session.loop
  exit_status
end