Allow to use many disks in vagrant box for libvirt provider

Adds support for a new multi disk box format and handling to upload the
multiple disks to the storage pool.

New format is:
{
  'disks': [
    {
      'name': 'disk1.img',
      'virtual_size': 10,
      'format': 'qcow2'
    },
    {
      'name': 'disk2.img',
      'virtual_size': 15,
      'format': 'qcow2'
    },
    {
      'name': 'disk3.img',
    }
  ],
  'provider': 'libvirt',
  'format': 'qcow2'
}

It is expected to remove format from being set at the top level when
using the new format, with the assuming that qcow2 should be the default
format, and other formats should be permitted to be specified as needed.

Includes tests for handling the box images and creation of domain
volumes. Additionally includes an integration test to ensure a box with
2 disks will work as expected.

Partially fixes: #602
This commit is contained in:
Richard Turc
2020-09-10 10:03:00 +02:00
committed by Darragh Bailey
parent 6f608c54bf
commit 225237b125
19 changed files with 802 additions and 144 deletions

View File

@@ -18,6 +18,7 @@ jobs:
- simple vm provision via shell - simple vm provision via shell
- bring up with custom default prefix - bring up with custom default prefix
- bring up with second disk - bring up with second disk
- bring up with two disks
- bring up with adjusted memory settings - bring up with adjusted memory settings
- bring up with adjusted cpu settings - bring up with adjusted cpu settings
- ip is reachable with private network - ip is reachable with private network

View File

