vagrant-libvirt 0.10.6 → 0.10.8

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.
@@ -3,6 +3,8 @@
3
3
  require 'log4r'
4
4
 
5
5
  require 'rexml/document'
6
+ require 'rexml/formatters/pretty'
7
+ require 'rexml/xpath'
6
8
 
7
9
  require 'vagrant-libvirt/util/xml'
8
10
 
@@ -20,438 +22,450 @@ module VagrantPlugins
20
22
  def call(env)
21
23
  domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
22
24
  raise Errors::NoDomainError if domain.nil?
25
+
23
26
  config = env[:machine].provider_config
24
27
 
25
- begin
26
- # update domain settings on change.
28
+ # update domain settings on change.
29
+ libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(env[:machine].id)
27
30
 
28
- libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(env[:machine].id)
31
+ # Libvirt API doesn't support modifying memory on NUMA enabled CPUs
32
+ # http://libvirt.org/git/?p=libvirt.git;a=commit;h=d174394105cf00ed266bf729ddf461c21637c736
33
+ if config.numa_nodes == nil
34
+ if config.memory.to_i * 1024 != libvirt_domain.max_memory
35
+ libvirt_domain.max_memory = config.memory.to_i * 1024
36
+ libvirt_domain.memory = libvirt_domain.max_memory
37
+ end
38
+ end
29
39
 
30
- # Libvirt API doesn't support modifying memory on NUMA enabled CPUs
31
- # http://libvirt.org/git/?p=libvirt.git;a=commit;h=d174394105cf00ed266bf729ddf461c21637c736
32
- if config.numa_nodes == nil
33
- if config.memory.to_i * 1024 != libvirt_domain.max_memory
34
- libvirt_domain.max_memory = config.memory.to_i * 1024
35
- libvirt_domain.memory = libvirt_domain.max_memory
36
- end
40
+ # XML definition manipulation
41
+ descr = libvirt_domain.xml_desc(1)
42
+ xml_descr = REXML::Document.new descr
43
+ descr_changed = false
44
+
45
+ # For outputting XML for comparison
46
+ formatter = REXML::Formatters::Pretty.new
47
+
48
+ # additional disk bus
49
+ config.disks.each do |disk|
50
+ device = disk[:device]
51
+ bus = disk[:bus]
52
+ REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="' + device + '"]') do |disk_target|
53
+ next unless disk_target.attributes['bus'] != bus
54
+ @logger.debug "disk #{device} bus updated from '#{disk_target.attributes['bus']}' to '#{bus}'"
55
+ descr_changed = true
56
+ disk_target.attributes['bus'] = bus
57
+ disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
37
58
  end
59
+ end
38
60
 
39
- begin
40
- # XML definition manipulation
41
- descr = libvirt_domain.xml_desc(1)
42
- xml_descr = REXML::Document.new descr
43
- descr_changed = false
44
-
45
- # For outputting XML for comparison
46
- formatter = REXML::Formatters::Pretty.new
47
-
48
- # additional disk bus
49
- config.disks.each do |disk|
50
- device = disk[:device]
51
- bus = disk[:bus]
52
- REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="' + device + '"]') do |disk_target|
53
- next unless disk_target.attributes['bus'] != bus
54
- @logger.debug "disk #{device} bus updated from '#{disk_target.attributes['bus']}' to '#{bus}'"
55
- descr_changed = true
56
- disk_target.attributes['bus'] = bus
57
- disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
58
- end
59
- end
61
+ # disk_bus
62
+ REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="vda"]') do |disk_target|
63
+ next unless disk_target.attributes['bus'] != config.disk_bus
64
+ @logger.debug "domain disk bus updated from '#{disk_target.attributes['bus']}' to '#{config.disk_bus}'"
65
+ descr_changed = true
66
+ disk_target.attributes['bus'] = config.disk_bus
67
+ disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
68
+ end
60
69
 
61
- # disk_bus
62
- REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="vda"]') do |disk_target|
63
- next unless disk_target.attributes['bus'] != config.disk_bus
64
- @logger.debug "domain disk bus updated from '#{disk_target.attributes['bus']}' to '#{config.disk_bus}'"
70
+ # Interface type
71
+ unless config.nic_model_type.nil?
72
+ REXML::XPath.each(xml_descr, '/domain/devices/interface/model') do |iface_model|
73
+ if iface_model.attributes['type'] != config.nic_model_type
74
+ @logger.debug "network type updated from '#{iface_model.attributes['type']}' to '#{config.nic_model_type}'"
65
75
  descr_changed = true
66
- disk_target.attributes['bus'] = config.disk_bus
67
- disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
76
+ iface_model.attributes['type'] = config.nic_model_type
68
77
  end
78
+ end
79
+ end
69
80
 
