vagrant-libvirt 0.10.4 → 0.10.7

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