vagrant-libvirt 0.10.6 → 0.10.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)