vagrant-libvirt 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|