vagrant-libvirt 0.1.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +530 -100
  3. data/lib/vagrant-libvirt/action.rb +7 -1
  4. data/lib/vagrant-libvirt/action/clean_machine_folder.rb +28 -0
  5. data/lib/vagrant-libvirt/action/create_domain.rb +78 -22
  6. data/lib/vagrant-libvirt/action/create_domain_volume.rb +57 -57
  7. data/lib/vagrant-libvirt/action/create_network_interfaces.rb +1 -3
  8. data/lib/vagrant-libvirt/action/create_networks.rb +11 -4
  9. data/lib/vagrant-libvirt/action/destroy_domain.rb +1 -1
  10. data/lib/vagrant-libvirt/action/forward_ports.rb +40 -40
  11. data/lib/vagrant-libvirt/action/halt_domain.rb +25 -9
  12. data/lib/vagrant-libvirt/action/handle_box_image.rb +163 -72
  13. data/lib/vagrant-libvirt/action/is_running.rb +1 -3
  14. data/lib/vagrant-libvirt/action/is_suspended.rb +4 -4
  15. data/lib/vagrant-libvirt/action/package_domain.rb +10 -4
  16. data/lib/vagrant-libvirt/action/start_domain.rb +86 -29
  17. data/lib/vagrant-libvirt/action/wait_till_up.rb +8 -52
  18. data/lib/vagrant-libvirt/cap/{mount_p9.rb → mount_9p.rb} +2 -2
  19. data/lib/vagrant-libvirt/cap/mount_virtiofs.rb +37 -0
  20. data/lib/vagrant-libvirt/cap/public_address.rb +16 -0
  21. data/lib/vagrant-libvirt/cap/{synced_folder.rb → synced_folder_9p.rb} +4 -5
  22. data/lib/vagrant-libvirt/cap/synced_folder_virtiofs.rb +109 -0
  23. data/lib/vagrant-libvirt/config.rb +257 -34
  24. data/lib/vagrant-libvirt/driver.rb +49 -32
  25. data/lib/vagrant-libvirt/errors.rb +24 -1
  26. data/lib/vagrant-libvirt/plugin.rb +19 -5
  27. data/lib/vagrant-libvirt/provider.rb +2 -9
  28. data/lib/vagrant-libvirt/templates/domain.xml.erb +40 -10
  29. data/lib/vagrant-libvirt/templates/private_network.xml.erb +1 -1
  30. data/lib/vagrant-libvirt/templates/public_interface.xml.erb +5 -1
  31. data/lib/vagrant-libvirt/util.rb +1 -0
  32. data/lib/vagrant-libvirt/util/erb_template.rb +6 -7
  33. data/lib/vagrant-libvirt/util/network_util.rb +21 -3
  34. data/lib/vagrant-libvirt/util/ui.rb +23 -0
  35. data/lib/vagrant-libvirt/version +1 -0
  36. data/lib/vagrant-libvirt/version.rb +72 -1
  37. data/locales/en.yml +12 -0
  38. data/spec/spec_helper.rb +37 -3
  39. data/spec/support/binding_proc.rb +24 -0
  40. data/spec/support/libvirt_context.rb +3 -1
  41. data/spec/support/matchers/have_file_content.rb +63 -0
  42. data/spec/support/sharedcontext.rb +7 -3
  43. data/spec/unit/action/clean_machine_folder_spec.rb +48 -0
  44. data/spec/unit/action/create_domain_spec.rb +166 -0
  45. data/spec/unit/action/create_domain_spec/default_system_storage_pool.xml +17 -0
  46. data/spec/unit/action/create_domain_spec/default_user_storage_pool.xml +17 -0
  47. data/spec/unit/action/create_domain_volume_spec.rb +102 -0
  48. data/spec/unit/action/create_domain_volume_spec/one_disk_in_storage.xml +21 -0
  49. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_0.xml +21 -0
  50. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_1.xml +21 -0
  51. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_2.xml +21 -0
  52. data/spec/unit/action/destroy_domain_spec.rb +3 -3
  53. data/spec/unit/action/forward_ports_spec.rb +202 -0
  54. data/spec/unit/action/halt_domain_spec.rb +90 -0
  55. data/spec/unit/action/handle_box_image_spec.rb +363 -0
  56. data/spec/unit/action/set_name_of_domain_spec.rb +2 -2
  57. data/spec/unit/action/start_domain_spec.rb +231 -0
  58. data/spec/unit/action/start_domain_spec/clock_timer_rtc.xml +50 -0
  59. data/spec/unit/action/start_domain_spec/default.xml +48 -0
  60. data/spec/unit/action/start_domain_spec/default_added_tpm_path.xml +48 -0
  61. data/spec/unit/action/start_domain_spec/default_added_tpm_version.xml +48 -0
  62. data/spec/unit/action/wait_till_up_spec.rb +22 -21
  63. data/spec/unit/config_spec.rb +438 -0
  64. data/spec/unit/provider_spec.rb +11 -0
  65. data/spec/unit/templates/domain_all_settings.xml +16 -3
  66. data/spec/unit/templates/domain_custom_cpu_model.xml +4 -1
  67. data/spec/unit/templates/domain_defaults.xml +4 -1
  68. data/spec/unit/templates/domain_spec.rb +102 -3
  69. data/spec/unit/templates/tpm/version_1.2.xml +54 -0
  70. data/spec/unit/templates/tpm/version_2.0.xml +53 -0
  71. metadata +108 -16
  72. data/lib/vagrant-libvirt/action/halt_confirm.rb +0 -20
