vagrant-libvirt 0.1.0 → 0.4.1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +321 -76
  3. data/lib/vagrant-libvirt/action.rb +1 -1
  4. data/lib/vagrant-libvirt/action/create_domain.rb +54 -14
  5. data/lib/vagrant-libvirt/action/create_domain_volume.rb +0 -2
  6. data/lib/vagrant-libvirt/action/create_network_interfaces.rb +1 -0
  7. data/lib/vagrant-libvirt/action/forward_ports.rb +4 -3
  8. data/lib/vagrant-libvirt/action/handle_box_image.rb +6 -3
  9. data/lib/vagrant-libvirt/action/package_domain.rb +10 -4
  10. data/lib/vagrant-libvirt/action/prune_nfs_exports.rb +4 -3
  11. data/lib/vagrant-libvirt/action/start_domain.rb +86 -29
  12. data/lib/vagrant-libvirt/action/wait_till_up.rb +7 -27
  13. data/lib/vagrant-libvirt/cap/public_address.rb +16 -0
  14. data/lib/vagrant-libvirt/config.rb +233 -32
  15. data/lib/vagrant-libvirt/driver.rb +49 -32
  16. data/lib/vagrant-libvirt/plugin.rb +6 -0
  17. data/lib/vagrant-libvirt/provider.rb +2 -9
  18. data/lib/vagrant-libvirt/templates/domain.xml.erb +34 -5
  19. data/lib/vagrant-libvirt/templates/public_interface.xml.erb +5 -1
  20. data/lib/vagrant-libvirt/util.rb +1 -0
  21. data/lib/vagrant-libvirt/util/erb_template.rb +6 -7
  22. data/lib/vagrant-libvirt/util/ui.rb +23 -0
  23. data/lib/vagrant-libvirt/version +1 -0
  24. data/lib/vagrant-libvirt/version.rb +72 -1
  25. data/spec/spec_helper.rb +28 -2
  26. data/spec/support/binding_proc.rb +24 -0
  27. data/spec/support/libvirt_context.rb +3 -1
  28. data/spec/support/sharedcontext.rb +7 -3
  29. data/spec/unit/action/create_domain_spec.rb +160 -0
  30. data/spec/unit/action/create_domain_spec/default_system_storage_pool.xml +17 -0
  31. data/spec/unit/action/create_domain_spec/default_user_storage_pool.xml +17 -0
  32. data/spec/unit/action/destroy_domain_spec.rb +2 -2
  33. data/spec/unit/action/set_name_of_domain_spec.rb +2 -2
  34. data/spec/unit/action/start_domain_spec.rb +231 -0
  35. data/spec/unit/action/start_domain_spec/clock_timer_rtc.xml +50 -0
  36. data/spec/unit/action/start_domain_spec/default.xml +48 -0
  37. data/spec/unit/action/start_domain_spec/default_added_tpm_path.xml +48 -0
  38. data/spec/unit/action/start_domain_spec/default_added_tpm_version.xml +48 -0
  39. data/spec/unit/action/wait_till_up_spec.rb +32 -9
  40. data/spec/unit/config_spec.rb +438 -0
  41. data/spec/unit/provider_spec.rb +11 -0
  42. data/spec/unit/templates/domain_all_settings.xml +8 -3
  43. data/spec/unit/templates/domain_custom_cpu_model.xml +4 -1
  44. data/spec/unit/templates/domain_defaults.xml +4 -1
  45. data/spec/unit/templates/domain_spec.rb +82 -2
  46. data/spec/unit/templates/tpm/version_1.2.xml +54 -0
  47. data/spec/unit/templates/tpm/version_2.0.xml +53 -0
  48. metadata +62 -6
@@ -186,11 +186,11 @@ module VagrantPlugins
186
186
 
187
187
  b2.use Call, DestroyConfirm do |env2, b3|
188
188
  if env2[:result]
189
+ b3.use ProvisionerCleanup, :before
189
190
  b3.use ClearForwardedPorts
190
191
  b3.use PruneNFSExports
191
192
  b3.use DestroyDomain
192
193
  b3.use DestroyNetworks
193
- b3.use ProvisionerCleanup
194
194
  else
195
195
  b3.use MessageWillNotDestroy
196
196
  end
@@ -5,6 +5,7 @@ module VagrantPlugins
5
5
  module Action
