Class: Vagrant::Communication::WinRM

Inherits:
Base
  • Object
show all
Includes:
Util::ANSIEscapeCodeRemover, Util::Retryable
Defined in:
lib/vagrant-windows/communication/winrm.rb

Overview

Provides communication with the VM via WinRM.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(vm) ⇒ WinRM

Returns a new instance of WinRM.



24
25
26
27
28
# File 'lib/vagrant-windows/communication/winrm.rb', line 24

def initialize(vm)
  @vm     = vm
  @logger = Log4r::Logger.new("vagrant::communication::winrm")
  @co = nil
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



21
22
23
# File 'lib/vagrant-windows/communication/winrm.rb', line 21

def logger
  @logger
end

#vmObject (readonly)

Returns the value of attribute vm.



22
23
24
# File 'lib/vagrant-windows/communication/winrm.rb', line 22

def vm
  @vm
end

Instance Method Details

#execute(command, opts = nil, &block) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/vagrant-windows/communication/winrm.rb', line 54

def execute(command, opts=nil, &block)

  # Connect to WinRM, giving it a few tries
  logger.info("Connecting to WinRM: #{@vm.winrm.info[:host]}:#{@vm.winrm.info[:port]}")

  opts = {
    :error_check => true,
    :error_class => Errors::VagrantError,
    :error_key   => :winrm_bad_exit_status,
    :command     => command,
    :sudo        => false,
    :shell      => :powershell
  }.merge(opts || {})

  # Connect via WinRM and execute the command in the shell.
  exceptions = [HTTPClient::KeepAliveDisconnected] 
  exit_status = retryable(:tries => @vm.config.winrm.max_tries,   :on => exceptions, :sleep => 10) do
    logger.debug "WinRM Trying to connect"
    shell_execute(command,opts[:shell], &block)
  end

  logger.debug("#{command} EXIT STATUS #{exit_status.inspect}")

  # Check for any errors
  if opts[:error_check] && exit_status != 0
    # The error classes expect the translation key to be _key,
    # but that makes for an ugly configuration parameter, so we
    # set it here from `error_key`
    error_opts = opts.merge(:_key => opts[:error_key])
    raise opts[:error_class], error_opts
  end

  # Return the exit status
  exit_status
end

#hObject



117
118
119
# File 'lib/vagrant-windows/communication/winrm.rb', line 117

def h
  @highline ||= HighLine.new
end

#new_sessionObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/vagrant-windows/communication/winrm.rb', line 90

def new_session
  opts = {
    :user => vm.config.winrm.username,
    :pass => vm.config.winrm.password,
    :host => vm.config.winrm.host,
    :port => vm.winrm.info[:port],
    :operation_timeout => vm.config.winrm.timeout,
    :basic_auth_only => true
  }.merge ({})

  # create a session
  logger.info("Attempting WinRM session with options: #{opts}")
  begin
    endpoint = "http://#{opts[:host]}:#{opts[:port]}/wsman"
    client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, opts)
    client.set_timeout(opts[:operation_timeout]) if opts[:operation_timeout]
  rescue ::WinRM::WinRMAuthorizationError => error
    raise ::WinRM::WinRMAuthorizationError.new("#{error.message}@#{opts[:host]}")
  end

  client
end


121
122
123
124
125
126
127
# File 'lib/vagrant-windows/communication/winrm.rb', line 121

def print_data(data, color = :green)
  if data =~ /\n/
    data.split(/\n/).each { |d| print_data(d, color) }
  else
    puts h.color(data.chomp, color)
  end
end

#ready?Boolean

Returns:

  • (Boolean)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/vagrant-windows/communication/winrm.rb', line 30

def ready?
  logger.debug("Checking whether WinRM is ready...")

  Timeout.timeout(@vm.config.winrm.timeout) do
    execute "hostname"
  end

  # If we reached this point then we successfully connected
  logger.info("WinRM is ready!")
  true
rescue Timeout::Error, HTTPClient::KeepAliveDisconnected => e
  #, Errors::SSHConnectionRefused, Net::SSH::Disconnect => e
  # The above errors represent various reasons that WinRM may not be
  # ready yet. Return false.
  logger.info("WinRM not up yet: #{e.inspect}")

  return false
end

#sessionObject



113
114
115
# File 'lib/vagrant-windows/communication/winrm.rb', line 113

def session
  @session ||= new_session
end

#sudo(command, opts = nil, &block) ⇒ Object

Wrap Sudo in execute.… One day we could integrate with UAC, but Icky



50
51
52
# File 'lib/vagrant-windows/communication/winrm.rb', line 50

def sudo(command, opts=nil, &block)
  execute(command,opts,&block)
end

#upload(from, to) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/vagrant-windows/communication/winrm.rb', line 129

def upload(from, to)
  file = "winrm-upload-#{rand()}"
  file_name = (session.cmd("echo %TEMP%\\#{file}"))[:data][0][:stdout].chomp
  session.powershell <<-EOH
    if(Test-Path #{to})
    {
      rm #{to}
    }
  EOH
  Base64.encode64(IO.binread(from)).gsub("\n",'').chars.to_a.each_slice(8000-file_name.size) do |chunk|
    out = session.cmd( "echo #{chunk.join} >> \"#{file_name}\"" )
  end
  execute "mkdir [System.IO.Path]::GetDirectoryName(\"#{to}\")"
  execute <<-EOH
    $base64_string = Get-Content \"#{file_name}\"
    $bytes  = [System.Convert]::FromBase64String($base64_string) 
    $new_file = [System.IO.Path]::GetFullPath(\"#{to}\")
    [System.IO.File]::WriteAllBytes($new_file,$bytes)
  EOH
end