vagrant-libvirt 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,6 +9,10 @@ module VagrantPlugins
9
9
  error_namespace('vagrant_libvirt.errors')
10
10
  end
11
11
 
12
+ class CallChainError < VagrantLibvirtError
13
+ error_key(:call_chain_error)
14
+ end
15
+
12
16
  # package not supported
13
17
  class PackageNotSupported < VagrantLibvirtError
14
18
  error_key(:package_not_supported)
@@ -177,6 +181,10 @@ module VagrantPlugins
177
181
  class DeleteSnapshotError < VagrantLibvirtError
178
182
  error_key(:delete_snapshot_error)
179
183
  end
184
+
185
+ class SerialCannotCreatePathError < VagrantLibvirtError
186
+ error_key(:serial_cannot_create_path_error)
187
+ end
180
188
  end
181
189
  end
182
190
  end
@@ -162,10 +162,19 @@
162
162
  </disk>
163
163
  <% end %>
164
164
 
165
- <serial type='pty'>
166
- <target port='0'/>
165
+ <% @serials.each_with_index do |serial, port| %>
166
+ <serial type='<%= serial[:type] %>'>
167
+ <% unless serial[:source].nil? %>
168
+ <source path='<%= serial[:source][:path] %>'/>
169
+ <% end %>
170
+ <target port='<%= port %>'/>
167
171
  </serial>
168
- <console type='pty'>
172
+ <% end %>
173
+ <% console_log = @serials.first %>
174
+ <console type='<%= console_log[:type] %>'>
175
+ <% unless console_log[:source].nil? %>
176
+ <source path='<%= console_log[:source][:path] %>'/>
177
+ <% end %>
169
178
  <target port='0'/>
170
179
  </console>
171
180
 
@@ -207,9 +216,13 @@
207
216
  <% end %>
208
217
  <% if @graphics_type != 'none' %>
209
218
  <%# Video device -%>
210
- <graphics type='<%= @graphics_type %>' port='<%= @graphics_port %>' autoport='<%= @graphics_autoport %>' listen='<%= @graphics_ip %>' keymap='<%= @keymap %>' <%= @graphics_passwd%> />
219
+ <graphics type='<%= @graphics_type %>' port='<%= @graphics_port %>' autoport='<%= @graphics_autoport %>' listen='<%= @graphics_ip %>' keymap='<%= @keymap %>' <%= @graphics_passwd %> <% if not @graphics_gl %>/><% else %>>
220
+ <gl enable='yes' />
221
+ </graphics><% end %>
211
222
  <video>
212
- <model type='<%= @video_type %>' vram='<%= @video_vram %>' heads='1'/>
223
+ <model type='<%= @video_type %>' vram='<%= @video_vram %>' heads='1'<% if not @video_accel3d %>/><% else %>>
224
+ <acceleration accel3d='yes'/>
225
+ </model><% end %>
213
226
  </video>
214
227
  <%#End Video -%>
215
228
  <% end %>
@@ -119,7 +119,8 @@ module VagrantPlugins
119
119
  IPAddr.new(options[:network_address]).get_mask :
120
120
  '255.255.255.0',
121
121
  dhcp_enabled: true,
122
- forward_mode: 'nat'
122
+ forward_mode: 'nat',
123
+ always_destroy: true
123
124
  }.merge(options)
124
125
 
125
126
  if options[:type].to_s == 'dhcp' && options[:ip].nil?
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'vagrant/action/builtin/mixin_synced_folders'
4
+
3
5
  module VagrantPlugins
4
6
  module ProviderLibvirt
5
7
  module Util
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
data/locales/en.yml CHANGED
@@ -15,6 +15,10 @@ en:
15
15
  manual_resize_required: |-
16
16
  Created volume larger than box defaults, will require manual resizing of
17
17
  filesystems to utilize.
18
+ box_version_missing: |-
19
+ No verison detected for %{name}, using timestamp to watch for modifications. Consider
20
+ generating a local metadata for the box with a version to allow better handling.
21
+ See https://www.vagrantup.com/docs/boxes/format#box-metadata for further details.
18
22
  uploading_volume: |-
19
23
  Uploading base box image as volume into Libvirt storage...
20
24
  creating_domain_volume: |-