6
6
  class CreateDomain
7
7
  include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
8
+ include VagrantPlugins::ProviderLibvirt::Util::StorageUtil
8
9
 
9
10
  def initialize(app, _env)
10
11
  @logger = Log4r::Logger.new('vagrant_libvirt::action::create_domain')
@@ -31,6 +32,8 @@ module VagrantPlugins
31
32
 
32
33
  # Gather some info about domain
33
34
  @name = env[:domain_name]
35
+ @title = config.title
36
+ @description = config.description
34
37
  @uuid = config.uuid
35
38
  @cpus = config.cpus.to_i
36
39
  @cpuset = config.cpuset
@@ -39,6 +42,8 @@ module VagrantPlugins
39
42
  @nodeset = config.nodeset
40
43
  @features = config.features
41
44
  @features_hyperv = config.features_hyperv
45
+ @clock_offset = config.clock_offset
46
+ @clock_timers = config.clock_timers
42
47
  @shares = config.shares
43
48
  @cpu_mode = config.cpu_mode
44
49
  @cpu_model = config.cpu_model
@@ -50,11 +55,12 @@ module VagrantPlugins
50
55
  @machine_arch = config.machine_arch
51
56
  @disk_bus = config.disk_bus
52
57
  @disk_device = config.disk_device
58
+ @disk_driver_opts = config.disk_driver_opts
53
59
  @nested = config.nested
54
60
  @memory_size = config.memory.to_i * 1024
55
61
  @memory_backing = config.memory_backing
56
62
  @management_network_mac = config.management_network_mac
57
- @domain_volume_cache = config.volume_cache
63
+ @domain_volume_cache = config.volume_cache || 'default'
58
64
  @kernel = config.kernel
59
65
  @cmd_line = config.cmd_line
60
66
  @emulator_path = config.emulator_path
@@ -78,6 +84,7 @@ module VagrantPlugins
78
84
  @tpm_model = config.tpm_model
79
85
  @tpm_type = config.tpm_type
80
86
  @tpm_path = config.tpm_path
87
+ @tpm_version = config.tpm_version
81
88
 
82
89
  # Boot order
83
90
  @boot_order = config.boot_order
@@ -145,15 +152,10 @@ module VagrantPlugins
145
152
  # If we have a box, take the path from the domain volume and set our storage_prefix.
146
153
  # If not, we dump the storage pool xml to get its defined path.
147
154
  # the default storage prefix is typically: /var/lib/libvirt/images/
148
- if !config.qemu_use_session
149
- if env[:machine].config.vm.box
150
- storage_prefix = File.dirname(@domain_volume_path) + '/' # steal
151
- else
152
- storage_pool = env[:machine].provider.driver.connection.client.lookup_storage_pool_by_name(@storage_pool_name)
153
- raise Errors::NoStoragePool if storage_pool.nil?
154
- xml = Nokogiri::XML(storage_pool.xml_desc)
155
- storage_prefix = xml.xpath('/pool/target/path').inner_text.to_s + '/'
156
- end
155
+ if env[:machine].config.vm.box
156
+ storage_prefix = File.dirname(@domain_volume_path) + '/' # steal
157
+ else
158
+ storage_prefix = get_disk_storage_prefix(env, @storage_pool_name)
157
159
  end
158
160
 
159
161
  @disks.each do |disk|
@@ -167,6 +169,16 @@ module VagrantPlugins
167
169
 
168
170
  disk[:absolute_path] = storage_prefix + disk[:path]
169
171
 
172
+ if not disk[:pool].nil?
173
+ disk_pool_name = disk[:pool]
174
+ @logger.debug "Overriding pool name with: #{disk_pool_name}"
175
+ disk_storage_prefix = get_disk_storage_prefix(env, disk_pool_name)
176
+ disk[:absolute_path] = disk_storage_prefix + disk[:path]
177
+ @logger.debug "Overriding disk path with: #{disk[:absolute_path]}"
178
+ else
179
+ disk_pool_name = @storage_pool_name
180
+ end
181
+
170
182
  # make the disk. equivalent to:
171
183
  # qemu-img create -f qcow2 <path> 5g
172
184
  begin
@@ -175,8 +187,10 @@ module VagrantPlugins
175
187
  format_type: disk[:type],
