vagrant-libvirt 0.3.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +421 -50
  3. data/lib/vagrant-libvirt/action.rb +7 -1
  4. data/lib/vagrant-libvirt/action/clean_machine_folder.rb +30 -0
  5. data/lib/vagrant-libvirt/action/create_domain.rb +56 -18
  6. data/lib/vagrant-libvirt/action/create_domain_volume.rb +57 -55
  7. data/lib/vagrant-libvirt/action/create_network_interfaces.rb +0 -3
  8. data/lib/vagrant-libvirt/action/create_networks.rb +11 -4
  9. data/lib/vagrant-libvirt/action/destroy_domain.rb +1 -1
  10. data/lib/vagrant-libvirt/action/forward_ports.rb +37 -38
  11. data/lib/vagrant-libvirt/action/halt_domain.rb +25 -9
  12. data/lib/vagrant-libvirt/action/handle_box_image.rb +163 -74
  13. data/lib/vagrant-libvirt/action/is_running.rb +1 -3
  14. data/lib/vagrant-libvirt/action/is_suspended.rb +4 -4
  15. data/lib/vagrant-libvirt/action/package_domain.rb +2 -1
  16. data/lib/vagrant-libvirt/action/set_boot_order.rb +6 -2
  17. data/lib/vagrant-libvirt/action/start_domain.rb +86 -29
  18. data/lib/vagrant-libvirt/action/wait_till_up.rb +8 -52
  19. data/lib/vagrant-libvirt/cap/{mount_p9.rb → mount_9p.rb} +2 -2
  20. data/lib/vagrant-libvirt/cap/mount_virtiofs.rb +37 -0
  21. data/lib/vagrant-libvirt/cap/{synced_folder.rb → synced_folder_9p.rb} +4 -5
  22. data/lib/vagrant-libvirt/cap/synced_folder_virtiofs.rb +109 -0
  23. data/lib/vagrant-libvirt/config.rb +236 -43
  24. data/lib/vagrant-libvirt/driver.rb +49 -32
  25. data/lib/vagrant-libvirt/errors.rb +24 -1
  26. data/lib/vagrant-libvirt/plugin.rb +14 -5
  27. data/lib/vagrant-libvirt/provider.rb +2 -9
  28. data/lib/vagrant-libvirt/templates/domain.xml.erb +35 -10
  29. data/lib/vagrant-libvirt/templates/private_network.xml.erb +1 -1
  30. data/lib/vagrant-libvirt/util/network_util.rb +21 -3
  31. data/lib/vagrant-libvirt/version +1 -1
  32. data/lib/vagrant-libvirt/version.rb +57 -9
  33. data/locales/en.yml +12 -0
  34. data/spec/spec_helper.rb +37 -3
  35. data/spec/support/binding_proc.rb +24 -0
  36. data/spec/support/libvirt_context.rb +2 -0
  37. data/spec/support/matchers/have_file_content.rb +63 -0
  38. data/spec/support/sharedcontext.rb +4 -0
  39. data/spec/unit/action/clean_machine_folder_spec.rb +58 -0
  40. data/spec/unit/action/create_domain_spec.rb +121 -36
  41. data/spec/unit/action/create_domain_spec/additional_disks_domain.xml +54 -0
  42. data/spec/unit/action/create_domain_spec/default_domain.xml +49 -0
  43. data/spec/unit/action/create_domain_spec/{default_storage_pool.xml → default_system_storage_pool.xml} +0 -0
  44. data/spec/unit/action/create_domain_spec/default_user_storage_pool.xml +17 -0
  45. data/spec/unit/action/create_domain_volume_spec.rb +102 -0
  46. data/spec/unit/action/create_domain_volume_spec/one_disk_in_storage.xml +21 -0
  47. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_0.xml +21 -0
  48. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_1.xml +21 -0
  49. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_2.xml +21 -0
  50. data/spec/unit/action/destroy_domain_spec.rb +1 -1
  51. data/spec/unit/action/forward_ports_spec.rb +202 -0
  52. data/spec/unit/action/halt_domain_spec.rb +90 -0
  53. data/spec/unit/action/handle_box_image_spec.rb +363 -0
  54. data/spec/unit/action/start_domain_spec.rb +183 -1
  55. data/spec/unit/action/start_domain_spec/clock_timer_rtc.xml +50 -0
  56. data/spec/unit/action/start_domain_spec/default.xml +2 -2
  57. data/spec/unit/action/start_domain_spec/default_added_tpm_path.xml +48 -0
  58. data/spec/unit/action/start_domain_spec/default_added_tpm_version.xml +48 -0
  59. data/spec/unit/action/wait_till_up_spec.rb +22 -21
  60. data/spec/unit/config_spec.rb +395 -127
  61. data/spec/unit/templates/domain_all_settings.xml +14 -3
  62. data/spec/unit/templates/domain_custom_cpu_model.xml +2 -1
  63. data/spec/unit/templates/domain_defaults.xml +2 -1
  64. data/spec/unit/templates/domain_spec.rb +100 -3
  65. data/spec/unit/templates/tpm/version_1.2.xml +54 -0
  66. data/spec/unit/templates/tpm/version_2.0.xml +53 -0
  67. metadata +105 -19