70
- # Interface type
71
- unless config.nic_model_type.nil?
72
- REXML::XPath.each(xml_descr, '/domain/devices/interface/model') do |iface_model|
73
- if iface_model.attributes['type'] != config.nic_model_type
74
- @logger.debug "network type updated from '#{iface_model.attributes['type']}' to '#{config.nic_model_type}'"
75
- descr_changed = true
76
- iface_model.attributes['type'] = config.nic_model_type
77
- end
78
- end
79
- end
81
+ # vCpu count
82
+ vcpus_count = libvirt_domain.num_vcpus(0)
83
+ if config.cpus.to_i != vcpus_count
84
+ @logger.debug "cpu count updated from '#{vcpus_count}' to '#{config.cpus}'"
85
+ descr_changed = true
86
+ REXML::XPath.first(xml_descr, '/domain/vcpu').text = config.cpus
87
+ end
80
88
 
81
- # vCpu count
82
- vcpus_count = libvirt_domain.num_vcpus(0)
83
- if config.cpus.to_i != vcpus_count
84
- @logger.debug "cpu count updated from '#{vcpus_count}' to '#{config.cpus}'"
89
+ # cpu_mode
90
+ cpu = REXML::XPath.first(xml_descr, '/domain/cpu')
91
+ if cpu.nil?
92
+ @logger.debug "cpu_mode updated from not set to '#{config.cpu_mode}'"
93
+ descr_changed = true
94
+ cpu = REXML::Element.new('cpu', REXML::XPath.first(xml_descr, '/domain'))
95
+ cpu.attributes['mode'] = config.cpu_mode
96
+ else
97
+ if cpu.attributes['mode'] != config.cpu_mode
98
+ @logger.debug "cpu_mode updated from '#{cpu.attributes['mode']}' to '#{config.cpu_mode}'"
99
+ descr_changed = true
100
+ cpu.attributes['mode'] = config.cpu_mode
101
+ end
102
+ end
103
+
104
+ if config.cpu_mode != 'host-passthrough'
105
+ cpu_model = REXML::XPath.first(xml_descr, '/domain/cpu/model')
106
+ if cpu_model.nil?
107
+ if config.cpu_model.strip != ''
108
+ @logger.debug "cpu_model updated from not set to '#{config.cpu_model}'"
85
109
  descr_changed = true
86
- REXML::XPath.first(xml_descr, '/domain/vcpu').text = config.cpus
110
+ cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
111
+ cpu_model.attributes['fallback'] = 'allow'
112
+ cpu_model.text = config.cpu_model
87
113
  end
88
-
89
- # cpu_mode
90
- cpu = REXML::XPath.first(xml_descr, '/domain/cpu')
91
- if cpu.nil?
92
- @logger.debug "cpu_mode updated from not set to '#{config.cpu_mode}'"
114
+ else
115
+ if (cpu_model.text or '').strip != config.cpu_model.strip
116
+ @logger.debug "cpu_model text updated from #{cpu_model.text} to '#{config.cpu_model}'"
93
117
  descr_changed = true
94
- cpu = REXML::Element.new('cpu', REXML::XPath.first(xml_descr, '/domain'))
95
- cpu.attributes['mode'] = config.cpu_mode
96
- else
97
- if cpu.attributes['mode'] != config.cpu_mode
98
- @logger.debug "cpu_mode updated from '#{cpu.attributes['mode']}' to '#{config.cpu_mode}'"
99
- descr_changed = true
100
- cpu.attributes['mode'] = config.cpu_mode
101
- end
118
+ cpu_model.text = config.cpu_model
102
119
  end