176
188
  path: disk[:absolute_path],
177
189
  capacity: disk[:size],
190
+ owner: storage_uid(env),
191
+ group: storage_uid(env),
178
192
  #:allocation => ?,
179
- pool_name: @storage_pool_name
193
+ pool_name: disk_pool_name
180
194
  )
181
195
  rescue Libvirt::Error => e
182
196
  # It is hard to believe that e contains just a string
@@ -186,7 +200,7 @@ module VagrantPlugins
186
200
  if e.message == msg and disk[:allow_existing]
187
201
  disk[:preexisting] = true
188
202
  else
189
- raise Errors::FogDomainVolumeCreateError,
203
+ raise Errors::FogCreateDomainVolumeError,
190
204
  error_message: e.message
191
205
  end
192
206
  end
@@ -195,6 +209,8 @@ module VagrantPlugins
195
209
  # Output the settings we're going to use to the user
196
210
  env[:ui].info(I18n.t('vagrant_libvirt.creating_domain'))
197
211
  env[:ui].info(" -- Name: #{@name}")
212
+ env[:ui].info(" -- Title: #{@title}") if @title != ''
213
+ env[:ui].info(" -- Description: #{@description}") if @description != ''
198
214
  env[:ui].info(" -- Forced UUID: #{@uuid}") if @uuid != ''
199
215
  env[:ui].info(" -- Domain type: #{@domain_type}")
200
216
  env[:ui].info(" -- Cpus: #{@cpus}")
@@ -213,6 +229,10 @@ module VagrantPlugins
213
229
  @features_hyperv.each do |feature|
214
230
  env[:ui].info(" -- Feature (HyperV): name=#{feature[:name]}, state=#{feature[:state]}")
215
231
  end
