Merge branch 'master' into debian-systemd-networkd

This commit is contained in:
Trey Tabner 2018-06-13 12:49:50 -05:00 committed by GitHub
commit c218267436
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 3194 additions and 1039 deletions

View File

@ -10,9 +10,10 @@ addons:
- bsdtar
rvm:
- 2.3.6
- 2.4.3
- 2.3.7
- 2.4.4
- 2.5.0
- 2.5.1
branches:
only:

View File

@ -6,12 +6,23 @@ IMPROVEMENTS:
- commands/suspend: Introduce flag for suspending all machines [GH-9829]
- commands/global-status: Improve message about removing stale entries [GH-9856]
- provider/hyper-v: Update implementation. Include support for modifications on reload [GH-9872]
- provisioners/ansible_local: Improve installation from PPA on Ubuntu guests.
The compatibility is maintained only for active long-term support (LTS) versions,
i.e. Ubuntu 12.04 (Precise Pangolin) is no longer supported. [GH-9879]
BUG FIXES:
- communicator/ssh: Update ssh private key file permission handling on Windows [GH-9923, GH-9900]
- core: Display plugin commands in help [GH-9808]
- core: Ensure guestpath or name is set with synced_folder option and dont set guestpath if not provided [GH-9692]
- guest/debian: Fix netplan generation when using DHCP [GH-9855]
- guest/debain: Update priority of network configuration file when using networkd [GH-9867]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
- host/windows: Prevent processing of last SMB line when using net share [GH-9917]
- provisioner/chef: Prevent node_name set on configuration with chef_apply [GH-9916]
- provisioner/salt: Remove usage of masterless? config attribute [GH-9833]
## 2.1.1 (May 7, 2018)

View File

@ -65,12 +65,6 @@ require 'i18n'
# there are issues with ciphers not being properly loaded.
require 'openssl'
# If we are on Windows, load in File helpers
if Vagrant::Util::Platform.windows?
require "ffi-win32-extensions"
require "win32/file/security"
end
# Always make the version available
require 'vagrant/version'
global_logger = Log4r::Logger.new("vagrant::global")

View File

@ -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,30 @@ 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
if mpath = opts.delete(:module_path)
m_env = opts.fetch(:env, {})
m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
opts[:env] = m_env
end
if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
command = [
executable,
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"&('#{path}')",
"#{resize_console}#{env}&('#{path}')",
args
].flatten
@ -68,10 +83,20 @@ 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!
if mpath = opts.delete(:module_path)
m_env = opts.fetch(:env, {})
m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
opts[:env] = m_env
end
if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
c = [
executable,
"-NoLogo",
@ -79,7 +104,7 @@ module Vagrant
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-Command",
command
"#{resize_console}#{env}#{command}"
].flatten.compact
r = Subprocess.execute(*c)
@ -91,9 +116,18 @@ 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!
if mpath = opts.delete(:module_path)
m_env = opts.fetch(:env, {})
m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
opts[:env] = m_env
end
if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
c = [
executable,
"-NoLogo",
@ -101,7 +135,7 @@ module Vagrant
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-Command",
command
"#{resize_console}#{env}#{command}"
].flatten.compact
c << opts
@ -220,6 +254,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

View File

@ -194,17 +194,9 @@ module VagrantPlugins
f.write(priv)
end
# Adjust private key file permissions
if Vagrant::Util::Platform.windows?
begin
priv_path = @machine.data_dir.join("private_key").to_s
File.set_permissions(priv_path, Etc.getlogin => File::FULL)
rescue => e
@logger.warn("Error encountered during private key permissions set - " \
"#{e.class}: #{e.message}")
end
else
@machine.data_dir.join("private_key").chmod(0600)
# Adjust private key file permissions if host provides capability
if @machine.env.host.capability?(:set_ssh_key_permissions)
@machine.env.host.capability(:set_ssh_key_permissions, @machine.data_dir.join("private_key"))
end
# Remove the old key if it exists

View File