103
-
104
- if config.cpu_mode != 'host-passthrough'
105
- cpu_model = REXML::XPath.first(xml_descr, '/domain/cpu/model')
106
- if cpu_model.nil?
107
- if config.cpu_model.strip != ''
108
- @logger.debug "cpu_model updated from not set to '#{config.cpu_model}'"
109
- descr_changed = true
110
- cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
111
- cpu_model.attributes['fallback'] = 'allow'
112
- cpu_model.text = config.cpu_model
113
- end
114
- else
115
- if (cpu_model.text or '').strip != config.cpu_model.strip
116
- @logger.debug "cpu_model text updated from #{cpu_model.text} to '#{config.cpu_model}'"
117
- descr_changed = true
118
- cpu_model.text = config.cpu_model
119
- end
120
- if cpu_model.attributes['fallback'] != config.cpu_fallback
121
- @logger.debug "cpu_model fallback attribute updated from #{cpu_model.attributes['fallback']} to '#{config.cpu_fallback}'"
122
- descr_changed = true
123
- cpu_model.attributes['fallback'] = config.cpu_fallback
124
- end
125
- end
126
- vmx_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="vmx"]')
127
- svm_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="svm"]')
128
- if config.nested
129
- if vmx_feature.nil?
130
- @logger.debug "nested mode enabled from unset by setting cpu vmx feature"
131
- descr_changed = true
132
- vmx_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
133
- vmx_feature.attributes['policy'] = 'optional'
134
- vmx_feature.attributes['name'] = 'vmx'
135
- end
136
- if svm_feature.nil?
137
- @logger.debug "nested mode enabled from unset by setting cpu svm feature"
138
- descr_changed = true
139
- svm_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
140
- svm_feature.attributes['policy'] = 'optional'
141
- svm_feature.attributes['name'] = 'svm'
142
- end
143
- else
144
- unless vmx_feature.nil?
145
- @logger.debug "nested mode disabled for cpu by removing vmx feature"
146
- descr_changed = true
147
- cpu.delete_element(vmx_feature)
148
- end
149
- unless svm_feature.nil?
150
- @logger.debug "nested mode disabled for cpu by removing svm feature"
151
- descr_changed = true
152
- cpu.delete_element(svm_feature)
153
- end
154
- end
155
- elsif config.numa_nodes == nil
156
- unless cpu.elements.to_a.empty?
157
- @logger.debug "switching cpu_mode to host-passthrough and removing emulated cpu features"
158
- descr_changed = true
159
- cpu.elements.each do |elem|
160
- cpu.delete_element(elem)
161
- end
162
- end
120
+ if cpu_model.attributes['fallback'] != config.cpu_fallback
121
+ @logger.debug "cpu_model fallback attribute updated from #{cpu_model.attributes['fallback']} to '#{config.cpu_fallback}'"
122
+ descr_changed = true
123
+ cpu_model.attributes['fallback'] = config.cpu_fallback
163
124
  end
164
-
165
- # Clock
166
- clock = REXML::XPath.first(xml_descr, '/domain/clock')
167
- if clock.attributes['offset'] != config.clock_offset
168
- @logger.debug "clock offset changed"
125
+ end
126
+ vmx_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="vmx"]')
127
+ svm_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="svm"]')
128
+ if config.nested
129
+ if vmx_feature.nil?
130
+ @logger.debug "nested mode enabled from unset by setting cpu vmx feature"
131
+ descr_changed = true
132
+ vmx_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
133
+ vmx_feature.attributes['policy'] = 'optional'
134
+ vmx_feature.attributes['name'] = 'vmx'
135
+ end
136
+ if svm_feature.nil?
137
+ @logger.debug "nested mode enabled from unset by setting cpu svm feature"
169
138
  descr_changed = true
170
- clock.attributes['offset'] = config.clock_offset
139
+ svm_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
140
+ svm_feature.attributes['policy'] = 'optional'
141
+ svm_feature.attributes['name'] = 'svm'
171
142
  end
143
+ else
144
+ unless vmx_feature.nil?
145
+ @logger.debug "nested mode disabled for cpu by removing vmx feature"
146
+ descr_changed = true
147
+ cpu.delete_element(vmx_feature)
148
+ end
149
+ unless svm_feature.nil?
150
+ @logger.debug "nested mode disabled for cpu by removing svm feature"
151
+ descr_changed = true
152
+ cpu.delete_element(svm_feature)
153
+ end
154
+ end
155
+ elsif config.numa_nodes == nil
156
+ unless cpu.elements.to_a.empty?
157
+ @logger.debug "switching cpu_mode to host-passthrough and removing emulated cpu features"
158
+ descr_changed = true
159
+ cpu.elements.each do |elem|
160
+ cpu.delete_element(elem)
161
+ end
162
+ end
163
+ end
172
164
 
173
- # clock timers - because timers can be added/removed, just rebuild and then compare
174
- if !config.clock_timers.empty? || clock.has_elements?
175
- oldclock = String.new
176
- formatter.write(REXML::XPath.first(xml_descr, '/domain/clock'), oldclock)
177
- clock.delete_element('//timer')
178
- config.clock_timers.each do |clock_timer|
179
- timer = REXML::Element.new('timer', clock)
180
- clock_timer.each do |attr, value|
181
- timer.attributes[attr.to_s] = value
182
- end
183
- end
165
+ # Clock
166
+ clock = REXML::XPath.first(xml_descr, '/domain/clock')
167
+ if clock.attributes['offset'] != config.clock_offset
168
+ @logger.debug "clock offset changed"
169
+ descr_changed = true
170
+ clock.attributes['offset'] = config.clock_offset
171
+ end
184
172
 
185
- newclock = String.new
186
- formatter.write(clock, newclock)
187
- unless newclock.eql? oldclock
188
- @logger.debug "clock timers config changed"
189
- descr_changed = true
190
- end
173
+ # clock timers - because timers can be added/removed, just rebuild and then compare
174
+ if !config.clock_timers.empty? || clock.has_elements?
175
+ oldclock = String.new
176
+ formatter.write(REXML::XPath.first(xml_descr, '/domain/clock'), oldclock)
177
+ clock.delete_element('//timer')
178
+ config.clock_timers.each do |clock_timer|
179
+ timer = REXML::Element.new('timer', clock)
180
+ clock_timer.each do |attr, value|
181
+ timer.attributes[attr.to_s] = value
191
182
  end
