diff --git a/plugins/providers/hyperv/action.rb b/plugins/providers/hyperv/action.rb index e69ae130a..1761afa61 100644 --- a/plugins/providers/hyperv/action.rb +++ b/plugins/providers/hyperv/action.rb @@ -66,6 +66,7 @@ module VagrantPlugins def self.action_start Vagrant::Action::Builder.new.tap do |b| b.use StartInstance + b.use WaitForIPAddress #b.use ShareFolders #b.use SyncFolders end @@ -139,9 +140,10 @@ module VagrantPlugins autoload :MessageAlreadyCreated, action_root.join('message_already_created') autoload :MessageNotRunning, action_root.join('message_not_running') autoload :SyncFolders, action_root.join('sync_folders') - autoload :WaitForState, action_root.join('wait_for_state') autoload :ReadGuestIP, action_root.join('read_guest_ip') autoload :ShareFolders, action_root.join('share_folders') + autoload :WaitForIPAddress, action_root.join("wait_for_ip_address") + autoload :WaitForState, action_root.join('wait_for_state') end end end diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index fad717ccc..2cf7240c7 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -44,10 +44,10 @@ module VagrantPlugins vhdx_path: vhdx_path.to_s.gsub("/", "\\") } - env[:ui].info "Importing a Hyper-V instance" + env[:ui].output("Importing a Hyper-V instance") server = env[:machine].provider.driver.execute( 'import_vm.ps1', options) - env[:ui].info "Successfully imported a VM with name: #{server['name']}" + env[:ui].detail("Successfully imported a VM with name: #{server['name']}") env[:machine].id = server["id"] @app.call(env) end diff --git a/plugins/providers/hyperv/action/read_guest_ip.rb b/plugins/providers/hyperv/action/read_guest_ip.rb index 209d02c58..92bfbdc8d 100644 --- a/plugins/providers/hyperv/action/read_guest_ip.rb +++ b/plugins/providers/hyperv/action/read_guest_ip.rb @@ -1,7 +1,3 @@ -#------------------------------------------------------------------------- -# Copyright (c) Microsoft Open Technologies, Inc. -# All Rights Reserved. Licensed under the MIT License. -#-------------------------------------------------------------------------- require "log4r" require "timeout" @@ -23,13 +19,14 @@ module VagrantPlugins def read_host_ip(env) return nil if env[:machine].id.nil? + # Get Network details from WMI Provider # Wait for 120 sec By then the machine should be ready host_ip = nil begin Timeout.timeout(120) do begin - options = { vm_id: env[:machine].id } + options = { VmId: env[:machine].id } network_info = env[:machine].provider.driver.execute('get_network_config.ps1', options) host_ip = network_info["ip"] sleep 10 if host_ip.empty? diff --git a/plugins/providers/hyperv/action/start_instance.rb b/plugins/providers/hyperv/action/start_instance.rb index db5906ae4..d97924c04 100644 --- a/plugins/providers/hyperv/action/start_instance.rb +++ b/plugins/providers/hyperv/action/start_instance.rb @@ -7,15 +7,10 @@ module VagrantPlugins end def call(env) - env[:ui].info('Starting the Machine') + env[:ui].output('Starting the machine...') options = { vm_id: env[:machine].id } - begin - response = env[:machine].provider.driver.execute('start_vm.ps1', options) - env[:ui].info "Machine #{response["name"]} started" - rescue Error::SubprocessError => e - env[:ui].info e.message - return - end + response = env[:machine].provider.driver.execute('start_vm.ps1', options) + @app.call(env) end end diff --git a/plugins/providers/hyperv/action/wait_for_ip_address.rb b/plugins/providers/hyperv/action/wait_for_ip_address.rb new file mode 100644 index 000000000..a6a14b8d6 --- /dev/null +++ b/plugins/providers/hyperv/action/wait_for_ip_address.rb @@ -0,0 +1,45 @@ +require "timeout" + +module VagrantPlugins + module HyperV + module Action + class WaitForIPAddress + def initialize(app, env) + @app = app + end + + def call(env) + timeout = env[:machine].provider_config.ip_address_timeout + + env[:ui].output("Waiting for the machine to report its IP address...") + env[:ui].detail("Timeout: #{timeout} seconds") + + guest_ip = nil + Timeout.timeout(timeout) do + while true + # If a ctrl-c came through, break out + return if env[:interrupted] + + # Try to get the IP + network_info = env[:machine].provider.driver.execute( + "get_network_config.ps1", VmId: env[:machine].id) + guest_ip = network_info["ip"] + break if guest_ip && guest_ip != "" + + sleep 1 + end + end + + # If we were interrupted then return now + return if env[:interrupted] + + env[:ui].detail("IP: #{guest_ip}") + + @app.call(env) + rescue Timeout::Error + raise Errors::IPAddrTimeout + end + end + end + end +end diff --git a/plugins/providers/hyperv/config.rb b/plugins/providers/hyperv/config.rb index 8513e8c7e..3a512a945 100644 --- a/plugins/providers/hyperv/config.rb +++ b/plugins/providers/hyperv/config.rb @@ -1,34 +1,32 @@ require "vagrant" -require_relative "guest_config/config" require_relative "host_share/config" module VagrantPlugins module HyperV class Config < Vagrant.plugin("2", :config) - # If set to `true`, then VirtualBox will be launched with a GUI. + # The timeout to wait for an IP address when booting the machine, + # in seconds. # - # @return [Boolean] - attr_accessor :gui + # @return [Integer] + attr_accessor :ip_address_timeout - attr_reader :host_share, :guest + attr_reader :host_share + + def initialize + @ip_address_timeout = UNSET_VALUE + @host_share = HostShare::Config.new + end def host_config(&block) block.call(@host_share) end - def guest_config(&block) - block.call(@guest) - end - def finalize! - @gui = nil if @gui == UNSET_VALUE + if @ip_address_timeout == UNSET_VALUE + @ip_address_timeout = 120 + end end - def initialize(region_specific=false) - @gui = UNSET_VALUE - @host_share = HostShare::Config.new - @guest = GuestConfig::Config.new - end def validate(machine) errors = _detected_errors @@ -36,10 +34,6 @@ module VagrantPlugins unless host_share.valid_config? errors << host_share.errors.flatten.join(" ") end - - unless guest.valid_config? - errors << guest.errors.flatten.join(" ") - end =end { "HyperV" => errors } end diff --git a/plugins/providers/hyperv/errors.rb b/plugins/providers/hyperv/errors.rb index 67e26b3ad..cd2519813 100644 --- a/plugins/providers/hyperv/errors.rb +++ b/plugins/providers/hyperv/errors.rb @@ -14,6 +14,10 @@ module VagrantPlugins error_key(:box_invalid) end + class IPAddrTimeout < HyperVError + error_key(:ip_addr_timeout) + end + class PowerShellError < HyperVError error_key(:powershell_error) end diff --git a/plugins/providers/hyperv/guest_config/config.rb b/plugins/providers/hyperv/guest_config/config.rb deleted file mode 100644 index 5b02d5a21..000000000 --- a/plugins/providers/hyperv/guest_config/config.rb +++ /dev/null @@ -1,33 +0,0 @@ -#------------------------------------------------------------------------- -# Copyright (c) Microsoft Open Technologies, Inc. -# All Rights Reserved. Licensed under the MIT License. -#-------------------------------------------------------------------------- -module VagrantPlugins - module HyperV - module GuestConfig - class Config < Vagrant.plugin("2", :config) - attr_accessor :username, :password - - def errors - @errors - end - - def validate - @errors = [] - if username.nil? - @errors << "Please configure a Guest VM's username" - end - if password.nil? - @errors << "Please configure a Guest VM's password" - end - end - - def valid_config? - validate - errors.empty? - end - - end - end - end -end diff --git a/plugins/providers/hyperv/scripts/get_network_config.ps1 b/plugins/providers/hyperv/scripts/get_network_config.ps1 index bc48163d8..f8554297c 100644 --- a/plugins/providers/hyperv/scripts/get_network_config.ps1 +++ b/plugins/providers/hyperv/scripts/get_network_config.ps1 @@ -1,28 +1,17 @@ -#------------------------------------------------------------------------- -# Copyright (c) Microsoft Open Technologies, Inc. -# All Rights Reserved. Licensed under the MIT License. -#-------------------------------------------------------------------------- - -param ( - [string]$vm_id = $(throw "-vm_id is required.") +Param( + [Parameter(Mandatory=$true)] + [string]$VmId ) # Include the following modules -$presentDir = Split-Path -parent $PSCommandPath -$modules = @() -$modules += $presentDir + "\utils\write_messages.ps1" -forEach ($module in $modules) { . $module } +$Dir = Split-Path $script:MyInvocation.MyCommand.Path +. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) -try { - $vm = Get-VM -Id $vm_id -ErrorAction "stop" - $network = Get-VMNetworkAdapter -VM $vm - $ip_address = $network.IpAddresses[0] - $resultHash = @{ +$vm = Get-VM -Id $VmId -ErrorAction "Stop" +$network = Get-VMNetworkAdapter -VM $vm +$ip_address = $network.IpAddresses[0] +$resultHash = @{ ip = "$ip_address" - } - $result = ConvertTo-Json $resultHash - Write-Output-Message $result -} -catch { - Write-Error-Message "Failed to obtain network info of VM $_" } +$result = ConvertTo-Json $resultHash +Write-Output-Message $result diff --git a/templates/locales/providers_hyperv.yml b/templates/locales/providers_hyperv.yml index edd2e21ae..e18fa4136 100644 --- a/templates/locales/providers_hyperv.yml +++ b/templates/locales/providers_hyperv.yml @@ -20,6 +20,16 @@ en: these directories or does not contain the files expected. Verify that you added the correct box. If this problem persists, please contact the creator of the box for assistance. + ip_addr_timeout: |- + Hyper-V failed to determine your machine's IP address within the + configured timeout. Please verify the machine properly booted and + the network works. To do this, open the Hyper-V manager, find your + virtual machine, and connect to it. + + The most common cause for this error is that the running virtual + machine doesn't have the latest Hyper-V integration drivers. Please + research for your operating system how to install these in order + for the VM to properly communicate its IP address to Hyper-V. powershell_error: |- An error occurred while executing a PowerShell script. This error is shown below. Please read the error message and see if this is diff --git a/test/unit/plugins/providers/hyperv/config_test.rb b/test/unit/plugins/providers/hyperv/config_test.rb new file mode 100644 index 000000000..0aac992ab --- /dev/null +++ b/test/unit/plugins/providers/hyperv/config_test.rb @@ -0,0 +1,18 @@ +require_relative "../../../base" + +require Vagrant.source_root.join("plugins/providers/hyperv/config") + +describe VagrantPlugins::HyperV::Config do + describe "#ip_address_timeout" do + it "can be set" do + subject.ip_address_timeout = 180 + subject.finalize! + expect(subject.ip_address_timeout).to eq(180) + end + + it "defaults to a number" do + subject.finalize! + expect(subject.ip_address_timeout).to eq(120) + end + end +end