@ -37,7 +37,9 @@ module VagrantPlugins
ethernets = {}.tap do |e_nets|
networks.each do |network|
e_config = {}.tap do |entry|
if network[:ip]
if network[:type].to_s == "dhcp"
entry["dhcp4"] = true
else
mask = network[:netmask]
if mask && IPAddr.new(network[:ip]).ipv4?
begin
@ -47,8 +49,6 @@ module VagrantPlugins
end
end
entry["addresses"] = [[network[:ip], mask].compact.join("/")]
else
entry["dhcp4"] = true
end
if network[:gateway]
entry["gateway4"] = network[:gateway]
@ -107,7 +107,7 @@ module VagrantPlugins
end
remote_path = upload_tmp_file(comm, net_conf.join("\n"))
dest_path = "#{NETWORKD_DIRECTORY}/10-vagrant-#{dev_name}.network"
dest_path = "#{NETWORKD_DIRECTORY}/50-vagrant-#{dev_name}.network"
comm.sudo(["mkdir -p #{NETWORKD_DIRECTORY}",
"mv -f '#{remote_path}' '#{dest_path}'",
"chown root:root '#{dest_path}'",

View File

@ -0,0 +1,16 @@
module VagrantPlugins
module HostBSD
module Cap
class SSH
# Set the ownership and permissions for SSH
# private key
#
# @param [Vagrant::Environment] env
# @param [Pathname] key_path
def self.set_ssh_key_permissions(env, key_path)
key_path.chmod(0600)
end
end
end
end
end

View File

@ -35,6 +35,11 @@ module VagrantPlugins
require_relative "cap/nfs"
Cap::NFS
end
host_capability("bsd", "set_ssh_key_permissions") do
require_relative "cap/ssh"
Cap::SSH
end
end
end
end

View File

@ -0,0 +1,16 @@
module VagrantPlugins
module HostLinux
module Cap
class SSH
# Set the ownership and permissions for SSH
# private key
#
# @param [Vagrant::Environment] env
# @param [Pathname] key_path
def self.set_ssh_key_permissions(env, key_path)
key_path.chmod(0600)
end
end
end
end
end

View File

@ -47,6 +47,11 @@ module VagrantPlugins
require_relative "cap/nfs"
Cap::NFS
end
host_capability("linux", "set_ssh_key_permissions") do
require_relative "cap/ssh"
Cap::SSH
end
end
end
end

View File

@ -38,7 +38,7 @@ module VagrantPlugins
m_id = machine_id(machine)
prune_shares = existing_shares.map do |share_name, share_info|
if share_info["Description"].start_with?("vgt-#{m_id}-")
if share_info["Description"].to_s.start_with?("vgt-#{m_id}-")
@@logger.info("removing smb share name=#{share_name} id=#{m_id}")
share_name
else
@ -159,6 +159,8 @@ module VagrantPlugins
share_names = result.strip.split("\n").map do |line|
line.strip.split(/\s+/).first
end
# Last item is command completion notification so remove it
share_names.pop
shares = {}
share_names.each do |share_name|
shares[share_name] = {}

View File

@ -0,0 +1,26 @@
module VagrantPlugins
module HostWindows
module Cap
class SSH
# Set the ownership and permissions for SSH
# private key
#
# @param [Vagrant::Environment] env
# @param [Pathname] key_path
def self.set_ssh_key_permissions(env, key_path)
script_path = Host.scripts_path.join("set_ssh_key_permissions.ps1")
result = Vagrant::Util::PowerShell.execute(
script_path.to_s, "-KeyPath", key_path.to_s,
module_path: Host.modules_path.to_s
)
if result.exit_code != 0
raise Vagrant::Errors::PowerShellError,
script: script_path,
stderr: result.stderr
end
result
end
end
end
end
end

View File

@ -8,6 +8,16 @@ module VagrantPlugins
def detect?(env)
Vagrant::Util::Platform.windows?
end
# @return [Pathname] Path to scripts directory
def self.scripts_path
Pathname.new(File.expand_path("../scripts", __FILE__))
end
# @return [Pathname] Path to modules directory
def self.modules_path
scripts_path.join("utils")
end
end
end
end

View File

@ -55,6 +55,11 @@ module VagrantPlugins
require_relative "cap/configured_ip_addresses"
Cap::ConfiguredIPAddresses
end
host_capability("windows", "set_ssh_key_permissions") do
require_relative "cap/ssh"
Cap::SSH
end
end
end
end

View File

@ -0,0 +1,17 @@
#Requires -Modules VagrantSSH
param(
[Parameter(Mandatory=$true)]
[string] $KeyPath,
[Parameter(Mandatory=$false)]
[string] $Principal=$null
)
$ErrorActionPreference = "Stop"
try {
Set-SSHKeyPermissions -SSHKeyPath $KeyPath -Principal $Principal
} catch {
Write-Error "Failed to set permissions on key: ${PSItem}"
exit 1
}

View File

@ -0,0 +1,26 @@
# Vagrant SSH capability functions
function Set-SSHKeyPermissions {
param (
[parameter(Mandatory=$true)]
[string] $SSHKeyPath,
[parameter(Mandatory=$false)]
[string] $Principal=$null
)
if(!$Principal) {
$Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
}
# Create the new ACL we want to apply
$NewAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$Principal, "FullControl", "None", "None", "Allow")
$ACL = Get-ACL "${SSHKeyPath}"
# Disable inherited rules
$ACL.SetAccessRuleProtection($true, $false)
# Scrub all existing ACLs from the file
$ACL.Access | %{$ACL.RemoveAccessRule($_)}
# Apply the new ACL
$ACL.SetAccessRule($NewAccessRule)
Set-ACL "${SSHKeyPath}" $ACL
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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}"
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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
{

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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 $Enable
} catch {
if($Enable){ $action = "enable" } else { $action = "disable" }
Write-Error-Message "Failed to ${action} VM integration service ${Name}: ${PSItem}"
exit 1
}

View File

@ -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
}
catch {
Write-Error-Message "Failed to start a VM $_"
$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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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==="
}