183
+ end
192
184
 
193
- # Graphics
194
- graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
195
- if config.graphics_type != 'none'
196
- if graphics.nil?
197
- descr_changed = true
198
- graphics = REXML::Element.new('graphics', REXML::XPath.first(xml_descr, '/domain/devices'))
199
- end
200
- if graphics.attributes['type'] != config.graphics_type
201
- descr_changed = true
202
- graphics.attributes['type'] = config.graphics_type
203
- end
204
- if graphics.attributes['listen'] != config.graphics_ip
205
- descr_changed = true
206
- graphics.attributes['listen'] = config.graphics_ip
207
- graphics.delete_element('//listen')
208
- end
209
- if graphics.attributes['autoport'] != config.graphics_autoport
210
- descr_changed = true
211
- graphics.attributes['autoport'] = config.graphics_autoport
212
- if config.graphics_autoport == 'no'
213
- graphics.attributes['port'] = config.graphics_port
214
- end
215
- end
216
- if graphics.attributes['keymap'] != config.keymap
217
- descr_changed = true
218
- graphics.attributes['keymap'] = config.keymap
219
- end
220
- if graphics.attributes['passwd'] != config.graphics_passwd
185
+ newclock = String.new
186
+ formatter.write(clock, newclock)
187
+ unless newclock.eql? oldclock
188
+ @logger.debug "clock timers config changed"
189
+ descr_changed = true
190
+ end
191
+ end
192
+
193
+ # Graphics
194
+ graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
195
+ if config.graphics_type != 'none'
196
+ if graphics.nil?
197
+ descr_changed = true
198
+ graphics = REXML::Element.new('graphics', REXML::XPath.first(xml_descr, '/domain/devices'))
199
+ end
200
+ if graphics.attributes['type'] != config.graphics_type
201
+ descr_changed = true
202
+ graphics.attributes['type'] = config.graphics_type
203
+ end
204
+ if graphics.attributes['listen'] != config.graphics_ip
205
+ descr_changed = true
206
+ graphics.attributes['listen'] = config.graphics_ip
207
+ graphics.delete_element('//listen')
208
+ end
209
+ if graphics.attributes['autoport'] != config.graphics_autoport
210
+ descr_changed = true
211
+ graphics.attributes['autoport'] = config.graphics_autoport
212
+ if config.graphics_autoport == 'no'
213
+ graphics.attributes.delete('autoport')
214
+ graphics.attributes['port'] = config.graphics_port
215
+ end
216
+ end
217
+ if graphics.attributes['keymap'] != config.keymap
218
+ descr_changed = true
219
+ graphics.attributes['keymap'] = config.keymap
220
+ end
221
+ if graphics.attributes['passwd'] != config.graphics_passwd
222
+ descr_changed = true
223
+ if config.graphics_passwd.nil?
224
+ graphics.attributes.delete 'passwd'
225
+ else
226
+ graphics.attributes['passwd'] = config.graphics_passwd
227
+ end
228
+ end
229
+ graphics_gl = REXML::XPath.first(xml_descr, '/domain/devices/graphics/gl')
230
+ if graphics_gl.nil?
231
+ if config.graphics_gl
232
+ graphics_gl = REXML::Element.new('gl', REXML::XPath.first(xml_descr, '/domain/devices/graphics'))
233
+ graphics_gl.attributes['enable'] = 'yes'
234
+ descr_changed = true
235
+ end
236
+ else
237
+ if config.graphics_gl
238
+ if graphics_gl.attributes['enable'] != 'yes'
239
+ graphics_gl.attributes['enable'] = 'yes'
221
240
  descr_changed = true
222
- if config.graphics_passwd.nil?
223
- graphics.attributes.delete 'passwd'
224
- else
225
- graphics.attributes['passwd'] = config.graphics_passwd
226
- end
227
- end
228
- graphics_gl = REXML::XPath.first(xml_descr, '/domain/devices/graphics/gl')
229
- if graphics_gl.nil?
230
- if config.graphics_gl
231
- graphics_gl = REXML::Element.new('gl', REXML::XPath.first(xml_descr, '/domain/devices/graphics'))
232
- graphics_gl.attributes['enable'] = 'yes'
233
- descr_changed = true
234
- end
235
- else
236
- if config.graphics_gl
237
- if graphics_gl.attributes['enable'] != 'yes'
238
- graphics_gl.attributes['enable'] = 'yes'
239
- descr_changed = true
240
- end
241
- else
242
- graphics_gl.parent.delete_element(graphics_gl)
243
- descr_changed = true
244
- end
245
241
  end
