Class: Chef::Knife::Ssh

Inherits:
Chef::Knife show all
Defined in:
lib/chef/knife/ssh.rb

Instance Attribute Summary collapse

Attributes inherited from Chef::Knife

#name_args

Instance Method Summary collapse

Methods inherited from Chef::Knife

#ask_question, build_sub_class, #bulk_delete, #configure_chef, #confirm, #create_object, #delete_object, #edit_data, #edit_object, #file_exists_and_is_readable?, find_command, #format_for_display, #format_list_for_display, list_commands, load_commands, #load_from_file, #output, #pretty_print, #rest, #stdin, #stdout

Methods included from Mixin::ConvertToClassName

#convert_to_class_name, #convert_to_snake_case, #filename_to_qualified_string, #snake_case_basename

Instance Attribute Details

#password=(value) ⇒ Object (writeonly)

Sets the attribute password

Parameters:

  • value

    the value to set the attribute password to.



26
27
28
# File 'lib/chef/knife/ssh.rb', line 26

def password=(value)
  @password = value
end

Instance Method Details

#configure_sessionObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/chef/knife/ssh.rb', line 67

def configure_session
  list = case config[:manual]
         when true
           @name_args[0].split(" ")
         when false
           r = Array.new
           q = Chef::Search::Query.new
           q.search(:node, @name_args[0]) do |item|
             r << format_for_display(item)[config[:attribute]]
           end
           r
         end
  session_from_list(list)
end

#fixup_sudo(command) ⇒ Object



96
97
98
# File 'lib/chef/knife/ssh.rb', line 96

def fixup_sudo(command)
  command.sub(/^sudo/, 'sudo -p \'knife sudo password: \'')
end

#get_passwordObject



129
130
131
# File 'lib/chef/knife/ssh.rb', line 129

def get_password
  @password ||= h.ask("Enter your password: ") { |q| q.echo = false }
end

#hObject



63
64
65
# File 'lib/chef/knife/ssh.rb', line 63

def h
  @highline ||= HighLine.new
end

#interactiveObject



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/chef/knife/ssh.rb', line 158

def interactive
  puts "Connected to #{h.list(session.servers_for.collect { |s| h.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


100
101
102
103
104
105
106
107
108
109
# File 'lib/chef/knife/ssh.rb', line 100

def print_data(host, data)
  if data =~ /\n/
    data.split(/\n/).each { |d| print_data(host, d) }
  else
    padding = @longest - host.length
    print h.color(host, :cyan)
    padding.downto(0) { print " " }
    puts data
  end
end

#read_lineObject

Present the prompt and read a single line from the console. It also detects ^D and returns “exit” in that case. Adds the input to the history, unless the input is empty. Loops repeatedly until a non-empty line is input.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/chef/knife/ssh.rb', line 137

def read_line
  loop do
    command = reader.readline("#{h.color('knife-ssh>', :bold)} ", true)

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

    unless command.empty?
      return command
    end
  end
end

#readerObject



154
155
156
# File 'lib/chef/knife/ssh.rb', line 154

def reader
  Readline
end

#runObject



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/chef/knife/ssh.rb', line 201

def run 
  @longest = 0

  require 'net/ssh/multi'
  require 'readline'
  require 'highline'

  configure_session

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

  session.close
end

#screenObject



187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/chef/knife/ssh.rb', line 187

def screen
  tf = Tempfile.new("knife-ssh-screen")
  tf.puts("caption always '%w'")
  tf.puts("hardstatus alwayslastline 'knife ssh #{@name_args[0]}'")
  window = 0
  session.servers_for.each do |server|
    tf.print("screen -t \"#{server.host}\" #{window} ssh ")
    server.user ? tf.puts("#{server.user}@#{server.host}") : tf.puts(server.host)
    window += 1
  end
  tf.close
  exec("screen -c #{tf.path}")
end

#sessionObject



59
60
61
# File 'lib/chef/knife/ssh.rb', line 59

def session
  @session ||= Net::SSH::Multi.start(:concurrent_connections => config[:concurrency])
end

#session_from_list(list) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/chef/knife/ssh.rb', line 82

def session_from_list(list)
  list.each do |item|
    Chef::Log.debug("Adding #{item}")
   
    if config[:password]
      session.use config[:ssh_user] ? "#{config[:ssh_user]}@#{item}" : item, :password => config[:password]
    else
      session.use config[:ssh_user] ? "#{config[:ssh_user]}@#{item}" : item
    end
    @longest = item.length if item.length > @longest
  end
  session
end

#ssh_command(command, subsession = nil) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/chef/knife/ssh.rb', line 111

def ssh_command(command, subsession=nil)
  subsession ||= session
  command = fixup_sudo(command)
  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: /
          ichannel.send_data("#{get_password}\n")
        end
      end
    end
  end
  session.loop
end