View File

@ -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.
#>
}

View File

@ -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
}
}

View File

@ -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==="
}

View File

@ -18,11 +18,17 @@ module VagrantPlugins
private
def self.ansible_apt_install(machine)
machine.communicate.sudo "apt-get update -y -qq"
machine.communicate.sudo "apt-get install -y -qq software-properties-common python-software-properties"
machine.communicate.sudo "add-apt-repository ppa:ansible/ansible -y"
machine.communicate.sudo "apt-get update -y -qq"
machine.communicate.sudo "apt-get install -y -qq ansible"
unless machine.communicate.test("test -x \"$(which add-apt-repository)\"")
machine.communicate.sudo """
apt-get update -y -qq && \
apt-get install -y -qq software-properties-common
"""
end
machine.communicate.sudo """
add-apt-repository ppa:ansible/ansible -y && \
apt-get update -y -qq && \
apt-get install -y -qq ansible
"""
end
end

View File

@ -24,7 +24,7 @@ module VagrantPlugins
@logger = Log4r::Logger.new("vagrant::provisioners::chef")
if !present?(@config.node_name)
if @config.respond_to?(:node_name) && !present?(@config.node_name)
# First attempt to get the node name from the hostname, and if that
# is not present, generate/retrieve a random hostname.
hostname = @machine.config.vm.hostname

View File

@ -385,7 +385,7 @@ module VagrantPlugins
@machine.env.ui.info "Calling state.highstate... (this may take a while)"
if @config.install_master
unless @config.masterless?
unless @config.masterless
@machine.communicate.sudo("salt '*' saltutil.sync_all")
end
options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_salt_args}"
@ -397,7 +397,7 @@ module VagrantPlugins
else
if @machine.config.vm.communicator == :winrm
opts = { elevated: true }
unless @config.masterless?
unless @config.masterless
@machine.communicate.execute("C:\\salt\\salt-call.bat saltutil.sync_all", opts)
end
# TODO: something equivalent to { error_key: :ssh_bad_exit_status_muted }?
@ -408,7 +408,7 @@ module VagrantPlugins
end
end
else
unless @config.masterless?
unless @config.masterless
@machine.communicate.sudo("salt-call saltutil.sync_all")
end
options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_call_args}"

