Merge pull request #11541 from briancain/feature/hyperv-disk-mgmt
[Feature] Hyper-V Virtual Hard Disk Management
This commit is contained in:
commit
88c2bc2be1
@ -12,6 +12,8 @@ module VagrantPlugins
|
||||
|
||||
DEFAULT_DISK_TYPES = [:disk, :dvd, :floppy].freeze
|
||||
|
||||
FILE_CHAR_REGEX = /[^-a-z0-9_]/i.freeze
|
||||
|
||||
# Note: This value is for internal use only
|
||||
#
|
||||
# @return [String]
|
||||
@ -97,7 +99,11 @@ module VagrantPlugins
|
||||
end
|
||||
|
||||
current = @provider_config.merge(current) if !@provider_config.empty?
|
||||
@provider_config = current
|
||||
if current
|
||||
@provider_config = current[:provider_config]
|
||||
else
|
||||
@provider_config = {}
|
||||
end
|
||||
end
|
||||
|
||||
def finalize!
|
||||
@ -107,27 +113,25 @@ module VagrantPlugins
|
||||
@size = nil if @size == UNSET_VALUE
|
||||
@file = nil if @file == UNSET_VALUE
|
||||
|
||||
@disk_ext = "vdi" if @disk_ext == UNSET_VALUE
|
||||
|
||||
if @primary == UNSET_VALUE
|
||||
@primary = false
|
||||
end
|
||||
|
||||
if @name == UNSET_VALUE
|
||||
if @name.is_a?(String) && @name.match(FILE_CHAR_REGEX)
|
||||
@logger.warn("Vagrant will remove detected invalid characters in '#{@name}' and convert the disk name into something usable for a file")
|
||||
@name.gsub!(FILE_CHAR_REGEX, "_")
|
||||
elsif @name == UNSET_VALUE
|
||||
if @primary
|
||||
@name = "vagrant_primary"
|
||||
else
|
||||
@name = nil
|
||||
end
|
||||
end
|
||||
|
||||
@provider_config = nil if @provider_config == {}
|
||||
end
|
||||
|
||||
# @return [Array] array of strings of error messages from config option validation
|
||||
def validate(machine)
|
||||
errors = _detected_errors
|
||||
|
||||
# validate type with list of known disk types
|
||||
|
||||
if !DEFAULT_DISK_TYPES.include?(@type)
|
||||
@ -135,13 +139,20 @@ module VagrantPlugins
|
||||
types: DEFAULT_DISK_TYPES.join(', '))
|
||||
end
|
||||
|
||||
if @disk_ext
|
||||
if @disk_ext == UNSET_VALUE
|
||||
if machine.provider.capability?(:set_default_disk_ext)
|
||||
@disk_ext = machine.provider.capability(:set_default_disk_ext)
|
||||
else
|
||||
@logger.warn("No provider capability defined to set default 'disk_ext' type. Will use 'vdi' for disk extension.")
|
||||
@disk_ext = "vdi"
|
||||
end
|
||||
elsif @disk_ext
|
||||
@disk_ext = @disk_ext.downcase
|
||||
|
||||
if machine.provider.capability?(:validate_disk_ext)
|
||||
if !machine.provider.capability(:validate_disk_ext, @disk_ext)
|
||||
if machine.provider.capability?(:get_default_disk_ext)
|
||||
disk_exts = machine.provider.capability(:get_default_disk_ext).join(', ')
|
||||
if machine.provider.capability?(:default_disk_exts)
|
||||
disk_exts = machine.provider.capability(:default_disk_exts).join(', ')
|
||||
else
|
||||
disk_exts = "not found"
|
||||
end
|
||||
@ -158,12 +169,13 @@ module VagrantPlugins
|
||||
if @size.is_a?(String)
|
||||
@size = Vagrant::Util::Numeric.string_to_bytes(@size)
|
||||
end
|
||||
|
||||
if !@size
|
||||
errors << I18n.t("vagrant.config.disk.invalid_size", name: @name, machine: machine.name)
|
||||
end
|
||||
end
|
||||
|
||||
if !@size
|
||||
errors << I18n.t("vagrant.config.disk.invalid_size", name: @name, machine: machine.name)
|
||||
end
|
||||
|
||||
|
||||
if @file
|
||||
if !@file.is_a?(String)
|
||||
errors << I18n.t("vagrant.config.disk.invalid_file_type", file: @file, machine: machine.name)
|
||||
@ -174,10 +186,12 @@ module VagrantPlugins
|
||||
end
|
||||
|
||||
if @provider_config
|
||||
if !@provider_config.keys.include?(machine.provider_name)
|
||||
machine.env.ui.warn(I18n.t("vagrant.config.disk.missing_provider",
|
||||
machine: machine.name,
|
||||
provider_name: machine.provider_name))
|
||||
if !@provider_config.empty?
|
||||
if !@provider_config.key?(machine.provider_name)
|
||||
machine.env.ui.warn(I18n.t("vagrant.config.disk.missing_provider",
|
||||
machine: machine.name,
|
||||
provider_name: machine.provider_name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -149,6 +149,8 @@ module VagrantPlugins
|
||||
b3.use NetSetMac
|
||||
end
|
||||
|
||||
b3.use CleanupDisks
|
||||
b3.use Disk
|
||||
b3.use StartInstance
|
||||
b3.use WaitForIPAddress
|
||||
b3.use WaitForCommunicator, [:running]
|
||||
|
||||
54
plugins/providers/hyperv/cap/cleanup_disks.rb
Normal file
54
plugins/providers/hyperv/cap/cleanup_disks.rb
Normal file
@ -0,0 +1,54 @@
|
||||
require "log4r"
|
||||
require "vagrant/util/experimental"
|
||||
|
||||
module VagrantPlugins
|
||||
module HyperV
|
||||
module Cap
|
||||
module CleanupDisks
|
||||
LOGGER = Log4r::Logger.new("vagrant::plugins::hyperv::cleanup_disks")
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [VagrantPlugins::Kernel_V2::VagrantConfigDisk] defined_disks
|
||||
# @param [Hash] disk_meta_file - A hash of all the previously defined disks from the last configure_disk action
|
||||
def self.cleanup_disks(machine, defined_disks, disk_meta_file)
|
||||
return if disk_meta_file.values.flatten.empty?
|
||||
|
||||
return if !Vagrant::Util::Experimental.feature_enabled?("disks")
|
||||
|
||||
handle_cleanup_disk(machine, defined_disks, disk_meta_file["disk"])
|
||||
# TODO: Floppy and DVD disks
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [VagrantPlugins::Kernel_V2::VagrantConfigDisk] defined_disks
|
||||
# @param [Hash] disk_meta - A hash of all the previously defined disks from the last configure_disk action
|
||||
def self.handle_cleanup_disk(machine, defined_disks, disk_meta)
|
||||
all_disks = machine.provider.driver.list_hdds
|
||||
|
||||
disk_meta.each do |d|
|
||||
# look at Path instead of Name or UUID
|
||||
disk_name = File.basename(d["Path"], '.*')
|
||||
dsk = defined_disks.select { |dk| dk.name == disk_name }
|
||||
|
||||
if !dsk.empty? || d["primary"] == true
|
||||
next
|
||||
else
|
||||
LOGGER.warn("Found disk not in Vagrantfile config: '#{d["Name"]}'. Removing disk from guest #{machine.name}")
|
||||
|
||||
machine.ui.warn(I18n.t("vagrant.cap.cleanup_disks.disk_cleanup", name: d["Name"]), prefix: true)
|
||||
|
||||
disk_actual = all_disks.select { |a| File.realdirpath(a["Path"]) == File.realdirpath(d["Path"]) }.first
|
||||
if !disk_actual
|
||||
machine.ui.warn(I18n.t("vagrant.cap.cleanup_disks.disk_not_found", name: d["Name"]), prefix: true)
|
||||
else
|
||||
machine.provider.driver.remove_disk(disk_actual["ControllerType"], disk_actual["ControllerNumber"], disk_actual["ControllerLocation"], disk_actual["Path"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
200
plugins/providers/hyperv/cap/configure_disks.rb
Normal file
200
plugins/providers/hyperv/cap/configure_disks.rb
Normal file
@ -0,0 +1,200 @@
|
||||
require "log4r"
|
||||
require "fileutils"
|
||||
require "vagrant/util/numeric"
|
||||
require "vagrant/util/experimental"
|
||||
|
||||
module VagrantPlugins
|
||||
module HyperV
|
||||
module Cap
|
||||
module ConfigureDisks
|
||||
LOGGER = Log4r::Logger.new("vagrant::plugins::hyperv::configure_disks")
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [VagrantPlugins::Kernel_V2::VagrantConfigDisk] defined_disks
|
||||
# @return [Hash] configured_disks - A hash of all the current configured disks
|
||||
def self.configure_disks(machine, defined_disks)
|
||||
return {} if defined_disks.empty?
|
||||
|
||||
return {} if !Vagrant::Util::Experimental.feature_enabled?("disks")
|
||||
|
||||
machine.ui.info(I18n.t("vagrant.cap.configure_disks.start"))
|
||||
|
||||
current_disks = machine.provider.driver.list_hdds
|
||||
|
||||
configured_disks = {disk: [], floppy: [], dvd: []}
|
||||
|
||||
defined_disks.each do |disk|
|
||||
if disk.type == :disk
|
||||
disk_data = handle_configure_disk(machine, disk, current_disks)
|
||||
configured_disks[:disk] << disk_data unless disk_data.empty?
|
||||
elsif disk.type == :floppy
|
||||
# TODO: Write me
|
||||
machine.ui.info(I18n.t("vagrant.cap.configure_disks.floppy_not_supported", name: disk.name))
|
||||
elsif disk.type == :dvd
|
||||
# TODO: Write me
|
||||
machine.ui.info(I18n.t("vagrant.cap.configure_disks.dvd_not_supported", name: disk.name))
|
||||
end
|
||||
end
|
||||
|
||||
configured_disks
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# @param [Vagrant::Machine] machine - the current machine
|
||||
# @param [Config::Disk] disk - the current disk to configure
|
||||
# @param [Array] all_disks - A list of all currently defined disks in VirtualBox
|
||||
# @return [Hash] current_disk - Returns the current disk. Returns nil if it doesn't exist
|
||||
def self.get_current_disk(machine, disk, all_disks)
|
||||
current_disk = nil
|
||||
if disk.primary
|
||||
# Ensure we grab the proper primary disk
|
||||
# We can't rely on the order of `all_disks`, as they will not
|
||||
# always come in port order, but primary should always be Location 0 Number 0.
|
||||
|
||||
current_disk = all_disks.detect { |d| d["ControllerLocation"] == 0 && d["ControllerNumber"] == 0 }
|
||||
|
||||
# Need to get actual disk info to obtain UUID instead of what's returned
|
||||
#
|
||||
# This is not required for newly created disks, as its metadata is
|
||||
# set when creating and attaching the disk. This is only for the primary
|
||||
# disk, since it already exists.
|
||||
current_disk = machine.provider.driver.get_disk(current_disk["Path"])
|
||||
else
|
||||
# Hyper-V disk names aren't the actual names of the disk, so we have
|
||||
# to grab the name from the file path instead
|
||||
current_disk = all_disks.detect { |d| File.basename(d["Path"], '.*') == disk.name}
|
||||
end
|
||||
|
||||
current_disk
|
||||
end
|
||||
|
||||
# Handles all disk configs of type `:disk`
|
||||
#
|
||||
# @param [Vagrant::Machine] machine - the current machine
|
||||
# @param [Config::Disk] disk - the current disk to configure
|
||||
# @param [Array] all_disks - A list of all currently defined disks in VirtualBox
|
||||
# @return [Hash] - disk_metadata
|
||||
def self.handle_configure_disk(machine, disk, all_disks)
|
||||
disk_metadata = {}
|
||||
|
||||
# Grab the existing configured disk, if it exists
|
||||
current_disk = get_current_disk(machine, disk, all_disks)
|
||||
|
||||
# Configure current disk
|
||||
if !current_disk
|
||||
# create new disk and attach
|
||||
disk_metadata = create_disk(machine, disk)
|
||||
elsif compare_disk_size(machine, disk, current_disk)
|
||||
disk_metadata = resize_disk(machine, disk, current_disk)
|
||||
else
|
||||
disk_metadata = {UUID: current_disk["DiskIdentifier"], Name: disk.name, Path: current_disk["Path"]}
|
||||
if disk.primary
|
||||
disk_metadata[:primary] = true
|
||||
end
|
||||
end
|
||||
|
||||
disk_metadata
|
||||
end
|
||||
|
||||
# Check to see if current disk is configured based on defined_disks
|
||||
#
|
||||
# @param [Kernel_V2::VagrantConfigDisk] disk_config
|
||||
# @param [Hash] defined_disk
|
||||
# @return [Boolean]
|
||||
def self.compare_disk_size(machine, disk_config, defined_disk)
|
||||
# Hyper-V returns disk size in bytes
|
||||
requested_disk_size = disk_config.size
|
||||
disk_actual = machine.provider.driver.get_disk(defined_disk["Path"])
|
||||
defined_disk_size = disk_actual["Size"]
|
||||
|
||||
if defined_disk_size > requested_disk_size
|
||||
if File.extname(disk_actual["Path"]) == ".vhdx"
|
||||
# VHDX formats can be shrunk
|
||||
return true
|
||||
else
|
||||
machine.ui.warn(I18n.t("vagrant.cap.configure_disks.shrink_size_not_supported", name: disk_config.name))
|
||||
return false
|
||||
end
|
||||
elsif defined_disk_size < requested_disk_size
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Creates and attaches a disk to a machine
|
||||
#
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [Kernel_V2::VagrantConfigDisk] disk_config
|
||||
def self.create_disk(machine, disk_config)
|
||||
machine.ui.detail(I18n.t("vagrant.cap.configure_disks.create_disk", name: disk_config.name))
|
||||
disk_provider_config = {}
|
||||
|
||||
if disk_config.provider_config && disk_config.provider_config.key?(:hyperv)
|
||||
disk_provider_config = disk_config.provider_config[:hyperv]
|
||||
end
|
||||
|
||||
if !disk_provider_config.empty?
|
||||
disk_provider_config = convert_size_vars!(disk_provider_config)
|
||||
end
|
||||
|
||||
# Get the machines data dir, that will now be the path for the new disk
|
||||
guest_disk_folder = machine.data_dir.join("Virtual Hard Disks")
|
||||
|
||||
if disk_config.file
|
||||
disk_file = disk_config.file
|
||||
LOGGER.info("Disk already defined by user at '#{disk_file}'. Using this disk instead of creating a new one...")
|
||||
else
|
||||
# Set the extension
|
||||
disk_ext = disk_config.disk_ext
|
||||
disk_file = File.join(guest_disk_folder, disk_config.name) + ".#{disk_ext}"
|
||||
|
||||
LOGGER.info("Attempting to create a new disk file '#{disk_file}' of size '#{disk_config.size}' bytes")
|
||||
|
||||
machine.provider.driver.create_disk(disk_file, disk_config.size, disk_provider_config)
|
||||
end
|
||||
|
||||
disk_info = machine.provider.driver.get_disk(disk_file)
|
||||
disk_metadata = {UUID: disk_info["DiskIdentifier"], Name: disk_config.name, Path: disk_info["Path"]}
|
||||
|
||||
machine.provider.driver.attach_disk(disk_file, disk_provider_config)
|
||||
|
||||
disk_metadata
|
||||
end
|
||||
|
||||
# Converts any "shortcut" options such as "123MB" into its byte form. This
|
||||
# is due to what parameter type is expected when calling the `New-VHD`
|
||||
# powershell command
|
||||
#
|
||||
# @param [Hash] disk_provider_config
|
||||
# @return [Hash] disk_provider_config
|
||||
def self.convert_size_vars!(disk_provider_config)
|
||||
if disk_provider_config.key?(:BlockSizeBytes)
|
||||
bytes = Vagrant::Util::Numeric.string_to_bytes(disk_provider_config[:BlockSizeBytes])
|
||||
disk_provider_config[:BlockSizeBytes] = bytes
|
||||
end
|
||||
|
||||
disk_provider_config
|
||||
end
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [Config::Disk] disk_config - the current disk to configure
|
||||
# @param [Hash] defined_disk - current disk as represented by VirtualBox
|
||||
# @return [Hash] - disk_metadata
|
||||
def self.resize_disk(machine, disk_config, defined_disk)
|
||||
machine.ui.detail(I18n.t("vagrant.cap.configure_disks.resize_disk", name: disk_config.name), prefix: true)
|
||||
|
||||
machine.provider.driver.resize_disk(defined_disk["Path"], disk_config.size.to_i)
|
||||
|
||||
disk_info = machine.provider.driver.get_disk(defined_disk["Path"])
|
||||
|
||||
# Store updated metadata
|
||||
disk_metadata = {UUID: disk_info["DiskIdentifier"], Name: disk_config.name, Path: disk_info["Path"]}
|
||||
|
||||
disk_metadata
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
34
plugins/providers/hyperv/cap/validate_disk_ext.rb
Normal file
34
plugins/providers/hyperv/cap/validate_disk_ext.rb
Normal file
@ -0,0 +1,34 @@
|
||||
require "log4r"
|
||||
|
||||
module VagrantPlugins
|
||||
module HyperV
|
||||
module Cap
|
||||
module ValidateDiskExt
|
||||
LOGGER = Log4r::Logger.new("vagrant::plugins::hyperv::validate_disk_ext")
|
||||
|
||||
# The default set of disk formats that Hyper-V supports
|
||||
DEFAULT_DISK_EXT_LIST = ["vhd", "vhdx"].map(&:freeze).freeze
|
||||
DEFAULT_DISK_EXT = "vhdx".freeze
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [String] disk_ext
|
||||
# @return [Bool]
|
||||
def self.validate_disk_ext(machine, disk_ext)
|
||||
DEFAULT_DISK_EXT_LIST.include?(disk_ext)
|
||||
end
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @return [Array]
|
||||
def self.default_disk_exts(machine)
|
||||
DEFAULT_DISK_EXT_LIST
|
||||
end
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @return [String]
|
||||
def self.set_default_disk_ext(machine)
|
||||
DEFAULT_DISK_EXT
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -218,6 +218,77 @@ module VagrantPlugins
|
||||
execute(:set_name, VMID: vm_id, VMName: vmname)
|
||||
end
|
||||
|
||||
#
|
||||
# Disk Driver methods
|
||||
#
|
||||
|
||||
# @param [String] controller_type
|
||||
# @param [String] controller_number
|
||||
# @param [String] controller_location
|
||||
# @param [Hash] opts
|
||||
# @option opts [String] :ControllerType
|
||||
# @option opts [String] :ControllerNumber
|
||||
# @option opts [String] :ControllerLocation
|
||||
def attach_disk(disk_file_path, **opts)
|
||||
execute(:attach_disk_drive, VmId: @vm_id, Path: disk_file_path, ControllerType: opts[:ControllerType],
|
||||
ControllerNumber: opts[:ControllerNumber], ControllerLocation: opts[:ControllerLocation])
|
||||
end
|
||||
|
||||
# @param [String] path
|
||||
# @param [Int] size_bytes
|
||||
# @param [Hash] opts
|
||||
# @option opts [Bool] :Fixed
|
||||
# @option opts [String] :BlockSizeBytes
|
||||
# @option opts [String] :LogicalSectorSizeBytes
|
||||
# @option opts [String] :PhysicalSectorSizeBytes
|
||||
# @option opts [String] :SourceDisk
|
||||
# @option opts [Bool] :Differencing
|
||||
# @option opts [String] :ParentPath
|
||||
def create_disk(path, size_bytes, **opts)
|
||||
execute(:new_vhd, Path: path, SizeBytes: size_bytes, Fixed: opts[:Fixed],
|
||||
BlockSizeBytes: opts[:BlockSizeBytes], LogicalSectorSizeBytes: opts[:LogicalSectorSizeBytes],
|
||||
PhysicalSectorSizeBytes: opts[:PhysicalSectorSizeBytes],
|
||||
SourceDisk: opts[:SourceDisk], Differencing: opts[:Differencing],
|
||||
ParentPath: opts[:ParentPath])
|
||||
end
|
||||
|
||||
# @param [String] disk_file_path
|
||||
def dismount_disk(disk_file_path)
|
||||
execute(:dismount_vhd, DiskFilePath: disk_file_path)
|
||||
end
|
||||
|
||||
# @param [String] disk_file_path
|
||||
def get_disk(disk_file_path)
|
||||
execute(:get_vhd, DiskFilePath: disk_file_path)
|
||||
end
|
||||
|
||||
# @return [Array[Hash]]
|
||||
def list_hdds
|
||||
execute(:list_hdds, VmId: @vm_id)
|
||||
end
|
||||
|
||||
# @param [String] controller_type
|
||||
# @param [String] controller_number
|
||||
# @param [String] controller_location
|
||||
# @param [String] disk_file_path
|
||||
# @param [Hash] opts
|
||||
# @option opts [String] :ControllerType
|
||||
# @option opts [String] :ControllerNumber
|
||||
# @option opts [String] :ControllerLocation
|
||||
def remove_disk(controller_type, controller_number, controller_location, disk_file_path, **opts)
|
||||
execute(:remove_disk_drive, VmId: @vm_id, ControllerType: controller_type,
|
||||
ControllerNumber: controller_number, ControllerLocation: controller_location,
|
||||
DiskFilePath: disk_file_path)
|
||||
end
|
||||
|
||||
# @param [String] path
|
||||
# @param [Int] size_bytes
|
||||
# @param [Hash] opts
|
||||
def resize_disk(disk_file_path, size_bytes, **opts)
|
||||
execute(:resize_disk_drive, VmId: @vm_id, DiskFilePath: disk_file_path,
|
||||
DiskSize: size_bytes)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def execute_powershell(path, options, &block)
|
||||
@ -227,6 +298,7 @@ module VagrantPlugins
|
||||
options = options || {}
|
||||
ps_options = []
|
||||
options.each do |key, value|
|
||||
next if !value || value.to_s.empty?
|
||||
next if value == false
|
||||
ps_options << "-#{key}"
|
||||
# If the value is a TrueClass assume switch
|
||||
|
||||
@ -32,6 +32,31 @@ module VagrantPlugins
|
||||
Cap::SnapshotList
|
||||
end
|
||||
|
||||
provider_capability(:hyperv, :configure_disks) do
|
||||
require_relative "cap/configure_disks"
|
||||
Cap::ConfigureDisks
|
||||
end
|
||||
|
||||
provider_capability(:hyperv, :cleanup_disks) do
|
||||
require_relative "cap/cleanup_disks"
|
||||
Cap::CleanupDisks
|
||||
end
|
||||
|
||||
provider_capability(:hyperv, :validate_disk_ext) do
|
||||
require_relative "cap/validate_disk_ext"
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
provider_capability(:hyperv, :default_disk_exts) do
|
||||
require_relative "cap/validate_disk_ext"
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
provider_capability(:hyperv, :set_default_disk_ext) do
|
||||
require_relative "cap/validate_disk_ext"
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.init!
|
||||
|
||||
28
plugins/providers/hyperv/scripts/attach_disk_drive.ps1
Normal file
28
plugins/providers/hyperv/scripts/attach_disk_drive.ps1
Normal file
@ -0,0 +1,28 @@
|
||||
#Requires -Modules VagrantMessages
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$VmId,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$Path,
|
||||
[string]$ControllerType,
|
||||
[string]$ControllerNumber,
|
||||
[string]$ControllerLocation
|
||||
)
|
||||
|
||||
$Params = @{}
|
||||
|
||||
foreach ($key in $MyInvocation.BoundParameters.keys) {
|
||||
$value = (Get-Variable -Exclude "ErrorAction" $key).Value
|
||||
if (($key -ne "VmId") -and ($key -ne "ErrorAction")) {
|
||||
$Params.Add($key, $value)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$VM = Hyper-V\Get-VM -Id $VmId
|
||||
Hyper-V\Add-VMHardDiskDrive -VMName $VM.Name @Params
|
||||
} catch {
|
||||
Write-ErrorMessage "Failed to attach disk ${DiskFilePath} to VM ${VM}: ${PSItem}"
|
||||
exit 1
|
||||
}
|
||||
13
plugins/providers/hyperv/scripts/dismount_vhd.ps1
Normal file
13
plugins/providers/hyperv/scripts/dismount_vhd.ps1
Normal file
@ -0,0 +1,13 @@
|
||||
#Requires -Modules VagrantMessages
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$DiskFilePath
|
||||
)
|
||||
|
||||
try {
|
||||
Hyper-V\Dismount-VHD -path $DiskFilePath
|
||||
} catch {
|
||||
Write-ErrorMessage "Failed to dismount disk info from disk file path ${DiskFilePath}: ${PSItem}"
|
||||
exit 1
|
||||
}
|
||||
16
plugins/providers/hyperv/scripts/get_vhd.ps1
Normal file
16
plugins/providers/hyperv/scripts/get_vhd.ps1
Normal file
@ -0,0 +1,16 @@
|
||||
#Requires -Modules VagrantMessages
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$DiskFilePath
|
||||
)
|
||||
|
||||
try {
|
||||
$Disk = Hyper-V\Get-VHD -path $DiskFilePath
|
||||
} catch {
|
||||
Write-ErrorMessage "Failed to retrieve disk info from disk file path ${DiskFilePath}: ${PSItem}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$result = ConvertTo-json $Disk
|
||||
Write-OutputMessage $result
|
||||
@ -14,7 +14,7 @@ try
|
||||
# type was loaded in Microsoft.HyperV.PowerShell
|
||||
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.HyperV.PowerShell.Objects, Culture=neutral, PublicKeyToken=31bf3856ad364e35')
|
||||
} catch {
|
||||
# Empty catch ok, since if we didn't load the types, we will fail in the next block
|
||||
# Empty catch ok, since if we didn't load the types, we will fail in the next block
|
||||
}
|
||||
|
||||
$VmmsPath = if ([environment]::Is64BitProcess) { "$($env:SystemRoot)\System32\vmms.exe" } else { "$($env:SystemRoot)\Sysnative\vmms.exe" }
|
||||
|
||||
17
plugins/providers/hyperv/scripts/list_hdds.ps1
Normal file
17
plugins/providers/hyperv/scripts/list_hdds.ps1
Normal file
@ -0,0 +1,17 @@
|
||||
#Requires -Modules VagrantMessages
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$VmId
|
||||
)
|
||||
|
||||
try {
|
||||
$VM = Hyper-V\Get-VM -Id $VmId
|
||||
$Disks = @(Hyper-V\Get-VMHardDiskDrive -VMName $VM.Name)
|
||||
} catch {
|
||||
Write-ErrorMessage "Failed to retrieve all disk info from ${VM}: ${PSItem}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$result = ConvertTo-json $Disks
|
||||
Write-OutputMessage $result
|
||||
31
plugins/providers/hyperv/scripts/new_vhd.ps1
Normal file
31
plugins/providers/hyperv/scripts/new_vhd.ps1
Normal file
@ -0,0 +1,31 @@
|
||||
#Requires -Modules VagrantMessages
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$Path,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[UInt64]$SizeBytes,
|
||||
[switch]$Fixed,
|
||||
[switch]$Differencing,
|
||||
[string]$ParentPath,
|
||||
[Uint32]$BlockSizeBytes,
|
||||
[UInt32]$LogicalSectorSizeBytes,
|
||||
[UInt32]$PhysicalSectorSizeBytes,
|
||||
[UInt32]$SourceDisk
|
||||
)
|
||||
|
||||
$Params = @{}
|
||||
|
||||
foreach ($key in $MyInvocation.BoundParameters.keys) {
|
||||
$value = (Get-Variable -Exclude "ErrorAction" $key).Value
|
||||
if ($key -ne "ErrorAction") {
|
||||
$Params.Add($key, $value)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Hyper-V\New-VHD @Params
|
||||
} catch {
|
||||
Write-ErrorMessage "Failed to create disk ${DiskFilePath}: ${PSItem}"
|
||||
exit 1
|
||||
}
|
||||
25
plugins/providers/hyperv/scripts/remove_disk_drive.ps1
Normal file
25
plugins/providers/hyperv/scripts/remove_disk_drive.ps1
Normal file
@ -0,0 +1,25 @@
|
||||
#Requires -Modules VagrantMessages
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$VmId,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$ControllerType,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$ControllerNumber,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$ControllerLocation,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$DiskFilePath
|
||||
)
|
||||
|
||||
try {
|
||||
$VM = Hyper-V\Get-VM -Id $VmId
|
||||
|
||||
Hyper-v\Remove-VMHardDiskDrive -VMName $VM.Name -ControllerType $ControllerType -ControllerNumber $ControllerNumber -ControllerLocation $ControllerLocation
|
||||
|
||||
Remove-Item -Path $DiskFilePath
|
||||
} catch {
|
||||
Write-ErrorMessage "Failed to remove disk ${DiskFilePath} to VM ${VM}: ${PSItem}"
|
||||
exit 1
|
||||
}
|
||||
18
plugins/providers/hyperv/scripts/resize_disk_drive.ps1
Normal file
18
plugins/providers/hyperv/scripts/resize_disk_drive.ps1
Normal file
@ -0,0 +1,18 @@
|
||||
#Requires -Modules VagrantMessages
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$VmId,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$DiskFilePath,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[UInt64]$DiskSize
|
||||
)
|
||||
|
||||
try {
|
||||
$VM = Hyper-V\Get-VM -Id $VmId
|
||||
Hyper-V\Resize-VHD -Path $DiskFilePath -SizeBytes $DiskSize
|
||||
} catch {
|
||||
Write-ErrorMessage "Failed to resize disk ${DiskFilePath} for VM ${VM}: ${PSItem}"
|
||||
exit 1
|
||||
}
|
||||
@ -36,7 +36,7 @@ module VagrantPlugins
|
||||
LOGGER.warn("Found disk not in Vagrantfile config: '#{d["name"]}'. Removing disk from guest #{machine.name}")
|
||||
disk_info = machine.provider.driver.get_port_and_device(d["uuid"])
|
||||
|
||||
machine.ui.warn("Disk '#{d["name"]}' no longer exists in Vagrant config. Removing and closing medium from guest...", prefix: true)
|
||||
machine.ui.warn(I18n.t("vagrant.cap.cleanup_disks.disk_cleanup", name: d["name"]), prefix: true)
|
||||
|
||||
if disk_info.empty?
|
||||
LOGGER.warn("Disk '#{d["name"]}' not attached to guest, but still exists.")
|
||||
|
||||
@ -7,18 +7,25 @@ module VagrantPlugins
|
||||
LOGGER = Log4r::Logger.new("vagrant::plugins::virtualbox::validate_disk_ext")
|
||||
|
||||
# The default set of disk formats that VirtualBox supports
|
||||
DEFAULT_DISK_EXT = ["vdi", "vmdk", "vhd"].map(&:freeze).freeze
|
||||
DEFAULT_DISK_EXT_LIST = ["vdi", "vmdk", "vhd"].map(&:freeze).freeze
|
||||
DEFAULT_DISK_EXT = "vdi".freeze
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [String] disk_ext
|
||||
# @return [Bool]
|
||||
def self.validate_disk_ext(machine, disk_ext)
|
||||
DEFAULT_DISK_EXT.include?(disk_ext)
|
||||
DEFAULT_DISK_EXT_LIST.include?(disk_ext)
|
||||
end
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @return [Array]
|
||||
def self.get_default_disk_ext(machine)
|
||||
def self.default_disk_exts(machine)
|
||||
DEFAULT_DISK_EXT_LIST
|
||||
end
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @return [String]
|
||||
def self.set_default_disk_ext(machine)
|
||||
DEFAULT_DISK_EXT
|
||||
end
|
||||
end
|
||||
|
||||
@ -54,7 +54,12 @@ module VagrantPlugins
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
provider_capability(:virtualbox, :get_default_disk_ext) do
|
||||
provider_capability(:virtualbox, :default_disk_exts) do
|
||||
require_relative "cap/validate_disk_ext"
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
provider_capability(:virtualbox, :set_default_disk_ext) do
|
||||
require_relative "cap/validate_disk_ext"
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
@ -2211,6 +2211,11 @@ en:
|
||||
# Translations for Vagrant middleware actions
|
||||
#-------------------------------------------------------------------------------
|
||||
cap:
|
||||
cleanup_disks:
|
||||
disk_cleanup: |-
|
||||
Disk '%{name}' no longer exists in Vagrant config. Removing and closing medium from guest...
|
||||
disk_not_found: |-
|
||||
Disk '%{name}' could not be found, and could not be properly removed. Please remove this disk manually if it still exists
|
||||
configure_disks:
|
||||
start: "Configuring storage mediums..."
|
||||
floppy_not_supported: "Floppy disk configuration not yet supported. Skipping disk '%{name}'..."
|
||||
|
||||
@ -9,8 +9,11 @@ describe VagrantPlugins::Kernel_V2::VagrantConfigDisk do
|
||||
|
||||
subject { described_class.new(type) }
|
||||
|
||||
let(:ui) { double("ui") }
|
||||
let(:env) { double("env", ui: ui) }
|
||||
let(:provider) { double("provider") }
|
||||
let(:machine) { double("machine", provider: provider) }
|
||||
let(:machine) { double("machine", name: "name", provider: provider, env: env,
|
||||
provider_name: :virtualbox) }
|
||||
|
||||
|
||||
def assert_invalid
|
||||
@ -34,6 +37,8 @@ describe VagrantPlugins::Kernel_V2::VagrantConfigDisk do
|
||||
subject.size = 100
|
||||
allow(provider).to receive(:capability?).with(:validate_disk_ext).and_return(true)
|
||||
allow(provider).to receive(:capability).with(:validate_disk_ext, "vdi").and_return(true)
|
||||
allow(provider).to receive(:capability?).with(:set_default_disk_ext).and_return(true)
|
||||
allow(provider).to receive(:capability).with(:set_default_disk_ext).and_return("vdi")
|
||||
end
|
||||
|
||||
describe "with defaults" do
|
||||
@ -53,8 +58,25 @@ describe VagrantPlugins::Kernel_V2::VagrantConfigDisk do
|
||||
end
|
||||
end
|
||||
|
||||
describe "defining a new config that needs to match internal restraints" do
|
||||
before do
|
||||
describe "with an invalid config" do
|
||||
let(:invalid_subject) { described_class.new(type) }
|
||||
|
||||
it "raises an error if size not set" do
|
||||
invalid_subject.name = "bar"
|
||||
subject.finalize!
|
||||
assert_invalid
|
||||
end
|
||||
|
||||
context "with an invalid disk extension" do
|
||||
before do
|
||||
allow(provider).to receive(:capability?).with(:validate_disk_ext).and_return(true)
|
||||
allow(provider).to receive(:capability).with(:validate_disk_ext, "fake").and_return(false)
|
||||
end
|
||||
|
||||
it "raises an error" do
|
||||
subject.finalize!
|
||||
assert_invalid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -8,7 +8,7 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:provider) { double("provider") }
|
||||
let(:machine) { double("machine", provider: provider) }
|
||||
let(:machine) { double("machine", provider: provider, provider_name: "provider") }
|
||||
|
||||
def assert_invalid
|
||||
errors = subject.validate(machine)
|
||||
@ -40,6 +40,8 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
|
||||
|
||||
allow(provider).to receive(:capability?).with(:validate_disk_ext).and_return(true)
|
||||
allow(provider).to receive(:capability).with(:validate_disk_ext, "vdi").and_return(true)
|
||||
allow(provider).to receive(:capability?).with(:set_default_disk_ext).and_return(true)
|
||||
allow(provider).to receive(:capability).with(:set_default_disk_ext).and_return("vdi")
|
||||
|
||||
subject.box = "foo"
|
||||
end
|
||||
|
||||
96
test/unit/plugins/providers/hyperv/cap/cleanup_disks_test.rb
Normal file
96
test/unit/plugins/providers/hyperv/cap/cleanup_disks_test.rb
Normal file
@ -0,0 +1,96 @@
|
||||
require_relative "../../../../base"
|
||||
require Vagrant.source_root.join("plugins/providers/hyperv/cap/cleanup_disks")
|
||||
|
||||
describe VagrantPlugins::HyperV::Cap::CleanupDisks do
|
||||
include_context "unit"
|
||||
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
env = isolated_environment
|
||||
env.vagrantfile("")
|
||||
env.create_vagrant_env
|
||||
end
|
||||
|
||||
let(:driver) { double("driver") }
|
||||
|
||||
let(:machine) do
|
||||
iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m|
|
||||
allow(m.provider).to receive(:driver).and_return(driver)
|
||||
allow(m).to receive(:state).and_return(state)
|
||||
end
|
||||
end
|
||||
|
||||
let(:state) do
|
||||
double(:state)
|
||||
end
|
||||
|
||||
let(:subject) { described_class }
|
||||
|
||||
let(:disk_meta_file) { {disk: [], floppy: [], dvd: []} }
|
||||
let(:defined_disks) { {} }
|
||||
|
||||
before do
|
||||
allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
context "#cleanup_disks" do
|
||||
it "returns if there's no data in meta file" do
|
||||
subject.cleanup_disks(machine, defined_disks, disk_meta_file)
|
||||
expect(subject).not_to receive(:handle_cleanup_disk)
|
||||
end
|
||||
|
||||
describe "with disks to clean up" do
|
||||
let(:disk_meta_file) { {disk: [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage"}], floppy: [], dvd: []} }
|
||||
|
||||
it "calls the cleanup method if a disk_meta file is defined" do
|
||||
expect(subject).to receive(:handle_cleanup_disk).
|
||||
with(machine, defined_disks, disk_meta_file["disk"]).
|
||||
and_return(true)
|
||||
|
||||
subject.cleanup_disks(machine, defined_disks, disk_meta_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#handle_cleanup_disk" do
|
||||
let(:disk_meta_file) { {disk: [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage"}], floppy: [], dvd: []} }
|
||||
let(:defined_disks) { [] }
|
||||
let(:all_disks) { [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage",
|
||||
"ControllerType"=>"IDE", "ControllerNumber"=>1, "ControllerLocation"=>0}] }
|
||||
let(:path) { "C:\\Users\\vagrant\\storage.vhdx" }
|
||||
|
||||
it "removes and closes medium from guest" do
|
||||
expect(driver).to receive(:list_hdds).and_return(all_disks)
|
||||
expect(driver).to receive(:remove_disk).with("IDE", 1, 0, "c:\\users\\vagrant\\storage.vhdx").and_return(true)
|
||||
|
||||
subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk])
|
||||
end
|
||||
|
||||
it "displays a warning if the disk could not be determined" do
|
||||
expect(driver).to receive(:list_hdds).and_return(all_disks)
|
||||
expect(File).to receive(:realdirpath).and_return(path)
|
||||
expect(File).to receive(:realdirpath).and_return("")
|
||||
expect(driver).not_to receive(:remove_disk)
|
||||
expect(machine.ui).to receive(:warn).twice
|
||||
|
||||
subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk])
|
||||
end
|
||||
|
||||
describe "when windows paths mix cases" do
|
||||
let(:disk_meta_file) { {disk: [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage"}], floppy: [], dvd: []} }
|
||||
let(:defined_disks) { [] }
|
||||
let(:all_disks) { [{"UUID"=>"1234", "Path"=> "C:\\Users\\vagrant\\storage.vhdx", "Name"=>"storage",
|
||||
"ControllerType"=>"IDE", "ControllerNumber"=>1, "ControllerLocation"=>0}] }
|
||||
|
||||
let(:path) { "C:\\Users\\vagrant\\storage.vhdx" }
|
||||
|
||||
it "still removes and closes the medium from the guest" do
|
||||
expect(driver).to receive(:list_hdds).and_return(all_disks)
|
||||
expect(File).to receive(:realdirpath).twice.and_return(path)
|
||||
expect(driver).to receive(:remove_disk).with("IDE", 1, 0, path).and_return(true)
|
||||
|
||||
subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
250
test/unit/plugins/providers/hyperv/cap/configure_disks_test.rb
Normal file
250
test/unit/plugins/providers/hyperv/cap/configure_disks_test.rb
Normal file
@ -0,0 +1,250 @@
|
||||
require_relative "../../../../base"
|
||||
require Vagrant.source_root.join("plugins/providers/hyperv/cap/configure_disks")
|
||||
|
||||
describe VagrantPlugins::HyperV::Cap::ConfigureDisks do
|
||||
include_context "unit"
|
||||
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
env = isolated_environment
|
||||
env.vagrantfile("")
|
||||
env.create_vagrant_env
|
||||
end
|
||||
|
||||
let(:driver) { double("driver") }
|
||||
|
||||
let(:machine) do
|
||||
iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m|
|
||||
allow(m.provider).to receive(:driver).and_return(driver)
|
||||
allow(m).to receive(:state).and_return(state)
|
||||
end
|
||||
end
|
||||
|
||||
let(:state) do
|
||||
double(:state)
|
||||
end
|
||||
|
||||
let(:defined_disks) { [double("disk", name: "vagrant_primary", size: "5GB", primary: true, type: :disk),
|
||||
double("disk", name: "disk-0", size: "5GB", primary: false, type: :disk),
|
||||
double("disk", name: "disk-1", size: "5GB", primary: false, type: :disk),
|
||||
double("disk", name: "disk-2", size: "5GB", primary: false, type: :disk)] }
|
||||
|
||||
let(:subject) { described_class }
|
||||
|
||||
let(:all_disks) { [{"UUID"=>"12345",
|
||||
"Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx",
|
||||
"ControllerLocation"=>0,
|
||||
"ControllerNumber"=>0},
|
||||
{"UUID"=>"67890",
|
||||
"Name"=>"disk-0",
|
||||
"Path"=>"C:/Users/vagrant/disks/disk-0.vhdx",
|
||||
"ControllerLocation"=>1,
|
||||
"ControllerNumber"=>0},
|
||||
{"UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8",
|
||||
"Path"=>"C:/Users/vagrant/disks/disk-1.vhdx",
|
||||
"Name"=>"disk-1",
|
||||
"ControllerLocation"=>2,
|
||||
"ControllerNumber"=>0}] }
|
||||
|
||||
before do
|
||||
allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
context "#configure_disks" do
|
||||
let(:dsk_data) { {"UUID"=>"1234", "Name"=>"disk", "Path"=> "C:/Users/vagrant/storage.vhdx"} }
|
||||
|
||||
it "configures disks and returns the disks defined" do
|
||||
allow(driver).to receive(:list_hdds).and_return([])
|
||||
expect(subject).to receive(:handle_configure_disk).exactly(4).and_return(dsk_data)
|
||||
|
||||
subject.configure_disks(machine, defined_disks)
|
||||
end
|
||||
|
||||
describe "with no disks to configure" do
|
||||
let(:defined_disks) { {} }
|
||||
it "returns empty hash if no disks to configure" do
|
||||
expect(subject.configure_disks(machine, defined_disks)).to eq({})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#get_current_disk" do
|
||||
it "gets primary disk uuid if disk to configure is primary" do
|
||||
expect(driver).to receive(:get_disk).with(all_disks.first["Path"]).and_return(all_disks.first)
|
||||
primary_disk = subject.get_current_disk(machine, defined_disks.first, all_disks)
|
||||
expect(primary_disk).to eq(all_disks.first)
|
||||
end
|
||||
|
||||
it "finds the disk to configure" do
|
||||
disk = subject.get_current_disk(machine, defined_disks[1], all_disks)
|
||||
expect(disk).to eq(all_disks[1])
|
||||
end
|
||||
|
||||
it "returns nil if disk is not found" do
|
||||
disk = subject.get_current_disk(machine, defined_disks[3], all_disks)
|
||||
expect(disk).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "#handle_configure_disk" do
|
||||
describe "when creating a new disk" do
|
||||
let(:all_disks) { [{"UUID"=>"12345",
|
||||
"Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx",
|
||||
"ControllerLocation"=>0,
|
||||
"ControllerNumber"=>0}] }
|
||||
|
||||
let(:disk_meta) { {"UUID"=>"12345", "Name"=>"vagrant_primary", "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx" } }
|
||||
|
||||
it "creates a new disk if it doesn't yet exist" do
|
||||
expect(subject).to receive(:create_disk).with(machine, defined_disks[1])
|
||||
.and_return(disk_meta)
|
||||
|
||||
subject.handle_configure_disk(machine, defined_disks[1], all_disks)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when a disk needs to be resized" do
|
||||
let(:all_disks) { [{"UUID"=>"12345",
|
||||
"Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx",
|
||||
"ControllerLocation"=>0,
|
||||
"ControllerNumber"=>0},
|
||||
{"UUID"=>"67890",
|
||||
"Name"=>"disk-0",
|
||||
"Path"=>"C:/Users/vagrant/disks/disk-0.vhdx",
|
||||
"ControllerLocation"=>1,
|
||||
"ControllerNumber"=>0},
|
||||
{"UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8",
|
||||
"Path"=>"C:/Users/vagrant/disks/disk-1.vhdx",
|
||||
"Name"=>"disk-1",
|
||||
"ControllerLocation"=>2,
|
||||
"ControllerNumber"=>0}] }
|
||||
|
||||
it "resizes a disk" do
|
||||
expect(subject).to receive(:get_current_disk).
|
||||
with(machine, defined_disks[1], all_disks).and_return(all_disks[1])
|
||||
|
||||
expect(subject).to receive(:compare_disk_size).
|
||||
with(machine, defined_disks[1], all_disks[1]).and_return(true)
|
||||
|
||||
expect(subject).to receive(:resize_disk).
|
||||
with(machine, defined_disks[1], all_disks[1]).and_return(true)
|
||||
|
||||
subject.handle_configure_disk(machine, defined_disks[1], all_disks)
|
||||
end
|
||||
end
|
||||
|
||||
describe "if no additional disk configuration is required" do
|
||||
let(:all_disks) { [{"UUID"=>"12345",
|
||||
"Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx",
|
||||
"ControllerLocation"=>0,
|
||||
"ControllerNumber"=>0},
|
||||
{"UUID"=>"67890",
|
||||
"Name"=>"disk-0",
|
||||
"Path"=>"C:/Users/vagrant/disks/disk-0.vhdx",
|
||||
"ControllerLocation"=>1,
|
||||
"ControllerNumber"=>0},
|
||||
{"UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8",
|
||||
"Path"=>"C:/Users/vagrant/disks/disk-1.vhdx",
|
||||
"Name"=>"disk-1",
|
||||
"ControllerLocation"=>2,
|
||||
"ControllerNumber"=>0}] }
|
||||
|
||||
it "does nothing if all disks are properly configured" do
|
||||
expect(subject).to receive(:get_current_disk).
|
||||
with(machine, defined_disks[1], all_disks).and_return(all_disks[1])
|
||||
|
||||
expect(subject).to receive(:compare_disk_size).
|
||||
with(machine, defined_disks[1], all_disks[1]).and_return(false)
|
||||
|
||||
subject.handle_configure_disk(machine, defined_disks[1], all_disks)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#compare_disk_size" do
|
||||
let(:disk_config_small) { double("disk", name: "disk-0", size: 41824.0, primary: false, type: :disk) }
|
||||
let(:disk_config_large) { double("disk", name: "disk-0", size: 123568719476736.0, primary: false, type: :disk) }
|
||||
|
||||
let(:disk_large) { [{"UUID"=>"12345",
|
||||
"Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx",
|
||||
"ControllerLocation"=>0,
|
||||
"ControllerNumber"=>0}] }
|
||||
|
||||
let(:disk_small) { {"UUID"=>"67890",
|
||||
"Path"=>"C:/Users/vagrant/disks/small_disk.vhd",
|
||||
"Size"=>1073741824.0,
|
||||
"ControllerLocation"=>1,
|
||||
"ControllerNumber"=>0} }
|
||||
|
||||
it "shows a warning if user attempts to shrink size of a vhd disk" do
|
||||
expect(machine.ui).to receive(:warn)
|
||||
expect(driver).to receive(:get_disk).with(all_disks[1]["Path"]).and_return(disk_small)
|
||||
|
||||
expect(subject.compare_disk_size(machine, disk_config_small, all_disks[1])).to be_falsey
|
||||
end
|
||||
|
||||
it "returns true if requested size is bigger than current size" do
|
||||
expect(driver).to receive(:get_disk).with(all_disks[2]["Path"]).and_return(disk_small)
|
||||
expect(subject.compare_disk_size(machine, disk_config_large, all_disks[2])).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "#create_disk" do
|
||||
let(:disk_provider_config) { {} }
|
||||
let(:disk_config) { double("disk", name: "disk-0", size: 1073741824.0,
|
||||
primary: false, type: :disk, disk_ext: "vhdx",
|
||||
provider_config: disk_provider_config,
|
||||
file: nil) }
|
||||
|
||||
let(:disk_file) { "C:/Users/vagrant/disks/Virtual Hard Disks/disk-0.vhdx" }
|
||||
|
||||
let(:data_dir) { Pathname.new("C:/Users/vagrant/disks") }
|
||||
|
||||
let(:disk) { {"DiskIdentifier"=>"12345",
|
||||
"Path"=>"C:/Users/vagrant/disks/Virtual Hard Disks/disk-0.vhdx",
|
||||
"ControllerLocation"=>1,
|
||||
"ControllerNumber"=>0} }
|
||||
|
||||
it "creates a disk and attaches it to a guest" do
|
||||
expect(machine).to receive(:data_dir).and_return(data_dir)
|
||||
expect(driver).to receive(:create_disk).with(disk_file, disk_config.size, {})
|
||||
expect(driver).to receive(:get_disk).with(disk_file).and_return(disk)
|
||||
|
||||
expect(driver).to receive(:attach_disk).with(disk_file, {})
|
||||
|
||||
subject.create_disk(machine, disk_config)
|
||||
end
|
||||
end
|
||||
|
||||
context "#convert_size_vars!" do
|
||||
let(:disk_provider_config) { {BlockSizeBytes: "128MB", LogicalSectorSizeBytes: 512, PhysicalSectorSizeBytes: 4096 } }
|
||||
it "converts certain powershell arguments into something usable" do
|
||||
updated_config = subject.convert_size_vars!(disk_provider_config)
|
||||
|
||||
expect(updated_config[:BlockSizeBytes]).to eq(134217728)
|
||||
expect(updated_config[:LogicalSectorSizeBytes]).to eq(512)
|
||||
expect(updated_config[:PhysicalSectorSizeBytes]).to eq(4096)
|
||||
end
|
||||
end
|
||||
|
||||
context "#resize_disk" do
|
||||
let(:disk_config) { double("disk", name: "disk-0", size: 1073741824.0,
|
||||
primary: false, type: :disk, disk_ext: "vhdx",
|
||||
provider_config: nil,
|
||||
file: nil) }
|
||||
|
||||
let(:disk) { {"DiskIdentifier"=>"12345",
|
||||
"Path"=>"C:/Users/vagrant/disks/disk-0.vhdx",
|
||||
"ControllerLocation"=>1,
|
||||
"ControllerNumber"=>0} }
|
||||
|
||||
let(:disk_file) { "C:/Users/vagrant/disks/disk-0.vhdx" }
|
||||
|
||||
it "resizes the disk" do
|
||||
expect(driver).to receive(:get_disk).with(disk_file).and_return(disk)
|
||||
expect(driver).to receive(:resize_disk).with(disk_file, disk_config.size.to_i).and_return(true)
|
||||
|
||||
subject.resize_disk(machine, disk_config, all_disks[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -119,6 +119,7 @@ export default [
|
||||
'configuration',
|
||||
'usage',
|
||||
{ category: 'virtualbox', content: ['usage', 'common-issues'] },
|
||||
{ category: 'hyperv', content: ['usage', 'common-issues'] },
|
||||
],
|
||||
},
|
||||
'multi-machine',
|
||||
|
||||
26
website/pages/docs/disks/hyperv/common-issues.mdx
Normal file
26
website/pages/docs/disks/hyperv/common-issues.mdx
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
layout: docs
|
||||
page_title: Common Issues - Disks Hyper-V Provider
|
||||
sidebar_title: Common Issues
|
||||
description: |-
|
||||
This page lists some common issues people run into with Vagrant and Hyper-V
|
||||
as well as solutions for those issues.
|
||||
---
|
||||
|
||||
# Common Issues and Troubleshooting
|
||||
|
||||
This page lists some common issues people run into with Vagrant and Hyper-V
|
||||
as well as solutions for those issues.
|
||||
|
||||
## Are my disks attached?
|
||||
|
||||
A handy way to figure out what disks are attached (or not attached) to your guest
|
||||
is to open up the Hyper-V GUI and select the guest. When selecting a guest on the GUI,
|
||||
it should open more information about the guest, including storage information. Here
|
||||
you should see a list of disks attached to your guest.
|
||||
|
||||
## Applying Vagrant disk configuration changes to guests
|
||||
|
||||
Due to how Hyper-V works, you must reload your guest for any disk config changes
|
||||
to be applied. So if you update your Vagrantfile to update or even remove disks, make
|
||||
sure to `vagrant reload` your guests for these changes to be applied.
|
||||
33
website/pages/docs/disks/hyperv/index.mdx
Normal file
33
website/pages/docs/disks/hyperv/index.mdx
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
layout: docs
|
||||
page_title: Disks for Hyper-V Provider
|
||||
sidebar_title: Hyper-V
|
||||
description: |-
|
||||
Vagrant comes with support out of the box for Hyper-V, a free,
|
||||
cross-platform consumer virtualization product.
|
||||
---
|
||||
|
||||
# Hyper-V
|
||||
|
||||
~> **Warning!** This feature is experimental and may break or
|
||||
change in between releases. Use at your own risk. It currently is not officially
|
||||
supported or functional.
|
||||
|
||||
This feature currently reqiures the experimental flag to be used. To explicitly enable this feature, you can set the experimental flag to:
|
||||
|
||||
```
|
||||
VAGRANT_EXPERIMENTAL="disks"
|
||||
```
|
||||
|
||||
Please note that `VAGRANT_EXPERIMENTAL` is an environment variable. For more
|
||||
information about this flag visit the [Experimental docs page](/docs/experimental/)
|
||||
for more info. Without this flag enabled, any disks defined will not be configured.
|
||||
|
||||
Because of how Hyper-V handles disk management, a Vagrant guest _must_ be powered
|
||||
off for any changes to be applied to a guest. If you make a configuration change
|
||||
with a guests disk, you will need to `vagrant reload` the guest for any changes
|
||||
to be applied.
|
||||
|
||||
For more information on how to use VirtualBox to configure disks for a guest, refer
|
||||
to the [general usage](/docs/disks/usage) and [configuration](/docs/disks/configuration)
|
||||
guide for more information.
|
||||
71
website/pages/docs/disks/hyperv/usage.mdx
Normal file
71
website/pages/docs/disks/hyperv/usage.mdx
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
layout: docs
|
||||
page_title: Usage - Disks Hyper-V Provider
|
||||
sidebar_title: Usage
|
||||
description: |-
|
||||
The Vagrant Hyper-V provider is used just like any other provider. Please
|
||||
read the general basic usage page for providers.
|
||||
---
|
||||
|
||||
# Usage
|
||||
|
||||
~> **Warning!** This feature is experimental and may break or
|
||||
change in between releases. Use at your own risk. It currently is not officially
|
||||
supported or functional.
|
||||
|
||||
This feature currently reqiures the experimental flag to be used. To explicitly enable this feature, you can set the experimental flag to:
|
||||
|
||||
```
|
||||
VAGRANT_EXPERIMENTAL="disks"
|
||||
```
|
||||
|
||||
Please note that `VAGRANT_EXPERIMENTAL` is an environment variable. For more
|
||||
information about this flag visit the [Experimental docs page](/docs/experimental/)
|
||||
for more info. Without this flag enabled, any disks defined will not be configured.
|
||||
|
||||
For examples of how to use the disk feature with Hyper-V, please refer to the
|
||||
[general disk usage guide](/docs/disks/usage) for more examples.
|
||||
|
||||
## provider_config options
|
||||
|
||||
Most options are used for either creating or attaching a hard disk to your guest.
|
||||
Vagrant supports most options for these operations. You should be able to define
|
||||
the powershell specific argument to a given Hyper-V command in the provider_config
|
||||
hash, and Vagrant should properly pass it along to the command.
|
||||
|
||||
To define a provider specific option, please refer to the [Disk Options documentation page](/docs/disks/configuration) for more info.
|
||||
|
||||
### Note about options defined below
|
||||
|
||||
It is possible these options could be out of date or stale. If you happen to see
|
||||
an option that has changed or is missing from this page, please open an issue
|
||||
or pull request on Vagrants GitHub page to correct this.
|
||||
|
||||
### New-VHD Supported Options
|
||||
|
||||
For more information about each option, please visit the [New-VHD Hyper-V documentation](https://docs.microsoft.com/en-us/powershell/module/hyper-v/new-vhd?view=win10-ps).
|
||||
|
||||
__Note:__ By default, all Hyper-V disks are defined as a Dynamic virtual hard disk. If you
|
||||
wish to make the disk a fixed size, you can set the `Fixed` option below when creating
|
||||
a new disk.
|
||||
|
||||
* `BlockSizeBytes` (string) - Optional argument, i.e. `"128MB"`
|
||||
* `Differencing` (bool) - If set, the disk will be used to store differencing changes from parent disk (must set `ParentPath`)
|
||||
* `Fixed` (bool) - If set, the disk will be a fixed size, not dynamically allocated.
|
||||
* `LogicalSectorSizeBytes` (int) - Optional argument, must be either `512` or `4096`
|
||||
* `ParentPath` (string) - The parent disk path used if a `Differencing` disk is defined
|
||||
* `PhysicalSectorSizeBytes` (string) - Optional argument, must be either `512` or `4096`
|
||||
* `SourceDisk` (int) - Existing disk to use as a source for the new disk
|
||||
|
||||
### Add-VMHardDiskDrive Supported Options
|
||||
|
||||
For more information about each option, please visit the [Add-VMHardDiskDrive Hyper-V documentation](https://docs.microsoft.com/en-us/powershell/module/hyper-v/add-vmharddiskdrive?view=win10-ps)
|
||||
|
||||
Generally, these options do not need to be set or handled by most users. Only
|
||||
use these options if you are sure you know what you are doing. Vagrant will
|
||||
be able to attach disks for you without these options, but they are available
|
||||
if it is required that you specificy a specific location for a disk.
|
||||
|
||||
* `ControllerLocation` (int) - The location that the disk should be attached to on the controller
|
||||
* `ControllerNumber` (int) - The controller to use for attaching the disk
|
||||
* `ControllerType` (string) - The kind of controller to use when attaching the a disk. Only `"IDE"` and `"SCSI"` are valid.
|
||||
@ -21,9 +21,6 @@ Please note that `VAGRANT_EXPERIMENTAL` is an environment variable. For more
|
||||
information about this flag visit the [Experimental docs page](/docs/experimental/)
|
||||
for more info. Without this flag enabled, any disks defined will not be configured.
|
||||
|
||||
Also note that the examples below use the VirtualBox provider, which is the current
|
||||
supported provider for this feature.
|
||||
|
||||
Below are some very simple examples of how to use Vagrant Disks with the VirtualBox provider.
|
||||
|
||||
## Basic Examples
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user