mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2025-02-25 18:55:27 -06:00
Move the unit context to a name matching the other contexts. Remove some unnecessary entries from it that are unused, and remove references to the old name relying on spec helper to load all contexts.
438 lines
15 KiB
Ruby
438 lines
15 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'fog/libvirt/requests/compute/dhcp_leases'
|
|
|
|
require 'spec_helper'
|
|
require 'support/binding_proc'
|
|
|
|
require 'vagrant-libvirt/driver'
|
|
|
|
describe VagrantPlugins::ProviderLibvirt::Driver do
|
|
include_context 'unit'
|
|
include_context 'libvirt'
|
|
|
|
subject { described_class.new(machine) }
|
|
|
|
# make it easier for distros that want to switch the default value for
|
|
# qemu_use_session to true by ensuring it is explicitly false for tests.
|
|
before do
|
|
allow(machine.provider_config).to receive(:qemu_use_session).and_return(false)
|
|
allow(logger).to receive(:info)
|
|
allow(logger).to receive(:debug)
|
|
end
|
|
|
|
describe 'connections' do
|
|
let(:vagrantfile) do
|
|
<<-EOF
|
|
Vagrant.configure('2') do |config|
|
|
config.vm.define :test1 do |node|
|
|
node.vm.provider :libvirt do |domain|
|
|
domain.uri = "qemu+ssh://user@remote1/system"
|
|
end
|
|
end
|
|
config.vm.define :test2 do |node|
|
|
node.vm.provider :libvirt do |domain|
|
|
domain.uri = "qemu+ssh://vms@remote2/system"
|
|
end
|
|
end
|
|
end
|
|
EOF
|
|
end
|
|
|
|
# need to override the default package iso_env as using a different
|
|
# name for the test machines above.
|
|
let(:machine) { iso_env.machine(:test1, :libvirt) }
|
|
let(:machine2) { iso_env.machine(:test2, :libvirt) }
|
|
let(:connection1) { double("connection 1") }
|
|
let(:connection2) { double("connection 2") }
|
|
let(:system_connection1) { double("system connection 1") }
|
|
let(:system_connection2) { double("system connection 2") }
|
|
|
|
# make it easier for distros that want to switch the default value for
|
|
# qemu_use_session to true by ensuring it is explicitly false for tests.
|
|
before do
|
|
allow(machine.provider).to receive('driver').and_call_original
|
|
allow(machine2.provider).to receive('driver').and_call_original
|
|
end
|
|
|
|
describe '#connection' do
|
|
it 'should configure a separate connection per machine' do
|
|
expect(Fog::Compute).to receive(:new).with(
|
|
hash_including({:libvirt_uri => 'qemu+ssh://user@remote1/system'})).and_return(connection1)
|
|
expect(Fog::Compute).to receive(:new).with(
|
|
hash_including({:libvirt_uri => 'qemu+ssh://vms@remote2/system'})).and_return(connection2)
|
|
|
|
expect(machine.provider.driver.connection).to eq(connection1)
|
|
expect(machine2.provider.driver.connection).to eq(connection2)
|
|
end
|
|
|
|
it 'should configure the connection once' do
|
|
expect(Fog::Compute).to receive(:new).once().and_return(connection1)
|
|
|
|
expect(machine.provider.driver.connection).to eq(connection1)
|
|
expect(machine.provider.driver.connection).to eq(connection1)
|
|
expect(machine.provider.driver.connection).to eq(connection1)
|
|
end
|
|
end
|
|
|
|
describe '#system_connection' do
|
|
# note that the urls for the two tests are currently
|
|
# incorrect here as they should be the following:
|
|
# qemu+ssh://user@remote1/system
|
|
# qemu+ssh://vms@remote2/system
|
|
#
|
|
# In that the system uri should be resolved based on
|
|
# the provider uri so that for:
|
|
# uri => qemu+ssh://user@remote1/session
|
|
# system_uri should be 'qemu+ssh://user@remote1/system'
|
|
# and not 'qemu:///system'.
|
|
it 'should configure a separate connection per machine' do
|
|
expect(Libvirt).to receive(:open_read_only).with('qemu+ssh://user@remote1/system').and_return(system_connection1)
|
|
expect(Libvirt).to receive(:open_read_only).with('qemu+ssh://vms@remote2/system').and_return(system_connection2)
|
|
|
|
expect(machine.provider.driver.system_connection).to eq(system_connection1)
|
|
expect(machine2.provider.driver.system_connection).to eq(system_connection2)
|
|
end
|
|
|
|
it 'should configure the connection once' do
|
|
expect(Libvirt).to receive(:open_read_only).with('qemu+ssh://user@remote1/system').and_return(system_connection1)
|
|
|
|
expect(machine.provider.driver.system_connection).to eq(system_connection1)
|
|
expect(machine.provider.driver.system_connection).to eq(system_connection1)
|
|
expect(machine.provider.driver.system_connection).to eq(system_connection1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#get_ipaddress' do
|
|
context 'when domain exists' do
|
|
# not used yet, but this is the form that is returned from addresses
|
|
let(:addresses) { {
|
|
:public => ["192.168.122.111"],
|
|
:private => ["192.168.122.111"],
|
|
} }
|
|
|
|
before do
|
|
allow(subject).to receive(:get_domain).and_return(domain)
|
|
end
|
|
|
|
it 'should retrieve the address via domain fog-libvirt API' do
|
|
# ideally should be able to yield a block to wait_for and check that
|
|
# the 'addresses' function on the domain is called correctly.
|
|
expect(domain).to receive(:wait_for).and_return(nil)
|
|
expect(subject.get_ipaddress).to eq(nil)
|
|
end
|
|
|
|
context 'when qemu_use_agent is enabled' do
|
|
let(:qemu_agent_interfaces) {
|
|
<<-EOF
|
|
{
|
|
"return": [
|
|
{
|
|
"name": "lo",
|
|
"ip-addresses": [
|
|
{
|
|
"ip-address-type": "ipv4",
|
|
"ip-address": "127.0.0.1",
|
|
"prefix": 8
|
|
}
|
|
],
|
|
"hardware-address": "00:00:00:00:00:00"
|
|
},
|
|
{
|
|
"name": "eth0",
|
|
"ip-addresses": [
|
|
{
|
|
"ip-address-type": "ipv4",
|
|
"ip-address": "192.168.122.42",
|
|
"prefix": 24
|
|
}
|
|
],
|
|
"hardware-address": "52:54:00:f8:67:98"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
}
|
|
|
|
before do
|
|
allow(machine.provider_config).to receive(:qemu_use_agent).and_return(true)
|
|
end
|
|
|
|
it 'should retrieve the address via the agent' do
|
|
expect(subject).to receive(:connection).and_return(connection)
|
|
expect(libvirt_client).to receive(:lookup_domain_by_uuid).and_return(libvirt_domain)
|
|
expect(libvirt_domain).to receive(:qemu_agent_command).and_return(qemu_agent_interfaces)
|
|
expect(domain).to receive(:mac).and_return("52:54:00:f8:67:98").exactly(2).times
|
|
|
|
expect(subject.get_ipaddress).to eq("192.168.122.42")
|
|
end
|
|
|
|
context 'when qemu_use_session is enabled' do
|
|
before do
|
|
allow(machine.provider_config).to receive(:qemu_use_session).and_return(true)
|
|
end
|
|
|
|
it 'should still retrieve the address via the agent' do
|
|
expect(subject).to receive(:connection).and_return(connection)
|
|
expect(libvirt_client).to receive(:lookup_domain_by_uuid).and_return(libvirt_domain)
|
|
expect(libvirt_domain).to receive(:qemu_agent_command).and_return(qemu_agent_interfaces)
|
|
expect(domain).to receive(:mac).and_return("52:54:00:f8:67:98").exactly(2).times
|
|
|
|
expect(subject.get_ipaddress).to eq("192.168.122.42")
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when qemu_use_session is enabled' do
|
|
let(:system_connection) { double("system connection") }
|
|
let(:networks) { [instance_double(::Libvirt::Network)] }
|
|
let(:dhcp_leases) {
|
|
{
|
|
"iface" =>"virbr0",
|
|
"expirytime" =>1636287162,
|
|
"type" =>0,
|
|
"mac" =>"52:54:00:8b:dc:5f",
|
|
"ipaddr" =>"192.168.122.43",
|
|
"prefix" =>24,
|
|
"hostname" =>"vagrant-default_test",
|
|
"clientid" =>"ff:00:8b:dc:5f:00:01:00:01:29:1a:65:42:52:54:00:8b:dc:5f",
|
|
}
|
|
}
|
|
|
|
before do
|
|
allow(machine.provider_config).to receive(:qemu_use_session).and_return(true)
|
|
end
|
|
|
|
it 'should retrieve the address via the system dhcp-leases API' do
|
|
expect(domain).to receive(:mac).and_return("52:54:00:8b:dc:5f")
|
|
expect(subject).to receive(:list_all_networks).and_return(networks)
|
|
expect(networks[0]).to receive(:dhcp_leases).and_return([dhcp_leases])
|
|
|
|
expect(subject.get_ipaddress).to eq("192.168.122.43")
|
|
end
|
|
|
|
context 'when qemu_use_agent is enabled' do
|
|
before do
|
|
allow(machine.provider_config).to receive(:qemu_use_agent).and_return(true)
|
|
end
|
|
|
|
it 'should retrieve the address via the agent' do
|
|
expect(subject).to receive(:get_ipaddress_from_qemu_agent).and_return("192.168.122.44")
|
|
|
|
expect(subject.get_ipaddress).to eq("192.168.122.44")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
describe '#list_all_networks' do
|
|
let(:vagrantfile_providerconfig) do
|
|
<<-EOF
|
|
libvirt.uri = "qemu:///system"
|
|
EOF
|
|
end
|
|
|
|
let(:libvirt_networks) { [
|
|
instance_double(::Libvirt::Network),
|
|
instance_double(::Libvirt::Network),
|
|
instance_double(::Libvirt::Network),
|
|
] }
|
|
|
|
before do
|
|
allow(subject).to receive(:system_connection).and_return(libvirt_client)
|
|
expect(libvirt_client).to receive(:list_all_networks).and_return(libvirt_networks)
|
|
end
|
|
|
|
it 'should list networks' do
|
|
expect(libvirt_networks[0]).to receive(:bridge_name).and_return('')
|
|
expect(libvirt_networks[1]).to receive(:bridge_name).and_return('virbr0')
|
|
expect(libvirt_networks[2]).to receive(:bridge_name).and_return('virbr1')
|
|
|
|
expect(subject.list_all_networks).to eq(libvirt_networks)
|
|
end
|
|
|
|
it 'should skip networks missing bridge_name' do
|
|
expect(libvirt_networks[0]).to receive(:bridge_name).and_return('')
|
|
expect(libvirt_networks[1]).to receive(:bridge_name).and_raise(Libvirt::Error)
|
|
expect(libvirt_networks[1]).to receive(:name).and_return('bad_network')
|
|
expect(libvirt_networks[2]).to receive(:bridge_name).and_return('virbr1')
|
|
|
|
expect(subject.list_all_networks).to eq([libvirt_networks[0], libvirt_networks[2]])
|
|
end
|
|
end
|
|
|
|
describe '#host_devices' do
|
|
let(:vagrantfile_providerconfig) do
|
|
<<-EOF
|
|
libvirt.uri = "qemu:///system"
|
|
EOF
|
|
end
|
|
|
|
let(:ip_link_show) {
|
|
JSON.dump(
|
|
[
|
|
# trimmed element details of what would be returned by 'ip -j link show'
|
|
{ "ifindex": 1, "ifname": "lo", "group": "default", "link_type": "loopback"},
|
|
{ "ifindex": 2, "ifname": "eth0", "group": "default", "link_type": "ether"},
|
|
{ "ifindex": 3, "ifname": "eth1", "group": "default", "link_type": "ether"},
|
|
{ "ifindex": 4, "ifname": "virbr0", "group": "default", "link_type": "ether"},
|
|
]
|
|
)
|
|
}
|
|
|
|
let(:libvirt_interfaces) { [
|
|
instance_double(Libvirt::Interface),
|
|
instance_double(Libvirt::Interface),
|
|
] }
|
|
let(:libvirt_networks) { [
|
|
instance_double(Libvirt::Network),
|
|
instance_double(Libvirt::Network),
|
|
] }
|
|
|
|
before do
|
|
allow(subject).to receive(:connection).and_return(connection)
|
|
|
|
allow(Vagrant::Util::Subprocess).to receive(:execute) do |*arr|
|
|
expect(arr[0]).to eq('ip')
|
|
end.and_return(Vagrant::Util::Subprocess::Result.new(exit_code=0, stdout=ip_link_show, stderr=''))
|
|
|
|
expect(libvirt_client).to receive(:list_all_interfaces).and_return(libvirt_interfaces)
|
|
expect(subject).to receive(:list_all_networks).and_return(libvirt_networks)
|
|
expect(libvirt_interfaces[0]).to receive(:name).and_return('eth0')
|
|
expect(libvirt_interfaces[1]).to receive(:name).and_return('virbr0')
|
|
expect(libvirt_networks[0]).to receive(:bridge_name).and_return('')
|
|
expect(libvirt_networks[1]).to receive(:bridge_name).and_return('virbr0')
|
|
end
|
|
|
|
it 'should query system and libvirt' do
|
|
expect(subject.host_devices).to eq(['lo', 'eth0', 'eth1', 'virbr0'])
|
|
end
|
|
|
|
it 'should handle empty string' do
|
|
expect(machine.provider_config).to receive(:proxy_command).and_return('').twice
|
|
|
|
expect(subject.host_devices).to eq(['lo', 'eth0', 'eth1', 'virbr0'])
|
|
end
|
|
|
|
it 'should cache the result' do
|
|
expect(machine.provider_config).to receive(:proxy_command).and_return(nil).once
|
|
|
|
expect(subject.host_devices).to eq(['lo', 'eth0', 'eth1', 'virbr0'])
|
|
expect(subject.host_devices).to eq(['lo', 'eth0', 'eth1', 'virbr0'])
|
|
end
|
|
|
|
context 'when libvirt is remote' do
|
|
let(:vagrantfile_providerconfig) do
|
|
<<-EOF
|
|
libvirt.uri = "qemu+ssh://remote-server/system"
|
|
EOF
|
|
end
|
|
|
|
before do
|
|
allow(machine.provider_config).to receive(:proxy_command).and_return('ssh remote-server -W %h:%p')
|
|
end
|
|
|
|
it 'should use ssh for ip link' do
|
|
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*arr|
|
|
expect(arr[0..3]).to eq(['ssh', 'remote-server', 'ip', '-j'])
|
|
end.and_return(Vagrant::Util::Subprocess::Result.new(exit_code=0, stdout=ip_link_show, stderr=''))
|
|
|
|
expect(subject.host_devices).to eq(['lo', 'eth0', 'eth1', 'virbr0'])
|
|
end
|
|
|
|
it 'should construct the ssh command with all options when needed' do
|
|
machine.provider_config.port = 2022
|
|
machine.provider_config.username = 'remote-user'
|
|
machine.provider_config.id_ssh_key_file = 'my-key-file'
|
|
|
|
expect(Vagrant::Util::Subprocess).to receive(:execute) do |*arr|
|
|
expect(arr[0..9]).to eq(['ssh', 'remote-server', '-p', '2022', '-l', 'remote-user', '-i', 'my-key-file', 'ip', '-j'])
|
|
end.and_return(Vagrant::Util::Subprocess::Result.new(exit_code=0, stdout=ip_link_show, stderr=''))
|
|
|
|
expect(subject.host_devices).to eq(['lo', 'eth0', 'eth1', 'virbr0'])
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#state' do
|
|
let(:domain) { double('domain') }
|
|
|
|
before do
|
|
allow(subject).to receive(:get_domain).and_return(domain)
|
|
end
|
|
|
|
[
|
|
[
|
|
'not found',
|
|
:not_created,
|
|
{
|
|
:setup => ProcWithBinding.new do
|
|
expect(subject).to receive(:get_domain).and_return(nil)
|
|
end,
|
|
}
|
|
],
|
|
[
|
|
'libvirt error',
|
|
:not_created,
|
|
{
|
|
:setup => ProcWithBinding.new do
|
|
expect(subject).to receive(:get_domain).and_raise(Libvirt::RetrieveError, 'missing')
|
|
end,
|
|
}
|
|
],
|
|
[
|
|
nil,
|
|
:unknown,
|
|
{
|
|
:setup => ProcWithBinding.new do
|
|
expect(domain).to receive(:state).and_return('unknown').at_least(:once)
|
|
end,
|
|
}
|
|
],
|
|
[
|
|
'terminated',
|
|
:not_created,
|
|
{
|
|
:setup => ProcWithBinding.new do
|
|
expect(domain).to receive(:state).and_return('terminated').at_least(:once)
|
|
end,
|
|
}
|
|
],
|
|
[
|
|
'no IP returned',
|
|
:inaccessible,
|
|
{
|
|
:setup => ProcWithBinding.new do
|
|
expect(domain).to receive(:state).and_return('running').at_least(:once)
|
|
expect(subject).to receive(:get_ipaddress).and_raise(Fog::Errors::TimeoutError)
|
|
end,
|
|
}
|
|
],
|
|
[
|
|
'running',
|
|
:running,
|
|
{
|
|
:setup => ProcWithBinding.new do
|
|
expect(domain).to receive(:state).and_return('running').at_least(:once)
|
|
expect(subject).to receive(:get_ipaddress).and_return('192.168.121.2')
|
|
end,
|
|
}
|
|
],
|
|
].each do |name, expected, options|
|
|
opts = {}
|
|
opts.merge!(options) if options
|
|
|
|
it "should handle '#{name}' by returning '#{expected}'" do
|
|
if !opts[:setup].nil?
|
|
opts[:setup].apply_binding(binding)
|
|
end
|
|
|
|
expect(subject.state).to eq(expected)
|
|
end
|
|
end
|
|
end
|
|
end
|