@@ -70,7 +70,7 @@ module VagrantPlugins
70
70
 
71
71
  # remove root storage
72
72
  root_disk = domain.volumes.select do |x|
73
- x.name == libvirt_domain.name + '.img'
73
+ x.name == libvirt_domain.name + '.img' if x
74
74
  end.first
75
75
  root_disk.destroy if root_disk
76
76
  end
@@ -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 @env[:forwarded_ports].any?
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
- @env[:forwarded_ports].each do |fp|
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
- @env[:ui].info(I18n.t(
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
- @env[:machine],
49
+ env,
50
+ env[:machine],
57
51
  fp[:host_ip] || '*',
58
52
  fp[:host],
59
- fp[:guest_ip] || @env[:machine].provider.ssh_info[:host],
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
- next unless type == :forwarded_port && options[:id] != 'ssh'
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(
@@ -98,6 +97,7 @@ module VagrantPlugins
98
97
  Port=#{ssh_info[:port]}
99
98
  UserKnownHostsFile=/dev/null
100
99
  ExitOnForwardFailure=yes
100
+ ControlMaster=no
101
101
  StrictHostKeyChecking=no
102
102
  PasswordAuthentication=no
103
103
  ForwardX11=#{ssh_info[:forward_x11] ? 'yes' : 'no'}
@@ -106,14 +106,14 @@ module VagrantPlugins
106
106
  "IdentityFile='\"#{pk}\"'"
107
107
  end).map { |s| s.prepend('-o ') }.join(' ')
108
108
 
109
- options += " -o ProxyCommand=\"#{ssh_info[:proxy_command]}\"" if machine.provider_config.connect_via_ssh
109
+ options += " -o ProxyCommand=\"#{ssh_info[:proxy_command]}\"" if machine.provider_config.proxy_command
110
110
 
111
111
  # TODO: instead of this, try and lock and get the stdin from spawn...
112
- ssh_cmd = 'exec '
112
+ ssh_cmd = ''
113
113
  if host_port <= 1024
114
114
  @@lock.synchronize do
115
115
  # TODO: add i18n
116
- @env[:ui].info 'Requesting sudo for host port(s) <= 1024'
116
+ env[:ui].info 'Requesting sudo for host port(s) <= 1024'
117
117
  r = system('sudo -v')
118
118
  if r
119
119
  ssh_cmd << 'sudo ' # add sudo prefix
@@ -124,14 +124,15 @@ module VagrantPlugins
124
124
  ssh_cmd << "ssh -n #{options} #{params}"
125
125
 
126
126
  @logger.debug "Forwarding port with `#{ssh_cmd}`"
127
- log_file = ssh_forward_log_file(host_ip, host_port,
128
- guest_ip, guest_port)
127
+ log_file = ssh_forward_log_file(
128
+ env[:machine], host_ip, host_port, guest_ip, guest_port,
129
+ )
129
130
  @logger.info "Logging to #{log_file}"
130
- spawn(ssh_cmd, [:out, :err] => [log_file, 'w'])
131
+ spawn(ssh_cmd, [:out, :err] => [log_file, 'w'], :pgroup => true)
131
132
  end
132
133
 
133
- def ssh_forward_log_file(host_ip, host_port, guest_ip, guest_port)
134
- log_dir = @env[:machine].data_dir.join('logs')
134
+ def ssh_forward_log_file(machine, host_ip, host_port, guest_ip, guest_port)
135
+ log_dir = machine.data_dir.join('logs')
135
136
  log_dir.mkdir unless log_dir.directory?
136
137
  File.join(
137
138
  log_dir,
@@ -140,8 +141,8 @@ module VagrantPlugins
140
141
  )
141
142
  end
142
143
 
143
- def store_ssh_pid(host_port, ssh_pid)
144
- data_dir = @env[:machine].data_dir.join('pids')
144
+ def store_ssh_pid(machine, host_port, ssh_pid)
145
+ data_dir = machine.data_dir.join('pids')
145
146
  data_dir.mkdir unless data_dir.directory?
146
147
 
147
148
  data_dir.join("ssh_#{host_port}.pid").open('w') do |pid_file|
@@ -168,13 +169,12 @@ module VagrantPlugins
168
169
  end
169
170
 
170
171
  def call(env)
171
- @env = env
172
-
173
- if ssh_pids.any?
172
+ pids = ssh_pids(env[:machine])
173
+ if pids.any?
174
174
  env[:ui].info I18n.t(
175
175
  'vagrant.actions.vm.clear_forward_ports.deleting'
176
176
  )
177
- ssh_pids.each do |tag|
177
+ pids.each do |tag|
178
178
  next unless ssh_pid?(tag[:pid])
179
179
  @logger.debug "Killing pid #{tag[:pid]}"
180
180
  kill_cmd = ''
@@ -190,7 +190,7 @@ module VagrantPlugins
190
190
  end
191
191
 
192
192
  @logger.info 'Removing ssh pid files'
193
- remove_ssh_pids
193
+ remove_ssh_pids(env[:machine])
194
194
  else
195
195
  @logger.info 'No ssh pids found'
196
196
  end
@@ -200,9 +200,9 @@ module VagrantPlugins
200
200
 
201
201
  protected
202
202
 
203
- def ssh_pids
204
- glob = @env[:machine].data_dir.join('pids').to_s + '/ssh_*.pid'
205
- @ssh_pids = Dir[glob].map do |file|
203
+ def ssh_pids(machine)
204
+ glob = machine.data_dir.join('pids').to_s + '/ssh_*.pid'
205
+ ssh_pids = Dir[glob].map do |file|
206
206
  {
207
207
  pid: File.read(file).strip.chomp,
208
208
  port: File.basename(file)['ssh_'.length..-1 * ('.pid'.length + 1)].to_i
@@ -216,8 +216,8 @@ module VagrantPlugins
216
216
  `ps -o command= #{pid}`.strip.chomp =~ /ssh/
217
217
  end
218
218
 
219
- def remove_ssh_pids
220
- glob = @env[:machine].data_dir.join('pids').to_s + '/ssh_*.pid'
219
+ def remove_ssh_pids(machine)
220
+ glob = machine.data_dir.join('pids').to_s + '/ssh_*.pid'
221
221
  Dir[glob].each do |file|
222
222
  File.delete file
223
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
- begin
20
- env[:machine].guest.capability(:halt)
21
- rescue
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
- domain.wait_for(30) do
29
- !ready?
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 Fog::Errors::TimeoutError
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)
@@ -5,6 +5,7 @@ module VagrantPlugins
5
5
  module Action
6
6
  class HandleBoxImage
7
7
  include VagrantPlugins::ProviderLibvirt::Util::StorageUtil
8
+ include VagrantPlugins::ProviderLibvirt::Util::Ui
8
9
 
9
10
 
10
11
  @@lock = Mutex.new
@@ -15,33 +16,69 @@ module VagrantPlugins
15
16
  end
16
17
 
17
18
  def call(env)
18
- # Verify box metadata for mandatory values.
19
- #
20
- # Virtual size has to be set for allocating space in storage pool.
21
- box_virtual_size = env[:machine].box.metadata['virtual_size']
22
- raise Errors::NoBoxVirtualSizeSet if box_virtual_size.nil?
19
+ # Handle box formats converting between v1 => v2 and ensuring
20
+ # any obsolete settings are rejected.
23
21
 
24
- # Support qcow2 format only for now, but other formats with backing
25
- # store capability should be usable.
26
- box_format = env[:machine].box.metadata['format']
27
- if box_format.nil?
28
- raise Errors::NoBoxFormatSet
29
- elsif box_format != 'qcow2'
30
- raise Errors::WrongBoxFormatSet
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
+ }
31
71
  end
32
72
 
33
73
  # Get config options
34
74
  config = env[:machine].provider_config
35
- box_image_file = env[:machine].box.directory.join('box.img').to_s
36
- env[:box_volume_name] = env[:machine].box.name.to_s.dup.gsub('/', '-VAGRANTSLASH-')
37
- env[:box_volume_name] << "_vagrant_box_image_#{
38
- begin
39
- env[:machine].box.version.to_s
40
- rescue
41
- ''
42
- end}.img"
75
+ box_image_files = []
76
+ env[:box_volumes].each do |d|
77
+ box_image_files.push(d[:path])
78
+ end
43
79
 