@@ -92,6 +92,7 @@ module VagrantPlugins
# Storage # Storage
@storage_pool_name = config.storage_pool_name @storage_pool_name = config.storage_pool_name
@snapshot_pool_name = config.snapshot_pool_name @snapshot_pool_name = config.snapshot_pool_name
@domain_volumes = []
@disks = config.disks @disks = config.disks
@cdroms = config.cdroms @cdroms = config.cdroms
@@ -141,19 +142,28 @@ module VagrantPlugins
else else
pool_name = @storage_pool_name pool_name = @storage_pool_name
end end
@logger.debug "Search for volume in pool: #{pool_name}" @logger.debug "Search for volumes in pool: #{pool_name}"
env[:box_volumes].each_index do |index|
suffix_index = index > 0 ? "_#{index}" : ''
domain_volume = env[:machine].provider.driver.connection.volumes.all( domain_volume = env[:machine].provider.driver.connection.volumes.all(
name: "#{@name}.img" name: "#{@name}#{suffix_index}.img"
).find { |x| x.pool_name == pool_name } ).find { |x| x.pool_name == pool_name }
raise Errors::DomainVolumeExists if domain_volume.nil? raise Errors::DomainVolumeExists if domain_volume.nil?
@domain_volume_path = domain_volume.path @domain_volumes.push({
:dev => (index+1).vdev.to_s,
:cache => @domain_volume_cache,
:bus => @disk_bus,
:path => domain_volume.path,
:virtual_size => env[:box_volumes][index][:virtual_size]
})
end
end end
# If we have a box, take the path from the domain volume and set our storage_prefix. # If we have a box, take the path from the domain volume and set our storage_prefix.
# If not, we dump the storage pool xml to get its defined path. # If not, we dump the storage pool xml to get its defined path.
# the default storage prefix is typically: /var/lib/libvirt/images/ # the default storage prefix is typically: /var/lib/libvirt/images/
if env[:machine].config.vm.box if env[:machine].config.vm.box
storage_prefix = File.dirname(@domain_volume_path) + '/' # steal storage_prefix = File.dirname(@domain_volumes[0][:path]) + '/' # steal
else else
storage_prefix = get_disk_storage_prefix(env, @storage_pool_name) storage_prefix = get_disk_storage_prefix(env, @storage_pool_name)
end end
@@ -250,7 +260,9 @@ module VagrantPlugins
env[:ui].info(" -- Base box: #{env[:machine].box.name}") env[:ui].info(" -- Base box: #{env[:machine].box.name}")
end end
env[:ui].info(" -- Storage pool: #{@storage_pool_name}") env[:ui].info(" -- Storage pool: #{@storage_pool_name}")
env[:ui].info(" -- Image: #{@domain_volume_path} (#{env[:box_virtual_size]}G)") @domain_volumes.each do |volume|
env[:ui].info(" -- Image(#{volume[:device]}): #{volume[:path]}, #{volume[:virtual_size]}G")
end
if not @disk_driver_opts.empty? if not @disk_driver_opts.empty?
env[:ui].info(" -- Disk driver opts: #{@disk_driver_opts.reject { |k,v| v.nil? }.map { |k,v| "#{k}='#{v}'"}.join(' ')}") env[:ui].info(" -- Disk driver opts: #{@disk_driver_opts.reject { |k,v| v.nil? }.map { |k,v| "#{k}='#{v}'"}.join(' ')}")

View File

@@ -18,11 +18,13 @@ module VagrantPlugins
def call(env) def call(env)
env[:ui].info(I18n.t('vagrant_libvirt.creating_domain_volume')) env[:ui].info(I18n.t('vagrant_libvirt.creating_domain_volume'))
env[:box_volumes].each_index do |index|
suffix_index = index > 0 ? "_#{index}" : ''
# Get config options. # Get config options.
config = env[:machine].provider_config config = env[:machine].provider_config
# This is name of newly created image for vm. # This is name of newly created image for vm.
@name = "#{env[:domain_name]}.img" @name = "#{env[:domain_name]}#{suffix_index}.img"
# Verify the volume doesn't exist already. # Verify the volume doesn't exist already.
domain_volume = env[:machine].provider.driver.connection.volumes.all( domain_volume = env[:machine].provider.driver.connection.volumes.all(
@@ -32,12 +34,12 @@ module VagrantPlugins
# Get path to backing image - box volume. # Get path to backing image - box volume.
box_volume = env[:machine].provider.driver.connection.volumes.all( box_volume = env[:machine].provider.driver.connection.volumes.all(
name: env[:box_volume_name] name: env[:box_volumes][index][:name]
).first ).first
@backing_file = box_volume.path @backing_file = box_volume.path
# Virtual size of image. Take value worked out by HandleBoxImage # Virtual size of image. Take value worked out by HandleBoxImage
@capacity = env[:box_virtual_size] # G @capacity = env[:box_volumes][index][:virtual_size] # G
# Create new volume from xml template. Fog currently doesn't support # Create new volume from xml template. Fog currently doesn't support
# volume snapshots directly. # volume snapshots directly.
@@ -83,7 +85,7 @@ module VagrantPlugins
raise Errors::FogDomainVolumeCreateError, raise Errors::FogDomainVolumeCreateError,
error_message: e.message error_message: e.message
end end
end
@app.call(env) @app.call(env)
end end
end end

View File

@@ -18,31 +18,46 @@ module VagrantPlugins
def call(env) def call(env)
# Verify box metadata for mandatory values. # Verify box metadata for mandatory values.
# #
# Virtual size has to be set for allocating space in storage pool. # Verify disk number
box_virtual_size = env[:machine].box.metadata['virtual_size'] disks = env[:machine].box.metadata.fetch('disks', [])
raise Errors::NoBoxVirtualSizeSet if box_virtual_size.nil? if disks.empty?
disks.push({
'path' => HandleBoxImage.get_default_box_image_path(0),
'name' => HandleBoxImage.get_volume_name(env, 0),
'virtual_size' => HandleBoxImage.get_virtual_size(env),
})
end
HandleBoxImage.verify_virtual_size_in_disks(disks)
# Support qcow2 format only for now, but other formats with backing # Support qcow2 format only for now, but other formats with backing
# store capability should be usable. # store capability should be usable.
box_format = env[:machine].box.metadata['format'] box_format = env[:machine].box.metadata['format']
if box_format.nil? HandleBoxImage.verify_box_format(box_format)
raise Errors::NoBoxFormatSet
elsif box_format != 'qcow2' env[:box_volume_number] = disks.length()
raise Errors::WrongBoxFormatSet env[:box_volumes] = Array.new(env[:box_volume_number]) {|i| {
end :path => HandleBoxImage.get_box_image_path(
env,
disks[i].fetch('path', HandleBoxImage.get_default_box_image_path(i))
),
:name => disks[i].fetch('name', HandleBoxImage.get_volume_name(env, i)),
:virtual_size => disks[i]['virtual_size'],
:format => HandleBoxImage.verify_box_format(
disks[i].fetch('format', box_format),
i
)
}
}
# Get config options # Get config options
config = env[:machine].provider_config config = env[:machine].provider_config
box_image_file = env[:machine].box.directory.join('box.img').to_s box_image_files = []
env[:box_volume_name] = env[:machine].box.name.to_s.dup.gsub('/', '-VAGRANTSLASH-') env[:box_volumes].each do |d|
env[:box_volume_name] << "_vagrant_box_image_#{ box_image_files.push(d[:path])
begin end
env[:machine].box.version.to_s
rescue
''
end}.img"
# Override box_virtual_size # Override box_virtual_size
box_virtual_size = env[:box_volumes][0][:virtual_size]
if config.machine_virtual_size if config.machine_virtual_size
if config.machine_virtual_size < box_virtual_size if config.machine_virtual_size < box_virtual_size
# Warn that a virtual size less than the box metadata size # Warn that a virtual size less than the box metadata size
@@ -57,18 +72,75 @@ module VagrantPlugins
end end
end end
# save for use by later actions # save for use by later actions
env[:box_virtual_size] = box_virtual_size env[:box_volumes][0][:virtual_size] = box_virtual_size
# while inside the synchronize block take care not to call the next # while inside the synchronize block take care not to call the next
# action in the chain, as must exit this block first to prevent # action in the chain, as must exit this block first to prevent
# locking all subsequent actions as well. # locking all subsequent actions as well.
@@lock.synchronize do @@lock.synchronize do
env[:box_volumes].each_index do |i|
# Don't continue if image already exists in storage pool. # Don't continue if image already exists in storage pool.
box_volume = env[:machine].provider.driver.connection.volumes.all( box_volume = env[:machine].provider.driver.connection.volumes.all(
name: env[:box_volume_name] name: env[:box_volumes][i][:name]
).first ).first
break if box_volume && box_volume.id next if box_volume && box_volume.id
send_box_image(env, config, box_image_files[i], env[:box_volumes][i])
end
end
@app.call(env)
end
protected
def self.get_volume_name(env, index)
name = env[:machine].box.name.to_s.dup.gsub('/', '-VAGRANTSLASH-')
name << "_vagrant_box_image_#{
begin
env[:machine].box.version.to_s
rescue
''
end}_#{index}.img"
return name
end
def self.get_virtual_size(env)
# 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?
return box_virtual_size
end
def self.get_default_box_image_path(index)
return index <= 0 ? 'box.img' : "box_#{index}.img"
end
def self.get_box_image_path(env, box_name)
return env[:machine].box.directory.join(box_name).to_s
end
def self.verify_box_format(box_format, disk_index=nil)
if box_format.nil?
raise Errors::NoBoxFormatSet
elsif box_format != 'qcow2'
if disk_index.nil?
raise Errors::WrongBoxFormatSet
else
raise Errors::WrongDiskFormatSet,
disk_index: disk_index
end
end
return box_format
end
def self.verify_virtual_size_in_disks(disks)
disks.each_with_index do |disk, index|
raise Errors::NoDiskVirtualSizeSet, disk_index:index if disk['virtual_size'].nil?
end
end
def send_box_image(env, config, box_image_file, box_volume)
# Box is not available as a storage pool volume. Create and upload # Box is not available as a storage pool volume. Create and upload
# it as a copy of local box image. # it as a copy of local box image.
env[:ui].info(I18n.t('vagrant_libvirt.uploading_volume')) env[:ui].info(I18n.t('vagrant_libvirt.uploading_volume'))
@@ -78,21 +150,18 @@ module VagrantPlugins
raise Vagrant::Errors::BoxNotFound, name: env[:machine].box.name raise Vagrant::Errors::BoxNotFound, name: env[:machine].box.name
end end
box_image_size = File.size(box_image_file) # B box_image_size = File.size(box_image_file) # B
message = "Creating volume #{env[:box_volume_name]}" message = "Creating volume #{box_volume[:name]}"
message << " in storage pool #{config.storage_pool_name}." message << " in storage pool #{config.storage_pool_name}."
@logger.info(message) @logger.info(message)
@storage_volume_uid = storage_uid env
@storage_volume_gid = storage_gid env
begin begin
fog_volume = env[:machine].provider.driver.connection.volumes.create( fog_volume = env[:machine].provider.driver.connection.volumes.create(
name: env[:box_volume_name], name: box_volume[:name],
allocation: "#{box_image_size / 1024 / 1024}M", allocation: "#{box_image_size / 1024 / 1024}M",
capacity: "#{box_virtual_size}G", capacity: "#{box_volume[:virtual_size]}G",
format_type: box_format, format_type: box_volume[:format],
owner: @storage_volume_uid, owner: storage_uid(env),
group: @storage_volume_gid, group: storage_gid(env),
pool_name: config.storage_pool_name pool_name: config.storage_pool_name
) )
rescue Fog::Errors::Error => e rescue Fog::Errors::Error => e
@@ -102,7 +171,7 @@ module VagrantPlugins
# Upload box image to storage pool # Upload box image to storage pool
ret = upload_image(box_image_file, config.storage_pool_name, ret = upload_image(box_image_file, config.storage_pool_name,
env[:box_volume_name], env) do |progress| box_volume[:name], env) do |progress|
rewriting(env[:ui]) do |ui| rewriting(env[:ui]) do |ui|
ui.clear_line ui.clear_line
ui.report_progress(progress, box_image_size, false) ui.report_progress(progress, box_image_size, false)
@@ -124,11 +193,6 @@ module VagrantPlugins
end end
end end
@app.call(env)
end
protected
# Fog Libvirt currently doesn't support uploading images to storage # Fog Libvirt currently doesn't support uploading images to storage
# pool volumes. Use ruby-libvirt client instead. # pool volumes. Use ruby-libvirt client instead.
def upload_image(image_file, pool_name, volume_name, env) def upload_image(image_file, pool_name, volume_name, env)

View File

@@ -46,6 +46,10 @@ module VagrantPlugins
error_key(:no_box_virtual_size) error_key(:no_box_virtual_size)
end end
class NoDiskVirtualSizeSet < VagrantLibvirtError
error_key(:no_disk_virtual_size)
end
class NoBoxFormatSet < VagrantLibvirtError class NoBoxFormatSet < VagrantLibvirtError
error_key(:no_box_format) error_key(:no_box_format)
end end
@@ -54,6 +58,10 @@ module VagrantPlugins
error_key(:wrong_box_format) error_key(:wrong_box_format)
end end
class WrongDiskFormatSet < VagrantLibvirtError
error_key(:wrong_disk_format)
end
# Fog Libvirt exceptions # Fog Libvirt exceptions
class FogError < VagrantLibvirtError class FogError < VagrantLibvirtError
error_key(:fog_error) error_key(:fog_error)

View File

@@ -113,18 +113,18 @@
<% if @emulator_path %> <% if @emulator_path %>
<emulator><%= @emulator_path %></emulator> <emulator><%= @emulator_path %></emulator>
<% end %> <% end %>
<% if @domain_volume_path %> <% @domain_volumes.each do |volume| -%>
<disk type='file' device='disk'> <disk type='file' device='disk'>
<driver name='qemu' type='qcow2' <%= <driver name='qemu' type='qcow2' <%=
@disk_driver_opts.empty? ? "cache='#{@domain_volume_cache}'" : @disk_driver_opts.empty? ? "cache='#{volume[:cache]}'" :
@disk_driver_opts.reject { |k,v| v.nil? } @disk_driver_opts.reject { |k,v| v.nil? }
.map { |k,v| "#{k}='#{v}'"} .map { |k,v| "#{k}='#{v}'"}
.join(' ') -%>/> .join(' ') -%>/>
<source file='<%= @domain_volume_path %>'/> <source file='<%= volume[:path] %>'/>
<%# we need to ensure a unique target dev -%> <%# we need to ensure a unique target dev -%>
<target dev='<%= @disk_device %>' bus='<%= @disk_bus %>'/> <target dev='<%= volume[:dev] %>' bus='<%= volume[:bus] %>'/>
</disk> </disk>
<% end %> <% end -%>
<%# additional disks -%> <%# additional disks -%>
<% @disks.each do |d| -%> <% @disks.each do |d| -%>
<disk type='file' device='disk'> <disk type='file' device='disk'>

View File

@@ -92,10 +92,14 @@ en:
Error: %{stderr} Error: %{stderr}
no_box_virtual_size: |- no_box_virtual_size: |-
No image virtual size specified for box. No image virtual size specified for box.
no_disk_virtual_size: |-
No image virtual size specified for disk with index %{disk_index}.
no_box_format: |- no_box_format: |-
No image format specified for box. No image format specified for box.
wrong_box_format: |- wrong_box_format: |-
Wrong image format specified for box. Wrong image format specified for box.
wrong_disk_format: |-
Wrong image format specified for disk with index %{disk_index}.
fog_libvirt_connection_error: |- fog_libvirt_connection_error: |-
Error while connecting to Libvirt: %{error_message} Error while connecting to Libvirt: %{error_message}
fog_create_volume_error: |- fog_create_volume_error: |-

View File

@@ -31,6 +31,12 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do
env[:domain_name] = "vagrant-test_default" env[:domain_name] = "vagrant-test_default"
env[:box_volumes] = []
env[:box_volumes].push({
:path=>"/test/box.img",
:name=>"test_vagrant_box_image_1.1.1_0.img",
:virtual_size=>5
})
# should be ignored for system session and used for user session # should be ignored for system session and used for user session
allow(Process).to receive(:uid).and_return(9999) allow(Process).to receive(:uid).and_return(9999)
allow(Process).to receive(:gid).and_return(9999) allow(Process).to receive(:gid).and_return(9999)

View File

@@ -0,0 +1,102 @@
require 'spec_helper'
require 'support/sharedcontext'
require 'support/libvirt_context'
require 'vagrant-libvirt/action/destroy_domain'
describe VagrantPlugins::ProviderLibvirt::Action::CreateDomainVolume do
subject { described_class.new(app, env) }
include_context 'unit'
include_context 'libvirt'
let(:libvirt_domain) { double('libvirt_domain') }
let(:libvirt_client) { double('libvirt_client') }
let(:volumes) { double('volumes') }
let(:all) { double('all') }
let(:box_volume) { double('box_volume') }
def read_test_file(name)
File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), name))
end
describe '#call' do
before do
allow_any_instance_of(VagrantPlugins::ProviderLibvirt::Driver)
.to receive(:connection).and_return(connection)
allow(connection).to receive(:client).and_return(libvirt_client)
allow(connection).to receive(:volumes).and_return(volumes)
allow(volumes).to receive(:all).and_return(all)
allow(all).to receive(:first).and_return(box_volume)
allow(box_volume).to receive(:id).and_return(nil)
env[:domain_name] = 'test'
end
context 'when one disk' do
before do
allow(box_volume).to receive(:path).and_return('/test/path_0.img')
env[:box_volumes] = [
{
:name=>"test_vagrant_box_image_1.1.1_0.img",
:virtual_size=>5
}
]
end
it 'should create one disk in storage' do
expected_xml = read_test_file('one_disk_in_storage.xml')
expect(ui).to receive(:info).with('Creating image (snapshot of base box volume).')
expect(logger).to receive(:debug).with('Using pool default for base box snapshot')
expect(volumes).to receive(:create).with(
:xml => expected_xml,
:pool_name => "default"
)
expect(subject.call(env)).to be_nil
end
end
context 'when three disks' do
before do
allow(box_volume).to receive(:path).and_return(
'/test/path_0.img',
'/test/path_1.img',
'/test/path_2.img',
)
env[:box_volumes] = [
{
:name=>"test_vagrant_box_image_1.1.1_0.img",
:virtual_size=>5
},
{
:name=>"test_vagrant_box_image_1.1.1_1.img",
:virtual_size=>10
},
{
:name=>"test_vagrant_box_image_1.1.1_2.img",
:virtual_size=>20
}
]
end
it 'should create three disks in storage' do
expect(ui).to receive(:info).with('Creating image (snapshot of base box volume).')
expect(logger).to receive(:debug).with('Using pool default for base box snapshot')
expect(volumes).to receive(:create).with(
:xml => read_test_file('three_disks_in_storage_disk_0.xml'),
:pool_name => "default"
)
expect(logger).to receive(:debug).with('Using pool default for base box snapshot')
expect(volumes).to receive(:create).with(
:xml => read_test_file('three_disks_in_storage_disk_1.xml'),
:pool_name => "default"
)
expect(logger).to receive(:debug).with('Using pool default for base box snapshot')
expect(volumes).to receive(:create).with(
:xml => read_test_file('three_disks_in_storage_disk_2.xml'),
:pool_name => "default"
)
expect(subject.call(env)).to be_nil
end
end
end
end