@@ -60,8 +64,11 @@ en:
60
64
  the minimum box image size of '%{minimum}'.
61
65
  forwarding_udp: |-
62
66
  Forwarding UDP ports is not supported. Ignoring.
67
+ creating_domain_console_access_disabled: |-
68
+ Serial console is being redirected, access via virsh will be disabled.
63
69
 
64
70
  errors:
71
+ call_chain_error: Invalid action chain, must ensure that '%{require_action}' is called prior to calling '%{current_action}'
65
72
  package_not_supported: No support for package with Libvirt. Create box manually.
66
73
  fog_error: |-
67
74
  There was an error talking to Libvirt. The error message is shown
@@ -161,6 +168,8 @@ en:
161
168
  management_network_required: |-
162
169
  Management network can't be disabled when VM use box.
163
170
  Please fix your configuration and run vagrant again.
171
+ serial_cannot_create_path_error: |-
172
+ Error creating path for serial port output log: %{path}
164
173
 
165
174
  states:
166
175
  paused: |-
data/spec/spec_helper.rb CHANGED
@@ -42,4 +42,12 @@ RSpec.configure do |config|
42
42
  config.before(:suite) do
43
43
  ENV.delete('LIBVIRT_DEFAULT_URI')
44
44
  end
45
+
46
+ config.mock_with :rspec do |mocks|
47
+ # This option should be set when all dependencies are being loaded
48
+ # before a spec run, as is the case in a typical spec helper. It will
49
+ # cause any verifying double instantiation for a class that does not
50
+ # exist to raise, protecting against incorrectly spelt names.
51
+ mocks.verify_doubled_constant_names = true
52
+ end
45
53
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'fog/libvirt'
4
+ require 'fog/libvirt/models/compute/server'
5
+ require 'libvirt'
4
6
 
5
7
  shared_context 'libvirt' do
6
8
  include_context 'unit'
@@ -8,7 +10,9 @@ shared_context 'libvirt' do
8
10
  let(:libvirt_context) { true }
9
11
  let(:id) { 'dummy-vagrant_dummy' }
10
12
  let(:connection) { double('connection') }
11
- let(:domain) { double('domain') }
13
+ let(:domain) { instance_double('::Fog::Libvirt::Compute::Server') }
14
+ let(:libvirt_client) { instance_double('::Libvirt::Connect') }
15
+ let(:libvirt_domain) { instance_double('::Libvirt::Domain') }
12
16
  let(:logger) { double('logger') }
13
17
 
14
18
  def connection_result(options = {})
@@ -22,11 +26,10 @@ shared_context 'libvirt' do
22
26
  stub_const('::Fog::Compute', connection)
23
27
 
24
28
  # drivers also call vm_exists? during init;
25
- allow(connection).to receive(:servers).with(kind_of(String))
29
+ allow(connection).to receive(:servers)
26
30
  .and_return(connection_result(result: nil))
27
31
 
28
- # return some information for domain when needed
29
- allow(domain).to receive(:mac).and_return('9C:D5:53:F1:5A:E7')
32
+ allow(connection).to receive(:client).and_return(libvirt_client)
30
33
 
31
34
  allow(machine).to receive(:id).and_return(id)
32
35
  allow(Log4r::Logger).to receive(:new).and_return(logger)
@@ -36,6 +36,6 @@ shared_context 'unit' do
36
36
 
37
37
  before (:each) do
38
38
  allow(machine).to receive(:guest).and_return(guest)
39
- allow(machine).to receive(:communicator).and_return(communicator)
39
+ allow(machine).to receive(:communicate).and_return(communicator)
40
40
  end
41
41
  end
@@ -98,6 +98,36 @@ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do
98
98
  )
99
99
  end
100
100
 