44
80
  # Override box_virtual_size
81
+ box_virtual_size = env[:box_volumes][0][:virtual_size]
45
82
  if config.machine_virtual_size
46
83
  if config.machine_virtual_size < box_virtual_size
47
84
  # Warn that a virtual size less than the box metadata size
@@ -56,75 +93,129 @@ module VagrantPlugins
56
93
  end
57
94
  end
58
95
  # save for use by later actions
59
- env[:box_virtual_size] = box_virtual_size
96
+ env[:box_volumes][0][:virtual_size] = box_virtual_size
60
97
 
61
98
  # while inside the synchronize block take care not to call the next
62
99
  # action in the chain, as must exit this block first to prevent
63
100
  # locking all subsequent actions as well.
64
101
  @@lock.synchronize do
65
- # Don't continue if image already exists in storage pool.
66
- box_volume = env[:machine].provider.driver.connection.volumes.all(
67
- name: env[:box_volume_name]
68
- ).first
69
- break if box_volume && box_volume.id
70
-
71
- # Box is not available as a storage pool volume. Create and upload
72
- # it as a copy of local box image.
73
- env[:ui].info(I18n.t('vagrant_libvirt.uploading_volume'))
74
-
75
- # Create new volume in storage pool
76
- unless File.exist?(box_image_file)
77
- 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])
78
110
  end
