2016-12-06 23:20:29 +01:00
|
|
|
require 'spec_helper'
|
|
|
|
|
require 'support/sharedcontext'
|
2016-11-25 15:08:33 +00:00
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
require 'vagrant-libvirt/config'
|
2016-11-25 15:08:33 +00:00
|
|
|
|
|
|
|
|
describe VagrantPlugins::ProviderLibvirt::Config do
|
2016-12-06 23:20:29 +01:00
|
|
|
include_context 'unit'
|
2016-11-25 15:08:33 +00:00
|
|
|
|
2020-08-16 16:27:27 +01:00
|
|
|
let(:fake_env) { Hash.new }
|
|
|
|
|
|
2020-12-16 20:19:24 +01:00
|
|
|
describe '#clock_timer' do
|
|
|
|
|
it 'should handle all options' do
|
|
|
|
|
expect(
|
|
|
|
|
subject.clock_timer(
|
|
|
|
|
:name => 'rtc',
|
|
|
|
|
:track => 'wall',
|
|
|
|
|
:tickpolicy => 'delay',
|
|
|
|
|
:present => 'yes',
|
|
|
|
|
).length
|
|
|
|
|
).to be(1)
|
|
|
|
|
expect(
|
|
|
|
|
subject.clock_timer(
|
|
|
|
|
:name => 'tsc',
|
|
|
|
|
:tickpolicy => 'delay',
|
|
|
|
|
:frequency => '100',
|
|
|
|
|
:mode => 'auto',
|
|
|
|
|
:present => 'yes',
|
|
|
|
|
).length
|
|
|
|
|
).to be(2)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'should correctly save the options' do
|
|
|
|
|
opts = {:name => 'rtc', :track => 'wall'}
|
|
|
|
|
expect(subject.clock_timer(opts).length).to be(1)
|
|
|
|
|
|
|
|
|
|
expect(subject.clock_timers[0]).to eq(opts)
|
|
|
|
|
|
|
|
|
|
opts[:name] = 'tsc'
|
|
|
|
|
expect(subject.clock_timers[0]).to_not eq(opts)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'should error name option is missing' do
|
|
|
|
|
expect{ subject.clock_timer(:track => "wall") }.to raise_error("Clock timer name must be specified")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'should error if nil value for option supplied' do
|
|
|
|
|
expect{ subject.clock_timer(:name => "rtc", :track => nil) }.to raise_error("Value of timer option track is nil")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'should error if unrecognized option specified' do
|
|
|
|
|
expect{ subject.clock_timer(:name => "tsc", :badopt => "value") }.to raise_error("Unknown clock timer option: badopt")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-08-16 16:27:27 +01:00
|
|
|
describe '#finalize!' do
|
|
|
|
|
it 'is valid with defaults' do
|
|
|
|
|
subject.finalize!
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context '@uri' do
|
|
|
|
|
before(:example) do
|
|
|
|
|
stub_const("ENV", fake_env)
|
2021-03-13 15:19:41 +00:00
|
|
|
fake_env['HOME'] = "/home/tests"
|
2020-08-16 16:27:27 +01:00
|
|
|
end
|
|
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
# table describing expected behaviour of inputs that affect the resulting uri as
|
|
|
|
|
# well as any subsequent settings that might be inferred if the uri was
|
|
|
|
|
# explicitly set.
|
|
|
|
|
[
|
|
|
|
|
# settings
|
|
|
|
|
[ # all default
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu:///system?no_verify=1&keyfile=/home/tests/.ssh/id_rsa"},
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
# with LIBVIRT_DEFAULT_URI
|
|
|
|
|
[ # all other set to default
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "custom:///custom_path", :qemu_use_session => false},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "custom:///custom_path"},
|
|
|
|
|
],
|
|
|
|
|
[ # with session
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu:///session", :qemu_use_session => true},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "qemu:///session"},
|
|
|
|
|
],
|
|
|
|
|
[ # with session and using ssh
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu+ssh:///session", :qemu_use_session => true},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "qemu+ssh:///session"},
|
|
|
|
|
],
|
|
|
|
|
[ # with session and using ssh infer host and connect by ssh
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu+ssh:///session", :qemu_use_session => true, :connect_via_ssh => true, :host => 'localhost'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "qemu+ssh:///session"},
|
|
|
|
|
"not yet inferring connect_via_ssh", # once working remove the preceding test
|
|
|
|
|
],
|
|
|
|
|
[ # with session and using ssh to specific host with additional query options provided
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu+ssh://remote/session?keyfile=my_id_rsa", :qemu_use_session => true},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "qemu+ssh://remote/session?keyfile=my_id_rsa"},
|
|
|
|
|
],
|
|
|
|
|
[ # with session and using ssh to specific host with additional query options provided, infer host and ssh
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu+ssh://remote/session?keyfile=my_id_rsa", :qemu_use_session => true, :connect_via_ssh => true, :host => 'remote'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "qemu+ssh://remote/session?keyfile=my_id_rsa"},
|
|
|
|
|
"not yet inferring host correctly", # once working remove the preceding test
|
|
|
|
|
],
|
|
|
|
|
[ # when session not set
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu:///system", :qemu_use_session => false},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "qemu:///system"},
|
|
|
|
|
],
|
|
|
|
|
[ # when session appearing elsewhere
|
|
|
|
|
{},
|
|
|
|
|
{:uri => "qemu://remote/system?keyfile=my_session_id", :qemu_use_session => false},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => "qemu://remote/system?keyfile=my_session_id"},
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
# ignore LIBVIRT_DEFAULT_URI due to explicit settings
|
|
|
|
|
[ # when uri explicitly set
|
|
|
|
|
{:uri => 'qemu:///system'},
|
|
|
|
|
{:uri => 'qemu:///system'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => 'qemu://session'},
|
|
|
|
|
],
|
|
|
|
|
[ # when driver explicitly set
|
|
|
|
|
{:driver => 'qemu'},
|
|
|
|
|
{:uri => 'qemu:///system?no_verify=1'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => 'qemu://session'},
|
|
|
|
|
"LIBVIRT_DEFAULT_URI is not yet just a fallback behaviour"
|
|
|
|
|
],
|
|
|
|
|
[ # when host explicitly set
|
|
|
|
|
{:host => 'remote'},
|
|
|
|
|
{:uri => 'qemu://remote/system?no_verify=1'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => 'qemu://session'},
|
|
|
|
|
"LIBVIRT_DEFAULT_URI is not yet just a fallback behaviour"
|
|
|
|
|
],
|
|
|
|
|
[ # when connect_via_ssh explicitly set
|
|
|
|
|
{:connect_via_ssh => true},
|
|
|
|
|
{:uri => 'qemu+ssh:///system?no_verify=1&keyfile=/home/tests/.ssh/id_rsa'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => 'qemu://session'},
|
|
|
|
|
"LIBVIRT_DEFAULT_URI is not yet just a fallback behaviour"
|
|
|
|
|
],
|
|
|
|
|
[ # when username explicitly set without host
|
|
|
|
|
{:username => 'my_user' },
|
|
|
|
|
{:uri => 'qemu://my_user@localhost/system?no_verify=1'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => 'qemu://session'},
|
|
|
|
|
"LIBVIRT_DEFAULT_URI is not yet just a fallback behaviour"
|
|
|
|
|
],
|
|
|
|
|
[ # when username explicitly set with host
|
|
|
|
|
{:username => 'my_user', :host => 'remote'},
|
|
|
|
|
{:uri => 'qemu://my_user@remote/system?no_verify=1'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => 'qemu://session'},
|
|
|
|
|
"LIBVIRT_DEFAULT_URI is not yet just a fallback behaviour"
|
|
|
|
|
],
|
|
|
|
|
[ # when password explicitly set
|
|
|
|
|
{:password => 'some_password'},
|
|
|
|
|
{:uri => 'qemu:///system?no_verify=1'},
|
|
|
|
|
{'LIBVIRT_DEFAULT_URI' => 'qemu://session'},
|
|
|
|
|
"LIBVIRT_DEFAULT_URI is not yet just a fallback behaviour"
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
# driver settings
|
|
|
|
|
[ # set to kvm only
|
|
|
|
|
{:driver => 'kvm', :id_ssh_key_file => nil},
|
|
|
|
|
{:uri => "qemu:///system?no_verify=1"},
|
|
|
|
|
],
|
|
|
|
|
[ # set to qemu only
|
|
|
|
|
{:driver => 'qemu', :id_ssh_key_file => nil},
|
|
|
|
|
{:uri => "qemu:///system?no_verify=1"},
|
|
|
|
|
],
|
|
|
|
|
[ # set to qemu with session enabled
|
|
|
|
|
{:driver => 'qemu', :qemu_use_session => true, :id_ssh_key_file => nil},
|
|
|
|
|
{:uri => "qemu:///session?no_verify=1"},
|
|
|
|
|
],
|
|
|
|
|
[ # set to openvz only
|
|
|
|
|
{:driver => 'openvz', :id_ssh_key_file => nil},
|
|
|
|
|
{:uri => "openvz:///system?no_verify=1"},
|
|
|
|
|
],
|
|
|
|
|
[ # set to esx
|
|
|
|
|
{:driver => 'esx'},
|
|
|
|
|
{:uri => "esx:///"},
|
|
|
|
|
{},
|
|
|
|
|
"Should not be adding query options that don't work to esx connection uri",
|
|
|
|
|
],
|
|
|
|
|
[ # set to vbox only
|
|
|
|
|
{:driver => 'vbox', :id_ssh_key_file => nil},
|
|
|
|
|
{:uri => "vbox:///session?no_verify=1"},
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
# connect_via_ssh settings
|
|
|
|
|
[ # enabled
|
|
|
|
|
{:connect_via_ssh => true},
|
|
|
|
|
{:uri => "qemu+ssh://localhost/system?no_verify=1&keyfile=/home/tests/.ssh/id_rsa"},
|
|
|
|
|
],
|
|
|
|
|
[ # enabled with user
|
|
|
|
|
{:connect_via_ssh => true, :username => 'my_user'},
|
|
|
|
|
{:uri => "qemu+ssh://my_user@localhost/system?no_verify=1&keyfile=/home/tests/.ssh/id_rsa"},
|
|
|
|
|
],
|
|
|
|
|
[ # enabled with host
|
|
|
|
|
{:connect_via_ssh => true, :host => 'remote_server'},
|
|
|
|
|
{:uri => "qemu+ssh://remote_server/system?no_verify=1&keyfile=/home/tests/.ssh/id_rsa"},
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
# id_ssh_key_file behaviour
|
|
|
|
|
[ # set
|
|
|
|
|
{:id_ssh_key_file => '/path/to/keyfile'},
|
|
|
|
|
{:uri => "qemu:///system?no_verify=1&keyfile=/path/to/keyfile"},
|
|
|
|
|
],
|
|
|
|
|
[ # set infer use of ssh
|
|
|
|
|
{:id_ssh_key_file => '/path/to/keyfile'},
|
|
|
|
|
{:uri => "qemu+ssh:///system?no_verify=1&keyfile=/path/to/keyfile"},
|
|
|
|
|
{},
|
|
|
|
|
"setting of ssh key file does not yet enable connect via ssh",
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
# socket behaviour
|
|
|
|
|
[ # set
|
|
|
|
|
{:socket => '/var/run/libvirt/libvirt-sock'},
|
|
|
|
|
{:uri => "qemu:///system?no_verify=1&keyfile=/home/tests/.ssh/id_rsa&socket=/var/run/libvirt/libvirt-sock"},
|
|
|
|
|
],
|
|
|
|
|
].each do |inputs, outputs, env, allow_failure|
|
|
|
|
|
it "should handle inputs #{inputs} with env #{env}" do
|
|
|
|
|
# allow some of these to fail for now if marked as such
|
|
|
|
|
if !allow_failure.nil?
|
|
|
|
|
pending(allow_failure)
|
|
|
|
|
end
|
2019-11-15 17:11:36 -05:00
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
inputs.each do |k, v|
|
|
|
|
|
subject.instance_variable_set("@#{k}", v)
|
2019-11-15 17:11:36 -05:00
|
|
|
end
|
|
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
if !env.nil?
|
|
|
|
|
env.each do |k, v|
|
|
|
|
|
fake_env[k] = v
|
2020-08-16 16:27:27 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
subject.finalize!
|
2020-08-16 16:27:27 +01:00
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
# ensure failed output indicates which settings are incorrect in the failed test
|
|
|
|
|
got = subject.instance_variables.each_with_object({}) do |name, hash|
|
|
|
|
|
if outputs.key?(name.to_s[1..-1].to_sym)
|
|
|
|
|
hash["#{name.to_s[1..-1]}".to_sym] =subject.instance_variable_get(name)
|
2020-08-16 16:27:27 +01:00
|
|
|
end
|
|
|
|
|
end
|
2021-03-13 15:19:41 +00:00
|
|
|
expect(got).to eq(outputs)
|
2020-08-16 16:27:27 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
context 'when invalid @driver is defined' do
|
|
|
|
|
it "should raise exception for unrecognized" do
|
|
|
|
|
subject.driver = "bad-driver"
|
2020-08-16 16:27:27 +01:00
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
expect { subject.finalize! }.to raise_error("Require specify driver bad-driver")
|
2020-08-16 16:27:27 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
context 'when invalid @uri is defined' do
|
|
|
|
|
it "should raise exception for unrecognized" do
|
|
|
|
|
subject.uri = "://bad-uri"
|
2020-08-16 16:27:27 +01:00
|
|
|
|
2021-03-13 15:19:41 +00:00
|
|
|
expect { subject.finalize! }.to raise_error("@uri set to invalid uri '://bad-uri'")
|
2020-08-16 16:27:27 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-11-25 15:08:33 +00:00
|
|
|
def assert_invalid
|
2019-11-15 17:11:36 -05:00
|
|
|
subject.finalize!
|
2016-11-25 15:08:33 +00:00
|
|
|
errors = subject.validate(machine)
|
2017-05-16 14:14:30 +01:00
|
|
|
raise "No errors: #{errors.inspect}" if errors.values.all?(&:empty?)
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def assert_valid
|
2019-11-15 17:11:36 -05:00
|
|
|
subject.finalize!
|
2016-11-25 15:08:33 +00:00
|
|
|
errors = subject.validate(machine)
|
2016-12-06 23:20:29 +01:00
|
|
|
raise "Errors: #{errors.inspect}" unless errors.values.all?(&:empty?)
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
describe '#validate' do
|
|
|
|
|
it 'is valid with defaults' do
|
2016-11-25 15:08:33 +00:00
|
|
|
assert_valid
|
|
|
|
|
end
|
|
|
|
|
|
2017-05-16 14:14:30 +01:00
|
|
|
context 'with disks defined' do
|
|
|
|
|
before { expect(machine).to receive(:provider_config).and_return(subject).at_least(:once) }
|
|
|
|
|
|
|
|
|
|
it 'is valid if relative path used for disk' do
|
|
|
|
|
subject.storage :file, path: '../path/to/file.qcow2'
|
|
|
|
|
assert_valid
|
|
|
|
|
end
|
2016-11-25 15:08:33 +00:00
|
|
|
|
2017-05-16 14:14:30 +01:00
|
|
|
it 'should be invalid if absolute path used for disk' do
|
|
|
|
|
subject.storage :file, path: '/absolute/path/to/file.qcow2'
|
|
|
|
|
assert_invalid
|
|
|
|
|
end
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
context 'with mac defined' do
|
|
|
|
|
let (:vm) { double('vm') }
|
2017-05-16 14:14:30 +01:00
|
|
|
before { expect(machine.config).to receive(:vm).and_return(vm) }
|
2016-11-25 15:08:33 +00:00
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
it 'is valid with valid mac' do
|
2017-05-16 14:14:30 +01:00
|
|
|
expect(vm).to receive(:networks).and_return([[:public, { mac: 'aa:bb:cc:dd:ee:ff' }]])
|
2016-11-25 15:08:33 +00:00
|
|
|
assert_valid
|
|
|
|
|
end
|
|
|
|
|
|
2017-05-16 14:20:44 +01:00
|
|
|
it 'is valid with MAC containing no delimiters' do
|
|
|
|
|
network = [:public, { mac: 'aabbccddeeff' }]
|
|
|
|
|
expect(vm).to receive(:networks).and_return([network])
|
|
|
|
|
assert_valid
|
|
|
|
|
expect(network[1][:mac]).to eql('aa:bb:cc:dd:ee:ff')
|
|
|
|
|
end
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
it 'should be invalid if MAC not formatted correctly' do
|
2017-05-16 14:20:44 +01:00
|
|
|
expect(vm).to receive(:networks).and_return([[:public, { mac: 'aa/bb/cc/dd/ee/ff' }]])
|
2016-11-25 15:08:33 +00:00
|
|
|
assert_invalid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
describe '#merge' do
|
2016-11-25 15:08:33 +00:00
|
|
|
let(:one) { described_class.new }
|
|
|
|
|
let(:two) { described_class.new }
|
|
|
|
|
|
|
|
|
|
subject { one.merge(two) }
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
context 'storage' do
|
|
|
|
|
context 'with disks' do
|
|
|
|
|
context 'assigned specific devices' do
|
|
|
|
|
it 'should merge disks with specific devices' do
|
|
|
|
|
one.storage(:file, device: 'vdb')
|
|
|
|
|
two.storage(:file, device: 'vdc')
|
2016-11-25 15:08:33 +00:00
|
|
|
subject.finalize!
|
2016-12-06 23:20:29 +01:00
|
|
|
expect(subject.disks).to include(include(device: 'vdb'),
|
|
|
|
|
include(device: 'vdc'))
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
context 'without devices given' do
|
|
|
|
|
it 'should merge disks with different devices assigned automatically' do
|
2016-11-25 15:08:33 +00:00
|
|
|
one.storage(:file)
|
|
|
|
|
two.storage(:file)
|
|
|
|
|
subject.finalize!
|
2016-12-06 23:20:29 +01:00
|
|
|
expect(subject.disks).to include(include(device: 'vdb'),
|
|
|
|
|
include(device: 'vdc'))
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
context 'with cdroms only' do
|
|
|
|
|
context 'assigned specific devs' do
|
|
|
|
|
it 'should merge disks with specific devices' do
|
|
|
|
|
one.storage(:file, device: :cdrom, dev: 'hda')
|
|
|
|
|
two.storage(:file, device: :cdrom, dev: 'hdb')
|
2016-11-25 15:08:33 +00:00
|
|
|
subject.finalize!
|
2016-12-06 23:20:29 +01:00
|
|
|
expect(subject.cdroms).to include(include(dev: 'hda'),
|
|
|
|
|
include(dev: 'hdb'))
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-12-06 23:20:29 +01:00
|
|
|
context 'without devs given' do
|
|
|
|
|
it 'should merge cdroms with different devs assigned automatically' do
|
|
|
|
|
one.storage(:file, device: :cdrom)
|
|
|
|
|
two.storage(:file, device: :cdrom)
|
2016-11-25 15:08:33 +00:00
|
|
|
subject.finalize!
|
2016-12-06 23:20:29 +01:00
|
|
|
expect(subject.cdroms).to include(include(dev: 'hda'),
|
|
|
|
|
include(dev: 'hdb'))
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-12-16 20:19:24 +01:00
|
|
|
|
|
|
|
|
context 'clock_timers' do
|
|
|
|
|
it 'should merge clock_timers' do
|
|
|
|
|
one.clock_timer(:name => 'rtc', :tickpolicy => 'catchup')
|
|
|
|
|
two.clock_timer(:name => 'hpet', :present => 'no')
|
|
|
|
|
|
|
|
|
|
expect(subject.clock_timers).to include(include(name: 'rtc'),
|
|
|
|
|
include(name: 'hpet'))
|
|
|
|
|
end
|
|
|
|
|
end
|
2016-11-25 15:08:33 +00:00
|
|
|
end
|
|
|
|
|
end
|