101
+ context 'when no box version set' do
102
+ let(:box_mtime) { Time.now }
103
+
104
+ before do
105
+ expect(env[:machine]).to receive_message_chain("box.version") { nil }
106
+ expect(File).to receive(:mtime).and_return(box_mtime)
107
+ end
108
+
109
+ it 'should use the box file timestamp' do
110
+ expect(ui).to receive(:warn).with(
111
+ "No verison detected for test, using timestamp to watch for modifications. Consider\n" +
112
+ "generating a local metadata for the box with a version to allow better handling.\n" +
113
+ 'See https://www.vagrantup.com/docs/boxes/format#box-metadata for further details.'
114
+ )
115
+
116
+ expect(subject.call(env)).to be_nil
117
+ expect(env[:box_volume_number]).to eq(1)
118
+ expect(env[:box_volumes]).to eq(
119
+ [
120
+ {
121
+ :path=>"/test/box.img",
122
+ :name=>"test_vagrant_box_image_0_#{box_mtime.to_i}_box.img",
123
+ :virtual_size=>byte_number_5G,
124
+ :format=>"qcow2"
125
+ }
126
+ ]
127
+ )
128
+ end
129
+ end
130
+
101
131
  context 'When config.machine_virtual_size is set and smaller than box_virtual_size' do
102
132
  before do
103
133
  allow(env[:machine]).to receive_message_chain("provider_config.machine_virtual_size").and_return(1)
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'support/sharedcontext'
5
+
6
+ require 'vagrant-libvirt/action/prepare_nfs_settings'
7
+
8
+
9
+ describe VagrantPlugins::ProviderLibvirt::Action::PrepareNFSSettings do
10
+ subject { described_class.new(app, env) }
11
+
12
+ include_context 'unit'
13
+
14
+ describe '#call' do
15
+ before do
16
+ # avoid requiring nfsd installed to run tests
17
+ allow(machine.env.host).to receive(:capability?).with(:nfs_installed).and_return(true)
18
+ allow(machine.env.host).to receive(:capability).with(:nfs_installed).and_return(true)
19
+ end
20
+
21
+ context 'when enabled' do
22
+ let(:vagrantfile) do
23
+ <<-EOF
24
+ Vagrant.configure('2') do |config|
25
+ config.vm.box = "vagrant-libvirt/test"
26
+ config.vm.define :test
27
+ config.vm.synced_folder ".", "/vagrant", type: "nfs"
28
+ config.vm.provider :libvirt do |libvirt|
29
+ #{vagrantfile_providerconfig}
30
+ end
31
+ end
32
+ EOF
33
+ end
34
+ let(:socket) { double('socket') }
35
+
36
+ before do
37
+ allow(::TCPSocket).to receive(:new).and_return(socket)
38
+ allow(socket).to receive(:close)
39
+ end
40
+
41
+ it 'should retrieve the guest IP address' do
42
+ times_called = 0
43
+ expect(::TCPSocket).to receive(:new) do
44
+ # force reaching later code
45
+ times_called += 1
46
+ times_called < 2 ? raise("StandardError") : socket
47
+ end
48
+ expect(machine).to receive(:ssh_info).and_return({:host => '192.168.1.2'})
49
+ expect(communicator).to receive(:execute).and_yield(:stdout, "192.168.1.2\n192.168.2.2")
50
+
51
+ expect(subject.call(env)).to be_nil
52
+ end
53
+ end
54
+ end
55
+ end
@@ -3,6 +3,20 @@ require 'support/sharedcontext'
3
3
  require 'support/libvirt_context'
4
4
  require 'vagrant-libvirt/action/shutdown_domain'
5
5
 
6
+ describe VagrantPlugins::ProviderLibvirt::Action::StartShutdownTimer do
7
+ subject { described_class.new(app, env) }
8
+
9
+ include_context 'unit'
10
+
11
+ describe '#call' do
12
+ it 'should set shutdown_start_time' do
13
+ expect(env[:shutdown_start_time]).to eq(nil)
14
+ expect(subject.call(env)).to eq(nil)
15
+ expect(env[:shutdown_start_time]).to_not eq(nil)
16
+ end
17
+ end
18
+ end
19
+
6
20
  describe VagrantPlugins::ProviderLibvirt::Action::ShutdownDomain do
7
21
  subject { described_class.new(app, env, target_state, current_state) }
8
22
 
@@ -26,6 +40,8 @@ describe VagrantPlugins::ProviderLibvirt::Action::ShutdownDomain do
26
40
  allow(connection).to receive(:servers).and_return(servers)
27
41
  allow(servers).to receive(:get).and_return(domain)
28
42
  allow(ui).to receive(:info).with('Attempting direct shutdown of domain...')