232
+ env[:ui].info(" -- Clock offset: #{@clock_offset}")
233
+ @clock_timers.each do |timer|
234
+ env[:ui].info(" -- Clock timer: #{timer.map { |k,v| "#{k}=#{v}"}.join(', ')}")
235
+ end
216
236
  env[:ui].info(" -- Memory: #{@memory_size / 1024}M")
217
237
  unless @nodeset.nil?
218
238
  env[:ui].info(" -- Nodeset: #{@nodeset}")
@@ -231,7 +251,13 @@ module VagrantPlugins
231
251
  end
232
252
  env[:ui].info(" -- Storage pool: #{@storage_pool_name}")
233
253
  env[:ui].info(" -- Image: #{@domain_volume_path} (#{env[:box_virtual_size]}G)")
234
- env[:ui].info(" -- Volume Cache: #{@domain_volume_cache}")
254
+
255
+ if not @disk_driver_opts.empty?
256
+ env[:ui].info(" -- Disk driver opts: #{@disk_driver_opts.reject { |k,v| v.nil? }.map { |k,v| "#{k}='#{v}'"}.join(' ')}")
257
+ else
258
+ env[:ui].info(" -- Disk driver opts: cache='#{@domain_volume_cache}'")
259
+ end
260
+
235
261
  env[:ui].info(" -- Kernel: #{@kernel}")
236
262
  env[:ui].info(" -- Initrd: #{@initrd}")
237
263
  env[:ui].info(" -- Graphics Type: #{@graphics_type}")
@@ -242,7 +268,13 @@ module VagrantPlugins
242
268
  env[:ui].info(" -- Video VRAM: #{@video_vram}")
243
269
  env[:ui].info(" -- Sound Type: #{@sound_type}")
244
270
  env[:ui].info(" -- Keymap: #{@keymap}")
245
- env[:ui].info(" -- TPM Path: #{@tpm_path}")
271
+ env[:ui].info(" -- TPM Backend: #{@tpm_type}")
272
+ if @tpm_type == 'emulator'
273
+ env[:ui].info(" -- TPM Model: #{@tpm_model}")
274
+ env[:ui].info(" -- TPM Version: #{@tpm_version}")
275
+ else
276
+ env[:ui].info(" -- TPM Path: #{@tpm_path}")
277
+ end
246
278
 
247
279
  @boot_order.each do |device|
248
280
  env[:ui].info(" -- Boot device: #{device}")
@@ -362,6 +394,14 @@ module VagrantPlugins
362
394
 
363
395
  @app.call(env)
364
396
  end
397
+
398
+ private
399
+ def get_disk_storage_prefix(env, disk_pool_name)
400
+ disk_storage_pool = env[:machine].provider.driver.connection.client.lookup_storage_pool_by_name(disk_pool_name)
401
+ raise Errors::NoStoragePool if disk_storage_pool.nil?
402
+ xml = Nokogiri::XML(disk_storage_pool.xml_desc)
403
+ disk_storage_prefix = xml.xpath('/pool/target/path').inner_text.to_s + '/'
404
+ end
365
405
  end
366
406
  end
367
407
  end
@@ -51,7 +51,6 @@ module VagrantPlugins
51
51
  xml.permissions do
52
52
  xml.owner storage_uid(env)
53
53
  xml.group storage_gid(env)
54
- xml.mode '0600'
55
54
  xml.label 'virt_image_t'
56
55
  end
57
56
  end
@@ -61,7 +60,6 @@ module VagrantPlugins
61
60
  xml.permissions do
62
61
  xml.owner storage_uid(env)
63
62
  xml.group storage_gid(env)
64
- xml.mode '0600'
65
63
  xml.label 'virt_image_t'
66
64
  end
67
65
  end
@@ -95,6 +95,7 @@ module VagrantPlugins
95
95
  template_name = 'public_interface'
96
96
  @logger.info("Setting up public interface using device #{@device} in mode #{@mode}")
97
97
  @ovs = iface_configuration.fetch(:ovs, false)
98
+ @ovs_interfaceid = iface_configuration.fetch(:ovs_interfaceid, false)
98
99
  @trust_guest_rx_filters = iface_configuration.fetch(:trust_guest_rx_filters, false)
99
100
  # configuration for udp or tcp tunnel interfaces (p2p conn btwn guest OSes)
100
101
  elsif iface_configuration.fetch(:tunnel_type, nil)
@@ -98,6 +98,7 @@ module VagrantPlugins
98
98
  Port=#{ssh_info[:port]}
99
99
  UserKnownHostsFile=/dev/null
100
100
  ExitOnForwardFailure=yes
101
+ ControlMaster=no
101
102
  StrictHostKeyChecking=no
102
103
  PasswordAuthentication=no
103
104
  ForwardX11=#{ssh_info[:forward_x11] ? 'yes' : 'no'}
@@ -106,10 +107,10 @@ module VagrantPlugins
106
107
  "IdentityFile='\"#{pk}\"'"
107
108
  end).map { |s| s.prepend('-o ') }.join(' ')
108
109
 
109
- options += " -o ProxyCommand=\"#{ssh_info[:proxy_command]}\"" if machine.provider_config.connect_via_ssh
110
+ options += " -o ProxyCommand=\"#{ssh_info[:proxy_command]}\"" if machine.provider_config.proxy_command
110
111
 
111
112
  # TODO: instead of this, try and lock and get the stdin from spawn...
112
- ssh_cmd = 'exec '
113
+ ssh_cmd = ''
113
114
  if host_port <= 1024
114
115
  @@lock.synchronize do
115
116
  # TODO: add i18n
@@ -127,7 +128,7 @@ module VagrantPlugins
127
128
  log_file = ssh_forward_log_file(host_ip, host_port,
128
129
  guest_ip, guest_port)
129
130
  @logger.info "Logging to #{log_file}"
130
- spawn(ssh_cmd, [:out, :err] => [log_file, 'w'])
131
+ spawn(ssh_cmd, [:out, :err] => [log_file, 'w'], :pgroup => true)
131
132
  end
132
133
 
133
134
  def ssh_forward_log_file(host_ip, host_port, guest_ip, guest_port)
@@ -5,6 +5,7 @@ module VagrantPlugins
5
5
  module Action
6
6
  class HandleBoxImage
7
7
  include VagrantPlugins::ProviderLibvirt::Util::StorageUtil
8
+ include VagrantPlugins::ProviderLibvirt::Util::Ui
8
9
 
9
10
 
10
11
  @@lock = Mutex.new
@@ -102,13 +103,15 @@ module VagrantPlugins
102
103
  # Upload box image to storage pool
103
104
  ret = upload_image(box_image_file, config.storage_pool_name,
104
105
  env[:box_volume_name], env) do |progress|
105
- env[:ui].clear_line
106
- env[:ui].report_progress(progress, box_image_size, false)
106
+ rewriting(env[:ui]) do |ui|
107
+ ui.clear_line
108
+ ui.report_progress(progress, box_image_size, false)
109
+ end
107
110
  end
108
111
 
109
112
  # Clear the line one last time since the progress meter doesn't
110
113
  # disappear immediately.
111
- env[:ui].clear_line
114
+ rewriting(env[:ui]) {|ui| ui.clear_line}
112
115
 
113
116
  # If upload failed or was interrupted, remove created volume from
114
117
  # storage pool.
@@ -6,6 +6,9 @@ module VagrantPlugins
6
6
  module Action
7
7
  # Action for create new box for Libvirt provider
8
8
  class PackageDomain
9
+ include VagrantPlugins::ProviderLibvirt::Util::Ui
10
+
11
+
9
12
  def initialize(app, env)
10
13
  @logger = Log4r::Logger.new('vagrant_libvirt::action::package_domain')
11
14
  @app = app
@@ -31,12 +34,14 @@ module VagrantPlugins
31
34
  env[:ui].info("Downloading #{root_disk.name} to #{@tmp_img}")
32
35
  ret = download_image(@tmp_img, env[:machine].provider_config.storage_pool_name,
33
36
  root_disk.name, env) do |progress,image_size|
34
- env[:ui].clear_line
35
- env[:ui].report_progress(progress, image_size, false)
37
+ rewriting(env[:ui]) do |ui|
38
+ ui.clear_line
39
+ ui.report_progress(progress, image_size, false)
40
+ end
36
41
  end
37
42
  # Clear the line one last time since the progress meter doesn't
38
43
  # disappear immediately.
39
- env[:ui].clear_line
44
+ rewriting(env[:ui]) {|ui| ui.clear_line}
40
45
  backing = `qemu-img info "#{@tmp_img}" | grep 'backing file:' | cut -d ':' -f2`.chomp
41
46
  if backing
42
47
  env[:ui].info('Image has backing image, copying image and rebasing ...')
@@ -45,8 +50,9 @@ module VagrantPlugins
45
50
  # remove hw association with interface
46
51
  # working for centos with lvs default disks
47
52
  options = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPTIONS', '')
48
- operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir')
53
+ operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir,-customize')
49
54
  `virt-sysprep --no-logfile --operations #{operations} -a #{@tmp_img} #{options}`
55
+ `virt-sparsify --in-place #{@tmp_img}`
50
56
  # add any user provided file
51
57
  extra = ''
52
58
  @tmp_include = @tmp_dir + '/_include'
@@ -8,15 +8,16 @@ module VagrantPlugins
8
8
  include VagrantPlugins::ProviderLibvirt::Util::Nfs
9
9
 
10
10
  def initialize(app, _env)
11
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::prune_nfs_exports')
11
12
  @app = app
12
13
  end
13
14
 
14
15
  def call(env)
15
- @machine = env[:machine]
16
+ @machine = env[:machine]
16
17
 
17
- if using_nfs?
18
+ if using_nfs?
18
19
  @logger.info('Using NFS, prunning NFS settings from host')
19
- if env[:host]
20
+ if env[:host]
20
21
  uuid = env[:machine].id
21
22
  # get all uuids
22
23
  uuids = env[:machine].provider.driver.connection.servers.all.map(&:id)
@@ -37,12 +37,16 @@ module VagrantPlugins
37
37
  xml_descr = REXML::Document.new descr
38
38
  descr_changed = false
39
39
 
40
+ # For outputting XML for comparison
41
+ formatter = REXML::Formatters::Pretty.new
42
+
40
43
  # additional disk bus
41
44
  config.disks.each do |disk|
42
45
  device = disk[:device]
43
46
  bus = disk[:bus]
44
47
  REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="' + device + '"]') do |disk_target|
