Download images with libvirt instead of copying (#928)

Packaging an existing vagrant environment relied on copying the backing
image. Users may not have permission to read this file despite being
able to interact with it through libvirt.

Use ruby-libvirt to download the image instead so any user with libvirt
access can run the package command.
This commit is contained in:
ryanfitzsimon 2020-05-10 22:13:56 +10:00 committed by GitHub
parent 7e966febbd
commit a83900f1ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 10 deletions

View File

@ -1,3 +1,4 @@
require 'fileutils'
require 'log4r'
module VagrantPlugins
@ -21,21 +22,26 @@ module VagrantPlugins
root_disk = domain.volumes.select do |x|
x.name == libvirt_domain.name + '.img'
end.first
raise Errors::NoDomainVolume if root_disk.nil?
boxname = env['package.output']
raise "#{boxname}: Already exists" if File.exist?(boxname)
@tmp_dir = Dir.pwd + '/_tmp_package'
@tmp_img = @tmp_dir + '/box.img'
Dir.mkdir(@tmp_dir)
if File.readable?(root_disk.path)
backing = `qemu-img info "#{root_disk.path}" | grep 'backing file:' | cut -d ':' -f2`.chomp
else
env[:ui].error("Require set read access to #{root_disk.path}. sudo chmod a+r #{root_disk.path}")
FileUtils.rm_rf(@tmp_dir)
raise 'Have no access'
FileUtils.mkdir_p(@tmp_dir)
env[:ui].info("Downloading #{root_disk.name} to #{@tmp_img}")
ret = download_image(@tmp_img, env[:machine].provider_config.storage_pool_name,
root_disk.name, env) do |progress,image_size|
env[:ui].clear_line
env[:ui].report_progress(progress, image_size, false)
end
# Clear the line one last time since the progress meter doesn't
# disappear immediately.
env[:ui].clear_line
backing = `qemu-img info "#{@tmp_img}" | grep 'backing file:' | cut -d ':' -f2`.chomp
if backing
env[:ui].info('Image has backing image, copying image and rebasing ...')
`qemu-img rebase -p -b "" #{@tmp_img}`
end
env[:ui].info('Image has backing image, copying image and rebasing ...')
FileUtils.cp(root_disk.path, @tmp_img)
`qemu-img rebase -p -b "" #{@tmp_img}`
# remove hw association with interface
# working for centos with lvs default disks
options = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPTIONS', '')
@ -101,6 +107,44 @@ module VagrantPlugins
}
EOF
end
protected
# Fog libvirt currently doesn't support downloading images from storage
# pool volumes. Use ruby-libvirt client instead.
def download_image(image_file, pool_name, volume_name, env)
begin
pool = env[:machine].provider.driver.connection.client.lookup_storage_pool_by_name(
pool_name
)
volume = pool.lookup_volume_by_name(volume_name)
image_size = volume.info.allocation # B
stream = env[:machine].provider.driver.connection.client.stream
# Use length of 0 to download remaining contents after offset
volume.download(stream, offset = 0, length = 0)
buf_size = 1024 * 250 # 250K, copied from upload_image in handle_box_image.rb
progress = 0
retval = stream.recv(buf_size)
open(image_file, 'wb') do |io|
while (retval.at(0) > 0)
recvd = io.write(retval.at(1))
progress += recvd
yield [progress, image_size]
retval = stream.recv(buf_size)
end
end
rescue => e
raise Errors::ImageDownloadError,
volume_name: volume_name,
pool_name: pool_name,
error_message: e.message
end
progress == image_size
end
end
end
end

View File

@ -33,6 +33,10 @@ module VagrantPlugins
error_key(:image_upload_error)
end
class ImageDownloadError < VagrantLibvirtError
error_key(:image_download_error)
end
# Box exceptions
class NoBoxVolume < VagrantLibvirtError
error_key(:no_box_volume)

View File

@ -111,6 +111,8 @@ en:
There was error while creating Libvirt storage pool: %{error_message}
image_upload_error: |-
Error while uploading image to storage pool: %{error_message}
image_download_error: |-
Error while downloading volume '%{volume_name}' from storage pool '%{pool_name}': %{error_message}
no_domain_error: |-
No domain found. %{error_message}
attach_device_error: |-