246
242
  else
247
- # graphics_type = none, remove entire element
248
- graphics.parent.delete_element(graphics) unless graphics.nil?
243
+ graphics_gl.parent.delete_element(graphics_gl)
244
+ descr_changed = true
249
245
  end
246
+ end
247
+ else
248
+ # graphics_type = none, remove entire element
249
+ graphics.parent.delete_element(graphics) unless graphics.nil?
250
+ end
250
251
 
251
- # TPM
252
- if [config.tpm_path, config.tpm_version].any?
253
- if config.tpm_path
254
- raise Errors::UpdateServerError, 'The TPM Path must be fully qualified' unless config.tpm_path[0].chr == '/'
255
- end
252
+ # TPM
253
+ if [config.tpm_path, config.tpm_version].any?
254
+ if config.tpm_path
255
+ raise Errors::UpdateServerError, 'The TPM Path must be fully qualified' unless config.tpm_path[0].chr == '/'
256
+ end
256
257
 
257
- # just build the tpm element every time
258
- # check at the end if it is different
259
- oldtpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
260
- REXML::XPath.first(xml_descr, '/domain/devices').delete_element("tpm")
261
- newtpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices'))
262
-
263
- newtpm.attributes['model'] = config.tpm_model
264
- backend = newtpm.add_element('backend')
265
- backend.attributes['type'] = config.tpm_type
266
-
267
- case config.tpm_type
268
- when 'emulator'
269
- backend.attributes['version'] = config.tpm_version
270
- when 'passthrough'
271
- backend.add_element('device').attributes['path'] = config.tpm_path
272
- end
258
+ # just build the tpm element every time
259
+ # check at the end if it is different
260
+ oldtpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
261
+ REXML::XPath.first(xml_descr, '/domain/devices').delete_element("tpm")
262
+ newtpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices'))
263
+
264
+ newtpm.attributes['model'] = config.tpm_model
265
+ backend = newtpm.add_element('backend')
266
+ backend.attributes['type'] = config.tpm_type
267
+
268
+ case config.tpm_type
269
+ when 'emulator'
270
+ backend.attributes['version'] = config.tpm_version
271
+ when 'passthrough'
272
+ backend.add_element('device').attributes['path'] = config.tpm_path
273
+ end
273
274
 
274
- unless "'#{newtpm}'".eql? "'#{oldtpm}'"
275
- @logger.debug "tpm config changed"
276
- descr_changed = true
277
- end
278
- end
275
+ unless "'#{newtpm}'".eql? "'#{oldtpm}'"
276
+ @logger.debug "tpm config changed"
277
+ descr_changed = true
278
+ end
279
+ end
279
280
 
280
- # Video device
281
- video = REXML::XPath.first(xml_descr, '/domain/devices/video')
282
- if !video.nil? && (config.graphics_type == 'none')
283
- # graphics_type = none, video devices are removed since there is no possible output
284
- @logger.debug "deleting video elements as config.graphics_type is none"
281
+ # Video device
282
+ video = REXML::XPath.first(xml_descr, '/domain/devices/video')
283
+ if !video.nil? && (config.graphics_type == 'none')
284
+ # graphics_type = none, video devices are removed since there is no possible output
285
+ @logger.debug "deleting video elements as config.graphics_type is none"
286
+ descr_changed = true
287
+ video.parent.delete_element(video)
288
+ else
289
+ video_model = REXML::XPath.first(xml_descr, '/domain/devices/video/model')
290
+ if video_model.nil?
291
+ @logger.debug "video updated from not set to type '#{config.video_type}' and vram '#{config.video_vram}'"
292
+ descr_changed = true
293
+ video_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/devices/video'))
294
+ video_model.attributes['type'] = config.video_type
295
+ video_model.attributes['vram'] = config.video_vram
296
+ else
297
+ if video_model.attributes['type'] != config.video_type || video_model.attributes['vram'] != config.video_vram.to_s
298
+ @logger.debug "video type updated from '#{video_model.attributes['type']}' to '#{config.video_type}'"
299
+ @logger.debug "video vram updated from '#{video_model.attributes['vram']}' to '#{config.video_vram}'"
285
300
  descr_changed = true