45
48
  next unless disk_target.attributes['bus'] != bus
49
+ @logger.debug "disk #{device} bus updated from '#{disk_target.attributes['bus']}' to '#{bus}'"
46
50
  descr_changed = true
47
51
  disk_target.attributes['bus'] = bus
48
52
  disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
@@ -52,6 +56,7 @@ module VagrantPlugins
52
56
  # disk_bus
53
57
  REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="vda"]') do |disk_target|
54
58
  next unless disk_target.attributes['bus'] != config.disk_bus
59
+ @logger.debug "domain disk bus updated from '#{disk_target.attributes['bus']}' to '#{bus}'"
55
60
  descr_changed = true
56
61
  disk_target.attributes['bus'] = config.disk_bus
57
62
  disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
@@ -61,6 +66,7 @@ module VagrantPlugins
61
66
  unless config.nic_model_type.nil?
62
67
  REXML::XPath.each(xml_descr, '/domain/devices/interface/model') do |iface_model|
63
68
  if iface_model.attributes['type'] != config.nic_model_type
69
+ @logger.debug "network type updated from '#{iface_model.attributes['type']}' to '#{config.nic_model_type}'"
64
70
  descr_changed = true
65
71
  iface_model.attributes['type'] = config.nic_model_type
66
72
  end
