vagrant-libvirt 0.3.0 → 0.4.0
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 +170 -17
- data/lib/vagrant-libvirt/action/create_domain.rb +30 -9
- data/lib/vagrant-libvirt/action/forward_ports.rb +1 -1
- data/lib/vagrant-libvirt/action/package_domain.rb +2 -1
- data/lib/vagrant-libvirt/action/start_domain.rb +86 -29
- data/lib/vagrant-libvirt/action/wait_till_up.rb +7 -27
- data/lib/vagrant-libvirt/config.rb +202 -41
- data/lib/vagrant-libvirt/driver.rb +46 -31
- data/lib/vagrant-libvirt/provider.rb +2 -9
- data/lib/vagrant-libvirt/templates/domain.xml.erb +29 -5
- data/lib/vagrant-libvirt/version +1 -1
- data/lib/vagrant-libvirt/version.rb +57 -9
- data/spec/spec_helper.rb +28 -2
- data/spec/support/libvirt_context.rb +2 -0
- data/spec/support/sharedcontext.rb +4 -0
- data/spec/unit/action/create_domain_spec.rb +110 -35
- data/spec/unit/action/create_domain_spec/{default_storage_pool.xml → default_system_storage_pool.xml} +0 -0
- data/spec/unit/action/create_domain_spec/default_user_storage_pool.xml +17 -0
- data/spec/unit/action/start_domain_spec.rb +183 -1
- data/spec/unit/action/start_domain_spec/clock_timer_rtc.xml +50 -0
- data/spec/unit/action/start_domain_spec/default.xml +2 -2
- data/spec/unit/action/start_domain_spec/default_added_tpm_path.xml +48 -0
- data/spec/unit/action/start_domain_spec/default_added_tpm_version.xml +48 -0
- data/spec/unit/action/wait_till_up_spec.rb +14 -9
- data/spec/unit/config_spec.rb +392 -127
- data/spec/unit/provider_spec.rb +11 -0
- data/spec/unit/templates/domain_all_settings.xml +6 -3
- data/spec/unit/templates/domain_custom_cpu_model.xml +2 -1
- data/spec/unit/templates/domain_defaults.xml +2 -1
- data/spec/unit/templates/domain_spec.rb +80 -2
- data/spec/unit/templates/tpm/version_1.2.xml +54 -0
- data/spec/unit/templates/tpm/version_2.0.xml +53 -0
- metadata +74 -17
@@ -50,8 +50,9 @@ module VagrantPlugins
|
|
50
50
|
# remove hw association with interface
|
51
51
|
# working for centos with lvs default disks
|
52
52
|
options = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPTIONS', '')
|
53
|
-
operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir')
|
53
|
+
operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir,-customize')
|
54
54
|
`virt-sysprep --no-logfile --operations #{operations} -a #{@tmp_img} #{options}`
|
55
|
+
`virt-sparsify --in-place #{@tmp_img}`
|
55
56
|
# add any user provided file
|
56
57
|
extra = ''
|
57
58
|
@tmp_include = @tmp_dir + '/_include'
|
@@ -37,12 +37,16 @@ module VagrantPlugins
|
|
37
37
|
xml_descr = REXML::Document.new descr
|
38
38
|
descr_changed = false
|
39
39
|
|
40
|
+
# For outputting XML for comparison
|
41
|
+
formatter = REXML::Formatters::Pretty.new
|
42
|
+
|
40
43
|
# additional disk bus
|
41
44
|
config.disks.each do |disk|
|
42
45
|
device = disk[:device]
|
43
46
|
bus = disk[:bus]
|
44
47
|
REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="' + device + '"]') do |disk_target|
|
45
48
|
next unless disk_target.attributes['bus'] != bus
|
49
|
+
@logger.debug "disk #{device} bus updated from '#{disk_target.attributes['bus']}' to '#{bus}'"
|
46
50
|
descr_changed = true
|
47
51
|
disk_target.attributes['bus'] = bus
|
48
52
|
disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
|
@@ -52,6 +56,7 @@ module VagrantPlugins
|
|
52
56
|
# disk_bus
|
53
57
|
REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="vda"]') do |disk_target|
|
54
58
|
next unless disk_target.attributes['bus'] != config.disk_bus
|
59
|
+
@logger.debug "domain disk bus updated from '#{disk_target.attributes['bus']}' to '#{bus}'"
|
55
60
|
descr_changed = true
|
56
61
|
disk_target.attributes['bus'] = config.disk_bus
|
57
62
|
disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
|
@@ -61,6 +66,7 @@ module VagrantPlugins
|
|
61
66
|
unless config.nic_model_type.nil?
|
62
67
|
REXML::XPath.each(xml_descr, '/domain/devices/interface/model') do |iface_model|
|
63
68
|
if iface_model.attributes['type'] != config.nic_model_type
|
69
|
+
@logger.debug "network type updated from '#{iface_model.attributes['type']}' to '#{config.nic_model_type}'"
|
64
70
|
descr_changed = true
|
65
71
|
iface_model.attributes['type'] = config.nic_model_type
|
66
72
|
end
|
@@ -68,7 +74,9 @@ module VagrantPlugins
|
|
68
74
|
end
|
69
75
|
|
70
76
|
# vCpu count
|
71
|
-
|
77
|
+
vcpus_count = libvirt_domain.num_vcpus(0)
|
78
|
+
if config.cpus.to_i != vcpus_count
|
79
|
+
@logger.debug "cpu count updated from '#{vcpus_count}' to '#{config.cpus}'"
|
72
80
|
descr_changed = true
|
73
81
|
REXML::XPath.first(xml_descr, '/domain/vcpu').text = config.cpus
|
74
82
|
end
|
@@ -76,11 +84,13 @@ module VagrantPlugins
|
|
76
84
|
# cpu_mode
|
77
85
|
cpu = REXML::XPath.first(xml_descr, '/domain/cpu')
|
78
86
|
if cpu.nil?
|
87
|
+
@logger.debug "cpu_mode updated from not set to '#{config.cpu_mode}'"
|
79
88
|
descr_changed = true
|
80
89
|
cpu = REXML::Element.new('cpu', REXML::XPath.first(xml_descr, '/domain'))
|
81
90
|
cpu.attributes['mode'] = config.cpu_mode
|
82
91
|
else
|
83
92
|
if cpu.attributes['mode'] != config.cpu_mode
|
93
|
+
@logger.debug "cpu_mode updated from '#{cpu.attributes['mode']}' to '#{config.cpu_mode}'"
|
84
94
|
descr_changed = true
|
85
95
|
cpu.attributes['mode'] = config.cpu_mode
|
86
96
|
end
|
@@ -89,16 +99,19 @@ module VagrantPlugins
|
|
89
99
|
if config.cpu_mode != 'host-passthrough'
|
90
100
|
cpu_model = REXML::XPath.first(xml_descr, '/domain/cpu/model')
|
91
101
|
if cpu_model.nil?
|
102
|
+
@logger.debug "cpu_model updated from not set to '#{config.cpu_model}'"
|
92
103
|
descr_changed = true
|
93
104
|
cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
|
94
105
|
cpu_model.attributes['fallback'] = 'allow'
|
95
106
|
cpu_model.text = config.cpu_model
|
96
107
|
else
|
97
|
-
if cpu_model.text.strip != config.cpu_model.strip
|
108
|
+
if (cpu_model.text or '').strip != config.cpu_model.strip
|
109
|
+
@logger.debug "cpu_model text updated from #{cpu_model.text} to '#{config.cpu_model}'"
|
98
110
|
descr_changed = true
|
99
111
|
cpu_model.text = config.cpu_model
|
100
112
|
end
|
101
113
|
if cpu_model.attributes['fallback'] != config.cpu_fallback
|
114
|
+
@logger.debug "cpu_model fallback attribute updated from #{cpu_model.attributes['fallback']} to '#{config.cpu_fallback}'"
|
102
115
|
descr_changed = true
|
103
116
|
cpu_model.attributes['fallback'] = config.cpu_fallback
|
104
117
|
end
|
@@ -107,12 +120,14 @@ module VagrantPlugins
|
|
107
120
|
svm_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="svm"]')
|
108
121
|
if config.nested
|
109
122
|
if vmx_feature.nil?
|
123
|
+
@logger.debug "nested mode enabled from unset by setting cpu vmx feature"
|
110
124
|
descr_changed = true
|
111
125
|
vmx_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
|
112
126
|
vmx_feature.attributes['policy'] = 'optional'
|
113
127
|
vmx_feature.attributes['name'] = 'vmx'
|
114
128
|
end
|
115
129
|
if svm_feature.nil?
|
130
|
+
@logger.debug "nested mode enabled from unset by setting cpu svm feature"
|
116
131
|
descr_changed = true
|
117
132
|
svm_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
|
118
133
|
svm_feature.attributes['policy'] = 'optional'
|
@@ -120,16 +135,19 @@ module VagrantPlugins
|
|
120
135
|
end
|
121
136
|
else
|
122
137
|
unless vmx_feature.nil?
|
138
|
+
@logger.debug "nested mode disabled for cpu by removing vmx feature"
|
123
139
|
descr_changed = true
|
124
140
|
cpu.delete_element(vmx_feature)
|
125
141
|
end
|
126
142
|
unless svm_feature.nil?
|
143
|
+
@logger.debug "nested mode disabled for cpu by removing svm feature"
|
127
144
|
descr_changed = true
|
128
145
|
cpu.delete_element(svm_feature)
|
129
146
|
end
|
130
147
|
end
|
131
148
|
elsif config.numa_nodes == nil
|
132
149
|
unless cpu.elements.to_a.empty?
|
150
|
+
@logger.debug "switching cpu_mode to host-passthrough and removing emulated cpu features"
|
133
151
|
descr_changed = true
|
134
152
|
cpu.elements.each do |elem|
|
135
153
|
cpu.delete_element(elem)
|
@@ -137,6 +155,34 @@ module VagrantPlugins
|
|
137
155
|
end
|
138
156
|
end
|
139
157
|
|
158
|
+
# Clock
|
159
|
+
clock = REXML::XPath.first(xml_descr, '/domain/clock')
|
160
|
+
if clock.attributes['offset'] != config.clock_offset
|
161
|
+
@logger.debug "clock offset changed"
|
162
|
+
descr_changed = true
|
163
|
+
clock.attributes['offset'] = config.clock_offset
|
164
|
+
end
|
165
|
+
|
166
|
+
# clock timers - because timers can be added/removed, just rebuild and then compare
|
167
|
+
if !config.clock_timers.empty? || clock.has_elements?
|
168
|
+
oldclock = ''
|
169
|
+
formatter.write(REXML::XPath.first(xml_descr, '/domain/clock'), oldclock)
|
170
|
+
clock.delete_element('//timer')
|
171
|
+
config.clock_timers.each do |clock_timer|
|
172
|
+
timer = REXML::Element.new('timer', clock)
|
173
|
+
clock_timer.each do |attr, value|
|
174
|
+
timer.attributes[attr.to_s] = value
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
newclock = ''
|
179
|
+
formatter.write(clock, newclock)
|
180
|
+
unless newclock.eql? oldclock
|
181
|
+
@logger.debug "clock timers config changed"
|
182
|
+
descr_changed = true
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
140
186
|
# Graphics
|
141
187
|
graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
|
142
188
|
if config.graphics_type != 'none'
|
@@ -178,31 +224,31 @@ module VagrantPlugins
|
|
178
224
|
end
|
179
225
|
|
180
226
|
# TPM
|
181
|
-
if config.tpm_path
|
182
|
-
|
227
|
+
if [config.tpm_path, config.tpm_version].any?
|
228
|
+
if config.tpm_path
|
229
|
+
raise Errors::FogCreateServerError, 'The TPM Path must be fully qualified' unless config.tpm_path[0].chr == '/'
|
230
|
+
end
|
183
231
|
|
184
|
-
tpm
|
185
|
-
if
|
232
|
+
# just build the tpm element every time
|
233
|
+
# check at the end if it is different
|
234
|
+
oldtpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
|
235
|
+
REXML::XPath.first(xml_descr, '/domain/devices').delete_element("tpm")
|
236
|
+
newtpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices'))
|
237
|
+
|
238
|
+
newtpm.attributes['model'] = config.tpm_model
|
239
|
+
backend = newtpm.add_element('backend')
|
240
|
+
backend.attributes['type'] = config.tpm_type
|
241
|
+
|
242
|
+
case config.tpm_type
|
243
|
+
when 'emulator'
|
244
|
+
backend.attributes['version'] = config.tpm_version
|
245
|
+
when 'passthrough'
|
246
|
+
backend.add_element('device').attributes['path'] = config.tpm_path
|
247
|
+
end
|
248
|
+
|
249
|
+
unless "'#{newtpm}'".eql? "'#{oldtpm}'"
|
250
|
+
@logger.debug "tpm config changed"
|
186
251
|
descr_changed = true
|
187
|
-
tpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices/tpm/model'))
|
188
|
-
tpm.attributes['model'] = config.tpm_model
|
189
|
-
tpm_backend_type = tpm.add_element('backend')
|
190
|
-
tpm_backend_type.attributes['type'] = config.tpm_type
|
191
|
-
tpm_device_path = tpm_backend_type.add_element('device')
|
192
|
-
tpm_device_path.attributes['path'] = config.tpm_path
|
193
|
-
else
|
194
|
-
if tpm.attributes['model'] != config.tpm_model
|
195
|
-
descr_changed = true
|
196
|
-
tpm.attributes['model'] = config.tpm_model
|
197
|
-
end
|
198
|
-
if tpm.elements['backend'].attributes['type'] != config.tpm_type
|
199
|
-
descr_changed = true
|
200
|
-
tpm.elements['backend'].attributes['type'] = config.tpm_type
|
201
|
-
end
|
202
|
-
if tpm.elements['backend'].elements['device'].attributes['path'] != config.tpm_path
|
203
|
-
descr_changed = true
|
204
|
-
tpm.elements['backend'].elements['device'].attributes['path'] = config.tpm_path
|
205
|
-
end
|
206
252
|
end
|
207
253
|
end
|
208
254
|
|
@@ -210,16 +256,21 @@ module VagrantPlugins
|
|
210
256
|
video = REXML::XPath.first(xml_descr, '/domain/devices/video')
|
211
257
|
if !video.nil? && (config.graphics_type == 'none')
|
212
258
|
# graphics_type = none, video devices are removed since there is no possible output
|
259
|
+
@logger.debug "deleting video elements as config.graphics_type is none"
|
213
260
|
descr_changed = true
|
214
261
|
video.parent.delete_element(video)
|
215
262
|
else
|
216
263
|
video_model = REXML::XPath.first(xml_descr, '/domain/devices/video/model')
|
217
264
|
if video_model.nil?
|
265
|
+
@logger.debug "video updated from not set to type '#{config.video_type}' and vram '#{config.video_vram}'"
|
266
|
+
descr_changed = true
|
218
267
|
video_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/devices/video'))
|
219
268
|
video_model.attributes['type'] = config.video_type
|
220
269
|
video_model.attributes['vram'] = config.video_vram
|
221
270
|
else
|
222
|
-
if video_model.attributes['type'] != config.video_type || video_model.attributes['vram'] != config.video_vram
|
271
|
+
if video_model.attributes['type'] != config.video_type || video_model.attributes['vram'] != config.video_vram.to_s
|
272
|
+
@logger.debug "video type updated from '#{video_model.attributes['type']}' to '#{config.video_type}'"
|
273
|
+
@logger.debug "video vram updated from '#{video_model.attributes['vram']}' to '#{config.video_vram}'"
|
223
274
|
descr_changed = true
|
224
275
|
video_model.attributes['type'] = config.video_type
|
225
276
|
video_model.attributes['vram'] = config.video_vram
|
@@ -237,11 +288,13 @@ module VagrantPlugins
|
|
237
288
|
if config.dtb
|
238
289
|
dtb = REXML::XPath.first(xml_descr, '/domain/os/dtb')
|
239
290
|
if dtb.nil?
|
291
|
+
@logger.debug "dtb updated from not set to '#{config.dtb}'"
|
240
292
|
descr_changed = true
|
241
293
|
dtb = REXML::Element.new('dtb', REXML::XPath.first(xml_descr, '/domain/os'))
|
242
294
|
dtb.text = config.dtb
|
243
295
|
else
|
244
|
-
if dtb.text != config.dtb
|
296
|
+
if (dtb.text or '') != config.dtb
|
297
|
+
@logger.debug "dtb updated from '#{dtb.text}' to '#{config.dtb}'"
|
245
298
|
descr_changed = true
|
246
299
|
dtb.text = config.dtb
|
247
300
|
end
|
@@ -252,11 +305,13 @@ module VagrantPlugins
|
|
252
305
|
if config.kernel
|
253
306
|
kernel = REXML::XPath.first(xml_descr, '/domain/os/kernel')
|
254
307
|
if kernel.nil?
|
308
|
+
@logger.debug "kernel updated from not set to '#{config.kernel}'"
|
255
309
|
descr_changed = true
|
256
310
|
kernel = REXML::Element.new('kernel', REXML::XPath.first(xml_descr, '/domain/os'))
|
257
311
|
kernel.text = config.kernel
|
258
312
|
else
|
259
|
-
if kernel.text != config.kernel
|
313
|
+
if (kernel.text or '').strip != config.kernel
|
314
|
+
@logger.debug "kernel updated from '#{kernel.text}' to '#{config.kernel}'"
|
260
315
|
descr_changed = true
|
261
316
|
kernel.text = config.kernel
|
262
317
|
end
|
@@ -265,11 +320,13 @@ module VagrantPlugins
|
|
265
320
|
if config.initrd
|
266
321
|
initrd = REXML::XPath.first(xml_descr, '/domain/os/initrd')
|
267
322
|
if initrd.nil?
|
323
|
+
@logger.debug "initrd updated from not set to '#{config.initrd}'"
|
268
324
|
descr_changed = true
|
269
325
|
initrd = REXML::Element.new('initrd', REXML::XPath.first(xml_descr, '/domain/os'))
|
270
326
|
initrd.text = config.initrd
|
271
327
|
else
|
272
|
-
if initrd.text != config.initrd
|
328
|
+
if (initrd.text or '').strip != config.initrd
|
329
|
+
@logger.debug "initrd updated from '#{initrd.text}' to '#{config.initrd}'"
|
273
330
|
descr_changed = true
|
274
331
|
initrd.text = config.initrd
|
275
332
|
end
|
@@ -21,7 +21,7 @@ module VagrantPlugins
|
|
21
21
|
env[:metrics] ||= {}
|
22
22
|
|
23
23
|
# Get domain object
|
24
|
-
domain = env[:machine].provider.driver.get_domain(env[:machine]
|
24
|
+
domain = env[:machine].provider.driver.get_domain(env[:machine])
|
25
25
|
if domain.nil?
|
26
26
|
raise Errors::NoDomainError,
|
27
27
|
error_message: "Domain #{env[:machine].id} not found"
|
@@ -34,33 +34,13 @@ module VagrantPlugins
|
|
34
34
|
@logger.debug("Searching for IP for MAC address: #{domain.mac}")
|
35
35
|
env[:ui].info(I18n.t('vagrant_libvirt.waiting_for_ip'))
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
return if env[:interrupted]
|
37
|
+
env[:metrics]['instance_ip_time'] = Util::Timer.time do
|
38
|
+
retryable(on: Fog::Errors::TimeoutError, tries: 300) do
|
39
|
+
# just return if interrupted and let the warden call recover
|
40
|
+
return if env[:interrupted]
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
env[:ip_address] = env[:machine].provider.driver.get_ipaddress_system(domain.mac)
|
46
|
-
!env[:ip_address].nil?
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
else
|
51
|
-
env[:metrics]['instance_ip_time'] = Util::Timer.time do
|
52
|
-
retryable(on: Fog::Errors::TimeoutError, tries: 300) do
|
53
|
-
# just return if interrupted and let the warden call recover
|
54
|
-
return if env[:interrupted]
|
55
|
-
|
56
|
-
# Wait for domain to obtain an ip address
|
57
|
-
domain.wait_for(2) do
|
58
|
-
addresses.each_pair do |_type, ip|
|
59
|
-
env[:ip_address] = ip[0] unless ip[0].nil?
|
60
|
-
end
|
61
|
-
!env[:ip_address].nil?
|
62
|
-
end
|
63
|
-
end
|
42
|
+
# Wait for domain to obtain an ip address
|
43
|
+
env[:ip_address] = env[:machine].provider.driver.get_domain_ipaddress(env[:machine], domain)
|
64
44
|
end
|
65
45
|
end
|
66
46
|
|
@@ -37,6 +37,8 @@ module VagrantPlugins
|
|
37
37
|
# ID SSH key file
|
38
38
|
attr_accessor :id_ssh_key_file
|
39
39
|
|
40
|
+
attr_accessor :proxy_command
|
41
|
+
|
40
42
|
# Libvirt storage pool name, where box image and instance snapshots will
|
41
43
|
# be stored.
|
42
44
|
attr_accessor :storage_pool_name
|
@@ -84,6 +86,8 @@ module VagrantPlugins
|
|
84
86
|
attr_accessor :shares
|
85
87
|
attr_accessor :features
|
86
88
|
attr_accessor :features_hyperv
|
89
|
+
attr_accessor :clock_offset
|
90
|
+
attr_accessor :clock_timers
|
87
91
|
attr_accessor :numa_nodes
|
88
92
|
attr_accessor :loader
|
89
93
|
attr_accessor :nvram
|
@@ -93,9 +97,10 @@ module VagrantPlugins
|
|
93
97
|
attr_accessor :machine_virtual_size
|
94
98
|
attr_accessor :disk_bus
|
95
99
|
attr_accessor :disk_device
|
100
|
+
attr_accessor :disk_driver_opts
|
96
101
|
attr_accessor :nic_model_type
|
97
102
|
attr_accessor :nested
|
98
|
-
attr_accessor :volume_cache
|
103
|
+
attr_accessor :volume_cache # deprecated, kept for backwards compatibility; use disk_driver
|
99
104
|
attr_accessor :kernel
|
100
105
|
attr_accessor :cmd_line
|
101
106
|
attr_accessor :initrd
|
@@ -117,6 +122,13 @@ module VagrantPlugins
|
|
117
122
|
attr_accessor :tpm_model
|
118
123
|
attr_accessor :tpm_type
|
119
124
|
attr_accessor :tpm_path
|
125
|
+
attr_accessor :tpm_version
|
126
|
+
|
127
|
+
# Configure the memballoon
|
128
|
+
attr_accessor :memballoon_enabled
|
129
|
+
attr_accessor :memballoon_model
|
130
|
+
attr_accessor :memballoon_pci_bus
|
131
|
+
attr_accessor :memballoon_pci_slot
|
120
132
|
|
121
133
|
# Sets the max number of NICs that can be created
|
122
134
|
# Default set to 8. Don't change the default unless you know
|
@@ -181,6 +193,8 @@ module VagrantPlugins
|
|
181
193
|
@username = UNSET_VALUE
|
182
194
|
@password = UNSET_VALUE
|
183
195
|
@id_ssh_key_file = UNSET_VALUE
|
196
|
+
@socket = UNSET_VALUE
|
197
|
+
@proxy_command = UNSET_VALUE
|
184
198
|
@storage_pool_name = UNSET_VALUE
|
185
199
|
@snapshot_pool_name = UNSET_VALUE
|
186
200
|
@random_hostname = UNSET_VALUE
|
@@ -215,6 +229,8 @@ module VagrantPlugins
|
|
215
229
|
@shares = UNSET_VALUE
|
216
230
|
@features = UNSET_VALUE
|
217
231
|
@features_hyperv = UNSET_VALUE
|
232
|
+
@clock_offset = UNSET_VALUE
|
233
|
+
@clock_timers = []
|
218
234
|
@numa_nodes = UNSET_VALUE
|
219
235
|
@loader = UNSET_VALUE
|
220
236
|
@nvram = UNSET_VALUE
|
@@ -223,6 +239,7 @@ module VagrantPlugins
|
|
223
239
|
@machine_virtual_size = UNSET_VALUE
|
224
240
|
@disk_bus = UNSET_VALUE
|
225
241
|
@disk_device = UNSET_VALUE
|
242
|
+
@disk_driver_opts = {}
|
226
243
|
@nic_model_type = UNSET_VALUE
|
227
244
|
@nested = UNSET_VALUE
|
228
245
|
@volume_cache = UNSET_VALUE
|
@@ -245,6 +262,12 @@ module VagrantPlugins
|
|
245
262
|
@tpm_model = UNSET_VALUE
|
246
263
|
@tpm_type = UNSET_VALUE
|
247
264
|
@tpm_path = UNSET_VALUE
|
265
|
+
@tpm_version = UNSET_VALUE
|
266
|
+
|
267
|
+
@memballoon_enabled = UNSET_VALUE
|
268
|
+
@memballoon_model = UNSET_VALUE
|
269
|
+
@memballoon_pci_bus = UNSET_VALUE
|
270
|
+
@memballoon_pci_slot = UNSET_VALUE
|
248
271
|
|
249
272
|
@nic_adapter_count = UNSET_VALUE
|
250
273
|
|
@@ -379,6 +402,25 @@ module VagrantPlugins
|
|
379
402
|
state: options[:state])
|
380
403
|
end
|
381
404
|
|
405
|
+
def clock_timer(options = {})
|
406
|
+
if options[:name].nil?
|
407
|
+
raise 'Clock timer name must be specified'
|
408
|
+
end
|
409
|
+
|
410
|
+
options.each do |key, value|
|
411
|
+
case key
|
412
|
+
when :name, :track, :tickpolicy, :frequency, :mode, :present
|
413
|
+
if value.nil?
|
414
|
+
raise "Value of timer option #{key} is nil"
|
415
|
+
end
|
416
|
+
else
|
417
|
+
raise "Unknown clock timer option: #{key}"
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
@clock_timers.push(options.dup)
|
422
|
+
end
|
423
|
+
|
382
424
|
def cputopology(options = {})
|
383
425
|
if options[:sockets].nil? || options[:cores].nil? || options[:threads].nil?
|
384
426
|
raise 'CPU topology must have all of sockets, cores and threads specified'
|
@@ -551,6 +593,12 @@ module VagrantPlugins
|
|
551
593
|
@smartcard_dev[:source_service] = options[:source_service] if @smartcard_dev[:type] == 'tcp'
|
552
594
|
end
|
553
595
|
|
596
|
+
# Disk driver options for primary disk
|
597
|
+
def disk_driver(options = {})
|
598
|
+
supported_opts = [:cache, :io, :copy_on_read, :discard, :detect_zeroes]
|
599
|
+
@disk_driver_opts = options.select { |k,_| supported_opts.include? k }
|
600
|
+
end
|
601
|
+
|
554
602
|
# NOTE: this will run twice for each time it's needed- keep it idempotent
|
555
603
|
def storage(storage_type, options = {})
|
556
604
|
if storage_type == :file
|
@@ -605,6 +653,10 @@ module VagrantPlugins
|
|
605
653
|
allow_existing: options[:allow_existing],
|
606
654
|
shareable: options[:shareable],
|
607
655
|
serial: options[:serial],
|
656
|
+
io: options[:io],
|
657
|
+
copy_on_read: options[:copy_on_read],
|
658
|
+
discard: options[:discard],
|
659
|
+
detect_zeroes: options[:detect_zeroes],
|
608
660
|
pool: options[:pool], # overrides storage_pool setting for additional disks
|
609
661
|
wwn: options[:wwn],
|
610
662
|
}
|
@@ -624,15 +676,25 @@ module VagrantPlugins
|
|
624
676
|
@qemu_env.merge!(options)
|
625
677
|
end
|
626
678
|
|
679
|
+
def _default_uri
|
680
|
+
# Determine if any settings except driver provided explicitly, if not
|
681
|
+
# and the LIBVIRT_DEFAULT_URI var is set, use that.
|
682
|
+
#
|
683
|
+
# Skipping driver because that may be set on individual boxes rather
|
684
|
+
# than by the user.
|
685
|
+
if [
|
686
|
+
@connect_via_ssh, @host, @username, @password,
|
687
|
+
@id_ssh_key_file, @qemu_use_session, @socket,
|
688
|
+
].none?{ |v| v != UNSET_VALUE }
|
689
|
+
if ENV.fetch('LIBVIRT_DEFAULT_URI', '') != ""
|
690
|
+
@uri = ENV['LIBVIRT_DEFAULT_URI']
|
691
|
+
end
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
627
695
|
# code to generate URI from from either the LIBVIRT_URI environment
|
628
696
|
# variable or a config moved out of the connect action
|
629
697
|
def _generate_uri(qemu_use_session)
|
630
|
-
|
631
|
-
# If the LIBVIRT_DEFAULT_URI var is set, we'll use that
|
632
|
-
if ENV.fetch('LIBVIRT_DEFAULT_URI', '') != ""
|
633
|
-
return ENV['LIBVIRT_DEFAULT_URI']
|
634
|
-
end
|
635
|
-
|
636
698
|
# builds the Libvirt connection URI from the given driver config
|
637
699
|
# Setup connection uri.
|
638
700
|
uri = @driver.dup
|
@@ -652,30 +714,34 @@ module VagrantPlugins
|
|
652
714
|
uri = 'qemu' # use QEMU uri for KVM domain type
|
653
715
|
end
|
654
716
|
|
655
|
-
if
|
717
|
+
# turn on ssh if an ssh key file is explicitly provided
|
718
|
+
if @connect_via_ssh == UNSET_VALUE && @id_ssh_key_file && @id_ssh_key_file != UNSET_VALUE
|
719
|
+
@connect_via_ssh = true
|
720
|
+
end
|
721
|
+
|
722
|
+
params = {}
|
723
|
+
|
724
|
+
if @connect_via_ssh == true
|
725
|
+
finalize_id_ssh_key_file
|
726
|
+
|
656
727
|
uri << '+ssh://'
|
657
|
-
uri << @username + '@' if @username
|
728
|
+
uri << @username + '@' if @username && @username != UNSET_VALUE
|
729
|
+
|
730
|
+
uri << ( @host && @host != UNSET_VALUE ? @host : 'localhost' )
|
658
731
|
|
659
|
-
|
732
|
+
params['no_verify'] = '1'
|
733
|
+
params['keyfile'] = @id_ssh_key_file if @id_ssh_key_file
|
660
734
|
else
|
661
735
|
uri << '://'
|
662
|
-
uri << @host if @host
|
736
|
+
uri << @host if @host && @host != UNSET_VALUE
|
663
737
|
end
|
664
738
|
|
665
739
|
uri << virt_path
|
666
740
|
|
667
|
-
params = {'no_verify' => '1'}
|
668
|
-
|
669
|
-
if @id_ssh_key_file
|
670
|
-
# set ssh key for access to Libvirt host
|
671
|
-
# if no slash, prepend $HOME/.ssh/
|
672
|
-
@id_ssh_key_file.prepend("#{ENV['HOME']}/.ssh/") if @id_ssh_key_file !~ /\A\//
|
673
|
-
params['keyfile'] = @id_ssh_key_file
|
674
|
-
end
|
675
741
|
# set path to Libvirt socket
|
676
742
|
params['socket'] = @socket if @socket
|
677
743
|
|
678
|
-
uri << "?" + params.map{|pair| pair.join('=')}.join('&')
|
744
|
+
uri << "?" + params.map{|pair| pair.join('=')}.join('&') if !params.empty?
|
679
745
|
uri
|
680
746
|
end
|
681
747
|
|
@@ -688,12 +754,22 @@ module VagrantPlugins
|
|
688
754
|
end
|
689
755
|
|
690
756
|
def finalize!
|
757
|
+
_default_uri if @uri == UNSET_VALUE
|
758
|
+
|
759
|
+
# settings which _generate_uri
|
691
760
|
@driver = 'kvm' if @driver == UNSET_VALUE
|
692
|
-
@host = nil if @host == UNSET_VALUE
|
693
|
-
@connect_via_ssh = false if @connect_via_ssh == UNSET_VALUE
|
694
|
-
@username = nil if @username == UNSET_VALUE
|
695
761
|
@password = nil if @password == UNSET_VALUE
|
696
|
-
@
|
762
|
+
@socket = nil if @socket == UNSET_VALUE
|
763
|
+
|
764
|
+
# If uri isn't set then let's build one from various sources.
|
765
|
+
# Default to passing false for qemu_use_session if it's not set.
|
766
|
+
if @uri == UNSET_VALUE
|
767
|
+
@uri = _generate_uri(@qemu_use_session == UNSET_VALUE ? false : @qemu_use_session)
|
768
|
+
end
|
769
|
+
|
770
|
+
finalize_from_uri
|
771
|
+
finalize_proxy_command
|
772
|
+
|
697
773
|
@storage_pool_name = 'default' if @storage_pool_name == UNSET_VALUE
|
698
774
|
@snapshot_pool_name = @storage_pool_name if @snapshot_pool_name == UNSET_VALUE
|
699
775
|
@storage_pool_path = nil if @storage_pool_path == UNSET_VALUE
|
@@ -710,22 +786,6 @@ module VagrantPlugins
|
|
710
786
|
@management_network_domain = nil if @management_network_domain == UNSET_VALUE
|
711
787
|
@system_uri = 'qemu:///system' if @system_uri == UNSET_VALUE
|
712
788
|
|
713
|
-
# If uri isn't set then let's build one from various sources.
|
714
|
-
# Default to passing false for qemu_use_session if it's not set.
|
715
|
-
if @uri == UNSET_VALUE
|
716
|
-
@uri = _generate_uri(@qemu_use_session == UNSET_VALUE ? false : @qemu_use_session)
|
717
|
-
end
|
718
|
-
|
719
|
-
# Set qemu_use_session based on the URI if it wasn't set by the user
|
720
|
-
if @qemu_use_session == UNSET_VALUE
|
721
|
-
uri = _parse_uri(@uri)
|
722
|
-
if (uri.scheme.start_with? "qemu") && (uri.path.include? "session")
|
723
|
-
@qemu_use_session = true
|
724
|
-
else
|
725
|
-
@qemu_use_session = false
|
726
|
-
end
|
727
|
-
end
|
728
|
-
|
729
789
|
# Domain specific settings.
|
730
790
|
@title = '' if @title == UNSET_VALUE
|
731
791
|
@description = '' if @description == UNSET_VALUE
|
@@ -749,6 +809,8 @@ module VagrantPlugins
|
|
749
809
|
@shares = nil if @shares == UNSET_VALUE
|
750
810
|
@features = ['acpi','apic','pae'] if @features == UNSET_VALUE
|
751
811
|
@features_hyperv = [] if @features_hyperv == UNSET_VALUE
|
812
|
+
@clock_offset = 'utc' if @clock_offset == UNSET_VALUE
|
813
|
+
@clock_timers = [] if @clock_timers == UNSET_VALUE
|
752
814
|
@numa_nodes = @numa_nodes == UNSET_VALUE ? nil : _generate_numa
|
753
815
|
@loader = nil if @loader == UNSET_VALUE
|
754
816
|
@nvram = nil if @nvram == UNSET_VALUE
|
@@ -757,9 +819,10 @@ module VagrantPlugins
|
|
757
819
|
@machine_virtual_size = nil if @machine_virtual_size == UNSET_VALUE
|
758
820
|
@disk_bus = 'virtio' if @disk_bus == UNSET_VALUE
|
759
821
|
@disk_device = 'vda' if @disk_device == UNSET_VALUE
|
822
|
+
@disk_driver_opts = {} if @disk_driver_opts == UNSET_VALUE
|
760
823
|
@nic_model_type = nil if @nic_model_type == UNSET_VALUE
|
761
824
|
@nested = false if @nested == UNSET_VALUE
|
762
|
-
@volume_cache =
|
825
|
+
@volume_cache = nil if @volume_cache == UNSET_VALUE
|
763
826
|
@kernel = nil if @kernel == UNSET_VALUE
|
764
827
|
@cmd_line = '' if @cmd_line == UNSET_VALUE
|
765
828
|
@initrd = '' if @initrd == UNSET_VALUE
|
@@ -781,6 +844,11 @@ module VagrantPlugins
|
|
781
844
|
@tpm_model = 'tpm-tis' if @tpm_model == UNSET_VALUE
|
782
845
|
@tpm_type = 'passthrough' if @tpm_type == UNSET_VALUE
|
783
846
|
@tpm_path = nil if @tpm_path == UNSET_VALUE
|
847
|
+
@tpm_version = nil if @tpm_version == UNSET_VALUE
|
848
|
+
@memballoon_enabled = nil if @memballoon_enabled == UNSET_VALUE
|
849
|
+
@memballoon_model = 'virtio' if @memballoon_model == UNSET_VALUE
|
850
|
+
@memballoon_pci_bus = '0x00' if @memballoon_pci_bus == UNSET_VALUE
|
851
|
+
@memballoon_pci_slot = '0x0f' if @memballoon_pci_slot == UNSET_VALUE
|
784
852
|
@nic_adapter_count = 8 if @nic_adapter_count == UNSET_VALUE
|
785
853
|
@emulator_path = nil if @emulator_path == UNSET_VALUE
|
786
854
|
|
@@ -872,6 +940,14 @@ module VagrantPlugins
|
|
872
940
|
end
|
873
941
|
end
|
874
942
|
|
943
|
+
if !machine.provider_config.volume_cache.nil? and machine.provider_config.volume_cache != UNSET_VALUE
|
944
|
+
machine.ui.warn("Libvirt Provider: volume_cache is deprecated. Use disk_driver :cache => '#{machine.provider_config.volume_cache}' instead.")
|
945
|
+
|
946
|
+
if !machine.provider_config.disk_driver_opts.empty?
|
947
|
+
machine.ui.warn("Libvirt Provider: volume_cache has no effect when disk_driver is defined.")
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
875
951
|
{ 'Libvirt Provider' => errors }
|
876
952
|
end
|
877
953
|
|
@@ -885,11 +961,96 @@ module VagrantPlugins
|
|
885
961
|
c += other.cdroms
|
886
962
|
result.cdroms = c
|
887
963
|
|
964
|
+
result.disk_driver_opts = disk_driver_opts.merge(other.disk_driver_opts)
|
965
|
+
|
966
|
+
c = clock_timers.dup
|
967
|
+
c += other.clock_timers
|
968
|
+
result.clock_timers = c
|
969
|
+
|
888
970
|
c = qemu_env != UNSET_VALUE ? qemu_env.dup : {}
|
889
971
|
c.merge!(other.qemu_env) if other.qemu_env != UNSET_VALUE
|
890
972
|
result.qemu_env = c
|
891
973
|
end
|
892
974
|
end
|
975
|
+
|
976
|
+
private
|
977
|
+
|
978
|
+
def finalize_from_uri
|
979
|
+
# Parse uri to extract individual components
|
980
|
+
uri = _parse_uri(@uri)
|
981
|
+
|
982
|
+
# only set @connect_via_ssh if not explicitly to avoid overriding
|
983
|
+
# and allow an error to occur if the @uri and @connect_via_ssh disagree
|
984
|
+
@connect_via_ssh = uri.scheme.include? "ssh" if @connect_via_ssh == UNSET_VALUE
|
985
|
+
|
986
|
+
# Set qemu_use_session based on the URI if it wasn't set by the user
|
987
|
+
if @qemu_use_session == UNSET_VALUE
|
988
|
+
if (uri.scheme.start_with? "qemu") && (uri.path.include? "session")
|
989
|
+
@qemu_use_session = true
|
990
|
+
else
|
991
|
+
@qemu_use_session = false
|
992
|
+
end
|
993
|
+
end
|
994
|
+
|
995
|
+
# Extract host and username values from uri if provided, otherwise nil
|
996
|
+
@host = uri.host
|
997
|
+
@username = uri.user
|
998
|
+
|
999
|
+
finalize_id_ssh_key_file
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def resolve_ssh_key_file(key_file)
|
1003
|
+
# set ssh key for access to Libvirt host
|
1004
|
+
# if no slash, prepend $HOME/.ssh/
|
1005
|
+
key_file.prepend("#{ENV['HOME']}/.ssh/") if key_file && key_file !~ /\A\//
|
1006
|
+
|
1007
|
+
key_file
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
def finalize_id_ssh_key_file
|
1011
|
+
# resolve based on the following roles
|
1012
|
+
# 1) if @connect_via_ssh is set to true, and id_ssh_key_file not current set,
|
1013
|
+
# set default if the file exists
|
1014
|
+
# 2) if supplied the key name, attempt to expand based on user home
|
1015
|
+
# 3) otherwise set to nil
|
1016
|
+
|
1017
|
+
if @connect_via_ssh == true && @id_ssh_key_file == UNSET_VALUE
|
1018
|
+
# set default if using ssh while allowing a user using nil to disable this
|
1019
|
+
id_ssh_key_file = resolve_ssh_key_file('id_rsa')
|
1020
|
+
id_ssh_key_file = nil if !File.file?(id_ssh_key_file)
|
1021
|
+
elsif @id_ssh_key_file != UNSET_VALUE
|
1022
|
+
id_ssh_key_file = resolve_ssh_key_file(@id_ssh_key_file)
|
1023
|
+
else
|
1024
|
+
id_ssh_key_file = nil
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
@id_ssh_key_file = id_ssh_key_file
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
def finalize_proxy_command
|
1031
|
+
if @connect_via_ssh
|
1032
|
+
if @proxy_command == UNSET_VALUE
|
1033
|
+
proxy_command = "ssh '#{@host}' "
|
1034
|
+
proxy_command << "-l '#{@username}' " if @username
|
1035
|
+
proxy_command << "-i '#{@id_ssh_key_file}' " if @id_ssh_key_file
|
1036
|
+
proxy_command << '-W %h:%p'
|
1037
|
+
else
|
1038
|
+
inputs = { host: @host }
|
1039
|
+
inputs[:username] = @username if @username
|
1040
|
+
inputs[:id_ssh_key_file] = @id_ssh_key_file if @id_ssh_key_file
|
1041
|
+
|
1042
|
+
proxy_command = @proxy_command
|
1043
|
+
# avoid needing to escape '%' symbols
|
1044
|
+
inputs.each do |key, value|
|
1045
|
+
proxy_command.gsub!("{#{key}}", value)
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
@proxy_command = proxy_command
|
1050
|
+
else
|
1051
|
+
@proxy_command = nil
|
1052
|
+
end
|
1053
|
+
end
|
893
1054
|
end
|
894
1055
|
end
|
895
1056
|
end
|