@@ -21,7 +21,7 @@ module VagrantPlugins
21
21
  env[:metrics] ||= {}
22
22
 
23
23
  # Get domain object
24
- domain = env[:machine].provider.driver.get_domain(env[:machine].id.to_s)
24
+ domain = env[:machine].provider.driver.get_domain(env[:machine])
25
25
  if domain.nil?
26
26
  raise Errors::NoDomainError,
27
27
  error_message: "Domain #{env[:machine].id} not found"
@@ -34,63 +34,19 @@ module VagrantPlugins
34
34
  @logger.debug("Searching for IP for MAC address: #{domain.mac}")
35
35
  env[:ui].info(I18n.t('vagrant_libvirt.waiting_for_ip'))
36
36
 
37
- if env[:machine].provider_config.qemu_use_session
38
- env[:metrics]['instance_ip_time'] = Util::Timer.time do
39
- retryable(on: Fog::Errors::TimeoutError, tries: 300) do
40
- # just return if interrupted and let the warden call recover
41
- return if env[:interrupted]
37
+ env[:metrics]['instance_ip_time'] = Util::Timer.time do
38
+ retryable(on: Fog::Errors::TimeoutError, tries: 300) do
39
+ # just return if interrupted and let the warden call recover
40
+ return if env[:interrupted]
42
41
 
43
- # Wait for domain to obtain an ip address
44
- domain.wait_for(2) do
45
- env[:ip_address] = env[:machine].provider.driver.get_ipaddress_system(domain.mac)
46
- !env[:ip_address].nil?
47
- end
48
- end
49
- end
50
- else
51
- env[:metrics]['instance_ip_time'] = Util::Timer.time do
52
- retryable(on: Fog::Errors::TimeoutError, tries: 300) do
53
- # just return if interrupted and let the warden call recover
54
- return if env[:interrupted]
55
-
56
- # Wait for domain to obtain an ip address
57
- domain.wait_for(2) do
58
- addresses.each_pair do |_type, ip|
59
- env[:ip_address] = ip[0] unless ip[0].nil?
60
- end
61
- !env[:ip_address].nil?
62
- end
63
- end
42
+ # Wait for domain to obtain an ip address
43
+ env[:ip_address] = env[:machine].provider.driver.get_domain_ipaddress(env[:machine], domain)
64
44
  end
65
45
  end
66
46
 
67
47
  @logger.info("Got IP address #{env[:ip_address]}")
68
48
  @logger.info("Time for getting IP: #{env[:metrics]['instance_ip_time']}")
69
49
 
70
- # Machine has ip address assigned, now wait till we are able to
71
- # connect via ssh.
72
- env[:metrics]['instance_ssh_time'] = Util::Timer.time do
73
- env[:ui].info(I18n.t('vagrant_libvirt.waiting_for_ssh'))
74
- retryable(on: Fog::Errors::TimeoutError, tries: 60) do
75
- # If we're interrupted don't worry about waiting
76
- next if env[:interrupted]
77
-
78
- # Wait till we are able to connect via ssh.
79
- loop do
80
- # If we're interrupted then just back out
81
- break if env[:interrupted]
82
- break if env[:machine].communicate.ready?
83
- sleep 2
84
- end
85
- end
86
- end
87
- # just return if interrupted and let the warden call recover
88
- return if env[:interrupted]
89
- @logger.info("Time for SSH ready: #{env[:metrics]['instance_ssh_time']}")
90
-
91
- # Booted and ready for use.
92
- # env[:ui].info(I18n.t("vagrant_libvirt.ready"))
93
-
94
50
  @app.call(env)
95
51
  end
96
52
 
@@ -100,7 +56,7 @@ module VagrantPlugins
100
56
  end
101
57
 
102
58
  def terminate(env)
103
- if env[:machine].provider.state.id != :not_created
59
+ if env[:machine].state.id != :not_created
104
60
  # If we're not supposed to destroy on error then just return
105
61
  return unless env[:destroy_on_error]
106
62
 
@@ -4,10 +4,10 @@ require 'vagrant/util/retryable'
4
4
  module VagrantPlugins
5
5
  module ProviderLibvirt
6
6
  module Cap
7
- class MountP9
7
+ class Mount9P
8
8
  extend Vagrant::Util::Retryable
9
9
 
10
- def self.mount_p9_shared_folder(machine, folders)
10
+ def self.mount_9p_shared_folder(machine, folders)
11
11
  folders.each do |_name, opts|
12
12
  # Expand the guest path so we can handle things like "~/vagrant"
