Better error handling for WinRM (using winrm v1.3.0.dev.2)

This commit is contained in:
Max Lincoln 2014-12-08 19:09:11 -05:00
parent e787291717
commit ba7b964b1e
4 changed files with 195 additions and 23 deletions

View File

@ -25,22 +25,95 @@ module VagrantPlugins
@logger.info("Initializing WinRMCommunicator")
end
def wait_for_ready(timeout)
Timeout.timeout(timeout) do
# Wait for winrm_info to be ready
winrm_info = nil
while true
winrm_info = Helper.winrm_info(@machine)
break if winrm_info
sleep 0.5
end
# Got it! Let the user know what we're connecting to.
@machine.ui.detail("WinRM address: #{shell.host}:#{shell.port}")
@machine.ui.detail("WinRM username: #{shell.username}")
@machine.ui.detail("WinRM transport: #{shell.config.transport}")
last_message = nil
last_message_repeat_at = 0
while true
message = nil
begin
begin
standard_shell = shell.cmd('echo vagrant')
powershell = shell.cmd('@PowerShell Write-Host vagrant')
unless standard_shell[:exitcode] == 0 || powershell[:exitcode] == 0
raise Errors::InvalidShell
end
return true if shell(true).sane?
rescue Vagrant::Errors::VagrantError => e
@logger.info("WinRM not ready: #{e.inspect}")
raise
end
rescue Errors::ConnectionTimeout
message = "Connection timeout."
rescue Errors::AuthenticationFailed
message = "Authentication failure."
rescue Errors::Disconnected
message = "Remote connection disconnect."
rescue Errors::ConnectionRefused
message = "Connection refused."
rescue Errors::ConnectionReset
message = "Connection reset."
rescue Errors::HostDown
message = "Host appears down."
rescue Errors::NoRoute
message = "Host unreachable."
rescue Errors::TransientError => e
# Any other retriable errors
message = e.message
end
# If we have a message to show, then show it. We don't show
# repeated messages unless they've been repeating longer than
# 10 seconds.
if message
message_at = Time.now.to_f
show_message = true
if last_message == message
show_message = (message_at - last_message_repeat_at) > 10.0
end
if show_message
@machine.ui.detail("Warning: #{message} Retrying...")
last_message = message
last_message_repeat_at = message_at
end
end
end
end
rescue Timeout::Error
return false
end
def ready?
@logger.info("Checking whether WinRM is ready...")
Timeout.timeout(@machine.config.winrm.timeout) do
result = Timeout.timeout(@machine.config.winrm.timeout) do
shell(true).powershell("hostname")
end
@logger.info("WinRM is ready!")
return true
rescue Vagrant::Errors::VagrantError => e
# We catch a `VagrantError` which would signal that something went
# wrong expectedly in the `connect`, which means we didn't connect.
rescue Errors::TransientError => e
# We catch a `TransientError` which would signal that something went
# that might work if we wait and retry.
@logger.info("WinRM not up: #{e.inspect}")
# We reset the shell to trigger calling of winrm_finder again.
# This resolves a problem when using vSphere where the ssh_info was not refreshing
# This resolves a problem when using vSphere where the winrm_info was not refreshing
# thus never getting the correct hostname.
@shell = nil
return false

View File

@ -6,8 +6,11 @@ module VagrantPlugins
error_namespace("vagrant_winrm.errors")
end
class AuthError < WinRMError
error_key(:auth_error)
class TransientError < WinRMError
end
class AuthenticationFailed < WinRMError
error_key(:authentication_failed)
end
class ExecutionError < WinRMError
@ -30,9 +33,37 @@ module VagrantPlugins
error_key(:winrm_file_transfer_error)
end
class WinRMInvalidTransport < WinRMError
class InvalidTransport < WinRMError
error_key(:invalid_transport)
end
class SSLError < WinRMError
error_key(:ssl_error)
end
class ConnectionTimeout < TransientError
error_key(:connection_timeout)
end
class Disconnected < TransientError
error_key(:disconnected)
end
class ConnectionRefused < TransientError
error_key(:connection_refused)
end
class ConnectionReset < TransientError
error_key(:connection_reset)
end
class HostDown < TransientError
error_key(:host_down)
end
class NoRoute < TransientError
error_key(:no_route)
end
end
end
end