43
+ allow(env).to receive(:[]).and_call_original
44
+ allow(env).to receive(:[]).with(:shutdown_start_time).and_return(Time.now)
29
45
  end
30
46
 
31
47
  context "when state is shutoff" do
@@ -34,7 +50,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::ShutdownDomain do
34
50
  end
35
51
 
36
52
  it "should not shutdown" do
37
- expect(domain).not_to receive(:shutoff)
53
+ expect(domain).not_to receive(:poweroff)
38
54
  subject.call(env)
39
55
  end
40
56
 
@@ -97,7 +113,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::ShutdownDomain do
97
113
  context "when timeout exceeded" do
98
114
  before do
99
115
  expect(machine).to receive_message_chain('config.vm.graceful_halt_timeout').and_return(1)
100
- expect(app).to receive(:call) { sleep 1.5 }
116
+ expect(Time).to receive(:now).and_return(env[:shutdown_start_time] + 2)
101
117
  expect(driver).to receive(:state).and_return(:running).exactly(1).times
102
118
  expect(domain).to_not receive(:wait_for)
103
119
  expect(domain).to_not receive(:shutdown)
@@ -112,7 +128,7 @@ describe VagrantPlugins::ProviderLibvirt::Action::ShutdownDomain do
112
128
  context "when timeout not exceeded" do
113
129
  before do
114
130
  expect(machine).to receive_message_chain('config.vm.graceful_halt_timeout').and_return(2)
115
- expect(app).to receive(:call) { sleep 1 }
131
+ expect(Time).to receive(:now).and_return(env[:shutdown_start_time] + 1.5)
116
132
  expect(driver).to receive(:state).and_return(:running).exactly(3).times
117
133
  expect(domain).to receive(:wait_for) do |time|
118
134
  expect(time).to be < 1
@@ -127,5 +143,18 @@ describe VagrantPlugins::ProviderLibvirt::Action::ShutdownDomain do
127
143
  end
128
144
  end
129
145
  end
146
+
147
+ context "when required action not run" do
148
+ before do
149
+ expect(env).to receive(:[]).with(:shutdown_start_time).and_call_original
150
+ end
151
+
152
+ it "should raise an exception" do
153
+ expect { subject.call(env) }.to raise_error(
154
+ VagrantPlugins::ProviderLibvirt::Errors::CallChainError,
155
+ /Invalid action chain, must ensure that '.*ShutdownTimer' is called prior to calling '.*ShutdownDomain'/
156
+ )
157
+ end
158
+ end
130
159
  end
131
160
  end
@@ -22,6 +22,8 @@ describe VagrantPlugins::ProviderLibvirt::Action::WaitTillUp do
22
22
  .and_return(driver)
23
23
  allow(driver).to receive(:get_domain).and_return(domain)
24
24
  allow(driver).to receive(:state).and_return(:running)
25
+ # return some information for domain when needed
26
+ allow(domain).to receive(:mac).and_return('9C:D5:53:F1:5A:E7')
25
27
  end
26
28
 
27
29
  context 'when machine does not exist' do
@@ -62,7 +64,6 @@ describe VagrantPlugins::ProviderLibvirt::Action::WaitTillUp do
62
64
  expect(app).to_not receive(:call)
63
65
  expect(ui).to receive(:info).with('Waiting for domain to get an IP address...')
64
66
  expect(ui).to_not receive(:info).with('Waiting for SSH to become available...')
65
- expect(env[:machine].communicate).to_not receive(:ready?)
66
67
  expect {subject.call(env) }.to raise_error(::Fog::Errors::TimeoutError)
67
68
  end
68
69
  end
@@ -93,4 +93,82 @@ describe VagrantPlugins::ProviderLibvirt::Action do
93
93
  end
94
94
  end
95
95
  end