13
13
  expanded_guest_path = machine.guest.capability(
@@ -0,0 +1,37 @@
1
+ require 'digest/md5'
2
+ require 'vagrant/util/retryable'
3
+
4
+ module VagrantPlugins
5
+ module ProviderLibvirt
6
+ module Cap
7
+ class MountVirtioFS
8
+ extend Vagrant::Util::Retryable
9
+
10
+ def self.mount_virtiofs_shared_folder(machine, folders)
11
+ folders.each do |_name, opts|
12
+ # Expand the guest path so we can handle things like "~/vagrant"
13
+ expanded_guest_path = machine.guest.capability(
14
+ :shell_expand_guest_path, opts[:guestpath]
15
+ )
16
+
17
+ # Do the actual creating and mounting
18
+ machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
19
+
20
+ # Mount
21
+ mount_tag = Digest::MD5.new.update(opts[:hostpath]).to_s[0, 31]
22
+
23
+ mount_opts = "-o #{opts[:mount_opts]}" if opts[:mount_opts]
24
+
25
+ mount_command = "mount -t virtiofs #{mount_opts} '#{mount_tag}' #{expanded_guest_path}"
26
+ retryable(on: Vagrant::Errors::LinuxMountFailed,
27
+ tries: 5,
28
+ sleep: 3) do
29
+ machine.communicate.sudo(mount_command,
30
+ error_class: Vagrant::Errors::LinuxMountFailed)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -6,10 +6,9 @@ require 'digest/md5'
6
6
  require 'vagrant/util/subprocess'
7
7
  require 'vagrant/errors'
8
8
  require 'vagrant-libvirt/errors'
9
- # require_relative "helper"
10
9
 
11
10
  module VagrantPlugins
12
- module SyncedFolder9p
11
+ module SyncedFolder9P
13
12
  class SyncedFolder < Vagrant.plugin('2', :synced_folder)
14
13
  include Vagrant::Util
15
14
  include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
@@ -69,10 +68,10 @@ module VagrantPlugins
69
68
  end
70
69
  end
71
70
 
72
- # TODO: once up, mount folders
71
+ # once up, mount folders
73
72
  def enable(machine, folders, _opts)
74
73
  # Go through each folder and mount
75
- machine.ui.info('mounting p9 share in guest')
74
+ machine.ui.info('mounting 9p share in guest')
76
75
  # Only mount folders that have a guest path specified.
77
76
  mount_folders = {}
78
77
  folders.each do |id, opts|
@@ -83,7 +82,7 @@ module VagrantPlugins
83
82
  end
84
83
  # Mount the actual folder
85
84
  machine.guest.capability(
86
- :mount_p9_shared_folder, mount_folders
85
+ :mount_9p_shared_folder, mount_folders
87
86
  )
88
87
  end
89
88
 
@@ -0,0 +1,109 @@
1
+ require 'log4r'
2
+ require 'ostruct'
3
+ require 'nokogiri'
4
+ require 'digest/md5'
5
+
6
+ require 'vagrant/util/subprocess'
7
+ require 'vagrant/errors'
8
+ require 'vagrant-libvirt/errors'
9
+
10
+ module VagrantPlugins
11
+ module SyncedFolderVirtioFS
12
+ class SyncedFolder < Vagrant.plugin('2', :synced_folder)
13
+ include Vagrant::Util
14
+ include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
15
+
16
+ def initialize(*args)
17
+ super
18
+ @logger = Log4r::Logger.new('vagrant_libvirt::synced_folders::virtiofs')
19
+ end
20
+
21
+ def usable?(machine, _raise_error = false)
22
+ # bail now if not using Libvirt since checking version would throw error
23
+ return false unless machine.provider_name == :libvirt
24
+
25
+ # virtiofs support introduced since 6.2.0
26
+ # version number format is major * 1,000,000 + minor * 1,000 + release
27
+ libvirt_version = machine.provider.driver.connection.client.libversion
28
+ libvirt_version >= 6_002_000
29
+ end
30
+
31
+ def prepare(machine, folders, _opts)
32
+ raise Vagrant::Errors::Error('No Libvirt connection') if machine.provider.driver.connection.nil?
33
+ @conn = machine.provider.driver.connection.client
34
+
35
+ begin
36
+ # loop through folders
37
+ folders.each do |id, folder_opts|
38
+ folder_opts.merge!(target: id,
39
+ mount: true,
40
+ readonly: nil) { |_k, ov, _nv| ov }
41
+
42
+ mount_tag = Digest::MD5.new.update(folder_opts[:hostpath]).to_s[0, 31]
43
+ folder_opts[:mount_tag] = mount_tag
44
+
45
+ machine.ui.info "================\nMachine id: #{machine.id}\nShould be mounting folders\n #{id}, opts: #{folder_opts}"
46
+
47
+ #xml = to_xml('filesystem', folder_opts)
48
+ xml = Nokogiri::XML::Builder.new do |xml|
49
+ xml.filesystem(type: 'mount', accessmode: 'passthrough') do
50
+ xml.driver(type: 'virtiofs')
51
+ xml.source(dir: folder_opts[:hostpath])
52
+ xml.target(dir: mount_tag)
53
+ xml.readonly unless folder_opts[:readonly].nil?
54
+ end
55
+ end.to_xml(
56
+ save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION |
57
+ Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS |
58
+ Nokogiri::XML::Node::SaveOptions::FORMAT
59
+ )
60
+ # puts "<<<<< XML:\n #{xml}\n >>>>>"
61
+ @conn.lookup_domain_by_uuid(machine.id).attach_device(xml, 0)
62
+ end
63
+ rescue => e
64
+ machine.ui.error("could not attach device because: #{e}")
65
+ raise VagrantPlugins::ProviderLibvirt::Errors::AttachDeviceError,
66
+ error_message: e.message
67
+ end
68
+ end
69
+
70
+ # once up, mount folders
71
+ def enable(machine, folders, _opts)
72
+ # Go through each folder and mount
73
+ machine.ui.info('mounting virtiofs share in guest')
74
+ # Only mount folders that have a guest path specified.
75
+ mount_folders = {}
76
+ folders.each do |id, opts|
77
+ next unless opts[:mount] && opts[:guestpath] && !opts[:guestpath].empty?
78
+ mount_folders[id] = opts.dup
79
+ end
80
+ # Mount the actual folder
81
+ machine.guest.capability(
82
+ :mount_virtiofs_shared_folder, mount_folders
83
+ )
84
+ end
85
+
86
+ def cleanup(machine, _opts)
87
+ if machine.provider.driver.connection.nil?
88
+ raise Vagrant::Errors::Error('No Libvirt connection')
89
+ end
90
+ @conn = machine.provider.driver.connection.client
91
+ begin
92
+ if machine.id && machine.id != ''
93
+ dom = @conn.lookup_domain_by_uuid(machine.id)
94
+ Nokogiri::XML(dom.xml_desc).xpath(
95
+ '/domain/devices/filesystem'
96
+ ).each do |xml|
97
+ dom.detach_device(xml.to_s)
98
+ machine.ui.info 'Cleaned up shared folders'
99
+ end
100
+ end
101
+ rescue => e
102
+ machine.ui.error("could not detach device because: #{e}")
103
+ raise VagrantPlugins::ProviderLibvirt::Errors::DetachDeviceError,
104
+ error_message: e.message
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,3 +1,5 @@
1
+ require 'cgi'
2
+
1
3
  require 'vagrant'
2
4
 
3
5
  class Numeric
@@ -37,6 +39,11 @@ module VagrantPlugins
37
39
  # ID SSH key file
38
40
  attr_accessor :id_ssh_key_file
39
41
 
42
+ attr_accessor :proxy_command
43
+
44
+ # Forward port with id 'ssh'
45
+ attr_accessor :forward_ssh_port
46
+
40
47
  # Libvirt storage pool name, where box image and instance snapshots will
41
48
  # be stored.
42
49
  attr_accessor :storage_pool_name
@@ -59,6 +66,7 @@ module VagrantPlugins
59
66
  attr_accessor :management_network_pci_bus
60
67
  attr_accessor :management_network_pci_slot
61
68
  attr_accessor :management_network_domain
69
+ attr_accessor :management_network_mtu
62
70
 
63
71
  # System connection information
64
72
  attr_accessor :system_uri
@@ -84,6 +92,8 @@ module VagrantPlugins
84
92
  attr_accessor :shares
85
93
  attr_accessor :features
86
94
  attr_accessor :features_hyperv
95
+ attr_accessor :clock_offset
96
+ attr_accessor :clock_timers
87
97
  attr_accessor :numa_nodes
88
98
  attr_accessor :loader
89
99
  attr_accessor :nvram
@@ -93,9 +103,10 @@ module VagrantPlugins
93
103
  attr_accessor :machine_virtual_size
94
104
  attr_accessor :disk_bus
95
105
  attr_accessor :disk_device
106
+ attr_accessor :disk_driver_opts
96
107
  attr_accessor :nic_model_type
97
108
  attr_accessor :nested
98
- attr_accessor :volume_cache
109
+ attr_accessor :volume_cache # deprecated, kept for backwards compatibility; use disk_driver
99
110
  attr_accessor :kernel
100
111
  attr_accessor :cmd_line
101
112
  attr_accessor :initrd
@@ -117,6 +128,13 @@ module VagrantPlugins
117
128
  attr_accessor :tpm_model
118
129
  attr_accessor :tpm_type
119
130
  attr_accessor :tpm_path
131
+ attr_accessor :tpm_version
132
+
133
+ # Configure the memballoon
134
+ attr_accessor :memballoon_enabled
135
+ attr_accessor :memballoon_model
136
+ attr_accessor :memballoon_pci_bus
137
+ attr_accessor :memballoon_pci_slot
120
138
 
121
139
  # Sets the max number of NICs that can be created
122
140
  # Default set to 8. Don't change the default unless you know
@@ -177,10 +195,14 @@ module VagrantPlugins
177
195
  @uri = UNSET_VALUE
178
196
  @driver = UNSET_VALUE
179
197
  @host = UNSET_VALUE
198
+ @port = UNSET_VALUE
180
199
  @connect_via_ssh = UNSET_VALUE
181
200
  @username = UNSET_VALUE
182
201
  @password = UNSET_VALUE
183
202
  @id_ssh_key_file = UNSET_VALUE
203
+ @socket = UNSET_VALUE
204
+ @proxy_command = UNSET_VALUE
205
+ @forward_ssh_port = UNSET_VALUE # forward port with id 'ssh'
184
206
  @storage_pool_name = UNSET_VALUE
185
207
  @snapshot_pool_name = UNSET_VALUE
186
208
  @random_hostname = UNSET_VALUE
@@ -194,6 +216,7 @@ module VagrantPlugins
194
216
  @management_network_pci_slot = UNSET_VALUE
195
217
  @management_network_pci_bus = UNSET_VALUE
196
218
  @management_network_domain = UNSET_VALUE
219
+ @management_network_mtu = UNSET_VALUE
197
220
 
198
221
  # System connection information
199
222
  @system_uri = UNSET_VALUE
@@ -215,6 +238,8 @@ module VagrantPlugins
215
238
  @shares = UNSET_VALUE
216
239
  @features = UNSET_VALUE
217
240
  @features_hyperv = UNSET_VALUE
241
+ @clock_offset = UNSET_VALUE
242
+ @clock_timers = []
218
243
  @numa_nodes = UNSET_VALUE
219
244
  @loader = UNSET_VALUE
220
245
  @nvram = UNSET_VALUE
@@ -223,6 +248,7 @@ module VagrantPlugins
223
248
  @machine_virtual_size = UNSET_VALUE
224
249
  @disk_bus = UNSET_VALUE
225
250
  @disk_device = UNSET_VALUE
251
+ @disk_driver_opts = {}
226
252
  @nic_model_type = UNSET_VALUE
227
253
  @nested = UNSET_VALUE
228
254
  @volume_cache = UNSET_VALUE
@@ -245,6 +271,12 @@ module VagrantPlugins
245
271
  @tpm_model = UNSET_VALUE
246
272
  @tpm_type = UNSET_VALUE
247
273
  @tpm_path = UNSET_VALUE
274
+ @tpm_version = UNSET_VALUE
275
+
276
+ @memballoon_enabled = UNSET_VALUE
277
+ @memballoon_model = UNSET_VALUE
278
+ @memballoon_pci_bus = UNSET_VALUE
279
+ @memballoon_pci_slot = UNSET_VALUE
248
280
 
249
281
  @nic_adapter_count = UNSET_VALUE
250
282
 
@@ -373,10 +405,39 @@ module VagrantPlugins
373
405
  raise 'Feature name AND state must be specified'
374
406
  end
375
407
 
408
+ if options[:name] == 'spinlocks' && options[:retries].nil?
409
+ raise 'Feature spinlocks requires retries parameter'
410
+ end
411
+
376
412
  @features_hyperv = [] if @features_hyperv == UNSET_VALUE
377
413
 
378
- @features_hyperv.push(name: options[:name],
379
- state: options[:state])
414
+ if options[:name] == 'spinlocks'
415
+ @features_hyperv.push(name: options[:name],
416
+ state: options[:state],
417
+ retries: options[:retries])
418
+ else
419
+ @features_hyperv.push(name: options[:name],
420
+ state: options[:state])
421
+ end
422
+ end
423
+
424
+ def clock_timer(options = {})
425
+ if options[:name].nil?
426
+ raise 'Clock timer name must be specified'
427
+ end
428
+
429
+ options.each do |key, value|
430
+ case key
431
+ when :name, :track, :tickpolicy, :frequency, :mode, :present
432
+ if value.nil?
433
+ raise "Value of timer option #{key} is nil"
434
+ end
435
+ else
436
+ raise "Unknown clock timer option: #{key}"
437
+ end
438
+ end
439
+
440
+ @clock_timers.push(options.dup)
380
441
  end
381
442
 
382
443
  def cputopology(options = {})
@@ -551,6 +612,12 @@ module VagrantPlugins
551
612
  @smartcard_dev[:source_service] = options[:source_service] if @smartcard_dev[:type] == 'tcp'
552
613
  end
553
614
 
615
+ # Disk driver options for primary disk
616
+ def disk_driver(options = {})
617
+ supported_opts = [:cache, :io, :copy_on_read, :discard, :detect_zeroes]
618
+ @disk_driver_opts = options.select { |k,_| supported_opts.include? k }
619
+ end
620
+
554
621
  # NOTE: this will run twice for each time it's needed- keep it idempotent
555
622
  def storage(storage_type, options = {})
556
623
  if storage_type == :file
@@ -574,11 +641,13 @@ module VagrantPlugins
574
641
  # as will the address unit number (unit=0, unit=1, etc)
575
642
 
576
643
  options = {
644
+ type: 'raw',
577
645
  bus: 'ide',
578
646
  path: nil
579
647
  }.merge(options)
580
648
 
581
649
  cdrom = {
650
+ type: options[:type],
582
651
  dev: options[:dev],
583
652
  bus: options[:bus],
584
653
  path: options[:path]
@@ -605,6 +674,10 @@ module VagrantPlugins
605
674
  allow_existing: options[:allow_existing],
606
675
  shareable: options[:shareable],
607
676
  serial: options[:serial],
677
+ io: options[:io],
678
+ copy_on_read: options[:copy_on_read],
679
+ discard: options[:discard],
680
+ detect_zeroes: options[:detect_zeroes],
608
681
  pool: options[:pool], # overrides storage_pool setting for additional disks
609
682
  wwn: options[:wwn],
610
683
  }
@@ -624,15 +697,25 @@ module VagrantPlugins
624
697
  @qemu_env.merge!(options)
625
698
  end
626
699
 
700
+ def _default_uri
701
+ # Determine if any settings except driver provided explicitly, if not
702
+ # and the LIBVIRT_DEFAULT_URI var is set, use that.
703
+ #
704
+ # Skipping driver because that may be set on individual boxes rather
705
+ # than by the user.
706
+ if [
707
+ @connect_via_ssh, @host, @username, @password,
708
+ @id_ssh_key_file, @qemu_use_session, @socket,
709
+ ].none?{ |v| v != UNSET_VALUE }
710
+ if ENV.fetch('LIBVIRT_DEFAULT_URI', '') != ""
711
+ @uri = ENV['LIBVIRT_DEFAULT_URI']
712
+ end
713
+ end
714
+ end
715
+
627
716
  # code to generate URI from from either the LIBVIRT_URI environment
628
717
  # variable or a config moved out of the connect action
629
718
  def _generate_uri(qemu_use_session)
630
-
631
- # If the LIBVIRT_DEFAULT_URI var is set, we'll use that
632
- if ENV.fetch('LIBVIRT_DEFAULT_URI', '') != ""
633
- return ENV['LIBVIRT_DEFAULT_URI']
634
- end
635
-
636
719
  # builds the Libvirt connection URI from the given driver config
637
720
  # Setup connection uri.
638
721
  uri = @driver.dup
@@ -652,30 +735,34 @@ module VagrantPlugins
652
735
  uri = 'qemu' # use QEMU uri for KVM domain type
653
736
  end
654
737
 
655
- if @connect_via_ssh
738
+ # turn on ssh if an ssh key file is explicitly provided
739
+ if @connect_via_ssh == UNSET_VALUE && @id_ssh_key_file && @id_ssh_key_file != UNSET_VALUE
740
+ @connect_via_ssh = true
741
+ end
742
+
743
+ params = {}
744
+
745
+ if @connect_via_ssh == true
746
+ finalize_id_ssh_key_file
747
+
656
748
  uri << '+ssh://'
657
- uri << @username + '@' if @username
749
+ uri << @username + '@' if @username && @username != UNSET_VALUE
658
750
 
659
- uri << ( @host ? @host : 'localhost' )
751
+ uri << ( @host && @host != UNSET_VALUE ? @host : 'localhost' )
752
+
753
+ params['no_verify'] = '1'
754
+ params['keyfile'] = @id_ssh_key_file if @id_ssh_key_file
660
755
  else
661
756
  uri << '://'
662
- uri << @host if @host
757
+ uri << @host if @host && @host != UNSET_VALUE
663
758
  end
664
759
 
665
760
  uri << virt_path
666
761
 
667
- params = {'no_verify' => '1'}
668
-
669
- if @id_ssh_key_file
670
- # set ssh key for access to Libvirt host
671
- # if no slash, prepend $HOME/.ssh/
672
- @id_ssh_key_file.prepend("#{ENV['HOME']}/.ssh/") if @id_ssh_key_file !~ /\A\//
673
- params['keyfile'] = @id_ssh_key_file
674
- end
675
762
  # set path to Libvirt socket
676
763
  params['socket'] = @socket if @socket
677
764
 
678
- uri << "?" + params.map{|pair| pair.join('=')}.join('&')
765
+ uri << "?" + params.map{|pair| pair.join('=')}.join('&') if !params.empty?
679
766
  uri
680
767
  end
681
768
 
@@ -688,12 +775,25 @@ module VagrantPlugins
688
775
  end
689
776
 
690
777
  def finalize!
778
+ _default_uri if @uri == UNSET_VALUE
779
+
780
+ # settings which _generate_uri
691
781
  @driver = 'kvm' if @driver == UNSET_VALUE
692
- @host = nil if @host == UNSET_VALUE
693
- @connect_via_ssh = false if @connect_via_ssh == UNSET_VALUE
694
- @username = nil if @username == UNSET_VALUE
695
782
  @password = nil if @password == UNSET_VALUE
696
- @id_ssh_key_file = 'id_rsa' if @id_ssh_key_file == UNSET_VALUE
783
+ @socket = nil if @socket == UNSET_VALUE
784
+
785
+ # If uri isn't set then let's build one from various sources.
786
+ # Default to passing false for qemu_use_session if it's not set.
787
+ if @uri == UNSET_VALUE
788
+ @uri = _generate_uri(@qemu_use_session == UNSET_VALUE ? false : @qemu_use_session)
789
+ end
790
+
791
+ finalize_from_uri
792
+ finalize_proxy_command
793
+
794
+ # forward port with id 'ssh'
795
+ @forward_ssh_port = false if @forward_ssh_port == UNSET_VALUE
796
+
697
797
  @storage_pool_name = 'default' if @storage_pool_name == UNSET_VALUE
698
798
  @snapshot_pool_name = @storage_pool_name if @snapshot_pool_name == UNSET_VALUE
699
799
  @storage_pool_path = nil if @storage_pool_path == UNSET_VALUE
@@ -708,24 +808,9 @@ module VagrantPlugins
708
808
  @management_network_pci_bus = nil if @management_network_pci_bus == UNSET_VALUE
709
809
  @management_network_pci_slot = nil if @management_network_pci_slot == UNSET_VALUE
710
810
  @management_network_domain = nil if @management_network_domain == UNSET_VALUE
811
+ @management_network_mtu = nil if @management_network_mtu == UNSET_VALUE
711
812
  @system_uri = 'qemu:///system' if @system_uri == UNSET_VALUE
712
813
 
713
- # If uri isn't set then let's build one from various sources.
714
- # Default to passing false for qemu_use_session if it's not set.
715
- if @uri == UNSET_VALUE
716
- @uri = _generate_uri(@qemu_use_session == UNSET_VALUE ? false : @qemu_use_session)
717
- end
718
-
719
- # Set qemu_use_session based on the URI if it wasn't set by the user
720
- if @qemu_use_session == UNSET_VALUE
721
- uri = _parse_uri(@uri)
722
- if (uri.scheme.start_with? "qemu") && (uri.path.include? "session")
723
- @qemu_use_session = true
724
- else
725
- @qemu_use_session = false
726
- end
727
- end
728
-
729
814
  # Domain specific settings.
730
815
  @title = '' if @title == UNSET_VALUE
731
816
  @description = '' if @description == UNSET_VALUE
@@ -749,6 +834,8 @@ module VagrantPlugins
749
834
  @shares = nil if @shares == UNSET_VALUE
750
835
  @features = ['acpi','apic','pae'] if @features == UNSET_VALUE
751
836
  @features_hyperv = [] if @features_hyperv == UNSET_VALUE
837
+ @clock_offset = 'utc' if @clock_offset == UNSET_VALUE
838
+ @clock_timers = [] if @clock_timers == UNSET_VALUE
752
839
  @numa_nodes = @numa_nodes == UNSET_VALUE ? nil : _generate_numa
753
840
  @loader = nil if @loader == UNSET_VALUE
754
841
  @nvram = nil if @nvram == UNSET_VALUE
@@ -757,9 +844,10 @@ module VagrantPlugins
757
844
  @machine_virtual_size = nil if @machine_virtual_size == UNSET_VALUE
758
845
  @disk_bus = 'virtio' if @disk_bus == UNSET_VALUE
759
846
  @disk_device = 'vda' if @disk_device == UNSET_VALUE
847
+ @disk_driver_opts = {} if @disk_driver_opts == UNSET_VALUE
760
848
  @nic_model_type = nil if @nic_model_type == UNSET_VALUE
761
849
  @nested = false if @nested == UNSET_VALUE
762
- @volume_cache = 'default' if @volume_cache == UNSET_VALUE
850
+ @volume_cache = nil if @volume_cache == UNSET_VALUE
763
851
  @kernel = nil if @kernel == UNSET_VALUE
764
852
  @cmd_line = '' if @cmd_line == UNSET_VALUE
765
853
  @initrd = '' if @initrd == UNSET_VALUE
@@ -781,6 +869,11 @@ module VagrantPlugins
781
869
  @tpm_model = 'tpm-tis' if @tpm_model == UNSET_VALUE
782
870
  @tpm_type = 'passthrough' if @tpm_type == UNSET_VALUE
783
871
  @tpm_path = nil if @tpm_path == UNSET_VALUE
872
+ @tpm_version = nil if @tpm_version == UNSET_VALUE
873
+ @memballoon_enabled = nil if @memballoon_enabled == UNSET_VALUE
874
+ @memballoon_model = 'virtio' if @memballoon_model == UNSET_VALUE
875
+ @memballoon_pci_bus = '0x00' if @memballoon_pci_bus == UNSET_VALUE
876
+ @memballoon_pci_slot = '0x0f' if @memballoon_pci_slot == UNSET_VALUE
784
877
  @nic_adapter_count = 8 if @nic_adapter_count == UNSET_VALUE
785
878
  @emulator_path = nil if @emulator_path == UNSET_VALUE
786
879
 
@@ -872,6 +965,14 @@ module VagrantPlugins
872
965
  end
873
966
  end
874
967
 
968
+ if !machine.provider_config.volume_cache.nil? and machine.provider_config.volume_cache != UNSET_VALUE
969
+ machine.ui.warn("Libvirt Provider: volume_cache is deprecated. Use disk_driver :cache => '#{machine.provider_config.volume_cache}' instead.")
970
+
971
+ if !machine.provider_config.disk_driver_opts.empty?
972
+ machine.ui.warn("Libvirt Provider: volume_cache has no effect when disk_driver is defined.")
973
+ end
974
+ end
975
+
875
976
  { 'Libvirt Provider' => errors }
876
977
  end
877
978
 
@@ -885,11 +986,103 @@ module VagrantPlugins
885
986
  c += other.cdroms
886
987
  result.cdroms = c
887
988
 
989
+ result.disk_driver_opts = disk_driver_opts.merge(other.disk_driver_opts)
990
+
991
+ c = clock_timers.dup
992
+ c += other.clock_timers
993
+ result.clock_timers = c
994
+
888
995
  c = qemu_env != UNSET_VALUE ? qemu_env.dup : {}
889
996
  c.merge!(other.qemu_env) if other.qemu_env != UNSET_VALUE
890
997
  result.qemu_env = c
891
998
  end
892
999
  end
1000
+
1001
+ private
1002
+
1003
+ def finalize_from_uri
1004
+ # Parse uri to extract individual components
1005
+ uri = _parse_uri(@uri)
1006
+
1007
+ # only set @connect_via_ssh if not explicitly to avoid overriding
1008
+ # and allow an error to occur if the @uri and @connect_via_ssh disagree
1009
+ @connect_via_ssh = uri.scheme.include? "ssh" if @connect_via_ssh == UNSET_VALUE
1010
+
1011
+ # Set qemu_use_session based on the URI if it wasn't set by the user
1012
+ if @qemu_use_session == UNSET_VALUE
1013
+ if (uri.scheme.start_with? "qemu") && (uri.path.include? "session")
1014
+ @qemu_use_session = true
1015
+ else
1016
+ @qemu_use_session = false
1017
+ end
1018
+ end
1019
+
1020
+ # Extract host and username values from uri if provided, otherwise nil
1021
+ @host = uri.host
1022
+ @port = uri.port
1023
+ @username = uri.user
1024
+ if uri.query
1025
+ params = CGI.parse(uri.query)
1026
+ @id_ssh_key_file = params['keyfile'].first if params.has_key?('keyfile')
1027
+ end
1028
+
1029
+ finalize_id_ssh_key_file
1030
+ end
1031
+
1032
+ def resolve_ssh_key_file(key_file)
1033
+ # set ssh key for access to Libvirt host
1034
+ # if no slash, prepend $HOME/.ssh/
1035
+ key_file.prepend("#{ENV['HOME']}/.ssh/") if key_file && key_file !~ /\A\//
1036
+
1037
+ key_file
1038
+ end
1039
+
1040
+ def finalize_id_ssh_key_file
1041
+ # resolve based on the following roles
1042
+ # 1) if @connect_via_ssh is set to true, and id_ssh_key_file not current set,
1043
+ # set default if the file exists
1044
+ # 2) if supplied the key name, attempt to expand based on user home
1045
+ # 3) otherwise set to nil
1046
+
1047
+ if @connect_via_ssh == true && @id_ssh_key_file == UNSET_VALUE
1048
+ # set default if using ssh while allowing a user using nil to disable this
1049
+ id_ssh_key_file = resolve_ssh_key_file('id_rsa')
1050
+ id_ssh_key_file = nil if !File.file?(id_ssh_key_file)
1051
+ elsif @id_ssh_key_file != UNSET_VALUE
1052
+ id_ssh_key_file = resolve_ssh_key_file(@id_ssh_key_file)
1053
+ else
1054
+ id_ssh_key_file = nil
1055
+ end
1056
+
1057
+ @id_ssh_key_file = id_ssh_key_file
1058
+ end
1059
+
1060
+ def finalize_proxy_command
1061
+ if @connect_via_ssh
1062
+ if @proxy_command == UNSET_VALUE
1063
+ proxy_command = "ssh '#{@host}' "
1064
+ proxy_command << "-p #{@port} " if @port
1065
+ proxy_command << "-l '#{@username}' " if @username
1066
+ proxy_command << "-i '#{@id_ssh_key_file}' " if @id_ssh_key_file
1067
+ proxy_command << '-W %h:%p'
1068
+ else
1069
+ inputs = { host: @host }
1070
+ inputs << { port: @port } if @port
1071
+ inputs[:username] = @username if @username
1072
+ inputs[:id_ssh_key_file] = @id_ssh_key_file if @id_ssh_key_file
1073
+
1074
+ proxy_command = @proxy_command
1075
+ # avoid needing to escape '%' symbols
1076
+ inputs.each do |key, value|
1077
+ proxy_command.gsub!("{#{key}}", value)
1078
+ end
1079
+ end
1080
+
1081
+ @proxy_command = proxy_command
1082
+ else
1083
+ @proxy_command = nil
1084
+ end
1085
+ end
893
1086
  end
894
1087
  end
895
1088
  end