From bca86637420ba82c568acc888c52ecc12f9d8a0b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 13 Aug 2012 20:03:35 -0700 Subject: [PATCH] `vagrant resume` works with the new machine abstraction --- plugins/commands/resume/command.rb | 18 ++-- plugins/providers/virtualbox/action.rb | 20 +++++ plugins/providers/virtualbox/action/boot.rb | 48 ++++++++++ .../action/check_port_collisions.rb | 89 +++++++++++++++++++ .../providers/virtualbox/action}/resume.rb | 8 +- 5 files changed, 166 insertions(+), 17 deletions(-) create mode 100644 plugins/providers/virtualbox/action/boot.rb create mode 100644 plugins/providers/virtualbox/action/check_port_collisions.rb rename {lib/vagrant/action/vm => plugins/providers/virtualbox/action}/resume.rb (71%) diff --git a/plugins/commands/resume/command.rb b/plugins/commands/resume/command.rb index 256593d90..db942b48d 100644 --- a/plugins/commands/resume/command.rb +++ b/plugins/commands/resume/command.rb @@ -4,10 +4,8 @@ module VagrantPlugins module CommandResume class Command < Vagrant.plugin("1", :command) def execute - options = {} - - opts = OptionParser.new do |opts| - opts.banner = "Usage: vagrant resume [vm-name]" + opts = OptionParser.new do |o| + o.banner = "Usage: vagrant resume [vm-name]" end # Parse the options @@ -15,19 +13,13 @@ module VagrantPlugins return if !argv @logger.debug("'resume' each target VM...") - with_target_vms(argv) do |vm| - if vm.created? - @logger.info("Resume: #{vm.name}") - vm.resume - else - @logger.info("Not created: #{vm.name}. Not resuming.") - vm.ui.info I18n.t("vagrant.commands.common.vm_not_created") - end + with_target_vms(argv) do |machine| + machine.action(:resume) end # Success, exit status 0 0 - end + end end end end diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index 33e6b3f11..e7c6b7021 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -3,8 +3,10 @@ require "vagrant/action/builder" module VagrantPlugins module ProviderVirtualBox module Action + autoload :Boot, File.expand_path("../action/boot", __FILE__) autoload :CheckAccessible, File.expand_path("../action/check_accessible", __FILE__) autoload :CheckCreated, File.expand_path("../action/check_created", __FILE__) + autoload :CheckPortCollisions, File.expand_path("../action/check_port_collisions", __FILE__) autoload :CheckRunning, File.expand_path("../action/check_running", __FILE__) autoload :CheckVirtualbox, File.expand_path("../action/check_virtualbox", __FILE__) autoload :Created, File.expand_path("../action/created", __FILE__) @@ -13,6 +15,7 @@ module VagrantPlugins autoload :Halt, File.expand_path("../action/halt", __FILE__) autoload :MessageNotCreated, File.expand_path("../action/message_not_created", __FILE__) autoload :MessageWillNotDestroy, File.expand_path("../action/message_will_not_destroy", __FILE__) + autoload :Resume, File.expand_path("../action/resume", __FILE__) autoload :Suspend, File.expand_path("../action/suspend", __FILE__) # Include the built-in modules so that we can use them as top-level @@ -59,6 +62,23 @@ module VagrantPlugins end end + # This is the action that is primarily responsible for resuming + # suspended machines. + def self.action_resume + Vagrant::Action::Builder.new.tap do |b| + b.use CheckVirtualbox + b.use Call, Created do |env, b2| + if env[:result] + b2.use CheckAccessible + b2.use CheckPortCollisions + b2.use Resume + else + b2.use MessageNotCreated + end + end + end + end + # This is the action that will exec into an SSH shell. def self.action_ssh Vagrant::Action::Builder.new.tap do |b| diff --git a/plugins/providers/virtualbox/action/boot.rb b/plugins/providers/virtualbox/action/boot.rb new file mode 100644 index 000000000..7c4c18f0e --- /dev/null +++ b/plugins/providers/virtualbox/action/boot.rb @@ -0,0 +1,48 @@ +module VagrantPlugins + module ProviderVirtualBox + module Action + class Boot + def initialize(app, env) + @app = app + end + + def call(env) + @env = env + + # Start up the VM and wait for it to boot. + env[:ui].info I18n.t("vagrant.actions.vm.boot.booting") + env[:machine].provider.driver.start(@env[:machine].config.vm.boot_mode) + raise Errors::VMFailedToBoot if !wait_for_boot + + @app.call(env) + end + + def wait_for_boot + @env[:ui].info I18n.t("vagrant.actions.vm.boot.waiting") + + @env[:machine].config.ssh.max_tries.to_i.times do |i| + if @env[:machine].communicate.ready? + @env[:ui].info I18n.t("vagrant.actions.vm.boot.ready") + return true + end + + # Return true so that the vm_failed_to_boot error doesn't + # get shown + return true if @env[:interrupted] + + # If the VM is not starting or running, something went wrong + # and we need to show a useful error. + state = @env[:machine].provider.state + raise Errors::VMFailedToRun if state != :starting && state != :running + + sleep 2 if !@env["vagrant.test"] + end + + @env[:ui].error I18n.t("vagrant.actions.vm.boot.failed") + false + end + + end + end + end +end diff --git a/plugins/providers/virtualbox/action/check_port_collisions.rb b/plugins/providers/virtualbox/action/check_port_collisions.rb new file mode 100644 index 000000000..c4a537d47 --- /dev/null +++ b/plugins/providers/virtualbox/action/check_port_collisions.rb @@ -0,0 +1,89 @@ +require "vagrant/util/is_port_open" + +module VagrantPlugins + module ProviderVirtualBox + module Action + class CheckPortCollisions + include Vagrant::Util::IsPortOpen + + def initialize(app, env) + @app = app + end + + def call(env) + # For the handlers... + @env = env + + # Figure out how we handle port collisions. By default we error. + handler = env[:port_collision_handler] || :error + + # Read our forwarded ports, if we have any, to override what + # we have configured. + current = {} + env[:machine].provider.driver.read_forwarded_ports.each do |nic, name, hostport, guestport| + current[name] = hostport.to_i + end + + existing = env[:machine].provider.driver.read_used_ports + env[:machine].config.vm.forwarded_ports.each do |options| + # Use the proper port, whether that be the configured port or the + # port that is currently on use of the VM. + hostport = options[:hostport].to_i + hostport = current[options[:name]] if current.has_key?(options[:name]) + + if existing.include?(hostport) || is_port_open?("localhost", hostport) + # We have a collision! Handle it + send("handle_#{handler}".to_sym, options, existing) + end + end + + @app.call(env) + end + + # Handles a port collision by raising an exception. + def handle_error(options, existing_ports) + raise Vagrant::Errors::ForwardPortCollisionResume + end + + # Handles a port collision by attempting to fix it. + def handle_correct(options, existing_ports) + # We need to keep this for messaging purposes + original_hostport = options[:hostport] + + if !options[:auto] + # Auto fixing is disabled for this port forward, so we + # must throw an error so the user can fix it. + raise Vagrant::Errors::ForwardPortCollision, + :host_port => options[:hostport].to_s, + :guest_port => options[:guestport].to_s + end + + # Get the auto port range and get rid of the used ports and + # ports which are being used in other forwards so we're just + # left with available ports. + range = @env[:machine].config.vm.auto_port_range.to_a + range -= @env[:machine].config.vm.forwarded_ports.collect { |opts| opts[:hostport].to_i } + range -= existing_ports + + if range.empty? + raise Vagrant::Errors::ForwardPortAutolistEmpty, + :vm_name => @env[:machine].name, + :host_port => options[:hostport].to_s, + :guest_port => options[:guestport].to_s + end + + # Set the port up to be the first one and add that port to + # the used list. + options[:hostport] = range.shift + existing_ports << options[:hostport] + + # Notify the user + @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.fixed_collision", + :host_port => original_hostport.to_s, + :guest_port => options[:guestport].to_s, + :new_port => options[:hostport])) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/resume.rb b/plugins/providers/virtualbox/action/resume.rb similarity index 71% rename from lib/vagrant/action/vm/resume.rb rename to plugins/providers/virtualbox/action/resume.rb index bf62c8b3e..9967b91fe 100644 --- a/lib/vagrant/action/vm/resume.rb +++ b/plugins/providers/virtualbox/action/resume.rb @@ -1,13 +1,13 @@ -module Vagrant - module Action - module VM +module VagrantPlugins + module ProviderVirtualBox + module Action class Resume def initialize(app, env) @app = app end def call(env) - if env[:vm].state == :saved + if env[:machine].provider.state == :saved env[:ui].info I18n.t("vagrant.actions.vm.resume.resuming") env[:action_runner].run(Boot, env) end