Merge pull request #9872 from chrisroberts/e-hyperv-2
Hyper-V provider overhaul
This commit is contained in:
commit
120fa07a8d
|
@ -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,19 +45,26 @@ module Vagrant
|
|||
# Execute a powershell script.
|
||||
#
|
||||
# @param [String] path Path to the PowerShell script to execute.
|
||||
# @param [Array<String>] 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!
|
||||
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}')",
|
||||
"#{resize_console}#{env}&('#{path}')",
|
||||
args
|
||||
].flatten
|
||||
|
||||
|
@ -68,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",
|
||||
|
@ -79,7 +96,7 @@ module Vagrant
|
|||
"-NonInteractive",
|
||||
"-ExecutionPolicy", "Bypass",
|
||||
"-Command",
|
||||
command
|
||||
"#{resize_console}#{env}#{command}"
|
||||
].flatten.compact
|
||||
|
||||
r = Subprocess.execute(*c)
|
||||
|
@ -91,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",
|
||||
|
@ -101,7 +123,7 @@ module Vagrant
|
|||
"-NonInteractive",
|
||||
"-ExecutionPolicy", "Bypass",
|
||||
"-Command",
|
||||
command
|
||||
"#{resize_console}#{env}#{command}"
|
||||
].flatten.compact
|
||||
c << opts
|
||||
|
||||
|
@ -220,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
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].to_s.downcase ||
|
||||
s["Id"].downcase == opts[:bridge].to_s.downcase
|
||||
}
|
||||
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
|
||||
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.empty?
|
||||
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
|
|
@ -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?
|
||||
raise Errors::BoxInvalid
|
||||
@logger.error("Required virtual machine directory not found!")
|
||||
raise Errors::BoxInvalid, name: env[:machine].name
|
||||
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, name: env[:machine].name
|
||||
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
|
||||
raise Errors::BoxInvalid
|
||||
if !image_path
|
||||
@logger.error("Failed to locate box image path")
|
||||
raise Errors::BoxInvalid, name: env[:machine].name
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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,21 +55,23 @@ 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
|
||||
@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
|
||||
}
|
||||
@enable_checkpoints = UNSET_VALUE
|
||||
@vm_integration_services = {}
|
||||
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
|
||||
@differencing_disk ||= @linked_clone
|
||||
if @ip_address_timeout == UNSET_VALUE
|
||||
@ip_address_timeout = 120
|
||||
end
|
||||
|
@ -48,20 +81,46 @@ 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
|
||||
end
|
||||
|
||||
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)
|
||||
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,
|
||||
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
|
||||
|
|
|
@ -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<state, status>]
|
||||
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<ip>]
|
||||
def read_guest_ip
|
||||
execute(:get_network_config, VmId: vm_id)
|
||||
end
|
||||
|
||||
# Get the MAC address of the VM
|
||||
#
|
||||
# @return [Hash<mac>]
|
||||
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<id>] 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<String>] 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).to_s}
|
||||
args[:Enable] = 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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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 $_"
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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==="
|
||||
}
|
|
@ -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.
|
||||
#>
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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==="
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
Write-Output $PSVersionTable.PSVersion.Major
|
|
@ -11,6 +11,34 @@ 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
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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, name: "machname") }
|
||||
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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -3,6 +3,10 @@ require_relative "../../../base"
|
|||
require Vagrant.source_root.join("plugins/providers/hyperv/config")
|
||||
|
||||
describe VagrantPlugins::HyperV::Config do
|
||||
|
||||
let(:machine){ double("machine", ui: ui) }
|
||||
let(:ui){ double("ui") }
|
||||
|
||||
describe "#ip_address_timeout" do
|
||||
it "can be set" do
|
||||
subject.ip_address_timeout = 180
|
||||
|
@ -30,7 +34,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 +66,159 @@ 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
|
||||
|
||||
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
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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.to_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.to_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.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"})
|
||||
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.to_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.to_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.to_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.to_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.to_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.to_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
|
||||
|
|
|
@ -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
|
||||
```
|
||||
* `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) - 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.
|
||||
|
|
Loading…
Reference in New Issue