diff --git a/CHANGELOG.md b/CHANGELOG.md index d4dac3be0..30d18e651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ as failing. [GH-720] - It is now an error to specify the packaging `output` as a directory. [GH-730] - Unix-style line endings are used properly for guest OS. [GH-727] + - Retry certain VirtualBox operations, since they intermittently fail. + [GH-726] ## 0.9.7 (February 9, 2012) diff --git a/lib/vagrant/driver/virtualbox_4_0.rb b/lib/vagrant/driver/virtualbox_4_0.rb index 8c01fd74f..27377e86e 100644 --- a/lib/vagrant/driver/virtualbox_4_0.rb +++ b/lib/vagrant/driver/virtualbox_4_0.rb @@ -23,7 +23,8 @@ module Vagrant end def clear_shared_folders - execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/ execute("sharedfolder", "remove", @uuid, "--name", $1.to_s) end @@ -70,8 +71,9 @@ module Vagrant execute("list", "vms").split("\n").each do |line| if line =~ /^".+?"\s+\{(.+?)\}$/ - execute("showvminfo", $1.to_s, "--machinereadable").split("\n").each do |info| - if info =~ /^hostonlyadapter\d+="(.+?)"$/ + info = execute("showvminfo", $1.to_s, "--machinereadable", :retryable => true) + info.split("\n").each do |line| + if line =~ /^hostonlyadapter\d+="(.+?)"$/ networks.delete($1.to_s) end end @@ -202,7 +204,8 @@ module Vagrant results = [] current_nic = nil - execute("showvminfo", uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ @@ -246,7 +249,8 @@ module Vagrant end def read_guest_additions_version - output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version") + output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", + :retryable => true) if output =~ /^Value: (.+?)$/ # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't @@ -260,7 +264,7 @@ module Vagrant def read_host_only_interfaces dhcp = {} - execute("list", "dhcpservers").split("\n\n").each do |block| + execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block| info = {} block.split("\n").each do |line| @@ -279,7 +283,7 @@ module Vagrant dhcp[info[:network]] = info end - execute("list", "hostonlyifs").split("\n\n").collect do |block| + execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| @@ -302,7 +306,8 @@ module Vagrant end def read_mac_address - execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| return $1.to_s if line =~ /^macaddress1="(.+?)"$/ end @@ -310,7 +315,7 @@ module Vagrant end def read_machine_folder - execute("list", "systemproperties").split("\n").each do |line| + execute("list", "systemproperties", :retryable => true).split("\n").each do |line| if line =~ /^Default machine folder:\s+(.+?)$/i return $1.to_s end @@ -321,7 +326,8 @@ module Vagrant def read_network_interfaces nics = {} - execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| if line =~ /^nic(\d+)="(.+?)"$/ adapter = $1.to_i type = $2.to_sym @@ -347,7 +353,7 @@ module Vagrant end def read_state - output = execute("showvminfo", @uuid, "--machinereadable") + output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) if output =~ /^name=""$/ return :inaccessible elsif output =~ /^VMState="(.+?)"$/ @@ -359,7 +365,7 @@ module Vagrant def read_used_ports ports = [] - execute("list", "vms").split("\n").each do |line| + execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ uuid = $1.to_s @@ -377,7 +383,7 @@ module Vagrant def read_vms results = [] - execute("list", "vms").split("\n").each do |line| + execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ results << $1.to_s end diff --git a/lib/vagrant/driver/virtualbox_4_1.rb b/lib/vagrant/driver/virtualbox_4_1.rb index 436121b12..9a43119cb 100644 --- a/lib/vagrant/driver/virtualbox_4_1.rb +++ b/lib/vagrant/driver/virtualbox_4_1.rb @@ -23,7 +23,8 @@ module Vagrant end def clear_shared_folders - execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/ execute("sharedfolder", "remove", @uuid, "--name", $1.to_s) end @@ -70,8 +71,9 @@ module Vagrant execute("list", "vms").split("\n").each do |line| if line =~ /^".+?"\s+\{(.+?)\}$/ - execute("showvminfo", $1.to_s, "--machinereadable").split("\n").each do |info| - if info =~ /^hostonlyadapter\d+="(.+?)"$/ + info = execute("showvminfo", $1.to_s, "--machinereadable", :retryable => true) + info.split("\n").each do |line| + if line =~ /^hostonlyadapter\d+="(.+?)"$/ networks.delete($1.to_s) end end @@ -202,7 +204,8 @@ module Vagrant results = [] current_nic = nil - execute("showvminfo", uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ @@ -246,7 +249,8 @@ module Vagrant end def read_guest_additions_version - output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version") + output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", + :retryable => true) if output =~ /^Value: (.+?)$/ # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't @@ -260,7 +264,7 @@ module Vagrant def read_host_only_interfaces dhcp = {} - execute("list", "dhcpservers").split("\n\n").each do |block| + execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block| info = {} block.split("\n").each do |line| @@ -279,7 +283,7 @@ module Vagrant dhcp[info[:network]] = info end - execute("list", "hostonlyifs").split("\n\n").collect do |block| + execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| @@ -302,7 +306,8 @@ module Vagrant end def read_mac_address - execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| return $1.to_s if line =~ /^macaddress1="(.+?)"$/ end @@ -310,7 +315,7 @@ module Vagrant end def read_machine_folder - execute("list", "systemproperties").split("\n").each do |line| + execute("list", "systemproperties", :retryable => true).split("\n").each do |line| if line =~ /^Default machine folder:\s+(.+?)$/i return $1.to_s end @@ -321,7 +326,8 @@ module Vagrant def read_network_interfaces nics = {} - execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line| + info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) + info.split("\n").each do |line| if line =~ /^nic(\d+)="(.+?)"$/ adapter = $1.to_i type = $2.to_sym @@ -347,7 +353,7 @@ module Vagrant end def read_state - output = execute("showvminfo", @uuid, "--machinereadable") + output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) if output =~ /^name=""$/ return :inaccessible elsif output =~ /^VMState="(.+?)"$/ @@ -359,7 +365,7 @@ module Vagrant def read_used_ports ports = [] - execute("list", "vms").split("\n").each do |line| + execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ uuid = $1.to_s @@ -377,7 +383,7 @@ module Vagrant def read_vms results = [] - execute("list", "vms").split("\n").each do |line| + execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ results << $1.to_s end diff --git a/lib/vagrant/driver/virtualbox_base.rb b/lib/vagrant/driver/virtualbox_base.rb index 71d1e15cc..96feb91c8 100644 --- a/lib/vagrant/driver/virtualbox_base.rb +++ b/lib/vagrant/driver/virtualbox_base.rb @@ -2,6 +2,7 @@ require 'log4r' require 'vagrant/util/busy' require 'vagrant/util/platform' +require 'vagrant/util/retryable' require 'vagrant/util/subprocess' module Vagrant @@ -13,6 +14,7 @@ module Vagrant class VirtualBoxBase # Include this so we can use `Subprocess` more easily. include Vagrant::Util + include Vagrant::Util::Retryable def initialize @logger = Log4r::Logger.new("vagrant::driver::virtualbox_base") @@ -247,24 +249,36 @@ module Vagrant # Execute the given subcommand for VBoxManage and return the output. def execute(*command, &block) - # Execute the command - r = raw(*command, &block) + # Get the options hash if it exists + opts = {} + opts = command.pop if command.last.is_a?(Hash) - # If the command was a failure, then raise an exception that is - # nicely handled by Vagrant. - if r.exit_code != 0 - if @interrupted - @logger.info("Exit code != 0, but interrupted. Ignoring.") + tries = 0 + tries = 3 if opts[:retryable] + + # Variable to store our execution result + r = nil + + retryable(:on => Errors::VBoxManageError, :tries => tries, :sleep => 1) do + # Execute the command + r = raw(*command, &block) + + # If the command was a failure, then raise an exception that is + # nicely handled by Vagrant. + if r.exit_code != 0 + if @interrupted + @logger.info("Exit code != 0, but interrupted. Ignoring.") + else + raise Errors::VBoxManageError, :command => command.inspect + end else - raise Errors::VBoxManageError, :command => command.inspect - end - else - # Sometimes, VBoxManage fails but doesn't actual return a non-zero - # exit code. For this we inspect the output and determine if an error - # occurred. - if r.stderr =~ /VBoxManage: error:/ - @logger.info("VBoxManage error text found, assuming error.") - raise Errors::VBoxManageError, :command => command.inspect + # Sometimes, VBoxManage fails but doesn't actual return a non-zero + # exit code. For this we inspect the output and determine if an error + # occurred. + if r.stderr =~ /VBoxManage: error:/ + @logger.info("VBoxManage error text found, assuming error.") + raise Errors::VBoxManageError, :command => command.inspect + end end end