vagrant-libvirt 0.4.1 → 0.5.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 +212 -27
- data/lib/vagrant-libvirt/action.rb +6 -0
- data/lib/vagrant-libvirt/action/clean_machine_folder.rb +28 -0
- data/lib/vagrant-libvirt/action/create_domain.rb +26 -10
- data/lib/vagrant-libvirt/action/create_domain_volume.rb +57 -55
- data/lib/vagrant-libvirt/action/create_network_interfaces.rb +0 -3
- data/lib/vagrant-libvirt/action/create_networks.rb +11 -4
- data/lib/vagrant-libvirt/action/destroy_domain.rb +1 -1
- data/lib/vagrant-libvirt/action/forward_ports.rb +36 -37
- data/lib/vagrant-libvirt/action/halt_domain.rb +25 -9
- data/lib/vagrant-libvirt/action/handle_box_image.rb +162 -74
- data/lib/vagrant-libvirt/action/is_running.rb +1 -3
- data/lib/vagrant-libvirt/action/is_suspended.rb +4 -4
- data/lib/vagrant-libvirt/action/wait_till_up.rb +1 -25
- data/lib/vagrant-libvirt/cap/{mount_p9.rb → mount_9p.rb} +2 -2
- data/lib/vagrant-libvirt/cap/mount_virtiofs.rb +37 -0
- data/lib/vagrant-libvirt/cap/{synced_folder.rb → synced_folder_9p.rb} +4 -5
- data/lib/vagrant-libvirt/cap/synced_folder_virtiofs.rb +109 -0
- data/lib/vagrant-libvirt/config.rb +24 -2
- data/lib/vagrant-libvirt/errors.rb +24 -1
- data/lib/vagrant-libvirt/plugin.rb +13 -5
- data/lib/vagrant-libvirt/templates/domain.xml.erb +7 -6
- data/lib/vagrant-libvirt/templates/private_network.xml.erb +1 -1
- data/lib/vagrant-libvirt/util/network_util.rb +21 -3
- data/lib/vagrant-libvirt/version +1 -1
- data/locales/en.yml +12 -0
- data/spec/spec_helper.rb +9 -1
- data/spec/support/matchers/have_file_content.rb +63 -0
- data/spec/unit/action/clean_machine_folder_spec.rb +48 -0
- data/spec/unit/action/create_domain_spec.rb +6 -0
- data/spec/unit/action/create_domain_volume_spec.rb +102 -0
- data/spec/unit/action/create_domain_volume_spec/one_disk_in_storage.xml +21 -0
- data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_0.xml +21 -0
- data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_1.xml +21 -0
- data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_2.xml +21 -0
- data/spec/unit/action/destroy_domain_spec.rb +1 -1
- data/spec/unit/action/forward_ports_spec.rb +202 -0
- data/spec/unit/action/halt_domain_spec.rb +90 -0
- data/spec/unit/action/handle_box_image_spec.rb +363 -0
- data/spec/unit/action/wait_till_up_spec.rb +1 -23
- data/spec/unit/templates/domain_all_settings.xml +8 -0
- data/spec/unit/templates/domain_spec.rb +20 -1
- metadata +41 -4
@@ -18,72 +18,74 @@ module VagrantPlugins
|
|
18
18
|
def call(env)
|
19
19
|
env[:ui].info(I18n.t('vagrant_libvirt.creating_domain_volume'))
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
env[:box_volumes].each_index do |index|
|
22
|
+
suffix_index = index > 0 ? "_#{index}" : ''
|
23
|
+
# Get config options.
|
24
|
+
config = env[:machine].provider_config
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
+
# This is name of newly created image for vm.
|
27
|
+
@name = "#{env[:domain_name]}#{suffix_index}.img"
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
# Verify the volume doesn't exist already.
|
30
|
+
domain_volume = env[:machine].provider.driver.connection.volumes.all(
|
31
|
+
name: @name
|
32
|
+
).first
|
33
|
+
raise Errors::DomainVolumeExists if domain_volume && domain_volume.id
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
# Get path to backing image - box volume.
|
36
|
+
box_volume = env[:machine].provider.driver.connection.volumes.all(
|
37
|
+
name: env[:box_volumes][index][:name]
|
38
|
+
).first
|
39
|
+
@backing_file = box_volume.path
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
+
# Virtual size of image. Take value worked out by HandleBoxImage
|
42
|
+
@capacity = env[:box_volumes][index][:virtual_size] # G
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
# Create new volume from xml template. Fog currently doesn't support
|
45
|
+
# volume snapshots directly.
|
46
|
+
begin
|
47
|
+
xml = Nokogiri::XML::Builder.new do |xml|
|
48
|
+
xml.volume do
|
49
|
+
xml.name(@name)
|
50
|
+
xml.capacity(@capacity, unit: 'G')
|
51
|
+
xml.target do
|
52
|
+
xml.format(type: 'qcow2')
|
53
|
+
xml.permissions do
|
54
|
+
xml.owner storage_uid(env)
|
55
|
+
xml.group storage_gid(env)
|
56
|
+
xml.label 'virt_image_t'
|
57
|
+
end
|
55
58
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
xml.backingStore do
|
60
|
+
xml.path(@backing_file)
|
61
|
+
xml.format(type: 'qcow2')
|
62
|
+
xml.permissions do
|
63
|
+
xml.owner storage_uid(env)
|
64
|
+
xml.group storage_gid(env)
|
65
|
+
xml.label 'virt_image_t'
|
66
|
+
end
|
64
67
|
end
|
65
68
|
end
|
69
|
+
end.to_xml(
|
70
|
+
save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION |
|
71
|
+
Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS |
|
72
|
+
Nokogiri::XML::Node::SaveOptions::FORMAT
|
73
|
+
)
|
74
|
+
if config.snapshot_pool_name != config.storage_pool_name
|
75
|
+
pool_name = config.snapshot_pool_name
|
76
|
+
else
|
77
|
+
pool_name = config.storage_pool_name
|
66
78
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
pool_name = config.storage_pool_name
|
79
|
+
@logger.debug "Using pool #{pool_name} for base box snapshot"
|
80
|
+
domain_volume = env[:machine].provider.driver.connection.volumes.create(
|
81
|
+
xml: xml,
|
82
|
+
pool_name: pool_name
|
83
|
+
)
|
84
|
+
rescue Fog::Errors::Error => e
|
85
|
+
raise Errors::FogDomainVolumeCreateError,
|
86
|
+
error_message: e.message
|
76
87
|
end
|
77
|
-
@logger.debug "Using pool #{pool_name} for base box snapshot"
|
78
|
-
domain_volume = env[:machine].provider.driver.connection.volumes.create(
|
79
|
-
xml: xml,
|
80
|
-
pool_name: pool_name
|
81
|
-
)
|
82
|
-
rescue Fog::Errors::Error => e
|
83
|
-
raise Errors::FogDomainVolumeCreateError,
|
84
|
-
error_message: e.message
|
85
88
|
end
|
86
|
-
|
87
89
|
@app.call(env)
|
88
90
|
end
|
89
91
|
end
|
@@ -54,6 +54,8 @@ module VagrantPlugins
|
|
54
54
|
env[:machine].provider.driver.connection.client
|
55
55
|
)
|
56
56
|
|
57
|
+
current_network = @available_networks.detect { |network| network[:name] == @options[:network_name] }
|
58
|
+
|
57
59
|
# Prepare a hash describing network for this specific interface.
|
58
60
|
@interface_network = {
|
59
61
|
name: nil,
|
@@ -64,11 +66,11 @@ module VagrantPlugins
|
|
64
66
|
domain_name: nil,
|
65
67
|
ipv6_address: options[:ipv6_address] || nil,
|
66
68
|
ipv6_prefix: options[:ipv6_prefix] || nil,
|
67
|
-
created: false,
|
68
|
-
active: false,
|
69
|
+
created: current_network.nil? ? false : true,
|
70
|
+
active: current_network.nil? ? false : current_network[:active],
|
69
71
|
autostart: options[:autostart] || false,
|
70
72
|
guest_ipv6: @options[:guest_ipv6] || 'yes',
|
71
|
-
libvirt_network: nil
|
73
|
+
libvirt_network: current_network.nil? ? nil : current_network[:libvirt_network]
|
72
74
|
}
|
73
75
|
|
74
76
|
if @options[:ip]
|
@@ -255,7 +257,9 @@ module VagrantPlugins
|
|
255
257
|
|
256
258
|
# Do we need to create new network?
|
257
259
|
unless @interface_network[:created]
|
258
|
-
@interface_network[:name] =
|
260
|
+
@interface_network[:name] = @options[:network_name] ?
|
261
|
+
@options[:network_name] :
|
262
|
+
'vagrant-private-dhcp'
|
259
263
|
@interface_network[:network_address] = net_address
|
260
264
|
|
261
265
|
# Set IP address of network (actually bridge). It will be used as
|
@@ -297,6 +301,9 @@ module VagrantPlugins
|
|
297
301
|
|
298
302
|
@network_ipv6_address = @interface_network[:ipv6_address]
|
299
303
|
@network_ipv6_prefix = @interface_network[:ipv6_prefix]
|
304
|
+
|
305
|
+
@network_bridge_stp = @options[:bridge_stp].nil? || @options[:bridge_stp] ? 'on' : 'off'
|
306
|
+
@network_bridge_delay = @options[:bridge_delay] ? @options[:bridge_delay] : 0
|
300
307
|
|
301
308
|
@network_forward_mode = @options[:forward_mode]
|
302
309
|
if @options[:forward_device]
|
@@ -11,10 +11,8 @@ module VagrantPlugins
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(env)
|
14
|
-
@env = env
|
15
|
-
|
16
14
|
# Get the ports we're forwarding
|
17
|
-
env[:forwarded_ports] = compile_forwarded_ports(env[:machine].config)
|
15
|
+
env[:forwarded_ports] = compile_forwarded_ports(env, env[:machine].config)
|
18
16
|
|
19
17
|
# Warn if we're port forwarding to any privileged ports
|
20
18
|
env[:forwarded_ports].each do |fp|
|
@@ -28,51 +26,52 @@ module VagrantPlugins
|
|
28
26
|
# Continue, we need the VM to be booted in order to grab its IP
|
29
27
|
@app.call env
|
30
28
|
|
31
|
-
if
|
29
|
+
if env[:forwarded_ports].any?
|
32
30
|
env[:ui].info I18n.t('vagrant.actions.vm.forward_ports.forwarding')
|
33
|
-
forward_ports
|
31
|
+
forward_ports(env)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
37
|
-
def forward_ports
|
38
|
-
|
35
|
+
def forward_ports(env)
|
36
|
+
env[:forwarded_ports].each do |fp|
|
39
37
|
message_attributes = {
|
40
38
|
adapter: fp[:adapter] || 'eth0',
|
41
39
|
guest_port: fp[:guest],
|
42
40
|
host_port: fp[:host]
|
43
41
|
}
|
44
42
|
|
45
|
-
|
43
|
+
env[:ui].info(I18n.t(
|
46
44
|
'vagrant.actions.vm.forward_ports.forwarding_entry',
|
47
|
-
message_attributes
|
45
|
+
**message_attributes
|
48
46
|
))
|
49
47
|
|
50
|
-
if fp[:protocol] == 'udp'
|
51
|
-
@env[:ui].warn I18n.t('vagrant_libvirt.warnings.forwarding_udp')
|
52
|
-
next
|
53
|
-
end
|
54
|
-
|
55
48
|
ssh_pid = redirect_port(
|
56
|
-
|
49
|
+
env,
|
50
|
+
env[:machine],
|
57
51
|
fp[:host_ip] || '*',
|
58
52
|
fp[:host],
|
59
|
-
fp[:guest_ip] ||
|
53
|
+
fp[:guest_ip] || env[:machine].provider.ssh_info[:host],
|
60
54
|
fp[:guest],
|
61
55
|
fp[:gateway_ports] || false
|
62
56
|
)
|
63
|
-
store_ssh_pid(fp[:host], ssh_pid)
|
57
|
+
store_ssh_pid(env[:machine], fp[:host], ssh_pid)
|
64
58
|
end
|
65
59
|
end
|
66
60
|
|
67
61
|
private
|
68
62
|
|
69
|
-
def compile_forwarded_ports(config)
|
63
|
+
def compile_forwarded_ports(env, config)
|
70
64
|
mappings = {}
|
71
65
|
|
72
66
|
config.vm.networks.each do |type, options|
|
73
67
|
next if options[:disabled]
|
74
68
|
|
75
|
-
|
69
|
+
if options[:protocol] == 'udp'
|
70
|
+
env[:ui].warn I18n.t('vagrant_libvirt.warnings.forwarding_udp')
|
71
|
+
next
|
72
|
+
end
|
73
|
+
|
74
|
+
next if type != :forwarded_port || ( options[:id] == 'ssh' && !env[:machine].provider_config.forward_ssh_port )
|
76
75
|
if options.fetch(:host_ip, '').to_s.strip.empty?
|
77
76
|
options.delete(:host_ip)
|
78
77
|
end
|
@@ -82,7 +81,7 @@ module VagrantPlugins
|
|
82
81
|
mappings.values
|
83
82
|
end
|
84
83
|
|
85
|
-
def redirect_port(machine, host_ip, host_port, guest_ip, guest_port,
|
84
|
+
def redirect_port(env, machine, host_ip, host_port, guest_ip, guest_port,
|
86
85
|
gateway_ports)
|
87
86
|
ssh_info = machine.ssh_info
|
88
87
|
params = %W(
|
@@ -114,7 +113,7 @@ module VagrantPlugins
|
|
114
113
|
if host_port <= 1024
|
115
114
|
@@lock.synchronize do
|
116
115
|
# TODO: add i18n
|
117
|
-
|
116
|
+
env[:ui].info 'Requesting sudo for host port(s) <= 1024'
|
118
117
|
r = system('sudo -v')
|
119
118
|
if r
|
120
119
|
ssh_cmd << 'sudo ' # add sudo prefix
|
@@ -125,14 +124,15 @@ module VagrantPlugins
|
|
125
124
|
ssh_cmd << "ssh -n #{options} #{params}"
|
126
125
|
|
127
126
|
@logger.debug "Forwarding port with `#{ssh_cmd}`"
|
128
|
-
log_file = ssh_forward_log_file(
|
129
|
-
|
127
|
+
log_file = ssh_forward_log_file(
|
128
|
+
env[:machine], host_ip, host_port, guest_ip, guest_port,
|
129
|
+
)
|
130
130
|
@logger.info "Logging to #{log_file}"
|
131
131
|
spawn(ssh_cmd, [:out, :err] => [log_file, 'w'], :pgroup => true)
|
132
132
|
end
|
133
133
|
|
134
|
-
def ssh_forward_log_file(host_ip, host_port, guest_ip, guest_port)
|
135
|
-
log_dir =
|
134
|
+
def ssh_forward_log_file(machine, host_ip, host_port, guest_ip, guest_port)
|
135
|
+
log_dir = machine.data_dir.join('logs')
|
136
136
|
log_dir.mkdir unless log_dir.directory?
|
137
137
|
File.join(
|
138
138
|
log_dir,
|
@@ -141,8 +141,8 @@ module VagrantPlugins
|
|
141
141
|
)
|
142
142
|
end
|
143
143
|
|
144
|
-
def store_ssh_pid(host_port, ssh_pid)
|
145
|
-
data_dir =
|
144
|
+
def store_ssh_pid(machine, host_port, ssh_pid)
|
145
|
+
data_dir = machine.data_dir.join('pids')
|
146
146
|
data_dir.mkdir unless data_dir.directory?
|
147
147
|
|
148
148
|
data_dir.join("ssh_#{host_port}.pid").open('w') do |pid_file|
|
@@ -169,13 +169,12 @@ module VagrantPlugins
|
|
169
169
|
end
|
170
170
|
|
171
171
|
def call(env)
|
172
|
-
|
173
|
-
|
174
|
-
if ssh_pids.any?
|
172
|
+
pids = ssh_pids(env[:machine])
|
173
|
+
if pids.any?
|
175
174
|
env[:ui].info I18n.t(
|
176
175
|
'vagrant.actions.vm.clear_forward_ports.deleting'
|
177
176
|
)
|
178
|
-
|
177
|
+
pids.each do |tag|
|
179
178
|
next unless ssh_pid?(tag[:pid])
|
180
179
|
@logger.debug "Killing pid #{tag[:pid]}"
|
181
180
|
kill_cmd = ''
|
@@ -191,7 +190,7 @@ module VagrantPlugins
|
|
191
190
|
end
|
192
191
|
|
193
192
|
@logger.info 'Removing ssh pid files'
|
194
|
-
remove_ssh_pids
|
193
|
+
remove_ssh_pids(env[:machine])
|
195
194
|
else
|
196
195
|
@logger.info 'No ssh pids found'
|
197
196
|
end
|
@@ -201,9 +200,9 @@ module VagrantPlugins
|
|
201
200
|
|
202
201
|
protected
|
203
202
|
|
204
|
-
def ssh_pids
|
205
|
-
glob =
|
206
|
-
|
203
|
+
def ssh_pids(machine)
|
204
|
+
glob = machine.data_dir.join('pids').to_s + '/ssh_*.pid'
|
205
|
+
ssh_pids = Dir[glob].map do |file|
|
207
206
|
{
|
208
207
|
pid: File.read(file).strip.chomp,
|
209
208
|
port: File.basename(file)['ssh_'.length..-1 * ('.pid'.length + 1)].to_i
|
@@ -217,8 +216,8 @@ module VagrantPlugins
|
|
217
216
|
`ps -o command= #{pid}`.strip.chomp =~ /ssh/
|
218
217
|
end
|
219
218
|
|
220
|
-
def remove_ssh_pids
|
221
|
-
glob =
|
219
|
+
def remove_ssh_pids(machine)
|
220
|
+
glob = machine.data_dir.join('pids').to_s + '/ssh_*.pid'
|
222
221
|
Dir[glob].each do |file|
|
223
222
|
File.delete file
|
224
223
|
end
|
@@ -13,24 +13,40 @@ module VagrantPlugins
|
|
13
13
|
def call(env)
|
14
14
|
env[:ui].info(I18n.t('vagrant_libvirt.halt_domain'))
|
15
15
|
|
16
|
+
timeout = env[:machine].config.vm.graceful_halt_timeout
|
16
17
|
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
|
17
18
|
raise Errors::NoDomainError if domain.nil?
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@logger.info('Trying Libvirt graceful shutdown.')
|
23
|
-
domain.shutdown
|
20
|
+
if env[:force_halt]
|
21
|
+
domain.poweroff
|
22
|
+
return @app.call(env)
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
25
|
begin
|
28
|
-
|
29
|
-
|
26
|
+
Timeout.timeout(timeout) do
|
27
|
+
begin
|
28
|
+
env[:machine].guest.capability(:halt)
|
29
|
+
rescue Timeout::Error
|
30
|
+
raise
|
31
|
+
rescue
|
32
|
+
@logger.info('Trying Libvirt graceful shutdown.')
|
33
|
+
# Read domain object again
|
34
|
+
dom = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
|
35
|
+
if dom.state.to_s == 'running'
|
36
|
+
dom.shutdown
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
domain.wait_for(timeout) do
|
41
|
+
!ready?
|
42
|
+
end
|
30
43
|
end
|
31
|
-
rescue
|
44
|
+
rescue Timeout::Error
|
32
45
|
@logger.info('VM is still running. Calling force poweroff.')
|
33
46
|
domain.poweroff
|
47
|
+
rescue
|
48
|
+
@logger.error('Failed to shutdown cleanly. Calling force poweroff.')
|
49
|
+
domain.poweroff
|
34
50
|
end
|
35
51
|
|
36
52
|
@app.call(env)
|
@@ -16,33 +16,69 @@ module VagrantPlugins
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def call(env)
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# Virtual size has to be set for allocating space in storage pool.
|
22
|
-
box_virtual_size = env[:machine].box.metadata['virtual_size']
|
23
|
-
raise Errors::NoBoxVirtualSizeSet if box_virtual_size.nil?
|
19
|
+
# Handle box formats converting between v1 => v2 and ensuring
|
20
|
+
# any obsolete settings are rejected.
|
24
21
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
disks = env[:machine].box.metadata.fetch('disks', [])
|
23
|
+
if disks.empty?
|
24
|
+
# Handle box v1 format
|
25
|
+
|
26
|
+
# Only qcow2 format is supported in v1, but other formats with backing
|
27
|
+
# store capability should be usable.
|
28
|
+
box_format = env[:machine].box.metadata['format']
|
29
|
+
HandleBoxImage.verify_box_format(box_format)
|
30
|
+
|
31
|
+
env[:box_volume_number] = 1
|
32
|
+
env[:box_volumes] = [{
|
33
|
+
:path => HandleBoxImage.get_box_image_path(env[:machine].box, 'box.img'),
|
34
|
+
:name => HandleBoxImage.get_volume_name(env[:machine].box, 'box'),
|
35
|
+
:virtual_size => HandleBoxImage.get_virtual_size(env),
|
36
|
+
:format => box_format,
|
37
|
+
}]
|
38
|
+
else
|
39
|
+
# Handle box v2 format
|
40
|
+
# {
|
41
|
+
# 'path': '<path-of-file-box>',
|
42
|
+
# 'name': '<name-to-use-in-storage>' # optional, will use index
|
43
|
+
# }
|
44
|
+
#
|
45
|
+
env[:box_volume_number] = disks.length()
|
46
|
+
target_volumes = Hash[]
|
47
|
+
env[:box_volumes] = Array.new(env[:box_volume_number]) { |i|
|
48
|
+
raise Errors::BoxFormatMissingAttribute, attribute: "disks[#{i}]['path']" if disks[i]['path'].nil?
|
49
|
+
|
50
|
+
image_path = HandleBoxImage.get_box_image_path(env[:machine].box, disks[i]['path'])
|
51
|
+
format, virtual_size = HandleBoxImage.get_box_disk_settings(image_path)
|
52
|
+
volume_name = HandleBoxImage.get_volume_name(
|
53
|
+
env[:machine].box,
|
54
|
+
disks[i].fetch('name', disks[i]['path'].sub(/#{File.extname(disks[i]['path'])}$/, '')),
|
55
|
+
)
|
56
|
+
|
57
|
+
# allowing name means needing to check that it doesn't cause a clash
|
58
|
+
existing = target_volumes[volume_name]
|
59
|
+
if !existing.nil?
|
60
|
+
raise Errors::BoxFormatDuplicateVolume, volume: volume_name, new_disk: "disks[#{i}]", orig_disk: "disks[#{existing}]"
|
61
|
+
end
|
62
|
+
target_volumes[volume_name] = i
|
63
|
+
|
64
|
+
{
|
65
|
+
:path => image_path,
|
66
|
+
:name => volume_name,
|
67
|
+
:virtual_size => virtual_size.to_i,
|
68
|
+
:format => HandleBoxImage.verify_box_format(format)
|
69
|
+
}
|
70
|
+
}
|
32
71
|
end
|
33
72
|
|
34
73
|
# Get config options
|
35
74
|
config = env[:machine].provider_config
|
36
|
-
|
37
|
-
env[:
|
38
|
-
|
39
|
-
|
40
|
-
env[:machine].box.version.to_s
|
41
|
-
rescue
|
42
|
-
''
|
43
|
-
end}.img"
|
75
|
+
box_image_files = []
|
76
|
+
env[:box_volumes].each do |d|
|
77
|
+
box_image_files.push(d[:path])
|
78
|
+
end
|
44
79
|
|
45
80
|
# Override box_virtual_size
|
81
|
+
box_virtual_size = env[:box_volumes][0][:virtual_size]
|
46
82
|
if config.machine_virtual_size
|
47
83
|
if config.machine_virtual_size < box_virtual_size
|
48
84
|
# Warn that a virtual size less than the box metadata size
|
@@ -57,77 +93,129 @@ module VagrantPlugins
|
|
57
93
|
end
|
58
94
|
end
|
59
95
|
# save for use by later actions
|
60
|
-
env[:
|
96
|
+
env[:box_volumes][0][:virtual_size] = box_virtual_size
|
61
97
|
|
62
98
|
# while inside the synchronize block take care not to call the next
|
63
99
|
# action in the chain, as must exit this block first to prevent
|
64
100
|
# locking all subsequent actions as well.
|
65
101
|
@@lock.synchronize do
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
env[:ui].info(I18n.t('vagrant_libvirt.uploading_volume'))
|
75
|
-
|
76
|
-
# Create new volume in storage pool
|
77
|
-
unless File.exist?(box_image_file)
|
78
|
-
raise Vagrant::Errors::BoxNotFound, name: env[:machine].box.name
|
102
|
+
env[:box_volumes].each_index do |i|
|
103
|
+
# Don't continue if image already exists in storage pool.
|
104
|
+
box_volume = env[:machine].provider.driver.connection.volumes.all(
|
105
|
+
name: env[:box_volumes][i][:name]
|
106
|
+
).first
|
107
|
+
next if box_volume && box_volume.id
|
108
|
+
|
109
|
+
send_box_image(env, config, box_image_files[i], env[:box_volumes][i])
|
79
110
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
111
|
+
end
|
112
|
+
|
113
|
+
@app.call(env)
|
114
|
+
end
|
84
115
|
|
85
|
-
|
86
|
-
@storage_volume_gid = storage_gid env
|
116
|
+
protected
|
87
117
|
|
118
|
+
def self.get_volume_name(box, name)
|
119
|
+
vol_name = box.name.to_s.dup.gsub('/', '-VAGRANTSLASH-')
|
120
|
+
vol_name << "_vagrant_box_image_#{
|
88
121
|
begin
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
capacity: "#{box_virtual_size}G",
|
93
|
-
format_type: box_format,
|
94
|
-
owner: @storage_volume_uid,
|
95
|
-
group: @storage_volume_gid,
|
96
|
-
pool_name: config.storage_pool_name
|
97
|
-
)
|
98
|
-
rescue Fog::Errors::Error => e
|
99
|
-
raise Errors::FogCreateVolumeError,
|
100
|
-
error_message: e.message
|
122
|
+
box.version.to_s
|
123
|
+
rescue
|
124
|
+
''
|
101
125
|
end
|
126
|
+
}_#{name.dup.gsub('/', '-SLASH-')}.img"
|
127
|
+
end
|
102
128
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
129
|
+
def self.get_virtual_size(env)
|
130
|
+
# Virtual size has to be set for allocating space in storage pool.
|
131
|
+
box_virtual_size = env[:machine].box.metadata['virtual_size']
|
132
|
+
raise Errors::NoBoxVirtualSizeSet if box_virtual_size.nil?
|
133
|
+
return box_virtual_size
|
134
|
+
end
|
111
135
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
136
|
+
def self.get_box_image_path(box, box_name)
|
137
|
+
return box.directory.join(box_name).to_s
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.verify_box_format(box_format, disk_index=nil)
|
141
|
+
if box_format.nil?
|
142
|
+
raise Errors::NoBoxFormatSet
|
143
|
+
elsif box_format != 'qcow2'
|
144
|
+
if disk_index.nil?
|
145
|
+
raise Errors::WrongBoxFormatSet
|
146
|
+
else
|
147
|
+
raise Errors::WrongDiskFormatSet,
|
148
|
+
disk_index: disk_index
|
124
149
|
end
|
125
150
|
end
|
151
|
+
return box_format
|
152
|
+
end
|
126
153
|
|
127
|
-
|
154
|
+
def self.get_box_disk_settings(image_path)
|
155
|
+
stdout, stderr, status = Open3.capture3('qemu-img', 'info', image_path)
|
156
|
+
if !status.success?
|
157
|
+
raise Errors::BadBoxImage, image: image_path, out: stdout, err: stderr
|
158
|
+
end
|
159
|
+
|
160
|
+
image_info_lines = stdout.split("\n")
|
161
|
+
format = image_info_lines.find { |l| l.start_with?('file format:') }.split(' ')[2]
|
162
|
+
virtual_size = image_info_lines.find { |l| l.start_with?('virtual size:') }.split(' ')[2]
|
163
|
+
|
164
|
+
return format, virtual_size
|
128
165
|
end
|
129
166
|
|
130
|
-
|
167
|
+
def send_box_image(env, config, box_image_file, box_volume)
|
168
|
+
# Box is not available as a storage pool volume. Create and upload
|
169
|
+
# it as a copy of local box image.
|
170
|
+
env[:ui].info(I18n.t('vagrant_libvirt.uploading_volume'))
|
171
|
+
|
172
|
+
# Create new volume in storage pool
|
173
|
+
unless File.exist?(box_image_file)
|
174
|
+
raise Vagrant::Errors::BoxNotFound, name: env[:machine].box.name
|
175
|
+
end
|
176
|
+
box_image_size = File.size(box_image_file) # B
|
177
|
+
message = "Creating volume #{box_volume[:name]}"
|
178
|
+
message << " in storage pool #{config.storage_pool_name}."
|
179
|
+
@logger.info(message)
|
180
|
+
|
181
|
+
begin
|
182
|
+
fog_volume = env[:machine].provider.driver.connection.volumes.create(
|
183
|
+
name: box_volume[:name],
|
184
|
+
allocation: "#{box_image_size / 1024 / 1024}M",
|
185
|
+
capacity: "#{box_volume[:virtual_size]}G",
|
186
|
+
format_type: box_volume[:format],
|
187
|
+
owner: storage_uid(env),
|
188
|
+
group: storage_gid(env),
|
189
|
+
pool_name: config.storage_pool_name
|
190
|
+
)
|
191
|
+
rescue Fog::Errors::Error => e
|
192
|
+
raise Errors::FogCreateVolumeError,
|
193
|
+
error_message: e.message
|
194
|
+
end
|
195
|
+
|
196
|
+
# Upload box image to storage pool
|
197
|
+
ret = upload_image(box_image_file, config.storage_pool_name,
|
198
|
+
box_volume[:name], env) do |progress|
|
199
|
+
rewriting(env[:ui]) do |ui|
|
200
|
+
ui.clear_line
|
201
|
+
ui.report_progress(progress, box_image_size, false)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Clear the line one last time since the progress meter doesn't
|
206
|
+
# disappear immediately.
|
207
|
+
rewriting(env[:ui]) {|ui| ui.clear_line}
|
208
|
+
|
209
|
+
# If upload failed or was interrupted, remove created volume from
|
210
|
+
# storage pool.
|
211
|
+
if env[:interrupted] || !ret
|
212
|
+
begin
|
213
|
+
fog_volume.destroy
|
214
|
+
rescue
|
215
|
+
nil
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
131
219
|
|
132
220
|
# Fog Libvirt currently doesn't support uploading images to storage
|
133
221
|
# pool volumes. Use ruby-libvirt client instead.
|