View File

@@ -0,0 +1,21 @@
<volume>
<name>test.img</name>
<capacity unit="G">5</capacity>
<target>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</target>
<backingStore>
<path>/test/path_0.img</path>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</backingStore>
</volume>

View File

@@ -0,0 +1,21 @@
<volume>
<name>test.img</name>
<capacity unit="G">5</capacity>
<target>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</target>
<backingStore>
<path>/test/path_0.img</path>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</backingStore>
</volume>

View File

@@ -0,0 +1,21 @@
<volume>
<name>test_1.img</name>
<capacity unit="G">10</capacity>
<target>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</target>
<backingStore>
<path>/test/path_1.img</path>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</backingStore>
</volume>

View File

@@ -0,0 +1,21 @@
<volume>
<name>test_2.img</name>
<capacity unit="G">20</capacity>
<target>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</target>
<backingStore>
<path>/test/path_2.img</path>
<format type="qcow2"></format>
<permissions>
<owner>0</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</backingStore>
</volume>

View File

@@ -0,0 +1,296 @@
require 'spec_helper'
require 'support/sharedcontext'
require 'support/libvirt_context'
require 'vagrant-libvirt/action/destroy_domain'
describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do
subject { described_class.new(app, env) }
include_context 'unit'
include_context 'libvirt'
let(:libvirt_client) { double('libvirt_client') }
let(:volumes) { double('volumes') }
let(:all) { double('all') }
let(:box_volume) { double('box_volume') }
let(:fog_volume) { double('fog_volume') }
describe '#call' do
before do
allow_any_instance_of(VagrantPlugins::ProviderLibvirt::Driver)
.to receive(:connection).and_return(connection)
allow(connection).to receive(:client).and_return(libvirt_client)
allow(connection).to receive(:volumes).and_return(volumes)
allow(volumes).to receive(:all).and_return(all)
allow(env[:ui]).to receive(:clear_line)
end
context 'when one disk in metadata.json' do
before do
allow(all).to receive(:first).and_return(box_volume)
allow(box_volume).to receive(:id).and_return(1)
allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
allow(env[:machine]).to receive_message_chain("box.metadata") { Hash[
'virtual_size'=> 5,
'format' => 'qcow2'
]
}
allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
'/test/'.concat(arg.to_s)
end
end
it 'should have one disk in machine env' do
expect(subject.call(env)).to be_nil
expect(env[:box_volume_number]).to eq(1)
expect(env[:box_volumes]).to eq(
[
{
:path=>"/test/box.img",
:name=>"test_vagrant_box_image_1.1.1_0.img",
:virtual_size=>5,
:format=>"qcow2"
}
]
)
end
context 'when disk image not in storage pool' do
before do
allow(File).to receive(:exist?).and_return(true)
allow(File).to receive(:size).and_return(5*1024*1024*1024)
allow(all).to receive(:first).and_return(nil)
allow(subject).to receive(:upload_image).and_return(true)
allow(volumes).to receive(:create).and_return(fog_volume)
end
it 'should upload disk' do
expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_0.img in storage pool default.')
expect(volumes).to receive(:create).with(
hash_including(
:name => "test_vagrant_box_image_1.1.1_0.img",
:allocation => "5120M",
:capacity => "5G",
)
)
expect(subject).to receive(:upload_image)
expect(subject.call(env)).to be_nil
end
end
context 'when disk image already in storage pool' do
before do
allow(all).to receive(:first).and_return(box_volume)
allow(box_volume).to receive(:id).and_return(1)
end
it 'should skip disk upload' do
expect(volumes).not_to receive(:create)
expect(subject).not_to receive(:upload_image)
expect(subject.call(env)).to be_nil
end
end
end
context 'when three disks in metadata.json' do
before do
allow(all).to receive(:first).and_return(box_volume)
allow(box_volume).to receive(:id).and_return(1)
allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
allow(env[:machine]).to receive_message_chain("box.metadata") { Hash[
'disks' => [
{
'name'=>'send_box_name.img',
'virtual_size'=> 5,
},
{
'path' => 'disk.qcow2',
'virtual_size'=> 10
},
{
'virtual_size'=> 20,
},
],
'format' => 'qcow2'
]
}
allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
'/test/'.concat(arg.to_s)
end
end
it 'should have three disks in machine env' do
expect(subject.call(env)).to be_nil
expect(env[:box_volume_number]).to eq(3)
expect(env[:box_volumes]).to eq(
[
{
:path=>"/test/box.img",
:name=>"send_box_name.img",
:virtual_size=>5,
:format=>"qcow2"
},
{
:path=>"/test/disk.qcow2",
:name=>"test_vagrant_box_image_1.1.1_1.img",
:virtual_size=>10,
:format=>"qcow2"
},
{
:path=>"/test/box_2.img",
:name=>"test_vagrant_box_image_1.1.1_2.img",
:virtual_size=>20,
:format=>"qcow2"
}
]
)
end
context 'when none of the disks in storage pool' do
before do
allow(File).to receive(:exist?).and_return(true)
allow(File).to receive(:size).and_return(5*1024*1024*1024, 10*1024*1024*1024, 20*1024*1024*1024)
allow(all).to receive(:first).and_return(nil)
allow(subject).to receive(:upload_image).and_return(true)
allow(volumes).to receive(:create).and_return(fog_volume)
end
it 'should upload all 3 disks' do
expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
expect(logger).to receive(:info).with('Creating volume send_box_name.img in storage pool default.')
expect(volumes).to receive(:create).with(
hash_including(
:name => "send_box_name.img",
:allocation => "5120M",
:capacity => "5G",
)
)
expect(subject).to receive(:upload_image)
expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_1.img in storage pool default.')
expect(volumes).to receive(:create).with(
hash_including(
:name => "test_vagrant_box_image_1.1.1_1.img",
:allocation => "10240M",
:capacity => "10G",
)
)
expect(subject).to receive(:upload_image)
expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_2.img in storage pool default.')
expect(volumes).to receive(:create).with(
hash_including(
:name => "test_vagrant_box_image_1.1.1_2.img",
:allocation => "20480M",
:capacity => "20G",
)
)
expect(subject).to receive(:upload_image)
expect(subject.call(env)).to be_nil
end
end
context 'when only disk 0 in storage pool' do
before do
allow(File).to receive(:exist?).and_return(true)
allow(File).to receive(:size).and_return(10*1024*1024*1024, 20*1024*1024*1024)
allow(all).to receive(:first).and_return(box_volume, nil, nil)
allow(box_volume).to receive(:id).and_return(1)
allow(subject).to receive(:upload_image).and_return(true)
allow(volumes).to receive(:create).and_return(fog_volume)
end
it 'upload disks 1 and 2 only' do
expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_1.img in storage pool default.')
expect(volumes).to receive(:create).with(hash_including(:name => "test_vagrant_box_image_1.1.1_1.img"))
expect(subject).to receive(:upload_image)
expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_2.img in storage pool default.')
expect(volumes).to receive(:create).with(hash_including(:name => "test_vagrant_box_image_1.1.1_2.img"))
expect(subject).to receive(:upload_image)
expect(subject.call(env)).to be_nil
end
end
context 'when has all disks on storage pool' do
before do
allow(all).to receive(:first).and_return(box_volume)
allow(box_volume).to receive(:id).and_return(1)
end
it 'should skip disk upload' do
expect(ui).not_to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
expect(volumes).not_to receive(:create)
expect(subject).not_to receive(:upload_image)
expect(subject.call(env)).to be_nil
end
end
end
context 'when wrong box format in metadata.json' do
before do
allow(all).to receive(:first).and_return(box_volume)
allow(box_volume).to receive(:id).and_return(1)
allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
allow(env[:machine]).to receive_message_chain("box.metadata") { Hash[
'virtual_size'=> 5,
'format' => 'wrongFormat'
]
}
allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
'/test/'.concat(arg.to_s)
end
end
it 'should raise WrongBoxFormatSet exception' do
expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::WrongBoxFormatSet)
end
end
context 'when one of a multi disk definition has wrong disk format in metadata.json' do
before do
allow(all).to receive(:first).and_return(box_volume)
allow(box_volume).to receive(:id).and_return(1)
allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
allow(env[:machine]).to receive_message_chain("box.metadata") {
Hash[
'disks' => [
{
'name'=>'send_box_name.img',
'virtual_size'=> 5,
'format'=> 'wrongFormat'
},
{
'path' => 'disk.qcow2',
'virtual_size'=> 10
},
{
'virtual_size'=> 20,
},
],
'format' => 'qcow2',
]
}
allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
'/test/'.concat(arg.to_s)
end
end
it 'should raise WrongDiskFormatSet exception' do
expect { subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::WrongDiskFormatSet)
end
end
end
end