286
- video.parent.delete_element(video)
287
- else
288
- video_model = REXML::XPath.first(xml_descr, '/domain/devices/video/model')
289
- if video_model.nil?
290
- @logger.debug "video updated from not set to type '#{config.video_type}' and vram '#{config.video_vram}'"
301
+ video_model.attributes['type'] = config.video_type
302
+ video_model.attributes['vram'] = config.video_vram
303
+ end
304
+ end
305
+ video_accel = REXML::XPath.first(xml_descr, '/domain/devices/video/model/acceleration')
306
+ if video_accel.nil?
307
+ if config.video_accel3d
308
+ video_accel = REXML::Element.new('acceleration', REXML::XPath.first(xml_descr, '/domain/devices/video/model'))
309
+ video_accel.attributes['accel3d'] = 'yes'
310
+ descr_changed = true
311
+ end
312
+ else
313
+ if config.video_accel3d
314
+ if video_accel.attributes['accel3d'] != 'yes'
315
+ video_accel.attributes['accel3d'] = 'yes'
291
316
  descr_changed = true
292
- video_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/devices/video'))
293
- video_model.attributes['type'] = config.video_type
294
- video_model.attributes['vram'] = config.video_vram
295
- else
296
- if video_model.attributes['type'] != config.video_type || video_model.attributes['vram'] != config.video_vram.to_s
297
- @logger.debug "video type updated from '#{video_model.attributes['type']}' to '#{config.video_type}'"
298
- @logger.debug "video vram updated from '#{video_model.attributes['vram']}' to '#{config.video_vram}'"
299
- descr_changed = true
300
- video_model.attributes['type'] = config.video_type
301
- video_model.attributes['vram'] = config.video_vram
302
- end
303
- end
304
- video_accel = REXML::XPath.first(xml_descr, '/domain/devices/video/model/acceleration')
305
- if video_accel.nil?
306
- if config.video_accel3d
307
- video_accel = REXML::Element.new('acceleration', REXML::XPath.first(xml_descr, '/domain/devices/video/model'))
308
- video_accel.attributes['accel3d'] = 'yes'
309
- descr_changed = true
310
- end
311
- else
312
- if config.video_accel3d
313
- if video_accel.attributes['accel3d'] != 'yes'
314
- video_accel.attributes['accel3d'] = 'yes'
315
- descr_changed = true
316
- end
317
- else
318
- video_accel.parent.delete_element(video_accel)
319
- descr_changed = true
320
- end
321
317
  end
318
+ else
319
+ video_accel.parent.delete_element(video_accel)
320
+ descr_changed = true
322
321
  end
322
+ end
323
+ end
323
324
 
324
- # Sound device
325
- if config.sound_type
326
- sound = REXML::XPath.first(xml_descr,'/domain/devices/sound/model')
327
- end
325
+ # Sound device
326
+ if config.sound_type
327
+ sound = REXML::XPath.first(xml_descr,'/domain/devices/sound/model')
328
+ end
328
329
 
329
330
 
330
- # dtb
331
- if config.dtb
332
- dtb = REXML::XPath.first(xml_descr, '/domain/os/dtb')
333
- if dtb.nil?
334
- @logger.debug "dtb updated from not set to '#{config.dtb}'"
335
- descr_changed = true
336
- dtb = REXML::Element.new('dtb', REXML::XPath.first(xml_descr, '/domain/os'))
337
- dtb.text = config.dtb
338
- else
339
- if (dtb.text or '') != config.dtb
340
- @logger.debug "dtb updated from '#{dtb.text}' to '#{config.dtb}'"
341
- descr_changed = true
342
- dtb.text = config.dtb
343
- end
344
- end
331
+ # dtb
332
+ if config.dtb
333
+ dtb = REXML::XPath.first(xml_descr, '/domain/os/dtb')
334
+ if dtb.nil?
335
+ @logger.debug "dtb updated from not set to '#{config.dtb}'"
336
+ descr_changed = true
337
+ dtb = REXML::Element.new('dtb', REXML::XPath.first(xml_descr, '/domain/os'))
338
+ dtb.text = config.dtb
339
+ else
340
+ if (dtb.text or '') != config.dtb
341
+ @logger.debug "dtb updated from '#{dtb.text}' to '#{config.dtb}'"
342
+ descr_changed = true
343
+ dtb.text = config.dtb
345
344
  end
345
+ end
346
+ end
346
347
 
347
- # kernel and initrd
348
- if config.kernel
349
- kernel = REXML::XPath.first(xml_descr, '/domain/os/kernel')
350
- if kernel.nil?
351
- @logger.debug "kernel updated from not set to '#{config.kernel}'"
352
- descr_changed = true
353
- kernel = REXML::Element.new('kernel', REXML::XPath.first(xml_descr, '/domain/os'))
354
- kernel.text = config.kernel
355
- else
356
- if (kernel.text or '').strip != config.kernel
357
- @logger.debug "kernel updated from '#{kernel.text}' to '#{config.kernel}'"
358
- descr_changed = true
359
- kernel.text = config.kernel
360
- end
361
- end
348
+ # kernel and initrd
349
+ if config.kernel
350
+ kernel = REXML::XPath.first(xml_descr, '/domain/os/kernel')
351
+ if kernel.nil?
352
+ @logger.debug "kernel updated from not set to '#{config.kernel}'"
353
+ descr_changed = true
354
+ kernel = REXML::Element.new('kernel', REXML::XPath.first(xml_descr, '/domain/os'))
355
+ kernel.text = config.kernel
356
+ else
357
+ if (kernel.text or '').strip != config.kernel
358
+ @logger.debug "kernel updated from '#{kernel.text}' to '#{config.kernel}'"
359
+ descr_changed = true
360
+ kernel.text = config.kernel
362
361
  end
