diff --git a/lib/vagrant/action/vm/check_accessible.rb b/lib/vagrant/action/vm/check_accessible.rb index 55b003db0..69df47194 100644 --- a/lib/vagrant/action/vm/check_accessible.rb +++ b/lib/vagrant/action/vm/check_accessible.rb @@ -7,7 +7,7 @@ module Vagrant end def call(env) - if env[:vm] && env[:vm].created? && !env[:vm].vm.accessible? + if env[:vm].state == :inaccessible # The VM we are attempting to manipulate is inaccessible. This # is a very bad situation and can only be fixed by the user. It # also prohibits us from actually doing anything with the virtual diff --git a/lib/vagrant/action/vm/check_guest_additions.rb b/lib/vagrant/action/vm/check_guest_additions.rb index 41399c15c..7044398fd 100644 --- a/lib/vagrant/action/vm/check_guest_additions.rb +++ b/lib/vagrant/action/vm/check_guest_additions.rb @@ -12,20 +12,20 @@ module Vagrant def call(env) # Use the raw interface for now, while the virtualbox gem # doesn't support guest properties (due to cross platform issues) - version = env[:vm].vm.interface.get_guest_property_value("/VirtualBox/GuestAdd/Version") + version = env[:vm].driver.guest_additions_version(env[:vm].uuid) if version.empty? env[:ui].warn I18n.t("vagrant.actions.vm.check_guest_additions.not_detected") else # Strip the -OSE/_OSE off from the guest additions and the virtual box # version since all the matters are that the version _numbers_ match up. - guest_version, vb_version = [version, VirtualBox.version].map do |v| + guest_version, vb_version = [version, env[:vm].driver.version].map do |v| v.gsub(/[-_]ose/i, '') end if guest_version != vb_version env[:ui].warn(I18n.t("vagrant.actions.vm.check_guest_additions.version_mismatch", :guest_version => version, - :virtualbox_version => VirtualBox.version)) + :virtualbox_version => vb_version)) end end diff --git a/lib/vagrant/action/vm/destroy.rb b/lib/vagrant/action/vm/destroy.rb index f38dd305d..271e1451d 100644 --- a/lib/vagrant/action/vm/destroy.rb +++ b/lib/vagrant/action/vm/destroy.rb @@ -8,8 +8,8 @@ module Vagrant def call(env) env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying") - env[:vm].vm.destroy - env[:vm].vm = nil + env[:vm].driver.delete(env[:vm].uuid) + env[:vm].uuid = nil @app.call(env) end diff --git a/lib/vagrant/action/vm/discard_state.rb b/lib/vagrant/action/vm/discard_state.rb index 608e29809..9fe58d779 100644 --- a/lib/vagrant/action/vm/discard_state.rb +++ b/lib/vagrant/action/vm/discard_state.rb @@ -9,7 +9,7 @@ module Vagrant end def call(env) - if env[:vm].created? && env[:vm].vm.saved? + if env[:vm].state == :saved env[:ui].info I18n.t("vagrant.actions.vm.discard_state.discarding") env[:vm].vm.discard_state end diff --git a/lib/vagrant/action/vm/halt.rb b/lib/vagrant/action/vm/halt.rb index 008d27d42..2f6a13d14 100644 --- a/lib/vagrant/action/vm/halt.rb +++ b/lib/vagrant/action/vm/halt.rb @@ -8,10 +8,10 @@ module Vagrant end def call(env) - if env[:vm].created? && env[:vm].vm.running? + if env[:vm].state == :running env[:vm].guest.halt if !env["force"] - if env[:vm].vm.state(true) != :powered_off + if env[:vm].state != :poweredoff env[:ui].info I18n.t("vagrant.actions.vm.halt.force") env[:vm].vm.stop end diff --git a/lib/vagrant/action/vm/import.rb b/lib/vagrant/action/vm/import.rb index 07806831d..600103bbe 100644 --- a/lib/vagrant/action/vm/import.rb +++ b/lib/vagrant/action/vm/import.rb @@ -10,7 +10,8 @@ module Vagrant env[:ui].info I18n.t("vagrant.actions.vm.import.importing", :name => env[:vm].box.name) # Import the virtual machine - env[:vm].vm = VirtualBox::VM.import(env[:vm].box.directory.join("box.ovf").to_s) do |progress| + ovf_file = env[:vm].box.directory.join("box.ovf").to_s + env[:vm].uuid = env[:vm].driver.import(ovf_file) do |progress| env[:ui].clear_line env[:ui].report_progress(progress.percent, 100, false) end @@ -20,7 +21,7 @@ module Vagrant env[:ui].clear_line # Flag as erroneous and return if import failed - raise Errors::VMImportFailure if !env[:vm].vm + raise Errors::VMImportFailure if !env[:vm].uuid # Import completed successfully. Continue the chain @app.call(env) diff --git a/lib/vagrant/command/status.rb b/lib/vagrant/command/status.rb index 346c7ad95..52a3de8b4 100644 --- a/lib/vagrant/command/status.rb +++ b/lib/vagrant/command/status.rb @@ -17,17 +17,8 @@ module Vagrant state = nil results = [] with_target_vms(argv[0]) do |vm| - if vm.created? - if vm.vm.accessible? - state = vm.vm.state.to_s - else - state = "inaccessible" - end - else - state = "not_created" - end - - results << "#{vm.name.to_s.ljust(25)}#{state.gsub("_", " ")}" + state = vm.state.to_s if !state + results << "#{vm.name.to_s.ljust(25)}#{vm.state.to_s.gsub("_", " ")}" end state = results.length == 1 ? state : "listing" diff --git a/lib/vagrant/driver/virtualbox.rb b/lib/vagrant/driver/virtualbox.rb index 04e90903c..1f50bf595 100644 --- a/lib/vagrant/driver/virtualbox.rb +++ b/lib/vagrant/driver/virtualbox.rb @@ -22,14 +22,63 @@ module Vagrant end end + # Imports the VM with the given path to the OVF file. It returns + # the UUID as a string. + def import(ovf) + output = execute("import", ovf) + if output =~ /VM name "(.+?)"/ + name = $1.to_s + output = execute("list", "vms") + if output =~ /^"#{name}" {(.+?)}$/ + return $1.to_s + end + end + + nil + end + + # This deletes the VM with the given name. + def delete(uuid) + execute("unregistervm", uuid, "--delete") + end + + # This reads the guest additions version for a VM. + def guest_additions_version(uuid) + output = execute("guestproperty", "get", uuid, "/VirtualBox/GuestAdd/Version") + return $1.to_s if output =~ /^Value: (.+?)$/ + return nil + end + + # This reads the state for the given UUID. The state of the VM + # will be returned as a symbol. + def read_state(uuid) + output = execute("showvminfo", uuid, "--machinereadable") + if output =~ /^name=""$/ + return :inaccessible + elsif output =~ /^VMState="(.+?)"$/ + return $1.to_sym + end + + nil + end + protected # This returns the version of VirtualBox that is running. # # @return [String] def read_version - result = Subprocess.execute("VBoxManage", "--version") - result.stdout.split("r")[0] + execute("--version").split("r")[0] + end + + # Execute the given subcommand for VBoxManage and return the output. + def execute(*command) + # TODO: Detect failures and handle them + r = Subprocess.execute("VBoxManage", *command) + if r.exit_code != 0 + raise Exception, "FAILURE: #{r.stderr}" + end + r.stdout end end end diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 0f366510f..f6c1595c9 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -4,6 +4,7 @@ module Vagrant class VM include Vagrant::Util + attr_reader :uuid attr_reader :env attr_reader :name attr_reader :vm @@ -23,7 +24,8 @@ module Vagrant # Look for the VM if it exists active = env.local_data[:active] || {} - @vm = VirtualBox::VM.find(active[@name.to_s]) if active[@name.to_s] + @uuid = active[@name.to_s] + # @vm = VirtualBox::VM.find(@uuid) if @uuid # Load the associated guest. load_guest! @@ -71,24 +73,34 @@ module Vagrant @ssh ||= SSH.new(self) end + # Returns the state of the VM as a symbol. + # + # @return [Symbol] + def state + return :not_created if !@uuid + state = @driver.read_state(@uuid) + return :not_created if !state + return state + end + # Returns a boolean true if the VM has been created, otherwise # returns false. # # @return [Boolean] def created? - !vm.nil? + state != :not_created end # Sets the currently active VM for this VM. If the VM is a valid, # created virtual machine, then it will also update the local data # to persist the VM. Otherwise, it will remove itself from the # local data (if it exists). - def vm=(value) - @vm = value - env.local_data[:active] ||= {} + def uuid=(value) + @uuid = value - if value && value.uuid - env.local_data[:active][name.to_s] = value.uuid + env.local_data[:active] ||= {} + if value + env.local_data[:active][name.to_s] = value else env.local_data[:active].delete(name.to_s) end @@ -98,10 +110,6 @@ module Vagrant env.local_data.commit end - def uuid - vm ? vm.uuid : nil - end - def reload! @vm = VirtualBox::VM.find(@vm.uuid) end @@ -115,8 +123,7 @@ module Vagrant end def start(options=nil) - raise Errors::VMInaccessible if !@vm.accessible? - return if @vm.running? + return if state == :running return resume if @vm.saved? run_action(:start, options) @@ -146,12 +153,6 @@ module Vagrant run_action(:resume) end - def saved? - @vm.saved? - end - - def powered_off?; @vm.powered_off? end - def ui return @_ui if defined?(@_ui) @_ui = @env.ui.dup diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 60afa7fca..d5f4d5ccd 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -227,13 +227,15 @@ en: not_created: |- The environment has not yet been created. Run `vagrant up` to create the environment. - powered_off: The VM is powered off. To restart the VM, simply run `vagrant up` + poweroff: |- + The VM is powered off. To restart the VM, simply run `vagrant up` running: |- The VM is running. To stop this VM, you can run `vagrant halt` to shut it down forcefully, or you can run `vagrant suspend` to simply suspend the virtual machine. In either case, to restart it again, simply run `vagrant up`. - saved: To resume this VM, simply run `vagrant up`. + saved: |- + To resume this VM, simply run `vagrant up`. stuck: |- The VM is "stuck!" This is a very rare state which means that VirtualBox is unable to recover the current state of the VM.