View File

@ -96,20 +96,49 @@ module VagrantPlugins
end
end
def raise_winrm_exception(winrm_exception, shell, command)
# If the error is a 401, we can return a more specific error message
if winrm_exception.message.include?("401")
raise Errors::AuthError,
user: @config.username,
password: @config.password,
endpoint: endpoint,
message: winrm_exception.message
end
def raise_winrm_exception(exception, shell = nil, command = nil)
case exception
when WinRM::WinRMAuthorizationError
raise Errors::AuthenticationFailed,
user: @config.username,
password: @config.password,
endpoint: endpoint,
message: exception.message
when WinRM::WinRMHTTPTransportError
case exception.response_code
# If the error is a 401, we can return a more specific error message
when 401
raise Errors::AuthenticationFailed,
user: @config.username,
password: @config.password,
endpoint: endpoint,
message: exception.message
end
raise Errors::ExecutionError,
shell: shell,
command: command,
message: winrm_exception.message
raise Errors::ExecutionError,
shell: shell,
command: command,
message: exception.message
when OpenSSL::SSL::SSLError
raise Errors::SSLError, message: exception.message
when HTTPClient::TimeoutError
raise Errors::ConnectionTimeout, message: exception.message
when Errno::ECONNREFUSED
# This is raised if we failed to connect the max amount of times
raise Errors::ConnectionRefused
when Errno::ECONNRESET
# This is raised if we failed to connect the max number of times
# due to an ECONNRESET.
raise Errors::ConnectionReset
when Errno::EHOSTDOWN
# This is raised if we get an ICMP DestinationUnknown error.
raise Errors::HostDown
when Errno::EHOSTUNREACH
# This is raised if we can't work out how to route traffic.
raise Errors::NoRoute
else
raise
end
end
def new_session

View File

@ -1,11 +1,10 @@
en:
vagrant_winrm:
errors:
auth_error: |-
authentication_failed: |-
An authorization error occurred while connecting to WinRM.
User: %{user}
Password: %{password}
Endpoint: %{endpoint}
Message: %{message}
winrm_bad_exit_status: |-
@ -31,6 +30,13 @@ en:
%{shell} is not a supported type of Windows shell.
invalid_transport: |-
%{transport} is not a supported WinRM transport.
ssl_error: |-
An SSL error occurred while connecting to WinRM. This usually
occurs when you are using a self-signed certificate and have
not set the WinRM `ssl_peer_verification` config setting to false.
Message: %{message}
winrm_not_ready: |-
The box is not able to report an address for WinRM to connect to yet.
WinRM cannot access this Vagrant environment. Please wait for the
@ -41,3 +47,36 @@ en:
From: %{from}
To: %{to}
Message: %{message}
connection_refused: |-
WinRM connection was refused! This usually happens if the VM failed to
boot properly. Some steps to try to fix this: First, try reloading your
VM with `vagrant reload`, since a simple restart sometimes fixes things.
If that doesn't work, destroy your VM and recreate it with a `vagrant destroy`
followed by a `vagrant up`. If that doesn't work, contact a Vagrant
maintainer (support channels listed on the website) for more assistance.
connection_reset: |-
WinRM connection was reset! This usually happens when the machine is
taking too long to reboot. First, try reloading your machine with
`vagrant reload`, since a simple restart sometimes fixes things.
If that doesn't work, destroy your machine and recreate it with
a `vagrant destroy` followed by a `vagrant up`. If that doesn't work,
contact support.
connection_timeout: |-
Vagrant timed out while attempting to connect via WinRM. This usually
means that the VM booted, but there are issues with the WinRM configuration
or network connectivity issues. Please try to `vagrant reload` or
`vagrant up` again.
disconnected: |-
The WinRM connection was unexpectedly closed by the remote end. This
usually indicates that WinRM within the guest machine was unable to
properly start up. Please boot the VM in GUI mode to check whether
it is booting properly.
no_route: |-
While attempting to connect with WinRM, a "no route to host" (EHOSTUNREACH)
error was received. Please verify your network settings are correct
and try again.
host_down: |-
While attempting to connect with WinRM, a "host is down" (EHOSTDOWN)
error was received. Please verify your WinRM settings are correct
and try again.