diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 114e3f2ad..532868cac 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -64,6 +64,7 @@ require "vagrant/registry" module Vagrant autoload :Action, 'vagrant/action' + autoload :BatchAction, 'vagrant/batch_action' autoload :Box, 'vagrant/box' autoload :BoxCollection, 'vagrant/box_collection' autoload :CLI, 'vagrant/cli' diff --git a/lib/vagrant/batch_action.rb b/lib/vagrant/batch_action.rb new file mode 100644 index 000000000..f12dbf204 --- /dev/null +++ b/lib/vagrant/batch_action.rb @@ -0,0 +1,41 @@ +require 'thread' + +require "log4r" + +module Vagrant + # This class executes multiple actions as a single batch, parallelizing + # the action calls if possible. + class BatchAction + def initialize + @actions = [] + @logger = Log4r::Logger.new("vagrant::batch_action") + end + + def action(machine, action, options=nil) + @actions << [machine, action, options] + end + + def run + par = true + @actions.each do |machine, _, _| + if !machine.provider_options[:parallel] + par = false + break + end + end + + @logger.info("Batch action will parallelize: #{par.inspect}") + + threads = [] + @actions.each do |machine, action, options| + @logger.info("Starting action: #{machine} #{action} #{options}") + + thread = Thread.new { machine.send(:action, action, options) } + thread.join if !par + threads << thread + end + + threads.map(&:join) + end + end +end diff --git a/test/unit/vagrant/batch_action_test.rb b/test/unit/vagrant/batch_action_test.rb new file mode 100644 index 000000000..55bb95cfd --- /dev/null +++ b/test/unit/vagrant/batch_action_test.rb @@ -0,0 +1,34 @@ +require 'thread' + +require File.expand_path("../../base", __FILE__) + +describe Vagrant::BatchAction do + let(:called_actions) { [] } + let!(:lock) { Mutex.new } + let(:provider_options) { {} } + + def new_machine(options) + double("machine").tap do |m| + m.stub(:provider_options => options) + m.stub(:action) do |action, opts| + lock.synchronize do + called_actions << [m, action, opts] + end + end + end + end + + describe "#run" do + let(:machine) { new_machine(provider_options) } + let(:machine2) { new_machine(provider_options) } + + it "should run the actions on the machines in order" do + subject.action(machine, "up") + subject.action(machine2, "destroy") + subject.run + + called_actions.include?([machine, "up", nil]).should be + called_actions.include?([machine2, "destroy", nil]).should be + end + end +end