View File

@@ -47,6 +47,11 @@
<source file='/var/lib/libvirt/images/test.qcow2'/> <source file='/var/lib/libvirt/images/test.qcow2'/>
<target dev='vda' bus='ide'/> <target dev='vda' bus='ide'/>
</disk> </disk>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='unsafe' io='threads' copy_on_read='on' discard='unmap' detect_zeroes='on'/>
<source file='/var/lib/libvirt/images/test2.qcow2'/>
<target dev='vdb' bus='ide'/>
</disk>
<disk type='file' device='disk'> <disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='default'/> <driver name='qemu' type='qcow2' cache='default'/>
<source file='/var/lib/libvirt/images/test-disk1.qcow2'/> <source file='/var/lib/libvirt/images/test-disk1.qcow2'/>

View File

@@ -9,6 +9,13 @@ describe 'templates/domain' do
class DomainTemplateHelper < VagrantPlugins::ProviderLibvirt::Config class DomainTemplateHelper < VagrantPlugins::ProviderLibvirt::Config
include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
attr_accessor :domain_volumes
def initialize
super
@domain_volumes = []
end
def finalize! def finalize!
super super
end end
@@ -44,11 +51,22 @@ describe 'templates/domain' do
domain.boot('cdrom') domain.boot('cdrom')
domain.boot('hd') domain.boot('hd')
domain.emulator_path = '/usr/bin/kvm-spice' domain.emulator_path = '/usr/bin/kvm-spice'
domain.instance_variable_set('@domain_volume_path', '/var/lib/libvirt/images/test.qcow2')
domain.instance_variable_set('@domain_volume_cache', 'deprecated') domain.instance_variable_set('@domain_volume_cache', 'deprecated')
domain.disk_bus = 'ide' domain.disk_bus = 'ide'
domain.disk_device = 'vda' domain.disk_device = 'vda'
domain.disk_driver(:cache => 'unsafe', :io => 'threads', :copy_on_read => 'on', :discard => 'unmap', :detect_zeroes => 'on') domain.disk_driver(:cache => 'unsafe', :io => 'threads', :copy_on_read => 'on', :discard => 'unmap', :detect_zeroes => 'on')
domain.domain_volumes.push({
:dev => 1.vdev.to_s,
:cache => 'unsafe',
:bus => domain.disk_bus,
:path => '/var/lib/libvirt/images/test.qcow2'
})
domain.domain_volumes.push({
:dev => 2.vdev.to_s,
:cache => 'unsafe',
:bus => domain.disk_bus,
:path => '/var/lib/libvirt/images/test2.qcow2'
})
domain.storage(:file, path: 'test-disk1.qcow2') domain.storage(:file, path: 'test-disk1.qcow2')
domain.storage(:file, path: 'test-disk2.qcow2', io: 'threads', copy_on_read: 'on', discard: 'unmap', detect_zeroes: 'on') domain.storage(:file, path: 'test-disk2.qcow2', io: 'threads', copy_on_read: 'on', discard: 'unmap', detect_zeroes: 'on')
domain.disks.each do |disk| domain.disks.each do |disk|