96
+
97
+ describe '#action_ssh' do
98
+ context 'when not created' do
99
+ before do
100
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsCreated, false)
101
+ end
102
+
103
+ it 'should cause an error' do
104
+ expect{ machine.action(:ssh, ssh_opts: {})}.to raise_error(Vagrant::Errors::VMNotCreatedError)
105
+ end
106
+ end
107
+
108
+ context 'when created' do
109
+ before do
110
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsCreated, true)
111
+ end
112
+
113
+ context 'when not running' do
114
+ before do
115
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsRunning, false)
116
+ end
117
+
118
+ it 'should cause an error' do
119
+ expect{ machine.action(:ssh, ssh_opts: {})}.to raise_error(Vagrant::Errors::VMNotRunningError)
120
+ end
121
+ end
122
+
123
+ context 'when running' do
124
+ before do
125
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsRunning, true)
126
+ end
127
+
128
+ it 'should call SSHExec' do
129
+ expect_any_instance_of(Vagrant::Action::Builtin::SSHExec).to receive(:call).and_return(0)
130
+ expect(machine.action(:ssh, ssh_opts: {})).to match(hash_including({:action_name => :machine_action_ssh}))
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ describe '#action_ssh_run' do
137
+ context 'when not created' do
138
+ before do
139
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsCreated, false)
140
+ end
141
+
142
+ it 'should cause an error' do
143
+ expect{ machine.action(:ssh_run, ssh_opts: {})}.to raise_error(Vagrant::Errors::VMNotCreatedError)
144
+ end
145
+ end
146
+
147
+ context 'when created' do
148
+ before do
149
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsCreated, true)
150
+ end
151
+
152
+ context 'when not running' do
153
+ before do
154
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsRunning, false)
155
+ end
156
+
157
+ it 'should cause an error' do
158
+ expect{ machine.action(:ssh_run, ssh_opts: {})}.to raise_error(Vagrant::Errors::VMNotRunningError)
159
+ end
160
+ end
161
+
162
+ context 'when running' do
163
+ before do
164
+ allow_action_env_result(VagrantPlugins::ProviderLibvirt::Action::IsRunning, true)
165
+ end
166
+
167
+ it 'should call SSHRun' do
168
+ expect_any_instance_of(Vagrant::Action::Builtin::SSHRun).to receive(:call).and_return(0)
169
+ expect(machine.action(:ssh_run, ssh_opts: {})).to match(hash_including({:action_name => :machine_action_ssh_run}))
170
+ end
171
+ end
172
+ end
173
+ end
96
174
  end
@@ -338,6 +338,54 @@ describe VagrantPlugins::ProviderLibvirt::Config do
338
338
  end
339
339
  end
340
340
 
341
+ context '@system_uri' do
342
+ [
343
+ # system uri
344
+ [ # transport and hostname
345
+ {:uri => "qemu+ssh://localhost/session"},
346
+ {:uri => "qemu+ssh://localhost/session", :system_uri => "qemu+ssh://localhost/system"},
347
+ ],
348
+ [ # explicitly set
349
+ {:qemu_use_session => true, :system_uri => "custom://remote/system"},
350
+ {:uri => "qemu:///session", :system_uri => "custom://remote/system"},
351
+ ],
352
+ ].each do |inputs, outputs, options|
353
+ opts = {}
354
+ opts.merge!(options) if options
355
+
356
+ it "should handle inputs #{inputs} with env (#{opts[:env]})" do
357
+ # allow some of these to fail for now if marked as such
358
+ if !opts[:allow_failure].nil?
359
+ pending(opts[:allow_failure])
360
+ end
361
+
362
+ if !opts[:setup].nil?
363
+ opts[:setup].apply_binding(binding)
364
+ end
365
+
366
+ inputs.each do |k, v|
367
+ subject.instance_variable_set("@#{k}", v)
368
+ end
369
+
370
+ if !opts[:env].nil?
371
+ opts[:env].each do |k, v|
372
+ fake_env[k] = v
373
+ end
374
+ end
375
+
376
+ subject.finalize!
377
+
378
+ # ensure failed output indicates which settings are incorrect in the failed test
379
+ got = subject.instance_variables.each_with_object({}) do |name, hash|
380
+ if outputs.key?(name.to_s[1..-1].to_sym)
381
+ hash["#{name.to_s[1..-1]}".to_sym] =subject.instance_variable_get(name)
382
+ end
383
+ end
384
+ expect(got).to eq(outputs)
385
+ end
386
+ end
387
+ end
388
+
341
389
  context '@proxy_command' do
342
390
  before(:example) do
343
391
  stub_const("ENV", fake_env)