From 06f8595bc05646fec4534d010f05815037fa14ce Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 11:37:35 -0400 Subject: [PATCH 1/9] kernel/v2: clone option --- plugins/kernel_v2/config/vm.rb | 7 +++++++ templates/locales/en.yml | 1 + test/unit/plugins/kernel_v2/config/vm_test.rb | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb index 2de1b343f..acd21f20d 100644 --- a/plugins/kernel_v2/config/vm.rb +++ b/plugins/kernel_v2/config/vm.rb @@ -29,6 +29,7 @@ module VagrantPlugins attr_accessor :box_download_client_cert attr_accessor :box_download_insecure attr_accessor :box_download_location_trusted + attr_accessor :clone attr_accessor :communicator attr_accessor :graceful_halt_timeout attr_accessor :guest @@ -54,6 +55,7 @@ module VagrantPlugins @box_download_location_trusted = UNSET_VALUE @box_url = UNSET_VALUE @box_version = UNSET_VALUE + @clone = UNSET_VALUE @communicator = UNSET_VALUE @graceful_halt_timeout = UNSET_VALUE @guest = UNSET_VALUE @@ -367,6 +369,7 @@ module VagrantPlugins @box_download_location_trusted = false if @box_download_location_trusted == UNSET_VALUE @box_url = nil if @box_url == UNSET_VALUE @box_version = nil if @box_version == UNSET_VALUE + @clone = nil if @clone == UNSET_VALUE @communicator = nil if @communicator == UNSET_VALUE @graceful_halt_timeout = 60 if @graceful_halt_timeout == UNSET_VALUE @guest = nil if @guest == UNSET_VALUE @@ -558,6 +561,10 @@ module VagrantPlugins errors << I18n.t("vagrant.config.vm.box_missing") end + if box && clone + errors << I18n.t("vagrant.config.vm.clone_and_box") + end + errors << I18n.t("vagrant.config.vm.hostname_invalid_characters") if \ @hostname && @hostname !~ /^[a-z0-9][-.a-z0-9]*$/i diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 929962453..ff2f1fac1 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1404,6 +1404,7 @@ en: box_download_checksum_notblank: |- Checksum specified but must also specify "box_download_checksum_type" box_missing: "A box must be specified." + clone_and_box: "Only one of clone or box can be specified." hostname_invalid_characters: |- The hostname set for the VM should only contain letters, numbers, hyphens or dots. It cannot start with a hyphen or dot. diff --git a/test/unit/plugins/kernel_v2/config/vm_test.rb b/test/unit/plugins/kernel_v2/config/vm_test.rb index 3d9b01213..146b3f344 100644 --- a/test/unit/plugins/kernel_v2/config/vm_test.rb +++ b/test/unit/plugins/kernel_v2/config/vm_test.rb @@ -65,6 +65,12 @@ describe VagrantPlugins::Kernel_V2::VMConfig do subject.finalize! assert_valid end + + it "is invalid if clone is set" do + subject.clone = "foo" + subject.finalize! + assert_invalid + end end context "#box_check_update" do From 20310dce0ca82872d72d8f33540ae35aca1e3054 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 11:50:06 -0400 Subject: [PATCH 2/9] noop --- plugins/kernel_v2/config/vm.rb | 4 +++- plugins/providers/virtualbox/action.rb | 1 + plugins/providers/virtualbox/action/create_clone.rb | 2 -- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb index acd21f20d..3d87e2b87 100644 --- a/plugins/kernel_v2/config/vm.rb +++ b/plugins/kernel_v2/config/vm.rb @@ -29,7 +29,6 @@ module VagrantPlugins attr_accessor :box_download_client_cert attr_accessor :box_download_insecure attr_accessor :box_download_location_trusted - attr_accessor :clone attr_accessor :communicator attr_accessor :graceful_halt_timeout attr_accessor :guest @@ -38,6 +37,9 @@ module VagrantPlugins attr_accessor :usable_port_range attr_reader :provisioners + # This is an experimental feature that isn't public yet. + attr_accessor :clone + def initialize @logger = Log4r::Logger.new("vagrant::config::vm") diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index 9c4d68c72..a64782973 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -394,6 +394,7 @@ module VagrantPlugins b2.use MatchMACAddress end end + b.use action_start end end diff --git a/plugins/providers/virtualbox/action/create_clone.rb b/plugins/providers/virtualbox/action/create_clone.rb index a76e23794..eae9eda5e 100644 --- a/plugins/providers/virtualbox/action/create_clone.rb +++ b/plugins/providers/virtualbox/action/create_clone.rb @@ -10,8 +10,6 @@ module VagrantPlugins end def call(env) - @logger.info("Creating linked clone from master '#{env[:master_id]}'") - # Get the snapshot to base the linked clone on. This defaults # to "base" which is automatically setup with linked clones. snapshot = "base" From 9f05d22eb063d3bedf0648a0447eaa690be63c34 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 12:03:58 -0400 Subject: [PATCH 3/9] providers/virtualbox: cloning can do a non-linked clone --- .../providers/virtualbox/action/create_clone.rb | 14 ++++++++------ plugins/providers/virtualbox/driver/version_4_3.rb | 14 ++++++++------ plugins/providers/virtualbox/driver/version_5_0.rb | 14 ++++++++------ templates/locales/en.yml | 2 +- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/plugins/providers/virtualbox/action/create_clone.rb b/plugins/providers/virtualbox/action/create_clone.rb index eae9eda5e..ac3ca519e 100644 --- a/plugins/providers/virtualbox/action/create_clone.rb +++ b/plugins/providers/virtualbox/action/create_clone.rb @@ -12,16 +12,18 @@ module VagrantPlugins def call(env) # Get the snapshot to base the linked clone on. This defaults # to "base" which is automatically setup with linked clones. - snapshot = "base" - if env[:machine].provider_config.linked_clone_snapshot - snapshot = env[:machine].provider_config.linked_clone_snapshot + snapshot = nil + if env[:machine].provider_config.linked_clone + snapshot = "base" + if env[:machine].provider_config.linked_clone_snapshot + snapshot = env[:machine].provider_config.linked_clone_snapshot + end end # Do the actual clone - env[:ui].info I18n.t( - "vagrant.actions.vm.clone.creating", name: env[:machine].box.name) + env[:ui].info I18n.t("vagrant.actions.vm.clone.creating") env[:machine].id = env[:machine].provider.driver.clonevm( - env[:master_id], env[:machine].box.name, snapshot) do |progress| + env[:master_id], snapshot) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end diff --git a/plugins/providers/virtualbox/driver/version_4_3.rb b/plugins/providers/virtualbox/driver/version_4_3.rb index b51eec37a..f895739d3 100644 --- a/plugins/providers/virtualbox/driver/version_4_3.rb +++ b/plugins/providers/virtualbox/driver/version_4_3.rb @@ -35,13 +35,15 @@ module VagrantPlugins end end - def clonevm(master_id, box_name, snapshot_name) - @logger.debug("Creating linked clone from master vm with id #{master_id} from snapshot '#{snapshot_name}'") + def clonevm(master_id, snapshot_name) + machine_name = "temp_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" + args = ["--register", "--name", machine_name] + if snapshot_name + args += ["--snapshot", snapshot_name, "--options", "link"] + end - machine_name = "#{box_name}_#{snapshot_name}_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" - execute("clonevm", master_id, "--snapshot", snapshot_name, "--options", "link", "--register", "--name", machine_name) - - return get_machine_id machine_name + execute("clonevm", master_id, *args) + return get_machine_id(machine_name) end def create_dhcp_server(network, options) diff --git a/plugins/providers/virtualbox/driver/version_5_0.rb b/plugins/providers/virtualbox/driver/version_5_0.rb index 9762eda42..d13330c00 100644 --- a/plugins/providers/virtualbox/driver/version_5_0.rb +++ b/plugins/providers/virtualbox/driver/version_5_0.rb @@ -34,13 +34,15 @@ module VagrantPlugins end end - def clonevm(master_id, box_name, snapshot_name) - @logger.debug("Creating linked clone from master vm with id #{master_id} from snapshot '#{snapshot_name}'") + def clonevm(master_id, snapshot_name) + machine_name = "temp_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" + args = ["--register", "--name", machine_name] + if snapshot_name + args += ["--snapshot", snapshot_name, "--options", "link"] + end - machine_name = "#{box_name}_#{snapshot_name}_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" - execute("clonevm", master_id, "--snapshot", snapshot_name, "--options", "link", "--register", "--name", machine_name) - - return get_machine_id machine_name + execute("clonevm", master_id, *args) + return get_machine_id(machine_name) end def create_dhcp_server(network, options) diff --git a/templates/locales/en.yml b/templates/locales/en.yml index ff2f1fac1..d5fa23d2c 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1636,7 +1636,7 @@ en: This is a one time operation. Once the master VM is prepared, it will be used as a base for linked clones, making the creation of new VMs take milliseconds on a modern system. - creating: Creating linked clone... + creating: Cloning VM... failure: Creation of the linked clone failed. create_master: failure: |- From c5c3ba616bc8b8ef51d04286e3a760ab10ef5fac Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 12:09:46 -0400 Subject: [PATCH 4/9] providers/virtualbox: some progress --- plugins/providers/virtualbox/action.rb | 9 +++++++- .../virtualbox/action/prepare_clone.rb | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 plugins/providers/virtualbox/action/prepare_clone.rb diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index a64782973..186f8debd 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -35,6 +35,7 @@ module VagrantPlugins autoload :NetworkFixIPv6, File.expand_path("../action/network_fix_ipv6", __FILE__) autoload :Package, File.expand_path("../action/package", __FILE__) autoload :PackageVagrantfile, File.expand_path("../action/package_vagrantfile", __FILE__) + autoload :PrepareClone, File.expand_path("../action/prepare_clone", __FILE__) autoload :PrepareNFSSettings, File.expand_path("../action/prepare_nfs_settings", __FILE__) autoload :PrepareNFSValidIds, File.expand_path("../action/prepare_nfs_valid_ids", __FILE__) autoload :PrepareForwardedPortCollisionParams, File.expand_path("../action/prepare_forwarded_port_collision_params", __FILE__) @@ -384,10 +385,16 @@ module VagrantPlugins b2.use CheckAccessible b2.use Customize, "pre-import" - if env[:machine].provider_config.linked_clone + if env[:machine].config.vm.clone + # We are cloning from another Vagrant environment + b2.use PrepareClone + b2.use CreateClone + elsif env[:machine].provider_config.linked_clone + # We are cloning from the box b2.use ImportMaster b2.use CreateClone else + # We are just doing a normal import from a box b2.use Import end diff --git a/plugins/providers/virtualbox/action/prepare_clone.rb b/plugins/providers/virtualbox/action/prepare_clone.rb new file mode 100644 index 000000000..e3451cfef --- /dev/null +++ b/plugins/providers/virtualbox/action/prepare_clone.rb @@ -0,0 +1,21 @@ +require "log4r" + +module VagrantPlugins + module ProviderVirtualBox + module Action + class PrepareClone + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant::action::vm::prepare_clone") + end + + def call(env) + # We need to get the machine ID from this Vagrant environment + + # Continue + @app.call(env) + end + end + end + end +end From f0ddac8c9a2a6c89b44fbc8cdd931a5fad3ac89c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 12:33:55 -0400 Subject: [PATCH 5/9] providers/virtualbox: clone --- lib/vagrant/environment.rb | 22 +++++++ lib/vagrant/errors.rb | 12 +++- plugins/providers/virtualbox/action.rb | 3 + .../virtualbox/action/create_clone.rb | 12 +--- .../virtualbox/action/import_master.rb | 26 ++------ .../virtualbox/action/prepare_clone.rb | 11 ++++ .../action/prepare_clone_snapshot.rb | 65 +++++++++++++++++++ templates/locales/en.yml | 11 ++++ 8 files changed, 130 insertions(+), 32 deletions(-) create mode 100644 plugins/providers/virtualbox/action/prepare_clone_snapshot.rb diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index c3dabd676..0981d9df6 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -9,6 +9,7 @@ require 'log4r' require 'vagrant/util/file_mode' require 'vagrant/util/platform' +require "vagrant/util/silence_warnings" require "vagrant/vagrantfile" require "vagrant/version" @@ -413,6 +414,27 @@ module Vagrant @config_loader end + # Loads another environment for the given Vagrantfile, sharing as much + # useful state from this Environment as possible (such as UI and paths). + # Any initialization options can be overidden using the opts hash. + # + # @param [String] vagrantfile Path to a Vagrantfile + # @return [Environment] + def environment(vagrantfile, **opts) + path = File.expand_path(vagrantfile, root_path) + file = File.basename(path) + path = File.dirname(path) + + Util::SilenceWarnings.silence! do + Environment.new({ + cwd: path, + home_path: home_path, + ui_class: ui_class, + vagrantfile_name: file, + }.merge(opts)) + end + end + # This defines a hook point where plugin action hooks that are registered # against the given name will be run in the context of this environment. # diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 2ce133618..906b8d9bc 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -288,6 +288,14 @@ module Vagrant error_key(:cli_invalid_options) end + class CloneNotFound < VagrantError + error_key(:clone_not_found) + end + + class CloneMachineNotFound < VagrantError + error_key(:clone_machine_not_found) + end + class CommandUnavailable < VagrantError error_key(:command_unavailable) end @@ -787,11 +795,11 @@ module Vagrant class VMCloneFailure < VagrantError error_key(:failure, "vagrant.actions.vm.clone") end - + class VMCreateMasterFailure < VagrantError error_key(:failure, "vagrant.actions.vm.clone.create_master") end - + class VMCustomizationFailed < VagrantError error_key(:failure, "vagrant.actions.vm.customize") end diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index 186f8debd..9a67f0b37 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -36,6 +36,7 @@ module VagrantPlugins autoload :Package, File.expand_path("../action/package", __FILE__) autoload :PackageVagrantfile, File.expand_path("../action/package_vagrantfile", __FILE__) autoload :PrepareClone, File.expand_path("../action/prepare_clone", __FILE__) + autoload :PrepareCloneSnapshot, File.expand_path("../action/prepare_clone_snapshot", __FILE__) autoload :PrepareNFSSettings, File.expand_path("../action/prepare_nfs_settings", __FILE__) autoload :PrepareNFSValidIds, File.expand_path("../action/prepare_nfs_valid_ids", __FILE__) autoload :PrepareForwardedPortCollisionParams, File.expand_path("../action/prepare_forwarded_port_collision_params", __FILE__) @@ -388,10 +389,12 @@ module VagrantPlugins if env[:machine].config.vm.clone # We are cloning from another Vagrant environment b2.use PrepareClone + b2.use PrepareCloneSnapshot b2.use CreateClone elsif env[:machine].provider_config.linked_clone # We are cloning from the box b2.use ImportMaster + b2.use PrepareCloneSnapshot b2.use CreateClone else # We are just doing a normal import from a box diff --git a/plugins/providers/virtualbox/action/create_clone.rb b/plugins/providers/virtualbox/action/create_clone.rb index ac3ca519e..526b5a5c1 100644 --- a/plugins/providers/virtualbox/action/create_clone.rb +++ b/plugins/providers/virtualbox/action/create_clone.rb @@ -10,20 +10,10 @@ module VagrantPlugins end def call(env) - # Get the snapshot to base the linked clone on. This defaults - # to "base" which is automatically setup with linked clones. - snapshot = nil - if env[:machine].provider_config.linked_clone - snapshot = "base" - if env[:machine].provider_config.linked_clone_snapshot - snapshot = env[:machine].provider_config.linked_clone_snapshot - end - end - # Do the actual clone env[:ui].info I18n.t("vagrant.actions.vm.clone.creating") env[:machine].id = env[:machine].provider.driver.clonevm( - env[:master_id], snapshot) do |progress| + env[:clone_id], env[:clone_snapshot]) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end diff --git a/plugins/providers/virtualbox/action/import_master.rb b/plugins/providers/virtualbox/action/import_master.rb index 985a346ba..17296bbcc 100644 --- a/plugins/providers/virtualbox/action/import_master.rb +++ b/plugins/providers/virtualbox/action/import_master.rb @@ -37,14 +37,14 @@ module VagrantPlugins master_id_file = env[:machine].box.directory.join("master_id") # Read the master ID if we have it in the file. - env[:master_id] = master_id_file.read.chomp if master_id_file.file? + env[:clone_id] = master_id_file.read.chomp if master_id_file.file? # If we have the ID and the VM exists already, then we # have nothing to do. Success! - if env[:master_id] && env[:machine].provider.driver.vm_exists?(env[:master_id]) + if env[:clone_id] && env[:machine].provider.driver.vm_exists?(env[:clone_id]) @logger.info( "Master VM for '#{env[:machine].box.name}' already exists " + - " (id=#{env[:master_id]}) - skipping import step.") + " (id=#{env[:clone_id]}) - skipping import step.") return end @@ -53,27 +53,15 @@ module VagrantPlugins # Import the virtual machine import_env = env[:action_runner].run(Import, env.dup.merge(skip_machine: true)) - env[:master_id] = import_env[:machine_id] + env[:clone_id] = import_env[:machine_id] @logger.info( "Imported box #{env[:machine].box.name} as master vm " + - "with id #{env[:master_id]}") + "with id #{env[:clone_id]}") - if !env[:machine].provider_config.linked_clone_snapshot - snapshots = env[:machine].provider.driver.list_snapshots(env[:master_id]) - if !snapshots.include?("base") - @logger.info("Creating base snapshot for master VM.") - env[:machine].provider.driver.create_snapshot( - env[:master_id], "base") do |progress| - env[:ui].clear_line - env[:ui].report_progress(progress, 100, false) - end - end - end - - @logger.debug("Writing id of master VM '#{env[:master_id]}' to #{master_id_file}") + @logger.debug("Writing id of master VM '#{env[:clone_id]}' to #{master_id_file}") master_id_file.open("w+") do |f| - f.write(env[:master_id]) + f.write(env[:clone_id]) end end end diff --git a/plugins/providers/virtualbox/action/prepare_clone.rb b/plugins/providers/virtualbox/action/prepare_clone.rb index e3451cfef..6b3bf34dd 100644 --- a/plugins/providers/virtualbox/action/prepare_clone.rb +++ b/plugins/providers/virtualbox/action/prepare_clone.rb @@ -11,6 +11,17 @@ module VagrantPlugins def call(env) # We need to get the machine ID from this Vagrant environment + clone_env = env[:machine].env.environment( + env[:machine].config.vm.clone) + raise Vagrant::Errors::CloneNotFound if !clone_env.root_path + + # Get the machine itself + clone_machine = clone_env.machine( + clone_env.primary_machine_name, env[:machine].provider_name) + raise Vagrant::Errors::CloneMachineNotFound if !clone_machine.id + + # Set the ID of the master so we know what to clone from + env[:clone_id] = clone_machine.id # Continue @app.call(env) diff --git a/plugins/providers/virtualbox/action/prepare_clone_snapshot.rb b/plugins/providers/virtualbox/action/prepare_clone_snapshot.rb new file mode 100644 index 000000000..08ce16a6d --- /dev/null +++ b/plugins/providers/virtualbox/action/prepare_clone_snapshot.rb @@ -0,0 +1,65 @@ +require "log4r" + +require "digest/md5" + +module VagrantPlugins + module ProviderVirtualBox + module Action + class PrepareCloneSnapshot + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant::action::vm::prepare_clone") + end + + def call(env) + if !env[:clone_id] + @logger.info("no clone master, not preparing clone snapshot") + return @app.call(env) + end + + # If we're not doing a linked clone, snapshots don't matter + if !env[:machine].provider_config.linked_clone + return @app.call(env) + end + + # We lock so that we don't snapshot in parallel + lock_key = Digest::MD5.hexdigest("#{env[:clone_id]}-snapshot") + env[:machine].env.lock(lock_key, retry: true) do + prepare_snapshot(env) + end + + # Continue + @app.call(env) + end + + protected + + def prepare_snapshot(env) + name = env[:machine].provider_config.linked_clone_snapshot + name_set = !!name + name = "base" if !name + env[:clone_snapshot] = name + + # Get the snapshots. We're done if it already exists + snapshots = env[:machine].provider.driver.list_snapshots(env[:clone_id]) + if snapshots.include?(name) + @logger.info("clone snapshot already exists, doing nothing") + return + end + + # If they asked for a specific snapshot, it is an error + if name_set + # TODO: Error + end + + @logger.info("Creating base snapshot for master VM.") + env[:machine].provider.driver.create_snapshot( + env[:clone_id], name) do |progress| + env[:ui].clear_line + env[:ui].report_progress(progress, 100, false) + end + end + end + end + end +end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index d5fa23d2c..9e8c680aa 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -650,6 +650,17 @@ en: available below. %{help} + clone_not_found: |- + The specified Vagrantfile to clone from was not found. Please verify + the `config.vm.clone` setting points to a valid Vagrantfile. + clone_machine_not_found: |- + The clone environment hasn't been created yet. To clone from + another Vagrantfile, it must already be created with `vagrant up`. + It doesn't need to be running. + + Additionally, the created environment must be started with a provider + matching this provider. For example, if you're using VirtualBox, + the clone environment must also be using VirtualBox. command_unavailable: |- The executable '%{file}' Vagrant is trying to run was not found in the PATH variable. This is an error. Please verify From dbcc936a713d2d7683dfbe2d691a758754fd8083 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 12:41:16 -0400 Subject: [PATCH 6/9] kernel/v2: box is optional if clone is set --- plugins/kernel_v2/config/vm.rb | 2 +- plugins/providers/virtualbox/action/match_mac_address.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb index 3d87e2b87..3020611f4 100644 --- a/plugins/kernel_v2/config/vm.rb +++ b/plugins/kernel_v2/config/vm.rb @@ -559,7 +559,7 @@ module VagrantPlugins def validate(machine) errors = _detected_errors - if !box && !machine.provider_options[:box_optional] + if !box && !clone && !machine.provider_options[:box_optional] errors << I18n.t("vagrant.config.vm.box_missing") end diff --git a/plugins/providers/virtualbox/action/match_mac_address.rb b/plugins/providers/virtualbox/action/match_mac_address.rb index a66f40bad..10e998b2e 100644 --- a/plugins/providers/virtualbox/action/match_mac_address.rb +++ b/plugins/providers/virtualbox/action/match_mac_address.rb @@ -7,6 +7,9 @@ module VagrantPlugins end def call(env) + # If we cloned, we don't need a base mac, it is already set! + return @app.call(env) if env[:machine].config.vm.clone + raise Vagrant::Errors::VMBaseMacNotSpecified if !env[:machine].config.vm.base_mac # Create the proc which we want to use to modify the virtual machine From e9922d17546fb9b0c5f72cf72e4bfc6e0c8ab092 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 12:50:02 -0400 Subject: [PATCH 7/9] providers/virtualbox: discard state if cloning --- plugins/providers/virtualbox/action.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index 9a67f0b37..e102625dc 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -391,11 +391,13 @@ module VagrantPlugins b2.use PrepareClone b2.use PrepareCloneSnapshot b2.use CreateClone + b2.use DiscardState elsif env[:machine].provider_config.linked_clone # We are cloning from the box b2.use ImportMaster b2.use PrepareCloneSnapshot b2.use CreateClone + b2.use DiscardState else # We are just doing a normal import from a box b2.use Import From 4908cd9cd956f1179b956787d3f4b7b043a7fe30 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 12:58:06 -0400 Subject: [PATCH 8/9] providers/virtualbox: copy SSH key --- plugins/providers/virtualbox/action.rb | 6 +++--- .../action/{create_clone.rb => clone.rb} | 14 +++++++++++++- .../providers/virtualbox/action/prepare_clone.rb | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) rename plugins/providers/virtualbox/action/{create_clone.rb => clone.rb} (78%) diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index e102625dc..f7984dca4 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -12,7 +12,7 @@ module VagrantPlugins autoload :CleanMachineFolder, File.expand_path("../action/clean_machine_folder", __FILE__) autoload :ClearForwardedPorts, File.expand_path("../action/clear_forwarded_ports", __FILE__) autoload :ClearNetworkInterfaces, File.expand_path("../action/clear_network_interfaces", __FILE__) - autoload :CreateClone, File.expand_path("../action/create_clone", __FILE__) + autoload :Clone, File.expand_path("../action/clone", __FILE__) autoload :Created, File.expand_path("../action/created", __FILE__) autoload :Customize, File.expand_path("../action/customize", __FILE__) autoload :Destroy, File.expand_path("../action/destroy", __FILE__) @@ -390,13 +390,13 @@ module VagrantPlugins # We are cloning from another Vagrant environment b2.use PrepareClone b2.use PrepareCloneSnapshot - b2.use CreateClone + b2.use Clone b2.use DiscardState elsif env[:machine].provider_config.linked_clone # We are cloning from the box b2.use ImportMaster b2.use PrepareCloneSnapshot - b2.use CreateClone + b2.use Clone b2.use DiscardState else # We are just doing a normal import from a box diff --git a/plugins/providers/virtualbox/action/create_clone.rb b/plugins/providers/virtualbox/action/clone.rb similarity index 78% rename from plugins/providers/virtualbox/action/create_clone.rb rename to plugins/providers/virtualbox/action/clone.rb index 526b5a5c1..6b389d4f1 100644 --- a/plugins/providers/virtualbox/action/create_clone.rb +++ b/plugins/providers/virtualbox/action/clone.rb @@ -1,9 +1,11 @@ require "log4r" +require "fileutils" + module VagrantPlugins module ProviderVirtualBox module Action - class CreateClone + class Clone def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::clone") @@ -25,6 +27,16 @@ module VagrantPlugins # Flag as erroneous and return if clone failed raise Vagrant::Errors::VMCloneFailure if !env[:machine].id + # Copy the SSH key from the clone machine if we can + if env[:clone_machine] + key_path = env[:clone_machine].data_dir.join("private_key") + if key_path.file? + FileUtils.cp( + key_path, + env[:machine].data_dir.join("private_key")) + end + end + # Continue @app.call(env) end diff --git a/plugins/providers/virtualbox/action/prepare_clone.rb b/plugins/providers/virtualbox/action/prepare_clone.rb index 6b3bf34dd..c306b9b54 100644 --- a/plugins/providers/virtualbox/action/prepare_clone.rb +++ b/plugins/providers/virtualbox/action/prepare_clone.rb @@ -22,6 +22,7 @@ module VagrantPlugins # Set the ID of the master so we know what to clone from env[:clone_id] = clone_machine.id + env[:clone_machine] = clone_machine # Continue @app.call(env) From 5ea24e39d021875c2d6cc3830236364f42f78f9f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 8 Oct 2015 13:07:05 -0400 Subject: [PATCH 9/9] providers/virtualbox: unify import/clone --- plugins/providers/virtualbox/action.rb | 13 +--- plugins/providers/virtualbox/action/clone.rb | 63 ------------------- plugins/providers/virtualbox/action/import.rb | 38 +++++++++++ 3 files changed, 41 insertions(+), 73 deletions(-) delete mode 100644 plugins/providers/virtualbox/action/clone.rb diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index f7984dca4..d0ef01bff 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -12,7 +12,6 @@ module VagrantPlugins autoload :CleanMachineFolder, File.expand_path("../action/clean_machine_folder", __FILE__) autoload :ClearForwardedPorts, File.expand_path("../action/clear_forwarded_ports", __FILE__) autoload :ClearNetworkInterfaces, File.expand_path("../action/clear_network_interfaces", __FILE__) - autoload :Clone, File.expand_path("../action/clone", __FILE__) autoload :Created, File.expand_path("../action/created", __FILE__) autoload :Customize, File.expand_path("../action/customize", __FILE__) autoload :Destroy, File.expand_path("../action/destroy", __FILE__) @@ -389,20 +388,14 @@ module VagrantPlugins if env[:machine].config.vm.clone # We are cloning from another Vagrant environment b2.use PrepareClone - b2.use PrepareCloneSnapshot - b2.use Clone - b2.use DiscardState elsif env[:machine].provider_config.linked_clone # We are cloning from the box b2.use ImportMaster - b2.use PrepareCloneSnapshot - b2.use Clone - b2.use DiscardState - else - # We are just doing a normal import from a box - b2.use Import end + b2.use PrepareCloneSnapshot + b2.use Import + b2.use DiscardState b2.use MatchMACAddress end end diff --git a/plugins/providers/virtualbox/action/clone.rb b/plugins/providers/virtualbox/action/clone.rb deleted file mode 100644 index 6b389d4f1..000000000 --- a/plugins/providers/virtualbox/action/clone.rb +++ /dev/null @@ -1,63 +0,0 @@ -require "log4r" - -require "fileutils" - -module VagrantPlugins - module ProviderVirtualBox - module Action - class Clone - def initialize(app, env) - @app = app - @logger = Log4r::Logger.new("vagrant::action::vm::clone") - end - - def call(env) - # Do the actual clone - env[:ui].info I18n.t("vagrant.actions.vm.clone.creating") - env[:machine].id = env[:machine].provider.driver.clonevm( - env[:clone_id], env[:clone_snapshot]) do |progress| - env[:ui].clear_line - env[:ui].report_progress(progress, 100, false) - end - - # Clear the line one last time since the progress meter doesn't - # disappear immediately. - env[:ui].clear_line - - # Flag as erroneous and return if clone failed - raise Vagrant::Errors::VMCloneFailure if !env[:machine].id - - # Copy the SSH key from the clone machine if we can - if env[:clone_machine] - key_path = env[:clone_machine].data_dir.join("private_key") - if key_path.file? - FileUtils.cp( - key_path, - env[:machine].data_dir.join("private_key")) - end - end - - # Continue - @app.call(env) - end - - def recover(env) - if env[:machine].state.id != :not_created - return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) - - # If we're not supposed to destroy on error then just return - return if !env[:destroy_on_error] - - # Interrupted, destroy the VM. We note that we don't want to - # validate the configuration here, and we don't want to confirm - # we want to destroy. - destroy_env = env.clone - destroy_env[:config_validate] = false - destroy_env[:force_confirm_destroy] = true - env[:action_runner].run(Action.action_destroy, destroy_env) - end - end - end - end - end -end diff --git a/plugins/providers/virtualbox/action/import.rb b/plugins/providers/virtualbox/action/import.rb index 92e004f01..fb540f50a 100644 --- a/plugins/providers/virtualbox/action/import.rb +++ b/plugins/providers/virtualbox/action/import.rb @@ -7,6 +7,44 @@ module VagrantPlugins end def call(env) + if env[:clone_id] + clone(env) + else + import(env) + end + end + + def clone(env) + # Do the actual clone + env[:ui].info I18n.t("vagrant.actions.vm.clone.creating") + env[:machine].id = env[:machine].provider.driver.clonevm( + env[:clone_id], env[:clone_snapshot]) do |progress| + env[:ui].clear_line + env[:ui].report_progress(progress, 100, false) + end + + # Clear the line one last time since the progress meter doesn't + # disappear immediately. + env[:ui].clear_line + + # Flag as erroneous and return if clone failed + raise Vagrant::Errors::VMCloneFailure if !env[:machine].id + + # Copy the SSH key from the clone machine if we can + if env[:clone_machine] + key_path = env[:clone_machine].data_dir.join("private_key") + if key_path.file? + FileUtils.cp( + key_path, + env[:machine].data_dir.join("private_key")) + end + end + + # Continue + @app.call(env) + end + + def import(env) env[:ui].info I18n.t("vagrant.actions.vm.import.importing", name: env[:machine].box.name)