View File

@ -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
}

View File

@ -1 +0,0 @@
Write-Output $PSVersionTable.PSVersion.Major

View File

@ -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

View File

@ -34,9 +34,12 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
double("machine",
config: config,
provider: provider,
ui: ui
ui: ui,
env: env
)
end
let(:env){ double("env", host: host) }
let(:host){ double("host") }
# SSH information of the machine
let(:machine_ssh_info){ {host: '10.1.2.3', port: 22} }
# Subject instance to test
@ -89,6 +92,10 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
allow(communicator).to receive(:retryable).and_return(connection)
end
before do
allow(host).to receive(:capability?).and_return(false)
end
describe ".wait_for_ready" do
before(&connection_setup)
context "with no static config (default scenario)" do
@ -208,41 +215,14 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
expect(private_key_file).to receive(:write).with(new_private_key)
end
it "should set private key file as user readable only" do
expect(private_key_file).to receive(:chmod).with(0600)
it "should call the set_ssh_key_permissions host capability" do
expect(host).to receive(:capability?).with(:set_ssh_key_permissions).and_return(true)
expect(host).to receive(:capability).with(:set_ssh_key_permissions, private_key_file)
end
it "should remove the default public key" do
expect(guest).to receive(:capability).with(:remove_public_key, any_args)
end
context "on windows platform" do
let(:owner){ "owner" }
before do
allow(private_key_file).to receive(:to_s).and_return("PRIVATE_KEY_PATH")
allow(File).to receive(:set_permissions)
allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true)
allow(Etc).to receive(:getlogin).and_return(owner)
stub_const('File::FULL', :full)
end
it "should get set new permissions on private key file" do
expect(File).to receive(:set_permissions).with("PRIVATE_KEY_PATH", any_args)
end
it "should proceed when error is encountered" do
expect(File).to receive(:set_permissions).and_raise(StandardError)
end
context "with multiple permissions on file" do
it "should delete all non-owner permissions" do
expect(File).to receive(:set_permissions).with("PRIVATE_KEY_PATH",
owner => :full)
end
end
end
end
end
end

View File

@ -0,0 +1,15 @@
require_relative "../../../../base"
require_relative "../../../../../../plugins/hosts/bsd/cap/ssh"
describe VagrantPlugins::HostBSD::Cap::SSH do
let(:subject){ VagrantPlugins::HostBSD::Cap::SSH }
let(:env){ double("env") }
let(:key_path){ double("key_path") }
it "should set file as user only read/write" do
expect(key_path).to receive(:chmod).with(0600)
subject.set_ssh_key_permissions(env, key_path)
end
end

View File

@ -0,0 +1,15 @@
require_relative "../../../../base"
require_relative "../../../../../../plugins/hosts/linux/cap/ssh"
describe VagrantPlugins::HostLinux::Cap::SSH do
let(:subject){ VagrantPlugins::HostLinux::Cap::SSH }
let(:env){ double("env") }
let(:key_path){ double("key_path") }
it "should set file as user only read/write" do
expect(key_path).to receive(:chmod).with(0600)
subject.set_ssh_key_permissions(env, key_path)
end
end

View File

