From f9f762dc0a0c09022bdcc94f1cae16f37248c893 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Mon, 7 May 2018 16:49:53 -0700
Subject: [PATCH 01/53] Add link to CHANGELOG for current release on downloads
page
---
website/source/downloads.html.erb | 3 +++
1 file changed, 3 insertions(+)
diff --git a/website/source/downloads.html.erb b/website/source/downloads.html.erb
index c3f6f6c81..46f766889 100644
--- a/website/source/downloads.html.erb
+++ b/website/source/downloads.html.erb
@@ -28,6 +28,9 @@ description: |-
which has been signed using HashiCorp's GPG key.
You can also download older versions of Vagrant from the releases service.
+
+ Check out the v<%= latest_version %> CHANGELOG for information on the latest release.
+
From 0b87184ad42810c655d814c98fd0d6bdd73b23c1 Mon Sep 17 00:00:00 2001
From: Alexander Fischer
Date: Fri, 11 May 2018 08:49:57 +0200
Subject: [PATCH 02/53] #9174 Remove masterless? config attribute
Remove the @config.masterless? attribute as there is no benefit from it
---
plugins/provisioners/salt/provisioner.rb | 6 +++---
test/unit/plugins/provisioners/salt/provisioner_test.rb | 4 ----
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/plugins/provisioners/salt/provisioner.rb b/plugins/provisioners/salt/provisioner.rb
index 33884f7f3..efa5500df 100644
--- a/plugins/provisioners/salt/provisioner.rb
+++ b/plugins/provisioners/salt/provisioner.rb
@@ -385,7 +385,7 @@ module VagrantPlugins
@machine.env.ui.info "Calling state.highstate... (this may take a while)"
if @config.install_master
- unless @config.masterless?
+ unless @config.masterless
@machine.communicate.sudo("salt '*' saltutil.sync_all")
end
options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_salt_args}"
@@ -397,7 +397,7 @@ module VagrantPlugins
else
if @machine.config.vm.communicator == :winrm
opts = { elevated: true }
- unless @config.masterless?
+ unless @config.masterless
@machine.communicate.execute("C:\\salt\\salt-call.bat saltutil.sync_all", opts)
end
# TODO: something equivalent to { error_key: :ssh_bad_exit_status_muted }?
@@ -408,7 +408,7 @@ module VagrantPlugins
end
end
else
- unless @config.masterless?
+ unless @config.masterless
@machine.communicate.sudo("salt-call saltutil.sync_all")
end
options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_call_args}"
diff --git a/test/unit/plugins/provisioners/salt/provisioner_test.rb b/test/unit/plugins/provisioners/salt/provisioner_test.rb
index ad9d0753e..e6bfece89 100644
--- a/test/unit/plugins/provisioners/salt/provisioner_test.rb
+++ b/test/unit/plugins/provisioners/salt/provisioner_test.rb
@@ -55,7 +55,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "passes along extra cli flags" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
- allow(config).to receive(:masterless?).and_return(false)
allow(config).to receive(:masterless).and_return(false)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)
@@ -74,7 +73,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "has no additional cli flags if not included" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
- allow(config).to receive(:masterless?).and_return(false)
allow(config).to receive(:masterless).and_return(false)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)
@@ -95,7 +93,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "passes along extra cli flags" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
- allow(config).to receive(:masterless?).and_return(true)
allow(config).to receive(:masterless).and_return(true)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)
@@ -115,7 +112,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "has no additional cli flags if not included" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
- allow(config).to receive(:masterless?).and_return(true)
allow(config).to receive(:masterless).and_return(true)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)
From be096c3ef4b41f463d06f2cbd5835a9670e9174e Mon Sep 17 00:00:00 2001
From: Igor Mazur
Date: Mon, 21 May 2018 23:48:40 +0300
Subject: [PATCH 03/53] Fix for ubuntu 17.10+ netplan
https://github.com/hashicorp/vagrant/issues/9570
---
plugins/guests/debian/cap/configure_networks.rb | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/plugins/guests/debian/cap/configure_networks.rb b/plugins/guests/debian/cap/configure_networks.rb
index 6ba4c2ca9..5f35b874d 100644
--- a/plugins/guests/debian/cap/configure_networks.rb
+++ b/plugins/guests/debian/cap/configure_networks.rb
@@ -37,7 +37,9 @@ module VagrantPlugins
ethernets = {}.tap do |e_nets|
networks.each do |network|
e_config = {}.tap do |entry|
- if network[:ip]
+ if network[:type] == :dhcp
+ entry["dhcp4"] = true
+ else
mask = network[:netmask]
if mask && IPAddr.new(network[:ip]).ipv4?
begin
@@ -47,8 +49,6 @@ module VagrantPlugins
end
end
entry["addresses"] = [[network[:ip], mask].compact.join("/")]
- else
- entry["dhcp4"] = true
end
if network[:gateway]
entry["gateway4"] = network[:gateway]
From 07bbaf9ac5ea0ebd87333971f3b68c1846e23916 Mon Sep 17 00:00:00 2001
From: Igor Mazur
Date: Tue, 22 May 2018 23:29:53 +0300
Subject: [PATCH 04/53] Fix fo tests (symbol and string dhcp)
---
plugins/guests/debian/cap/configure_networks.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/guests/debian/cap/configure_networks.rb b/plugins/guests/debian/cap/configure_networks.rb
index 5f35b874d..bde7957e9 100644
--- a/plugins/guests/debian/cap/configure_networks.rb
+++ b/plugins/guests/debian/cap/configure_networks.rb
@@ -37,7 +37,7 @@ module VagrantPlugins
ethernets = {}.tap do |e_nets|
networks.each do |network|
e_config = {}.tap do |entry|
- if network[:type] == :dhcp
+ if network[:type] == :dhcp || network[:type] == "dhcp"
entry["dhcp4"] = true
else
mask = network[:netmask]
From 66b28663730f7a0df7f7777b41efef768d3f0ace Mon Sep 17 00:00:00 2001
From: Igor Mazur
Date: Wed, 23 May 2018 11:17:40 +0300
Subject: [PATCH 05/53] Shorten if
---
plugins/guests/debian/cap/configure_networks.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/guests/debian/cap/configure_networks.rb b/plugins/guests/debian/cap/configure_networks.rb
index bde7957e9..4747b2e19 100644
--- a/plugins/guests/debian/cap/configure_networks.rb
+++ b/plugins/guests/debian/cap/configure_networks.rb
@@ -37,7 +37,7 @@ module VagrantPlugins
ethernets = {}.tap do |e_nets|
networks.each do |network|
e_config = {}.tap do |entry|
- if network[:type] == :dhcp || network[:type] == "dhcp"
+ if network[:type].to_s == "dhcp"
entry["dhcp4"] = true
else
mask = network[:netmask]
From e6457d50617e558e9d8953ca76c597eb4469455d Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Thu, 24 May 2018 09:57:55 -0700
Subject: [PATCH 06/53] Overhaul the Hyper-V provider
---
lib/vagrant/util/powershell.rb | 6 +-
plugins/providers/hyperv/action.rb | 4 +
plugins/providers/hyperv/action/configure.rb | 102 +++
plugins/providers/hyperv/action/import.rb | 160 +----
plugins/providers/hyperv/action/set_name.rb | 43 ++
plugins/providers/hyperv/config.rb | 89 ++-
plugins/providers/hyperv/driver.rb | 221 +++++--
.../providers/hyperv/scripts/check_hyperv.ps1 | 6 +-
.../providers/hyperv/scripts/clone_vhd.ps1 | 13 +-
.../providers/hyperv/scripts/configure_vm.ps1 | 95 +++
.../hyperv/scripts/create_snapshot.ps1 | 15 +-
.../hyperv/scripts/delete_snapshot.ps1 | 14 +-
.../providers/hyperv/scripts/delete_vm.ps1 | 15 +-
.../providers/hyperv/scripts/export_vm.ps1 | 28 +-
.../providers/hyperv/scripts/file_sync.ps1 | 28 +-
.../hyperv/scripts/get_network_config.ps1 | 94 +--
.../hyperv/scripts/get_network_mac.ps1 | 46 +-
.../providers/hyperv/scripts/get_switches.ps1 | 4 +-
.../hyperv/scripts/get_vm_status.ps1 | 10 +-
.../hyperv/scripts/has_vmcx_support.ps1 | 4 +-
.../providers/hyperv/scripts/import_vm.ps1 | 37 ++
.../hyperv/scripts/import_vm_vmcx.ps1 | 165 -----
.../hyperv/scripts/import_vm_xml.ps1 | 221 -------
.../hyperv/scripts/list_snapshots.ps1 | 21 +-
.../hyperv/scripts/restore_snapshot.ps1 | 15 +-
.../providers/hyperv/scripts/resume_vm.ps1 | 15 +-
plugins/providers/hyperv/scripts/set_name.ps1 | 24 +
.../hyperv/scripts/set_network_mac.ps1 | 28 +-
.../hyperv/scripts/set_network_vlan.ps1 | 10 +-
.../scripts/set_vm_integration_services.ps1 | 52 +-
plugins/providers/hyperv/scripts/start_vm.ps1 | 45 +-
plugins/providers/hyperv/scripts/stop_vm.ps1 | 15 +-
.../providers/hyperv/scripts/suspend_vm.ps1 | 15 +-
.../VagrantMessages/VagrantMessages.psm1 | 27 +
.../scripts/utils/VagrantVM/VagrantVM.psm1 | 616 ++++++++++++++++++
.../hyperv/scripts/utils/write_messages.ps1 | 20 -
36 files changed, 1510 insertions(+), 813 deletions(-)
create mode 100644 plugins/providers/hyperv/action/configure.rb
create mode 100644 plugins/providers/hyperv/action/set_name.rb
create mode 100644 plugins/providers/hyperv/scripts/configure_vm.ps1
create mode 100644 plugins/providers/hyperv/scripts/import_vm.ps1
delete mode 100644 plugins/providers/hyperv/scripts/import_vm_vmcx.ps1
delete mode 100644 plugins/providers/hyperv/scripts/import_vm_xml.ps1
create mode 100644 plugins/providers/hyperv/scripts/set_name.ps1
create mode 100644 plugins/providers/hyperv/scripts/utils/VagrantMessages/VagrantMessages.psm1
create mode 100644 plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1
delete mode 100644 plugins/providers/hyperv/scripts/utils/write_messages.ps1
diff --git a/lib/vagrant/util/powershell.rb b/lib/vagrant/util/powershell.rb
index 4ffa0a75b..9afb4da65 100644
--- a/lib/vagrant/util/powershell.rb
+++ b/lib/vagrant/util/powershell.rb
@@ -47,13 +47,17 @@ module Vagrant
if opts.delete(:sudo) || opts.delete(:runas)
powerup_command(path, args, opts)
else
+ env = opts.delete(:env)
+ if env
+ env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
+ end
command = [
executable,
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
- "&('#{path}')",
+ "#{env}&('#{path}')",
args
].flatten
diff --git a/plugins/providers/hyperv/action.rb b/plugins/providers/hyperv/action.rb
index f5e9b25c6..a42c9966a 100644
--- a/plugins/providers/hyperv/action.rb
+++ b/plugins/providers/hyperv/action.rb
@@ -140,6 +140,8 @@ module VagrantPlugins
end
b2.use Provision
+ b2.use Configure
+ b2.use SetName
b2.use NetSetVLan
b2.use NetSetMac
b2.use StartInstance
@@ -288,6 +290,7 @@ module VagrantPlugins
autoload :Export, action_root.join("export")
autoload :CheckEnabled, action_root.join("check_enabled")
+ autoload :Configure, action_root.join("configure")
autoload :DeleteVM, action_root.join("delete_vm")
autoload :Import, action_root.join("import")
autoload :Package, action_root.join("package")
@@ -304,6 +307,7 @@ module VagrantPlugins
autoload :SnapshotDelete, action_root.join("snapshot_delete")
autoload :SnapshotRestore, action_root.join("snapshot_restore")
autoload :SnapshotSave, action_root.join("snapshot_save")
+ autoload :SetName, action_root.join("set_name")
end
end
end
diff --git a/plugins/providers/hyperv/action/configure.rb b/plugins/providers/hyperv/action/configure.rb
new file mode 100644
index 000000000..f8d1e7d74
--- /dev/null
+++ b/plugins/providers/hyperv/action/configure.rb
@@ -0,0 +1,102 @@
+require "fileutils"
+
+require "log4r"
+
+module VagrantPlugins
+ module HyperV
+ module Action
+ class Configure
+ def initialize(app, env)
+ @app = app
+ @logger = Log4r::Logger.new("vagrant::hyperv::configure")
+ end
+
+ def call(env)
+ switches = env[:machine].provider.driver.execute(:get_switches)
+ if switches.empty?
+ raise Errors::NoSwitches
+ end
+
+ switch = nil
+ env[:machine].config.vm.networks.each do |type, opts|
+ next if type != :public_network && type != :private_network
+
+ if opts[:bridge]
+ @logger.debug("Looking for switch with name or ID: #{opts[:bridge]}")
+ switch = switches.find{ |s|
+ s["Name"].downcase == opts[:bridge] ||
+ s["Id"].downcase == opts[:bridge]
+ }
+ if switch
+ @logger.debug("Found switch - Name: #{switch["Name"]} ID: #{switch["Id"]}")
+ switch = switch["Id"]
+ break
+ end
+ end
+ end
+
+ # If we already configured previously don't prompt for switch
+ sentinel = env[:machine].data_dir.join("action_configure")
+
+ if !switch && !sentinel.file?
+ if switches.length > 1
+ env[:ui].detail(I18n.t("vagrant_hyperv.choose_switch") + "\n ")
+ switches.each_index do |i|
+ switch = switches[i]
+ env[:ui].detail("#{i+1}) #{switch["Name"]}")
+ end
+ env[:ui].detail(" ")
+
+ switch = nil
+ while !switch
+ switch = env[:ui].ask("What switch would you like to use? ")
+ next if !switch
+ switch = switch.to_i - 1
+ switch = nil if switch < 0 || switch >= switches.length
+ end
+ switch = switches[switch]["Id"]
+ else
+ switch = switches.first["Id"]
+ @logger.debug("Only single switch available so using that.")
+ end
+ end
+
+ options = {
+ "VMID" => env[:machine].id,
+ "SwitchID" => switch,
+ "Memory" => env[:machine].provider_config.memory,
+ "MaxMemory" => env[:machine].provider_config.maxmemory,
+ "Processors" => env[:machine].provider_config.cpus,
+ "AutoStartAction" => env[:machine].provider_config.auto_start_action,
+ "AutoStopAction" => env[:machine].provider_config.auto_stop_action,
+ "EnableCheckpoints" => env[:machine].provider_config.enable_checkpoints,
+ "VirtualizationExtensions" => !!env[:machine].provider_config.enable_virtualization_extensions,
+ }
+ options.delete_if{|_,v| v.nil? }
+
+ env[:ui].detail("Configuring the VM...")
+ env[:machine].provider.driver.execute(:configure_vm, options)
+
+ # Create the sentinel
+ sentinel.open("w") do |f|
+ f.write(Time.now.to_i.to_s)
+ end
+
+ if env[:machine].provider_config.vm_integration_services
+ env[:ui].detail("Setting VM Integration Services")
+
+ env[:machine].provider_config.vm_integration_services.each do |key, value|
+ state = value ? "enabled" : "disabled"
+ env[:ui].output("#{key} is #{state}")
+ end
+
+ env[:machine].provider.driver.set_vm_integration_services(
+ env[:machine].provider_config.vm_integration_services)
+ end
+
+ @app.call(env)
+ end
+ end
+ end
+ end
+end
diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb
index 434d991ad..bbe19e0c3 100644
--- a/plugins/providers/hyperv/action/import.rb
+++ b/plugins/providers/hyperv/action/import.rb
@@ -1,11 +1,13 @@
require "fileutils"
-
require "log4r"
module VagrantPlugins
module HyperV
module Action
class Import
+
+ VALID_HD_EXTENSIONS = [".vhd".freeze, ".vhdx".freeze].freeze
+
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::hyperv::import")
@@ -14,160 +16,64 @@ module VagrantPlugins
def call(env)
vm_dir = env[:machine].box.directory.join("Virtual Machines")
hd_dir = env[:machine].box.directory.join("Virtual Hard Disks")
- memory = env[:machine].provider_config.memory
- maxmemory = env[:machine].provider_config.maxmemory
- cpus = env[:machine].provider_config.cpus
- vmname = env[:machine].provider_config.vmname
- differencing_disk = env[:machine].provider_config.differencing_disk
- auto_start_action = env[:machine].provider_config.auto_start_action
- auto_stop_action = env[:machine].provider_config.auto_stop_action
- enable_virtualization_extensions = env[:machine].provider_config.enable_virtualization_extensions
- vm_integration_services = env[:machine].provider_config.vm_integration_services
-
- env[:ui].output("Configured Dynamic memory allocation, maxmemory is #{maxmemory}") if maxmemory
- env[:ui].output("Configured startup memory is #{memory}") if memory
- env[:ui].output("Configured cpus number is #{cpus}") if cpus
- env[:ui].output("Configured enable virtualization extensions is #{enable_virtualization_extensions}") if enable_virtualization_extensions
- env[:ui].output("Configured vmname is #{vmname}") if vmname
- env[:ui].output("Configured differencing disk instead of cloning") if differencing_disk
- env[:ui].output("Configured automatic start action is #{auto_start_action}") if auto_start_action
- env[:ui].output("Configured automatic stop action is #{auto_stop_action}") if auto_stop_action
if !vm_dir.directory? || !hd_dir.directory?
+ @logger.error("Required virtual machine directory not found!")
raise Errors::BoxInvalid
end
+ valid_config_ext = [".xml"]
+ if env[:machine].provider.driver.has_vmcx_support?
+ valid_config_ext << ".vmcx"
+ end
+
config_path = nil
- config_type = nil
- vm_dir.each_child do |f|
- if f.extname.downcase == '.xml'
- @logger.debug("Found XML config...")
- config_path = f
- config_type = 'xml'
+ vm_dir.each_child do |file|
+ if valid_config_ext.include?(file.extname.downcase)
+ config_path = file
break
end
end
- vmcx_support = env[:machine].provider.driver.execute("has_vmcx_support.ps1", {})['result']
- if vmcx_support
- vm_dir.each_child do |f|
- if f.extname.downcase == '.vmcx'
- @logger.debug("Found VMCX config and support...")
- config_path = f
- config_type = 'vmcx'
- break
- end
- end
+ if !config_path
+ @logger.error("Failed to locate box configuration path")
+ raise Errors::BoxInvalid
+ else
+ @logger.info("Found box configuration path: #{config_path}")
end
image_path = nil
- image_ext = nil
- image_filename = nil
- hd_dir.each_child do |f|
- if %w{.vhd .vhdx}.include?(f.extname.downcase)
- image_path = f
- image_ext = f.extname.downcase
- image_filename = File.basename(f, image_ext)
+ hd_dir.each_child do |file|
+ if VALID_HD_EXTENSIONS.include?(file.extname.downcase)
+ image_path = file
break
end
end
- if !config_path || !image_path
+ if !image_path
+ @logger.error("Failed to locate box image path")
raise Errors::BoxInvalid
+ else
+ @logger.info("Found box image path: #{image_path}")
end
env[:ui].output("Importing a Hyper-V instance")
+ dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join(image_path.basename).to_s
- switches = env[:machine].provider.driver.execute("get_switches.ps1", {})
- raise Errors::NoSwitches if switches.empty?
-
- switch = nil
- env[:machine].config.vm.networks.each do |type, opts|
- next if type != :public_network && type != :private_network
-
- switchToFind = opts[:bridge]
-
- if switchToFind
- @logger.debug("Looking for switch with name: #{switchToFind}")
- switch = switches.find { |s| s["Name"].downcase == switchToFind.downcase }["Id"]
- @logger.debug("Found switch: #{switch}")
- end
- end
-
- if switch.nil?
- if switches.length > 1
- env[:ui].detail(I18n.t("vagrant_hyperv.choose_switch") + "\n ")
- switches.each_index do |i|
- switch = switches[i]
- env[:ui].detail("#{i+1}) #{switch["Name"]}")
- end
- env[:ui].detail(" ")
-
- switch = nil
- while !switch
- switch = env[:ui].ask("What switch would you like to use? ")
- next if !switch
- switch = switch.to_i - 1
- switch = nil if switch < 0 || switch >= switches.length
- end
- switch = switches[switch]["Id"]
- else
- switch = switches[0]["Id"]
- end
- end
-
- env[:ui].detail("Cloning virtual hard drive...")
- source_path = image_path.to_s
- dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s
-
- # Still hard copy the disk of old XML configurations
- if config_type == 'xml'
- if differencing_disk
- env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path})
- else
- FileUtils.mkdir_p(env[:machine].data_dir.join("Virtual Hard Disks"))
- FileUtils.cp(source_path, dest_path)
- end
- end
- image_path = dest_path
-
- # We have to normalize the paths to be Windows paths since
- # we're executing PowerShell.
options = {
- vm_config_file: config_path.to_s.gsub("/", "\\"),
- vm_config_type: config_type,
- source_path: source_path.to_s,
- dest_path: dest_path,
- data_path: env[:machine].data_dir.to_s.gsub("/", "\\")
+ "VMConfigFile" => config_path.to_s.gsub("/", "\\"),
+ "DestinationPath" => dest_path.to_s.gsub("/", "\\"),
+ "DataPath" => env[:machine].data_dir.to_s.gsub("/", "\\"),
+ "LinkedClone" => !!env[:machine].provider_config.linked_clone,
+ "SourcePath" => image_path.to_s.gsub("/", "\\"),
+ "VMName" => env[:machine].provider_config.vmname,
}
- options[:switchid] = switch if switch
- options[:memory] = memory if memory
- options[:maxmemory] = maxmemory if maxmemory
- options[:cpus] = cpus if cpus
- options[:vmname] = vmname if vmname
- options[:auto_start_action] = auto_start_action if auto_start_action
- options[:auto_stop_action] = auto_stop_action if auto_stop_action
- options[:differencing_disk] = differencing_disk if differencing_disk
- options[:enable_virtualization_extensions] = "True" if enable_virtualization_extensions and enable_virtualization_extensions == true
+
env[:ui].detail("Creating and registering the VM...")
server = env[:machine].provider.driver.import(options)
- env[:ui].detail("Setting VM Integration Services")
- vm_integration_services.each do |key, value|
- state = false
- if value === true
- state = "enabled"
- elsif value === false
- state = "disabled"
- end
- env[:ui].output("#{key} is #{state}") if state
- end
-
- vm_integration_services[:VmId] = server["id"]
- env[:machine].provider.driver.set_vm_integration_services(vm_integration_services)
-
- env[:ui].detail("Successfully imported a VM with name: #{server['name']}")
+ env[:ui].detail("Successfully imported VM")
env[:machine].id = server["id"]
@app.call(env)
end
diff --git a/plugins/providers/hyperv/action/set_name.rb b/plugins/providers/hyperv/action/set_name.rb
new file mode 100644
index 000000000..91a82695a
--- /dev/null
+++ b/plugins/providers/hyperv/action/set_name.rb
@@ -0,0 +1,43 @@
+require "log4r"
+
+module VagrantPlugins
+ module HyperV
+ module Action
+ class SetName
+ def initialize(app, env)
+ @app = app
+ @logger = Log4r::Logger.new("vagrant::hyperv::set_name")
+ end
+
+ def call(env)
+ name = env[:machine].provider_config.vmname
+
+ # If we already set the name before, then don't do anything
+ sentinel = env[:machine].data_dir.join("action_set_name")
+ if !name && sentinel.file?
+ @logger.info("Default name was already set before, not doing it again.")
+ return @app.call(env)
+ end
+
+ # If no name was manually set, then use a default
+ if !name
+ prefix = "#{env[:root_path].basename.to_s}_#{env[:machine].name}"
+ prefix.gsub!(/[^-a-z0-9_]/i, "")
+
+ # milliseconds + random number suffix to allow for simultaneous
+ # `vagrant up` of the same box in different dirs
+ name = prefix + "_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
+ end
+
+ env[:machine].provider.driver.set_name(name)
+
+ # Create the sentinel
+ sentinel.open("w") do |f|
+ f.write(Time.now.to_i.to_s)
+ end
+ @app.call(env)
+ end
+ end
+ end
+ end
+end
diff --git a/plugins/providers/hyperv/config.rb b/plugins/providers/hyperv/config.rb
index 9a4296701..684cf36f1 100644
--- a/plugins/providers/hyperv/config.rb
+++ b/plugins/providers/hyperv/config.rb
@@ -3,18 +3,49 @@ require "vagrant"
module VagrantPlugins
module HyperV
class Config < Vagrant.plugin("2", :config)
- attr_accessor :ip_address_timeout # Time to wait for an IP address when booting, in seconds @return [Integer]
- attr_accessor :memory # Memory size in mb @return [Integer]
- attr_accessor :maxmemory # Maximal memory size in mb enables dynamical memory allocation @return [Integer]
- attr_accessor :cpus # Number of cpu's @return [Integer]
- attr_accessor :vmname # Name that will be shoen in Hyperv Manager @return [String]
- attr_accessor :vlan_id # VLAN ID for network interface for the virtual machine. @return [Integer]
- attr_accessor :mac # MAC address for network interface for the virtual machine. @return [String]
- attr_accessor :differencing_disk # Create differencing disk instead of cloning whole VHD [Boolean]
- attr_accessor :auto_start_action #action on automatic start of VM. Values: Nothing, StartIfRunning, Start
- attr_accessor :auto_stop_action #action on automatic stop of VM. Values: ShutDown, TurnOff, Save
- attr_accessor :enable_virtualization_extensions # Enable virtualization extensions (nested virtualization). Values: true, false
- attr_accessor :vm_integration_services # Options for VMServiceIntegration [Hash]
+ # Allowed automatic start actions for VM
+ ALLOWED_AUTO_START_ACTIONS = [
+ "Nothing".freeze,
+ "StartIfRunning".freeze,
+ "Start".freeze
+ ].freeze
+
+ # Allowed automatic stop actions for VM
+ ALLOWED_AUTO_STOP_ACTIONS = [
+ "ShutDown".freeze,
+ "TurnOff".freeze,
+ "Save".freeze
+ ].freeze
+
+ # @return [Integer] Seconds to wait for an IP address when booting
+ attr_accessor :ip_address_timeout
+ # @return [Integer] Memory size in MB
+ attr_accessor :memory
+ # @return [Integer] Maximum memory size in MB. Enables dynamic memory.
+ attr_accessor :maxmemory
+ # @return [Integer] Number of CPUs
+ attr_accessor :cpus
+ # @return [String] Name of the VM (Shown in the Hyper-V Manager)
+ attr_accessor :vmname
+ # @return [Integer] VLAN ID for network interface
+ attr_accessor :vlan_id
+ # @return [String] MAC address for network interface
+ attr_accessor :mac
+ # @return [Boolean] Create linked clone instead of full clone
+ # @note **DEPRECATED** use #linked_clone instead
+ attr_accessor :differencing_disk
+ # @return [Boolean] Create linked clone instead of full clone
+ attr_accessor :linked_clone
+ # @return [String] Automatic action on start of host. Default: Nothing (Nothing, StartIfRunning, Start)
+ attr_accessor :auto_start_action
+ # @return [String] Automatic action on stop of host. Default: ShutDown (ShutDown, TurnOff, Save)
+ attr_accessor :auto_stop_action
+ # @return [Boolean] Enable automatic checkpoints. Default: false
+ attr_accessor :enable_checkpoints
+ # @return [Boolean] Enable virtualization extensions
+ attr_accessor :enable_virtualization_extensions
+ # @return [Hash] Options for VMServiceIntegration
+ attr_accessor :vm_integration_services
def initialize
@ip_address_timeout = UNSET_VALUE
@@ -24,10 +55,12 @@ module VagrantPlugins
@vmname = UNSET_VALUE
@vlan_id = UNSET_VALUE
@mac = UNSET_VALUE
+ @linked_clone = UNSET_VALUE
@differencing_disk = UNSET_VALUE
@auto_start_action = UNSET_VALUE
@auto_stop_action = UNSET_VALUE
@enable_virtualization_extensions = UNSET_VALUE
+ @enable_checkpoints = UNSET_VALUE
@vm_integration_services = {
guest_service_interface: UNSET_VALUE,
heartbeat: UNSET_VALUE,
@@ -39,6 +72,10 @@ module VagrantPlugins
end
def finalize!
+ @linked_clone = false if @linked_clone == UNSET_VALUE
+ @differencing_disk = false if @differencing_disk == UNSET_VALUE
+ @linked_clone ||= @differencing_disk
+ @differencing_disk ||= @linked_clone
if @ip_address_timeout == UNSET_VALUE
@ip_address_timeout = 120
end
@@ -48,20 +85,32 @@ module VagrantPlugins
@vmname = nil if @vmname == UNSET_VALUE
@vlan_id = nil if @vlan_id == UNSET_VALUE
@mac = nil if @mac == UNSET_VALUE
- @differencing_disk = false if @differencing_disk == UNSET_VALUE
- @auto_start_action = nil if @auto_start_action == UNSET_VALUE
- @auto_stop_action = nil if @auto_stop_action == UNSET_VALUE
- @enable_virtualization_extensions = false if @enable_virtualization_extensions == UNSET_VALUE # TODO will this work?
- @vm_integration_services.each { |key, value|
- @vm_integration_services[key] = nil if value == UNSET_VALUE
- }
- @vm_integration_services = nil if @vm_integration_services.length == 0
+ @auto_start_action = "Nothing" if @auto_start_action == UNSET_VALUE
+ @auto_stop_action = "ShutDown" if @auto_stop_action == UNSET_VALUE
+ @enable_virtualization_extensions = false if @enable_virtualization_extensions == UNSET_VALUE
+ if @enable_checkpoints == UNSET_VALUE
+ @enable_checkpoints = false
+ else
+ @enable_checkpoints = !!@enable_checkpoints
+ end
+ @vm_integration_services.delete_if{|_, v| v == UNSET_VALUE }
+ @vm_integration_services = nil if @vm_integration_services.empty?
end
def validate(machine)
errors = _detected_errors
+ if !ALLOWED_AUTO_START_ACTIONS.include?(auto_start_action)
+ errors << I18n.t("vagrant.hyperv.config.invalid_auto_start_action", action: auto_start_action,
+ allowed_actions: ALLOWED_AUTO_START_ACTIONS.join(", "))
+ end
+
+ if !ALLOWED_AUTO_STOP_ACTIONS.include?(auto_stop_action)
+ errors << I18n.t("vagrant.hyperv.config.invalid_auto_stop_action", action: auto_stop_action,
+ allowed_actions: ALLOWED_AUTO_STOP_ACTIONS.join(", "))
+ end
+
{"Hyper-V" => errors}
end
end
diff --git a/plugins/providers/hyperv/driver.rb b/plugins/providers/hyperv/driver.rb
index 359e14b12..83c43f3ce 100644
--- a/plugins/providers/hyperv/driver.rb
+++ b/plugins/providers/hyperv/driver.rb
@@ -10,19 +10,41 @@ module VagrantPlugins
ERROR_REGEXP = /===Begin-Error===(.+?)===End-Error===/m
OUTPUT_REGEXP = /===Begin-Output===(.+?)===End-Output===/m
+ # Name mapping for integration services for nicer keys
+ INTEGRATION_SERVICES_MAP = {
+ guest_service_interface: "Guest Service Interface",
+ heartbeat: "Heartbeat",
+ key_value_pair_exchange: "Key-Value Pair Exchange",
+ shutdown: "Shutdown",
+ time_synchronization: "Time Synchronization",
+ vss: "VSS",
+ }
+
+ # @return [String] VM ID
attr_reader :vm_id
def initialize(id)
@vm_id = id
end
- def execute(path, options)
- r = execute_powershell(path, options)
- if r.exit_code != 0
- raise Errors::PowerShellError,
- script: path,
- stderr: r.stderr
+ # @return [Boolean] Supports VMCX
+ def has_vmcx_support?
+ !!execute(:has_vmcx_support)["result"]
+ end
+
+ # Execute a PowerShell command and process the results
+ #
+ # @param [String] path Path to PowerShell script
+ # @param [Hash] options Options to pass to command
+ #
+ # @return [Object, nil] If the command returned JSON content
+ # it will be parsed and returned, otherwise
+ # nil will be returned
+ def execute(path, options={})
+ if path.is_a?(Symbol)
+ path = "#{path}.ps1"
end
+ r = execute_powershell(path, options)
# We only want unix-style line endings within Vagrant
r.stdout.gsub!("\r\n", "\n")
@@ -40,104 +62,185 @@ module VagrantPlugins
stderr: data["error"]
end
+ if r.exit_code != 0
+ raise Errors::PowerShellError,
+ script: path,
+ stderr: r.stderr
+ end
+
# Nothing
return nil if !output_match
return JSON.parse(output_match[1])
end
+ # Fetch current state of the VM
+ #
+ # @return [Hash]
def get_current_state
- execute('get_vm_status.ps1', { VmId: vm_id })
+ execute(:get_vm_status, { VmId: vm_id })
end
- def delete_vm
- execute('delete_vm.ps1', { VmId: vm_id })
- end
+ # Delete the VM
+ #
+ # @return [nil]
+ def delete_vm
+ execute(:delete_vm, { VmId: vm_id })
+ end
+ # Export the VM to the given path
+ #
+ # @param [String] path Path for export
+ # @return [nil]
def export(path)
- execute('export_vm.ps1', {VmId: vm_id, Path: path})
+ execute(:export_vm, {VmId: vm_id, Path: path})
end
- def read_guest_ip
- execute('get_network_config.ps1', { VmId: vm_id })
- end
+ # Get the IP address of the VM
+ #
+ # @return [Hash]
+ def read_guest_ip
+ execute(:get_network_config, { VmId: vm_id })
+ end
+ # Get the MAC address of the VM
+ #
+ # @return [Hash]
def read_mac_address
- execute('get_network_mac.ps1', { VmId: vm_id })
+ execute(:get_network_mac, { VmId: vm_id })
end
- def resume
- execute('resume_vm.ps1', { VmId: vm_id })
- end
+ # Resume the VM from suspension
+ #
+ # @return [nil]
+ def resume
+ execute(:resume_vm, { VmId: vm_id })
+ end
- def start
- execute('start_vm.ps1', { VmId: vm_id })
- end
+ # Start the VM
+ #
+ # @return [nil]
+ def start
+ execute(:start_vm, { VmId: vm_id })
+ end
- def stop
- execute('stop_vm.ps1', { VmId: vm_id })
- end
+ # Stop the VM
+ #
+ # @return [nil]
+ def stop
+ execute(:stop_vm, { VmId: vm_id })
+ end
- def suspend
- execute("suspend_vm.ps1", { VmId: vm_id })
- end
+ # Suspend the VM
+ #
+ # @return [nil]
+ def suspend
+ execute(:suspend_vm, { VmId: vm_id })
+ end
- def import(options)
- config_type = options.delete(:vm_config_type)
- if config_type === "vmcx"
- execute('import_vm_vmcx.ps1', options)
- else
- options.delete(:data_path)
- options.delete(:source_path)
- options.delete(:differencing_disk)
- execute('import_vm_xml.ps1', options)
- end
- end
+ # Import a new VM
+ #
+ # @param [Hash] options Configuration options
+ # @return [Hash] New VM ID
+ def import(options)
+ execute(:import_vm, options)
+ end
- def net_set_vlan(vlan_id)
- execute("set_network_vlan.ps1", { VmId: vm_id, VlanId: vlan_id })
- end
+ # Set the VLAN ID
+ #
+ # @param [String] vlan_id VLAN ID
+ # @return [nil]
+ def net_set_vlan(vlan_id)
+ execute(:set_network_vlan, { VmId: vm_id, VlanId: vlan_id })
+ end
- def net_set_mac(mac_addr)
- execute("set_network_mac.ps1", { VmId: vm_id, Mac: mac_addr })
- end
+ # Set the VM adapter MAC address
+ #
+ # @param [String] mac_addr MAC address
+ # @return [nil]
+ def net_set_mac(mac_addr)
+ execute(:set_network_mac, { VmId: vm_id, Mac: mac_addr })
+ end
- def create_snapshot(snapshot_name)
- execute("create_snapshot.ps1", { VmId: vm_id, SnapName: (snapshot_name) } )
- end
+ # Create a new snapshot with the given name
+ #
+ # @param [String] snapshot_name Name of the new snapshot
+ # @return [nil]
+ def create_snapshot(snapshot_name)
+ execute(:create_snapshot, { VmId: vm_id, SnapName: (snapshot_name) } )
+ end
- def restore_snapshot(snapshot_name)
- execute("restore_snapshot.ps1", { VmId: vm_id, SnapName: (snapshot_name) } )
- end
+ # Restore the given snapshot
+ #
+ # @param [String] snapshot_name Name of snapshot to restore
+ # @return [nil]
+ def restore_snapshot(snapshot_name)
+ execute(:restore_snapshot, { VmId: vm_id, SnapName: (snapshot_name) } )
+ end
- def list_snapshots()
- snaps = execute("list_snapshots.ps1", { VmID: vm_id } )
- snaps.map { |s| s['Name'] }
- end
+ # Get list of current snapshots
+ #
+ # @return [Array] snapshot names
+ def list_snapshots
+ snaps = execute(:list_snapshots, { VmID: vm_id } )
+ snaps.map { |s| s['Name'] }
+ end
- def delete_snapshot(snapshot_name)
- execute("delete_snapshot.ps1", {VmID: vm_id, SnapName: snapshot_name})
- end
+ # Delete snapshot with the given name
+ #
+ # @param [String] snapshot_name Name of snapshot to delete
+ # @return [nil]
+ def delete_snapshot(snapshot_name)
+ execute(:delete_snapshot, {VmID: vm_id, SnapName: snapshot_name})
+ end
+ # Enable or disable VM integration services
+ #
+ # @param [Hash] config Integration services to enable or disable
+ # @return [nil]
+ # @note Keys in the config hash will be remapped if found in the
+ # INTEGRATION_SERVICES_MAP. If they are not, the name will
+ # be passed directly. This allows new integration services
+ # to configurable even if Vagrant is not aware of them.
def set_vm_integration_services(config)
- execute("set_vm_integration_services.ps1", config)
+ config.each_pair do |srv_name, srv_enable|
+ args = {VMID: vm_id, Name: INTEGRATION_SERVICES_MAP.fetch(srv_name.to_sym, srv_name)}
+ args[:Name] = true if srv_enable
+ execute(:set_vm_integration_services, args)
+ end
+ end
+
+ # Set the name of the VM
+ #
+ # @param [String] vmname Name of the VM
+ # @return [nil]
+ def set_name(vmname)
+ execute(:set_name, VMID: vm_id, VMName: vmname)
end
protected
def execute_powershell(path, options, &block)
lib_path = Pathname.new(File.expand_path("../scripts", __FILE__))
+ mod_path = lib_path.join("utils").to_s.gsub("/", "\\")
path = lib_path.join(path).to_s.gsub("/", "\\")
options = options || {}
ps_options = []
options.each do |key, value|
+ next if value == false
ps_options << "-#{key}"
+ # If the value is a TrueClass assume switch
+ next if value == true
ps_options << "'#{value}'"
end
# Always have a stop error action for failures
ps_options << "-ErrorAction" << "Stop"
- opts = { notify: [:stdout, :stderr, :stdin] }
+ # Include our module path so we can nicely load helper modules
+ opts = {
+ notify: [:stdout, :stderr, :stdin],
+ env: {"PSModulePath" => "$env:PSModulePath+';#{mod_path}'"}
+ }
Vagrant::Util::PowerShell.execute(path, *ps_options, **opts, &block)
end
end
diff --git a/plugins/providers/hyperv/scripts/check_hyperv.ps1 b/plugins/providers/hyperv/scripts/check_hyperv.ps1
index d9992b1e9..1d1775314 100644
--- a/plugins/providers/hyperv/scripts/check_hyperv.ps1
+++ b/plugins/providers/hyperv/scripts/check_hyperv.ps1
@@ -1,8 +1,6 @@
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
+#Requires -Modules VagrantMessages
-$check = $(-Not (-Not (Get-Command "Hyper-V\Get-VMSwitch" -errorAction SilentlyContinue)))
+$check = $(-Not (-Not (Get-Command "Hyper-V\Get-VMSwitch" -ErrorAction SilentlyContinue)))
$result = @{
result = $check
}
diff --git a/plugins/providers/hyperv/scripts/clone_vhd.ps1 b/plugins/providers/hyperv/scripts/clone_vhd.ps1
index fc83a2c06..829366635 100644
--- a/plugins/providers/hyperv/scripts/clone_vhd.ps1
+++ b/plugins/providers/hyperv/scripts/clone_vhd.ps1
@@ -1,4 +1,6 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$Source,
@@ -6,4 +8,11 @@ Param(
[string]$Destination
)
-Hyper-V\New-VHD -Path $Destination -ParentPath $Source -ErrorAction Stop
+$ErrorActionPreference = "Stop"
+
+try {
+ Hyper-V\New-VHD -Path $Destination -ParentPath $Source
+} catch {
+ Write-Error-Message "Failed to clone drive: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/configure_vm.ps1 b/plugins/providers/hyperv/scripts/configure_vm.ps1
new file mode 100644
index 000000000..908bb1f24
--- /dev/null
+++ b/plugins/providers/hyperv/scripts/configure_vm.ps1
@@ -0,0 +1,95 @@
+#Requires -Modules VagrantVM, VagrantMessages
+
+param(
+ [parameter (Mandatory=$true)]
+ [Guid] $VMID,
+ [parameter (Mandatory=$false)]
+ [string] $SwitchID=$null,
+ [parameter (Mandatory=$false)]
+ [string] $Memory=$null,
+ [parameter (Mandatory=$false)]
+ [string] $MaxMemory=$null,
+ [parameter (Mandatory=$false)]
+ [string] $Processors=$null,
+ [parameter (Mandatory=$false)]
+ [string] $AutoStartAction=$null,
+ [parameter (Mandatory=$false)]
+ [string] $AutoStopAction=$null,
+ [parameter (Mandatory=$false)]
+ [switch] $VirtualizationExtensions,
+ [parameter (Mandatory=$false)]
+ [switch] $EnableCheckpoints
+)
+
+$ErrorActionPreference = "Stop"
+
+try {
+ $VM = Hyper-V\Get-VM -Id $VMID
+} catch {
+ Write-Error-Message "Failed to locate VM: ${PSItem}"
+ exit 1
+}
+
+if($Processors) {
+ try {
+ Set-VagrantVMCPUS -VM $VM -CPUCount ($Processors -as [int])
+ } catch {
+ Write-Error-Message "Failed to configure CPUs: ${PSItem}"
+ exit 1
+ }
+}
+
+if($Memory -or $MaxMemory) {
+ try {
+ Set-VagrantVMMemory -VM $VM -Memory $Memory -MaxMemory $MaxMemory
+ } catch {
+ Write-Error-Message "Failed to configure memory: ${PSItem}"
+ exit 1
+ }
+}
+
+if($AutoStartAction -or $AutoStopAction) {
+ try {
+ Set-VagrantVMAutoActions -VM $VM -AutoStartAction $AutoStartAction -AutoStopAction $AutoStopAction
+ } catch {
+ Write-Error-Message "Failed to configure automatic actions: ${PSItem}"
+ exit 1
+ }
+}
+
+if($VirtualizationExtensions) {
+ $virtex = $true
+} else {
+ $virtex = $false
+}
+
+try {
+ Set-VagrantVMVirtExtensions -VM $VM -Enabled $virtex
+} catch {
+ Write-Error-Message "Failed to configure virtualization extensions: ${PSItem}"
+ exit 1
+}
+
+if($SwitchID) {
+ try {
+ $SwitchName = Get-VagrantVMSwitch -NameOrID $SwitchID
+ Set-VagrantVMSwitch -VM $VM -SwitchName $SwitchName
+ } catch {
+ Write-Error-Message "Failed to configure network adapter: ${PSItem}"
+ }
+}
+
+if($EnableCheckpoints) {
+ $checkpoints = "Standard"
+ $CheckpointAction = "enable"
+} else {
+ $checkpoints = "Disabled"
+ $CheckpointAction = "disable"
+}
+
+try {
+ Hyper-V\Set-VM -VM $VM -CheckpointType $checkpoints
+} catch {
+ Write-Error-Message "Failed to ${CheckpointAction} checkpoints on VM: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/create_snapshot.ps1 b/plugins/providers/hyperv/scripts/create_snapshot.ps1
index 080c47d7a..775f91898 100644
--- a/plugins/providers/hyperv/scripts/create_snapshot.ps1
+++ b/plugins/providers/hyperv/scripts/create_snapshot.ps1
@@ -1,8 +1,17 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId,
[string]$SnapName
)
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-Hyper-V\Checkpoint-VM $VM -SnapshotName $SnapName
+$ErrorActionPreference = "Stop"
+
+try {
+ $VM = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Checkpoint-VM $VM -SnapshotName $SnapName
+} catch {
+ Write-Error-Message "Failed to create snapshot: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/delete_snapshot.ps1 b/plugins/providers/hyperv/scripts/delete_snapshot.ps1
index 54c2c33b7..bba44b588 100644
--- a/plugins/providers/hyperv/scripts/delete_snapshot.ps1
+++ b/plugins/providers/hyperv/scripts/delete_snapshot.ps1
@@ -1,8 +1,16 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId,
[string]$SnapName
)
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-Hyper-V\Remove-VMSnapshot $VM -Name $SnapName
+$ErrorActionPreference = "Stop"
+
+try {
+ $VM = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Remove-VMSnapshot $VM -Name $SnapName
+} catch {
+ Write-Error-Message "Failed to delete snapshot: ${PSItem}"
+}
diff --git a/plugins/providers/hyperv/scripts/delete_vm.ps1 b/plugins/providers/hyperv/scripts/delete_vm.ps1
index ce83cb6a7..53e160b16 100644
--- a/plugins/providers/hyperv/scripts/delete_vm.ps1
+++ b/plugins/providers/hyperv/scripts/delete_vm.ps1
@@ -1,7 +1,16 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-Hyper-V\Remove-VM $VM -Force
+$ErrorActionPreference = "Stop"
+
+try {
+ $VM = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Remove-VM $VM -Force
+} catch {
+ Write-Error-Message "Failed to delete VM: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/export_vm.ps1 b/plugins/providers/hyperv/scripts/export_vm.ps1
index 207275981..263c12be2 100644
--- a/plugins/providers/hyperv/scripts/export_vm.ps1
+++ b/plugins/providers/hyperv/scripts/export_vm.ps1
@@ -1,15 +1,29 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId,
[Parameter(Mandatory=$true)]
[string]$Path
)
-$vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-$vm | Hyper-V\Export-VM -Path $Path
+$ErrorActionPreference = "Stop"
+
+try {
+ $vm = Hyper-V\Get-VM -Id $VmId
+ $vm | Hyper-V\Export-VM -Path $Path
+} catch {
+ Write-Error-Message "Failed to export VM: ${PSItem}"
+ exit 1
+}
# Prepare directory structure for box import
-$name = $vm.Name
-Move-Item $Path/$name/* $Path
-Remove-Item -Path $Path/Snapshots -Force -Recurse
-Remove-Item -Path $Path/$name -Force
\ No newline at end of file
+try {
+ $name = $vm.Name
+ Move-Item $Path/$name/* $Path
+ Remove-Item -Path $Path/Snapshots -Force -Recurse
+ Remove-Item -Path $Path/$name -Force
+} catch {
+ Write-Error-Message "Failed to format exported box: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/file_sync.ps1 b/plugins/providers/hyperv/scripts/file_sync.ps1
index bca5e41d6..ffe340407 100644
--- a/plugins/providers/hyperv/scripts/file_sync.ps1
+++ b/plugins/providers/hyperv/scripts/file_sync.ps1
@@ -1,22 +1,23 @@
+#Requires -Modules VagrantMessages
#-------------------------------------------------------------------------
# Copyright (c) Microsoft Open Technologies, Inc.
# All Rights Reserved. Licensed under the MIT License.
#--------------------------------------------------------------------------
param (
- [string]$vm_id = $(throw "-vm_id is required."),
- [string]$guest_ip = $(throw "-guest_ip is required."),
- [string]$username = $(throw "-guest_username is required."),
- [string]$password = $(throw "-guest_password is required."),
- [string]$host_path = $(throw "-host_path is required."),
- [string]$guest_path = $(throw "-guest_path is required.")
- )
-
-# Include the following modules
-$presentDir = Split-Path -parent $PSCommandPath
-$modules = @()
-$modules += $presentDir + "\utils\write_messages.ps1"
-forEach ($module in $modules) { . $module }
+ [parameter (Mandatory=$true)]
+ [string]$vm_id,
+ [parameter (Mandatory=$true)]
+ [string]$guest_ip,
+ [parameter (Mandatory=$true)]
+ [string]$username,
+ [parameter (Mandatory=$true)]
+ [string]$password,
+ [parameter (Mandatory=$true)]
+ [string]$host_path,
+ [parameter (Mandatory=$true)]
+ [string]$guest_path
+)
function Get-file-hash($source_path, $delimiter) {
$source_files = @()
@@ -120,4 +121,3 @@ $resultHash = @{
}
$result = ConvertTo-Json $resultHash
Write-Output-Message $result
-
diff --git a/plugins/providers/hyperv/scripts/get_network_config.ps1 b/plugins/providers/hyperv/scripts/get_network_config.ps1
index 5f9186a2c..959501add 100644
--- a/plugins/providers/hyperv/scripts/get_network_config.ps1
+++ b/plugins/providers/hyperv/scripts/get_network_config.ps1
@@ -1,56 +1,68 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
+$ErrorActionPreference = "Stop"
-$vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-$networks = Hyper-V\Get-VMNetworkAdapter -VM $vm
-foreach ($network in $networks) {
- if ($network.IpAddresses.Length -gt 0) {
- foreach ($ip_address in $network.IpAddresses) {
- if ($ip_address.Contains(".") -And [string]::IsNullOrEmpty($ip4_address)) {
- $ip4_address = $ip_address
- } elseif ($ip_address.Contains(":") -And [string]::IsNullOrEmpty($ip6_address)) {
- $ip6_address = $ip_address
+try {
+ $vm = Hyper-V\Get-VM -Id $VmId
+} catch {
+ Write-Error-Message "Failed to locate VM: ${PSItem}"
+ exit 1
+}
+
+try {
+ $networks = Hyper-V\Get-VMNetworkAdapter -VM $vm
+ foreach ($network in $networks) {
+ if ($network.IpAddresses.Length -gt 0) {
+ foreach ($ip_address in $network.IpAddresses) {
+ if ($ip_address.Contains(".") -And [string]::IsNullOrEmpty($ip4_address)) {
+ $ip4_address = $ip_address
+ } elseif ($ip_address.Contains(":") -And [string]::IsNullOrEmpty($ip6_address)) {
+ $ip6_address = $ip_address
+ }
}
}
}
-}
-# If no address was found in the network settings, check for
-# neighbor with mac address and see if an IP exists
-if (([string]::IsNullOrEmpty($ip4_address)) -And ([string]::IsNullOrEmpty($ip6_address))) {
- $macaddresses = $vm | select -ExpandProperty NetworkAdapters | select MacAddress
- foreach ($macaddr in $macaddresses) {
- $macaddress = $macaddr.MacAddress -replace '(.{2})(?!$)', '${1}-'
- $addr = Get-NetNeighbor -LinkLayerAddress $macaddress -ErrorAction SilentlyContinue | select IPAddress
- if ($ip_address) {
- $ip_address = $addr.IPAddress
- if ($ip_address.Contains(".")) {
- $ip4_address = $ip_address
- } elseif ($ip_address.Contains(":")) {
- $ip6_address = $ip_address
+ # If no address was found in the network settings, check for
+ # neighbor with mac address and see if an IP exists
+ if (([string]::IsNullOrEmpty($ip4_address)) -And ([string]::IsNullOrEmpty($ip6_address))) {
+ $macaddresses = $vm | select -ExpandProperty NetworkAdapters | select MacAddress
+ foreach ($macaddr in $macaddresses) {
+ $macaddress = $macaddr.MacAddress -replace '(.{2})(?!$)', '${1}-'
+ $addr = Get-NetNeighbor -LinkLayerAddress $macaddress -ErrorAction SilentlyContinue | select IPAddress
+ if ($ip_address) {
+ $ip_address = $addr.IPAddress
+ if ($ip_address.Contains(".")) {
+ $ip4_address = $ip_address
+ } elseif ($ip_address.Contains(":")) {
+ $ip6_address = $ip_address
+ }
}
}
}
-}
-if (-Not ([string]::IsNullOrEmpty($ip4_address))) {
- $guest_ipaddress = $ip4_address
-} elseif (-Not ([string]::IsNullOrEmpty($ip6_address))) {
- $guest_ipaddress = $ip6_address
-}
-
-if (-Not ([string]::IsNullOrEmpty($guest_ipaddress))) {
- $resultHash = @{
- ip = $guest_ipaddress
+ if (-Not ([string]::IsNullOrEmpty($ip4_address))) {
+ $guest_ipaddress = $ip4_address
+ } elseif (-Not ([string]::IsNullOrEmpty($ip6_address))) {
+ $guest_ipaddress = $ip6_address
}
- $result = ConvertTo-Json $resultHash
- Write-Output-Message $result
-} else {
- Write-Error-Message "Failed to determine IP address"
+
+ if (-Not ([string]::IsNullOrEmpty($guest_ipaddress))) {
+ $resultHash = @{
+ ip = $guest_ipaddress
+ }
+ $result = ConvertTo-Json $resultHash
+ Write-Output-Message $result
+ } else {
+ Write-Error-Message "Failed to determine IP address"
+ exit 1
+ }
+} catch {
+ Write-Error-Message "Unexpected error while detecting network configuration: ${PSItem}"
+ exit 1
}
diff --git a/plugins/providers/hyperv/scripts/get_network_mac.ps1 b/plugins/providers/hyperv/scripts/get_network_mac.ps1
index 9424917df..88d4c0306 100644
--- a/plugins/providers/hyperv/scripts/get_network_mac.ps1
+++ b/plugins/providers/hyperv/scripts/get_network_mac.ps1
@@ -1,28 +1,32 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId
- )
+)
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
+$ErrorActionPreference = "Stop"
-$ip_address = ""
-$vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-$networks = Hyper-V\Get-VMNetworkAdapter -VM $vm
-foreach ($network in $networks) {
- if ($network.MacAddress -gt 0) {
- $mac_address = $network.MacAddress
- if (-Not ([string]::IsNullOrEmpty($mac_address))) {
- # We found our mac address!
- break
+try {
+ $ip_address = ""
+ $vm = Hyper-V\Get-VM -Id $VmId
+ $networks = Hyper-V\Get-VMNetworkAdapter -VM $vm
+ foreach ($network in $networks) {
+ if ($network.MacAddress -gt 0) {
+ $mac_address = $network.MacAddress
+ if (-Not ([string]::IsNullOrEmpty($mac_address))) {
+ # We found our mac address!
+ break
+ }
+ }
}
- }
-}
-
-$resultHash = @{
- mac = "$mac_address"
+ $resultHash = @{
+ mac = "$mac_address"
+ }
+ $result = ConvertTo-Json $resultHash
+ Write-Output-Message $result
+} catch {
+ Write-Error-Message "Unexpected error while fetching MAC: ${PSItem}"
+ exit 1
}
-$result = ConvertTo-Json $resultHash
-Write-Output-Message $result
\ No newline at end of file
diff --git a/plugins/providers/hyperv/scripts/get_switches.ps1 b/plugins/providers/hyperv/scripts/get_switches.ps1
index 85c0f12fe..83632aaa8 100644
--- a/plugins/providers/hyperv/scripts/get_switches.ps1
+++ b/plugins/providers/hyperv/scripts/get_switches.ps1
@@ -1,11 +1,9 @@
+#Requires -Modules VagrantMessages
# This will have a SwitchType property. As far as I know the values are:
#
# 0 - Private
# 1 - Internal
#
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
$Switches = @(Hyper-V\Get-VMSwitch `
| Select-Object Name,SwitchType,NetAdapterInterfaceDescription,Id)
diff --git a/plugins/providers/hyperv/scripts/get_vm_status.ps1 b/plugins/providers/hyperv/scripts/get_vm_status.ps1
index 43c8595c9..90c05cbcc 100644
--- a/plugins/providers/hyperv/scripts/get_vm_status.ps1
+++ b/plugins/providers/hyperv/scripts/get_vm_status.ps1
@@ -1,12 +1,10 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
-
# Make sure the exception type is loaded
try
{
@@ -37,7 +35,7 @@ try {
$State = "not_created"
$Status = $State
}
- else
+ else
{
throw;
}
diff --git a/plugins/providers/hyperv/scripts/has_vmcx_support.ps1 b/plugins/providers/hyperv/scripts/has_vmcx_support.ps1
index 79dac42d7..bf7d89cda 100644
--- a/plugins/providers/hyperv/scripts/has_vmcx_support.ps1
+++ b/plugins/providers/hyperv/scripts/has_vmcx_support.ps1
@@ -1,6 +1,4 @@
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
+#Requires -Modules VagrantMessages
# Windows version 10 and up have support for binary format
$check = [System.Environment]::OSVersion.Version.Major -ge 10
diff --git a/plugins/providers/hyperv/scripts/import_vm.ps1 b/plugins/providers/hyperv/scripts/import_vm.ps1
new file mode 100644
index 000000000..06fc65622
--- /dev/null
+++ b/plugins/providers/hyperv/scripts/import_vm.ps1
@@ -0,0 +1,37 @@
+#Requires -Modules VagrantVM, VagrantMessages
+
+param(
+ [parameter (Mandatory=$true)]
+ [string] $VMConfigFile,
+ [parameter (Mandatory=$true)]
+ [string] $DestinationPath,
+ [parameter (Mandatory=$true)]
+ [string] $DataPath,
+ [parameter (Mandatory=$true)]
+ [string] $SourcePath,
+ [parameter (Mandatory=$false)]
+ [switch] $LinkedClone,
+ [parameter (Mandatory=$false)]
+ [string] $VMName=$null
+)
+
+$ErrorActionPreference = "Stop"
+
+try {
+ if($LinkedClone) {
+ $linked = $true
+ } else {
+ $linked = $false
+ }
+
+ $VM = New-VagrantVM -VMConfigFile $VMConfigFile -DestinationPath $DestinationPath `
+ -DataPath $DataPath -SourcePath $SourcePath -LinkedClone $linked -VMName $VMName
+
+ $Result = @{
+ id = $VM.Id.Guid;
+ }
+ Write-Output-Message (ConvertTo-Json $Result)
+} catch {
+ Write-Error-Message "${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1
deleted file mode 100644
index 682cabfbd..000000000
--- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1
+++ /dev/null
@@ -1,165 +0,0 @@
-Param(
- [Parameter(Mandatory=$true)]
- [string]$vm_config_file,
- [Parameter(Mandatory=$true)]
- [string]$source_path,
- [Parameter(Mandatory=$true)]
- [string]$dest_path,
- [Parameter(Mandatory=$true)]
- [string]$data_path,
-
- [string]$switchid=$null,
- [string]$memory=$null,
- [string]$maxmemory=$null,
- [string]$cpus=$null,
- [string]$vmname=$null,
- [string]$auto_start_action=$null,
- [string]$auto_stop_action=$null,
- [string]$differencing_disk=$null,
- [string]$enable_virtualization_extensions=$False
-)
-
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
-
-$VmProperties = @{
- Path = $vm_config_file
- SnapshotFilePath = Join-Path $data_path 'Snapshots'
- VhdDestinationPath = Join-Path $data_path 'Virtual Hard Disks'
- VirtualMachinePath = $data_path
-}
-
-$vmConfig = (Hyper-V\Compare-VM -Copy -GenerateNewID @VmProperties)
-
-$generation = $vmConfig.VM.Generation
-
-if (!$vmname) {
- # Get the name of the vm
- $vm_name = $vmconfig.VM.VMName
-} else {
- $vm_name = $vmname
-}
-
-if (!$cpus) {
- # Get the processorcount of the VM
- $processors = (Hyper-V\Get-VMProcessor -VM $vmConfig.VM).Count
-}else {
- $processors = $cpus
-}
-
-function GetUniqueName($name) {
- Hyper-V\Get-VM | ForEach-Object -Process {
- if ($name -eq $_.Name) {
- $name = $name + "_1"
- }
- }
- return $name
-}
-
-do {
- $name = $vm_name
- $vm_name = GetUniqueName $name
-} while ($vm_name -ne $name)
-
-if (!$memory) {
- $configMemory = Hyper-V\Get-VMMemory -VM $vmConfig.VM
- $dynamicmemory = $configMemory.DynamicMemoryEnabled
-
- $MemoryMaximumBytes = ($configMemory.Maximum)
- $MemoryStartupBytes = ($configMemory.Startup)
- $MemoryMinimumBytes = ($configMemory.Minimum)
-} else {
- if (!$maxmemory){
- $dynamicmemory = $False
- $MemoryMaximumBytes = ($memory -as [int]) * 1MB
- $MemoryStartupBytes = ($memory -as [int]) * 1MB
- $MemoryMinimumBytes = ($memory -as [int]) * 1MB
- } else {
- $dynamicmemory = $True
- $MemoryMaximumBytes = ($maxmemory -as [int]) * 1MB
- $MemoryStartupBytes = ($memory -as [int]) * 1MB
- $MemoryMinimumBytes = ($memory -as [int]) * 1MB
- }
-}
-
-if (!$switchid) {
- $switchname = (Hyper-V\Get-VMNetworkAdapter -VM $vmConfig.VM).SwitchName
-} else {
- $switchname = $(Hyper-V\Get-VMSwitch -Id $switchid).Name
-}
-
-# Enable nested virtualization if configured
-if ($enable_virtualization_extensions -eq "True") {
- Hyper-V\Set-VMProcessor -VM $vmConfig.VM -ExposeVirtualizationExtensions $true
-}
-
-$vmNetworkAdapter = Hyper-V\Get-VMNetworkAdapter -VM $vmConfig.VM
-Hyper-V\Connect-VMNetworkAdapter -VMNetworkAdapter $vmNetworkAdapter -SwitchName $switchname
-Hyper-V\Set-VM -VM $vmConfig.VM -NewVMName $vm_name
-Hyper-V\Set-VM -VM $vmConfig.VM -ErrorAction "Stop"
-Hyper-V\Set-VM -VM $vmConfig.VM -ProcessorCount $processors
-
-if ($dynamicmemory) {
- Hyper-V\Set-VM -VM $vmConfig.VM -DynamicMemory
- Hyper-V\Set-VM -VM $vmConfig.VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes $MemoryMaximumBytes -MemoryStartupBytes $MemoryStartupBytes
-} else {
- Hyper-V\Set-VM -VM $vmConfig.VM -StaticMemory
- Hyper-V\Set-VM -VM $vmConfig.VM -MemoryStartupBytes $MemoryStartupBytes
-}
-
-if ($notes) {
- Hyper-V\Set-VM -VM $vmConfig.VM -Notes $notes
-}
-
-if ($auto_start_action) {
- Hyper-V\Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_start_action
-}
-
-if ($auto_stop_action) {
- Hyper-V\Set-VM -VM $vmConfig.VM -AutomaticStopAction $auto_stop_action
-}
-
-# Only set EFI secure boot for Gen 2 machines, not gen 1
-if ($generation -ne 1) {
- Hyper-V\Set-VMFirmware -VM $vmConfig.VM -EnableSecureBoot (Hyper-V\Get-VMFirmware -VM $vmConfig.VM).SecureBoot
-}
-
-$report = Hyper-V\Compare-VM -CompatibilityReport $vmConfig
-
-# Stop if there are incompatibilities
-if($report.Incompatibilities.Length -gt 0){
- Write-Error-Message $(ConvertTo-Json $($report.Incompatibilities | Select -ExpandProperty Message))
- exit 0
-}
-
-if($differencing_disk){
- # Get all controller on the VM, first scsi, then IDE if it is a Gen 1 device
- $controllers = Hyper-V\Get-VMScsiController -VM $vmConfig.VM
- if($generation -eq 1){
- $controllers = @($controllers) + @(Hyper-V\Get-VMIdeController -VM $vmConfig.VM)
- }
-
- foreach($controller in $controllers){
- foreach($drive in $controller.Drives){
- if([System.IO.Path]::GetFileName($drive.Path) -eq [System.IO.Path]::GetFileName($source_path)){
- # Remove the old disk and replace it with a differencing version
- $path = $drive.Path
- Hyper-V\Remove-VMHardDiskDrive $drive
- Hyper-V\New-VHD -Path $dest_path -ParentPath $source_path -ErrorAction Stop
- Hyper-V\Add-VMHardDiskDrive -VM $vmConfig.VM -Path $dest_path
- }
- }
- }
-}
-
-Hyper-V\Import-VM -CompatibilityReport $vmConfig
-
-$vm_id = (Hyper-V\Get-VM $vm_name).id.guid
-$resultHash = @{
- name = $vm_name
- id = $vm_id
-}
-
-$result = ConvertTo-Json $resultHash
-Write-Output-Message $result
diff --git a/plugins/providers/hyperv/scripts/import_vm_xml.ps1 b/plugins/providers/hyperv/scripts/import_vm_xml.ps1
deleted file mode 100644
index 655380c0e..000000000
--- a/plugins/providers/hyperv/scripts/import_vm_xml.ps1
+++ /dev/null
@@ -1,221 +0,0 @@
-Param(
- [Parameter(Mandatory=$true)]
- [string]$vm_config_file,
- [Parameter(Mandatory=$true)]
- [string]$dest_path,
-
- [string]$switchname=$null,
- [string]$memory=$null,
- [string]$maxmemory=$null,
- [string]$cpus=$null,
- [string]$vmname=$null,
- [string]$auto_start_action=$null,
- [string]$auto_stop_action=$null,
- [string]$enable_virtualization_extensions=$False
-)
-
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
-
-[xml]$vmconfig = Get-Content -Path $vm_config_file
-
-$generation = [int]($vmconfig.configuration.properties.subtype.'#text')+1
-
-if (!$vmname) {
- # Get the name of the vm
- $vm_name = $vmconfig.configuration.properties.name.'#text'
-}else {
- $vm_name = $vmname
-}
-
-if (!$cpus) {
- # Get the name of the vm
- $processors = $vmconfig.configuration.settings.processors.count.'#text'
-}else {
- $processors = $cpus
-}
-
-function GetUniqueName($name) {
- Hyper-V\Get-VM | ForEach-Object -Process {
- if ($name -eq $_.Name) {
- $name = $name + "_1"
- }
- }
- return $name
-}
-
-do {
- $name = $vm_name
- $vm_name = GetUniqueName $name
-} while ($vm_name -ne $name)
-
-if (!$memory) {
- $xmlmemory = (Select-Xml -xml $vmconfig -XPath "//memory").node.Bank
- if ($xmlmemory.dynamic_memory_enabled."#text" -eq "True") {
- $dynamicmemory = $True
- }
- else {
- $dynamicmemory = $False
- }
- # Memory values need to be in bytes
- $MemoryMaximumBytes = ($xmlmemory.limit."#text" -as [int]) * 1MB
- $MemoryStartupBytes = ($xmlmemory.size."#text" -as [int]) * 1MB
- $MemoryMinimumBytes = ($xmlmemory.reservation."#text" -as [int]) * 1MB
-}
-else {
- if (!$maxmemory){
- $dynamicmemory = $False
- $MemoryMaximumBytes = ($memory -as [int]) * 1MB
- $MemoryStartupBytes = ($memory -as [int]) * 1MB
- $MemoryMinimumBytes = ($memory -as [int]) * 1MB
- }
- else {
- $dynamicmemory = $True
- $MemoryMaximumBytes = ($maxmemory -as [int]) * 1MB
- $MemoryStartupBytes = ($memory -as [int]) * 1MB
- $MemoryMinimumBytes = ($memory -as [int]) * 1MB
- }
-}
-
-
-if (!$switchname) {
- # Get the name of the virtual switch
- $switchname = (Select-Xml -xml $vmconfig -XPath "//AltSwitchName").node."#text"
-}
-
-if ($generation -eq 1) {
- # Determine boot device
- Switch ((Select-Xml -xml $vmconfig -XPath "//boot").node.device0."#text") {
- "Floppy" { $bootdevice = "Floppy" }
- "HardDrive" { $bootdevice = "IDE" }
- "Optical" { $bootdevice = "CD" }
- "Network" { $bootdevice = "LegacyNetworkAdapter" }
- "Default" { $bootdevice = "IDE" }
- } #switch
-} else {
- # Determine boot device
- Switch ((Select-Xml -xml $vmconfig -XPath "//boot").node.device0."#text") {
- "HardDrive" { $bootdevice = "VHD" }
- "Optical" { $bootdevice = "CD" }
- "Network" { $bootdevice = "NetworkAdapter" }
- "Default" { $bootdevice = "VHD" }
- } #switch
-}
-
-# Determine secure boot options
-$secure_boot_enabled = (Select-Xml -xml $vmconfig -XPath "//secure_boot_enabled").Node."#text"
-
-# Define a hash map of parameter values for New-VM
-
-$vm_params = @{
- Name = $vm_name
- NoVHD = $True
- MemoryStartupBytes = $MemoryStartupBytes
- SwitchName = $switchname
- BootDevice = $bootdevice
- ErrorAction = "Stop"
-}
-
-# Generation parameter was added in ps v4
-if((get-command Hyper-V\New-VM).Parameters.Keys.Contains("generation")) {
- $vm_params.Generation = $generation
-}
-
-# Create the VM using the values in the hash map
-
-$vm = Hyper-V\New-VM @vm_params
-
-$notes = (Select-Xml -xml $vmconfig -XPath "//notes").node.'#text'
-
-# Set-VM parameters to configure new VM with old values
-
-$more_vm_params = @{
- ProcessorCount = $processors
- MemoryStartupBytes = $MemoryStartupBytes
-}
-
-If ($dynamicmemory) {
- $more_vm_params.Add("DynamicMemory",$True)
- $more_vm_params.Add("MemoryMinimumBytes",$MemoryMinimumBytes)
- $more_vm_params.Add("MemoryMaximumBytes", $MemoryMaximumBytes)
-} else {
- $more_vm_params.Add("StaticMemory",$True)
-}
-
-if ($notes) {
- $more_vm_params.Add("Notes",$notes)
-}
-
-if ($auto_start_action) {
- $more_vm_params.Add("AutomaticStartAction",$auto_start_action)
-}
-
-if ($auto_stop_action) {
- $more_vm_params.Add("AutomaticStopAction",$auto_stop_action)
-}
-
-# Set the values on the VM
-$vm | Hyper-V\Set-VM @more_vm_params -Passthru
-
-# Add drives to the virtual machine
-$controllers = Select-Xml -xml $vmconfig -xpath "//*[starts-with(name(.),'controller')]"
-
-# Only set EFI secure boot for Gen 2 machines, not gen 1
-if ($generation -ne 1) {
- # Set EFI secure boot
- if ($secure_boot_enabled -eq "True") {
- Hyper-V\Set-VMFirmware -VM $vm -EnableSecureBoot On
- } else {
- Hyper-V\Set-VMFirmware -VM $vm -EnableSecureBoot Off
- }
-}
-
-# Enable nested virtualization if configured
-if ($enable_virtualization_extensions -eq "True") {
- Hyper-V\Set-VMProcessor -VM $vm -ExposeVirtualizationExtensions $true
-}
-
-# A regular expression pattern to pull the number from controllers
-[regex]$rx="\d"
-
-foreach ($controller in $controllers) {
- $node = $controller.Node
-
- # Check for SCSI
- if ($node.ParentNode.ChannelInstanceGuid) {
- $ControllerType = "SCSI"
- } else {
- $ControllerType = "IDE"
- }
-
- $drives = $node.ChildNodes | where {$_.pathname."#text"}
- foreach ($drive in $drives) {
- #if drive type is ISO then set DVD Drive accordingly
- $driveType = $drive.type."#text"
-
- $addDriveParam = @{
- ControllerNumber = $rx.Match($controller.node.name).value
- Path = $dest_path
- }
-
- if ($drive.pool_id."#text") {
- $ResourcePoolName = $drive.pool_id."#text"
- $addDriveParam.Add("ResourcePoolname",$ResourcePoolName)
- }
-
- if ($drivetype -eq 'VHD') {
- $addDriveParam.add("ControllerType",$ControllerType)
- $vm | Hyper-V\Add-VMHardDiskDrive @AddDriveparam
- }
- }
-}
-
-$vm_id = (Hyper-V\Get-VM $vm_name).id.guid
-$resultHash = @{
- name = $vm_name
- id = $vm_id
-}
-
-$result = ConvertTo-Json $resultHash
-Write-Output-Message $result
diff --git a/plugins/providers/hyperv/scripts/list_snapshots.ps1 b/plugins/providers/hyperv/scripts/list_snapshots.ps1
index d5bd05811..7e4b85583 100644
--- a/plugins/providers/hyperv/scripts/list_snapshots.ps1
+++ b/plugins/providers/hyperv/scripts/list_snapshots.ps1
@@ -1,12 +1,19 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-$Snapshots = @(Hyper-V\Get-VMSnapshot $VM | Select-Object Name)
-$result = ConvertTo-json $Snapshots
+$ErrorActionPreference = "Stop"
-Write-Host "===Begin-Output==="
-Write-Host $result
-Write-Host "===End-Output==="
+try {
+ $VM = Hyper-V\Get-VM -Id $VmId
+ $Snapshots = @(Hyper-V\Get-VMSnapshot $VM | Select-Object Name)
+} catch {
+ Write-Error-Message "Failed to get snapshot list: ${PSItem}"
+ exit 1
+}
+
+$result = ConvertTo-json $Snapshots
+Write-Output-Message $result
diff --git a/plugins/providers/hyperv/scripts/restore_snapshot.ps1 b/plugins/providers/hyperv/scripts/restore_snapshot.ps1
index 406804394..192e56075 100644
--- a/plugins/providers/hyperv/scripts/restore_snapshot.ps1
+++ b/plugins/providers/hyperv/scripts/restore_snapshot.ps1
@@ -1,8 +1,17 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId,
[string]$SnapName
)
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-Hyper-V\Restore-VMSnapshot $VM -Name $SnapName -Confirm:$false
+$ErrorActionPreference = "Stop"
+
+try {
+ $VM = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Restore-VMSnapshot $VM -Name $SnapName -Confirm:$false
+} catch {
+ Write-Error-Message "Failed to restore snapshot: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/resume_vm.ps1 b/plugins/providers/hyperv/scripts/resume_vm.ps1
index 8cbca00d4..f30e05b73 100644
--- a/plugins/providers/hyperv/scripts/resume_vm.ps1
+++ b/plugins/providers/hyperv/scripts/resume_vm.ps1
@@ -1,7 +1,16 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-Hyper-V\Resume-VM $VM
+$ErrorActionPreference = "Stop"
+
+try {
+ $VM = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Resume-VM $VM
+} catch {
+ Write-Error-Message "Failed to resume VM: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/set_name.ps1 b/plugins/providers/hyperv/scripts/set_name.ps1
new file mode 100644
index 000000000..ac97fad48
--- /dev/null
+++ b/plugins/providers/hyperv/scripts/set_name.ps1
@@ -0,0 +1,24 @@
+#Requires -Modules VagrantMessages
+
+param (
+ [parameter (Mandatory=$true)]
+ [Guid] $VMID,
+ [parameter (Mandatory=$true)]
+ [string] $VMName
+)
+
+$ErrorActionPreference = "Stop"
+
+try {
+ $VM = Hyper-V\Get-VM -Id $VMID
+} catch {
+ Write-Error-Message "Failed to locate VM: ${PSItem}"
+ exit 1
+}
+
+try {
+ Hyper-V\Set-VM -VM $VM -NewVMName $VMName
+} catch {
+ Write-Error-Message "Failed to assign new VM name ${VMName}: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/set_network_mac.ps1 b/plugins/providers/hyperv/scripts/set_network_mac.ps1
index 4ef157f38..3942eb621 100644
--- a/plugins/providers/hyperv/scripts/set_network_mac.ps1
+++ b/plugins/providers/hyperv/scripts/set_network_mac.ps1
@@ -1,18 +1,18 @@
-param (
- [string]$VmId = $(throw "-VmId is required."),
- [string]$Mac = $(throw "-Mac ")
- )
+#Requires -Modules VagrantMessages
-# Include the following modules
-$presentDir = Split-Path -parent $PSCommandPath
-$modules = @()
-$modules += $presentDir + "\utils\write_messages.ps1"
-forEach ($module in $modules) { . $module }
+param (
+ [parameter (Mandatory=$true)]
+ [string]$VmId,
+ [parameter (Mandatory=$true)]
+ [string]$Mac
+)
+
+$ErrorActionPreference = "Stop"
try {
- $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "stop"
- Hyper-V\Set-VMNetworkAdapter $vm -StaticMacAddress $Mac -ErrorAction "stop"
-}
-catch {
- Write-Error-Message "Failed to set VM's MAC address $_"
+ $vm = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Set-VMNetworkAdapter $vm -StaticMacAddress $Mac
+} catch {
+ Write-Error-Message "Failed to set VM MAC address: ${PSItem}"
+ exit 1
}
diff --git a/plugins/providers/hyperv/scripts/set_network_vlan.ps1 b/plugins/providers/hyperv/scripts/set_network_vlan.ps1
index b385dc9c5..26dc8941d 100644
--- a/plugins/providers/hyperv/scripts/set_network_vlan.ps1
+++ b/plugins/providers/hyperv/scripts/set_network_vlan.ps1
@@ -1,7 +1,11 @@
+#Requires -Modules VagrantMessages
+
param (
- [string]$VmId = $(throw "-VmId is required."),
- [int]$VlanId = $(throw "-VlanId ")
- )
+ [parameter (Mandatory=$true)]
+ [string]$VmId,
+ [parameter (Mandatory=$true)]
+ [int]$VlanId
+)
# Include the following modules
$presentDir = Split-Path -parent $PSCommandPath
diff --git a/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1 b/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1
index fd0d30063..a28d1229b 100644
--- a/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1
+++ b/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1
@@ -1,37 +1,27 @@
+#Requires -Modules VagrantVM, VagrantMessages
+
param (
- [string] $VmId,
- [string] $guest_service_interface = $null,
- [string] $heartbeat = $null,
- [string] $key_value_pair_exchange = $null,
- [string] $shutdown = $null,
- [string] $time_synchronization = $null,
- [string] $vss = $null
+ [parameter (Mandatory=$true)]
+ [string] $VMID,
+ [parameter (Mandatory=$true)]
+ [string] $Name,
+ [parameter (Mandatory=$false)]
+ [switch] $Enable
)
-# Include the following modules
-$Dir = Split-Path $script:MyInvocation.MyCommand.Path
-. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
+$ErrorActionPreference = "Stop"
-$vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "stop"
-
-# Set the service based on value
-function VmSetService
-{
- param ([string] $Name, [string] $Value, [Microsoft.HyperV.PowerShell.VirtualMachine] $Vm)
-
- if ($Value -ne $null){
- if($Value -eq "true"){
- Hyper-V\Enable-VMIntegrationService -VM $Vm -Name $Name
- }
- if($Value -eq "false"){
- Hyper-V\Disable-VMIntegrationService -VM $Vm -Name $Name
- }
- }
+try {
+ $VM = Hyper-V\Get-VM -Id $VMID
+} catch {
+ Write-Error-Message "Failed to locate VM: ${PSItem}"
+ exit 1
}
-VmSetService -Name "Guest Service Interface" -Value $guest_service_interface -Vm $vm
-VmSetService -Name "Heartbeat" -Value $heartbeat -Vm $vm
-VmSetService -Name "Key-Value Pair Exchange" -Value $key_value_pair_exchange -Vm $vm
-VmSetService -Name "Shutdown" -Value $shutdown -Vm $vm
-VmSetService -Name "Time Synchronization" -Value $time_synchronization -Vm $vm
-VmSetService -Name "VSS" -Value $vss -Vm $vm
\ No newline at end of file
+try {
+ Set-VagrantVMService -VM $VM -Name $Name -Enable $enabled
+} catch {
+ if($enabled){ $action = "enable" } else { $action = "disable" }
+ Write-Error-Message "Failed to ${action} VM integration service ${Name}: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/start_vm.ps1 b/plugins/providers/hyperv/scripts/start_vm.ps1
index bfa67bc92..8380e1c8f 100644
--- a/plugins/providers/hyperv/scripts/start_vm.ps1
+++ b/plugins/providers/hyperv/scripts/start_vm.ps1
@@ -1,27 +1,26 @@
-param (
- [string]$VmId = $(throw "-VmId is required.")
- )
+#Requires -Modules VagrantMessages
-# Include the following modules
-$presentDir = Split-Path -parent $PSCommandPath
-$modules = @()
-$modules += $presentDir + "\utils\write_messages.ps1"
-forEach ($module in $modules) { . $module }
+param (
+ [parameter (Mandatory=$true)]
+ [string]$VmId
+)
+
+$ErrorActionPreference = "Stop"
try {
- $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "stop"
- Hyper-V\Start-VM $vm -ErrorAction "stop"
- $state = $vm.state
- $status = $vm.status
- $name = $vm.name
- $resultHash = @{
- state = "$state"
- status = "$status"
- name = "$name"
- }
- $result = ConvertTo-Json $resultHash
- Write-Output-Message $result
+ $vm = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Start-VM $vm
+ $state = $vm.state
+ $status = $vm.status
+ $name = $vm.name
+ $resultHash = @{
+ state = "$state"
+ status = "$status"
+ name = "$name"
+ }
+ $result = ConvertTo-Json $resultHash
+ Write-Output-Message $result
+} catch {
+ Write-Error-Message "Failed to start VM ${PSItem}"
+ exit 1
}
-catch {
- Write-Error-Message "Failed to start a VM $_"
-}
\ No newline at end of file
diff --git a/plugins/providers/hyperv/scripts/stop_vm.ps1 b/plugins/providers/hyperv/scripts/stop_vm.ps1
index 524a45fba..5cf88e636 100644
--- a/plugins/providers/hyperv/scripts/stop_vm.ps1
+++ b/plugins/providers/hyperv/scripts/stop_vm.ps1
@@ -1,8 +1,17 @@
+#Requires -Modules VagrantMessages
+
Param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
-# Shuts down virtual machine regardless of any unsaved application data
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-Hyper-V\Stop-VM $VM -Force
+$ErrorActionPreference = "Stop"
+
+try{
+ # Shuts down virtual machine regardless of any unsaved application data
+ $VM = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Stop-VM $VM -Force
+} catch {
+ Write-Error-Message "Failed to stop VM: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/suspend_vm.ps1 b/plugins/providers/hyperv/scripts/suspend_vm.ps1
index 2f1d69da8..43993ac15 100644
--- a/plugins/providers/hyperv/scripts/suspend_vm.ps1
+++ b/plugins/providers/hyperv/scripts/suspend_vm.ps1
@@ -1,7 +1,16 @@
-Param(
+#Requires -Modules VagrantMessages
+
+param(
[Parameter(Mandatory=$true)]
[string]$VmId
)
-$VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop"
-Hyper-V\Suspend-VM $VM
+$ErrorActionPreference = "Stop"
+
+try{
+ $VM = Hyper-V\Get-VM -Id $VmId
+ Hyper-V\Suspend-VM $VM
+} catch {
+ Write-Error-Message "Failed to suspend VM: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/providers/hyperv/scripts/utils/VagrantMessages/VagrantMessages.psm1 b/plugins/providers/hyperv/scripts/utils/VagrantMessages/VagrantMessages.psm1
new file mode 100644
index 000000000..2f934b6c0
--- /dev/null
+++ b/plugins/providers/hyperv/scripts/utils/VagrantMessages/VagrantMessages.psm1
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------
+# Copyright (c) Microsoft Open Technologies, Inc.
+# All Rights Reserved. Licensed under the MIT License.
+#--------------------------------------------------------------------------
+
+function Write-Error-Message {
+ param (
+ [parameter (Mandatory=$true,Position=0)]
+ [string] $Message
+ )
+ $error_message = @{
+ error = $Message
+ }
+ Write-Host "===Begin-Error==="
+ Write-Host (ConvertTo-Json $error_message)
+ Write-Host "===End-Error==="
+}
+
+function Write-Output-Message {
+ param (
+ [parameter (Mandatory=$true,Position=0)]
+ [string] $Message
+ )
+ Write-Host "===Begin-Output==="
+ Write-Host $Message
+ Write-Host "===End-Output==="
+}
diff --git a/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 b/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1
new file mode 100644
index 000000000..885d3c6b1
--- /dev/null
+++ b/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1
@@ -0,0 +1,616 @@
+# Vagrant VM creation functions
+
+function New-VagrantVM {
+ param (
+ [parameter(Mandatory=$true)]
+ [string] $VMConfigFile,
+ [parameter(Mandatory=$true)]
+ [string] $DestinationPath,
+ [parameter (Mandatory=$true)]
+ [string] $DataPath,
+ [parameter (Mandatory=$true)]
+ [string] $SourcePath,
+ [parameter (Mandatory=$false)]
+ [bool] $LinkedClone = $false,
+ [parameter(Mandatory=$false)]
+ [string] $VMName
+ )
+ if([IO.Path]::GetExtension($VMConfigFile).ToLower() -eq ".xml") {
+ return New-VagrantVMXML @PSBoundParameters
+ } else {
+ return New-VagrantVMVMCX @PSBoundParameters
+ }
+<#
+.SYNOPSIS
+
+Create a new Vagrant Hyper-V VM by cloning original. This
+is the general use function with will call the specialized
+function based on the extension of the configuration file.
+
+.DESCRIPTION
+
+Using an existing Hyper-V VM a new Hyper-V VM is created
+by cloning the original.
+
+.PARAMETER VMConfigFile
+Path to the original Hyper-V VM configuration file.
+
+.PARAMETER DestinationPath
+Path to new Hyper-V VM hard drive.
+
+.PARAMETER DataPath
+Directory path of the original Hyper-V VM to be cloned.
+
+.PARAMETER SourcePath
+Path to the original Hyper-V VM hard drive.
+
+.PARAMETER LinkedClone
+New Hyper-V VM should be linked clone instead of complete copy.
+
+.PARAMETER VMName
+Name of the new Hyper-V VM.
+
+.INPUTS
+
+None.
+
+.OUTPUTS
+
+VirtualMachine. The cloned Hyper-V VM.
+#>
+}
+
+function New-VagrantVMVMCX {
+ param (
+ [parameter(Mandatory=$true)]
+ [string] $VMConfigFile,
+ [parameter(Mandatory=$true)]
+ [string] $DestinationPath,
+ [parameter (Mandatory=$true)]
+ [string] $DataPath,
+ [parameter (Mandatory=$true)]
+ [string] $SourcePath,
+ [parameter (Mandatory=$false)]
+ [bool] $LinkedClone = $false,
+ [parameter(Mandatory=$false)]
+ [string] $VMName
+ )
+
+ $NewVMConfig = @{
+ Path = $VMConfigFile;
+ SnapshotFilePath = Join-Path $DataPath "Snapshots";
+ VhdDestinationPath = Join-Path $DataPath "Virtual Hard Disks";
+ VirtualMachinePath = $DataPath;
+ }
+ $VMConfig = (Hyper-V\Compare-VM -Copy -GenerateNewID @NewVMConfig)
+ $VM = $VMConfig.VM
+ $Gen = $VM.Generation
+
+ # Set VM name if name has been provided
+ if($VMName) {
+ Hyper-V\Set-VM -VM $VM -NewVMName $VMName
+ }
+
+ # Set EFI secure boot on machines after Gen 1
+ if($Gen -gt 1) {
+ Hyper-V\Set-VMFirmware -VM $VM -EnableSecureBoot (Hyper-V\Get-VMFirmware -VM $VM).SecureBoot
+ }
+
+ # Verify new VM
+ $Report = Hyper-V\Compare-VM -CompatibilityReport $VMConfig
+ if($Report.Incompatibilities.Length -gt 0){
+ throw $(ConvertTo-Json $($Report.Incompatibilities | Select -ExpandProperty Message))
+ }
+
+ if($LinkedClone) {
+ $Controllers = Hyper-V\Get-VMScsiController -VM $VM
+ if($Gen -eq 1){
+ $Controllers = @($Controllers) + @(Hyper-V\Get-VMIdeController -VM $VM)
+ }
+ foreach($Controller in $Controllers) {
+ foreach($Drive in $Controller.Drives) {
+ if([System.IO.Path]::GetFileName($Drive.Path) -eq [System.IO.Path]::GetFileName($SourcePath)) {
+ $Path = $Drive.Path
+ Hyper-V\Remove-VMHardDiskDrive $Drive
+ Hyper-V\New-VHD -Path $DestinationPath -ParentPath $SourcePath
+ Hyper-V\AddVMHardDiskDrive -VM $VM -Path $DestinationPath
+ break
+ }
+ }
+ }
+
+ }
+ return Hyper-V\Import-VM -CompatibilityReport $VMConfig
+<#
+.SYNOPSIS
+
+Create a new Vagrant Hyper-V VM by cloning original (VMCX based).
+
+.DESCRIPTION
+
+Using an existing Hyper-V VM a new Hyper-V VM is created
+by cloning the original.
+
+.PARAMETER VMConfigFile
+Path to the original Hyper-V VM configuration file.
+
+.PARAMETER DestinationPath
+Path to new Hyper-V VM hard drive.
+
+.PARAMETER DataPath
+Directory path of the original Hyper-V VM to be cloned.
+
+.PARAMETER SourcePath
+Path to the original Hyper-V VM hard drive.
+
+.PARAMETER LinkedClone
+New Hyper-V VM should be linked clone instead of complete copy.
+
+.PARAMETER VMName
+Name of the new Hyper-V VM.
+
+.INPUTS
+
+None.
+
+.OUTPUTS
+
+VirtualMachine. The cloned Hyper-V VM.
+#>
+}
+
+function New-VagrantVMXML {
+ param (
+ [parameter(Mandatory=$true)]
+ [string] $VMConfigFile,
+ [parameter(Mandatory=$true)]
+ [string] $DestinationPath,
+ [parameter (Mandatory=$true)]
+ [string] $DataPath,
+ [parameter (Mandatory=$true)]
+ [string] $SourcePath,
+ [parameter (Mandatory=$false)]
+ [bool] $LinkedClone = $false,
+ [parameter(Mandatory=$false)]
+ [string] $VMName
+ )
+
+ $DestinationDirectory = [System.IO.Path]::GetDirectoryName($DestinationPath)
+ New-Item -ItemType Directory -Force -Path $DestinationDirectory
+
+ if($LinkedClone){
+ Hyper-V\New-VHD -Path $DestinationPath -ParentPath $SourcePath -ErrorAction Stop
+ } else {
+ Copy-Item $SourcePath -Destination $DestinationPath -ErrorAction Stop
+ }
+
+ [xml]$VMConfig = Get-Content -Path $VMConfigFile
+ $Gen = [int]($VMConfig.configuration.properties.subtype."#text") + 1
+ if(!$VMName) {
+ $VMName = $VMConfig.configuration.properties.name."#text"
+ }
+
+ # Determine boot device
+ if($Gen -eq 1) {
+ Switch ((Select-Xml -xml $VMConfig -XPath "//boot").node.device0."#text") {
+ "Floppy" { $BootDevice = "Floppy" }
+ "HardDrive" { $BootDevice = "IDE" }
+ "Optical" { $BootDevice = "CD" }
+ "Network" { $BootDevice = "LegacyNetworkAdapter" }
+ "Default" { $BootDevice = "IDE" }
+ }
+ } else {
+ Switch ((Select-Xml -xml $VMConfig -XPath "//boot").node.device0."#text") {
+ "HardDrive" { $BootDevice = "VHD" }
+ "Optical" { $BootDevice = "CD" }
+ "Network" { $BootDevice = "NetworkAdapter" }
+ "Default" { $BootDevice = "VHD" }
+ }
+ }
+
+ # Determine if secure boot is enabled
+ $SecureBoot = (Select-Xml -XML $VMConfig -XPath "//secure_boot_enabled").Node."#text"
+
+ $NewVMConfig = @{
+ Name = $VMName;
+ NoVHD = $true;
+ BootDevice = $BootDevice;
+ }
+
+ # Generation parameter in PS4 so validate before using
+ if((Get-Command Hyper-V\New-VM).Parameters.Keys.Contains("generation")) {
+ $NewVMConfig.Generation = $Gen
+ }
+
+ # Create new VM instance
+ $VM = Hyper-V\New-VM @NewVMConfig
+
+ # Configure secure boot
+ if($Gen -gt 1) {
+ if($SecureBoot -eq "True") {
+ Hyper-V\Set-VMFirmware -VM $VM -EnableSecureBoot On
+ } else {
+ Hyper-V\Set-VMFirmware -VM $VM -EnableSecureBoot Off
+ }
+ }
+
+ # Configure drives
+ [regex]$DriveNumberMatcher = "\d"
+ $Controllers = Select-Xml -XML $VMConfig -XPath "//*[starts-with(name(.),'controller')]"
+
+ foreach($Controller in $Controllers) {
+ $Node = $Controller.Node
+ if($Node.ParentNode.ChannelInstanceGuid) {
+ $ControllerType = "SCSI"
+ } else {
+ $ControllerType = "IDE"
+ }
+ $Drives = $Node.ChildNodes | where {$_.pathname."#text"}
+ foreach($Drive in $Drives) {
+ $DriveType = $Drive.type."#text"
+ if($DriveType -ne "VHD") {
+ continue
+ }
+
+ $NewDriveConfig = @{
+ ControllerNumber = $DriveNumberMatcher.Match($Controller.node.name).value;
+ Path = $DestinationPath;
+ ControllerType = $ControllerType;
+ }
+ if($Drive.pool_id."#text") {
+ $NewDriveConfig.ResourcePoolname = $Drive.pool_id."#text"
+ }
+ $VM | Hyper-V\Add-VMHardDiskDrive @NewDriveConfig
+ }
+ }
+
+ # Apply original VM configuration to new VM instance
+
+ $processors = $VMConfig.configuration.settings.processors.count."#text"
+ $notes = (Select-Xml -XML $VMConfig -XPath "//notes").node."#text"
+ $memory = (Select-Xml -XML $VMConfig -XPath "//memory").node.Bank
+ if ($memory.dynamic_memory_enabled."#text" -eq "True") {
+ $dynamicmemory = $True
+ }
+ else {
+ $dynamicmemory = $False
+ }
+ # Memory values need to be in bytes
+ $MemoryMaximumBytes = ($memory.limit."#text" -as [int]) * 1MB
+ $MemoryStartupBytes = ($memory.size."#text" -as [int]) * 1MB
+ $MemoryMinimumBytes = ($memory.reservation."#text" -as [int]) * 1MB
+
+ $Config = @{
+ ProcessorCount = $processors;
+ MemoryStartupBytes = $MemoryStartupBytes
+ }
+ if($dynamicmemory) {
+ $Config.DynamicMemory = $true
+ $Config.MemoryMinimumBytes = $MemoryMinimumBytes
+ $Config.MemoryMaximumBytes = $MemoryMaximumBytes
+ } else {
+ $Config.StaticMemory = $true
+ }
+ if($notes) {
+ $Config.Notes = $notes
+ }
+ Hyper-V\Set-VM -VM $VM @Config
+
+ return $VM
+<#
+.SYNOPSIS
+
+Create a new Vagrant Hyper-V VM by cloning original (XML based).
+
+.DESCRIPTION
+
+Using an existing Hyper-V VM a new Hyper-V VM is created
+by cloning the original.
+
+.PARAMETER VMConfigFile
+Path to the original Hyper-V VM configuration file.
+
+.PARAMETER DestinationPath
+Path to new Hyper-V VM hard drive.
+
+.PARAMETER DataPath
+Directory path of the original Hyper-V VM to be cloned.
+
+.PARAMETER SourcePath
+Path to the original Hyper-V VM hard drive.
+
+.PARAMETER LinkedClone
+New Hyper-V VM should be linked clone instead of complete copy.
+
+.PARAMETER VMName
+Name of the new Hyper-V VM.
+
+.INPUTS
+
+None.
+
+.OUTPUTS
+
+VirtualMachine. The cloned Hyper-V VM.
+#>
+}
+
+# Vagrant VM configuration functions
+
+function Set-VagrantVMMemory {
+ param (
+ [parameter (Mandatory=$true)]
+ [Microsoft.HyperV.PowerShell.VirtualMachine] $VM,
+ [parameter (Mandatory=$false)]
+ [int] $Memory,
+ [parameter (Mandatory=$false)]
+ [int] $MaxMemory
+ )
+
+ $ConfigMemory = Hyper-V\Get-VMMemory -VM $VM
+
+ if(!$Memory) {
+ $MemoryStartupBytes = ($ConfigMemory.Startup)
+ $MemoryMinimumBytes = ($ConfigMemory.Minimum)
+ $MemoryMaximumBytes = ($ConfigMemory.Maximum)
+ } else {
+ $MemoryStartupBytes = $Memory * 1MB
+ $MemoryMinimumBytes = $Memory * 1MB
+ $MemoryMaximumBytes = $Memory * 1MB
+ }
+
+ if($MaxMemory) {
+ $DynamicMemory = $true
+ $MemoryMaximumBytes = $MaxMemory * 1MB
+ }
+
+ if($DynamicMemory) {
+ Hyper-V\Set-VM -VM $VM -DynamicMemory
+ Hyper-V\Set-VM -VM $VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes `
+ $MemoryMaximumBytes -MemoryStartupBytes $MemoryStartupBytes
+ } else {
+ Hyper-V\Set-VM -VM $VM -StaticMemory
+ Hyper-V\Set-VM -VM $VM -MemoryStartupBytes $MemoryStartupBytes
+ }
+ return $VM
+<#
+.SYNOPSIS
+
+Configure VM memory settings.
+
+.DESCRIPTION
+
+Adjusts the VM memory settings. If MaxMemory is defined, dynamic memory
+is enabled on the VM.
+
+.PARAMETER VM
+
+Hyper-V VM for modification.
+
+.Parameter Memory
+
+Memory to allocate to the given VM in MB.
+
+.Parameter MaxMemory
+
+Maximum memory to allocate to the given VM in MB. When this value is
+provided dynamic memory is enabled for the VM. The Memory value or
+the currently configured memory of the VM will be used as the minimum
+and startup memory value.
+
+.Output
+
+VirtualMachine.
+#>
+}
+
+function Set-VagrantVMCPUS {
+ param (
+ [parameter (Mandatory=$true)]
+ [Microsoft.HyperV.PowerShell.VirtualMachine] $VM,
+ [parameter (Mandatory=$false)]
+ [int] $CPUCount
+ )
+
+ if($CPUCount) {
+ Hyper-V\Set-VM -VM $VM -ProcessorCount $CPUCount
+ }
+ return $VM
+<#
+.SYNOPSIS
+
+Configure VM CPU count.
+
+.DESCRIPTION
+
+Configure the number of CPUs on the given VM.
+
+.PARAMETER VM
+
+Hyper-V VM for modification.
+
+.PARAMETER CPUCount
+
+Number of CPUs.
+
+.Output
+
+VirtualMachine.
+#>
+}
+
+function Set-VagrantVMVirtExtensions {
+ param (
+ [parameter (Mandatory=$true)]
+ [Microsoft.HyperV.PowerShell.VirtualMachine] $VM,
+ [parameter (Mandatory=$false)]
+ [bool] $Enabled=$false
+ )
+
+ Hyper-V\Set-VMProcessor -VM $VM -ExposeVirtualizationExtensions $Enabled
+ return $VM
+<#
+.SYNOPSIS
+
+Enable virtualization extensions on VM.
+
+.PARAMETER VM
+
+Hyper-V VM for modification.
+
+.PARAMETER Enabled
+
+Enable virtualization extensions on given VM.
+
+.OUTPUT
+
+VirtualMachine.
+#>
+}
+
+function Set-VagrantVMAutoActions {
+ param (
+ [parameter (Mandatory=$true)]
+ [Microsoft.HyperV.PowerShell.VirtualMachine] $VM,
+ [parameter (Mandatory=$false)]
+ [string] $AutoStartAction="Nothing",
+ [parameter (Mandatory=$false)]
+ [string] $AutoStopAction="ShutDown"
+ )
+
+ Hyper-V\Set-VM -VM $VM -AutomaticStartAction $AutoStartAction
+ Hyper-V\Set-VM -VM $VM -AutomaticStopAction $AutoStopAction
+ return $VM
+<#
+.SYNOPSIS
+
+Configure automatic start and stop actions for VM
+
+.DESCRIPTION
+
+Configures the automatic start and automatic stop actions for
+the given VM.
+
+.PARAMETER VM
+
+Hyper-V VM for modification.
+
+.PARAMETER AutoStartAction
+
+Action the VM should automatically take when the host is started.
+
+.PARAMETER AutoStopAction
+
+Action the VM should automatically take when the host is stopped.
+
+.OUTPUT
+
+VirtualMachine.
+#>
+}
+
+function Set-VagrantVMService {
+ param (
+ [parameter (Mandatory=$true)]
+ [Microsoft.HyperV.PowerShell.VirtualMachine] $VM,
+ [parameter (Mandatory=$true)]
+ [string] $Name,
+ [parameter (Mandatory=$true)]
+ [bool] $Enable
+ )
+
+ if($Enable) {
+ Hyper-V\Enable-VMIntegrationService -VM $VM -Name $Name
+ } else {
+ Hyper-V\Disable-VMIntegrationService -VM $VM -Name $Name
+ }
+ return $VM
+<#
+.SYNOPSIS
+
+Enable or disable Hyper-V VM integration services.
+
+.PARAMETER VM
+
+Hyper-V VM for modification.
+
+.PARAMETER Name
+
+Name of the integration service.
+
+.PARAMETER Enable
+
+Enable or disable the service.
+
+.OUTPUT
+
+VirtualMachine.
+#>
+}
+
+# Vagrant networking functions
+
+function Get-VagrantVMSwitch {
+ param (
+ [parameter (Mandatory=$true)]
+ [string] $NameOrID
+ )
+ $SwitchName = $(Hyper-V\Get-VMSwitch -Id $NameOrID).Name
+ if(!$SwitchName) {
+ $SwitchName = $(Hyper-V\Get-VMSwitch -Name $NameOrID).Name
+ }
+ if(!$SwitchName) {
+ throw "Failed to locate switch with name or ID: ${NameOrID}"
+ }
+ return $SwitchName
+<#
+.SYNOPSIS
+
+Get name of VMSwitch.
+
+.DESCRIPTION
+
+Find VMSwitch by name or ID and return name.
+
+.PARAMETER NameOrID
+
+Name or ID of VMSwitch.
+
+.OUTPUT
+
+Name of VMSwitch.
+#>
+}
+
+function Set-VagrantVMSwitch {
+ param (
+ [parameter (Mandatory=$true)]
+ [Microsoft.HyperV.PowerShell.VirtualMachine] $VM,
+ [parameter (Mandatory=$true)]
+ [String] $SwitchName
+ )
+ $Adapter = Hyper-V\Get-VMNetworkAdapter -VM $VM
+ Hyper-V\Connect-VMNetworkAdapter -VMNetworkAdapter $Adapter -SwitchName $SwitchName
+ return $VM
+<#
+.SYNOPSIS
+
+Configure VM to use given switch.
+
+.DESCRIPTION
+
+Configures VM adapter to use the the VMSwitch with the given name.
+
+.PARAMETER VM
+
+Hyper-V VM for modification.
+
+.PARAMETER SwitchName
+
+Name of the VMSwitch.
+
+.OUTPUT
+
+VirtualMachine.
+#>
+}
diff --git a/plugins/providers/hyperv/scripts/utils/write_messages.ps1 b/plugins/providers/hyperv/scripts/utils/write_messages.ps1
deleted file mode 100644
index ce0d8d8cf..000000000
--- a/plugins/providers/hyperv/scripts/utils/write_messages.ps1
+++ /dev/null
@@ -1,20 +0,0 @@
-#-------------------------------------------------------------------------
-# Copyright (c) Microsoft Open Technologies, Inc.
-# All Rights Reserved. Licensed under the MIT License.
-#--------------------------------------------------------------------------
-
-function Write-Error-Message($message) {
- $error_message = @{
- error = "$message"
- }
- Write-Host "===Begin-Error==="
- $result = ConvertTo-json $error_message
- Write-Host $result
- Write-Host "===End-Error==="
-}
-
-function Write-Output-Message($message) {
- Write-Host "===Begin-Output==="
- Write-Host $message
- Write-Host "===End-Output==="
-}
From 995c43dd0cb0e365e46304d98587e1759923576e Mon Sep 17 00:00:00 2001
From: Jaroslaw Gorny
Date: Fri, 25 May 2018 00:06:20 +0200
Subject: [PATCH 07/53] Fix issue 9592 when systemd-networkd is used by Debian
guest
---
plugins/guests/debian/cap/configure_networks.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/guests/debian/cap/configure_networks.rb b/plugins/guests/debian/cap/configure_networks.rb
index 6ba4c2ca9..fcc907a25 100644
--- a/plugins/guests/debian/cap/configure_networks.rb
+++ b/plugins/guests/debian/cap/configure_networks.rb
@@ -109,7 +109,7 @@ module VagrantPlugins
end
remote_path = upload_tmp_file(comm, net_conf.join("\n"))
- dest_path = "#{NETWORKD_DIRECTORY}/99-vagrant.network"
+ dest_path = "#{NETWORKD_DIRECTORY}/50-vagrant.network"
comm.sudo(["mkdir -p #{NETWORKD_DIRECTORY}",
"mv -f '#{remote_path}' '#{dest_path}'",
"chown root:root '#{dest_path}'",
From 52bae2193342ed33381c7f59ee9507de24e422c9 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Thu, 24 May 2018 15:33:36 -0700
Subject: [PATCH 08/53] Remove unused PowerShell scripts
---
.../hyperv/scripts/utils/create_session.ps1 | 34 -----------
.../smb/scripts/mount_share.ps1 | 56 -------------------
.../synced_folders/smb/scripts/ps_version.ps1 | 1 -
3 files changed, 91 deletions(-)
delete mode 100644 plugins/providers/hyperv/scripts/utils/create_session.ps1
delete mode 100644 plugins/synced_folders/smb/scripts/mount_share.ps1
delete mode 100644 plugins/synced_folders/smb/scripts/ps_version.ps1
diff --git a/plugins/providers/hyperv/scripts/utils/create_session.ps1 b/plugins/providers/hyperv/scripts/utils/create_session.ps1
deleted file mode 100644
index fcf0f7f10..000000000
--- a/plugins/providers/hyperv/scripts/utils/create_session.ps1
+++ /dev/null
@@ -1,34 +0,0 @@
-#-------------------------------------------------------------------------
-# Copyright (c) Microsoft Open Technologies, Inc.
-# All Rights Reserved. Licensed under the MIT License.
-#--------------------------------------------------------------------------
-
-function Get-Remote-Session($guest_ip, $username, $password) {
- $secstr = convertto-securestring -AsPlainText -Force -String $password
- $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
- New-PSSession -ComputerName $guest_ip -Credential $cred -ErrorAction "stop"
-}
-
-function Create-Remote-Session($guest_ip, $username, $password) {
- $count = 0
- $session_error = ""
- $session = ""
- do {
- $count++
- try {
- $session = Get-Remote-Session $guest_ip $username $password
- $session_error = ""
- }
- catch {
- Start-Sleep -s 1
- $session_error = $_
- $session = ""
- }
- }
- while (!$session -and $count -lt 20)
-
- return @{
- session = $session
- error = $session_error
- }
-}
diff --git a/plugins/synced_folders/smb/scripts/mount_share.ps1 b/plugins/synced_folders/smb/scripts/mount_share.ps1
deleted file mode 100644
index fa1f98133..000000000
--- a/plugins/synced_folders/smb/scripts/mount_share.ps1
+++ /dev/null
@@ -1,56 +0,0 @@
-param (
- [string]$share_name = $(throw "-share_name is required."),
- [string]$guest_path = $(throw "-guest_path is required."),
- [string]$guest_ip = $(throw "-guest_ip is required."),
- [string]$username = $(throw "-username is required."),
- [string]$password = $(throw "-password is required."),
- [string]$host_ip = $(throw "-host_ip is required."),
- [string]$host_share_username = $(throw "-host_share_username is required."),
- [string]$host_share_password = $(throw "-host_share_password is required.")
- )
-
-# Include the following modules
-$presentDir = Split-Path -parent $PSCommandPath
-$modules = @()
-$modules += $presentDir + "\utils\create_session.ps1"
-$modules += $presentDir + "\utils\write_messages.ps1"
-
-forEach ($module in $modules) { . $module }
-
-try {
- function Mount-File($share_name, $guest_path, $host_path, $host_share_username, $host_share_password) {
- try {
- # TODO: Check for folder exist.
- # Use net use and prompt for password
- $guest_path = $guest_path.replace("/", "\")
- # Map a network drive to the guest machine
- $result = net use * $host_path /user:$host_share_username $host_share_password /persistent:yes
- $mapped_drive = (($result -match "\w:") -split (" "))[1]
- Write-Host cmd /c mklink /d $guest_path $mapped_drive
- # If a folder exist remove it.
- if (Test-Path $guest_path) {
- $junction = Get-Item $guest_path
- $junction.Delete()
- }
- cmd /c mklink /d $guest_path $mapped_drive
- } catch {
- return $_
- }
- }
-
- $response = Create-Remote-Session $guest_ip $username $password
-
- if (!$response["session"] -and $response["error"]) {
- Write-Error-Message $response["error"]
- return
- }
- $host_path = "\\$host_ip\$share_name"
- $host_share_username = "$host_ip\$host_share_username"
- $result = Invoke-Command -Session $response["session"] -ScriptBlock ${function:Mount-File} -ArgumentList $share_name, $guest_path, $host_path, $host_share_username, $host_share_password -ErrorAction "stop"
- Remove-PSSession -Id $response["session"].Id
- Write-Error-Message $result
-}
-catch {
- Write-Error-Message "Failed to mount files VM $_"
- return
-}
diff --git a/plugins/synced_folders/smb/scripts/ps_version.ps1 b/plugins/synced_folders/smb/scripts/ps_version.ps1
deleted file mode 100644
index 89fbab28e..000000000
--- a/plugins/synced_folders/smb/scripts/ps_version.ps1
+++ /dev/null
@@ -1 +0,0 @@
-Write-Output $PSVersionTable.PSVersion.Major
From d2bc634623a49343f555a79d387b1a3ae57c3352 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Thu, 24 May 2018 16:55:35 -0700
Subject: [PATCH 09/53] Add vm_integration_services validation
---
plugins/providers/hyperv/config.rb | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/plugins/providers/hyperv/config.rb b/plugins/providers/hyperv/config.rb
index 684cf36f1..3a4b2a22c 100644
--- a/plugins/providers/hyperv/config.rb
+++ b/plugins/providers/hyperv/config.rb
@@ -61,14 +61,7 @@ module VagrantPlugins
@auto_stop_action = UNSET_VALUE
@enable_virtualization_extensions = UNSET_VALUE
@enable_checkpoints = UNSET_VALUE
- @vm_integration_services = {
- guest_service_interface: UNSET_VALUE,
- heartbeat: UNSET_VALUE,
- key_value_pair_exchange: UNSET_VALUE,
- shutdown: UNSET_VALUE,
- time_synchronization: UNSET_VALUE,
- vss: UNSET_VALUE
- }
+ @vm_integration_services = {}
end
def finalize!
@@ -94,20 +87,30 @@ module VagrantPlugins
else
@enable_checkpoints = !!@enable_checkpoints
end
- @vm_integration_services.delete_if{|_, v| v == UNSET_VALUE }
- @vm_integration_services = nil if @vm_integration_services.empty?
end
def validate(machine)
errors = _detected_errors
+ if !vm_integration_services.is_a?(Hash)
+ errors << I18n.t("vagrant_hyperv.config.invalid_integration_services_type",
+ received: vm_integration_services.class)
+ else
+ vm_integration_services.each do |key, value|
+ if ![true, false].include?(value)
+ errors << I18n.t("vagrant_hyperv.config.invalid_integration_services_entry",
+ entry_name: name, entry_value: value)
+ end
+ end
+ end
+
if !ALLOWED_AUTO_START_ACTIONS.include?(auto_start_action)
- errors << I18n.t("vagrant.hyperv.config.invalid_auto_start_action", action: auto_start_action,
+ errors << I18n.t("vagrant_hyperv.config.invalid_auto_start_action", action: auto_start_action,
allowed_actions: ALLOWED_AUTO_START_ACTIONS.join(", "))
end
if !ALLOWED_AUTO_STOP_ACTIONS.include?(auto_stop_action)
- errors << I18n.t("vagrant.hyperv.config.invalid_auto_stop_action", action: auto_stop_action,
+ errors << I18n.t("vagrant_hyperv.config.invalid_auto_stop_action", action: auto_stop_action,
allowed_actions: ALLOWED_AUTO_STOP_ACTIONS.join(", "))
end
From b1f0f1566d607004b6a9bd9ad8000d757ede2bb4 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Thu, 24 May 2018 16:56:18 -0700
Subject: [PATCH 10/53] Add new entries for hyper-v config validation errors
---
templates/locales/providers_hyperv.yml | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/templates/locales/providers_hyperv.yml b/templates/locales/providers_hyperv.yml
index d81875297..a8a002fc3 100644
--- a/templates/locales/providers_hyperv.yml
+++ b/templates/locales/providers_hyperv.yml
@@ -11,6 +11,30 @@ en:
message_not_running: |-
Hyper-V machine isn't running. Can't SSH in!
+ config:
+ invalid_auto_start_action: |-
+ The requested auto start action for the Hyper-V VM is not a
+ valid action. Please provide a valid action and run the command
+ again.
+
+ Received: %{action}
+ Allowed: %{allowed_actions}
+ invalid_auto_stop_action: |-
+ The requested auto stop action for the Hyper-V VM is not a
+ valid action. Please provide a valid action and run the command
+ again.
+
+ Received: %{action}
+ Allowed: %{allowed_actions}
+ invalid_integration_services_type: |-
+ Invalid type provided for `vm_integration_services`. Type received
+ is `%{received}` but `Hash` was expected.
+ invalid_integration_services_entry: |-
+ The `%{entry_name}` entry in the `vm_integration_services` is set
+ to an unexpected value.
+
+ Received: %{entry_value}
+ Allowed: true, false
errors:
admin_required: |-
The Hyper-V provider requires that Vagrant be run with
From beacb5bada0fa65478f0eabc1d22336ffead3dd0 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Thu, 24 May 2018 16:56:46 -0700
Subject: [PATCH 11/53] Add test coverage for all hyper-v configuration options
---
.../plugins/providers/hyperv/config_test.rb | 153 +++++++++++++++++-
1 file changed, 152 insertions(+), 1 deletion(-)
diff --git a/test/unit/plugins/providers/hyperv/config_test.rb b/test/unit/plugins/providers/hyperv/config_test.rb
index 467fc7f97..efce526be 100644
--- a/test/unit/plugins/providers/hyperv/config_test.rb
+++ b/test/unit/plugins/providers/hyperv/config_test.rb
@@ -3,6 +3,9 @@ require_relative "../../../base"
require Vagrant.source_root.join("plugins/providers/hyperv/config")
describe VagrantPlugins::HyperV::Config do
+
+ let(:machine){ double("machine") }
+
describe "#ip_address_timeout" do
it "can be set" do
subject.ip_address_timeout = 180
@@ -30,7 +33,7 @@ describe VagrantPlugins::HyperV::Config do
expect(subject.mac).to eq("001122334455")
end
end
-
+
describe "#vmname" do
it "can be set" do
subject.vmname = "test"
@@ -62,4 +65,152 @@ describe VagrantPlugins::HyperV::Config do
expect(subject.cpus).to eq(2)
end
end
+
+ describe "#vmname" do
+ it "can be set" do
+ subject.vmname = "custom"
+ subject.finalize!
+ expect(subject.vmname).to eq("custom")
+ end
+ end
+
+ describe "#differencing_disk" do
+ it "is false by default" do
+ subject.finalize!
+ expect(subject.differencing_disk).to eq(false)
+ end
+
+ it "can be set" do
+ subject.differencing_disk = true
+ subject.finalize!
+ expect(subject.differencing_disk).to eq(true)
+ end
+
+ it "should set linked_clone" do
+ subject.differencing_disk = true
+ subject.finalize!
+ expect(subject.differencing_disk).to eq(true)
+ expect(subject.linked_clone).to eq(true)
+ end
+ end
+
+ describe "#linked_clone" do
+ it "is false by default" do
+ subject.finalize!
+ expect(subject.linked_clone).to eq(false)
+ end
+
+ it "can be set" do
+ subject.linked_clone = true
+ subject.finalize!
+ expect(subject.linked_clone).to eq(true)
+ end
+
+ it "should set differencing_disk" do
+ subject.linked_clone = true
+ subject.finalize!
+ expect(subject.linked_clone).to eq(true)
+ expect(subject.differencing_disk).to eq(true)
+ end
+ end
+
+ describe "#auto_start_action" do
+ it "should be Nothing by default" do
+ subject.finalize!
+ expect(subject.auto_start_action).to eq("Nothing")
+ end
+
+ it "can be set" do
+ subject.auto_start_action = "Start"
+ subject.finalize!
+ expect(subject.auto_start_action).to eq("Start")
+ end
+
+ it "does not accept invalid values" do
+ subject.auto_start_action = "Invalid"
+ subject.finalize!
+ result = subject.validate(machine)
+ expect(result["Hyper-V"]).not_to be_empty
+ end
+ end
+
+ describe "#auto_stop_action" do
+ it "should be ShutDown by default" do
+ subject.finalize!
+ expect(subject.auto_stop_action).to eq("ShutDown")
+ end
+
+ it "can be set" do
+ subject.auto_stop_action = "Save"
+ subject.finalize!
+ expect(subject.auto_stop_action).to eq("Save")
+ end
+
+ it "does not accept invalid values" do
+ subject.auto_stop_action = "Invalid"
+ subject.finalize!
+ result = subject.validate(machine)
+ expect(result["Hyper-V"]).not_to be_empty
+ end
+ end
+
+ describe "#enable_checkpoints" do
+ it "is false by default" do
+ subject.finalize!
+ expect(subject.enable_checkpoints).to eq(false)
+ end
+
+ it "can be set" do
+ subject.enable_checkpoints = true
+ subject.finalize!
+ expect(subject.enable_checkpoints).to eq(true)
+ end
+ end
+
+ describe "#enable_virtualization_extensions" do
+ it "is false by default" do
+ subject.finalize!
+ expect(subject.enable_virtualization_extensions).to eq(false)
+ end
+
+ it "can be set" do
+ subject.enable_virtualization_extensions = true
+ subject.finalize!
+ expect(subject.enable_virtualization_extensions).to eq(true)
+ end
+ end
+
+ describe "#vm_integration_services" do
+ it "is empty by default" do
+ subject.finalize!
+ expect(subject.vm_integration_services).to be_empty
+ end
+
+ it "accepts new entries" do
+ subject.vm_integration_services["entry"] = "value"
+ subject.finalize!
+ expect(subject.vm_integration_services["entry"]).to eq("value")
+ end
+
+ it "does not accept non-Hash types" do
+ subject.vm_integration_services = "value"
+ subject.finalize!
+ result = subject.validate(machine)
+ expect(result["Hyper-V"]).not_to be_empty
+ end
+
+ it "accepts boolean values within Hash" do
+ subject.vm_integration_services["custom"] = true
+ subject.finalize!
+ result = subject.validate(machine)
+ expect(result["Hyper-V"]).to be_empty
+ end
+
+ it "does not accept non-boolean values within Hash" do
+ subject.vm_integration_services["custom"] = "value"
+ subject.finalize!
+ result = subject.validate(machine)
+ expect(result["Hyper-V"]).not_to be_empty
+ end
+ end
end
From 2bd6f537efd61f7069c9b270c4f3c95123e820b0 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 25 May 2018 10:11:05 -0700
Subject: [PATCH 12/53] Clean up syntax and force string type when setting
integration option
---
plugins/providers/hyperv/driver.rb | 34 +++++++++++++++---------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/plugins/providers/hyperv/driver.rb b/plugins/providers/hyperv/driver.rb
index 83c43f3ce..04efa3eb1 100644
--- a/plugins/providers/hyperv/driver.rb
+++ b/plugins/providers/hyperv/driver.rb
@@ -77,14 +77,14 @@ module VagrantPlugins
#
# @return [Hash]
def get_current_state
- execute(:get_vm_status, { VmId: vm_id })
+ execute(:get_vm_status, VmId: vm_id)
end
# Delete the VM
#
# @return [nil]
def delete_vm
- execute(:delete_vm, { VmId: vm_id })
+ execute(:delete_vm, VmId: vm_id)
end
# Export the VM to the given path
@@ -92,49 +92,49 @@ module VagrantPlugins
# @param [String] path Path for export
# @return [nil]
def export(path)
- execute(:export_vm, {VmId: vm_id, Path: path})
+ execute(:export_vm, VmId: vm_id, Path: path)
end
# Get the IP address of the VM
#
# @return [Hash]
def read_guest_ip
- execute(:get_network_config, { VmId: vm_id })
+ execute(:get_network_config, VmId: vm_id)
end
# Get the MAC address of the VM
#
# @return [Hash]
def read_mac_address
- execute(:get_network_mac, { VmId: vm_id })
+ execute(:get_network_mac, VmId: vm_id)
end
# Resume the VM from suspension
#
# @return [nil]
def resume
- execute(:resume_vm, { VmId: vm_id })
+ execute(:resume_vm, VmId: vm_id)
end
# Start the VM
#
# @return [nil]
def start
- execute(:start_vm, { VmId: vm_id })
+ execute(:start_vm, VmId: vm_id )
end
# Stop the VM
#
# @return [nil]
def stop
- execute(:stop_vm, { VmId: vm_id })
+ execute(:stop_vm, VmId: vm_id)
end
# Suspend the VM
#
# @return [nil]
def suspend
- execute(:suspend_vm, { VmId: vm_id })
+ execute(:suspend_vm, VmId: vm_id)
end
# Import a new VM
@@ -150,7 +150,7 @@ module VagrantPlugins
# @param [String] vlan_id VLAN ID
# @return [nil]
def net_set_vlan(vlan_id)
- execute(:set_network_vlan, { VmId: vm_id, VlanId: vlan_id })
+ execute(:set_network_vlan, VmId: vm_id, VlanId: vlan_id)
end
# Set the VM adapter MAC address
@@ -158,7 +158,7 @@ module VagrantPlugins
# @param [String] mac_addr MAC address
# @return [nil]
def net_set_mac(mac_addr)
- execute(:set_network_mac, { VmId: vm_id, Mac: mac_addr })
+ execute(:set_network_mac, VmId: vm_id, Mac: mac_addr)
end
# Create a new snapshot with the given name
@@ -166,7 +166,7 @@ module VagrantPlugins
# @param [String] snapshot_name Name of the new snapshot
# @return [nil]
def create_snapshot(snapshot_name)
- execute(:create_snapshot, { VmId: vm_id, SnapName: (snapshot_name) } )
+ execute(:create_snapshot, VmId: vm_id, SnapName: snapshot_name)
end
# Restore the given snapshot
@@ -174,14 +174,14 @@ module VagrantPlugins
# @param [String] snapshot_name Name of snapshot to restore
# @return [nil]
def restore_snapshot(snapshot_name)
- execute(:restore_snapshot, { VmId: vm_id, SnapName: (snapshot_name) } )
+ execute(:restore_snapshot, VmId: vm_id, SnapName: snapshot_name)
end
# Get list of current snapshots
#
# @return [Array] snapshot names
def list_snapshots
- snaps = execute(:list_snapshots, { VmID: vm_id } )
+ snaps = execute(:list_snapshots, VmID: vm_id)
snaps.map { |s| s['Name'] }
end
@@ -190,7 +190,7 @@ module VagrantPlugins
# @param [String] snapshot_name Name of snapshot to delete
# @return [nil]
def delete_snapshot(snapshot_name)
- execute(:delete_snapshot, {VmID: vm_id, SnapName: snapshot_name})
+ execute(:delete_snapshot, VmID: vm_id, SnapName: snapshot_name)
end
# Enable or disable VM integration services
@@ -203,8 +203,8 @@ module VagrantPlugins
# to configurable even if Vagrant is not aware of them.
def set_vm_integration_services(config)
config.each_pair do |srv_name, srv_enable|
- args = {VMID: vm_id, Name: INTEGRATION_SERVICES_MAP.fetch(srv_name.to_sym, srv_name)}
- args[:Name] = true if srv_enable
+ args = {VMID: vm_id, Name: INTEGRATION_SERVICES_MAP.fetch(srv_name.to_sym, srv_name).to_s}
+ args[:Enable] = true if srv_enable
execute(:set_vm_integration_services, args)
end
end
From 6c731fb86cd7033ab02f0e9a0b868f1b6cc1fdf6 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 25 May 2018 10:11:44 -0700
Subject: [PATCH 13/53] Add test coverage on hyper-v provider driver
---
.../plugins/providers/hyperv/driver_test.rb | 176 ++++++++++++++++++
1 file changed, 176 insertions(+)
create mode 100644 test/unit/plugins/providers/hyperv/driver_test.rb
diff --git a/test/unit/plugins/providers/hyperv/driver_test.rb b/test/unit/plugins/providers/hyperv/driver_test.rb
new file mode 100644
index 000000000..fa99484f0
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/driver_test.rb
@@ -0,0 +1,176 @@
+require_relative "../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/driver")
+
+describe VagrantPlugins::HyperV::Driver do
+ def generate_result(obj)
+ "===Begin-Output===\n" +
+ JSON.dump(obj) +
+ "\n===End-Output==="
+ end
+
+ def generate_error(msg)
+ "===Begin-Error===\n#{JSON.dump(error: msg)}\n===End-Error===\n"
+ end
+
+ let(:result){
+ Vagrant::Util::Subprocess::Result.new(
+ result_exit, result_stdout, result_stderr) }
+ let(:subject){ described_class.new(vm_id) }
+ let(:vm_id){ 1 }
+ let(:result_stdout){ "" }
+ let(:result_stderr){ "" }
+ let(:result_exit){ 0 }
+
+ context "public methods" do
+ before{ allow(subject).to receive(:execute_powershell).and_return(result) }
+
+ describe "#execute" do
+ it "should convert symbol into path string" do
+ expect(subject).to receive(:execute_powershell).with(kind_of(String), any_args)
+ .and_return(result)
+ subject.execute(:thing)
+ end
+
+ it "should append extension when converting symbol" do
+ expect(subject).to receive(:execute_powershell).with("thing.ps1", any_args)
+ .and_return(result)
+ subject.execute(:thing)
+ end
+
+ context "when command returns non-zero exit code" do
+ let(:result_exit){ 1 }
+
+ it "should raise an error" do
+ expect{ subject.execute(:thing) }.to raise_error(VagrantPlugins::HyperV::Errors::PowerShellError)
+ end
+ end
+
+ context "when command stdout matches error pattern" do
+ let(:result_stdout){ generate_error("Error Message") }
+
+ it "should raise an error" do
+ expect{ subject.execute(:thing) }.to raise_error(VagrantPlugins::HyperV::Errors::PowerShellError)
+ end
+ end
+
+ context "with valid JSON output" do
+ let(:result_stdout){ generate_result(:custom => "value") }
+
+ it "should return parsed JSON data" do
+ expect(subject.execute(:thing)).to eq("custom" => "value")
+ end
+ end
+
+ context "with invalid JSON output" do
+ let(:result_stdout){ "value" }
+ it "should return nil" do
+ expect(subject.execute(:thing)).to be_nil
+ end
+ end
+ end
+
+ describe "#has_vmcx_support?" do
+ context "when support is available" do
+ let(:result_stdout){ generate_result(:result => true) }
+
+ it "should be true" do
+ expect(subject.has_vmcx_support?).to eq(true)
+ end
+ end
+
+ context "when support is not available" do
+ let(:result_stdout){ generate_result(:result => false) }
+
+ it "should be false" do
+ expect(subject.has_vmcx_support?).to eq(false)
+ end
+ end
+ end
+
+ describe "#set_vm_integration_services" do
+ it "should map known integration services names automatically" do
+ expect(subject).to receive(:execute) do |name, args|
+ expect(args[:Name]).to eq("Shutdown")
+ end
+ subject.set_vm_integration_services(shutdown: true)
+ end
+
+ it "should set enable when value is true" do
+ expect(subject).to receive(:execute) do |name, args|
+ expect(args[:Enable]).to eq(true)
+ end
+ subject.set_vm_integration_services(shutdown: true)
+ end
+
+ it "should not set enable when value is false" do
+ expect(subject).to receive(:execute) do |name, args|
+ expect(args[:Enable]).to be_nil
+ end
+ subject.set_vm_integration_services(shutdown: false)
+ end
+
+ it "should pass unknown key names directly through" do
+ expect(subject).to receive(:execute) do |name, args|
+ expect(args[:Name]).to eq("CustomKey")
+ end
+ subject.set_vm_integration_services(CustomKey: true)
+ end
+ end
+ end
+
+ describe "#execute_powershell" do
+ before{ allow(Vagrant::Util::PowerShell).to receive(:execute) }
+
+ it "should call the PowerShell module to execute" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute)
+ subject.send(:execute_powershell, "path", {})
+ end
+
+ it "should modify the path separators" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute)
+ .with("\\path\\to\\script.ps1", any_args)
+ subject.send(:execute_powershell, "/path/to/script.ps1", {})
+ end
+
+ it "should include ErrorAction option as Stop" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute) do |path, *args|
+ expect(args).to include("-ErrorAction")
+ expect(args).to include("Stop")
+ end
+ subject.send(:execute_powershell, "path", {})
+ end
+
+ it "should automatically include module path" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute) do |path, *args|
+ opts = args.detect{|i| i.is_a?(Hash)}
+ expect(opts[:env]).not_to be_nil
+ expect(opts[:env]["PSModulePath"]).to include("$env:PSModulePath+")
+ end
+ subject.send(:execute_powershell, "path", {})
+ end
+
+ it "should covert hash options into arguments" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute) do |path, *args|
+ expect(args).to include("-Custom")
+ expect(args).to include("'Value'")
+ end
+ subject.send(:execute_powershell, "path", "Custom" => "Value")
+ end
+
+ it "should treat keys with `true` value as switches" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute) do |path, *args|
+ expect(args).to include("-Custom")
+ expect(args).not_to include("'true'")
+ end
+ subject.send(:execute_powershell, "path", "Custom" => true)
+ end
+
+ it "should not include keys with `false` value" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute) do |path, *args|
+ expect(args).not_to include("-Custom")
+ end
+ subject.send(:execute_powershell, "path", "Custom" => false)
+ end
+ end
+end
From 35d89203256fa7e27ab9566d39ad92544b031726 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 25 May 2018 15:11:58 -0700
Subject: [PATCH 14/53] Case insensitive switch check and only write sentinel
file if not found
---
plugins/providers/hyperv/action/configure.rb | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/plugins/providers/hyperv/action/configure.rb b/plugins/providers/hyperv/action/configure.rb
index f8d1e7d74..30831ac12 100644
--- a/plugins/providers/hyperv/action/configure.rb
+++ b/plugins/providers/hyperv/action/configure.rb
@@ -24,8 +24,8 @@ module VagrantPlugins
if opts[:bridge]
@logger.debug("Looking for switch with name or ID: #{opts[:bridge]}")
switch = switches.find{ |s|
- s["Name"].downcase == opts[:bridge] ||
- s["Id"].downcase == opts[:bridge]
+ s["Name"].downcase == opts[:bridge].to_s.downcase ||
+ s["Id"].downcase == opts[:bridge].to_s.downcase
}
if switch
@logger.debug("Found switch - Name: #{switch["Name"]} ID: #{switch["Id"]}")
@@ -78,11 +78,13 @@ module VagrantPlugins
env[:machine].provider.driver.execute(:configure_vm, options)
# Create the sentinel
- sentinel.open("w") do |f|
- f.write(Time.now.to_i.to_s)
+ if !sentinel.file?
+ sentinel.open("w") do |f|
+ f.write(Time.now.to_i.to_s)
+ end
end
- if env[:machine].provider_config.vm_integration_services
+ if !env[:machine].provider_config.vm_integration_services.empty?
env[:ui].detail("Setting VM Integration Services")
env[:machine].provider_config.vm_integration_services.each do |key, value|
From d24b43227300ed38e652fd58dcfb628cce89fd9f Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 25 May 2018 15:12:39 -0700
Subject: [PATCH 15/53] Add Hyper-V provider actions test coverage
---
.../hyperv/action/check_enabled_test.rb | 27 ++++
.../providers/hyperv/action/configure_test.rb | 125 +++++++++++++++++
.../providers/hyperv/action/export_test.rb | 42 ++++++
.../providers/hyperv/action/import_test.rb | 130 ++++++++++++++++++
4 files changed, 324 insertions(+)
create mode 100644 test/unit/plugins/providers/hyperv/action/check_enabled_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/configure_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/export_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/import_test.rb
diff --git a/test/unit/plugins/providers/hyperv/action/check_enabled_test.rb b/test/unit/plugins/providers/hyperv/action/check_enabled_test.rb
new file mode 100644
index 000000000..288d61b1f
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/check_enabled_test.rb
@@ -0,0 +1,27 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/check_enabled")
+
+describe VagrantPlugins::HyperV::Action::CheckEnabled do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider) }
+ let(:subject){ described_class.new(app, env) }
+
+ before{ allow(ui).to receive(:output) }
+
+ it "should continue when Hyper-V is enabled" do
+ expect(driver).to receive(:execute).and_return("result" => true)
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should raise error when Hyper-V is not enabled" do
+ expect(driver).to receive(:execute).and_return("result" => false)
+ expect(app).not_to receive(:call)
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::HyperV::Errors::PowerShellFeaturesDisabled)
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/configure_test.rb b/test/unit/plugins/providers/hyperv/action/configure_test.rb
new file mode 100644
index 000000000..9b2d8222b
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/configure_test.rb
@@ -0,0 +1,125 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/configure")
+
+describe VagrantPlugins::HyperV::Action::Configure do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, config: config, provider_config: provider_config, data_dir: data_dir, id: "machineID") }
+ let(:data_dir){ double("data_dir") }
+ let(:config){ double("config", vm: vm) }
+ let(:vm){ double("vm", networks: networks) }
+ let(:networks){ [] }
+ let(:switches){ [
+ {"Name" => "Switch1", "Id" => "ID1"},
+ {"Name" => "Switch2", "Id" => "ID2"}
+ ]}
+ let(:sentinel){ double("sentinel") }
+ let(:provider_config){
+ double("provider_config",
+ memory: "1024",
+ maxmemory: "1024",
+ cpus: 1,
+ auto_start_action: "Nothing",
+ auto_stop_action: "Save",
+ enable_checkpoints: false,
+ enable_virtualization_extensions: false,
+ vm_integration_services: vm_integration_services
+ )
+ }
+ let(:vm_integration_services){ {} }
+
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(driver).to receive(:execute)
+ allow(app).to receive(:call)
+ expect(driver).to receive(:execute).with(:get_switches).and_return(switches)
+ allow(ui).to receive(:detail)
+ allow(ui).to receive(:output)
+ allow(ui).to receive(:ask).and_return("1")
+ allow(data_dir).to receive(:join).and_return(sentinel)
+ allow(sentinel).to receive(:file?).and_return(false)
+ allow(sentinel).to receive(:open)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ context "with missing switch sentinel file" do
+ it "should prompt for switch to use" do
+ expect(ui).to receive(:ask)
+ subject.call(env)
+ end
+
+ it "should write sentinel file" do
+ expect(sentinel).to receive(:open)
+ subject.call(env)
+ end
+ end
+
+ context "with existing switch sentinel file" do
+ before{ allow(sentinel).to receive(:file?).twice.and_return(true) }
+
+ it "should not prompt for switch to use" do
+ expect(ui).not_to receive(:ask)
+ subject.call(env)
+ end
+
+ it "should not write sentinel file" do
+ expect(sentinel).not_to receive(:open)
+ subject.call(env)
+ end
+ end
+
+ context "with bridge defined in networks" do
+ context "with valid bridge switch name" do
+ let(:networks){ [[:public_network, {bridge: "Switch1"}]] }
+
+ it "should not prompt for switch" do
+ expect(ui).not_to receive(:ask)
+ subject.call(env)
+ end
+ end
+
+ context "with valid bridge switch ID" do
+ let(:networks){ [[:public_network, {bridge: "ID1"}]] }
+
+ it "should not prompt for switch" do
+ expect(ui).not_to receive(:ask)
+ subject.call(env)
+ end
+ end
+
+ context "with invalid bridge switch name" do
+ let(:networks){ [[:public_network, {bridge: "UNKNOWN"}]] }
+
+ it "should prompt for switch" do
+ expect(ui).to receive(:ask)
+ subject.call(env)
+ end
+ end
+ end
+
+ context "with integration services enabled" do
+ let(:vm_integration_services){ {service: true} }
+
+ it "should call the driver to set the services" do
+ expect(driver).to receive(:set_vm_integration_services)
+ subject.call(env)
+ end
+ end
+
+ context "without available switches" do
+ let(:switches){ [] }
+
+ it "should raise an error" do
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::HyperV::Errors::NoSwitches)
+ end
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/export_test.rb b/test/unit/plugins/providers/hyperv/action/export_test.rb
new file mode 100644
index 000000000..257225219
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/export_test.rb
@@ -0,0 +1,42 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/export")
+
+describe VagrantPlugins::HyperV::Action::Export do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, state: state) }
+ let(:state){ double("state", id: machine_state) }
+ let(:machine_state){ :off }
+
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(app).to receive(:call)
+ allow(ui).to receive(:info)
+ allow(ui).to receive(:clear_line)
+ allow(ui).to receive(:report_progress)
+ allow(driver).to receive(:export)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should call the driver to perform the export" do
+ expect(driver).to receive(:export)
+ subject.call(env)
+ end
+
+ context "with invalid machine state" do
+ let(:machine_state){ :on }
+
+ it "should raise an error" do
+ expect{ subject.call(env) }.to raise_error(Vagrant::Errors::VMPowerOffToPackage)
+ end
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/import_test.rb b/test/unit/plugins/providers/hyperv/action/import_test.rb
new file mode 100644
index 000000000..32c68695c
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/import_test.rb
@@ -0,0 +1,130 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/import")
+
+describe VagrantPlugins::HyperV::Action::Import do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, provider_config: provider_config, box: box, data_dir: data_dir) }
+ let(:provider_config){
+ double("provider_config",
+ linked_clone: false,
+ vmname: "VMNAME"
+ )
+ }
+ let(:box){ double("box", directory: box_directory) }
+ let(:box_directory){ double("box_directory") }
+ let(:data_dir){ double("data_dir") }
+ let(:vm_dir){ double("vm_dir") }
+ let(:hd_dir){ double("hd_dir") }
+
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(app).to receive(:call)
+ allow(box_directory).to receive(:join).with("Virtual Machines").and_return(vm_dir)
+ allow(box_directory).to receive(:join).with("Virtual Hard Disks").and_return(hd_dir)
+ allow(vm_dir).to receive(:directory?).and_return(true)
+ allow(vm_dir).to receive(:each_child).and_yield(Pathname.new("file.txt"))
+ allow(hd_dir).to receive(:directory?).and_return(true)
+ allow(hd_dir).to receive(:each_child).and_yield(Pathname.new("file.txt"))
+ allow(driver).to receive(:has_vmcx_support?).and_return(true)
+ allow(data_dir).to receive(:join).and_return(data_dir)
+ allow(data_dir).to receive(:to_s).and_return("DATA_DIR_PATH")
+ allow(driver).to receive(:import).and_return("id" => "VMID")
+ allow(machine).to receive(:id=)
+ allow(ui).to receive(:output)
+ allow(ui).to receive(:detail)
+ end
+
+ context "with missing virtual machines directory" do
+ before{ expect(vm_dir).to receive(:directory?).and_return(false) }
+
+ it "should raise an error" do
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::HyperV::Errors::BoxInvalid)
+ end
+ end
+
+ context "with missing hard disks directory" do
+ before{ expect(hd_dir).to receive(:directory?).and_return(false) }
+
+ it "should raise an error" do
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::HyperV::Errors::BoxInvalid)
+ end
+ end
+
+ context "with missing configuration file" do
+ before do
+ allow(hd_dir).to receive(:each_child).and_yield(Pathname.new("image.vhd"))
+ end
+
+ it "should raise an error" do
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::HyperV::Errors::BoxInvalid)
+ end
+ end
+
+ context "with missing image file" do
+ before do
+ allow(vm_dir).to receive(:each_child).and_yield(Pathname.new("config.xml"))
+ end
+
+ it "should raise an error" do
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::HyperV::Errors::BoxInvalid)
+ end
+ end
+
+ context "with image and config files" do
+ before do
+ allow(vm_dir).to receive(:each_child).and_yield(Pathname.new("config.xml"))
+ allow(hd_dir).to receive(:each_child).and_yield(Pathname.new("image.vhd"))
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should request import via the driver" do
+ expect(driver).to receive(:import).and_return("id" => "VMID")
+ subject.call(env)
+ end
+
+ it "should set the machine ID after import" do
+ expect(machine).to receive(:id=).with("VMID")
+ subject.call(env)
+ end
+
+ context "with no vmcx support" do
+ before do
+ expect(driver).to receive(:has_vmcx_support?).and_return(false)
+ end
+
+ it "should match XML config file" do
+ subject.call(env)
+ end
+
+ it "should not match VMCX config file" do
+ expect(vm_dir).to receive(:each_child).and_yield(Pathname.new("config.vmcx"))
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::HyperV::Errors::BoxInvalid)
+ end
+ end
+
+ context "with vmcx support" do
+ before do
+ expect(driver).to receive(:has_vmcx_support?).and_return(true)
+ end
+
+ it "should match XML config file" do
+ subject.call(env)
+ end
+
+ it "should match VMCX config file" do
+ expect(vm_dir).to receive(:each_child).and_yield(Pathname.new("config.vmcx"))
+ subject.call(env)
+ end
+ end
+ end
+end
From eba552ea73135fa460c73c7c97b1152573504918 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 25 May 2018 16:23:45 -0700
Subject: [PATCH 16/53] Add more test coverage on Hyper-V Provider actions
---
.../providers/hyperv/action/delete_vm_test.rb | 29 +++++++++
.../hyperv/action/is_windows_test.rb | 37 +++++++++++
.../hyperv/action/net_set_mac_test.rb | 40 ++++++++++++
.../hyperv/action/net_set_vlan_test.rb | 40 ++++++++++++
.../hyperv/action/read_guest_ip_test.rb | 39 ++++++++++++
.../hyperv/action/read_state_test.rb | 56 +++++++++++++++++
.../providers/hyperv/action/set_name_test.rb | 61 +++++++++++++++++++
.../hyperv/action/wait_for_ip_address_test.rb | 40 ++++++++++++
8 files changed, 342 insertions(+)
create mode 100644 test/unit/plugins/providers/hyperv/action/delete_vm_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/is_windows_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/net_set_mac_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/net_set_vlan_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/read_guest_ip_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/read_state_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/set_name_test.rb
create mode 100644 test/unit/plugins/providers/hyperv/action/wait_for_ip_address_test.rb
diff --git a/test/unit/plugins/providers/hyperv/action/delete_vm_test.rb b/test/unit/plugins/providers/hyperv/action/delete_vm_test.rb
new file mode 100644
index 000000000..dda24c90c
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/delete_vm_test.rb
@@ -0,0 +1,29 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/delete_vm")
+
+describe VagrantPlugins::HyperV::Action::DeleteVM do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider) }
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(app).to receive(:call)
+ allow(ui).to receive(:info)
+ allow(driver).to receive(:delete_vm)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should call the driver to delete the vm" do
+ expect(driver).to receive(:delete_vm)
+ subject.call(env)
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/is_windows_test.rb b/test/unit/plugins/providers/hyperv/action/is_windows_test.rb
new file mode 100644
index 000000000..6ed9aaf68
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/is_windows_test.rb
@@ -0,0 +1,37 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/is_windows")
+
+describe VagrantPlugins::HyperV::Action::IsWindows do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, config: config) }
+ let(:config){ double("config", vm: vm) }
+ let(:vm){ double("vm", guest: :windows) }
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(app).to receive(:call)
+ allow(env).to receive(:[]=)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should update the env with the result" do
+ expect(env).to receive(:[]=).with(:result, true)
+ subject.call(env)
+ end
+
+ it "should set the result to false when not windows" do
+ expect(vm).to receive(:guest).and_return(:linux)
+ expect(env).to receive(:[]=).with(:result, false)
+ subject.call(env)
+ end
+
+end
diff --git a/test/unit/plugins/providers/hyperv/action/net_set_mac_test.rb b/test/unit/plugins/providers/hyperv/action/net_set_mac_test.rb
new file mode 100644
index 000000000..01bc3d6ef
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/net_set_mac_test.rb
@@ -0,0 +1,40 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/net_set_mac")
+
+describe VagrantPlugins::HyperV::Action::NetSetMac do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, provider_config: provider_config) }
+ let(:provider_config){ double("provider_config", mac: mac) }
+ let(:mac){ "ADDRESS" }
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(ui).to receive(:info)
+ allow(driver).to receive(:net_set_mac)
+ allow(app).to receive(:call)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should call the driver to set the MAC address" do
+ expect(driver).to receive(:net_set_mac).with(mac)
+ subject.call(env)
+ end
+
+ context "with no MAC address provided" do
+ let(:mac){ nil }
+
+ it "should not call driver to set the MAC address" do
+ expect(driver).not_to receive(:net_set_mac)
+ subject.call(env)
+ end
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/net_set_vlan_test.rb b/test/unit/plugins/providers/hyperv/action/net_set_vlan_test.rb
new file mode 100644
index 000000000..c8b128d73
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/net_set_vlan_test.rb
@@ -0,0 +1,40 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/net_set_vlan")
+
+describe VagrantPlugins::HyperV::Action::NetSetVLan do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, provider_config: provider_config) }
+ let(:provider_config){ double("provider_config", vlan_id: vlan_id) }
+ let(:vlan_id){ "VID" }
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(ui).to receive(:info)
+ allow(driver).to receive(:net_set_vlan)
+ allow(app).to receive(:call)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should call the driver to set the vlan id" do
+ expect(driver).to receive(:net_set_vlan).with(vlan_id)
+ subject.call(env)
+ end
+
+ context "with no vlan id provided" do
+ let(:vlan_id){ nil }
+
+ it "should not call driver to set the vlan id" do
+ expect(driver).not_to receive(:net_set_vlan)
+ subject.call(env)
+ end
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/read_guest_ip_test.rb b/test/unit/plugins/providers/hyperv/action/read_guest_ip_test.rb
new file mode 100644
index 000000000..1ec91639a
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/read_guest_ip_test.rb
@@ -0,0 +1,39 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/read_guest_ip")
+
+describe VagrantPlugins::HyperV::Action::ReadGuestIP do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider) }
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(app).to receive(:call)
+ allow(env).to receive(:[]=)
+ allow(machine).to receive(:id)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ context "with machine ID set" do
+ before{ allow(machine).to receive(:id).and_return("VMID") }
+
+ it "should request guest IP from the driver" do
+ expect(driver).to receive(:read_guest_ip).and_return("ip" => "ADDRESS")
+ subject.call(env)
+ end
+
+ it "should set the host information into the env" do
+ expect(env).to receive(:[]=).with(:machine_ssh_info, host: "ADDRESS")
+ expect(driver).to receive(:read_guest_ip).and_return("ip" => "ADDRESS")
+ subject.call(env)
+ end
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/read_state_test.rb b/test/unit/plugins/providers/hyperv/action/read_state_test.rb
new file mode 100644
index 000000000..50602f67d
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/read_state_test.rb
@@ -0,0 +1,56 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/read_state")
+
+describe VagrantPlugins::HyperV::Action::ReadState do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine, machine_state_id: state_id} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider) }
+ let(:state_id){ nil }
+
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(app).to receive(:call)
+ allow(env).to receive(:[]=)
+ allow(machine).to receive(:id)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should set machine state into the env as not created" do
+ expect(env).to receive(:[]=).with(:machine_state_id, :not_created)
+ subject.call(env)
+ end
+
+ context "with machine ID set" do
+ before{ allow(machine).to receive(:id).and_return("VMID") }
+
+ it "should request machine state from the driver" do
+ expect(driver).to receive(:get_current_state).and_return("state" => "running")
+ subject.call(env)
+ end
+
+ it "should set machine state into the env" do
+ expect(driver).to receive(:get_current_state).and_return("state" => "running")
+ expect(env).to receive(:[]=).with(:machine_state_id, :running)
+ subject.call(env)
+ end
+
+ context "with machine state ID as not_created" do
+ let(:state_id){ :not_created }
+
+ it "should clear the machine ID" do
+ expect(driver).to receive(:get_current_state).and_return("state" => "not_created")
+ expect(machine).to receive(:id=).with(nil)
+ subject.call(env)
+ end
+ end
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/set_name_test.rb b/test/unit/plugins/providers/hyperv/action/set_name_test.rb
new file mode 100644
index 000000000..9708140e8
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/set_name_test.rb
@@ -0,0 +1,61 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/set_name")
+
+describe VagrantPlugins::HyperV::Action::SetName do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine, root_path: Pathname.new("path")} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, provider_config: provider_config, data_dir: data_dir, name: "machname") }
+ let(:data_dir){ double("data_dir") }
+ let(:provider_config){ double("provider_config", vmname: vmname) }
+ let(:vmname){ "VMNAME" }
+ let(:sentinel){ double("sentinel") }
+
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(ui).to receive(:info)
+ allow(driver).to receive(:set_name)
+ allow(app).to receive(:call)
+ allow(data_dir).to receive(:join).and_return(sentinel)
+ allow(sentinel).to receive(:file?).and_return(false)
+ allow(sentinel).to receive(:open)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should call the driver to set the name" do
+ expect(driver).to receive(:set_name)
+ subject.call(env)
+ end
+
+ it "should use the configured name when setting" do
+ expect(driver).to receive(:set_name).with(vmname)
+ subject.call(env)
+ end
+
+ it "should write sentinel after name is set" do
+ expect(sentinel).to receive(:open)
+ subject.call(env)
+ end
+
+ context "when no name is provided in the config" do
+ let(:vmname){ nil }
+
+ it "should generate a name based on path and machine" do
+ expect(driver).to receive(:set_name).with(/^#{env[:root_path].to_s}_#{machine.name}_.+/)
+ subject.call(env)
+ end
+
+ it "should not set name if sentinel exists" do
+ expect(sentinel).to receive(:file?).and_return(true)
+ subject.call(env)
+ end
+ end
+end
diff --git a/test/unit/plugins/providers/hyperv/action/wait_for_ip_address_test.rb b/test/unit/plugins/providers/hyperv/action/wait_for_ip_address_test.rb
new file mode 100644
index 000000000..4d5173236
--- /dev/null
+++ b/test/unit/plugins/providers/hyperv/action/wait_for_ip_address_test.rb
@@ -0,0 +1,40 @@
+require_relative "../../../../base"
+
+require Vagrant.source_root.join("plugins/providers/hyperv/action/wait_for_ip_address")
+
+describe VagrantPlugins::HyperV::Action::WaitForIPAddress do
+ let(:app){ double("app") }
+ let(:env){ {ui: ui, machine: machine} }
+ let(:ui){ double("ui") }
+ let(:provider){ double("provider", driver: driver) }
+ let(:driver){ double("driver") }
+ let(:machine){ double("machine", provider: provider, provider_config: provider_config) }
+ let(:provider_config){ double("provider_config", ip_address_timeout: ip_address_timeout) }
+ let(:ip_address_timeout){ 1 }
+
+ let(:subject){ described_class.new(app, env) }
+
+ before do
+ allow(ui).to receive(:output)
+ allow(ui).to receive(:detail)
+ allow(driver).to receive(:read_guest_ip).and_return("ip" => "127.0.0.1")
+ allow(app).to receive(:call)
+ end
+
+ it "should call the app on success" do
+ expect(app).to receive(:call)
+ subject.call(env)
+ end
+
+ it "should set a timeout for waiting" do
+ expect(Timeout).to receive(:timeout).with(ip_address_timeout)
+ subject.call(env)
+ end
+
+ it "should retry until it receives a valid address" do
+ expect(driver).to receive(:read_guest_ip).and_return("ip" => "ADDRESS")
+ expect(driver).to receive(:read_guest_ip).and_return("ip" => "127.0.0.1")
+ expect(subject).to receive(:sleep)
+ subject.call(env)
+ end
+end
From c238dc0a3541be82f4ef406068f225efcd83417b Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 25 May 2018 16:43:17 -0700
Subject: [PATCH 17/53] Include machine name when raising invalid box errors
---
plugins/providers/hyperv/action/import.rb | 6 +++---
test/unit/plugins/providers/hyperv/action/import_test.rb | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb
index bbe19e0c3..25229e3d0 100644
--- a/plugins/providers/hyperv/action/import.rb
+++ b/plugins/providers/hyperv/action/import.rb
@@ -19,7 +19,7 @@ module VagrantPlugins
if !vm_dir.directory? || !hd_dir.directory?
@logger.error("Required virtual machine directory not found!")
- raise Errors::BoxInvalid
+ raise Errors::BoxInvalid, name: env[:machine].name
end
valid_config_ext = [".xml"]
@@ -37,7 +37,7 @@ module VagrantPlugins
if !config_path
@logger.error("Failed to locate box configuration path")
- raise Errors::BoxInvalid
+ raise Errors::BoxInvalid, name: env[:machine].name
else
@logger.info("Found box configuration path: #{config_path}")
end
@@ -52,7 +52,7 @@ module VagrantPlugins
if !image_path
@logger.error("Failed to locate box image path")
- raise Errors::BoxInvalid
+ raise Errors::BoxInvalid, name: env[:machine].name
else
@logger.info("Found box image path: #{image_path}")
end
diff --git a/test/unit/plugins/providers/hyperv/action/import_test.rb b/test/unit/plugins/providers/hyperv/action/import_test.rb
index 32c68695c..9c66bb0c2 100644
--- a/test/unit/plugins/providers/hyperv/action/import_test.rb
+++ b/test/unit/plugins/providers/hyperv/action/import_test.rb
@@ -8,7 +8,7 @@ describe VagrantPlugins::HyperV::Action::Import do
let(:ui){ double("ui") }
let(:provider){ double("provider", driver: driver) }
let(:driver){ double("driver") }
- let(:machine){ double("machine", provider: provider, provider_config: provider_config, box: box, data_dir: data_dir) }
+ let(:machine){ double("machine", provider: provider, provider_config: provider_config, box: box, data_dir: data_dir, name: "machname") }
let(:provider_config){
double("provider_config",
linked_clone: false,
From 73c09de1a99772b970be4850cdba3e77d5db1809 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 29 May 2018 10:21:06 -0700
Subject: [PATCH 18/53] Add deprecation warning when `differencing_disk` option
is used
---
plugins/providers/hyperv/config.rb | 7 +++++++
templates/locales/providers_hyperv.yml | 4 ++++
2 files changed, 11 insertions(+)
diff --git a/plugins/providers/hyperv/config.rb b/plugins/providers/hyperv/config.rb
index 3a4b2a22c..3403ef682 100644
--- a/plugins/providers/hyperv/config.rb
+++ b/plugins/providers/hyperv/config.rb
@@ -65,6 +65,9 @@ module VagrantPlugins
end
def finalize!
+ if @differencing_disk != UNSET_VALUE
+ @_differencing_disk_deprecation = true
+ end
@linked_clone = false if @linked_clone == UNSET_VALUE
@differencing_disk = false if @differencing_disk == UNSET_VALUE
@linked_clone ||= @differencing_disk
@@ -92,6 +95,10 @@ module VagrantPlugins
def validate(machine)
errors = _detected_errors
+ if @_differencing_disk_deprecation && machine
+ machine.ui.warn I18n.t("vagrant_hyperv.config.differencing_disk_deprecation")
+ end
+
if !vm_integration_services.is_a?(Hash)
errors << I18n.t("vagrant_hyperv.config.invalid_integration_services_type",
received: vm_integration_services.class)
diff --git a/templates/locales/providers_hyperv.yml b/templates/locales/providers_hyperv.yml
index a8a002fc3..d4deb6153 100644
--- a/templates/locales/providers_hyperv.yml
+++ b/templates/locales/providers_hyperv.yml
@@ -35,6 +35,10 @@ en:
Received: %{entry_value}
Allowed: true, false
+ differencing_disk_deprecation: |-
+ The `differencing_disk` configuration option is deprecated and should
+ no longer be used. The `linked_clone` configuration option should
+ be used instead.
errors:
admin_required: |-
The Hyper-V provider requires that Vagrant be run with
From 12194cba5add08e62db1457e878d65d6fe79f8ec Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 29 May 2018 10:21:52 -0700
Subject: [PATCH 19/53] Update Hyper-V provider configuration documentation
---
.../source/docs/hyperv/configuration.html.md | 80 ++++++++++---------
1 file changed, 42 insertions(+), 38 deletions(-)
diff --git a/website/source/docs/hyperv/configuration.html.md b/website/source/docs/hyperv/configuration.html.md
index 2fed569e9..224964eb1 100644
--- a/website/source/docs/hyperv/configuration.html.md
+++ b/website/source/docs/hyperv/configuration.html.md
@@ -12,41 +12,45 @@ description: |-
The Vagrant Hyper-V provider has some provider-specific configuration options
you may set. A complete reference is shown below:
- * `vmname` (string) - Name of virtual machine as shown in Hyper-V manager.
- Defaults is taken from box image XML.
- * `cpus` (integer) - Number of virtual CPU given to machine.
- Defaults is taken from box image XML.
- * `memory` (integer) - Number of MegaBytes allocated to VM at startup.
- Defaults is taken from box image XML.
- * `maxmemory` (integer) - Number of MegaBytes maximal allowed to allocate for VM
- This parameter is switch on Dynamic Allocation of memory.
- Defaults is taken from box image XML.
- * `vlan_id` (integer) - Number of Vlan ID for your guest network interface
- Defaults is not defined, vlan configuration will be untouched if not set.
- * `mac` (string) - MAC address for your guest network interface
- Default is not defined, MAC address will be dynamically assigned by Hyper-V if not set.
- * `ip_address_timeout` (integer) - The time in seconds to wait for the
- virtual machine to report an IP address. This defaults to 120 seconds.
- This may have to be increased if your VM takes longer to boot.
- * `differencing_disk` (boolean) - Switch to use differencing disk instead of cloning whole VHD.
- * `enable_virtualization_extensions` (boolean) - Enable virtualization extensions for the virtual CPUs.
- This allows Hyper-V to be nested and run inside another Hyper-VM VM. It requires Windows 10 - 1511 (build 10586) or newer.
- Default is not defined. This will be disabled if not set.
- * `auto_start_action` (Nothing, StartIfRunning, Start) - Action on automatic start of VM when booting OS
- * `auto_stop_action` (ShutDown, TurnOff, Save) - Action on automatic stop of VM when shutting down OS.
- * `vm_integration_services` (Hash) - Hash to set the state of integration services.
-
- Example:
-
- ```ruby
- config.vm.provider "hyperv" do |h|
- h.vm_integration_services = {
- guest_service_interface: true,
- heartbeat: true,
- key_value_pair_exchange: true,
- shutdown: true,
- time_synchronization: true,
- vss: true
- }
- end
- ```
\ No newline at end of file
+* `auto_start_action` (Nothing, StartIfRunning, Start) - Automatic start action for VM on host startup. Default: Nothing.
+* `auto_stop_action` (ShutDown, TurnOff, Save) - Automatic stop action for VM on host shutdown. Default: ShutDown.
+* `cpus` (integer) - Number of virtual CPUs allocated to VM at startup.
+* `differencing_disk` (boolean) - **Deprecated** Use differencing disk instead of cloning entire VHD (use `linked_clone` instead) Default: false.
+* `enable_virtualization_extensions` (boolean) - Enable virtualization extensions for the virtual CPUs. Default: false
+* `enable_checkpoints` (boolean) Enable automatic checkpoints of the VM. Default: false
+* `ip_address_timeout` (integer) - Number of seconds to wait for the VM to report an IP address. Default: 120.
+* `linked_clone` (boolean) - **Deprecated** Use differencing disk instead of cloning entire VHD. Default: false
+* `mac` (string) - MAC address for the guest network interface
+* `maxmemory` (integer) - Maximum number of megabytes allowed to be allocated for the VM. When set Dynamic Memory Allocation will be enabled.
+* `memory` (integer) - Number of megabytes allocated to VM at startup. If `maxmemory` is set, this will be amount of memory allocated at startup.
+* `vlan_id` (integer) - VLAN ID for the guest network interface.
+* `vmname` (string) - Name of virtual machine as shown in Hyper-V manager. Default: Generated name.
+* `vm_integration_services` (Hash) - Hash to set the state of integration services. (Note: Unknown key values will be passed directly.)
+ * `guest_service_interface` (boolean)
+ * `heartbeat` (boolean)
+ * `key_value_pair_exchange` (boolean)
+ * `shutdown` (boolean)
+ * `time_synchronization` (boolean)
+ * `vss` (boolean)
+
+## VM Integration Services
+
+The `vm_integration_services` configuration option consists of a simple Hash. The key values are the
+names of VM integration services to enable or disable for the VM. Vagrant includes an internal
+mapping of known services which allows them to be provided in a "snake case" format. When a provided
+key is unknown, the key value is used "as-is" without any modifications.
+
+For example, if a new `CustomVMSRV` VM integration service was added and Vagrant is not aware of this
+new service name, it can be provided as the key value explicitly:
+
+```ruby
+config.vm.provider "hyperv" do |h|
+ h.vm_integration_services = {
+ guest_service_interface: true,
+ CustomVMSRV: true
+ }
+end
+```
+
+This example would enable the `GuestServiceInterface` (which Vagrant is aware) and `CustomVMSRV` (which
+Vagrant is _not_ aware) VM integration services.
From 1becae50a5f680934d9a519d4ab43ca6f9534778 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 29 May 2018 10:25:48 -0700
Subject: [PATCH 20/53] Include test coverage on provider option deprecation
---
test/unit/plugins/providers/hyperv/config_test.rb | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/test/unit/plugins/providers/hyperv/config_test.rb b/test/unit/plugins/providers/hyperv/config_test.rb
index efce526be..23328cf3b 100644
--- a/test/unit/plugins/providers/hyperv/config_test.rb
+++ b/test/unit/plugins/providers/hyperv/config_test.rb
@@ -4,7 +4,8 @@ require Vagrant.source_root.join("plugins/providers/hyperv/config")
describe VagrantPlugins::HyperV::Config do
- let(:machine){ double("machine") }
+ let(:machine){ double("machine", ui: ui) }
+ let(:ui){ double("ui") }
describe "#ip_address_timeout" do
it "can be set" do
@@ -92,6 +93,13 @@ describe VagrantPlugins::HyperV::Config do
expect(subject.differencing_disk).to eq(true)
expect(subject.linked_clone).to eq(true)
end
+
+ it "should provide a deprecation warning when set" do
+ expect(ui).to receive(:warn)
+ subject.differencing_disk = true
+ subject.finalize!
+ subject.validate(machine)
+ end
end
describe "#linked_clone" do
From 4e09a15acd1fa329539a7e2728337e2d4b1d78d4 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 29 May 2018 11:43:13 -0700
Subject: [PATCH 21/53] Remove deprecated warning from non-deprecated option
---
website/source/docs/hyperv/configuration.html.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/source/docs/hyperv/configuration.html.md b/website/source/docs/hyperv/configuration.html.md
index 224964eb1..178fadf10 100644
--- a/website/source/docs/hyperv/configuration.html.md
+++ b/website/source/docs/hyperv/configuration.html.md
@@ -19,7 +19,7 @@ you may set. A complete reference is shown below:
* `enable_virtualization_extensions` (boolean) - Enable virtualization extensions for the virtual CPUs. Default: false
* `enable_checkpoints` (boolean) Enable automatic checkpoints of the VM. Default: false
* `ip_address_timeout` (integer) - Number of seconds to wait for the VM to report an IP address. Default: 120.
-* `linked_clone` (boolean) - **Deprecated** Use differencing disk instead of cloning entire VHD. Default: false
+* `linked_clone` (boolean) - Use differencing disk instead of cloning entire VHD. Default: false
* `mac` (string) - MAC address for the guest network interface
* `maxmemory` (integer) - Maximum number of megabytes allowed to be allocated for the VM. When set Dynamic Memory Allocation will be enabled.
* `memory` (integer) - Number of megabytes allocated to VM at startup. If `maxmemory` is set, this will be amount of memory allocated at startup.
From 0cd2ea654885dab59b0fa3f3006e34f178d733aa Mon Sep 17 00:00:00 2001
From: Hendy Irawan
Date: Wed, 30 May 2018 17:11:14 +0700
Subject: [PATCH 22/53] Remove .
The command should be stand-alone, not part of a "sentence":
```
Enable-WindowsOptionalFeature : A positional parameter cannot be found that accepts argument '.'.
At line:1 char:1
+ Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Enable-WindowsOptionalFeature], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.Dism.Commands.EnableWindowsOptionalFeatureCommand
```
---
website/source/docs/hyperv/index.html.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/source/docs/hyperv/index.html.md b/website/source/docs/hyperv/index.html.md
index ff860ef77..95706f377 100644
--- a/website/source/docs/hyperv/index.html.md
+++ b/website/source/docs/hyperv/index.html.md
@@ -24,7 +24,7 @@ To enable Hyper-V, go to "Programs and Features", click on "Turn Windows
features on or off" and check the box next to "Hyper-V". Or install via
PowerShell with:
-Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
.
+Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
See official documentation [here](https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v).
From 5f4e661155730f0be708cdabb4e70b76005c456d Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Wed, 30 May 2018 15:04:59 -0700
Subject: [PATCH 23/53] Add automatic console resize and environment support
for powershell commands
---
lib/vagrant/util/powershell.rb | 49 +++++++++++++++++++++++++++-------
1 file changed, 40 insertions(+), 9 deletions(-)
diff --git a/lib/vagrant/util/powershell.rb b/lib/vagrant/util/powershell.rb
index 9afb4da65..8e48aad1d 100644
--- a/lib/vagrant/util/powershell.rb
+++ b/lib/vagrant/util/powershell.rb
@@ -20,12 +20,16 @@ module Vagrant
if !defined?(@_powershell_executable)
@_powershell_executable = "powershell"
- # Try to use WSL interoperability if PowerShell is not symlinked to
- # the container.
- if Which.which(@_powershell_executable).nil? && Platform.wsl?
- @_powershell_executable += ".exe"
+ if Which.which(@_powershell_executable).nil?
+ # Try to use WSL interoperability if PowerShell is not symlinked to
+ # the container.
+ if Platform.wsl?
+ @_powershell_executable += ".exe"
- if Which.which(@_powershell_executable).nil?
+ if Which.which(@_powershell_executable).nil?
+ @_powershell_executable = nil
+ end
+ else
@_powershell_executable = nil
end
end
@@ -41,6 +45,9 @@ module Vagrant
# Execute a powershell script.
#
# @param [String] path Path to the PowerShell script to execute.
+ # @param [Array] args Command arguments
+ # @param [Hash] opts Options passed to execute
+ # @option opts [Hash] :env Custom environment variables
# @return [Subprocess::Result]
def self.execute(path, *args, **opts, &block)
validate_install!
@@ -57,7 +64,7 @@ module Vagrant
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
- "#{env}&('#{path}')",
+ "#{resize_console}#{env}&('#{path}')",
args
].flatten
@@ -72,10 +79,16 @@ module Vagrant
# Execute a powershell command.
#
# @param [String] command PowerShell command to execute.
+ # @param [Hash] opts Extra options
+ # @option opts [Hash] :env Custom environment variables
# @return [nil, String] Returns nil if exit code is non-zero.
# Returns stdout string if exit code is zero.
- def self.execute_cmd(command)
+ def self.execute_cmd(command, **opts)
validate_install!
+ env = opts.delete(:env)
+ if env
+ env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
+ end
c = [
executable,
"-NoLogo",
@@ -83,7 +96,7 @@ module Vagrant
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-Command",
- command
+ "#{resize_console}#{env}#{command}"
].flatten.compact
r = Subprocess.execute(*c)
@@ -95,9 +108,14 @@ module Vagrant
#
# @param [String] command PowerShell command to execute.
# @param [Hash] opts A collection of options for subprocess::execute
+ # @option opts [Hash] :env Custom environment variables
# @param [Block] block Ruby block
def self.execute_inline(*command, **opts, &block)
validate_install!
+ env = opts.delete(:env)
+ if env
+ env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
+ end
c = [
executable,
"-NoLogo",
@@ -105,7 +123,7 @@ module Vagrant
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-Command",
- command
+ "#{resize_console}#{env}#{command}"
].flatten.compact
c << opts
@@ -224,6 +242,19 @@ module Vagrant
def self.reset!
instance_variables.each(&method(:remove_instance_variable))
end
+
+ # @private
+ # This is a helper method that provides the PowerShell command to resize
+ # the "console" to prevent output wrapping or truncating. An environment
+ # variable guard is provided to disable the behavior in cases where it
+ # may cause unexpected results (VAGRANT_POWERSHELL_RESIZE_DISABLE)
+ def self.resize_console
+ if ENV["VAGRANT_POWERSHELL_RESIZE_DISABLE"]
+ ""
+ else
+ "$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size(512,50); "
+ end
+ end
end
end
end
From 1be87662da5acce325382d773f58185248fa58aa Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Wed, 30 May 2018 15:05:27 -0700
Subject: [PATCH 24/53] Add missing test coverage for powershell util methods
---
test/unit/vagrant/util/powershell_test.rb | 246 +++++++++++++++++++++-
1 file changed, 245 insertions(+), 1 deletion(-)
diff --git a/test/unit/vagrant/util/powershell_test.rb b/test/unit/vagrant/util/powershell_test.rb
index d5760fc28..f60626eb7 100644
--- a/test/unit/vagrant/util/powershell_test.rb
+++ b/test/unit/vagrant/util/powershell_test.rb
@@ -4,6 +4,9 @@ require 'vagrant/util/powershell'
describe Vagrant::Util::PowerShell do
include_context "unit"
+
+ after{ described_class.reset! }
+
describe ".version" do
before do
allow(described_class).to receive(:executable)
@@ -13,7 +16,6 @@ describe Vagrant::Util::PowerShell do
after do
described_class.version
- described_class.reset!
end
it "should execute powershell command" do
@@ -41,4 +43,246 @@ describe Vagrant::Util::PowerShell do
end
end
end
+
+ describe ".executable" do
+ before{ allow(Vagrant::Util::Platform).to receive(:wsl?).and_return(false) }
+
+ context "when found in PATH" do
+ before{ expect(Vagrant::Util::Which).to receive(:which).with("powershell").and_return(true) }
+
+ it "should return powershell string" do
+ expect(described_class.executable).to eq("powershell")
+ end
+ end
+
+ context "when not found in PATH" do
+ before{ expect(Vagrant::Util::Which).to receive(:which).with("powershell").and_return(nil) }
+
+ it "should return nil" do
+ expect(described_class.executable).to be_nil
+ end
+
+ context "when within WSL" do
+ before{ expect(Vagrant::Util::Platform).to receive(:wsl?).and_return(true) }
+
+ it "should check PATH with .exe extension" do
+ expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe")
+ described_class.executable
+ end
+
+ it "should return powershell.exe when found" do
+ expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe").and_return(true)
+ expect(described_class.executable).to eq("powershell.exe")
+ end
+
+ it "should return nil when not found" do
+ expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe").and_return(nil)
+ expect(described_class.executable).to be_nil
+ end
+ end
+ end
+ end
+
+ describe ".available?" do
+ context "when powershell executable is available" do
+ before{ expect(described_class).to receive(:executable).and_return("powershell") }
+
+ it "should be true" do
+ expect(described_class.available?).to be(true)
+ end
+ end
+
+ context "when powershell executable is not available" do
+ before{ expect(described_class).to receive(:executable).and_return(nil) }
+
+ it "should be false" do
+ expect(described_class.available?).to be(false)
+ end
+ end
+ end
+
+ describe ".execute" do
+ before do
+ allow(described_class).to receive(:validate_install!)
+ allow(Vagrant::Util::Subprocess).to receive(:execute)
+ end
+
+ it "should validate installation before use" do
+ expect(described_class).to receive(:validate_install!)
+ described_class.execute("command")
+ end
+
+ it "should include command to execute" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("custom-command")
+ end
+ described_class.execute("custom-command")
+ end
+
+ it "should automatically include console resize" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("BufferSize")
+ end
+ described_class.execute("custom-command")
+ end
+
+ it "should accept custom environment" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("$env:TEST_KEY=test-value")
+ end
+ described_class.execute("custom-command", env: {"TEST_KEY" => "test-value"})
+ end
+ end
+
+ describe ".execute_cmd" do
+ let(:result) do
+ Vagrant::Util::Subprocess::Result.new(
+ exit_code, stdout, stderr)
+ end
+ let(:exit_code){ 0 }
+ let(:stdout){ "" }
+ let(:stderr){ "" }
+
+ before do
+ allow(described_class).to receive(:validate_install!)
+ allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(result)
+ end
+
+ it "should validate installation before use" do
+ expect(described_class).to receive(:validate_install!)
+ described_class.execute_cmd("command")
+ end
+
+ it "should include command to execute" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("custom-command")
+ result
+ end
+ described_class.execute_cmd("custom-command")
+ end
+
+ it "should automatically include console resize" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("BufferSize")
+ result
+ end
+ described_class.execute_cmd("custom-command")
+ end
+
+ it "should accept custom environment" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("$env:TEST_KEY=test-value")
+ result
+ end
+ described_class.execute_cmd("custom-command", env: {"TEST_KEY" => "test-value"})
+ end
+
+ context "with command output" do
+ let(:stdout){ "custom-output" }
+
+ it "should return stdout" do
+ expect(described_class.execute_cmd("cmd")).to eq(stdout)
+ end
+ end
+
+ context "with failed command" do
+ let(:exit_code){ 1 }
+
+ it "should return nil" do
+ expect(described_class.execute_cmd("cmd")).to be_nil
+ end
+ end
+ end
+
+ describe ".execute_inline" do
+ let(:result) do
+ Vagrant::Util::Subprocess::Result.new(
+ exit_code, stdout, stderr)
+ end
+ let(:exit_code){ 0 }
+ let(:stdout){ "" }
+ let(:stderr){ "" }
+
+ before do
+ allow(described_class).to receive(:validate_install!)
+ allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(result)
+ end
+
+ it "should validate installation before use" do
+ expect(described_class).to receive(:validate_install!)
+ described_class.execute_inline("command")
+ end
+
+ it "should include command to execute" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("custom-command")
+ result
+ end
+ described_class.execute_inline("custom-command")
+ end
+
+ it "should automatically include console resize" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("BufferSize")
+ result
+ end
+ described_class.execute_inline("custom-command")
+ end
+
+ it "should accept custom environment" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.include?("custom-command") }
+ expect(comm.to_s).to include("$env:TEST_KEY=test-value")
+ result
+ end
+ described_class.execute_inline("custom-command", env: {"TEST_KEY" => "test-value"})
+ end
+
+ it "should return a result instance" do
+ expect(described_class.execute_inline("cmd")).to eq(result)
+ end
+ end
+
+ describe ".validate_install!" do
+ before do
+ allow(described_class).to receive(:available?).and_return(true)
+ end
+
+ context "with version under minimum required" do
+ before{ expect(described_class).to receive(:version).and_return("2.1").at_least(:once) }
+
+ it "should raise an error" do
+ expect{ described_class.validate_install! }.to raise_error(Vagrant::Errors::PowerShellInvalidVersion)
+ end
+ end
+
+ context "with version above minimum required" do
+ before{ expect(described_class).to receive(:version).and_return("3.1").at_least(:once) }
+
+ it "should return true" do
+ expect(described_class.validate_install!).to be(true)
+ end
+ end
+
+ end
+
+ describe ".resize_console" do
+ it "should return command string" do
+ expect(described_class.resize_console).to include("BufferSize")
+ end
+
+ it "should return empty string when disabled" do
+ with_temp_env("VAGRANT_POWERSHELL_RESIZE_DISABLE" => "1") do
+ expect(described_class.resize_console).to be_empty
+ end
+ end
+ end
end
From 5e68c896cf9e31586878b6e53dce92a86c2d7884 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Wed, 30 May 2018 15:42:04 -0700
Subject: [PATCH 25/53] Force types for testing
---
test/unit/vagrant/util/powershell_test.rb | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/test/unit/vagrant/util/powershell_test.rb b/test/unit/vagrant/util/powershell_test.rb
index f60626eb7..ce30bd4d1 100644
--- a/test/unit/vagrant/util/powershell_test.rb
+++ b/test/unit/vagrant/util/powershell_test.rb
@@ -114,7 +114,7 @@ describe Vagrant::Util::PowerShell do
it "should include command to execute" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("custom-command")
end
described_class.execute("custom-command")
@@ -122,7 +122,7 @@ describe Vagrant::Util::PowerShell do
it "should automatically include console resize" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("BufferSize")
end
described_class.execute("custom-command")
@@ -130,7 +130,7 @@ describe Vagrant::Util::PowerShell do
it "should accept custom environment" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("$env:TEST_KEY=test-value")
end
described_class.execute("custom-command", env: {"TEST_KEY" => "test-value"})
@@ -158,7 +158,7 @@ describe Vagrant::Util::PowerShell do
it "should include command to execute" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("custom-command")
result
end
@@ -167,7 +167,7 @@ describe Vagrant::Util::PowerShell do
it "should automatically include console resize" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("BufferSize")
result
end
@@ -176,7 +176,7 @@ describe Vagrant::Util::PowerShell do
it "should accept custom environment" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("$env:TEST_KEY=test-value")
result
end
@@ -221,7 +221,7 @@ describe Vagrant::Util::PowerShell do
it "should include command to execute" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("custom-command")
result
end
@@ -230,7 +230,7 @@ describe Vagrant::Util::PowerShell do
it "should automatically include console resize" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("BufferSize")
result
end
@@ -239,7 +239,7 @@ describe Vagrant::Util::PowerShell do
it "should accept custom environment" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
- comm = args.detect{|s| s.include?("custom-command") }
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("$env:TEST_KEY=test-value")
result
end
From d5b5e9ea8c26eefb7b3104529ebff822b6aa6971 Mon Sep 17 00:00:00 2001
From: Gilles Cornu
Date: Tue, 29 May 2018 08:36:33 +0200
Subject: [PATCH 26/53] ansible_local: Improve installation from PPA
- Fix #9796: Failing installation on bionic (18.04 LTS)
- Remove support for precise (12.04 LTS), which is out of public support
(On this version, the package python-software-properties contained
the add-apt-repository tool)
- Reduce the number of SSH calls, and avoid unnecessary apt-get calls.
---
.../ansible/cap/guest/ubuntu/ansible_install.rb | 16 +++++++++++-----
.../docs/provisioning/ansible_local.html.md | 2 +-
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb
index 3629239d9..d319ac071 100644
--- a/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb
+++ b/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb
@@ -18,11 +18,17 @@ module VagrantPlugins
private
def self.ansible_apt_install(machine)
- machine.communicate.sudo "apt-get update -y -qq"
- machine.communicate.sudo "apt-get install -y -qq software-properties-common python-software-properties"
- machine.communicate.sudo "add-apt-repository ppa:ansible/ansible -y"
- machine.communicate.sudo "apt-get update -y -qq"
- machine.communicate.sudo "apt-get install -y -qq ansible"
+ unless machine.communicate.test("test -x \"$(which add-apt-repository)\"")
+ machine.communicate.sudo """
+ apt-get update -y -qq && \
+ apt-get install -y -qq software-properties-common
+ """
+ end
+ machine.communicate.sudo """
+ add-apt-repository ppa:ansible/ansible -y && \
+ apt-get update -y -qq && \
+ apt-get install -y -qq ansible
+ """
end
end
diff --git a/website/source/docs/provisioning/ansible_local.html.md b/website/source/docs/provisioning/ansible_local.html.md
index d25318b93..14df9d53f 100644
--- a/website/source/docs/provisioning/ansible_local.html.md
+++ b/website/source/docs/provisioning/ansible_local.html.md
@@ -73,7 +73,7 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
- `install_mode` (`:default`, `:pip`, or `:pip_args_only`) - Select the way to automatically install Ansible on the guest system.
- `:default`: Ansible is installed from the operating system package manager. This mode doesn't support `version` selection. For many platforms (e.g Debian, FreeBSD, OpenSUSE) the official package repository is used, except for the following Linux distributions:
- - On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository.
+ - On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository. The compatibility is maintained only for active long-term support (LTS) versions.
- On RedHat-like systems, the latest Ansible release is installed from the [EPEL](http://fedoraproject.org/wiki/EPEL) repository.
- `:pip`: Ansible is installed from [PyPI](https://pypi.python.org/pypi) with [pip](https://pip.pypa.io) package installer. With this mode, Vagrant will systematically try to [install the latest pip version](https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py). With the `:pip` mode you can optionally install a specific Ansible release by setting the [`version`](/docs/provisioning/ansible_common.html#version) option.
From 56051c1fb03f9350f23e606e382aeb1b301439be Mon Sep 17 00:00:00 2001
From: Gilles Cornu
Date: Thu, 31 May 2018 05:52:19 +0200
Subject: [PATCH 27/53] Update CHANGELOG about #9879 and #9796
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7626f643c..7bcd15e40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,12 +6,16 @@ IMPROVEMENTS:
- commands/suspend: Introduce flag for suspending all machines [GH-9829]
- commands/global-status: Improve message about removing stale entries [GH-9856]
+- provisioners/ansible_local: Improve installation from PPA on Ubuntu guests.
+ The compatibility is maintained only for active long-term support (LTS) versions,
+ i.e. Ubuntu 12.04 (Precise Pangolin) is no longer supported. [GH-9879]
BUG FIXES:
- core: Display plugin commands in help [GH-9808]
- core: Ensure guestpath or name is set with synced_folder option and dont set guestpath if not provided [GH-9692]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
+- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
## 2.1.1 (May 7, 2018)
From 7b8818d985cdb1720bd5de379193ca0524ab8901 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Mon, 4 Jun 2018 16:53:13 -0700
Subject: [PATCH 28/53] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bcd15e40..56eb032a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ IMPROVEMENTS:
- commands/suspend: Introduce flag for suspending all machines [GH-9829]
- commands/global-status: Improve message about removing stale entries [GH-9856]
+- provider/hyper-v: Update implementation. Include support for modifications on reload [GH-9872]
- provisioners/ansible_local: Improve installation from PPA on Ubuntu guests.
The compatibility is maintained only for active long-term support (LTS) versions,
i.e. Ubuntu 12.04 (Precise Pangolin) is no longer supported. [GH-9879]
From 97806a693eb3ff6ccd7f61695861267ec286bde6 Mon Sep 17 00:00:00 2001
From: Brian Cain
Date: Wed, 6 Jun 2018 15:03:39 -0700
Subject: [PATCH 29/53] Fix broken link in Vagrant Cloud API docs
This commit fixes the #TODO link with the proper docs link to upload a
provider.
---
website/source/docs/vagrant-cloud/api.html.md.erb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/source/docs/vagrant-cloud/api.html.md.erb b/website/source/docs/vagrant-cloud/api.html.md.erb
index 999e5cc29..693d42fe3 100644
--- a/website/source/docs/vagrant-cloud/api.html.md.erb
+++ b/website/source/docs/vagrant-cloud/api.html.md.erb
@@ -1234,7 +1234,7 @@ Response body is identical to [Reading a provider](#read-a-provider).
* `provider`
* `name` - The name of the provider.
- * `url` - A valid URL to download this provider. If omitted, you must [upload](#TODO) the Vagrant box image for this provider to Vagrant Cloud before the provider can be used.
+ * `url` - A valid URL to download this provider. If omitted, you must [upload](#upload-a-provider) the Vagrant box image for this provider to Vagrant Cloud before the provider can be used.
#### Example Request
From 5ac8bb8620aecdb76b57942f69d0793db0d7d3a8 Mon Sep 17 00:00:00 2001
From: Tony Apuzzo
Date: Thu, 7 Jun 2018 15:00:09 -0600
Subject: [PATCH 30/53] Correct typo, $Enable instead of $enabled.
---
.../providers/hyperv/scripts/set_vm_integration_services.ps1 | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1 b/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1
index a28d1229b..458ddbaf3 100644
--- a/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1
+++ b/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1
@@ -19,9 +19,9 @@ try {
}
try {
- Set-VagrantVMService -VM $VM -Name $Name -Enable $enabled
+ Set-VagrantVMService -VM $VM -Name $Name -Enable $Enable
} catch {
- if($enabled){ $action = "enable" } else { $action = "disable" }
+ if($Enable){ $action = "enable" } else { $action = "disable" }
Write-Error-Message "Failed to ${action} VM integration service ${Name}: ${PSItem}"
exit 1
}
From 77d229edaa3defc7eecfc7fe40728904a0752157 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 8 Jun 2018 16:04:44 -0700
Subject: [PATCH 31/53] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 56eb032a1..2a7c30a77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ BUG FIXES:
- core: Display plugin commands in help [GH-9808]
- core: Ensure guestpath or name is set with synced_folder option and dont set guestpath if not provided [GH-9692]
+- guest/debian: Fix netplan generation when using DHCP [GH-9855]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
From 924189b6c2fa9e013ad2633a28f3632024b23660 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 8 Jun 2018 16:09:37 -0700
Subject: [PATCH 32/53] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a7c30a77..7080637e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ BUG FIXES:
- core: Display plugin commands in help [GH-9808]
- core: Ensure guestpath or name is set with synced_folder option and dont set guestpath if not provided [GH-9692]
- guest/debian: Fix netplan generation when using DHCP [GH-9855]
+- guest/debain: Update priority of network configuration file when using networkd [GH-9867]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
From 53b760923ab8a6a32fa61626ed1bfc42c2f03265 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Fri, 8 Jun 2018 16:18:59 -0700
Subject: [PATCH 33/53] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7080637e1..f254eb220 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ BUG FIXES:
- guest/debain: Update priority of network configuration file when using networkd [GH-9867]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
+- provisioner/salt: Remove usage of masterless? config attribute [GH-9833]
## 2.1.1 (May 7, 2018)
From 8e5dc0dfe85a7585c399258d4d42b3a32a5fea7f Mon Sep 17 00:00:00 2001
From: marjune
Date: Sun, 10 Jun 2018 22:36:45 +0800
Subject: [PATCH 34/53] add missing section separator for config.ssh part
---
website/source/docs/vagrantfile/ssh_settings.html.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/website/source/docs/vagrantfile/ssh_settings.html.md b/website/source/docs/vagrantfile/ssh_settings.html.md
index 9653f6f1d..1529cebb5 100644
--- a/website/source/docs/vagrantfile/ssh_settings.html.md
+++ b/website/source/docs/vagrantfile/ssh_settings.html.md
@@ -155,18 +155,26 @@ is:
config.ssh.export_command_template = 'export %ENV_KEY%="%ENV_VALUE%"'
```
+
+
`config.ssh.sudo_command` - The command to use when executing a command
with `sudo`. This defaults to `sudo -E -H %c`. The `%c` will be replaced by
the command that is being executed.
+
+
`config.ssh.compression` - If `false`, this setting will not include the
compression setting when ssh'ing into a machine. If this is not set, it will
default to `true` and `Compression=yes` will be enabled with ssh.
+
+
`config.ssh.dsa_authentication` - If `false`, this setting will not include
`DSAAuthentication` when ssh'ing into a machine. If this is not set, it will
default to `true` and `DSAAuthentication=yes` will be used with ssh.
+
+
`config.ssh.extra_args` - This settings value is passed directly into the
ssh executable. This allows you to pass any arbitrary commands to do things such
as reverse tunneling down into the ssh program. These options can either be
From 7df6ea5a30f6b0030bb8de69ecff50c0680864b3 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Mon, 11 Jun 2018 10:33:28 -0700
Subject: [PATCH 35/53] Fix Chef apply provisioner to not set node_name
The base chef provisioner class will set the node name automatically
if not provided in the configuration. Since the chef apply provisioner
does not provide a node_name option, setting it will invalidate the
configuration. This checks for the node name before attempting to
use it.
Fixes #9901
---
plugins/provisioners/chef/provisioner/base.rb | 2 +-
.../provisioners/chef/provisioner/base_test.rb | 11 ++++++++---
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/plugins/provisioners/chef/provisioner/base.rb b/plugins/provisioners/chef/provisioner/base.rb
index 28c30bb64..607909a4e 100644
--- a/plugins/provisioners/chef/provisioner/base.rb
+++ b/plugins/provisioners/chef/provisioner/base.rb
@@ -24,7 +24,7 @@ module VagrantPlugins
@logger = Log4r::Logger.new("vagrant::provisioners::chef")
- if !present?(@config.node_name)
+ if @config.respond_to?(:node_name) && !present?(@config.node_name)
# First attempt to get the node name from the hostname, and if that
# is not present, generate/retrieve a random hostname.
hostname = @machine.config.vm.hostname
diff --git a/test/unit/plugins/provisioners/chef/provisioner/base_test.rb b/test/unit/plugins/provisioners/chef/provisioner/base_test.rb
index 10536f035..9afa712b8 100644
--- a/test/unit/plugins/provisioners/chef/provisioner/base_test.rb
+++ b/test/unit/plugins/provisioners/chef/provisioner/base_test.rb
@@ -39,16 +39,21 @@ describe VagrantPlugins::Chef::Provisioner::Base do
it "defaults to hostname if given" do
machine.config.vm.hostname = "by.hostname"
- instance = described_class.new(machine, OpenStruct.new)
+ instance = described_class.new(machine, OpenStruct.new(node_name: nil))
expect(instance.config.node_name).to eq("by.hostname")
end
it "generates a random name if no hostname or node_name is given" do
- config = OpenStruct.new(node_name: nil)
machine.config.vm.hostname = nil
- instance = described_class.new(machine, OpenStruct.new)
+ instance = described_class.new(machine, OpenStruct.new(node_name: nil))
expect(instance.config.node_name).to match(/vagrant\-.+/)
end
+
+ it "does not set node_name if configuration does not define it" do
+ expect(config).to receive(:respond_to?).with(:node_name).and_return(false)
+ expect(config).not_to receive(:node_name)
+ described_class.new(machine, config)
+ end
end
describe "#encrypted_data_bag_secret_key_path" do
From ef5eca16d22141cb7343df74ecb4c9c025587012 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Mon, 11 Jun 2018 12:58:35 -0700
Subject: [PATCH 36/53] Fix SMB information generation when using net share
Adds the final line of output to tests and properly simulates
failure. Fix with type casting to prevent calling methods on
nil and drop the final entry as it's not valid.
Fixes #9806
---
plugins/hosts/windows/cap/smb.rb | 4 +++-
test/unit/plugins/hosts/windows/cap/smb_test.rb | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/plugins/hosts/windows/cap/smb.rb b/plugins/hosts/windows/cap/smb.rb
index e767eb202..c0ed42a49 100644
--- a/plugins/hosts/windows/cap/smb.rb
+++ b/plugins/hosts/windows/cap/smb.rb
@@ -38,7 +38,7 @@ module VagrantPlugins
m_id = machine_id(machine)
prune_shares = existing_shares.map do |share_name, share_info|
- if share_info["Description"].start_with?("vgt-#{m_id}-")
+ if share_info["Description"].to_s.start_with?("vgt-#{m_id}-")
@@logger.info("removing smb share name=#{share_name} id=#{m_id}")
share_name
else
@@ -159,6 +159,8 @@ module VagrantPlugins
share_names = result.strip.split("\n").map do |line|
line.strip.split(/\s+/).first
end
+ # Last item is command completion notification so remove it
+ share_names.pop
shares = {}
share_names.each do |share_name|
shares[share_name] = {}
diff --git a/test/unit/plugins/hosts/windows/cap/smb_test.rb b/test/unit/plugins/hosts/windows/cap/smb_test.rb
index d482ad0dd..d791c53d9 100644
--- a/test/unit/plugins/hosts/windows/cap/smb_test.rb
+++ b/test/unit/plugins/hosts/windows/cap/smb_test.rb
@@ -31,7 +31,7 @@ Share name Resource Remark
vgt-CUSTOM_ID-1 /a/path vgt-CUSTOM_ID-1
vgt-CUSTOM_ID-2 /other/path vgt-CUSTOM_ID-2
my-share /my/path Not Vagran...
-
+The command completed successfully.
EOF
}
let(:netshare1){ <<-EOF
From 5325000fa616cf9b28bf3a05608a04435e62941d Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 10:44:12 -0700
Subject: [PATCH 37/53] Add helper option for setting custom module path on
PowerShell executes
---
lib/vagrant/util/powershell.rb | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/lib/vagrant/util/powershell.rb b/lib/vagrant/util/powershell.rb
index 8e48aad1d..3347e6d36 100644
--- a/lib/vagrant/util/powershell.rb
+++ b/lib/vagrant/util/powershell.rb
@@ -54,8 +54,12 @@ module Vagrant
if opts.delete(:sudo) || opts.delete(:runas)
powerup_command(path, args, opts)
else
- env = opts.delete(:env)
- if env
+ if mpath = opts.delete(:module_path)
+ m_env = opts.fetch(:env, {})
+ m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
+ opts[:env] = m_env
+ end
+ if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
command = [
@@ -85,8 +89,12 @@ module Vagrant
# Returns stdout string if exit code is zero.
def self.execute_cmd(command, **opts)
validate_install!
- env = opts.delete(:env)
- if env
+ if mpath = opts.delete(:module_path)
+ m_env = opts.fetch(:env, {})
+ m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
+ opts[:env] = m_env
+ end
+ if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
c = [
@@ -112,8 +120,12 @@ module Vagrant
# @param [Block] block Ruby block
def self.execute_inline(*command, **opts, &block)
validate_install!
- env = opts.delete(:env)
- if env
+ if mpath = opts.delete(:module_path)
+ m_env = opts.fetch(:env, {})
+ m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
+ opts[:env] = m_env
+ end
+ if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
c = [
From 5ba91de4d87cb315001ee50478678748119a274a Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 10:46:53 -0700
Subject: [PATCH 38/53] Use host capability to update key file permissions if
available
---
plugins/communicators/ssh/communicator.rb | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/plugins/communicators/ssh/communicator.rb b/plugins/communicators/ssh/communicator.rb
index 3d3d5a4b8..a7e1cb0ad 100644
--- a/plugins/communicators/ssh/communicator.rb
+++ b/plugins/communicators/ssh/communicator.rb
@@ -194,17 +194,9 @@ module VagrantPlugins
f.write(priv)
end
- # Adjust private key file permissions
- if Vagrant::Util::Platform.windows?
- begin
- priv_path = @machine.data_dir.join("private_key").to_s
- File.set_permissions(priv_path, Etc.getlogin => File::FULL)
- rescue => e
- @logger.warn("Error encountered during private key permissions set - " \
- "#{e.class}: #{e.message}")
- end
- else
- @machine.data_dir.join("private_key").chmod(0600)
+ # Adjust private key file permissions if host provides capability
+ if @machine.env.host.has_capability?(:set_ssh_key_permissions)
+ @machine.env.host.capability(:set_ssh_key_permissions, @machine.data_dir.join("private_key"))
end
# Remove the old key if it exists
From a5582eb1c8b7864a4116672896017058292b95a9 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 10:49:10 -0700
Subject: [PATCH 39/53] Add ssh key permissions set caps to hosts
---
plugins/hosts/bsd/cap/ssh.rb | 16 ++++++++++++
plugins/hosts/bsd/plugin.rb | 5 ++++
plugins/hosts/linux/cap/ssh.rb | 16 ++++++++++++
plugins/hosts/linux/plugin.rb | 5 ++++
plugins/hosts/windows/cap/ssh.rb | 25 +++++++++++++++++++
plugins/hosts/windows/host.rb | 10 ++++++++
plugins/hosts/windows/plugin.rb | 5 ++++
.../scripts/set_ssh_key_permissions.ps1 | 17 +++++++++++++
.../scripts/utils/VagrantSSH/VagrantSSH.psm1 | 24 ++++++++++++++++++
9 files changed, 123 insertions(+)
create mode 100644 plugins/hosts/bsd/cap/ssh.rb
create mode 100644 plugins/hosts/linux/cap/ssh.rb
create mode 100644 plugins/hosts/windows/cap/ssh.rb
create mode 100644 plugins/hosts/windows/scripts/set_ssh_key_permissions.ps1
create mode 100644 plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1
diff --git a/plugins/hosts/bsd/cap/ssh.rb b/plugins/hosts/bsd/cap/ssh.rb
new file mode 100644
index 000000000..9d38c8bea
--- /dev/null
+++ b/plugins/hosts/bsd/cap/ssh.rb
@@ -0,0 +1,16 @@
+module VagrantPlugins
+ module HostBSD
+ module Cap
+ class SSH
+ # Set the ownership and permissions for SSH
+ # private key
+ #
+ # @param [Vagrant::Environment] env
+ # @param [Pathname] key_path
+ def self.set_ssh_key_permissions(env, key_path)
+ key_path.chmod(0600)
+ end
+ end
+ end
+ end
+end
diff --git a/plugins/hosts/bsd/plugin.rb b/plugins/hosts/bsd/plugin.rb
index 359d4f7ad..ce79ede31 100644
--- a/plugins/hosts/bsd/plugin.rb
+++ b/plugins/hosts/bsd/plugin.rb
@@ -35,6 +35,11 @@ module VagrantPlugins
require_relative "cap/nfs"
Cap::NFS
end
+
+ host_capability("bsd", "set_ssh_key_permissions") do
+ require_relative "cap/ssh"
+ Cap::SSH
+ end
end
end
end
diff --git a/plugins/hosts/linux/cap/ssh.rb b/plugins/hosts/linux/cap/ssh.rb
new file mode 100644
index 000000000..c3a17a5f7
--- /dev/null
+++ b/plugins/hosts/linux/cap/ssh.rb
@@ -0,0 +1,16 @@
+module VagrantPlugins
+ module HostLinux
+ module Cap
+ class SSH
+ # Set the ownership and permissions for SSH
+ # private key
+ #
+ # @param [Vagrant::Environment] env
+ # @param [Pathname] key_path
+ def self.set_ssh_key_permissions(env, key_path)
+ key_path.chmod(0600)
+ end
+ end
+ end
+ end
+end
diff --git a/plugins/hosts/linux/plugin.rb b/plugins/hosts/linux/plugin.rb
index 8b89fc522..a4c6311bf 100644
--- a/plugins/hosts/linux/plugin.rb
+++ b/plugins/hosts/linux/plugin.rb
@@ -47,6 +47,11 @@ module VagrantPlugins
require_relative "cap/nfs"
Cap::NFS
end
+
+ host_capability("linux", "set_ssh_key_permissions") do
+ require_relative "cap/ssh"
+ Cap::SSH
+ end
end
end
end
diff --git a/plugins/hosts/windows/cap/ssh.rb b/plugins/hosts/windows/cap/ssh.rb
new file mode 100644
index 000000000..e94a683c4
--- /dev/null
+++ b/plugins/hosts/windows/cap/ssh.rb
@@ -0,0 +1,25 @@
+module VagrantPlugins
+ module HostWindows
+ module Cap
+ class SSH
+ # Set the ownership and permissions for SSH
+ # private key
+ #
+ # @param [Vagrant::Environment] env
+ # @param [Pathname] key_path
+ def self.set_ssh_key_permissions(env, key_path)
+ script_path = Host.scripts_path.join("set_ssh_key_permissions.ps1")
+ result = Vagrant::Util::PowerShell.execute(
+ script_path.to_s, path.to_s,
+ module_path: Host.module_path.to_s
+ )
+ if result.exit_code != 0
+ raise Vagrant::Errors::PowerShellError,
+ script: script_path,
+ stderr: result.stderr
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/plugins/hosts/windows/host.rb b/plugins/hosts/windows/host.rb
index 4491f629a..b92ac335e 100644
--- a/plugins/hosts/windows/host.rb
+++ b/plugins/hosts/windows/host.rb
@@ -8,6 +8,16 @@ module VagrantPlugins
def detect?(env)
Vagrant::Util::Platform.windows?
end
+
+ # @return [Pathname] Path to scripts directory
+ def self.scripts_path
+ Pathname.new(File.expand_path("..", __FILE__))
+ end
+
+ # @return [Pathname] Path to modules directory
+ def self.modules_path
+ scripts_path.join("utils")
+ end
end
end
end
diff --git a/plugins/hosts/windows/plugin.rb b/plugins/hosts/windows/plugin.rb
index 78d9239e1..5668141fb 100644
--- a/plugins/hosts/windows/plugin.rb
+++ b/plugins/hosts/windows/plugin.rb
@@ -55,6 +55,11 @@ module VagrantPlugins
require_relative "cap/configured_ip_addresses"
Cap::ConfiguredIPAddresses
end
+
+ host_capability("windows", "set_ssh_key_permissions") do
+ require_relative "cap/ssh"
+ Cap::SSH
+ end
end
end
end
diff --git a/plugins/hosts/windows/scripts/set_ssh_key_permissions.ps1 b/plugins/hosts/windows/scripts/set_ssh_key_permissions.ps1
new file mode 100644
index 000000000..9fc5a1d20
--- /dev/null
+++ b/plugins/hosts/windows/scripts/set_ssh_key_permissions.ps1
@@ -0,0 +1,17 @@
+#Requires -Modules VagrantSSH
+
+param(
+ [Parameter(Mandatory=$true)]
+ [string] $KeyPath,
+ [Parameter(Mandatory=$false)]
+ [string] $Principal=$null
+)
+
+$ErrorActionPreference = "Stop"
+
+try {
+ Set-SSHKeyPermissions -SSHKeyPath $KeyPath -Principal $Principal
+} catch {
+ Write-Error "Failed to set permissions on key: ${PSItem}"
+ exit 1
+}
diff --git a/plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1 b/plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1
new file mode 100644
index 000000000..04b1e327b
--- /dev/null
+++ b/plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1
@@ -0,0 +1,24 @@
+# Vagrant SSH capability functions
+
+function Set-SSHKeyPermissions {
+ param (
+ [parameter(Mandatory=$true)]
+ [string] $SSHKeyPath,
+ [parameter(Mandatory=$false)]
+ [string] $Principal=$null
+ )
+
+ if(!$Principal) {
+ $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
+ }
+
+ # Create the new ACL we want to apply
+ $NewAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
+ $Principal, "FullControl", "None", "None", "Allow")
+ # Scrub all existing ACLs from the file
+ $ACL = Get-ACL "${SSHKeyPath}"
+ $ACL.Access | %{$ACL.RemoveAccessRule($_)}
+ # Apply the new ACL
+ $ACL.SetAccessRule($NewAccessRule)
+ Set-ACL "${SSHKeyPath}" $ACL
+}
From 98c6903e9c757af91e1c59a51d409631b2e52a7b Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 11:56:37 -0700
Subject: [PATCH 40/53] Fix path variable name. Return process result.
---
plugins/hosts/windows/cap/ssh.rb | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/plugins/hosts/windows/cap/ssh.rb b/plugins/hosts/windows/cap/ssh.rb
index e94a683c4..8cf0129f7 100644
--- a/plugins/hosts/windows/cap/ssh.rb
+++ b/plugins/hosts/windows/cap/ssh.rb
@@ -10,14 +10,15 @@ module VagrantPlugins
def self.set_ssh_key_permissions(env, key_path)
script_path = Host.scripts_path.join("set_ssh_key_permissions.ps1")
result = Vagrant::Util::PowerShell.execute(
- script_path.to_s, path.to_s,
- module_path: Host.module_path.to_s
+ script_path.to_s, key_path.to_s,
+ module_path: Host.modules_path.to_s
)
if result.exit_code != 0
raise Vagrant::Errors::PowerShellError,
script: script_path,
stderr: result.stderr
end
+ result
end
end
end
From 42cbae1e90ee73cd26399195c07a3a90f956e1bc Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 11:57:14 -0700
Subject: [PATCH 41/53] Add test coverage on ssh capabilities
---
.../communicators/ssh/communicator_test.rb | 42 +++++--------------
test/unit/plugins/hosts/bsd/cap/ssh_test.rb | 15 +++++++
test/unit/plugins/hosts/linux/cap/ssh_test.rb | 15 +++++++
.../plugins/hosts/windows/cap/ssh_test.rb | 38 +++++++++++++++++
4 files changed, 79 insertions(+), 31 deletions(-)
create mode 100644 test/unit/plugins/hosts/bsd/cap/ssh_test.rb
create mode 100644 test/unit/plugins/hosts/linux/cap/ssh_test.rb
create mode 100644 test/unit/plugins/hosts/windows/cap/ssh_test.rb
diff --git a/test/unit/plugins/communicators/ssh/communicator_test.rb b/test/unit/plugins/communicators/ssh/communicator_test.rb
index 1b244b8a3..d9ccb6835 100644
--- a/test/unit/plugins/communicators/ssh/communicator_test.rb
+++ b/test/unit/plugins/communicators/ssh/communicator_test.rb
@@ -34,9 +34,12 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
double("machine",
config: config,
provider: provider,
- ui: ui
+ ui: ui,
+ env: env
)
end
+ let(:env){ double("env", host: host) }
+ let(:host){ double("host") }
# SSH information of the machine
let(:machine_ssh_info){ {host: '10.1.2.3', port: 22} }
# Subject instance to test
@@ -89,6 +92,10 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
allow(communicator).to receive(:retryable).and_return(connection)
end
+ before do
+ allow(host).to receive(:has_capability?).and_return(false)
+ end
+
describe ".wait_for_ready" do
before(&connection_setup)
context "with no static config (default scenario)" do
@@ -208,41 +215,14 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
expect(private_key_file).to receive(:write).with(new_private_key)
end
- it "should set private key file as user readable only" do
- expect(private_key_file).to receive(:chmod).with(0600)
+ it "should call the set_ssh_key_permissions host capability" do
+ expect(host).to receive(:has_capability?).with(:set_ssh_key_permissions).and_return(true)
+ expect(host).to receive(:capability).with(:set_ssh_key_permissions, private_key_file)
end
it "should remove the default public key" do
expect(guest).to receive(:capability).with(:remove_public_key, any_args)
end
-
- context "on windows platform" do
- let(:owner){ "owner" }
-
- before do
- allow(private_key_file).to receive(:to_s).and_return("PRIVATE_KEY_PATH")
- allow(File).to receive(:set_permissions)
- allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true)
- allow(Etc).to receive(:getlogin).and_return(owner)
- stub_const('File::FULL', :full)
- end
-
- it "should get set new permissions on private key file" do
- expect(File).to receive(:set_permissions).with("PRIVATE_KEY_PATH", any_args)
- end
-
- it "should proceed when error is encountered" do
- expect(File).to receive(:set_permissions).and_raise(StandardError)
- end
-
- context "with multiple permissions on file" do
-
- it "should delete all non-owner permissions" do
- expect(File).to receive(:set_permissions).with("PRIVATE_KEY_PATH",
- owner => :full)
- end
- end
- end
end
end
end
diff --git a/test/unit/plugins/hosts/bsd/cap/ssh_test.rb b/test/unit/plugins/hosts/bsd/cap/ssh_test.rb
new file mode 100644
index 000000000..1b571a907
--- /dev/null
+++ b/test/unit/plugins/hosts/bsd/cap/ssh_test.rb
@@ -0,0 +1,15 @@
+require_relative "../../../../base"
+
+require_relative "../../../../../../plugins/hosts/bsd/cap/ssh"
+
+describe VagrantPlugins::HostBSD::Cap::SSH do
+ let(:subject){ VagrantPlugins::HostBSD::Cap::SSH }
+
+ let(:env){ double("env") }
+ let(:key_path){ double("key_path") }
+
+ it "should set file as user only read/write" do
+ expect(key_path).to receive(:chmod).with(0600)
+ subject.set_ssh_key_permissions(env, key_path)
+ end
+end
diff --git a/test/unit/plugins/hosts/linux/cap/ssh_test.rb b/test/unit/plugins/hosts/linux/cap/ssh_test.rb
new file mode 100644
index 000000000..1dd67b5e9
--- /dev/null
+++ b/test/unit/plugins/hosts/linux/cap/ssh_test.rb
@@ -0,0 +1,15 @@
+require_relative "../../../../base"
+
+require_relative "../../../../../../plugins/hosts/linux/cap/ssh"
+
+describe VagrantPlugins::HostLinux::Cap::SSH do
+ let(:subject){ VagrantPlugins::HostLinux::Cap::SSH }
+
+ let(:env){ double("env") }
+ let(:key_path){ double("key_path") }
+
+ it "should set file as user only read/write" do
+ expect(key_path).to receive(:chmod).with(0600)
+ subject.set_ssh_key_permissions(env, key_path)
+ end
+end
diff --git a/test/unit/plugins/hosts/windows/cap/ssh_test.rb b/test/unit/plugins/hosts/windows/cap/ssh_test.rb
new file mode 100644
index 000000000..e1a9f93fc
--- /dev/null
+++ b/test/unit/plugins/hosts/windows/cap/ssh_test.rb
@@ -0,0 +1,38 @@
+require_relative "../../../../base"
+
+require_relative "../../../../../../plugins/hosts/windows/cap/ssh"
+
+describe VagrantPlugins::HostWindows::Cap::SSH do
+ let(:subject){ VagrantPlugins::HostWindows::Cap::SSH }
+ let(:result){ Vagrant::Util::Subprocess::Result.new(exit_code, stdout, stderr) }
+ let(:exit_code){ 0 }
+ let(:stdout){ "" }
+ let(:stderr){ "" }
+
+ let(:key_path){ double("keypath", to_s: "keypath") }
+ let(:env){ double("env") }
+
+ before do
+ allow(Vagrant::Util::PowerShell).to receive(:execute).and_return(result)
+ end
+
+ it "should execute PowerShell script" do
+ expect(Vagrant::Util::PowerShell).to receive(:execute).with(
+ /set_ssh_key_permissions.ps1/, key_path.to_s, any_args
+ ).and_return(result)
+ subject.set_ssh_key_permissions(env, key_path)
+ end
+
+ it "should return the result" do
+
+ expect(subject.set_ssh_key_permissions(env, key_path)).to eq(result)
+ end
+
+ context "when command fails" do
+ let(:exit_code){ 1 }
+
+ it "should raise an error" do
+ expect{ subject.set_ssh_key_permissions(env, key_path) }.to raise_error(Vagrant::Errors::PowerShellError)
+ end
+ end
+end
From bf7cd4fb0ab6c20a20a883128a2d164f82d5ee7c Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 12:32:04 -0700
Subject: [PATCH 42/53] Add test coverage for powershell module option
---
test/unit/vagrant/util/powershell_test.rb | 26 +++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/test/unit/vagrant/util/powershell_test.rb b/test/unit/vagrant/util/powershell_test.rb
index ce30bd4d1..0733f7783 100644
--- a/test/unit/vagrant/util/powershell_test.rb
+++ b/test/unit/vagrant/util/powershell_test.rb
@@ -135,6 +135,14 @@ describe Vagrant::Util::PowerShell do
end
described_class.execute("custom-command", env: {"TEST_KEY" => "test-value"})
end
+
+ it "should define a custom module path" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
+ expect(comm.to_s).to include("$env:PSModulePath+';C:\\My-Path'")
+ end
+ described_class.execute("custom-command", module_path: "C:\\My-Path")
+ end
end
describe ".execute_cmd" do
@@ -183,6 +191,15 @@ describe Vagrant::Util::PowerShell do
described_class.execute_cmd("custom-command", env: {"TEST_KEY" => "test-value"})
end
+ it "should define a custom module path" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
+ expect(comm.to_s).to include("$env:PSModulePath+';C:\\My-Path'")
+ result
+ end
+ described_class.execute_cmd("custom-command", module_path: "C:\\My-Path")
+ end
+
context "with command output" do
let(:stdout){ "custom-output" }
@@ -246,6 +263,15 @@ describe Vagrant::Util::PowerShell do
described_class.execute_inline("custom-command", env: {"TEST_KEY" => "test-value"})
end
+ it "should define a custom module path" do
+ expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
+ comm = args.detect{|s| s.to_s.include?("custom-command") }
+ expect(comm.to_s).to include("$env:PSModulePath+';C:\\My-Path'")
+ result
+ end
+ described_class.execute_inline("custom-command", module_path: "C:\\My-Path")
+ end
+
it "should return a result instance" do
expect(described_class.execute_inline("cmd")).to eq(result)
end
From 87b8321702f62fad1b36fa5c00d61d25f0f04333 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 13:54:37 -0700
Subject: [PATCH 43/53] Remove win32 dependencies
---
lib/vagrant.rb | 6 ------
vagrant.gemspec | 2 --
2 files changed, 8 deletions(-)
diff --git a/lib/vagrant.rb b/lib/vagrant.rb
index d980521c6..c775b5f37 100644
--- a/lib/vagrant.rb
+++ b/lib/vagrant.rb
@@ -65,12 +65,6 @@ require 'i18n'
# there are issues with ciphers not being properly loaded.
require 'openssl'
-# If we are on Windows, load in File helpers
-if Vagrant::Util::Platform.windows?
- require "ffi-win32-extensions"
- require "win32/file/security"
-end
-
# Always make the version available
require 'vagrant/version'
global_logger = Log4r::Logger.new("vagrant::global")
diff --git a/vagrant.gemspec b/vagrant.gemspec
index 4096fd684..edc12f032 100644
--- a/vagrant.gemspec
+++ b/vagrant.gemspec
@@ -28,8 +28,6 @@ Gem::Specification.new do |s|
s.add_dependency "rb-kqueue", "~> 0.2.0"
s.add_dependency "rest-client", ">= 1.6.0", "< 3.0"
s.add_dependency "wdm", "~> 0.1.0"
- s.add_dependency "win32-file", "~> 0.8.1"
- s.add_dependency "win32-file-security", "~> 1.0.10"
s.add_dependency "winrm", "~> 2.1"
s.add_dependency "winrm-fs", "~> 1.0"
s.add_dependency "winrm-elevated", "~> 1.1"
From 2628d93370ee46272c1bc3311d1ddd0910870597 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 14:13:10 -0700
Subject: [PATCH 44/53] Rename method name when checking capability
---
plugins/communicators/ssh/communicator.rb | 2 +-
test/unit/plugins/communicators/ssh/communicator_test.rb | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/plugins/communicators/ssh/communicator.rb b/plugins/communicators/ssh/communicator.rb
index a7e1cb0ad..1ae8b9047 100644
--- a/plugins/communicators/ssh/communicator.rb
+++ b/plugins/communicators/ssh/communicator.rb
@@ -195,7 +195,7 @@ module VagrantPlugins
end
# Adjust private key file permissions if host provides capability
- if @machine.env.host.has_capability?(:set_ssh_key_permissions)
+ if @machine.env.host.capability?(:set_ssh_key_permissions)
@machine.env.host.capability(:set_ssh_key_permissions, @machine.data_dir.join("private_key"))
end
diff --git a/test/unit/plugins/communicators/ssh/communicator_test.rb b/test/unit/plugins/communicators/ssh/communicator_test.rb
index d9ccb6835..a236c1931 100644
--- a/test/unit/plugins/communicators/ssh/communicator_test.rb
+++ b/test/unit/plugins/communicators/ssh/communicator_test.rb
@@ -93,7 +93,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end
before do
- allow(host).to receive(:has_capability?).and_return(false)
+ allow(host).to receive(:capability?).and_return(false)
end
describe ".wait_for_ready" do
@@ -216,7 +216,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end
it "should call the set_ssh_key_permissions host capability" do
- expect(host).to receive(:has_capability?).with(:set_ssh_key_permissions).and_return(true)
+ expect(host).to receive(:capability?).with(:set_ssh_key_permissions).and_return(true)
expect(host).to receive(:capability).with(:set_ssh_key_permissions, private_key_file)
end
From 16bcc1d874786c5f4bf2229da7467e4a816a70fa Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:20:30 -0700
Subject: [PATCH 45/53] Include scripts directory when building pathname
---
plugins/hosts/windows/host.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/hosts/windows/host.rb b/plugins/hosts/windows/host.rb
index b92ac335e..5658f9b91 100644
--- a/plugins/hosts/windows/host.rb
+++ b/plugins/hosts/windows/host.rb
@@ -11,7 +11,7 @@ module VagrantPlugins
# @return [Pathname] Path to scripts directory
def self.scripts_path
- Pathname.new(File.expand_path("..", __FILE__))
+ Pathname.new(File.expand_path("../scripts", __FILE__))
end
# @return [Pathname] Path to modules directory
From ae8e25aabcb09722e5aab445d0e6d869e05ad46d Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:20:49 -0700
Subject: [PATCH 46/53] Include key path flag when calling script
---
plugins/hosts/windows/cap/ssh.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/hosts/windows/cap/ssh.rb b/plugins/hosts/windows/cap/ssh.rb
index 8cf0129f7..63415bcb5 100644
--- a/plugins/hosts/windows/cap/ssh.rb
+++ b/plugins/hosts/windows/cap/ssh.rb
@@ -10,7 +10,7 @@ module VagrantPlugins
def self.set_ssh_key_permissions(env, key_path)
script_path = Host.scripts_path.join("set_ssh_key_permissions.ps1")
result = Vagrant::Util::PowerShell.execute(
- script_path.to_s, key_path.to_s,
+ script_path.to_s, "-KeyPath", key_path.to_s,
module_path: Host.modules_path.to_s
)
if result.exit_code != 0
From e39b3555e693414174677eb98c45da8964658d1c Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:21:13 -0700
Subject: [PATCH 47/53] Disable inherit on key file for parent ACL rules
---
.../hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1 | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1 b/plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1
index 04b1e327b..5d49a93db 100644
--- a/plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1
+++ b/plugins/hosts/windows/scripts/utils/VagrantSSH/VagrantSSH.psm1
@@ -15,8 +15,10 @@ function Set-SSHKeyPermissions {
# Create the new ACL we want to apply
$NewAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$Principal, "FullControl", "None", "None", "Allow")
- # Scrub all existing ACLs from the file
$ACL = Get-ACL "${SSHKeyPath}"
+ # Disable inherited rules
+ $ACL.SetAccessRuleProtection($true, $false)
+ # Scrub all existing ACLs from the file
$ACL.Access | %{$ACL.RemoveAccessRule($_)}
# Apply the new ACL
$ACL.SetAccessRule($NewAccessRule)
From d8e64dd9be13a25990702d638fce201e442d1f23 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:24:21 -0700
Subject: [PATCH 48/53] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f254eb220..9abaf056f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ BUG FIXES:
- guest/debain: Update priority of network configuration file when using networkd [GH-9867]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
+- provisioner/chef: Prevent node_name set on configuration with chef_apply [GH-9916]
- provisioner/salt: Remove usage of masterless? config attribute [GH-9833]
## 2.1.1 (May 7, 2018)
From d2723f2008860e45cd41a5ab58e2f5f8c5257694 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:25:44 -0700
Subject: [PATCH 49/53] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9abaf056f..04fdce5d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ BUG FIXES:
- guest/debain: Update priority of network configuration file when using networkd [GH-9867]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
+- host/windows: Prevent processing of last SMB line when using net share [GH-9917]
- provisioner/chef: Prevent node_name set on configuration with chef_apply [GH-9916]
- provisioner/salt: Remove usage of masterless? config attribute [GH-9833]
From 7e0cc99ffd51577fd3b227df2cbd77a07e20c19a Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:30:51 -0700
Subject: [PATCH 50/53] Include flag check on ssh cap test
---
test/unit/plugins/hosts/windows/cap/ssh_test.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/unit/plugins/hosts/windows/cap/ssh_test.rb b/test/unit/plugins/hosts/windows/cap/ssh_test.rb
index e1a9f93fc..e258574e5 100644
--- a/test/unit/plugins/hosts/windows/cap/ssh_test.rb
+++ b/test/unit/plugins/hosts/windows/cap/ssh_test.rb
@@ -18,7 +18,7 @@ describe VagrantPlugins::HostWindows::Cap::SSH do
it "should execute PowerShell script" do
expect(Vagrant::Util::PowerShell).to receive(:execute).with(
- /set_ssh_key_permissions.ps1/, key_path.to_s, any_args
+ /set_ssh_key_permissions.ps1/, "-KeyPath", key_path.to_s, any_args
).and_return(result)
subject.set_ssh_key_permissions(env, key_path)
end
From 305a251c7b1456b5386ba74dd6b959e50024a63c Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:31:53 -0700
Subject: [PATCH 51/53] Update Ruby test versions
---
.travis.yml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index d09bd961d..ccb5c188c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,9 +10,10 @@ addons:
- bsdtar
rvm:
- - 2.3.6
- - 2.4.3
+ - 2.3.7
+ - 2.4.4
- 2.5.0
+ - 2.5.1
branches:
only:
From e43a94b79735f259b88a64f2b6f956a090071743 Mon Sep 17 00:00:00 2001
From: Chris Roberts
Date: Tue, 12 Jun 2018 16:46:57 -0700
Subject: [PATCH 52/53] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04fdce5d2..c3f992d95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ IMPROVEMENTS:
BUG FIXES:
+- communicator/ssh: Update ssh private key file permission handling on Windows [GH-9923, GH-9900]
- core: Display plugin commands in help [GH-9808]
- core: Ensure guestpath or name is set with synced_folder option and dont set guestpath if not provided [GH-9692]
- guest/debian: Fix netplan generation when using DHCP [GH-9855]
From 3ce7c0cf3f84e46b27070f3e006cd01eba02ac17 Mon Sep 17 00:00:00 2001
From: Milan Stovicek <4922479+mstovicek@users.noreply.github.com>
Date: Wed, 13 Jun 2018 18:51:45 +0200
Subject: [PATCH 53/53] Add `suspend` into a list of available actions
`suspend` action is not listed, even though it can be used and it works.
```
$ vagrant --version
Vagrant 2.1.1
```
```
config.trigger.before [:suspend] do |trigger|
trigger.name = "Before suspend"
trigger.run = {inline: "..."}
end
````
---
website/source/docs/triggers/configuration.html.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/website/source/docs/triggers/configuration.html.md b/website/source/docs/triggers/configuration.html.md
index d66938741..bd1581220 100644
--- a/website/source/docs/triggers/configuration.html.md
+++ b/website/source/docs/triggers/configuration.html.md
@@ -21,6 +21,7 @@ The trigger class takes various options.
- `provision`
- `reload`
- `resume`
+ - `suspend`
- `up`
* `ignore` (symbol, array) - Symbol or array of symbols corresponding to the action that a trigger should not fire on.