vagrant-libvirt 0.10.4 → 0.10.7

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.
@@ -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)