@ -31,7 +31,7 @@ Share name Resource Remark
vgt-CUSTOM_ID-1 /a/path vgt-CUSTOM_ID-1
vgt-CUSTOM_ID-2 /other/path vgt-CUSTOM_ID-2
my-share /my/path Not Vagran...
The command completed successfully.
EOF
}
let(:netshare1){ <<-EOF

View File

@ -0,0 +1,38 @@
require_relative "../../../../base"
require_relative "../../../../../../plugins/hosts/windows/cap/ssh"
describe VagrantPlugins::HostWindows::Cap::SSH do
let(:subject){ VagrantPlugins::HostWindows::Cap::SSH }
let(:result){ Vagrant::Util::Subprocess::Result.new(exit_code, stdout, stderr) }
let(:exit_code){ 0 }
let(:stdout){ "" }
let(:stderr){ "" }
let(:key_path){ double("keypath", to_s: "keypath") }
let(:env){ double("env") }
before do
allow(Vagrant::Util::PowerShell).to receive(:execute).and_return(result)
end
it "should execute PowerShell script" do
expect(Vagrant::Util::PowerShell).to receive(:execute).with(
/set_ssh_key_permissions.ps1/, "-KeyPath", key_path.to_s, any_args
).and_return(result)
subject.set_ssh_key_permissions(env, key_path)
end
it "should return the result" do
expect(subject.set_ssh_key_permissions(env, key_path)).to eq(result)
end
context "when command fails" do
let(:exit_code){ 1 }
it "should raise an error" do
expect{ subject.set_ssh_key_permissions(env, key_path) }.to raise_error(Vagrant::Errors::PowerShellError)
end
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
@ -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

View File

@ -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

View File

@ -39,16 +39,21 @@ describe VagrantPlugins::Chef::Provisioner::Base do
it "defaults to hostname if given" do
machine.config.vm.hostname = "by.hostname"
instance = described_class.new(machine, OpenStruct.new)
instance = described_class.new(machine, OpenStruct.new(node_name: nil))
expect(instance.config.node_name).to eq("by.hostname")
end
it "generates a random name if no hostname or node_name is given" do
config = OpenStruct.new(node_name: nil)
machine.config.vm.hostname = nil
instance = described_class.new(machine, OpenStruct.new)
instance = described_class.new(machine, OpenStruct.new(node_name: nil))
expect(instance.config.node_name).to match(/vagrant\-.+/)
end
it "does not set node_name if configuration does not define it" do
expect(config).to receive(:respond_to?).with(:node_name).and_return(false)
expect(config).not_to receive(:node_name)
described_class.new(machine, config)
end
end
describe "#encrypted_data_bag_secret_key_path" do

View File

@ -55,7 +55,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "passes along extra cli flags" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
allow(config).to receive(:masterless?).and_return(false)
allow(config).to receive(:masterless).and_return(false)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)
@ -74,7 +73,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "has no additional cli flags if not included" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
allow(config).to receive(:masterless?).and_return(false)
allow(config).to receive(:masterless).and_return(false)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)
@ -95,7 +93,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "passes along extra cli flags" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
allow(config).to receive(:masterless?).and_return(true)
allow(config).to receive(:masterless).and_return(true)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)
@ -115,7 +112,6 @@ describe VagrantPlugins::Salt::Provisioner do
it "has no additional cli flags if not included" do
allow(config).to receive(:run_highstate).and_return(true)
allow(config).to receive(:verbose).and_return(true)
allow(config).to receive(:masterless?).and_return(true)
allow(config).to receive(:masterless).and_return(true)
allow(config).to receive(:minion_id).and_return(nil)
allow(config).to receive(:log_level).and_return(nil)

View File

@ -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,272 @@ 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
it "should define a custom module path" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("$env:PSModulePath+';C:\\My-Path'")
end
described_class.execute("custom-command", module_path: "C:\\My-Path")
end
end
describe ".execute_cmd" do
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
it "should define a custom module path" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("$env:PSModulePath+';C:\\My-Path'")
result
end
described_class.execute_cmd("custom-command", module_path: "C:\\My-Path")
end
context "with command output" do
let(:stdout){ "custom-output" }
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 define a custom module path" do
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args|
comm = args.detect{|s| s.to_s.include?("custom-command") }
expect(comm.to_s).to include("$env:PSModulePath+';C:\\My-Path'")
result
end
described_class.execute_inline("custom-command", module_path: "C:\\My-Path")
end
it "should return a result instance" do
expect(described_class.execute_inline("cmd")).to eq(result)
end
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

View File