79
- box_image_size = File.size(box_image_file) # B
80
- message = "Creating volume #{env[:box_volume_name]}"
81
- message << " in storage pool #{config.storage_pool_name}."
82
- @logger.info(message)
111
+ end
83
112
 
84
- @storage_volume_uid = storage_uid env
85
- @storage_volume_gid = storage_gid env
113
+ @app.call(env)
114
+ end
115
+
116
+ protected
86
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_#{
87
121
  begin
88
- fog_volume = env[:machine].provider.driver.connection.volumes.create(
89
- name: env[:box_volume_name],
90
- allocation: "#{box_image_size / 1024 / 1024}M",
91
- capacity: "#{box_virtual_size}G",
92
- format_type: box_format,
93
- owner: @storage_volume_uid,
94
- group: @storage_volume_gid,
95
- pool_name: config.storage_pool_name
96
- )
97
- rescue Fog::Errors::Error => e
98
- raise Errors::FogCreateVolumeError,
99
- error_message: e.message
122
+ box.version.to_s
123
+ rescue
124
+ ''
100
125
  end
126
+ }_#{name.dup.gsub('/', '-SLASH-')}.img"
127
+ end
101
128
 
102
- # Upload box image to storage pool
103
- ret = upload_image(box_image_file, config.storage_pool_name,
104
- env[:box_volume_name], env) do |progress|
105
- env[:ui].clear_line
106
- env[:ui].report_progress(progress, box_image_size, false)
107
- 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
108
135
 
109
- # Clear the line one last time since the progress meter doesn't
110
- # disappear immediately.
111
- env[:ui].clear_line
112
-
113
- # If upload failed or was interrupted, remove created volume from
114
- # storage pool.
115
- if env[:interrupted] || !ret
116
- begin
117
- fog_volume.destroy
118
- rescue
119
- nil
120
- end
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
121
149
  end
122
150
  end
151
+ return box_format
152
+ end
123
153
 
124
- @app.call(env)
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
125
165
  end
126
166
 
127
- protected
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
128
219
 
129
220
  # Fog Libvirt currently doesn't support uploading images to storage
130
221
  # pool volumes. Use ruby-libvirt client instead.