From 37597e22f937a7ecdb4400d23f418186e4dee0bc Mon Sep 17 00:00:00 2001 From: Darragh Bailey Date: Thu, 2 Jun 2022 19:09:18 +0100 Subject: [PATCH] Add action to resolve disk settings (#1502) With multi volume boxes, need to ensure that disk settings such as the device assigned are resolved dynamically once it has been established which devices have already been assigned to the box volumes on either initial creation or subsequent boots. Otherwise users are forced to always explicitly define the device for additional storage instead of having it be automatically assigned the next available device. Consequently previous changes have broken the ability for machines with additional storage to be halted and restarted correctly. Include an integration test that for additional storage checks that the machine can be stopped and started again. Fixes: #1490 --- lib/vagrant-libvirt/action.rb | 4 + lib/vagrant-libvirt/action/create_domain.rb | 110 +----- lib/vagrant-libvirt/action/destroy_domain.rb | 2 +- .../action/handle_box_image.rb | 2 + .../action/resolve_disk_settings.rb | 174 +++++++++ lib/vagrant-libvirt/templates/domain.xml.erb | 4 +- locales/en.yml | 6 +- spec/unit/action/create_domain_spec.rb | 268 ++++++------- spec/unit/action/destroy_domain_spec.rb | 4 +- spec/unit/action/handle_box_image_spec.rb | 20 +- .../unit/action/resolve_disk_settings_spec.rb | 361 ++++++++++++++++++ .../default_domain.xml | 43 +++ .../default_no_aliases.xml | 42 ++ .../default_system_storage_pool.xml | 0 .../multi_volume_box.xml | 55 +++ ...e_box_additional_and_custom_no_aliases.xml | 67 ++++ .../multi_volume_box_additional_storage.xml | 67 ++++ spec/unit/templates/domain_spec.rb | 4 +- tests/runtests.bats | 8 + tests/second_disk/Vagrantfile | 1 + 20 files changed, 986 insertions(+), 256 deletions(-) create mode 100644 lib/vagrant-libvirt/action/resolve_disk_settings.rb create mode 100644 spec/unit/action/resolve_disk_settings_spec.rb create mode 100644 spec/unit/action/resolve_disk_settings_spec/default_domain.xml create mode 100644 spec/unit/action/resolve_disk_settings_spec/default_no_aliases.xml rename spec/unit/action/{create_domain_spec => resolve_disk_settings_spec}/default_system_storage_pool.xml (100%) create mode 100644 spec/unit/action/resolve_disk_settings_spec/multi_volume_box.xml create mode 100644 spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_and_custom_no_aliases.xml create mode 100644 spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_storage.xml diff --git a/lib/vagrant-libvirt/action.rb b/lib/vagrant-libvirt/action.rb index 8c90c8f..64bfb8f 100644 --- a/lib/vagrant-libvirt/action.rb +++ b/lib/vagrant-libvirt/action.rb @@ -35,6 +35,7 @@ module VagrantPlugins autoload :ReadMacAddresses, action_root.join('read_mac_addresses') autoload :RemoveLibvirtImage, action_root.join('remove_libvirt_image') autoload :RemoveStaleVolume, action_root.join('remove_stale_volume') + autoload :ResolveDiskSettings, action_root.join('resolve_disk_settings') autoload :ResumeDomain, action_root.join('resume_domain') autoload :SetNameOfDomain, action_root.join('set_name_of_domain') autoload :SetBootOrder, action_root.join('set_boot_order') @@ -81,6 +82,7 @@ module VagrantPlugins b2.use SetNameOfDomain if !env[:machine].config.vm.box + b2.use ResolveDiskSettings b2.use CreateDomain b2.use CreateNetworks b2.use CreateNetworkInterfaces @@ -94,6 +96,7 @@ module VagrantPlugins b2.use HandleBox b2.use HandleBoxImage b2.use CreateDomainVolume + b2.use ResolveDiskSettings b2.use CreateDomain b2.use CreateNetworks b2.use CreateNetworkInterfaces @@ -104,6 +107,7 @@ module VagrantPlugins end else env[:halt_on_error] = true + b2.use ResolveDiskSettings b2.use CreateNetworks b2.use action_start end diff --git a/lib/vagrant-libvirt/action/create_domain.rb b/lib/vagrant-libvirt/action/create_domain.rb index 48ce8e5..7899424 100644 --- a/lib/vagrant-libvirt/action/create_domain.rb +++ b/lib/vagrant-libvirt/action/create_domain.rb @@ -16,20 +16,6 @@ module VagrantPlugins @app = app end - def _disk_name(name, disk) - "#{name}-#{disk[:device]}.#{disk[:type]}" # disk name - end - - def _disks_print(disks) - disks.collect do |x| - "#{x[:device]}(#{x[:type]}, #{x[:bus]}, #{x[:size]})" - end.join(', ') - end - - def _cdroms_print(cdroms) - cdroms.collect { |x| x[:dev] }.join(', ') - end - def call(env) # Get config. config = env[:machine].provider_config @@ -58,8 +44,6 @@ module VagrantPlugins @nvram = config.nvram @machine_type = config.machine_type @machine_arch = config.machine_arch - @disk_bus = config.disk_bus - @disk_device = config.disk_device @disk_driver_opts = config.disk_driver_opts @nested = config.nested @memory_size = config.memory.to_i * 1024 @@ -94,9 +78,8 @@ module VagrantPlugins # Storage @storage_pool_name = config.storage_pool_name - @snapshot_pool_name = config.snapshot_pool_name - @domain_volumes = [] - @disks = config.disks + @domain_volumes = env[:domain_volumes] || [] + @disks = env[:disks] || [] @cdroms = config.cdroms # Input @@ -139,77 +122,19 @@ module VagrantPlugins @memballoon_pci_bus = config.memballoon_pci_bus @memballoon_pci_slot = config.memballoon_pci_slot - config = env[:machine].provider_config @domain_type = config.driver @os_type = 'hvm' - resolver = ::VagrantPlugins::ProviderLibvirt::Util::DiskDeviceResolver.new(prefix=@disk_device[0..1]) - - # Get path to domain image from the storage pool selected if we have a box. - if env[:machine].config.vm.box - if @snapshot_pool_name != @storage_pool_name - pool_name = @snapshot_pool_name - else - pool_name = @storage_pool_name - end - - # special handling for domain volume - env[:box_volumes][0][:device] = env[:box_volumes][0].fetch(:device, @disk_device) - - resolver.resolve!(env[:box_volumes]) - - @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( - name: "#{@name}#{suffix_index}.img" - ).find { |x| x.pool_name == pool_name } - raise Errors::DomainVolumeExists if domain_volume.nil? - - @domain_volumes.push({ - :dev => env[:box_volumes][index][:device], - :cache => @domain_volume_cache, - :bus => @disk_bus, - :path => domain_volume.path, - :virtual_size => env[:box_volumes][index][:virtual_size] - }) - end - + env[:domain_volumes].each_with_index do |vol, index| + suffix_index = index > 0 ? "_#{index}" : '' + domain_volume = env[:machine].provider.driver.connection.volumes.all( + name: "#{@name}#{suffix_index}.img" + ).find { |x| x.pool_name == vol[:pool] } + raise Errors::NoDomainVolume if domain_volume.nil? end - # 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. - # the default storage prefix is typically: /var/lib/libvirt/images/ - if env[:machine].config.vm.box - storage_prefix = File.dirname(@domain_volumes[0][:path]) + '/' # steal - else - storage_prefix = get_disk_storage_prefix(env, @storage_pool_name) - end - - resolver.resolve!(@disks) - @disks.each do |disk| - disk[:path] ||= _disk_name(@name, disk) - - # On volume creation, the element inside - # is oddly ignored; instead the path is taken from the - # element: - # http://www.redhat.com/archives/libvir-list/2008-August/msg00329.html - disk[:name] = disk[:path] - - disk[:absolute_path] = storage_prefix + disk[:path] - - if not disk[:pool].nil? - disk_pool_name = disk[:pool] - @logger.debug "Overriding pool name with: #{disk_pool_name}" - disk_storage_prefix = get_disk_storage_prefix(env, disk_pool_name) - disk[:absolute_path] = disk_storage_prefix + disk[:path] - @logger.debug "Overriding disk path with: #{disk[:absolute_path]}" - else - disk_pool_name = @storage_pool_name - end - # make the disk. equivalent to: # qemu-img create -f qcow2 5g begin @@ -221,13 +146,13 @@ module VagrantPlugins owner: storage_uid(env), group: storage_gid(env), #:allocation => ?, - pool_name: disk_pool_name + pool_name: disk[:pool], ) rescue Libvirt::Error => e # It is hard to believe that e contains just a string # and no useful error code! msg = "Call to virStorageVolCreateXML failed: " + - "storage volume '#{disk[:path]}' exists already" + "storage volume '#{disk[:absolute_path]}' exists already" if e.message == msg and disk[:allow_existing] disk[:preexisting] = true else @@ -300,7 +225,7 @@ module VagrantPlugins end env[:ui].info(" -- Storage pool: #{@storage_pool_name}") @domain_volumes.each do |volume| - env[:ui].info(" -- Image(#{volume[:dev]}): #{volume[:path]}, #{volume[:bus]}, #{volume[:virtual_size].to_GB}G") + env[:ui].info(" -- Image(#{volume[:device]}): #{volume[:absolute_path]}, #{volume[:bus]}, #{volume[:virtual_size].to_GB}G") end if not @disk_driver_opts.empty? @@ -466,11 +391,14 @@ module VagrantPlugins end private - def get_disk_storage_prefix(env, disk_pool_name) - disk_storage_pool = env[:machine].provider.driver.connection.client.lookup_storage_pool_by_name(disk_pool_name) - raise Errors::NoStoragePool if disk_storage_pool.nil? - xml = Nokogiri::XML(disk_storage_pool.xml_desc) - disk_storage_prefix = xml.xpath('/pool/target/path').inner_text.to_s + '/' + def _disks_print(disks) + disks.collect do |x| + "#{x[:device]}(#{x[:type]}, #{x[:bus]}, #{x[:size]})" + end.join(', ') + end + + def _cdroms_print(cdroms) + cdroms.collect { |x| x[:dev] }.join(', ') end end end diff --git a/lib/vagrant-libvirt/action/destroy_domain.rb b/lib/vagrant-libvirt/action/destroy_domain.rb index f462df8..3401d94 100644 --- a/lib/vagrant-libvirt/action/destroy_domain.rb +++ b/lib/vagrant-libvirt/action/destroy_domain.rb @@ -62,7 +62,7 @@ module VagrantPlugins disks_xml = REXML::XPath.match(xml_descr, '/domain/devices/disk[@device="disk"]') have_aliases = !(REXML::XPath.match(disks_xml, './alias[@name="ua-box-volume-0"]').first).nil? if !have_aliases - env[:ui].warn(I18n.t('vagrant_libvirt.destroy.obsolete_method')) + env[:ui].warn(I18n.t('vagrant_libvirt.domain_xml.obsolete_method')) end destroy_domain(domain, destroy_volumes: false, flags: undefine_flags) diff --git a/lib/vagrant-libvirt/action/handle_box_image.rb b/lib/vagrant-libvirt/action/handle_box_image.rb index 3c706c1..c6e9237 100644 --- a/lib/vagrant-libvirt/action/handle_box_image.rb +++ b/lib/vagrant-libvirt/action/handle_box_image.rb @@ -104,6 +104,8 @@ module VagrantPlugins end # save for use by later actions env[:box_volumes][0][:virtual_size] = box_virtual_size + # special handling for domain volume + env[:box_volumes][0][:device] ||= config.disk_device # while inside the synchronize block take care not to call the next # action in the chain, as must exit this block first to prevent diff --git a/lib/vagrant-libvirt/action/resolve_disk_settings.rb b/lib/vagrant-libvirt/action/resolve_disk_settings.rb new file mode 100644 index 0000000..f60fc9d --- /dev/null +++ b/lib/vagrant-libvirt/action/resolve_disk_settings.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +require 'log4r' +require 'rexml' + +require 'vagrant-libvirt/util/resolvers' + +module VagrantPlugins + module ProviderLibvirt + module Action + class ResolveDiskSettings + def initialize(app, _env) + @logger = Log4r::Logger.new('vagrant_libvirt::action::resolve_disk_devices') + @app = app + end + + def call(env) + # Get config. + config = env[:machine].provider_config + + domain_name = env[:domain_name] # only set on create + disk_bus = config.disk_bus + disk_device = config.disk_device + domain_volume_cache = config.volume_cache || 'default' + + # Storage + storage_pool_name = config.storage_pool_name + snapshot_pool_name = config.snapshot_pool_name + domain_volumes = [] + disks = config.disks.dup + + resolver = ::VagrantPlugins::ProviderLibvirt::Util::DiskDeviceResolver.new(disk_device[0..1]) + + # Get path to domain image from the storage pool selected if we have a box. + if env[:machine].config.vm.box + pool_name = if snapshot_pool_name == storage_pool_name + storage_pool_name + else + snapshot_pool_name + end + + if env[:box_volumes].nil? + # domain must be already created, need to read domain volumes from domain XML + libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid( + env[:machine].id + ) + domain_xml = libvirt_domain.xml_desc(1) + xml_descr = REXML::Document.new(domain_xml) + domain_name = xml_descr.elements['domain'].elements['name'].text + disks_xml = REXML::XPath.match(xml_descr, '/domain/devices/disk[@device="disk"]') + have_aliases = !REXML::XPath.match(disks_xml, './alias[@name="ua-box-volume-0"]').first.nil? + env[:ui].warn(I18n.t('vagrant_libvirt.domain_xml.obsolete_method')) unless have_aliases + + if have_aliases + REXML::XPath.match(disks_xml, + './alias[contains(@name, "ua-box-volume-")]').each_with_index do |alias_xml, idx| + domain_volumes.push(volume_from_xml(alias_xml.parent, domain_name, idx)) + end + else + # fallback to try and infer which boxes are box images, as they are listed first + # as soon as there is no match, can exit + disks_xml.each_with_index do |box_disk_xml, idx| + diskname = box_disk_xml.elements['source'].attributes['file'].rpartition('/').last + + break if volume_name(domain_name, idx) != diskname + + domain_volumes.push(volume_from_xml(box_disk_xml, domain_name, idx)) + end + end + else + + @logger.debug "Search for volumes in pool: #{pool_name}" + env[:box_volumes].each_index do |index| + domain_volume = env[:machine].provider.driver.connection.volumes.all( + name: volume_name(domain_name, index) + ).find { |x| x.pool_name == pool_name } + raise Errors::NoDomainVolume if domain_volume.nil? + + domain_volumes.push( + { + name: volume_name(domain_name, index), + device: env[:box_volumes][index][:device], + cache: domain_volume_cache, + bus: disk_bus, + absolute_path: domain_volume.path, + virtual_size: env[:box_volumes][index][:virtual_size], + pool: pool_name, + } + ) + end + end + + resolver.resolve!(domain_volumes) + + # 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. + # the default storage prefix is typically: /var/lib/libvirt/images/ + storage_prefix = "#{File.dirname(domain_volumes[0][:absolute_path])}/" # steal + else + # Ensure domain name is set for subsequent steps if restarting a machine without a box + libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid( + env[:machine].id + ) + domain_xml = libvirt_domain.xml_desc(1) + xml_descr = REXML::Document.new(domain_xml) + domain_name = xml_descr.elements['domain'].elements['name'].text + + storage_prefix = get_disk_storage_prefix(env[:machine], storage_pool_name) + end + + resolver.resolve!(disks) + + disks.each do |disk| + disk[:path] ||= disk_name(domain_name, disk) + + # On volume creation, the element inside + # is oddly ignored; instead the path is taken from the + # element: + # http://www.redhat.com/archives/libvir-list/2008-August/msg00329.html + disk[:name] = disk[:path] + + disk[:absolute_path] = storage_prefix + disk[:path] + + if disk[:pool].nil? + disk[:pool] = storage_pool_name + else + @logger.debug "Overriding pool name with: #{disk[:pool]}" + disk_storage_prefix = get_disk_storage_prefix(env[:machine], disk[:pool]) + disk[:absolute_path] = disk_storage_prefix + disk[:path] + @logger.debug "Overriding disk path with: #{disk[:absolute_path]}" + end + end + + env[:domain_volumes] = domain_volumes + env[:disks] = disks + + @app.call(env) + end + + private + + def disk_name(name, disk) + "#{name}-#{disk[:device]}.#{disk[:type]}" # disk name + end + + def get_disk_storage_prefix(machine, disk_pool_name) + disk_storage_pool = machine.provider.driver.connection.client.lookup_storage_pool_by_name(disk_pool_name) + raise Errors::NoStoragePool if disk_storage_pool.nil? + + xml = Nokogiri::XML(disk_storage_pool.xml_desc) + "#{xml.xpath('/pool/target/path').inner_text}/" + end + + def volume_name(domain_name, index) + domain_name + (index.zero? ? '.img' : "_#{index}.img") + end + + def volume_from_xml(device_xml, domain_name, index) + driver = device_xml.elements['driver'] + source = device_xml.elements['source'] + target = device_xml.elements['target'] + + { + name: volume_name(domain_name, index), + device: target.attributes['dev'], + cache: driver.attributes['cache'], + bus: target.attributes['bus'], + absolute_path: source.attributes['file'], + } + end + end + end + end +end diff --git a/lib/vagrant-libvirt/templates/domain.xml.erb b/lib/vagrant-libvirt/templates/domain.xml.erb index 15330eb..82fc84c 100644 --- a/lib/vagrant-libvirt/templates/domain.xml.erb +++ b/lib/vagrant-libvirt/templates/domain.xml.erb @@ -117,9 +117,9 @@ @disk_driver_opts.reject { |k,v| v.nil? } .map { |k,v| "#{k}='#{v}'"} .join(' ') -%>/> - + <%# we need to ensure a unique target dev -%> - + <%- end -%> <%# additional disks -%> diff --git a/locales/en.yml b/locales/en.yml index 08e25a9..5a7a27c 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -58,9 +58,11 @@ en: remove_stale_volume: |- Remove stale volume... - destroy: + domain_xml: obsolete_method: | - Destroying machine that was originally created without device aliases (pre 0.6.0), using fallback approach. + Machine that was originally created without device aliases (pre 0.6.0), using fall-back approach for device identification. + + destroy: unexpected_volumes: | Unexpected number of volumes detected, possibly additional volumes attached outside of vagrant-libvirt. Attempting to handle this correctly, however may experience unexpected behaviour in the domain destroy. diff --git a/spec/unit/action/create_domain_spec.rb b/spec/unit/action/create_domain_spec.rb index 51a045b..ec8f5fc 100644 --- a/spec/unit/action/create_domain_spec.rb +++ b/spec/unit/action/create_domain_spec.rb @@ -21,8 +21,6 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do let(:domain_volume) { instance_double(::Fog::Libvirt::Compute::Volume) } let(:domain_xml) { File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), domain_xml_file)) } - let(:storage_pool_xml) { File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), storage_pool_xml_file)) } - let(:libvirt_storage_pool) { double('storage_pool') } describe '#call' do before do @@ -39,15 +37,17 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do env[:domain_name] = "vagrant-test_default" - env[:box_volumes] = [] - env[:box_volumes].push({ + env[:domain_volumes] = [] + env[:domain_volumes].push({ + :device=>'vda', + :bus=>'virtio', + :cache=>'default', + :absolute_path=>'/var/lib/libvirt/images/vagrant-test_default.img', :path=>"/test/box.img", - :name=>"test_vagrant_box_image_1.1.1_0.img", + :name=>'test_vagrant_box_image_1.1.1_0.img', :virtual_size=> ByteNumber.new(5), + :pool=>'default', }) - # should be ignored for system session and used for user session - allow(Process).to receive(:uid).and_return(9999) - allow(Process).to receive(:gid).and_return(9999) end context 'connection => qemu:///system' do @@ -57,103 +57,50 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do allow(machine.provider_config).to receive(:qemu_use_session).and_return(false) end - context 'default pool' do - it 'should execute correctly' do - expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine) - expect(volumes).to_not receive(:create) # additional disks only + it 'should execute correctly' do + expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine) + expect(volumes).to_not receive(:create) # additional disks only - expect(subject.call(env)).to be_nil + expect(subject.call(env)).to be_nil + end + + context 'additional disks' do + let(:disks) do + [ + :device => 'vdb', + :cache => 'default', + :bus => 'virtio', + :type => 'qcow2', + :absolute_path => '/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2', + :virtual_size => ByteNumber.new(20*1024*1024*1024), + :pool => 'default', + ] end - context 'with no box' do - let(:storage_pool_xml_file) { 'default_system_storage_pool.xml' } - let(:vagrantfile) do - <<-EOF - Vagrant.configure('2') do |config| - config.vm.define :test - end - EOF - end + before do + env[:disks] = disks + end - it 'should query for the storage pool path' do - expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(libvirt_storage_pool) - expect(libvirt_storage_pool).to receive(:xml_desc).and_return(storage_pool_xml) - expect(servers).to receive(:create).and_return(machine) + context 'volume create failed' do + it 'should raise an exception' do + expect(volumes).to receive(:create).and_raise(Libvirt::Error) - expect(subject.call(env)).to be_nil + expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::FogCreateDomainVolumeError) end end - context 'additional disks' do - let(:vagrantfile_providerconfig) do - <<-EOF - libvirt.storage :file, :size => '20G' - EOF - end + context 'volume create succeeded' do + let(:domain_xml_file) { 'additional_disks_domain.xml' } - context 'volume create failed' do - it 'should raise an exception' do - expect(volumes).to receive(:create).and_raise(Libvirt::Error) - - expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::FogCreateDomainVolumeError) - end - end - - context 'volume create succeeded' do - let(:domain_xml_file) { 'additional_disks_domain.xml' } - - it 'should complete' do - expect(volumes).to receive(:create).with( - hash_including( - :path => "/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2", - :owner => 0, - :group => 0, - :pool_name => "default", - ) + it 'should complete' do + expect(volumes).to receive(:create).with( + hash_including( + :path => "/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2", + :owner => 0, + :group => 0, + :pool_name => "default", ) - expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine) - - expect(subject.call(env)).to be_nil - end - end - end - - context 'with custom disk device setting' do - let(:domain_xml_file) { 'custom_disk_settings.xml' } - let(:vagrantfile_providerconfig) { - <<-EOF - libvirt.disk_device = 'sda' - EOF - } - - it 'should set the domain device' do - expect(ui).to receive(:info).with(/ -- Image\(sda\):.*/) - expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine) - - expect(subject.call(env)).to be_nil - end - end - - context 'with two domain disks' do - let(:domain_xml_file) { 'two_disk_settings.xml' } - let(:domain_volume_2) { double('domain_volume 2') } - - before do - expect(volumes).to receive(:all).and_return([domain_volume]) - expect(volumes).to receive(:all).and_return([domain_volume_2]) - expect(domain_volume_2).to receive(:pool_name).and_return('default') - expect(domain_volume_2).to receive(:path).and_return('/var/lib/libvirt/images/vagrant-test_default_1.img') - - env[:box_volumes].push({ - :path=>"/test/box_1.img", - :name=>"test_vagrant_box_image_1.1.1_1.img", - :virtual_size=> ByteNumber.new(5), - }) - end - - it 'should correctly assign device entries' do - expect(ui).to receive(:info).with(/ -- Image\(vda\):.*/) - expect(ui).to receive(:info).with(/ -- Image\(vdb\):.*/) + ) expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine) expect(subject.call(env)).to be_nil @@ -161,19 +108,48 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do end end - context 'no default pool' do - let(:vagrantfile) do - <<-EOF - Vagrant.configure('2') do |config| - config.vm.define :test - end - EOF + context 'with custom disk device setting' do + let(:domain_xml_file) { 'custom_disk_settings.xml' } + + before do + env[:domain_volumes][0][:device] = 'sda' end - it 'should raise an exception' do - expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(nil) + it 'should set the domain device' do + expect(ui).to receive(:info).with(/ -- Image\(sda\):.*/) + expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine) - expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::NoStoragePool) + expect(subject.call(env)).to be_nil + end + end + + context 'with two domain disks' do + let(:domain_xml_file) { 'two_disk_settings.xml' } + let(:domain_volume_2) { double('domain_volume 2') } + + before do + expect(volumes).to receive(:all).with(name: 'vagrant-test_default.img').and_return([domain_volume]) + expect(volumes).to receive(:all).with(name: 'vagrant-test_default_1.img').and_return([domain_volume_2]) + expect(domain_volume_2).to receive(:pool_name).and_return('default') + + env[:domain_volumes].push({ + :device=>'vdb', + :bus=>'virtio', + :cache=>'default', + :absolute_path=>'/var/lib/libvirt/images/vagrant-test_default_1.img', + :path=>"/test/box_1.img", + :name=>"test_vagrant_box_image_1.1.1_1.img", + :virtual_size=> ByteNumber.new(5), + :pool=>'default', + }) + end + + it 'should list multiple device entries' do + expect(ui).to receive(:info).with(/ -- Image\(vda\):.*/) + expect(ui).to receive(:info).with(/ -- Image\(vdb\):.*/) + expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine) + + expect(subject.call(env)).to be_nil end end end @@ -183,59 +159,53 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do allow(machine.provider_config).to receive(:qemu_use_session).and_return(true) end - context 'default pool' do - it 'should execute correctly' do - expect(servers).to receive(:create).and_return(machine) + it 'should execute correctly' do + expect(servers).to receive(:create).and_return(machine) - expect(subject.call(env)).to be_nil + expect(subject.call(env)).to be_nil + end + + context 'additional disks' do + let(:vagrantfile_providerconfig) do + <<-EOF + libvirt.qemu_use_session = true + EOF end - context 'with no box' do - let(:storage_pool_xml_file) { 'default_user_storage_pool.xml' } - let(:vagrantfile) do - <<-EOF - Vagrant.configure('2') do |config| - config.vm.define :test - config.vm.provider :libvirt do |libvirt| - #{vagrantfile_providerconfig} - end - end - EOF - end + let(:disks) do + [ + :device => 'vdb', + :cache => 'default', + :bus => 'virtio', + :type => 'qcow2', + :absolute_path => '/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2', + :virtual_size => ByteNumber.new(20*1024*1024*1024), + :pool => 'default', + ] + end - it 'should query for the storage pool path' do - expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(libvirt_storage_pool) - expect(libvirt_storage_pool).to receive(:xml_desc).and_return(storage_pool_xml) + before do + expect(Process).to receive(:uid).and_return(9999).at_least(:once) + expect(Process).to receive(:gid).and_return(9999).at_least(:once) + + env[:disks] = disks + end + + context 'volume create succeeded' do + it 'should complete' do + expect(volumes).to receive(:create).with( + hash_including( + :path => "/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2", + :owner => 9999, + :group => 9999, + :pool_name => "default", + ) + ) expect(servers).to receive(:create).and_return(machine) expect(subject.call(env)).to be_nil end end - - context 'additional disks' do - let(:vagrantfile_providerconfig) do - <<-EOF - libvirt.qemu_use_session = true - libvirt.storage :file, :size => '20G' - EOF - end - - context 'volume create succeeded' do - it 'should complete' do - expect(volumes).to receive(:create).with( - hash_including( - :path => "/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2", - :owner => 9999, - :group => 9999, - :pool_name => "default", - ) - ) - expect(servers).to receive(:create).and_return(machine) - - expect(subject.call(env)).to be_nil - end - end - end end end end diff --git a/spec/unit/action/destroy_domain_spec.rb b/spec/unit/action/destroy_domain_spec.rb index 07953de..db1e5eb 100644 --- a/spec/unit/action/destroy_domain_spec.rb +++ b/spec/unit/action/destroy_domain_spec.rb @@ -145,7 +145,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::DestroyDomain do let(:domain_xml_file) { 'box_multiple_disks_and_additional_and_custom_disks_no_aliases.xml' } it 'only destroys expected disks' do - expect(ui).to receive(:warn).with(/Destroying machine that was originally created without device aliases.*/) + expect(ui).to receive(:warn).with(/Machine that was originally created without device aliases.*/) expect(ui).to receive(:warn).with(/Unexpected number of volumes detected/) expect(ui).to receive(:warn).with(/box metadata not available to get volume list during destroy, assuming inferred list/) domain_disks.each do |disk, name| @@ -172,7 +172,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::DestroyDomain do end it 'only destroys expected disks' do - expect(ui).to receive(:warn).with(/Destroying machine that was originally created without device aliases.*/) + expect(ui).to receive(:warn).with(/Machine that was originally created without device aliases.*/) expect(ui).to receive(:warn).with(/Unexpected number of volumes detected/) domain_disks.each do |disk, name| expect(disk).to receive(:name).and_return(name).at_least(:once) diff --git a/spec/unit/action/handle_box_image_spec.rb b/spec/unit/action/handle_box_image_spec.rb index 03526ad..7388d58 100644 --- a/spec/unit/action/handle_box_image_spec.rb +++ b/spec/unit/action/handle_box_image_spec.rb @@ -59,6 +59,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do allow(volumes).to receive(:all).and_return(all) allow(env[:ui]).to receive(:clear_line) + env[:machine].provider_config.disk_device = 'vda' end context 'when one disk in metadata.json' do @@ -86,7 +87,8 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do :path=>"/test/box.img", :name=>"test_vagrant_box_image_1.1.1_box.img", :virtual_size=>byte_number_5G, - :format=>"qcow2" + :format=>"qcow2", + :device=>'vda', } ] ) @@ -115,7 +117,8 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do :path=>"/test/box.img", :name=>"test_vagrant_box_image_0_#{box_mtime.to_i}_box.img", :virtual_size=>byte_number_5G, - :format=>"qcow2" + :format=>"qcow2", + :device=>'vda', } ] ) @@ -124,7 +127,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do context 'When config.machine_virtual_size is set and smaller than box_virtual_size' do before do - allow(env[:machine]).to receive_message_chain("provider_config.machine_virtual_size").and_return(1) + env[:machine].provider_config.machine_virtual_size = 1 end it 'should warning must be raise' do expect(ui).to receive(:warn).with("Ignoring requested virtual disk size of '1' as it is below\nthe minimum box image size of '5'.") @@ -135,7 +138,8 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do :path=>"/test/box.img", :name=>"test_vagrant_box_image_1.1.1_box.img", :virtual_size=>byte_number_5G, - :format=>"qcow2" + :format=>"qcow2", + :device=>'vda', } ] ) @@ -144,7 +148,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do context 'When config.machine_virtual_size is set and higher than box_virtual_size' do before do - allow(env[:machine]).to receive_message_chain("provider_config.machine_virtual_size").and_return(20) + env[:machine].provider_config.machine_virtual_size = 20 end it 'should be use' do expect(ui).to receive(:info).with("Created volume larger than box defaults, will require manual resizing of\nfilesystems to utilize.") @@ -155,7 +159,8 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do :path=>"/test/box.img", :name=>"test_vagrant_box_image_1.1.1_box.img", :virtual_size=>byte_number_20G, - :format=>"qcow2" + :format=>"qcow2", + :device=>'vda', } ] ) @@ -246,7 +251,8 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do :path=>"/test/box.img", :name=>"test_vagrant_box_image_1.1.1_send_box_name.img", :virtual_size=>byte_number_5G, - :format=>"qcow2" + :format=>"qcow2", + :device=>'vda', }, { :path=>"/test/disk.qcow2", diff --git a/spec/unit/action/resolve_disk_settings_spec.rb b/spec/unit/action/resolve_disk_settings_spec.rb new file mode 100644 index 0000000..a1f557b --- /dev/null +++ b/spec/unit/action/resolve_disk_settings_spec.rb @@ -0,0 +1,361 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require 'fog/libvirt/models/compute/volume' + +require 'vagrant-libvirt/action/resolve_disk_settings' + +describe VagrantPlugins::ProviderLibvirt::Action::ResolveDiskSettings do + subject { described_class.new(app, env) } + + include_context 'unit' + include_context 'libvirt' + + let(:servers) { double('servers') } + let(:volumes) { double('volumes') } + let(:domain_volume) { instance_double(::Fog::Libvirt::Compute::Volume) } + let(:libvirt_storage_pool) { double('storage_pool') } + + let(:domain_xml) { File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), domain_xml_file)) } + let(:storage_pool_xml) do + File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), storage_pool_xml_file)) + end + + before do + allow(logger).to receive(:info) + allow(logger).to receive(:debug) + end + + describe '#call' do + before do + allow(connection).to receive(:volumes).and_return(volumes) + allow(volumes).to receive(:all).and_return([domain_volume]) + allow(domain_volume).to receive(:pool_name).and_return('default') + allow(domain_volume).to receive(:path).and_return('/var/lib/libvirt/images/vagrant-test_default.img') + end + + context 'when vm box is in use' do + context 'when box metadata is available' do + let(:box_volumes) do + [ + { + path: '/test/box.img', + name: 'test_vagrant_box_image_1.1.1_box.img', + }, + ] + end + + before do + env[:domain_name] = 'vagrant-test_default' + env[:box_volumes] = box_volumes + end + + it 'should populate domain volume' do + expect(subject.call(env)).to be_nil + expect(env[:domain_volumes]).to match( + [ + hash_including( + device: 'vda', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + ] + ) + end + + context 'when additional storage specified' do + let(:vagrantfile_providerconfig) do + <<-EOF + libvirt.storage :file, :size => '20G' + EOF + end + + it 'should use the next device for storage' do + expect(subject.call(env)).to be_nil + expect(env[:disks]).to match( + [ + hash_including( + device: 'vdb', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2' + ), + ] + ) + end + end + + context 'when custom disk device setting' do + before do + machine.provider_config.disk_device = 'sda' + end + + it 'should set the domain device' do + expect(subject.call(env)).to be_nil + expect(env[:domain_volumes]).to match( + [ + hash_including( + device: 'sda', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + ] + ) + end + + context 'when additional storage specified' do + let(:vagrantfile_providerconfig) do + <<-EOF + libvirt.storage :file, :size => '20G' + EOF + end + + it 'should use the next custom disk device for storage' do + expect(subject.call(env)).to be_nil + expect(env[:disks]).to match( + [ + hash_including( + device: 'sdb', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-sdb.qcow2' + ), + ] + ) + end + end + end + + context 'when multiple box volumes' do + let(:box_volumes) do + [ + { + path: '/test/box.img', + name: 'test_vagrant_box_image_1.1.1_box.img', + }, + { + path: '/test/box_2.img', + name: 'test_vagrant_box_image_1.1.1_box_2.img', + }, + ] + end + it 'should populate all domain volumes' do + expect(subject.call(env)).to be_nil + expect(env[:domain_volumes]).to match( + [ + hash_including( + device: 'vda', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + hash_including( + device: 'vdb', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + ] + ) + end + + context 'when additional storage specified' do + let(:vagrantfile_providerconfig) do + <<-EOF + libvirt.storage :file, :size => '20G' + EOF + end + + it 'should use the next device for storage' do + expect(subject.call(env)).to be_nil + expect(env[:disks]).to match( + [ + hash_including( + device: 'vdc', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-vdc.qcow2' + ), + ] + ) + end + end + end + end + + context 'when box metadata is not available' do + let(:domain_xml_file) { 'default_domain.xml' } + + before do + expect(libvirt_client).to receive(:lookup_domain_by_uuid).and_return(libvirt_domain) + expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml) + end + + it 'should query the domain xml' do + expect(subject.call(env)).to be_nil + expect(env[:domain_volumes]).to match( + [ + hash_including( + device: 'vda', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + ] + ) + end + + context 'when multiple volumes in domain config' do + let(:domain_xml_file) { 'multi_volume_box.xml' } + + it 'should populate domain volumes with devices' do + expect(subject.call(env)).to be_nil + expect(env[:domain_volumes]).to match( + [ + hash_including( + device: 'vda', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + hash_including( + device: 'vdb', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default_1.img' + ), + hash_including( + device: 'vdc', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default_2.img' + ), + ] + ) + end + + context 'when additional storage in domain config' do + let(:domain_xml_file) { 'multi_volume_box_additional_storage.xml' } + let(:vagrantfile_providerconfig) do + <<-EOF + libvirt.storage :file, :size => '20G' + libvirt.storage :file, :size => '20G' + EOF + end + + it 'should populate disks with devices' do + expect(subject.call(env)).to be_nil + expect(env[:disks]).to match( + [ + hash_including( + device: 'vdd', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-vdd.qcow2' + ), + hash_including( + device: 'vde', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-vde.qcow2' + ), + ] + ) + end + end + end + + context 'when no aliases available' do + let(:domain_xml_file) { 'default_no_aliases.xml' } + + it 'should assume a single box volume' do + expect(subject.call(env)).to be_nil + expect(env[:domain_volumes]).to match( + [ + hash_including( + device: 'vda', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + ] + ) + end + + context 'when additional storage and a custom disk device attached' do + let(:domain_xml_file) { 'multi_volume_box_additional_and_custom_no_aliases.xml' } + let(:vagrantfile_providerconfig) do + <<-EOF + libvirt.storage :file, :size => '20G' + libvirt.storage :file, :size => '20G' + EOF + end + + it 'should detect the domain volumes and disks while ignoring the last one' do + expect(subject.call(env)).to be_nil + expect(env[:domain_volumes]).to match( + [ + hash_including( + device: 'vda', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img' + ), + hash_including( + device: 'vdb', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default_1.img' + ), + hash_including( + device: 'vdc', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default_2.img' + ), + ] + ) + expect(env[:disks]).to match( + [ + hash_including( + device: 'vdd', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-vdd.qcow2' + ), + hash_including( + device: 'vde', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-vde.qcow2' + ), + ] + ) + end + end + end + end + + context 'no default pool' do + let(:domain_xml_file) { 'default_domain.xml' } + let(:vagrantfile) do + <<-EOF + Vagrant.configure('2') do |config| + config.vm.define :test + end + EOF + end + + it 'should raise an exception' do + expect(libvirt_client).to receive(:lookup_domain_by_uuid).and_return(libvirt_domain) + expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml) + expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(nil) + + expect { subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::NoStoragePool) + end + end + end + + context 'when no box defined' do + let(:domain_xml_file) { 'default_domain.xml' } + let(:storage_pool_xml_file) { 'default_system_storage_pool.xml' } + let(:vagrantfile) do + <<-EOF + Vagrant.configure('2') do |config| + config.vm.define :test + config.vm.provider :libvirt do |libvirt| + libvirt.storage :file, :size => '20G' + end + end + EOF + end + + it 'should query for domain name and storage pool path' do + expect(libvirt_client).to receive(:lookup_domain_by_uuid).and_return(libvirt_domain) + expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml) + expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(libvirt_storage_pool) + expect(libvirt_storage_pool).to receive(:xml_desc).and_return(storage_pool_xml) + + expect(subject.call(env)).to be_nil + expect(env[:disks]).to match( + [ + hash_including( + device: 'vda', + cache: 'default', + bus: 'virtio', + path: 'vagrant-test_default-vda.qcow2', + absolute_path: '/var/lib/libvirt/images/vagrant-test_default-vda.qcow2', + size: '20G', + pool: 'default' + ), + ] + ) + end + end + end +end diff --git a/spec/unit/action/resolve_disk_settings_spec/default_domain.xml b/spec/unit/action/resolve_disk_settings_spec/default_domain.xml new file mode 100644 index 0000000..1dc7bcd --- /dev/null +++ b/spec/unit/action/resolve_disk_settings_spec/default_domain.xml @@ -0,0 +1,43 @@ + + vagrant-test_default + + Source: /rootpath/Vagrantfile + + 524288 + 1 + + + + + hvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/unit/action/resolve_disk_settings_spec/default_no_aliases.xml b/spec/unit/action/resolve_disk_settings_spec/default_no_aliases.xml new file mode 100644 index 0000000..94031e2 --- /dev/null +++ b/spec/unit/action/resolve_disk_settings_spec/default_no_aliases.xml @@ -0,0 +1,42 @@ + + vagrant-test_default + + Source: /rootpath/Vagrantfile + + 524288 + 1 + + + + + hvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/unit/action/create_domain_spec/default_system_storage_pool.xml b/spec/unit/action/resolve_disk_settings_spec/default_system_storage_pool.xml similarity index 100% rename from spec/unit/action/create_domain_spec/default_system_storage_pool.xml rename to spec/unit/action/resolve_disk_settings_spec/default_system_storage_pool.xml diff --git a/spec/unit/action/resolve_disk_settings_spec/multi_volume_box.xml b/spec/unit/action/resolve_disk_settings_spec/multi_volume_box.xml new file mode 100644 index 0000000..13fe1a3 --- /dev/null +++ b/spec/unit/action/resolve_disk_settings_spec/multi_volume_box.xml @@ -0,0 +1,55 @@ + + vagrant-test_default + + Source: /rootpath/Vagrantfile + + 524288 + 1 + + + + + hvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_and_custom_no_aliases.xml b/spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_and_custom_no_aliases.xml new file mode 100644 index 0000000..446ed90 --- /dev/null +++ b/spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_and_custom_no_aliases.xml @@ -0,0 +1,67 @@ + + vagrant-test_default + + Source: /rootpath/Vagrantfile + + 524288 + 1 + + + + + hvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_storage.xml b/spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_storage.xml new file mode 100644 index 0000000..c2d99bf --- /dev/null +++ b/spec/unit/action/resolve_disk_settings_spec/multi_volume_box_additional_storage.xml @@ -0,0 +1,67 @@ + + vagrant-test_default + + Source: /rootpath/Vagrantfile + + 524288 + 1 + + + + + hvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/unit/templates/domain_spec.rb b/spec/unit/templates/domain_spec.rb index 9d9bf46..fef3caa 100644 --- a/spec/unit/templates/domain_spec.rb +++ b/spec/unit/templates/domain_spec.rb @@ -62,13 +62,13 @@ describe 'templates/domain' do :dev => 'vda', :cache => 'unsafe', :bus => domain.disk_bus, - :path => '/var/lib/libvirt/images/test.qcow2' + :absolute_path => '/var/lib/libvirt/images/test.qcow2' }) domain.domain_volumes.push({ :dev => 'vdb', :cache => 'unsafe', :bus => domain.disk_bus, - :path => '/var/lib/libvirt/images/test2.qcow2' + :absolute_path => '/var/lib/libvirt/images/test2.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') diff --git a/tests/runtests.bats b/tests/runtests.bats index b659019..a41263d 100644 --- a/tests/runtests.bats +++ b/tests/runtests.bats @@ -78,6 +78,14 @@ cleanup() { [ "$status" -eq 0 ] echo "${output}" [ $(expr "$output" : ".*second_disk_default-vdb.*") -ne 0 ] + run ${VAGRANT_CMD} halt + echo "${output}" + echo "status = ${status}" + [ "$status" -eq 0 ] + run ${VAGRANT_CMD} up ${VAGRANT_OPT} + echo "${output}" + echo "status = ${status}" + [ "$status" -eq 0 ] cleanup } diff --git a/tests/second_disk/Vagrantfile b/tests/second_disk/Vagrantfile index 6bdbc22..5683566 100644 --- a/tests/second_disk/Vagrantfile +++ b/tests/second_disk/Vagrantfile @@ -6,6 +6,7 @@ Vagrant.configure("2") do |config| config.vm.box = "infernix/tinycore" config.ssh.shell = "/bin/sh" + config.ssh.insert_key = false # needed for testing halt as inserted key is persisted config.vm.synced_folder ".", "/vagrant", disabled: true config.vm.provider :libvirt do |libvirt|