Merge pull request #11349 from briancain/feature/virtualbox-disk-mgmt
[FEATURE] Disk management with the VirtualBox provider
This commit is contained in:
commit
3d2eafc414
@ -12,6 +12,7 @@ module Vagrant
|
||||
autoload :BoxCheckOutdated, "vagrant/action/builtin/box_check_outdated"
|
||||
autoload :BoxRemove, "vagrant/action/builtin/box_remove"
|
||||
autoload :Call, "vagrant/action/builtin/call"
|
||||
autoload :CleanupDisks, "vagrant/action/builtin/cleanup_disks"
|
||||
autoload :Confirm, "vagrant/action/builtin/confirm"
|
||||
autoload :ConfigValidate, "vagrant/action/builtin/config_validate"
|
||||
autoload :DestroyConfirm, "vagrant/action/builtin/destroy_confirm"
|
||||
|
||||
56
lib/vagrant/action/builtin/cleanup_disks.rb
Normal file
56
lib/vagrant/action/builtin/cleanup_disks.rb
Normal file
@ -0,0 +1,56 @@
|
||||
require "json"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
class CleanupDisks
|
||||
# Removes any attached disks no longer defined in a Vagrantfile config
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant::action::builtin::disk")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
machine = env[:machine]
|
||||
defined_disks = get_disks(machine, env)
|
||||
|
||||
# Call into providers machine implementation for disk management
|
||||
disk_meta_file = read_disk_metadata(machine)
|
||||
|
||||
if !disk_meta_file.empty?
|
||||
if machine.provider.capability?(:cleanup_disks)
|
||||
machine.provider.capability(:cleanup_disks, defined_disks, disk_meta_file)
|
||||
else
|
||||
env[:ui].warn(I18n.t("vagrant.actions.disk.provider_unsupported",
|
||||
provider: machine.provider_name))
|
||||
end
|
||||
end
|
||||
|
||||
# Continue On
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def read_disk_metadata(machine)
|
||||
meta_file = machine.data_dir.join("disk_meta")
|
||||
if File.file?(meta_file)
|
||||
disk_meta = JSON.parse(meta_file.read)
|
||||
else
|
||||
@logger.info("No previous disk_meta file defined for guest #{machine.name}")
|
||||
disk_meta = {}
|
||||
end
|
||||
|
||||
return disk_meta
|
||||
end
|
||||
|
||||
def get_disks(machine, env)
|
||||
return @_disks if @_disks
|
||||
|
||||
@_disks = []
|
||||
@_disks = machine.config.vm.disks
|
||||
|
||||
@_disks
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,3 +1,5 @@
|
||||
require "json"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
@ -12,19 +14,30 @@ module Vagrant
|
||||
defined_disks = get_disks(machine, env)
|
||||
|
||||
# Call into providers machine implementation for disk management
|
||||
configured_disks = {}
|
||||
if !defined_disks.empty?
|
||||
if machine.provider.capability?(:configure_disks)
|
||||
machine.provider.capability(:configure_disks, defined_disks)
|
||||
configured_disks = machine.provider.capability(:configure_disks, defined_disks)
|
||||
else
|
||||
env[:ui].warn(I18n.t("vagrant.actions.disk.provider_unsupported",
|
||||
provider: machine.provider_name))
|
||||
end
|
||||
end
|
||||
|
||||
write_disk_metadata(machine, configured_disks) unless configured_disks.empty?
|
||||
|
||||
# Continue On
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def write_disk_metadata(machine, current_disks)
|
||||
meta_file = machine.data_dir.join("disk_meta")
|
||||
@logger.debug("Writing disk metadata file to #{meta_file}")
|
||||
File.open(meta_file.to_s, "w+") do |file|
|
||||
file.write(JSON.dump(current_disks))
|
||||
end
|
||||
end
|
||||
|
||||
def get_disks(machine, env)
|
||||
return @_disks if @_disks
|
||||
|
||||
|
||||
@ -904,6 +904,10 @@ module Vagrant
|
||||
error_key(:virtualbox_broken_version_040214)
|
||||
end
|
||||
|
||||
class VirtualBoxDisksDefinedExceedLimit < VagrantError
|
||||
error_key(:virtualbox_disks_defined_exceed_limit)
|
||||
end
|
||||
|
||||
class VirtualBoxGuestPropertyNotFound < VagrantError
|
||||
error_key(:virtualbox_guest_property_not_found)
|
||||
end
|
||||
|
||||
@ -49,6 +49,14 @@ module Vagrant
|
||||
bytes
|
||||
end
|
||||
|
||||
# Rounds actual value to two decimal places
|
||||
#
|
||||
# @param [Integer] bytes
|
||||
# @return [Integer] megabytes - bytes representation in megabytes
|
||||
def bytes_to_megabytes(bytes)
|
||||
(bytes / MEGABYTE.to_f).round(2)
|
||||
end
|
||||
|
||||
# @private
|
||||
# Reset the cached values for platform. This is not considered a public
|
||||
# API and should only be used for testing.
|
||||
|
||||
@ -29,6 +29,11 @@ module VagrantPlugins
|
||||
# @return [Symbol]
|
||||
attr_accessor :type
|
||||
|
||||
# Type of disk extension to create. Defaults to `vdi`
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :disk_ext
|
||||
|
||||
# Size of disk to create
|
||||
#
|
||||
# @return [Integer,String]
|
||||
@ -61,6 +66,7 @@ module VagrantPlugins
|
||||
@size = UNSET_VALUE
|
||||
@primary = UNSET_VALUE
|
||||
@file = UNSET_VALUE
|
||||
@disk_ext = UNSET_VALUE
|
||||
|
||||
# Internal options
|
||||
@id = SecureRandom.uuid
|
||||
@ -101,6 +107,8 @@ 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
|
||||
@ -109,7 +117,7 @@ module VagrantPlugins
|
||||
if @primary
|
||||
@name = "vagrant_primary"
|
||||
else
|
||||
@name = "name_#{@type.to_s}_#{@id.split("-").last}"
|
||||
@name = nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -127,6 +135,25 @@ module VagrantPlugins
|
||||
types: DEFAULT_DISK_TYPES.join(', '))
|
||||
end
|
||||
|
||||
if @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(', ')
|
||||
else
|
||||
disk_exts = "not found"
|
||||
end
|
||||
errors << I18n.t("vagrant.config.disk.invalid_ext", ext: @disk_ext,
|
||||
name: @name,
|
||||
exts: disk_exts)
|
||||
end
|
||||
else
|
||||
@logger.warn("No provider capability defined to validate 'disk_ext' type")
|
||||
end
|
||||
end
|
||||
|
||||
if @size && !@size.is_a?(Integer)
|
||||
if @size.is_a?(String)
|
||||
@size = Vagrant::Util::Numeric.string_to_bytes(@size)
|
||||
@ -154,6 +181,10 @@ module VagrantPlugins
|
||||
end
|
||||
end
|
||||
|
||||
if !@name
|
||||
errors << I18n.t("vagrant.config.disk.no_name_set", machine: machine.name)
|
||||
end
|
||||
|
||||
errors
|
||||
end
|
||||
|
||||
|
||||
@ -433,8 +433,8 @@ module VagrantPlugins
|
||||
# Add provider config
|
||||
disk_config.add_provider_config(provider_options, &block)
|
||||
|
||||
if !Vagrant::Util::Experimental.feature_enabled?("disk_base_config")
|
||||
@logger.warn("Disk config defined, but experimental feature is not enabled. To use this feature, enable it with the experimental flag `disk_base_config`. Disk will not be added to internal config, and will be ignored.")
|
||||
if !Vagrant::Util::Experimental.feature_enabled?("disks")
|
||||
@logger.warn("Disk config defined, but experimental feature is not enabled. To use this feature, enable it with the experimental flag `disks`. Disk will not be added to internal config, and will be ignored.")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@ -79,6 +79,7 @@ module VagrantPlugins
|
||||
b.use ForwardPorts
|
||||
b.use SetHostname
|
||||
b.use SaneDefaults
|
||||
b.use CleanupDisks
|
||||
b.use Disk
|
||||
b.use Customize, "pre-boot"
|
||||
b.use Boot
|
||||
|
||||
54
plugins/providers/virtualbox/cap/cleanup_disks.rb
Normal file
54
plugins/providers/virtualbox/cap/cleanup_disks.rb
Normal file
@ -0,0 +1,54 @@
|
||||
require "log4r"
|
||||
require "vagrant/util/experimental"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Cap
|
||||
module CleanupDisks
|
||||
LOGGER = Log4r::Logger.new("vagrant::plugins::virtualbox::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)
|
||||
vm_info = machine.provider.driver.show_vm_info
|
||||
primary_disk = vm_info["SATA Controller-ImageUUID-0-0"]
|
||||
|
||||
disk_meta.each do |d|
|
||||
dsk = defined_disks.select { |dk| dk.name == d["name"] }
|
||||
if !dsk.empty? || d["uuid"] == primary_disk
|
||||
next
|
||||
else
|
||||
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)
|
||||
|
||||
if disk_info.empty?
|
||||
LOGGER.warn("Disk '#{d["name"]}' not attached to guest, but still exists.")
|
||||
else
|
||||
machine.provider.driver.remove_disk(disk_info[:port], disk_info[:device])
|
||||
end
|
||||
|
||||
machine.provider.driver.close_medium(d["uuid"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
287
plugins/providers/virtualbox/cap/configure_disks.rb
Normal file
287
plugins/providers/virtualbox/cap/configure_disks.rb
Normal file
@ -0,0 +1,287 @@
|
||||
require "log4r"
|
||||
require "fileutils"
|
||||
require "vagrant/util/numeric"
|
||||
require "vagrant/util/experimental"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Cap
|
||||
module ConfigureDisks
|
||||
LOGGER = Log4r::Logger.new("vagrant::plugins::virtualbox::configure_disks")
|
||||
|
||||
# The max amount of disks that can be attached to a single device in a controller
|
||||
MAX_DISK_NUMBER = 30.freeze
|
||||
|
||||
# @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")
|
||||
|
||||
if defined_disks.size > MAX_DISK_NUMBER
|
||||
# you can only attach up to 30 disks per controller, INCLUDING the primary disk
|
||||
raise Vagrant::Errors::VirtualBoxDisksDefinedExceedLimit
|
||||
end
|
||||
|
||||
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 is always Port 0 Device 0.
|
||||
vm_info = machine.provider.driver.show_vm_info
|
||||
primary_uuid = vm_info["SATA Controller-ImageUUID-0-0"]
|
||||
|
||||
current_disk = all_disks.select { |d| d["UUID"] == primary_uuid }.first
|
||||
else
|
||||
current_disk = all_disks.select { |d| d["Disk Name"] == disk.name}.first
|
||||
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
|
||||
# TODO: What if it needs to be resized?
|
||||
|
||||
disk_info = machine.provider.driver.get_port_and_device(current_disk["UUID"])
|
||||
if disk_info.empty?
|
||||
LOGGER.warn("Disk '#{disk.name}' is not connected to guest '#{machine.name}', Vagrant will attempt to connect disk to guest")
|
||||
dsk_info = get_next_port(machine)
|
||||
machine.provider.driver.attach_disk(dsk_info[:port],
|
||||
dsk_info[:device],
|
||||
current_disk["Location"])
|
||||
else
|
||||
LOGGER.info("No further configuration required for disk '#{disk.name}'")
|
||||
end
|
||||
|
||||
disk_metadata = {uuid: current_disk["UUID"], name: disk.name}
|
||||
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)
|
||||
requested_disk_size = Vagrant::Util::Numeric.bytes_to_megabytes(disk_config.size)
|
||||
defined_disk_size = defined_disk["Capacity"].split(" ").first.to_f
|
||||
|
||||
if defined_disk_size > requested_disk_size
|
||||
machine.ui.warn(I18n.t("vagrant.cap.configure_disks.shrink_size_not_supported", name: disk_config.name))
|
||||
return false
|
||||
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))
|
||||
# NOTE: At the moment, there are no provider specific configs for VirtualBox
|
||||
# but we grab it anyway for future use.
|
||||
disk_provider_config = disk_config.provider_config[:virtualbox] if disk_config.provider_config
|
||||
|
||||
guest_info = machine.provider.driver.show_vm_info
|
||||
guest_folder = File.dirname(guest_info["CfgFile"])
|
||||
|
||||
disk_ext = disk_config.disk_ext
|
||||
disk_file = File.join(guest_folder, disk_config.name) + ".#{disk_ext}"
|
||||
|
||||
LOGGER.info("Attempting to create a new disk file '#{disk_file}' of size '#{disk_config.size}' bytes")
|
||||
|
||||
disk_var = machine.provider.driver.create_disk(disk_file, disk_config.size, disk_ext.upcase)
|
||||
disk_metadata = {uuid: disk_var.split(':').last.strip, name: disk_config.name}
|
||||
|
||||
dsk_controller_info = get_next_port(machine)
|
||||
machine.provider.driver.attach_disk(dsk_controller_info[:port], dsk_controller_info[:device], disk_file)
|
||||
|
||||
disk_metadata
|
||||
end
|
||||
|
||||
# Finds the next available port
|
||||
#
|
||||
# SATA Controller-ImageUUID-0-0 (sub out ImageUUID)
|
||||
# - Controller: SATA Controller
|
||||
# - Port: 0
|
||||
# - Device: 0
|
||||
#
|
||||
# Note: Virtualbox returns the string above with the port and device info
|
||||
# disk_info = key.split("-")
|
||||
# port = disk_info[2]
|
||||
# device = disk_info[3]
|
||||
#
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @return [Hash] dsk_info - The next available port and device on a given controller
|
||||
def self.get_next_port(machine)
|
||||
vm_info = machine.provider.driver.show_vm_info
|
||||
dsk_info = {device: "0", port: "0"}
|
||||
|
||||
disk_images = vm_info.select { |v| v.include?("ImageUUID") && v.include?("SATA Controller") }
|
||||
used_ports = disk_images.keys.map { |k| k.split('-') }.map {|v| v[2].to_i}
|
||||
next_available_port = ((0..(MAX_DISK_NUMBER-1)).to_a - used_ports).first
|
||||
|
||||
dsk_info[:port] = next_available_port.to_s
|
||||
if dsk_info[:port].empty?
|
||||
# This likely only occurs if additional disks have been added outside of Vagrant configuration
|
||||
LOGGER.warn("There are no more available ports to attach disks to for the SATA Controller. Clear up some space on the SATA controller to attach new disks.")
|
||||
raise Vagrant::Errors::VirtualBoxDisksDefinedExceedLimit
|
||||
end
|
||||
|
||||
dsk_info
|
||||
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)
|
||||
|
||||
if defined_disk["Storage format"] == "VMDK"
|
||||
LOGGER.warn("Disk type VMDK cannot be resized in VirtualBox. Vagrant will convert disk to VDI format to resize first, and then convert resized disk back to VMDK format")
|
||||
|
||||
# grab disk to be resized port and device number
|
||||
disk_info = machine.provider.driver.get_port_and_device(defined_disk["UUID"])
|
||||
# original disk information in case anything goes wrong during clone/resize
|
||||
original_disk = defined_disk
|
||||
backup_disk_location = "#{original_disk["Location"]}.backup"
|
||||
|
||||
# clone disk to vdi formatted disk
|
||||
vdi_disk_file = machine.provider.driver.vmdk_to_vdi(defined_disk["Location"])
|
||||
# resize vdi
|
||||
machine.provider.driver.resize_disk(vdi_disk_file, disk_config.size.to_i)
|
||||
|
||||
begin
|
||||
# Danger Zone
|
||||
|
||||
# remove and close original volume
|
||||
machine.provider.driver.remove_disk(disk_info[:port], disk_info[:device])
|
||||
# Create a backup of the original disk if something goes wrong
|
||||
LOGGER.warn("Making a backup of the original disk at #{defined_disk["Location"]}")
|
||||
FileUtils.mv(defined_disk["Location"], backup_disk_location)
|
||||
|
||||
# we have to close here, otherwise we can't re-clone after
|
||||
# resizing the vdi disk
|
||||
machine.provider.driver.close_medium(defined_disk["UUID"])
|
||||
|
||||
# clone back to original vmdk format and attach resized disk
|
||||
vmdk_disk_file = machine.provider.driver.vdi_to_vmdk(vdi_disk_file)
|
||||
machine.provider.driver.attach_disk(disk_info[:port], disk_info[:device], vmdk_disk_file, "hdd")
|
||||
rescue ScriptError, SignalException, StandardError
|
||||
LOGGER.warn("Vagrant encountered an error while trying to resize a disk. Vagrant will now attempt to reattach and preserve the original disk...")
|
||||
machine.ui.error(I18n.t("vagrant.cap.configure_disks.recovery_from_resize",
|
||||
location: original_disk["Location"],
|
||||
name: machine.name))
|
||||
recover_from_resize(machine, disk_info, backup_disk_location, original_disk, vdi_disk_file)
|
||||
|
||||
raise
|
||||
ensure
|
||||
# Remove backup disk file if all goes well
|
||||
FileUtils.remove(backup_disk_location, force: true)
|
||||
end
|
||||
|
||||
# Remove cloned resized volume format
|
||||
machine.provider.driver.close_medium(vdi_disk_file)
|
||||
|
||||
# Get new updated disk UUID for vagrant disk_meta file
|
||||
new_disk_info = machine.provider.driver.list_hdds.select { |h| h["Location"] == defined_disk["Location"] }.first
|
||||
defined_disk = new_disk_info
|
||||
else
|
||||
machine.provider.driver.resize_disk(defined_disk["Location"], disk_config.size.to_i)
|
||||
end
|
||||
|
||||
disk_metadata = {uuid: defined_disk["UUID"], name: disk_config.name}
|
||||
|
||||
disk_metadata
|
||||
end
|
||||
|
||||
# Recovery method for when an exception occurs during the process of resizing disks
|
||||
#
|
||||
# It attempts to move back the backup disk into place, and reattach it to the guest before
|
||||
# raising the original error
|
||||
#
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [Hash] disk_info - The disk device and port number to attach back to
|
||||
# @param [String] backup_disk_location - The place on disk where vagrant made a backup of the original disk being resized
|
||||
# @param [Hash] original_disk - The disk information from VirtualBox
|
||||
# @param [String] vdi_disk_file - The place on disk where vagrant made a clone of the original disk being resized
|
||||
def self.recover_from_resize(machine, disk_info, backup_disk_location, original_disk, vdi_disk_file)
|
||||
begin
|
||||
# move backup to original name
|
||||
FileUtils.mv(backup_disk_location, original_disk["Location"], force: true)
|
||||
# Attach disk
|
||||
machine.provider.driver.
|
||||
attach_disk(disk_info[:port], disk_info[:device], original_disk["Location"], "hdd")
|
||||
|
||||
# Remove cloned disk if still hanging around
|
||||
if vdi_disk_file
|
||||
machine.provider.driver.close_medium(vdi_disk_file)
|
||||
end
|
||||
|
||||
# We recovered!
|
||||
machine.ui.warn(I18n.t("vagrant.cap.configure_disks.recovery_attached_disks"))
|
||||
rescue => e
|
||||
LOGGER.error("Vagrant encountered an error while trying to recover. It will now show the original error and continue...")
|
||||
LOGGER.error(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
27
plugins/providers/virtualbox/cap/validate_disk_ext.rb
Normal file
27
plugins/providers/virtualbox/cap/validate_disk_ext.rb
Normal file
@ -0,0 +1,27 @@
|
||||
require "log4r"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Cap
|
||||
module ValidateDiskExt
|
||||
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
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @param [String] disk_ext
|
||||
# @return [Bool]
|
||||
def self.validate_disk_ext(machine, disk_ext)
|
||||
DEFAULT_DISK_EXT.include?(disk_ext)
|
||||
end
|
||||
|
||||
# @param [Vagrant::Machine] machine
|
||||
# @return [Array]
|
||||
def self.get_default_disk_ext(machine)
|
||||
DEFAULT_DISK_EXT
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -368,6 +368,21 @@ module VagrantPlugins
|
||||
def vm_exists?(uuid)
|
||||
end
|
||||
|
||||
# Returns a hash of information about a given virtual machine
|
||||
#
|
||||
# @param [String] uuid
|
||||
# @return [Hash] info
|
||||
def show_vm_info
|
||||
info = {}
|
||||
execute('showvminfo', @uuid, '--machinereadable', retryable: true).split("\n").each do |line|
|
||||
parts = line.partition('=')
|
||||
key = parts.first.gsub('"', '')
|
||||
value = parts.last.gsub('"', '')
|
||||
info[key] = value
|
||||
end
|
||||
info
|
||||
end
|
||||
|
||||
# Execute the given subcommand for VBoxManage and return the output.
|
||||
def execute(*command, &block)
|
||||
# Get the options hash if it exists
|
||||
|
||||
@ -97,10 +97,15 @@ module VagrantPlugins
|
||||
end
|
||||
end
|
||||
|
||||
def_delegators :@driver, :clear_forwarded_ports,
|
||||
def_delegators :@driver,
|
||||
:attach_disk,
|
||||
:clear_forwarded_ports,
|
||||
:clear_shared_folders,
|
||||
:clone_disk,
|
||||
:clonevm,
|
||||
:close_medium,
|
||||
:create_dhcp_server,
|
||||
:create_disk,
|
||||
:create_host_only_network,
|
||||
:create_snapshot,
|
||||
:delete,
|
||||
@ -111,9 +116,11 @@ module VagrantPlugins
|
||||
:execute_command,
|
||||
:export,
|
||||
:forward_ports,
|
||||
:get_port_and_device,
|
||||
:halt,
|
||||
:import,
|
||||
:list_snapshots,
|
||||
:list_hdds,
|
||||
:read_forwarded_ports,
|
||||
:read_bridged_interfaces,
|
||||
:read_dhcp_servers,
|
||||
@ -130,6 +137,8 @@ module VagrantPlugins
|
||||
:read_vms,
|
||||
:reconfig_host_only,
|
||||
:remove_dhcp_server,
|
||||
:remove_disk,
|
||||
:resize_disk,
|
||||
:restore_snapshot,
|
||||
:resume,
|
||||
:set_mac_address,
|
||||
@ -138,9 +147,11 @@ module VagrantPlugins
|
||||
:ssh_port,
|
||||
:start,
|
||||
:suspend,
|
||||
:vdi_to_vmdk,
|
||||
:verify!,
|
||||
:verify_image,
|
||||
:vm_exists?
|
||||
:vm_exists?,
|
||||
:vmdk_to_vdi
|
||||
|
||||
protected
|
||||
|
||||
|
||||
@ -16,6 +16,28 @@ module VagrantPlugins
|
||||
@uuid = uuid
|
||||
end
|
||||
|
||||
# Controller-Port-Device looks like:
|
||||
# SATA Controller-ImageUUID-0-0 (sub out ImageUUID)
|
||||
# - Controller: SATA Controller
|
||||
# - Port: 0
|
||||
# - Device: 0
|
||||
#
|
||||
# @param [String] port - port on device to attach disk to
|
||||
# @param [String] device - device on controller for disk
|
||||
# @param [String] file - disk file path
|
||||
# @param [String] type - type of disk to attach
|
||||
# @param [Hash] opts - additional options
|
||||
def attach_disk(port, device, file, type="hdd", **opts)
|
||||
# Maybe only support SATA Controller for `:disk`???
|
||||
controller = "SATA Controller"
|
||||
|
||||
comment = "This disk is managed externally by Vagrant. Removing or adjusting settings could potentially cause issues with Vagrant."
|
||||
|
||||
execute('storageattach', @uuid, '--storagectl', controller, '--port',
|
||||
port.to_s, '--device', device.to_s, '--type', type, '--medium',
|
||||
file, '--comment', comment)
|
||||
end
|
||||
|
||||
def clear_forwarded_ports
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
args = []
|
||||
@ -38,6 +60,13 @@ module VagrantPlugins
|
||||
end
|
||||
end
|
||||
|
||||
# @param [String] source
|
||||
# @param [String] destination
|
||||
# @param [String] disk_format
|
||||
def clone_disk(source, destination, disk_format, **opts)
|
||||
execute("clonemedium", source, destination, '--format', disk_format)
|
||||
end
|
||||
|
||||
def clonevm(master_id, snapshot_name)
|
||||
machine_name = "temp_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
|
||||
args = ["--register", "--name", machine_name]
|
||||
@ -49,6 +78,14 @@ module VagrantPlugins
|
||||
return get_machine_id(machine_name)
|
||||
end
|
||||
|
||||
# Removes a disk from the given virtual machine
|
||||
#
|
||||
# @param [String] disk_uuid or file path
|
||||
# @param [Hash] opts - additional options
|
||||
def close_medium(disk_uuid)
|
||||
execute("closemedium", disk_uuid, '--delete')
|
||||
end
|
||||
|
||||
def create_dhcp_server(network, options)
|
||||
retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do
|
||||
begin
|
||||
@ -65,6 +102,17 @@ module VagrantPlugins
|
||||
end
|
||||
end
|
||||
|
||||
# Creates a disk. Default format is VDI unless overridden
|
||||
#
|
||||
# @param [String] disk_file
|
||||
# @param [Integer] disk_size - size in bytes
|
||||
# @param [String] disk_format - format of disk, defaults to "VDI"
|
||||
# @param [Hash] opts - additional options
|
||||
def create_disk(disk_file, disk_size, disk_format="VDI", **opts)
|
||||
execute("createmedium", '--filename', disk_file, '--sizebyte', disk_size.to_i.to_s, '--format', disk_format)
|
||||
end
|
||||
|
||||
|
||||
def create_host_only_network(options)
|
||||
# Create the interface
|
||||
execute("hostonlyif", "create", retryable: true) =~ /^Interface '(.+?)' was successfully created$/
|
||||
@ -130,6 +178,33 @@ module VagrantPlugins
|
||||
end
|
||||
end
|
||||
|
||||
# Lists all attached harddisks from a given virtual machine. Additionally,
|
||||
# this method adds a new key "Disk Name" based on the disks file path from "Location"
|
||||
#
|
||||
# @return [Array] hdds An array of hashes of harddrive info for a guest
|
||||
def list_hdds
|
||||
hdds = []
|
||||
tmp_drive = {}
|
||||
execute('list', 'hdds', retryable: true).split("\n").each do |line|
|
||||
if line == "" # separator between disks
|
||||
hdds << tmp_drive
|
||||
tmp_drive = {}
|
||||
next
|
||||
end
|
||||
parts = line.partition(":")
|
||||
key = parts.first.strip
|
||||
value = parts.last.strip
|
||||
tmp_drive[key] = value
|
||||
|
||||
if key == "Location"
|
||||
tmp_drive["Disk Name"] = File.basename(value, ".*")
|
||||
end
|
||||
end
|
||||
hdds << tmp_drive unless tmp_drive.empty?
|
||||
|
||||
hdds
|
||||
end
|
||||
|
||||
def list_snapshots(machine_id)
|
||||
output = execute(
|
||||
"snapshot", machine_id, "list", "--machinereadable",
|
||||
@ -149,6 +224,21 @@ module VagrantPlugins
|
||||
raise
|
||||
end
|
||||
|
||||
# @param [String] port - port on device to attach disk to
|
||||
# @param [String] device - device on controller for disk
|
||||
# @param [Hash] opts - additional options
|
||||
def remove_disk(port, device)
|
||||
controller = "SATA Controller"
|
||||
execute('storageattach', @uuid, '--storagectl', controller, '--port', port.to_s, '--device', device.to_s, '--medium', "none")
|
||||
end
|
||||
|
||||
# @param [String] disk_file
|
||||
# @param [Integer] disk_size in bytes
|
||||
# @param [Hash] opts - additional options
|
||||
def resize_disk(disk_file, disk_size, **opts)
|
||||
execute("modifymedium", disk_file, '--resizebyte', disk_size.to_i.to_s)
|
||||
end
|
||||
|
||||
def restore_snapshot(machine_id, snapshot_name)
|
||||
# Start with 0%
|
||||
last = 0
|
||||
@ -295,6 +385,27 @@ module VagrantPlugins
|
||||
nil
|
||||
end
|
||||
|
||||
# Returns port and device for an attached disk given a disk uuid. Returns
|
||||
# empty hash if disk is not attachd to guest
|
||||
#
|
||||
# @param [Hash] vm_info - A guests information from vboxmanage
|
||||
# @param [String] disk_uuid - the UUID for the disk we are searching for
|
||||
# @return [Hash] disk_info - Contains a device and port number
|
||||
def get_port_and_device(disk_uuid)
|
||||
vm_info = show_vm_info
|
||||
|
||||
disk = {}
|
||||
disk_info_key = vm_info.key(disk_uuid)
|
||||
return disk if !disk_info_key
|
||||
|
||||
disk_info = disk_info_key.split("-")
|
||||
|
||||
disk[:port] = disk_info[2]
|
||||
disk[:device] = disk_info[3]
|
||||
|
||||
return disk
|
||||
end
|
||||
|
||||
def halt
|
||||
execute("controlvm", @uuid, "poweroff", retryable: true)
|
||||
end
|
||||
@ -783,6 +894,30 @@ module VagrantPlugins
|
||||
return true
|
||||
end
|
||||
|
||||
# @param [VagrantPlugins::VirtualboxProvider::Driver] driver
|
||||
# @param [String] defined_disk_path
|
||||
# @return [String] destination - The cloned disk
|
||||
def vmdk_to_vdi(defined_disk_path)
|
||||
source = defined_disk_path
|
||||
destination = File.join(File.dirname(source), File.basename(source, ".*")) + ".vdi"
|
||||
|
||||
clone_disk(source, destination, 'VDI')
|
||||
|
||||
destination
|
||||
end
|
||||
|
||||
# @param [VagrantPlugins::VirtualboxProvider::Driver] driver
|
||||
# @param [String] defined_disk_path
|
||||
# @return [String] destination - The cloned disk
|
||||
def vdi_to_vmdk(defined_disk_path)
|
||||
source = defined_disk_path
|
||||
destination = File.join(File.dirname(source), File.basename(source, ".*")) + ".vmdk"
|
||||
|
||||
clone_disk(source, destination, 'VMDK')
|
||||
|
||||
destination
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def valid_ip_address?(ip)
|
||||
|
||||
@ -39,6 +39,26 @@ module VagrantPlugins
|
||||
Cap::PublicAddress
|
||||
end
|
||||
|
||||
provider_capability(:virtualbox, :configure_disks) do
|
||||
require_relative "cap/configure_disks"
|
||||
Cap::ConfigureDisks
|
||||
end
|
||||
|
||||
provider_capability(:virtualbox, :cleanup_disks) do
|
||||
require_relative "cap/cleanup_disks"
|
||||
Cap::CleanupDisks
|
||||
end
|
||||
|
||||
provider_capability(:virtualbox, :validate_disk_ext) do
|
||||
require_relative "cap/validate_disk_ext"
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
provider_capability(:virtualbox, :get_default_disk_ext) do
|
||||
require_relative "cap/validate_disk_ext"
|
||||
Cap::ValidateDiskExt
|
||||
end
|
||||
|
||||
provider_capability(:virtualbox, :snapshot_list) do
|
||||
require_relative "cap"
|
||||
Cap
|
||||
|
||||
@ -1641,6 +1641,11 @@ en:
|
||||
4.2.14 contains a critical bug which prevents it from working with
|
||||
Vagrant. VirtualBox 4.2.16+ fixes this problem. Please upgrade
|
||||
VirtualBox.
|
||||
virtualbox_disks_defined_exceed_limit: |-
|
||||
VirtualBox only allows up to 30 disks to be attached to a single guest using the SATA Controller,
|
||||
including the primray disk.
|
||||
|
||||
Please ensure only up to 30 disks are configured for your guest.
|
||||
virtualbox_guest_property_not_found: |-
|
||||
Could not find a required VirtualBox guest property:
|
||||
%{guest_property}
|
||||
@ -1802,6 +1807,8 @@ en:
|
||||
#-------------------------------------------------------------------------------
|
||||
config:
|
||||
disk:
|
||||
invalid_ext: |-
|
||||
Disk type '%{ext}' is not a valid disk extention for '%{name}'. Please pick one of the following supported disk types: %{exts}
|
||||
invalid_type: |-
|
||||
Disk type '%{type}' is not a valid type. Please pick one of the following supported disk types: %{types}
|
||||
invalid_size: |-
|
||||
@ -1812,6 +1819,8 @@ en:
|
||||
Disk file '%{file_path}' for disk '%{name}' on machine '%{machine}' does not exist.
|
||||
missing_provider: |-
|
||||
Guest '%{machine}' using provider '%{provider_name}' has provider specific config options for a provider other than '%{provider_name}'. These provider config options will be ignored for this guest
|
||||
no_name_set: |-
|
||||
A 'name' option is required when defining a disk for guest '%{machine}'.
|
||||
common:
|
||||
bad_field: "The following settings shouldn't exist: %{fields}"
|
||||
chef:
|
||||
@ -2164,11 +2173,30 @@ en:
|
||||
#-------------------------------------------------------------------------------
|
||||
# Translations for Vagrant middleware actions
|
||||
#-------------------------------------------------------------------------------
|
||||
cap:
|
||||
configure_disks:
|
||||
start: "Configuring storage mediums..."
|
||||
floppy_not_supported: "Floppy disk configuration not yet supported. Skipping disk '%{name}'..."
|
||||
dvd_not_supported: "DVD disk configuration not yet supported. Skipping disk '%{name}'..."
|
||||
shrink_size_not_supported: |-
|
||||
VirtualBox does not support shrinking disk sizes. Cannot shrink '%{name}' disks size.
|
||||
create_disk: |-
|
||||
Disk '%{name}' not found in guest. Creating and attaching disk to guest...
|
||||
resize_disk: |-
|
||||
Disk '%{name}' needs to be resized. Resizing disk...
|
||||
recovery_from_resize: |-
|
||||
Vagrant has encountered an exception while trying to resize a disk. It will now attempt to reattach the original disk, as to prevent any data loss.
|
||||
The original disk is located at %{location}
|
||||
If Vagrant fails to reattach the original disk, it is recommended that you open the VirtualBox GUI and navigate to the current guests settings for '%{name}' and look at the 'storage' section. Here is where you can reattach a missing disk if Vagrant fails to do so...
|
||||
recovery_attached_disks: |-
|
||||
Disk has been reattached. Vagrant will now continue on an raise the exception receieved
|
||||
actions:
|
||||
runner:
|
||||
waiting_cleanup: "Waiting for cleanup before exiting..."
|
||||
exit_immediately: "Exiting immediately, without cleanup!"
|
||||
disk:
|
||||
cleanup_provider_unsupported: |-
|
||||
Guest provider '%{provider}' does not support the cleaning up disks, and will not attempt to clean up attached disks on the guest..
|
||||
provider_unsupported: |-
|
||||
Guest provider '%{provider}' does not support the disk feature, and will not use the disk configuration defined.
|
||||
vm:
|
||||
|
||||
@ -9,7 +9,9 @@ describe VagrantPlugins::Kernel_V2::VagrantConfigDisk do
|
||||
|
||||
subject { described_class.new(type) }
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
let(:provider) { double("provider") }
|
||||
let(:machine) { double("machine", provider: provider) }
|
||||
|
||||
|
||||
def assert_invalid
|
||||
errors = subject.validate(machine)
|
||||
@ -30,6 +32,8 @@ describe VagrantPlugins::Kernel_V2::VagrantConfigDisk do
|
||||
|
||||
subject.name = "foo"
|
||||
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)
|
||||
end
|
||||
|
||||
describe "with defaults" do
|
||||
|
||||
@ -7,7 +7,8 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
let(:provider) { double("provider") }
|
||||
let(:machine) { double("machine", provider: provider) }
|
||||
|
||||
def assert_invalid
|
||||
errors = subject.validate(machine)
|
||||
@ -37,6 +38,9 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
|
||||
allow(machine).to receive(:provider_config).and_return(nil)
|
||||
allow(machine).to receive(:provider_options).and_return({})
|
||||
|
||||
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)
|
||||
|
||||
subject.box = "foo"
|
||||
end
|
||||
|
||||
@ -552,12 +556,12 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
|
||||
describe "#disk" do
|
||||
before(:each) do
|
||||
allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).
|
||||
with("disk_base_config").and_return("true")
|
||||
with("disks").and_return("true")
|
||||
end
|
||||
|
||||
it "stores the disks" do
|
||||
subject.disk(:disk, size: 100)
|
||||
subject.disk(:disk, size: 1000, primary: false, name: "storage")
|
||||
subject.disk(:disk, size: 100, primary: true)
|
||||
subject.disk(:disk, size: 1000, name: "storage")
|
||||
subject.finalize!
|
||||
|
||||
assert_valid
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
require_relative "../base"
|
||||
|
||||
require Vagrant.source_root.join("plugins/providers/virtualbox/cap/cleanup_disks")
|
||||
|
||||
describe VagrantPlugins::ProviderVirtualBox::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) { {} }
|
||||
|
||||
let(:vm_info) { {"SATA Controller-ImageUUID-0-0" => "12345",
|
||||
"SATA Controller-ImageUUID-1-0" => "67890"} }
|
||||
|
||||
before do
|
||||
allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).and_return(true)
|
||||
allow(driver).to receive(:show_vm_info).and_return(vm_info)
|
||||
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", 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"=>"67890", "name"=>"storage"}], floppy: [], dvd: []} }
|
||||
let(:defined_disks) { [] }
|
||||
let(:device_info) { {port: "1", device: "0"} }
|
||||
|
||||
it "removes and closes medium from guest" do
|
||||
allow(driver).to receive(:get_port_and_device).
|
||||
with("67890").
|
||||
and_return(device_info)
|
||||
|
||||
expect(driver).to receive(:remove_disk).with("1", "0").and_return(true)
|
||||
expect(driver).to receive(:close_medium).with("67890").and_return(true)
|
||||
|
||||
subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk])
|
||||
end
|
||||
|
||||
describe "when the disk isn't attached to a guest" do
|
||||
it "only closes the medium" do
|
||||
allow(driver).to receive(:get_port_and_device).
|
||||
with("67890").
|
||||
and_return({})
|
||||
|
||||
expect(driver).to receive(:close_medium).with("67890").and_return(true)
|
||||
|
||||
subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,365 @@
|
||||
require_relative "../base"
|
||||
|
||||
require Vagrant.source_root.join("plugins/providers/virtualbox/cap/configure_disks")
|
||||
|
||||
describe VagrantPlugins::ProviderVirtualBox::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(:vm_info) { {"SATA Controller-ImageUUID-0-0" => "12345",
|
||||
"SATA Controller-ImageUUID-1-0" => "67890"} }
|
||||
|
||||
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(:all_disks) { [{"UUID"=>"12345",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/ubuntu-18.04-amd64-disk001.vmdk",
|
||||
"Disk Name"=>"ubuntu-18.04-amd64-disk001",
|
||||
"Storage format"=>"VMDK",
|
||||
"Capacity"=>"65536 MBytes",
|
||||
"Encryption"=>"disabled"},
|
||||
{"UUID"=>"67890",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/disk-0.vdi",
|
||||
"Disk Name"=>"disk-0",
|
||||
"Storage format"=>"VDI",
|
||||
"Capacity"=>"10240 MBytes",
|
||||
"Encryption"=>"disabled"},
|
||||
{"UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/disk-1.vdi",
|
||||
"Disk Name"=>"disk-1",
|
||||
"Storage format"=>"VDI",
|
||||
"Capacity"=>"5120 MBytes",
|
||||
"Encryption"=>"disabled"}] }
|
||||
|
||||
let(:subject) { described_class }
|
||||
|
||||
before do
|
||||
allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).and_return(true)
|
||||
allow(driver).to receive(:show_vm_info).and_return(vm_info)
|
||||
end
|
||||
|
||||
context "#configure_disks" do
|
||||
let(:dsk_data) { {uuid: "1234", name: "disk"} }
|
||||
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
|
||||
|
||||
describe "with over the disk limit for a given device" do
|
||||
let(:defined_disks) { (1..31).each { |i| double("disk-#{i}") }.to_a }
|
||||
|
||||
it "raises an exception if the disks defined exceed the limit for a SATA Controller" do
|
||||
expect{subject.configure_disks(machine, defined_disks)}.
|
||||
to raise_error(Vagrant::Errors::VirtualBoxDisksDefinedExceedLimit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#get_current_disk" do
|
||||
it "gets primary disk uuid if disk to configure is primary" do
|
||||
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",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/ubuntu-18.04-amd64-disk001.vmdk",
|
||||
"Disk Name"=>"ubuntu-18.04-amd64-disk001",
|
||||
"Storage format"=>"VMDK",
|
||||
"Capacity"=>"65536 MBytes",
|
||||
"Encryption"=>"disabled"}] }
|
||||
|
||||
let(:disk_meta) { {uuid: "67890", name: "disk-0"} }
|
||||
|
||||
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",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/ubuntu-18.04-amd64-disk001.vmdk",
|
||||
"Disk Name"=>"ubuntu-18.04-amd64-disk001",
|
||||
"Storage format"=>"VMDK",
|
||||
"Capacity"=>"65536 MBytes",
|
||||
"Encryption"=>"disabled"},
|
||||
{"UUID"=>"67890",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/disk-0.vdi",
|
||||
"Disk Name"=>"disk-0",
|
||||
"Storage format"=>"VDI",
|
||||
"Capacity"=>"10240 MBytes",
|
||||
"Encryption"=>"disabled"}] }
|
||||
|
||||
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",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/ubuntu-18.04-amd64-disk001.vmdk",
|
||||
"Disk Name"=>"ubuntu-18.04-amd64-disk001",
|
||||
"Storage format"=>"VMDK",
|
||||
"Capacity"=>"65536 MBytes",
|
||||
"Encryption"=>"disabled"},
|
||||
{"UUID"=>"67890",
|
||||
"Parent UUID"=>"base",
|
||||
"State"=>"created",
|
||||
"Type"=>"normal (base)",
|
||||
"Location"=>"/home/vagrant/VirtualBox VMs/disk-0.vdi",
|
||||
"Disk Name"=>"disk-0",
|
||||
"Storage format"=>"VDI",
|
||||
"Capacity"=>"10240 MBytes",
|
||||
"Encryption"=>"disabled"}] }
|
||||
|
||||
let(:disk_info) { {port: "1", device: "0"} }
|
||||
|
||||
it "reattaches disk if vagrant defined disk exists but is not attached to guest" 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)
|
||||
|
||||
expect(driver).to receive(:get_port_and_device).with("67890").
|
||||
and_return({})
|
||||
|
||||
expect(driver).to receive(:attach_disk).with((disk_info[:port].to_i + 1).to_s,
|
||||
disk_info[:device],
|
||||
all_disks[1]["Location"])
|
||||
|
||||
subject.handle_configure_disk(machine, defined_disks[1], all_disks)
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
expect(driver).to receive(:get_port_and_device).with("67890").
|
||||
and_return(disk_info)
|
||||
|
||||
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: 1073741824.0, primary: false, type: :disk) }
|
||||
let(:disk_config_large) { double("disk", name: "disk-0", size: 68719476736.0, primary: false, type: :disk) }
|
||||
|
||||
it "shows a warning if user attempts to shrink size" do
|
||||
expect(machine.ui).to receive(:warn)
|
||||
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(subject.compare_disk_size(machine, disk_config_large, all_disks[1])).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "#create_disk" do
|
||||
let(:disk_config) { double("disk", name: "disk-0", size: 1073741824.0,
|
||||
primary: false, type: :disk, disk_ext: "vdi",
|
||||
provider_config: nil) }
|
||||
let(:vm_info) { {"CfgFile"=>"/home/vagrant/VirtualBox VMs/disks/"} }
|
||||
let(:disk_file) { "/home/vagrant/VirtualBox VMs/disk-0.vdi" }
|
||||
let(:disk_data) { "Medium created. UUID: 67890\n" }
|
||||
|
||||
let(:port_and_device) { {port: "1", device: "0"} }
|
||||
|
||||
it "creates a disk and attaches it to a guest" do
|
||||
expect(driver).to receive(:show_vm_info).and_return(vm_info)
|
||||
|
||||
expect(driver).to receive(:create_disk).
|
||||
with(disk_file, disk_config.size, "VDI").and_return(disk_data)
|
||||
|
||||
expect(subject).to receive(:get_next_port).with(machine).
|
||||
and_return(port_and_device)
|
||||
|
||||
expect(driver).to receive(:attach_disk).with(port_and_device[:port],
|
||||
port_and_device[:device],
|
||||
disk_file)
|
||||
|
||||
subject.create_disk(machine, disk_config)
|
||||
end
|
||||
end
|
||||
|
||||
context "#get_next_port" do
|
||||
it "determines the next available port to use" do
|
||||
dsk_info = subject.get_next_port(machine)
|
||||
expect(dsk_info[:device]).to eq("0")
|
||||
expect(dsk_info[:port]).to eq("2")
|
||||
end
|
||||
end
|
||||
|
||||
context "#resize_disk" do
|
||||
describe "when a disk is vmdk format" do
|
||||
let(:disk_config) { double("disk", name: "vagrant_primary", size: 1073741824.0,
|
||||
primary: false, type: :disk, disk_ext: "vmdk",
|
||||
provider_config: nil) }
|
||||
let(:attach_info) { {port: "0", device: "0"} }
|
||||
let(:vdi_disk_file) { "/home/vagrant/VirtualBox VMs/ubuntu-18.04-amd64-disk001.vdi" }
|
||||
let(:vmdk_disk_file) { "/home/vagrant/VirtualBox VMs/ubuntu-18.04-amd64-disk001.vmdk" }
|
||||
|
||||
it "converts the disk to vdi, resizes it, and converts back to vmdk" do
|
||||
expect(FileUtils).to receive(:mv).with(vmdk_disk_file, "#{vmdk_disk_file}.backup").
|
||||
and_return(true)
|
||||
|
||||
expect(driver).to receive(:get_port_and_device).with("12345").
|
||||
and_return(attach_info)
|
||||
|
||||
expect(driver).to receive(:vmdk_to_vdi).with(all_disks[0]["Location"]).
|
||||
and_return(vdi_disk_file)
|
||||
|
||||
expect(driver).to receive(:resize_disk).with(vdi_disk_file, disk_config.size.to_i).
|
||||
and_return(true)
|
||||
|
||||
expect(driver).to receive(:remove_disk).with(attach_info[:port], attach_info[:device]).
|
||||
and_return(true)
|
||||
expect(driver).to receive(:close_medium).with("12345")
|
||||
|
||||
expect(driver).to receive(:vdi_to_vmdk).with(vdi_disk_file).
|
||||
and_return(vmdk_disk_file)
|
||||
|
||||
expect(driver).to receive(:attach_disk).
|
||||
with(attach_info[:port], attach_info[:device], vmdk_disk_file, "hdd").and_return(true)
|
||||
expect(driver).to receive(:close_medium).with(vdi_disk_file).and_return(true)
|
||||
|
||||
expect(driver).to receive(:list_hdds).and_return(all_disks)
|
||||
|
||||
expect(FileUtils).to receive(:remove).with("#{vmdk_disk_file}.backup", force: true).
|
||||
and_return(true)
|
||||
|
||||
subject.resize_disk(machine, disk_config, all_disks[0])
|
||||
end
|
||||
|
||||
it "reattaches original disk if something goes wrong" do
|
||||
expect(FileUtils).to receive(:mv).with(vmdk_disk_file, "#{vmdk_disk_file}.backup").
|
||||
and_return(true)
|
||||
|
||||
expect(driver).to receive(:get_port_and_device).with("12345").
|
||||
and_return(attach_info)
|
||||
|
||||
expect(driver).to receive(:vmdk_to_vdi).with(all_disks[0]["Location"]).
|
||||
and_return(vdi_disk_file)
|
||||
|
||||
expect(driver).to receive(:resize_disk).with(vdi_disk_file, disk_config.size.to_i).
|
||||
and_return(true)
|
||||
|
||||
expect(driver).to receive(:remove_disk).with(attach_info[:port], attach_info[:device]).
|
||||
and_return(true)
|
||||
expect(driver).to receive(:close_medium).with("12345")
|
||||
|
||||
allow(driver).to receive(:vdi_to_vmdk).and_raise(StandardError)
|
||||
|
||||
expect(FileUtils).to receive(:mv).with("#{vmdk_disk_file}.backup", vmdk_disk_file, force: true).
|
||||
and_return(true)
|
||||
|
||||
expect(driver).to receive(:attach_disk).
|
||||
with(attach_info[:port], attach_info[:device], vmdk_disk_file, "hdd").and_return(true)
|
||||
expect(driver).to receive(:close_medium).with(vdi_disk_file).and_return(true)
|
||||
|
||||
expect{subject.resize_disk(machine, disk_config, all_disks[0])}.to raise_error(Exception)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when a disk is vdi format" do
|
||||
let(:disk_config) { double("disk", name: "disk-0", size: 1073741824.0,
|
||||
primary: false, type: :disk, disk_ext: "vdi",
|
||||
provider_config: nil) }
|
||||
it "resizes the disk" do
|
||||
expect(driver).to receive(:resize_disk).with(all_disks[1]["Location"], disk_config.size.to_i)
|
||||
|
||||
subject.resize_disk(machine, disk_config, all_disks[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#vmdk_to_vdi" do
|
||||
it "converts a disk from vmdk to vdi" do
|
||||
end
|
||||
end
|
||||
|
||||
context "#vdi_to_vmdk" do
|
||||
it "converts a disk from vdi to vmdk" do
|
||||
end
|
||||
end
|
||||
end
|
||||
56
test/unit/vagrant/action/builtin/cleanup_disks_test.rb
Normal file
56
test/unit/vagrant/action/builtin/cleanup_disks_test.rb
Normal file
@ -0,0 +1,56 @@
|
||||
require File.expand_path("../../../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Action::Builtin::CleanupDisks do
|
||||
let(:app) { lambda { |env| } }
|
||||
let(:vm) { double("vm") }
|
||||
let(:config) { double("config", vm: vm) }
|
||||
let(:provider) { double("provider") }
|
||||
let(:machine) { double("machine", config: config, provider: provider, name: "machine",
|
||||
provider_name: "provider", data_dir: Pathname.new("/fake/dir")) }
|
||||
let(:env) { { ui: ui, machine: machine} }
|
||||
|
||||
let(:disks) { [double("disk")] }
|
||||
|
||||
let(:ui) { double("ui") }
|
||||
|
||||
let(:disk_meta_file) { {disk: [{uuid: "123456789", name: "storage"}], floppy: [], dvd: []} }
|
||||
|
||||
describe "#call" do
|
||||
it "calls configure_disks if disk config present" do
|
||||
allow(vm).to receive(:disks).and_return(disks)
|
||||
allow(machine).to receive(:disks).and_return(disks)
|
||||
allow(machine.provider).to receive(:capability?).with(:cleanup_disks).and_return(true)
|
||||
subject = described_class.new(app, env)
|
||||
|
||||
expect(app).to receive(:call).with(env).ordered
|
||||
expect(subject).to receive(:read_disk_metadata).with(machine).and_return(disk_meta_file)
|
||||
expect(machine.provider).to receive(:capability).
|
||||
with(:cleanup_disks, disks, disk_meta_file)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "continues on if no disk config present" do
|
||||
allow(vm).to receive(:disks).and_return([])
|
||||
subject = described_class.new(app, env)
|
||||
|
||||
expect(app).to receive(:call).with(env).ordered
|
||||
expect(machine.provider).not_to receive(:capability).with(:cleanup_disks, disks)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "prints a warning if disk config capability is unsupported" do
|
||||
allow(vm).to receive(:disks).and_return(disks)
|
||||
allow(machine.provider).to receive(:capability?).with(:cleanup_disks).and_return(false)
|
||||
subject = described_class.new(app, env)
|
||||
expect(subject).to receive(:read_disk_metadata).with(machine).and_return(disk_meta_file)
|
||||
|
||||
expect(app).to receive(:call).with(env).ordered
|
||||
expect(machine.provider).not_to receive(:capability).with(:cleanup_disks, disks)
|
||||
expect(ui).to receive(:warn)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -5,13 +5,16 @@ describe Vagrant::Action::Builtin::Disk do
|
||||
let(:vm) { double("vm") }
|
||||
let(:config) { double("config", vm: vm) }
|
||||
let(:provider) { double("provider") }
|
||||
let(:machine) { double("machine", config: config, provider: provider, provider_name: "provider") }
|
||||
let(:machine) { double("machine", config: config, provider: provider,
|
||||
provider_name: "provider", data_dir: Pathname.new("/fake/dir")) }
|
||||
let(:env) { { ui: ui, machine: machine} }
|
||||
|
||||
let(:disks) { [double("disk")] }
|
||||
|
||||
let(:ui) { double("ui") }
|
||||
|
||||
let(:disk_data) { {disk: [{uuid: "123456789", name: "storage"}], floppy: [], dvd: []} }
|
||||
|
||||
describe "#call" do
|
||||
it "calls configure_disks if disk config present" do
|
||||
allow(vm).to receive(:disks).and_return(disks)
|
||||
@ -20,7 +23,10 @@ describe Vagrant::Action::Builtin::Disk do
|
||||
subject = described_class.new(app, env)
|
||||
|
||||
expect(app).to receive(:call).with(env).ordered
|
||||
expect(machine.provider).to receive(:capability).with(:configure_disks, disks)
|
||||
expect(machine.provider).to receive(:capability).
|
||||
with(:configure_disks, disks).and_return(disk_data)
|
||||
|
||||
expect(subject).to receive(:write_disk_metadata).and_return(true)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
@ -32,6 +38,8 @@ describe Vagrant::Action::Builtin::Disk do
|
||||
expect(app).to receive(:call).with(env).ordered
|
||||
expect(machine.provider).not_to receive(:capability).with(:configure_disks, disks)
|
||||
|
||||
expect(subject).not_to receive(:write_disk_metadata)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
@ -46,5 +54,13 @@ describe Vagrant::Action::Builtin::Disk do
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "writes down a disk_meta file if disks are configured" do
|
||||
subject = described_class.new(app, env)
|
||||
|
||||
expect(File).to receive(:open).with("/fake/dir/disk_meta", "w+").and_return(true)
|
||||
|
||||
subject.write_disk_metadata(machine, disk_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -18,4 +18,10 @@ describe Vagrant::Util::Numeric do
|
||||
expect(bytes).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "bytes to megabytes" do
|
||||
it "converts bytes to megabytes" do
|
||||
expect(subject.bytes_to_megabytes(1000000)).to eq(0.95)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -8,32 +8,22 @@ description: |-
|
||||
|
||||
# Configuration
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning!</strong> 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="disk_base_config"
|
||||
```
|
||||
|
||||
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, triggers with the `:type` option
|
||||
will be ignored.
|
||||
</div>
|
||||
|
||||
Vagrant Disks has several options that allow users to define and attach disks to guests.
|
||||
|
||||
## Disk Options
|
||||
|
||||
* `name` (string) - Optional argument to give the disk a name
|
||||
* `type` (symbol) - The type of disk to manage. This option defaults to `:disk`. Please read the provider specific documentation for supported types.
|
||||
* `file` (string) - Optional argument that defines a path on disk pointing to the location of a disk file.
|
||||
* `primary` (boolean) - Optional argument that configures a given disk to be the "primary" disk to manage on the guest. There can only be one `primary` disk per guest.
|
||||
* `provider_config` (hash) - Additional provider specific options for managing a given disk.
|
||||
* `disk_ext` (string) - Optional argument that defines what kind of file
|
||||
extension a disk should have. Defaults to `"vdi"` if unspecified. For a list of
|
||||
supported disk extensions, please check the specific provider being used.
|
||||
* `file` (string) - Optional argument that defines a path on disk pointing to
|
||||
the location of a disk file that already exists.
|
||||
* `name` (string) - Required option to give the disk a name. This name will be
|
||||
used as the filename when creating the disk.
|
||||
* `primary` (boolean) - Optional argument that configures a given disk to be the
|
||||
"primary" disk to manage on the guest. There can only be one `primary` disk per guest.
|
||||
Defaults to `false` if not specified.
|
||||
* `provider_config` (hash) - Additional provider specific options for managing a given disk. Please refer to
|
||||
the provider specific documentation to see any available provider_config options.
|
||||
|
||||
Generally, the disk option accepts two kinds of ways to define a provider config:
|
||||
|
||||
@ -41,8 +31,12 @@ Vagrant Disks has several options that allow users to define and attach disks to
|
||||
- The provider name followed by a double underscore, and then the provider specific option for that disk
|
||||
+ `{providername: {diskoption: value}, otherprovidername: {diskoption: value}`
|
||||
- A hash where the top level key(s) are one or more providers, and each provider keys values are a hash of options and their values.
|
||||
* `size` (String) - The size of the disk to create. For example, `"10GB"`.
|
||||
|
||||
**Note:** More specific examples of these can be found under the provider specific disk page. The `provider_config` option will depend on the provider you are using. Please read the provider specific documentation for disk management to learn about what options are available to use.
|
||||
**Note:** More specific examples of these can be found under the provider
|
||||
specific disk page. The `provider_config` option will depend on the provider
|
||||
you are using. Please read the provider specific documentation for disk
|
||||
management to learn about what options are available to use.
|
||||
|
||||
## Disk Types
|
||||
|
||||
@ -52,6 +46,9 @@ The disk config currently accepts three kinds of disk types:
|
||||
* `dvd` (symbol)
|
||||
* `floppy` (symbol)
|
||||
|
||||
**NOTE:** These types depend on the provider used, and may not yet be functional. Please
|
||||
refer to the provider specific implementation for more details for what is supported.
|
||||
|
||||
You can set a disk type with the first argument of a disk config in your Vagrantfile:
|
||||
|
||||
```ruby
|
||||
@ -65,12 +62,50 @@ If you are a vagrant plugin author who maintains a provider for Vagrant, this sh
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning!</strong> This guide is still being written as we develop this
|
||||
new feature for Vagrant. Some points below are what we plan on covering once this
|
||||
feature is more fully developed in Vagrant.
|
||||
new feature for Vagrant. Is something missing, or could this be improved? Please
|
||||
let us know on GitHub by opening an issue or open a pull request directly.
|
||||
</div>
|
||||
|
||||
- Entry level builtin action `disk` and how to use it as a provider author
|
||||
- `id` is unique to each disk config object
|
||||
- `provider_config` and how to its structured and how to use/validate it
|
||||
All providers must implement the capability `configure_disks`, and `cleanup_disks`.
|
||||
These methods are responsible for the following:
|
||||
|
||||
More information should be coming once the disk feature is more functional.
|
||||
- `configure_disks` - Reads in a Vagrant config for defined disks from a Vagrantfile,
|
||||
and creates and attaches the disks based on the given config
|
||||
- `cleanup_disks` - Compares the current Vagrant config for defined disks and detaches
|
||||
any disks that are no longer valid for a guest.
|
||||
|
||||
These methods are called in the builtin Vagrant actions _Disk_ and _CleanupDisks_.
|
||||
If the provider does not support these capabilities, they will be skipped over and no
|
||||
disks will be configured. It is the providers job to implement these provider capabilities
|
||||
and handle the methods required to support disk creation and deletion. Vagrant will
|
||||
handle parsing and supplying the config object based on what has been defined inside
|
||||
a users Vagrantfile.
|
||||
|
||||
For a more detailed example of how to use this disk configuration with Vagrant, please
|
||||
check out how it was implemented using the VirtualBox provider.
|
||||
|
||||
### The disk_meta file
|
||||
|
||||
Both builtin disk actions `configure_disks` and `cleanup_disks` expect to read and
|
||||
write down a `disk_meta` file inside a machines data dir. This file is specifically
|
||||
for keeping track of the _last configured state_ for disks in a given provider.
|
||||
Generally, this file is used as a way for Vagrant to keep track of what disks
|
||||
are being managed by Vagrant with the provider uses, so that it does not accidentally
|
||||
delete or manage disks that were configured outside of Vagrants configuration.
|
||||
|
||||
For the VirtualBox provider, Vagrant uses this file to see what disks were configured
|
||||
on the _last run_ of Vagrant, and compares that to the current configured state for
|
||||
the Vagrantfile on the _current run_ of Vagrant. It specifically stores each disks
|
||||
UUID and disk name for use. If it notices a disk that is no longer in the
|
||||
Vagrantfile, it can be assumed that the disk is no longer valid for that guest,
|
||||
and cleans up the disk.
|
||||
|
||||
This may not be required for your provider, however with the VirtualBox provider, Vagrant
|
||||
needs a way to keep track of the defined disks managed by Vagrant and their disk UUIDs
|
||||
that VirtualBox uses to keep track of these disks.
|
||||
|
||||
### The provider_config hash
|
||||
|
||||
The disk config class supports an optional hash of options called `provider_config`.
|
||||
This allows the user to define some additional options for a provider to use that
|
||||
may be non-standard across different providers.
|
||||
|
||||
@ -11,24 +11,19 @@ description: |-
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning!</strong> 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="disk_base_config"
|
||||
```
|
||||
|
||||
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, triggers with the `:type` option
|
||||
will be ignored.
|
||||
|
||||
<strong>NOTE:</strong> Vagrant disks is currently a future feature for Vagrant that is not yet supported.
|
||||
Some documentation exists here for future reference, however the Disk feature is
|
||||
not yet functional. Please be patient for us to develop this new feature, and stay
|
||||
tuned for a future release of Vagrant with this new functionality!
|
||||
supported or functional. Please refer to the providier specific disk documentation
|
||||
for more information on how to use and enable this feature.
|
||||
</div>
|
||||
|
||||
Vagrant Disks is a feature that allows users to define what mediums should be attached
|
||||
to their guests, as well as allowing users to resize their primary disk.
|
||||
|
||||
For examples on how to achieve this, among other use cases, please refer to the [usage](/docs/disks/usage.html)
|
||||
guide for more information!
|
||||
|
||||
For more information about what options are available for configuring disks, see the
|
||||
[configuration section](/docs/disks/configuration.html).
|
||||
|
||||
## Supported Providers
|
||||
|
||||
Currently, only VirtualBox is supported. Please refer to the [VirtualBox documentation](/docs/disks/virtualbox/index.html) for more information on using disks with the VirtualBox provider!
|
||||
|
||||
@ -16,19 +16,88 @@ description: |-
|
||||
This feature currently reqiures the experimental flag to be used. To explicitly enable this feature, you can set the experimental flag to:
|
||||
|
||||
```
|
||||
VAGRANT_EXPERIMENTAL="disk_base_config"
|
||||
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, triggers with the `:type` option
|
||||
will be ignored.
|
||||
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 providier for this feature.
|
||||
</div>
|
||||
|
||||
Below are some very simple examples of how to use Vagrant Disks.
|
||||
Below are some very simple examples of how to use Vagrant Disks with the VirtualBox provider.
|
||||
|
||||
## Examples
|
||||
## Basic Examples
|
||||
|
||||
- Resizing a disk (primary)
|
||||
- Attaching a new disk
|
||||
- Using provider specific options
|
||||
### Resizing your primary disk
|
||||
|
||||
Sometimes, the primary disk for a guest is not large enough and you will need to
|
||||
add more space. To resize a disk, you can simply add a config like this below
|
||||
to expand the size of your guests drive:
|
||||
|
||||
```ruby
|
||||
config.vm.disk :disk, size: "100GB", primary: true
|
||||
```
|
||||
|
||||
Note: the `primary: true` is what tells Vagrant to expand the guests main drive.
|
||||
Without this option, Vagrant will instead attach a _new_ disk to the guest.
|
||||
|
||||
For example, this Ubuntu guest will now come with 100GB of space, rather than the default:
|
||||
|
||||
```ruby
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.define "hashicorp" do |h|
|
||||
h.vm.box = "hashicorp/bionic64"
|
||||
h.vm.provider :virtualbox
|
||||
|
||||
h.vm.disk :disk, size: "100GB", primary: true
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
It should be noted that due to how VirtualBox functions, it is not possible to shrink
|
||||
the size of a disk.
|
||||
|
||||
### Attaching new disks
|
||||
|
||||
Vagrant can attach multiple disks to a guest using the VirtualBox provider. An example
|
||||
of attaching a single disk to a guest with 10 GB of storage can be found below:
|
||||
|
||||
```ruby
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.define "hashicorp" do |h|
|
||||
h.vm.box = "hashicorp/bionic64"
|
||||
h.vm.provider :virtualbox
|
||||
|
||||
h.vm.disk :disk, size: "10GB", name: "extra_storage"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Optionally, if you need to attach many disks, you can use Ruby to generate multiple
|
||||
disks for Vagrant to create and attach to your guest:
|
||||
|
||||
```ruby
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.define "hashicorp" do |h|
|
||||
h.vm.box = "hashicorp/bionic64"
|
||||
h.vm.provider :virtualbox
|
||||
|
||||
(0..3).each do |i|
|
||||
h.vm.disk :disk, size: "5GB", name: "disk-#{i}"
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Note: Virtualbox only allows for up to 30 disks to be attached to a given SATA Controller,
|
||||
and this number includes the primary disk! Attempting to configure more than 30 will
|
||||
result in a Vagrant error.
|
||||
|
||||
### Removing Disks
|
||||
|
||||
If you have removed a disk from your Vagrant config and wish for it to be detached from the guest,
|
||||
you will need to `vagrant reload` your guest to apply these changes. **NOTE:** Doing so
|
||||
will also delete the medium from your hard drive.
|
||||
|
||||
42
website/source/docs/disks/virtualbox/common-issues.html.md
Normal file
42
website/source/docs/disks/virtualbox/common-issues.html.md
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
layout: "docs"
|
||||
page_title: "Common Issues - Disks VirtualBox Provider"
|
||||
sidebar_current: "disks-providers-virtualbox-issues"
|
||||
description: |-
|
||||
This page lists some common issues people run into with Vagrant and VirtualBox
|
||||
as well as solutions for those issues.
|
||||
---
|
||||
|
||||
# Common Issues and Troubleshooting
|
||||
|
||||
This page lists some common issues people run into with Vagrant and VirtualBox
|
||||
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 VirtualBox 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.
|
||||
|
||||
## How many disks can I attach?
|
||||
|
||||
Vagrant attaches all new disks defined to a guests SATA Controller. As of VirtualBox 6.1.x,
|
||||
SATA Controllers can only support up to **30 disks** per guest. Therefore if you try
|
||||
to define and attach more than 30, it will result in an error. This number _includes_
|
||||
the primary disk for the guest.
|
||||
|
||||
## Resizing VMDK format disks
|
||||
|
||||
VMDK disks cannot be resized in their current state, so Vagrant will automatically
|
||||
convert these disks to VDI, resize the disk, and convert it back to its original format.
|
||||
Many Vagrant boxes default to using the VMDK disk format, so resizing disks for
|
||||
many users will require Vagrant to convert these disks. Generally, this will be transparent
|
||||
to the user. However if Vagrant crashes or if a user interrupts Vagrant during the
|
||||
cloning process, there is a chance that you might lose your data.
|
||||
|
||||
## Applying Vagrant disk configuration changes to guests
|
||||
|
||||
Due to how VirtualBox 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.
|
||||
42
website/source/docs/disks/virtualbox/index.html.md
Normal file
42
website/source/docs/disks/virtualbox/index.html.md
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
layout: "docs"
|
||||
page_title: "Disks for VirtualBox Provider"
|
||||
sidebar_current: "disks-providers-virtualbox"
|
||||
description: |-
|
||||
Vagrant comes with support out of the box for VirtualBox, a free,
|
||||
cross-platform consumer virtualization product.
|
||||
---
|
||||
|
||||
# VirtualBox
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning!</strong> 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.
|
||||
</div>
|
||||
|
||||
**Vagrant currently only supports VirtualBox version 5.x and newer for configuring and
|
||||
attaching disks.**
|
||||
|
||||
Because of how VirtualBox 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.
|
||||
|
||||
When new disks are defined to be attached to a guest, Vagrant will create and attach
|
||||
these disks to a guests SATA Controller. It should be noted that up to 30 disks
|
||||
can be attached to the SATA Controller.
|
||||
|
||||
For more information on how to use VirtualBox to configure disks for a guest, refer
|
||||
to the [general usage](/docs/disks/usage.html) and [configuration](/docs/disks/configuration.html)
|
||||
guide for more information.
|
||||
34
website/source/docs/disks/virtualbox/usage.html.md
Normal file
34
website/source/docs/disks/virtualbox/usage.html.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
layout: "docs"
|
||||
page_title: "Usage - Disks VirtualBox Provider"
|
||||
sidebar_current: "disks-providers-virtualbox-usage"
|
||||
description: |-
|
||||
The Vagrant VirtualBox provider is used just like any other provider. Please
|
||||
read the general basic usage page for providers.
|
||||
---
|
||||
|
||||
# Usage
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning!</strong> 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.
|
||||
</div>
|
||||
|
||||
For examples of how to use the disk feature with VirtualBox, please refer to the
|
||||
[general disk usage guide](/docs/disks/usage.html) for more examples.
|
||||
|
||||
## provider_config options
|
||||
|
||||
Currently, there are no additional options supported for the `provider_config` option.
|
||||
This page will be updated with any valid options as they become supported.
|
||||
@ -139,6 +139,13 @@
|
||||
<ul class="nav">
|
||||
<li<%= sidebar_current("disks-configuration") %>><a href="/docs/disks/configuration.html">Configuration</a></li>
|
||||
<li<%= sidebar_current("disks-usage") %>><a href="/docs/disks/usage.html">Usage</a></li>
|
||||
<li<%= sidebar_current("disks-providers-virtualbox") %>>
|
||||
<a href="/docs/disks/virtualbox/">VirtualBox</a>
|
||||
<ul class="nav">
|
||||
<li<%= sidebar_current("disks-providers-virtualbox-usage") %>><a href="/docs/disks/virtualbox/usage.html">Usage</a></li>
|
||||
<li<%= sidebar_current("disks-providers-virtualbox-issues") %>><a href="/docs/disks/virtualbox/common-issues.html">Common Issues</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user