providers/hyperv: wait for IP on boot

This commit is contained in:
Mitchell Hashimoto 2014-02-26 11:12:24 -08:00
parent 64abd95c6f
commit 5d19285774
11 changed files with 111 additions and 90 deletions

View File

@ -66,6 +66,7 @@ module VagrantPlugins
def self.action_start def self.action_start
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|
b.use StartInstance b.use StartInstance
b.use WaitForIPAddress
#b.use ShareFolders #b.use ShareFolders
#b.use SyncFolders #b.use SyncFolders
end end
@ -139,9 +140,10 @@ module VagrantPlugins
autoload :MessageAlreadyCreated, action_root.join('message_already_created') autoload :MessageAlreadyCreated, action_root.join('message_already_created')
autoload :MessageNotRunning, action_root.join('message_not_running') autoload :MessageNotRunning, action_root.join('message_not_running')
autoload :SyncFolders, action_root.join('sync_folders') autoload :SyncFolders, action_root.join('sync_folders')
autoload :WaitForState, action_root.join('wait_for_state')
autoload :ReadGuestIP, action_root.join('read_guest_ip') autoload :ReadGuestIP, action_root.join('read_guest_ip')
autoload :ShareFolders, action_root.join('share_folders') 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 end
end end

View File

@ -44,10 +44,10 @@ module VagrantPlugins
vhdx_path: vhdx_path.to_s.gsub("/", "\\") 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( server = env[:machine].provider.driver.execute(
'import_vm.ps1', options) '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"] env[:machine].id = server["id"]
@app.call(env) @app.call(env)
end end

View File

@ -1,7 +1,3 @@
#-------------------------------------------------------------------------
# Copyright (c) Microsoft Open Technologies, Inc.
# All Rights Reserved. Licensed under the MIT License.
#--------------------------------------------------------------------------
require "log4r" require "log4r"
require "timeout" require "timeout"
@ -23,13 +19,14 @@ module VagrantPlugins
def read_host_ip(env) def read_host_ip(env)
return nil if env[:machine].id.nil? return nil if env[:machine].id.nil?
# Get Network details from WMI Provider # Get Network details from WMI Provider
# Wait for 120 sec By then the machine should be ready # Wait for 120 sec By then the machine should be ready
host_ip = nil host_ip = nil
begin begin
Timeout.timeout(120) do Timeout.timeout(120) do
begin begin
options = { vm_id: env[:machine].id } options = { VmId: env[:machine].id }
network_info = env[:machine].provider.driver.execute('get_network_config.ps1', options) network_info = env[:machine].provider.driver.execute('get_network_config.ps1', options)
host_ip = network_info["ip"] host_ip = network_info["ip"]
sleep 10 if host_ip.empty? sleep 10 if host_ip.empty?

View File

@ -7,15 +7,10 @@ module VagrantPlugins
end end
def call(env) def call(env)
env[:ui].info('Starting the Machine') env[:ui].output('Starting the machine...')
options = { vm_id: env[:machine].id } options = { vm_id: env[:machine].id }
begin response = env[:machine].provider.driver.execute('start_vm.ps1', options)
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
@app.call(env) @app.call(env)
end end
end end

View File

@ -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

View File

@ -1,34 +1,32 @@
require "vagrant" require "vagrant"
require_relative "guest_config/config"
require_relative "host_share/config" require_relative "host_share/config"
module VagrantPlugins module VagrantPlugins
module HyperV module HyperV
class Config < Vagrant.plugin("2", :config) 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] # @return [Integer]
attr_accessor :gui 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) def host_config(&block)
block.call(@host_share) block.call(@host_share)
end end
def guest_config(&block)
block.call(@guest)
end
def finalize! def finalize!
@gui = nil if @gui == UNSET_VALUE if @ip_address_timeout == UNSET_VALUE
@ip_address_timeout = 120
end
end end
def initialize(region_specific=false)
@gui = UNSET_VALUE
@host_share = HostShare::Config.new
@guest = GuestConfig::Config.new
end
def validate(machine) def validate(machine)
errors = _detected_errors errors = _detected_errors
@ -36,10 +34,6 @@ module VagrantPlugins
unless host_share.valid_config? unless host_share.valid_config?
errors << host_share.errors.flatten.join(" ") errors << host_share.errors.flatten.join(" ")
end end
unless guest.valid_config?
errors << guest.errors.flatten.join(" ")
end
=end =end
{ "HyperV" => errors } { "HyperV" => errors }
end end

View File

@ -14,6 +14,10 @@ module VagrantPlugins
error_key(:box_invalid) error_key(:box_invalid)
end end
class IPAddrTimeout < HyperVError
error_key(:ip_addr_timeout)
end
class PowerShellError < HyperVError class PowerShellError < HyperVError
error_key(:powershell_error) error_key(:powershell_error)
end end

View File

@ -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

View File

@ -1,28 +1,17 @@
#------------------------------------------------------------------------- Param(
# Copyright (c) Microsoft Open Technologies, Inc. [Parameter(Mandatory=$true)]
# All Rights Reserved. Licensed under the MIT License. [string]$VmId
#--------------------------------------------------------------------------
param (
[string]$vm_id = $(throw "-vm_id is required.")
) )
# Include the following modules # Include the following modules
$presentDir = Split-Path -parent $PSCommandPath $Dir = Split-Path $script:MyInvocation.MyCommand.Path
$modules = @() . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
$modules += $presentDir + "\utils\write_messages.ps1"
forEach ($module in $modules) { . $module }
try { $vm = Get-VM -Id $VmId -ErrorAction "Stop"
$vm = Get-VM -Id $vm_id -ErrorAction "stop" $network = Get-VMNetworkAdapter -VM $vm
$network = Get-VMNetworkAdapter -VM $vm $ip_address = $network.IpAddresses[0]
$ip_address = $network.IpAddresses[0] $resultHash = @{
$resultHash = @{
ip = "$ip_address" 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

View File

@ -20,6 +20,16 @@ en:
these directories or does not contain the files expected. Verify these directories or does not contain the files expected. Verify
that you added the correct box. If this problem persists, that you added the correct box. If this problem persists,
please contact the creator of the box for assistance. 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: |- powershell_error: |-
An error occurred while executing a PowerShell script. This error An error occurred while executing a PowerShell script. This error
is shown below. Please read the error message and see if this is is shown below. Please read the error message and see if this is

View File

@ -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