vagrant-libvirt 0.10.6 → 0.10.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/vagrant-libvirt/action/create_domain.rb +2 -0
- data/lib/vagrant-libvirt/action/destroy_domain.rb +4 -5
- data/lib/vagrant-libvirt/action/forward_ports.rb +16 -15
- data/lib/vagrant-libvirt/action/handle_box_image.rb +2 -0
- data/lib/vagrant-libvirt/action/package_domain.rb +1 -0
- data/lib/vagrant-libvirt/action/resolve_disk_settings.rb +2 -5
- data/lib/vagrant-libvirt/action/set_boot_order.rb +44 -42
- data/lib/vagrant-libvirt/action/start_domain.rb +393 -379
- data/lib/vagrant-libvirt/cap/synced_folder_9p.rb +2 -0
- data/lib/vagrant-libvirt/cap/synced_folder_virtiofs.rb +3 -1
- data/lib/vagrant-libvirt/config.rb +1 -2
- data/lib/vagrant-libvirt/driver.rb +1 -1
- data/lib/vagrant-libvirt/errors.rb +4 -0
- data/lib/vagrant-libvirt/util/compat.rb +1 -1
- data/lib/vagrant-libvirt/util/network_util.rb +1 -1
- data/lib/vagrant-libvirt/version +1 -1
- data/locales/en.yml +2 -0
- data/spec/acceptance/support-skeletons/package_complex/scripts/sysprep.sh +0 -0
- data/spec/spec_helper.rb +18 -5
- data/spec/unit/action/set_boot_order_spec/default.xml +76 -0
- data/spec/unit/action/set_boot_order_spec/explicit_boot_order.xml +77 -0
- data/spec/unit/action/set_boot_order_spec.rb +67 -0
- data/spec/unit/action/start_domain_spec/existing_reordered.xml +62 -0
- data/spec/unit/action/start_domain_spec.rb +41 -18
- data/spec/unit/plugin_spec.rb +1 -0
- metadata +80 -72
@@ -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
|
-
|
26
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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/
|
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
|
-
|
90
|
-
|
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
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
-
|
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
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
248
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
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
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
275
|
+
unless "'#{newtpm}'".eql? "'#{oldtpm}'"
|
276
|
+
@logger.debug "tpm config changed"
|
277
|
+
descr_changed = true
|
278
|
+
end
|
279
|
+
end
|
279
280
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
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
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
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
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
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
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
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
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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.
|
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
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
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.
|
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
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
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
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
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
|
-
|
436
|
-
|
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
|
-
|
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
|
-
|
441
|
-
|
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
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
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::
|
478
|
+
raise Errors::DomainStartError, error_message: e.message
|
465
479
|
end
|
466
480
|
|
467
481
|
@app.call(env)
|