363
- if config.initrd
364
- initrd = REXML::XPath.first(xml_descr, '/domain/os/initrd')
365
- if initrd.nil?
366
- if config.initrd.strip != ''
367
- @logger.debug "initrd updated from not set to '#{config.initrd}'"
368
- descr_changed = true
369
- initrd = REXML::Element.new('initrd', REXML::XPath.first(xml_descr, '/domain/os'))
370
- initrd.text = config.initrd
371
- end
372
- else
373
- if (initrd.text or '').strip != config.initrd
374
- @logger.debug "initrd updated from '#{initrd.text}' to '#{config.initrd}'"
375
- descr_changed = true
376
- initrd.text = config.initrd
377
- end
378
- end
362
+ end
363
+ end
364
+ if config.initrd
365
+ initrd = REXML::XPath.first(xml_descr, '/domain/os/initrd')
366
+ if initrd.nil?
367
+ if config.initrd.strip != ''
368
+ @logger.debug "initrd updated from not set to '#{config.initrd}'"
369
+ descr_changed = true
370
+ initrd = REXML::Element.new('initrd', REXML::XPath.first(xml_descr, '/domain/os'))
371
+ initrd.text = config.initrd
379
372
  end
373
+ else
374
+ if (initrd.text or '').strip != config.initrd
375
+ @logger.debug "initrd updated from '#{initrd.text}' to '#{config.initrd}'"
376
+ descr_changed = true
377
+ initrd.text = config.initrd
378
+ end
379
+ end
380
+ end
380
381
 
381
- loader = REXML::XPath.first(xml_descr, '/domain/os/loader')
382
- if config.loader
383
- if loader.nil?
384
- descr_changed = true
385
- loader = REXML::Element.new('loader')
386
- REXML::XPath.first(xml_descr, '/domain/os').insert_after('//type', loader)
387
- loader.text = config.loader
388
- else
389
- if (loader.text or '').strip != config.loader
390
- descr_changed = true
391
- loader.text = config.loader
392
- end
393
- end
394
- loader.attributes['type'] = config.nvram ? 'pflash' : 'rom'
395
- elsif !loader.nil?
382
+ loader = REXML::XPath.first(xml_descr, '/domain/os/loader')
383
+ if config.loader
384
+ if loader.nil?
385
+ descr_changed = true
386
+ loader = REXML::Element.new('loader')
387
+ REXML::XPath.first(xml_descr, '/domain/os').insert_after('//type', loader)
388
+ loader.text = config.loader
389
+ else
390
+ if (loader.text or '').strip != config.loader
396
391
  descr_changed = true
397
- loader.parent.delete_element(loader)
392
+ loader.text = config.loader
398
393
  end
394
+ end
395
+ loader.attributes['type'] = config.nvram ? 'pflash' : 'rom'
396
+ elsif !loader.nil?
397
+ descr_changed = true
398
+ loader.parent.delete_element(loader)
399
+ end
399
400
 
400
- nvram = REXML::XPath.first(xml_descr, '/domain/os/nvram')
401
- if config.nvram
402
- if nvram.nil?
403
- descr_changed = true
404
- nvram = REXML::Element.new('nvram')
405
- REXML::XPath.first(xml_descr, '/domain/os').insert_after(loader, nvram)
406
- nvram.text = config.nvram
407
- else
408
- if (nvram.text or '').strip != config.nvram
409
- descr_changed = true
410
- nvram.text = config.nvram
411
- end
412
- end
413
- elsif !nvram.nil?
401
+ nvram = REXML::XPath.first(xml_descr, '/domain/os/nvram')
402
+ if config.nvram
403
+ if nvram.nil?
404
+ descr_changed = true
405
+ nvram = REXML::Element.new('nvram')
406
+ REXML::XPath.first(xml_descr, '/domain/os').insert_after(loader, nvram)
407
+ nvram.text = config.nvram
408
+ else
409
+ if (nvram.text or '').strip != config.nvram
414
410
  descr_changed = true
415
- nvram.parent.delete_element(nvram)
411
+ nvram.text = config.nvram
416
412
  end
413
+ end
414
+ elsif !nvram.nil?
415
+ descr_changed = true
416
+ nvram.parent.delete_element(nvram)
417
+ end
417
418
 