@ -28,8 +28,6 @@ Gem::Specification.new do |s|
s.add_dependency "rb-kqueue", "~> 0.2.0"
s.add_dependency "rest-client", ">= 1.6.0", "< 3.0"
s.add_dependency "wdm", "~> 0.1.0"
s.add_dependency "win32-file", "~> 0.8.1"
s.add_dependency "win32-file-security", "~> 1.0.10"
s.add_dependency "winrm", "~> 2.1"
s.add_dependency "winrm-fs", "~> 1.0"
s.add_dependency "winrm-elevated", "~> 1.1"

View File

@ -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.
* `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)
Example:
## VM Integration Services
```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
```
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.

View File

@ -24,7 +24,7 @@ To enable Hyper-V, go to "Programs and Features", click on "Turn Windows
features on or off" and check the box next to "Hyper-V". Or install via
PowerShell with:
<code>Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All</code>.
<code>Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All</code>
See official documentation [here](https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v).

View File

@ -73,7 +73,7 @@ This section lists the _specific_ options for the Ansible Local provisioner. In
- `install_mode` (`:default`, `:pip`, or `:pip_args_only`) - Select the way to automatically install Ansible on the guest system.
- `:default`: Ansible is installed from the operating system package manager. This mode doesn't support `version` selection. For many platforms (e.g Debian, FreeBSD, OpenSUSE) the official package repository is used, except for the following Linux distributions:
- On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository.
- On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository. The compatibility is maintained only for active long-term support (LTS) versions.
- On RedHat-like systems, the latest Ansible release is installed from the [EPEL](http://fedoraproject.org/wiki/EPEL) repository.
- `:pip`: Ansible is installed from [PyPI](https://pypi.python.org/pypi) with [pip](https://pip.pypa.io) package installer. With this mode, Vagrant will systematically try to [install the latest pip version](https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py). With the `:pip` mode you can optionally install a specific Ansible release by setting the [`version`](/docs/provisioning/ansible_common.html#version) option.

View File

@ -21,6 +21,7 @@ The trigger class takes various options.
- `provision`
- `reload`
- `resume`
- `suspend`
- `up`
* `ignore` (symbol, array) - Symbol or array of symbols corresponding to the action that a trigger should not fire on.

View File

@ -1234,7 +1234,7 @@ Response body is identical to [Reading a provider](#read-a-provider).
* `provider`
* `name` - The name of the provider.
* `url` - A valid URL to download this provider. If omitted, you must [upload](#TODO) the Vagrant box image for this provider to Vagrant Cloud before the provider can be used.
* `url` - A valid URL to download this provider. If omitted, you must [upload](#upload-a-provider) the Vagrant box image for this provider to Vagrant Cloud before the provider can be used.
#### Example Request

View File

@ -155,18 +155,26 @@ is:
config.ssh.export_command_template = 'export %ENV_KEY%="%ENV_VALUE%"'
```
<hr>
`config.ssh.sudo_command` - The command to use when executing a command
with `sudo`. This defaults to `sudo -E -H %c`. The `%c` will be replaced by
the command that is being executed.
<hr>
`config.ssh.compression` - If `false`, this setting will not include the
compression setting when ssh'ing into a machine. If this is not set, it will
default to `true` and `Compression=yes` will be enabled with ssh.
<hr>
`config.ssh.dsa_authentication` - If `false`, this setting will not include
`DSAAuthentication` when ssh'ing into a machine. If this is not set, it will
default to `true` and `DSAAuthentication=yes` will be used with ssh.
<hr>
`config.ssh.extra_args` - This settings value is passed directly into the
ssh executable. This allows you to pass any arbitrary commands to do things such
as reverse tunneling down into the ssh program. These options can either be

View File

@ -28,6 +28,9 @@ description: |-
which has been signed using <a href="https://www.hashicorp.com/security.html" target="_blank" rel="nofollow noopener noreferrer">HashiCorp's GPG key</a>.
You can also <a href="https://releases.hashicorp.com/vagrant/" target="_blank" rel="nofollow noopener noreferrer">download older versions of Vagrant</a> from the releases service.
</p>
<p>
Check out the <a href="https://github.com/<%= github_slug %>/blob/v<%= latest_version %>/CHANGELOG.md">v<%= latest_version %> CHANGELOG</a> for information on the latest release.
</p>
</div>
</div>