View File

@@ -6,6 +6,7 @@ VAGRANT_OPT="--provider=libvirt"
TEMPDIR= TEMPDIR=
setup_file() { setup_file() {
# set VAGRANT_HOME to something else to reuse for tests to avoid clashes with # set VAGRANT_HOME to something else to reuse for tests to avoid clashes with
# user installed plugins when running tests locally. # user installed plugins when running tests locally.
@@ -77,6 +78,19 @@ cleanup() {
cleanup cleanup
} }
@test "bring up with two disks" {
export VAGRANT_CWD=tests/two_disks
cleanup
run ${VAGRANT_CMD} up ${VAGRANT_OPT}
echo "${output}"
echo "status = ${status}"
[ "$status" -eq 0 ]
echo "${output}"
[ $(expr "$output" : ".*Image.*2G") -ne 0 ]
[ $(expr "$output" : ".*Image.*10G") -ne 0 ]
cleanup
}
@test "bring up with adjusted memory settings" { @test "bring up with adjusted memory settings" {
export VAGRANT_CWD=tests/memory export VAGRANT_CWD=tests/memory
cleanup cleanup

10
tests/two_disks/Vagrantfile vendored Normal file
View File

@@ -0,0 +1,10 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "yamatoRT/tinycore-two-disks"
config.vm.box_version = "0.0.2"
config.ssh.shell = "/bin/sh"
config.vm.synced_folder ".", "/vagrant", disabled: true
end

View File

@@ -0,0 +1,32 @@
VAGRANT_HOME=${1:-$HOME/.vagrant.d/}
VAGRANT_CMD=${2:-vagrant}
echo 'Create box with two disks'
${VAGRANT_CMD} box list
if [ "$(${VAGRANT_CMD} box list | grep -c -E '^infernix/tinycore-two-disks\s')" -eq 0 ]
then
if [ "$(${VAGRANT_CMD} box list | grep -c -E '^infernix/tinycore\s')" -eq 0 ]
then
${VAGRANT_CMD} box add infernix/tinycore
fi
NEW_PATH="${VAGRANT_HOME}/boxes/infernix-VAGRANTSLASH-tinycore-two-disks"
cp -r "${VAGRANT_HOME}/boxes/infernix-VAGRANTSLASH-tinycore" "${NEW_PATH}"
BOX_VERSION="$(${VAGRANT_CMD} box list --machine-readable | grep -A 10 infernix/tinycore-two-disks | grep box-version | cut -d, -f4)"
qemu-img create -f qcow2 "${NEW_PATH}/${BOX_VERSION}/libvirt/disk2.qcow2" 10G
cat > "${NEW_PATH}/${BOX_VERSION}/libvirt/metadata.json" <<EOF
{
"provider": "libvirt",
"format": "qcow2",
"disks" : [
{
"virtual_size": 2
},
{
"path":"disk2.qcow2",
"virtual_size":10
}
]
}
EOF
fi