mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2025-02-25 18:55:27 -06:00
164 lines
6.0 KiB
Ruby
164 lines
6.0 KiB
Ruby
require 'log4r'
|
|
|
|
module VagrantPlugins
|
|
module ProviderLibvirt
|
|
module Action
|
|
class HandleBoxImage
|
|
include VagrantPlugins::ProviderLibvirt::Util::StorageUtil
|
|
|
|
|
|
@@lock = Mutex.new
|
|
|
|
def initialize(app, _env)
|
|
@logger = Log4r::Logger.new('vagrant_libvirt::action::handle_box_image')
|
|
@app = app
|
|
end
|
|
|
|
def call(env)
|
|
# Verify box metadata for mandatory values.
|
|
#
|
|
# Virtual size has to be set for allocating space in storage pool.
|
|
box_virtual_size = env[:machine].box.metadata['virtual_size']
|
|
raise Errors::NoBoxVirtualSizeSet if box_virtual_size.nil?
|
|
|
|
# Support qcow2 format only for now, but other formats with backing
|
|
# store capability should be usable.
|
|
box_format = env[:machine].box.metadata['format']
|
|
if box_format.nil?
|
|
raise Errors::NoBoxFormatSet
|
|
elsif box_format != 'qcow2'
|
|
raise Errors::WrongBoxFormatSet
|
|
end
|
|
|
|
# Get config options
|
|
config = env[:machine].provider_config
|
|
box_image_file = env[:machine].box.directory.join('box.img').to_s
|
|
env[:box_volume_name] = env[:machine].box.name.to_s.dup.gsub('/', '-VAGRANTSLASH-')
|
|
env[:box_volume_name] << "_vagrant_box_image_#{
|
|
begin
|
|
env[:machine].box.version.to_s
|
|
rescue
|
|
''
|
|
end}.img"
|
|
|
|
# Override box_virtual_size
|
|
if config.machine_virtual_size
|
|
if config.machine_virtual_size < box_virtual_size
|
|
# Warn that a virtual size less than the box metadata size
|
|
# is not supported and will be ignored
|
|
env[:ui].warn I18n.t(
|
|
'vagrant_libvirt.warnings.ignoring_virtual_size_too_small',
|
|
requested: config.machine_virtual_size, minimum: box_virtual_size
|
|
)
|
|
else
|
|
env[:ui].info I18n.t('vagrant_libvirt.manual_resize_required')
|
|
box_virtual_size = config.machine_virtual_size
|
|
end
|
|
end
|
|
# save for use by later actions
|
|
env[:box_virtual_size] = box_virtual_size
|
|
|
|
# while inside the synchronize block take care not to call the next
|
|
# action in the chain, as must exit this block first to prevent
|
|
# locking all subsequent actions as well.
|
|
@@lock.synchronize do
|
|
# Don't continue if image already exists in storage pool.
|
|
break if ProviderLibvirt::Util::Collection.find_matching(
|
|
env[:machine].provider.driver.connection.volumes.all, env[:box_volume_name]
|
|
)
|
|
|
|
# Box is not available as a storage pool volume. Create and upload
|
|
# it as a copy of local box image.
|
|
env[:ui].info(I18n.t('vagrant_libvirt.uploading_volume'))
|
|
|
|
# Create new volume in storage pool
|
|
unless File.exist?(box_image_file)
|
|
raise Vagrant::Errors::BoxNotFound, name: env[:machine].box.name
|
|
end
|
|
box_image_size = File.size(box_image_file) # B
|
|
message = "Creating volume #{env[:box_volume_name]}"
|
|
message << " in storage pool #{config.storage_pool_name}."
|
|
@logger.info(message)
|
|
|
|
begin
|
|
fog_volume = env[:machine].provider.driver.connection.volumes.create(
|
|
name: env[:box_volume_name],
|
|
allocation: "#{box_image_size / 1024 / 1024}M",
|
|
capacity: "#{box_virtual_size}G",
|
|
format_type: box_format,
|
|
owner: storage_uid env,
|
|
group: storage_gid env,
|
|
pool_name: config.storage_pool_name
|
|
)
|
|
rescue Fog::Errors::Error => e
|
|
raise Errors::FogCreateVolumeError,
|
|
error_message: e.message
|
|
end
|
|
|
|
# Upload box image to storage pool
|
|
ret = upload_image(box_image_file, config.storage_pool_name,
|
|
env[:box_volume_name], env) do |progress|
|
|
env[:ui].clear_line
|
|
env[:ui].report_progress(progress, box_image_size, false)
|
|
end
|
|
|
|
# Clear the line one last time since the progress meter doesn't
|
|
# disappear immediately.
|
|
env[:ui].clear_line
|
|
|
|
# If upload failed or was interrupted, remove created volume from
|
|
# storage pool.
|
|
if env[:interrupted] || !ret
|
|
begin
|
|
fog_volume.destroy
|
|
rescue
|
|
nil
|
|
end
|
|
end
|
|
end
|
|
|
|
@app.call(env)
|
|
end
|
|
|
|
protected
|
|
|
|
# Fog Libvirt currently doesn't support uploading images to storage
|
|
# pool volumes. Use ruby-libvirt client instead.
|
|
def upload_image(image_file, pool_name, volume_name, env)
|
|
image_size = File.size(image_file) # B
|
|
|
|
begin
|
|
pool = env[:machine].provider.driver.connection.client.lookup_storage_pool_by_name(
|
|
pool_name
|
|
)
|
|
volume = pool.lookup_volume_by_name(volume_name)
|
|
stream = env[:machine].provider.driver.connection.client.stream
|
|
volume.upload(stream, offset = 0, length = image_size)
|
|
|
|
# Exception ProviderLibvirt::RetrieveError can be raised if buffer is
|
|
# longer than length accepted by API send function.
|
|
#
|
|
# TODO: How to find out if buffer is too large and what is the
|
|
# length that send function will accept?
|
|
|
|
buf_size = 1024 * 250 # 250K
|
|
progress = 0
|
|
open(image_file, 'rb') do |io|
|
|
while (buff = io.read(buf_size))
|
|
sent = stream.send buff
|
|
progress += sent
|
|
yield progress
|
|
end
|
|
end
|
|
rescue => e
|
|
raise Errors::ImageUploadError,
|
|
error_message: e.message
|
|
end
|
|
|
|
progress == image_size
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|