@@ -68,7 +74,9 @@ module VagrantPlugins
68
74
  end
69
75
 
70
76
  # vCpu count
71
- if config.cpus.to_i != libvirt_domain.num_vcpus(0)
77
+ vcpus_count = libvirt_domain.num_vcpus(0)
78
+ if config.cpus.to_i != vcpus_count
79
+ @logger.debug "cpu count updated from '#{vcpus_count}' to '#{config.cpus}'"
72
80
  descr_changed = true
73
81
  REXML::XPath.first(xml_descr, '/domain/vcpu').text = config.cpus
74
82
  end
@@ -76,11 +84,13 @@ module VagrantPlugins
76
84
  # cpu_mode
77
85
  cpu = REXML::XPath.first(xml_descr, '/domain/cpu')
78
86
  if cpu.nil?
87
+ @logger.debug "cpu_mode updated from not set to '#{config.cpu_mode}'"
79
88
  descr_changed = true
80
89
  cpu = REXML::Element.new('cpu', REXML::XPath.first(xml_descr, '/domain'))
81
90
  cpu.attributes['mode'] = config.cpu_mode
82
91
  else
83
92
  if cpu.attributes['mode'] != config.cpu_mode
93
+ @logger.debug "cpu_mode updated from '#{cpu.attributes['mode']}' to '#{config.cpu_mode}'"
84
94
  descr_changed = true
85
95
  cpu.attributes['mode'] = config.cpu_mode
86
96
  end
@@ -89,16 +99,19 @@ module VagrantPlugins
89
99
  if config.cpu_mode != 'host-passthrough'
90
100
  cpu_model = REXML::XPath.first(xml_descr, '/domain/cpu/model')
91
101
  if cpu_model.nil?
102
+ @logger.debug "cpu_model updated from not set to '#{config.cpu_model}'"
92
103
  descr_changed = true
93
104
  cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
94
105
  cpu_model.attributes['fallback'] = 'allow'
95
106
  cpu_model.text = config.cpu_model
96
107
  else
97
- if cpu_model.text != config.cpu_model
108
+ if (cpu_model.text or '').strip != config.cpu_model.strip
109
+ @logger.debug "cpu_model text updated from #{cpu_model.text} to '#{config.cpu_model}'"
98
110
  descr_changed = true
99
111
  cpu_model.text = config.cpu_model
100
112
  end
101
113
  if cpu_model.attributes['fallback'] != config.cpu_fallback
114
+ @logger.debug "cpu_model fallback attribute updated from #{cpu_model.attributes['fallback']} to '#{config.cpu_fallback}'"
102
115
  descr_changed = true
103
116
  cpu_model.attributes['fallback'] = config.cpu_fallback
104
117
  end
@@ -107,12 +120,14 @@ module VagrantPlugins
107
120
  svm_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="svm"]')
108
121
  if config.nested
109
122
  if vmx_feature.nil?
123
+ @logger.debug "nested mode enabled from unset by setting cpu vmx feature"
110
124
  descr_changed = true
111
125
  vmx_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
112
126
  vmx_feature.attributes['policy'] = 'optional'
113
127
  vmx_feature.attributes['name'] = 'vmx'
114
128
  end
115
129
  if svm_feature.nil?
130
+ @logger.debug "nested mode enabled from unset by setting cpu svm feature"
116
131
  descr_changed = true
117
132
  svm_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
118
133
  svm_feature.attributes['policy'] = 'optional'
@@ -120,16 +135,19 @@ module VagrantPlugins
120
135
  end
