201 lines
8.0 KiB
Ruby
201 lines
8.0 KiB
Ruby
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
|