418
- # Apply
419
- if descr_changed
420
- env[:ui].info(I18n.t('vagrant_libvirt.updating_domain'))
421
- new_xml = String.new
422
- xml_descr.write(new_xml)
423
- begin
424
- # providing XML for the same name and UUID will update the existing domain
425
- libvirt_domain = env[:machine].provider.driver.connection.define_domain(new_xml)
426
- rescue ::Libvirt::Error => e
427
- raise Errors::UpdateServerError, error_message: e.message
428
- end
419
+ # Apply
420
+ if descr_changed
421
+ env[:ui].info(I18n.t('vagrant_libvirt.updating_domain'))
422
+ new_xml = String.new
423
+ xml_descr.write(new_xml)
424
+ begin
425
+ # providing XML for the same name and UUID will update the existing domain
426
+ libvirt_domain = env[:machine].provider.driver.connection.define_domain(new_xml)
427
+ rescue ::Libvirt::Error => e
428
+ env[:ui].error("Error when updating domain settings: #{e.message}")
429
+ raise Errors::UpdateServerError, error_message: e.message
430
+ end
429
431
 
430
- begin
431
- # need to check whether the updated XML contains all the changes requested
432
- proposed = VagrantPlugins::ProviderLibvirt::Util::Xml.new(new_xml)
433
- applied = VagrantPlugins::ProviderLibvirt::Util::Xml.new(libvirt_domain.xml_desc(1))
432
+ # this normalises the attribute order to be the same as what was sent in the above
433
+ # request to update the domain XML. Without this, if the XML documents are not
434
+ # equivalent, many more differences will be reported than there actually are.
435
+ applied_xml = String.new
436
+ REXML::Document.new(libvirt_domain.xml_desc(1)).write(applied_xml)
434
437
 
435
- if proposed != applied
436
- require 'diffy'
438
+ # need to check whether the updated XML contains all the changes requested
439
+ proposed = VagrantPlugins::ProviderLibvirt::Util::Xml.new(new_xml)
440
+ applied = VagrantPlugins::ProviderLibvirt::Util::Xml.new(applied_xml)
437
441
 
438
- diff = Diffy::Diff.new(proposed, applied, :context => 3).to_s(:text)
442
+ # perform some sorting to allow comparison otherwise order of devices differing
443
+ # even if they are equivalent will be reported as being different.
444
+ proposed.xml['devices'][0].each { |_, v| next unless v.is_a?(Array); v.sort_by! { |e| [e['type'], e['index']]} }
445
+ applied.xml['devices'][0].each { |_, v| next unless v.is_a?(Array); v.sort_by! { |e| [e['type'], e['index']]} }
439
446
 
440
- error_msg = "Libvirt failed to fully update the domain with the specified XML. Result differs from requested:\n" +
441
- "--- requested\n+++ result\n#{diff}\n" +
442
- "Typically this means there is a bug in the XML being sent, please log an issue"
447
+ if proposed != applied
448
+ require 'diffy'
443
449
 
444
- raise Errors::UpdateServerError, error_message: error_msg
445
- end
446
- rescue Exception => e
447
- env[:machine].provider.driver.connection.define_domain(descr)
448
- raise
449
- end
450
- end
451
- rescue Errors::VagrantLibvirtError => e
452
- env[:ui].error("Error when updating domain settings: #{e.message}")
453
- raise
450
+ diff = Diffy::Diff.new(proposed.to_str, applied.to_str, :context => 3).to_s(:text)
451
+
452
+ error_msg = "Libvirt failed to fully update the domain with the specified XML. Result differs from requested:\n" +
453
+ "--- requested\n+++ result\n#{diff}\n" +
454
+ "Typically this means there is a bug in the XML being sent, please log an issue as this will halt machine start in the future."
455
+
456
+ env[:ui].warn(error_msg)
457
+ #env[:ui].error("Updated domain settings did not fully apply, attempting restore to previous definition: #{error_msg}")
458
+ #begin
459
+ # env[:machine].provider.driver.connection.define_domain(descr)
460
+ #rescue Fog::Errors::Error => e
461
+ # env[:ui].error("Failed to restore previous domain definition: #{e.message}")
462
+ #end
463
+
464
+ #raise Errors::UpdateServerError, error_message: error_msg
454
465
  end
466
+ end
467
+
468
+ begin
455
469
  # Autostart with host if enabled in Vagrantfile
456
470
  libvirt_domain.autostart = config.autostart
457
471
  @logger.debug {
@@ -461,7 +475,7 @@ module VagrantPlugins
461
475
  env[:ui].info(I18n.t('vagrant_libvirt.starting_domain'))
462
476
  domain.start
463
477
  rescue Fog::Errors::Error, Errors::VagrantLibvirtError => e
464
- raise Errors::FogError, message: e.message
478
+ raise Errors::DomainStartError, error_message: e.message
465
479
  end
466
480
 
467
481
  @app.call(env)