121
136
  else
122
137
  unless vmx_feature.nil?
138
+ @logger.debug "nested mode disabled for cpu by removing vmx feature"
123
139
  descr_changed = true
124
140
  cpu.delete_element(vmx_feature)
125
141
  end
126
142
  unless svm_feature.nil?
143
+ @logger.debug "nested mode disabled for cpu by removing svm feature"
127
144
  descr_changed = true
128
145
  cpu.delete_element(svm_feature)
129
146
  end
130
147
  end
131
148
  elsif config.numa_nodes == nil
132
149
  unless cpu.elements.to_a.empty?
150
+ @logger.debug "switching cpu_mode to host-passthrough and removing emulated cpu features"
133
151
  descr_changed = true
134
152
  cpu.elements.each do |elem|
135
153
  cpu.delete_element(elem)
@@ -137,6 +155,34 @@ module VagrantPlugins
137
155
  end
138
156
  end
139
157
 
158
+ # Clock
159
+ clock = REXML::XPath.first(xml_descr, '/domain/clock')
160
+ if clock.attributes['offset'] != config.clock_offset
161
+ @logger.debug "clock offset changed"
162
+ descr_changed = true
163
+ clock.attributes['offset'] = config.clock_offset
164
+ end
165
+
166
+ # clock timers - because timers can be added/removed, just rebuild and then compare
167
+ if !config.clock_timers.empty? || clock.has_elements?
168
+ oldclock = ''
169
+ formatter.write(REXML::XPath.first(xml_descr, '/domain/clock'), oldclock)
170
+ clock.delete_element('//timer')
171
+ config.clock_timers.each do |clock_timer|
172
+ timer = REXML::Element.new('timer', clock)
173
+ clock_timer.each do |attr, value|
174
+ timer.attributes[attr.to_s] = value
175
+ end
176
+ end
177
+
178
+ newclock = ''
179
+ formatter.write(clock, newclock)
180
+ unless newclock.eql? oldclock
181
+ @logger.debug "clock timers config changed"
182
+ descr_changed = true
183
+ end
184
+ end
185
+
140
186
  # Graphics
141
187
  graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
142
188
  if config.graphics_type != 'none'
@@ -178,31 +224,31 @@ module VagrantPlugins
178
224
  end
179
225
 
180
226
  # TPM
181
- if config.tpm_path
182
- raise Errors::FogCreateServerError, 'The TPM Path must be fully qualified' unless config.tpm_path[0].chr == '/'
227
+ if [config.tpm_path, config.tpm_version].any?
228
+ if config.tpm_path
229
+ raise Errors::FogCreateServerError, 'The TPM Path must be fully qualified' unless config.tpm_path[0].chr == '/'
230
+ end
183
231
 
184
- tpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
185
- if tpm.nil?
232
+ # just build the tpm element every time
233
+ # check at the end if it is different
234
+ oldtpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
235
+ REXML::XPath.first(xml_descr, '/domain/devices').delete_element("tpm")
236
+ newtpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices'))
237
+
238
+ newtpm.attributes['model'] = config.tpm_model
239
+ backend = newtpm.add_element('backend')
240
+ backend.attributes['type'] = config.tpm_type
241
+
242
+ case config.tpm_type
243
+ when 'emulator'
244
+ backend.attributes['version'] = config.tpm_version
245
+ when 'passthrough'
246
+ backend.add_element('device').attributes['path'] = config.tpm_path
247
+ end
248
+
249
+ unless "'#{newtpm}'".eql? "'#{oldtpm}'"
250
+ @logger.debug "tpm config changed"
186
251
  descr_changed = true
187
- tpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices/tpm/model'))
188
- tpm.attributes['model'] = config.tpm_model
189
- tpm_backend_type = tpm.add_element('backend')
190
- tpm_backend_type.attributes['type'] = config.tpm_type
191
- tpm_device_path = tpm_backend_type.add_element('device')
192
- tpm_device_path.attributes['path'] = config.tpm_path
193
- else
194
- if tpm.attributes['model'] != config.tpm_model
195
- descr_changed = true
196
- tpm.attributes['model'] = config.tpm_model
197
- end
198
- if tpm.elements['backend'].attributes['type'] != config.tpm_type
199
- descr_changed = true
200
- tpm.elements['backend'].attributes['type'] = config.tpm_type
201
- end
202
- if tpm.elements['backend'].elements['device'].attributes['path'] != config.tpm_path
203
- descr_changed = true
204
- tpm.elements['backend'].elements['device'].attributes['path'] = config.tpm_path
205
- end
206
252
  end
