vagrant-libvirt 0.4.1 → 0.5.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 +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.
|