Class: Chef::Knife::Winrm

Inherits:
Chef::Knife show all
Includes:
WinrmBase
Defined in:
lib/chef/knife/winrm.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from WinrmBase

included

Instance Attribute Details

#password=(value) ⇒ Object (writeonly)

Sets the attribute password

Parameters:

  • value

    the value to set the attribute password to.



34
35
36
# File 'lib/chef/knife/winrm.rb', line 34

def password=(value)
  @password = value
end

Instance Method Details

#check_for_errors!(exit_codes) ⇒ Object



240
241
242
243
244
245
246
247
248
249
# File 'lib/chef/knife/winrm.rb', line 240

def check_for_errors!(exit_codes)

  exit_codes.each do |host, value|
    unless Chef::Config[:knife][:returns].include? value.to_i
      @exit_code = 1
      ui.error "Failed to execute command on #{host} return code #{value}"
    end
  end

end

#configure_sessionObject



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/chef/knife/winrm.rb', line 98

def configure_session
  list = case config[:manual]
         when true
           @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|
             i = extract_nested_value(item, config[:attribute])
             r.push(i) unless i.nil?
           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 (#{config[:attribute]}) to establish the connection. " +
               "Try setting another attribute to open the connection using --attribute.")
    end
    exit 10
  end
  session_from_list(list)
end

#extract_nested_value(data, nested_value_spec) ⇒ Object

TODO: Copied from Knife::Core:GenericPresenter. Should be extracted



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/chef/knife/winrm.rb', line 79

def extract_nested_value(data, nested_value_spec)
  nested_value_spec.split(".").each do |attr|
    if data.nil?
      nil # don't get no method error on nil
    elsif data.respond_to?(attr.to_sym)
      data = data.send(attr.to_sym)
    elsif data.respond_to?(:[])
      data = data[attr]
    else
      data = begin
               data.send(attr.to_sym)
             rescue NoMethodError
               nil
             end
    end
  end
  ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
end

#get_passwordObject



181
182
183
# File 'lib/chef/knife/winrm.rb', line 181

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

#interactiveObject



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/chef/knife/winrm.rb', line 210

def interactive
  puts "Connected to #{ui.list(session.servers.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!'
      session.close
      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
      winrm_command(command, session.on(*server_list))
    else
      winrm_command(command)
    end
  end
end


165
166
167
168
169
170
171
172
173
174
# File 'lib/chef/knife/winrm.rb', line 165

def print_data(host, data, color = :cyan)
  if data =~ /\n/
    data.split(/\n/).each { |d| print_data(host, d, color) }
  else
    padding = @longest - host.length
    print ui.color(host, color)
    padding.downto(0) { print " " }
    puts data.chomp
  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.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/chef/knife/winrm.rb', line 189

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

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

    unless command.empty?
      return command
    end
  end
end

#readerObject



206
207
208
# File 'lib/chef/knife/winrm.rb', line 206

def reader
  Readline
end

#runObject



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/chef/knife/winrm.rb', line 251

def run
  STDOUT.sync = STDERR.sync = true

  begin
    @longest = 0

    configure_session

    case @name_args[1]
    when "interactive"
      interactive
    else
      winrm_command(@name_args[1..-1].join(" "))

      if config[:returns]
        check_for_errors! session.exit_codes
      end

      session.close
      @exit_code || 0
    end
  rescue WinRM::WinRMHTTPTransportError => e
    case e.message
    when /401/
      ui.error "Failed to authenticate to #{@name_args[0].split(" ")} as #{config[:winrm_user]}"
      ui.info "Response: #{e.message}"
    else
      raise e
    end
  end
end

#sessionObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/chef/knife/winrm.rb', line 58

def session
  session_opts = {}
  session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
  @session ||= begin
    s = EventMachine::WinRM::Session.new(session_opts)
    s.on_output do |host, data|
      print_data(host, data)
    end
    s.on_error do |host, err|
      print_data(host, err, :red)
    end
    s.on_command_complete do |host|
      host = host == :all ? 'All Servers' : host
      Chef::Log.debug("command complete on #{host}")
    end
    s
  end

end

#session_from_list(list) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/chef/knife/winrm.rb', line 125

def session_from_list(list)
  list.each do |item|
    Chef::Log.debug("Adding #{item}")
    session_opts = {}
    session_opts[:user] = config[:winrm_user] = Chef::Config[:knife][:winrm_user] || config[:winrm_user]
    session_opts[:password] = config[:winrm_password] = Chef::Config[:knife][:winrm_password] || config[:winrm_password]
    session_opts[:port] = Chef::Config[:knife][:winrm_port] || config[:winrm_port]
    session_opts[:keytab] = Chef::Config[:knife][:kerberos_keytab_file] if Chef::Config[:knife][:kerberos_keytab_file]
    session_opts[:realm] = Chef::Config[:knife][:kerberos_realm] if Chef::Config[:knife][:kerberos_realm]
    session_opts[:service] = Chef::Config[:knife][:kerberos_service] if Chef::Config[:knife][:kerberos_service]
    session_opts[:ca_trust_path] = Chef::Config[:knife][:ca_trust_file] if Chef::Config[:knife][:ca_trust_file]
    session_opts[:operation_timeout] = 1800 # 30 min OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8

    ## If you have a \\ in your name you need to use NTLM domain authentication
    if session_opts[:user].split("\\").length.eql?(2)
      session_opts[:basic_auth_only] = false
    else
      session_opts[:basic_auth_only] = true
    end

    if config.keys.any? {|k| k.to_s =~ /kerberos/ }
      session_opts[:transport] = :kerberos
      session_opts[:basic_auth_only] = false
    else
      session_opts[:transport] = (Chef::Config[:knife][:winrm_transport] || config[:winrm_transport]).to_sym
      session_opts[:disable_sspi] = true
      if session_opts[:user] and
          (not session_opts[:password])
        session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password

      end
    end

    session.use(item, session_opts)

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

#winrm_command(command, subsession = nil) ⇒ Object



176
177
178
179
# File 'lib/chef/knife/winrm.rb', line 176

def winrm_command(command, subsession=nil)
  subsession ||= session
  subsession.relay_command(command)
end