lib/vagrant-libvirt/action/package_domain.rb in vagrant-libvirt-0.5.3 vs lib/vagrant-libvirt/action/package_domain.rb in vagrant-libvirt-0.6.0
- old
+ new
@@ -1,8 +1,16 @@
+# frozen_string_literal: true
+
require 'fileutils'
require 'log4r'
+class String
+ def unindent
+ gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")
+ end
+end
+
module VagrantPlugins
module ProviderLibvirt
module Action
# Action for create new box for Libvirt provider
class PackageDomain
@@ -10,112 +18,169 @@
def initialize(app, env)
@logger = Log4r::Logger.new('vagrant_libvirt::action::package_domain')
@app = app
- env['package.files'] ||= {}
- env['package.output'] ||= 'package.box'
+
+ @options = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPTIONS', '')
+ @operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir,-ssh-hostkeys,-customize')
end
def call(env)
env[:ui].info(I18n.t('vagrant_libvirt.package_domain'))
libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(
env[:machine].id
)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
- root_disk = domain.volumes.select do |x|
+
+ volumes = domain.volumes.select { |x| !x.nil? }
+ root_disk = 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'
- 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|
- rewriting(env[:ui]) do |ui|
- ui.clear_line
- ui.report_progress(progress, image_size, false)
+
+ package_func = method(:package_v1)
+
+ box_format = ENV.fetch('VAGRANT_LIBVIRT_BOX_FORMAT_VERSION', nil)
+
+ case box_format
+ when nil
+ if volumes.length() > 1
+ msg = "Detected more than one volume for machine, in the future this will switch to using the v2 "
+ msg += "box format v2 automatically."
+ msg += "\nIf you want to include the additional disks attached when packaging please set the "
+ msg += "env variable VAGRANT_LIBVIRT_BOX_FORMAT_VERSION=v2 to use the new format. If you want "
+ msg += "to ensure that your box uses the old format for single disk only, please set the "
+ msg += "environment variable explicitly to 'v1'"
+ env[:ui].warn(msg)
end
+ when 'v2'
+ package_func = method(:package_v2)
+ when 'v1'
+ else
+ env[:ui].warn("Unrecognized value for 'VAGRANT_LIBVIRT_BOX_FORMAT_VERSION', defaulting to v1")
end
- # Clear the line one last time since the progress meter doesn't
- # disappear immediately.
- rewriting(env[:ui]) {|ui| 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
- # remove hw association with interface
- # working for centos with lvs default disks
- options = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPTIONS', '')
- operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir,-customize')
- `virt-sysprep --no-logfile --operations #{operations} -a #{@tmp_img} #{options}`
- `virt-sparsify --in-place #{@tmp_img}`
- # add any user provided file
- extra = ''
- @tmp_include = @tmp_dir + '/_include'
- if env['package.include']
- extra = './_include'
- Dir.mkdir(@tmp_include)
- env['package.include'].each do |f|
- env[:ui].info("Including user file: #{f}")
- FileUtils.cp(f, @tmp_include)
- end
- end
- if env['package.vagrantfile']
- extra = './_include'
- Dir.mkdir(@tmp_include) unless File.directory?(@tmp_include)
- env[:ui].info('Including user Vagrantfile')
- FileUtils.cp(env['package.vagrantfile'], @tmp_include + '/Vagrantfile')
- end
- Dir.chdir(@tmp_dir)
- info = JSON.parse(`qemu-img info --output=json #{@tmp_img}`)
- img_size = (Float(info['virtual-size'])/(1024**3)).ceil
- File.write(@tmp_dir + '/metadata.json', metadata_content(img_size))
- File.write(@tmp_dir + '/Vagrantfile', vagrantfile_content)
- assemble_box(boxname, extra)
- FileUtils.mv(@tmp_dir + '/' + boxname, '../' + boxname)
- FileUtils.rm_rf(@tmp_dir)
- env[:ui].info('Box created')
- env[:ui].info('You can now add the box:')
- env[:ui].info("vagrant box add #{boxname} --name any_comfortable_name")
+
+ metadata = package_func.call(env, volumes)
+
+ # metadata / Vagrantfile
+ package_directory = env["package.directory"]
+ File.write(package_directory + '/metadata.json', metadata)
+ File.write(package_directory + '/Vagrantfile', vagrantfile_content(env))
+
@app.call(env)
end
- def assemble_box(boxname, extra)
- `tar cvzf "#{boxname}" --totals ./metadata.json ./Vagrantfile ./box.img #{extra}`
+ def package_v1(env, volumes)
+ domain_img = download_volume(env, volumes.first, 'box.img')
+
+ sysprep_domain(domain_img)
+ sparsify_volume(domain_img)
+
+ info = JSON.parse(`qemu-img info --output=json #{domain_img}`)
+ img_size = (Float(info['virtual-size'])/(1024**3)).ceil
+
+ return metadata_content_v1(img_size)
end
- def vagrantfile_content
- <<-EOF
+ def package_v2(env, volumes)
+ disks = []
+ volumes.each_with_index do |vol, idx|
+ disk = {:path => "box_#{idx+1}.img"}
+ volume_img = download_volume(env, vol, disk[:path])
+
+ if idx == 0
+ sysprep_domain(volume_img)
+ end
+
+ sparsify_volume(volume_img)
+
+ disks.push(disk)
+ end
+
+ return metadata_content_v2(disks)
+ end
+
+ def vagrantfile_content(env)
+ include_vagrantfile = ""
+
+ if env["package.vagrantfile"]
+ include_vagrantfile = <<-EOF
+
+ # Load include vagrant file if it exists after the auto-generated
+ # so it can override any of the settings
+ include_vagrantfile = File.expand_path("../include/_Vagrantfile", __FILE__)
+ load include_vagrantfile if File.exist?(include_vagrantfile)
+ EOF
+ end
+
+ <<-EOF.unindent
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.driver = "kvm"
- libvirt.host = ""
- libvirt.connect_via_ssh = false
- libvirt.storage_pool_name = "default"
end
+ #{include_vagrantfile}
end
-
- user_vagrantfile = File.expand_path('../_include/Vagrantfile', __FILE__)
- load user_vagrantfile if File.exists?(user_vagrantfile)
EOF
end
- def metadata_content(filesize)
- <<-EOF
+ def metadata_content_v1(filesize)
+ <<-EOF.unindent
{
"provider": "libvirt",
"format": "qcow2",
"virtual_size": #{filesize}
}
EOF
end
+ def metadata_content_v2(disks)
+ data = {
+ "provider": "libvirt",
+ "format": "qcow2",
+ "disks": disks.each do |disk|
+ {'path': disk[:path]}
+ end
+ }
+ JSON.pretty_generate(data)
+ end
+
protected
+
+ def sparsify_volume(volume_img)
+ `virt-sparsify --in-place #{volume_img}`
+ end
+
+ def sysprep_domain(domain_img)
+ # remove hw association with interface
+ # working for centos with lvs default disks
+ `virt-sysprep --no-logfile --operations #{@operations} -a #{domain_img} #{@options}`
+ end
+
+ def download_volume(env, volume, disk_path)
+ package_directory = env["package.directory"]
+ volume_img = package_directory + '/' + disk_path
+ env[:ui].info("Downloading #{volume.name} to #{volume_img}")
+ download_image(volume_img, env[:machine].provider_config.storage_pool_name,
+ volume.name, env) do |progress,image_size|
+ rewriting(env[:ui]) do |ui|
+ ui.clear_line
+ ui.report_progress(progress, image_size, false)
+ end
+ end
+ # Clear the line one last time since the progress meter doesn't
+ # disappear immediately.
+ rewriting(env[:ui]) {|ui| ui.clear_line}
+
+ # Prep domain disk
+ backing = `qemu-img info "#{volume_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 "" #{volume_img}`
+ end
+
+ return volume_img
+ end
# 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