207
253
  end
208
254
 
@@ -210,16 +256,21 @@ module VagrantPlugins
210
256
  video = REXML::XPath.first(xml_descr, '/domain/devices/video')
211
257
  if !video.nil? && (config.graphics_type == 'none')
212
258
  # graphics_type = none, video devices are removed since there is no possible output
259
+ @logger.debug "deleting video elements as config.graphics_type is none"
213
260
  descr_changed = true
214
261
  video.parent.delete_element(video)
215
262
  else
216
263
  video_model = REXML::XPath.first(xml_descr, '/domain/devices/video/model')
217
264
  if video_model.nil?
265
+ @logger.debug "video updated from not set to type '#{config.video_type}' and vram '#{config.video_vram}'"
266
+ descr_changed = true
218
267
  video_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/devices/video'))
219
268
  video_model.attributes['type'] = config.video_type
220
269
  video_model.attributes['vram'] = config.video_vram
221
270
  else
222
- if video_model.attributes['type'] != config.video_type || video_model.attributes['vram'] != config.video_vram
271
+ if video_model.attributes['type'] != config.video_type || video_model.attributes['vram'] != config.video_vram.to_s
272
+ @logger.debug "video type updated from '#{video_model.attributes['type']}' to '#{config.video_type}'"
273
+ @logger.debug "video vram updated from '#{video_model.attributes['vram']}' to '#{config.video_vram}'"
223
274
  descr_changed = true
224
275
  video_model.attributes['type'] = config.video_type
225
276
  video_model.attributes['vram'] = config.video_vram
@@ -237,11 +288,13 @@ module VagrantPlugins
237
288
  if config.dtb
238
289
  dtb = REXML::XPath.first(xml_descr, '/domain/os/dtb')
239
290
  if dtb.nil?
291
+ @logger.debug "dtb updated from not set to '#{config.dtb}'"
240
292
  descr_changed = true
241
293
  dtb = REXML::Element.new('dtb', REXML::XPath.first(xml_descr, '/domain/os'))
242
294
  dtb.text = config.dtb
243
295
  else
244
- if dtb.text != config.dtb
296
+ if (dtb.text or '') != config.dtb
297
+ @logger.debug "dtb updated from '#{dtb.text}' to '#{config.dtb}'"
245
298
  descr_changed = true
246
299
  dtb.text = config.dtb
247
300
  end
@@ -252,11 +305,13 @@ module VagrantPlugins
252
305
  if config.kernel
253
306
  kernel = REXML::XPath.first(xml_descr, '/domain/os/kernel')
254
307
  if kernel.nil?
308
+ @logger.debug "kernel updated from not set to '#{config.kernel}'"
255
309
  descr_changed = true
256
310
  kernel = REXML::Element.new('kernel', REXML::XPath.first(xml_descr, '/domain/os'))
257
311
  kernel.text = config.kernel
258
312
  else
259
- if kernel.text != config.kernel
313
+ if (kernel.text or '').strip != config.kernel
314
+ @logger.debug "kernel updated from '#{kernel.text}' to '#{config.kernel}'"
260
315
  descr_changed = true
261
316
  kernel.text = config.kernel
262
317
  end
@@ -265,11 +320,13 @@ module VagrantPlugins
265
320
  if config.initrd
266
321
  initrd = REXML::XPath.first(xml_descr, '/domain/os/initrd')
267
322
  if initrd.nil?
323
+ @logger.debug "initrd updated from not set to '#{config.initrd}'"
268
324
  descr_changed = true
269
325
  initrd = REXML::Element.new('initrd', REXML::XPath.first(xml_descr, '/domain/os'))
270
326
  initrd.text = config.initrd
271
327
  else
272
- if initrd.text != config.initrd
328
+ if (initrd.text or '').strip != config.initrd
329
+ @logger.debug "initrd updated from '#{initrd.text}' to '#{config.initrd}'"
273
330
  descr_changed = true
274
331
  initrd.text = config.initrd
275
332
  end