-
Notifications
You must be signed in to change notification settings - Fork 83
/
winrmshell.rb
137 lines (115 loc) · 4.04 KB
/
winrmshell.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
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
89
90
91
92
93
94
95
96
97
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
require 'timeout'
require 'log4r'
require 'winrm'
require 'vagrant/util/retryable'
require_relative '../errors'
module VagrantWindows
module Communication
class WinRMShell
include Vagrant::Util::Retryable
# These are the exceptions that we retry because they represent
# errors that are generally fixed from a retry and don't
# necessarily represent immediate failure cases.
@@exceptions_to_retry_on = [
HTTPClient::KeepAliveDisconnected,
WinRM::WinRMHTTPTransportError,
Errno::EACCES,
Errno::EADDRINUSE,
Errno::ECONNREFUSED,
Errno::ECONNRESET,
Errno::ENETUNREACH,
Errno::EHOSTUNREACH,
Timeout::Error
]
attr_reader :logger
attr_reader :username
attr_reader :password
attr_reader :host
attr_reader :port
attr_reader :timeout_in_seconds
attr_reader :max_tries
def initialize(host, username, password, options = {})
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmshell")
@logger.debug("initializing WinRMShell")
@host = host
@port = options[:port] || 5985
@username = username
@password = password
@timeout_in_seconds = options[:timeout_in_seconds] || 60
@max_tries = options[:max_tries] || 20
end
def powershell(command, &block)
execute_shell(command, :powershell, &block)
end
def cmd(command, &block)
execute_shell(command, :cmd, &block)
end
def wql(query, &block)
execute_shell(query, :wql, &block)
end
def upload(from, to)
WinRMFileManager.new(self).upload(from, to)
end
def download(from, to)
WinRMFileManager.new(self).download(from, to)
end
protected
def execute_shell(command, shell=:powershell, &block)
raise Errors::WinRMInvalidShell, :shell => shell unless [:powershell, :cmd, :wql].include?(shell)
begin
execute_shell_with_retry(command, shell, &block)
rescue => e
raise_winrm_exception(e, shell, command)
end
end
def execute_shell_with_retry(command, shell, &block)
retryable(:tries => @max_tries, :on => @@exceptions_to_retry_on, :sleep => 10) do
@logger.debug("#{shell} executing:\n#{command}")
output = session.send(shell, command) do |out, err|
block.call(:stdout, out) if block_given? && out
block.call(:stderr, err) if block_given? && err
end
@logger.debug("Output: #{output.inspect}")
return output
end
end
def raise_winrm_exception(winrm_exception, shell, command)
if winrm_exception.message.include?("401") # return a more specific auth error for 401 errors
raise Errors::WinRMAuthorizationError,
:user => @username,
:password => @password,
:endpoint => endpoint,
:message => winrm_exception.message
end
raise Errors::WinRMExecutionError,
:shell => shell,
:command => command,
:message => winrm_exception.message
end
def new_session
@logger.info("Attempting to connect to WinRM...")
@logger.info(" - Host: #{@host}")
@logger.info(" - Port: #{@port}")
@logger.info(" - Username: #{@username}")
client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, endpoint_options)
client.set_timeout(@timeout_in_seconds)
client.toggle_nori_type_casting(:off) #we don't want coersion of types
client
end
def session
@session ||= new_session
end
def endpoint
"http://#{@host}:#{@port}/wsman"
end
def endpoint_options
{ :user => @username,
:pass => @password,
:host => @host,
:port => @port,
:operation_timeout => @timeout_in_seconds,
:basic_auth_only => true }
end
end #WinShell class
end
end