vagrant-libvirt 0.5.0 → 0.6.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +134 -23
  3. data/lib/vagrant-libvirt/action/clean_machine_folder.rb +4 -0
  4. data/lib/vagrant-libvirt/action/create_domain.rb +11 -3
  5. data/lib/vagrant-libvirt/action/create_domain_volume.rb +7 -2
  6. data/lib/vagrant-libvirt/action/create_network_interfaces.rb +8 -2
  7. data/lib/vagrant-libvirt/action/create_networks.rb +12 -8
  8. data/lib/vagrant-libvirt/action/destroy_domain.rb +2 -0
  9. data/lib/vagrant-libvirt/action/destroy_networks.rb +2 -0
  10. data/lib/vagrant-libvirt/action/forward_ports.rb +7 -5
  11. data/lib/vagrant-libvirt/action/halt_domain.rb +4 -34
  12. data/lib/vagrant-libvirt/action/handle_box_image.rb +18 -13
  13. data/lib/vagrant-libvirt/action/handle_storage_pool.rb +7 -1
  14. data/lib/vagrant-libvirt/action/is_created.rb +2 -0
  15. data/lib/vagrant-libvirt/action/is_running.rb +2 -0
  16. data/lib/vagrant-libvirt/action/is_suspended.rb +2 -0
  17. data/lib/vagrant-libvirt/action/message_already_created.rb +2 -0
  18. data/lib/vagrant-libvirt/action/message_not_created.rb +2 -0
  19. data/lib/vagrant-libvirt/action/message_not_running.rb +2 -0
  20. data/lib/vagrant-libvirt/action/message_not_suspended.rb +2 -0
  21. data/lib/vagrant-libvirt/action/message_will_not_destroy.rb +2 -0
  22. data/lib/vagrant-libvirt/action/package_domain.rb +133 -68
  23. data/lib/vagrant-libvirt/action/prepare_nfs_settings.rb +2 -0
  24. data/lib/vagrant-libvirt/action/prepare_nfs_valid_ids.rb +2 -0
  25. data/lib/vagrant-libvirt/action/prune_nfs_exports.rb +2 -0
  26. data/lib/vagrant-libvirt/action/read_mac_addresses.rb +2 -0
  27. data/lib/vagrant-libvirt/action/remove_libvirt_image.rb +2 -0
  28. data/lib/vagrant-libvirt/action/remove_stale_volume.rb +2 -0
  29. data/lib/vagrant-libvirt/action/resume_domain.rb +2 -0
  30. data/lib/vagrant-libvirt/action/set_boot_order.rb +8 -2
  31. data/lib/vagrant-libvirt/action/set_name_of_domain.rb +3 -1
  32. data/lib/vagrant-libvirt/action/share_folders.rb +2 -0
  33. data/lib/vagrant-libvirt/action/shutdown_domain.rb +49 -0
  34. data/lib/vagrant-libvirt/action/start_domain.rb +26 -17
  35. data/lib/vagrant-libvirt/action/suspend_domain.rb +2 -0
  36. data/lib/vagrant-libvirt/action/wait_till_up.rb +2 -0
  37. data/lib/vagrant-libvirt/action.rb +34 -4
  38. data/lib/vagrant-libvirt/cap/mount_9p.rb +2 -0
  39. data/lib/vagrant-libvirt/cap/mount_virtiofs.rb +2 -0
  40. data/lib/vagrant-libvirt/cap/nic_mac_addresses.rb +2 -0
  41. data/lib/vagrant-libvirt/cap/public_address.rb +2 -0
  42. data/lib/vagrant-libvirt/cap/synced_folder_9p.rb +5 -2
  43. data/lib/vagrant-libvirt/cap/synced_folder_virtiofs.rb +5 -2
  44. data/lib/vagrant-libvirt/config.rb +58 -24
  45. data/lib/vagrant-libvirt/driver.rb +67 -12
  46. data/lib/vagrant-libvirt/errors.rb +2 -0
  47. data/lib/vagrant-libvirt/plugin.rb +2 -0
  48. data/lib/vagrant-libvirt/provider.rb +2 -0
  49. data/lib/vagrant-libvirt/templates/domain.xml.erb +4 -2
  50. data/lib/vagrant-libvirt/templates/public_interface.xml.erb +1 -0
  51. data/lib/vagrant-libvirt/util/byte_number.rb +71 -0
  52. data/lib/vagrant-libvirt/util/collection.rb +2 -0
  53. data/lib/vagrant-libvirt/util/erb_template.rb +2 -0
  54. data/lib/vagrant-libvirt/util/error_codes.rb +2 -0
  55. data/lib/vagrant-libvirt/util/network_util.rb +3 -0
  56. data/lib/vagrant-libvirt/util/nfs.rb +2 -0
  57. data/lib/vagrant-libvirt/util/storage_util.rb +1 -0
  58. data/lib/vagrant-libvirt/util/timer.rb +2 -0
  59. data/lib/vagrant-libvirt/util/ui.rb +1 -0
  60. data/lib/vagrant-libvirt/util.rb +2 -0
  61. data/lib/vagrant-libvirt/version +1 -1
  62. data/lib/vagrant-libvirt/version.rb +2 -0
  63. data/lib/vagrant-libvirt.rb +2 -0
  64. data/locales/en.yml +2 -0
  65. data/spec/spec_helper.rb +2 -0
  66. data/spec/support/binding_proc.rb +2 -0
  67. data/spec/support/environment_helper.rb +2 -0
  68. data/spec/support/libvirt_context.rb +2 -0
  69. data/spec/support/matchers/have_file_content.rb +2 -0
  70. data/spec/support/sharedcontext.rb +3 -0
  71. data/spec/support/temporary_dir.rb +12 -0
  72. data/spec/unit/action/clean_machine_folder_spec.rb +16 -4
  73. data/spec/unit/action/create_domain_spec/additional_disks_domain.xml +61 -0
  74. data/spec/unit/action/create_domain_spec/default_domain.xml +55 -0
  75. data/spec/unit/action/create_domain_spec.rb +68 -32
  76. data/spec/unit/action/create_domain_volume_spec/one_disk_in_storage.xml +1 -1
  77. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_0.xml +1 -1
  78. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_1.xml +1 -1
  79. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_2.xml +1 -1
  80. data/spec/unit/action/create_domain_volume_spec.rb +10 -4
  81. data/spec/unit/action/destroy_domain_spec.rb +8 -2
  82. data/spec/unit/action/forward_ports_spec.rb +2 -0
  83. data/spec/unit/action/halt_domain_spec.rb +30 -57
  84. data/spec/unit/action/handle_box_image_spec.rb +104 -24
  85. data/spec/unit/action/package_domain_spec.rb +304 -0
  86. data/spec/unit/action/set_name_of_domain_spec.rb +2 -0
  87. data/spec/unit/action/shutdown_domain_spec.rb +131 -0
  88. data/spec/unit/action/start_domain_spec/existing.xml +62 -0
  89. data/spec/unit/action/start_domain_spec.rb +18 -28
  90. data/spec/unit/action/wait_till_up_spec.rb +2 -0
  91. data/spec/unit/action_spec.rb +96 -0
  92. data/spec/unit/config_spec.rb +56 -3
  93. data/spec/unit/driver_spec.rb +155 -0
  94. data/spec/unit/templates/domain_all_settings.xml +4 -0
  95. data/spec/unit/templates/domain_spec.rb +2 -0
  96. data/spec/unit/util/byte_number_spec.rb +28 -0
  97. metadata +59 -38
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
 
3
5
  module VagrantPlugins
@@ -42,8 +44,12 @@ module VagrantPlugins
42
44
  @storage_pool_path = storage_pool_path(env)
43
45
  @storage_pool_uid = storage_uid(env)
44
46
  @storage_pool_gid = storage_gid(env)
47
+ xml = to_xml('default_storage_pool')
48
+ @logger.debug {
49
+ "Creating Storage Pool with XML:\n#{xml}"
50
+ }
45
51
  libvirt_pool = env[:machine].provider.driver.connection.client.define_storage_pool_xml(
46
- to_xml('default_storage_pool')
52
+ xml
47
53
  )
48
54
  libvirt_pool.build
49
55
  libvirt_pool.create
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,6 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
  require 'log4r'
3
5
 
6
+ class String
7
+ def unindent
8
+ gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")
9
+ end
10
+ end
11
+
4
12
  module VagrantPlugins
5
13
  module ProviderLibvirt
6
14
  module Action
@@ -12,8 +20,9 @@ module VagrantPlugins
12
20
  def initialize(app, env)
13
21
  @logger = Log4r::Logger.new('vagrant_libvirt::action::package_domain')
14
22
  @app = app
15
- env['package.files'] ||= {}
16
- env['package.output'] ||= 'package.box'
23
+
24
+ @options = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPTIONS', '')
25
+ @operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir,-ssh-hostkeys,-customize')
17
26
  end
18
27
 
19
28
  def call(env)
@@ -22,90 +31,100 @@ module VagrantPlugins
22
31
  env[:machine].id
23
32
  )
24
33
  domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
25
- root_disk = domain.volumes.select do |x|
34
+
35
+ volumes = domain.volumes.select { |x| !x.nil? }
36
+ root_disk = volumes.select do |x|
26
37
  x.name == libvirt_domain.name + '.img'
27
38
  end.first
28
39
  raise Errors::NoDomainVolume if root_disk.nil?
29
- boxname = env['package.output']
30
- raise "#{boxname}: Already exists" if File.exist?(boxname)
31
- @tmp_dir = Dir.pwd + '/_tmp_package'
32
- @tmp_img = @tmp_dir + '/box.img'
33
- FileUtils.mkdir_p(@tmp_dir)
34
- env[:ui].info("Downloading #{root_disk.name} to #{@tmp_img}")
35
- ret = download_image(@tmp_img, env[:machine].provider_config.storage_pool_name,
36
- root_disk.name, env) do |progress,image_size|
37
- rewriting(env[:ui]) do |ui|
38
- ui.clear_line
39
- ui.report_progress(progress, image_size, false)
40
- end
41
- end
42
- # Clear the line one last time since the progress meter doesn't
43
- # disappear immediately.
44
- rewriting(env[:ui]) {|ui| ui.clear_line}
45
- backing = `qemu-img info "#{@tmp_img}" | grep 'backing file:' | cut -d ':' -f2`.chomp
46
- if backing
47
- env[:ui].info('Image has backing image, copying image and rebasing ...')
48
- `qemu-img rebase -p -b "" #{@tmp_img}`
49
- end
50
- # remove hw association with interface
51
- # working for centos with lvs default disks
52
- options = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPTIONS', '')
53
- operations = ENV.fetch('VAGRANT_LIBVIRT_VIRT_SYSPREP_OPERATIONS', 'defaults,-ssh-userdir,-customize')
54
- `virt-sysprep --no-logfile --operations #{operations} -a #{@tmp_img} #{options}`
55
- `virt-sparsify --in-place #{@tmp_img}`
56
- # add any user provided file
57
- extra = ''
58
- @tmp_include = @tmp_dir + '/_include'
59
- if env['package.include']
60
- extra = './_include'
61
- Dir.mkdir(@tmp_include)
62
- env['package.include'].each do |f|
63
- env[:ui].info("Including user file: #{f}")
64
- FileUtils.cp(f, @tmp_include)
40
+
41
+ package_func = method(:package_v1)
42
+
43
+ box_format = ENV.fetch('VAGRANT_LIBVIRT_BOX_FORMAT_VERSION', nil)
44
+
45
+ case box_format
46
+ when nil
47
+ if volumes.length() > 1
48
+ msg = "Detected more than one volume for machine, in the future this will switch to using the v2 "
49
+ msg += "box format v2 automatically."
50
+ msg += "\nIf you want to include the additional disks attached when packaging please set the "
51
+ msg += "env variable VAGRANT_LIBVIRT_BOX_FORMAT_VERSION=v2 to use the new format. If you want "
52
+ msg += "to ensure that your box uses the old format for single disk only, please set the "
53
+ msg += "environment variable explicitly to 'v1'"
54
+ env[:ui].warn(msg)
65
55
  end
56
+ when 'v2'
57
+ package_func = method(:package_v2)
58
+ when 'v1'
59
+ else
60
+ env[:ui].warn("Unrecognized value for 'VAGRANT_LIBVIRT_BOX_FORMAT_VERSION', defaulting to v1")
66
61
  end
67
- if env['package.vagrantfile']
68
- extra = './_include'
69
- Dir.mkdir(@tmp_include) unless File.directory?(@tmp_include)
70
- env[:ui].info('Including user Vagrantfile')
71
- FileUtils.cp(env['package.vagrantfile'], @tmp_include + '/Vagrantfile')
72
- end
73
- Dir.chdir(@tmp_dir)
74
- info = JSON.parse(`qemu-img info --output=json #{@tmp_img}`)
75
- img_size = (Float(info['virtual-size'])/(1024**3)).ceil
76
- File.write(@tmp_dir + '/metadata.json', metadata_content(img_size))
77
- File.write(@tmp_dir + '/Vagrantfile', vagrantfile_content)
78
- assemble_box(boxname, extra)
79
- FileUtils.mv(@tmp_dir + '/' + boxname, '../' + boxname)
80
- FileUtils.rm_rf(@tmp_dir)
81
- env[:ui].info('Box created')
82
- env[:ui].info('You can now add the box:')
83
- env[:ui].info("vagrant box add #{boxname} --name any_comfortable_name")
62
+
63
+ metadata = package_func.call(env, volumes)
64
+
65
+ # metadata / Vagrantfile
66
+ package_directory = env["package.directory"]
67
+ File.write(package_directory + '/metadata.json', metadata)
68
+ File.write(package_directory + '/Vagrantfile', vagrantfile_content(env))
69
+
84
70
  @app.call(env)
85
71
  end
86
72
 
87
- def assemble_box(boxname, extra)
88
- `tar cvzf "#{boxname}" --totals ./metadata.json ./Vagrantfile ./box.img #{extra}`
73
+ def package_v1(env, volumes)
74
+ domain_img = download_volume(env, volumes.first, 'box.img')
75
+
76
+ sysprep_domain(domain_img)
77
+ sparsify_volume(domain_img)
78
+
79
+ info = JSON.parse(`qemu-img info --output=json #{domain_img}`)
80
+ img_size = (Float(info['virtual-size'])/(1024**3)).ceil
81
+
82
+ return metadata_content_v1(img_size)
83
+ end
84
+
85
+ def package_v2(env, volumes)
86
+ disks = []
87
+ volumes.each_with_index do |vol, idx|
88
+ disk = {:path => "box_#{idx+1}.img"}
89
+ volume_img = download_volume(env, vol, disk[:path])
90
+
91
+ if idx == 0
92
+ sysprep_domain(volume_img)
93
+ end
94
+
95
+ sparsify_volume(volume_img)
96
+
97
+ disks.push(disk)
98
+ end
99
+
100
+ return metadata_content_v2(disks)
89
101
  end
90
102
 
91
- def vagrantfile_content
92
- <<-EOF
103
+ def vagrantfile_content(env)
104
+ include_vagrantfile = ""
105
+
106
+ if env["package.vagrantfile"]
107
+ include_vagrantfile = <<-EOF
108
+
109
+ # Load include vagrant file if it exists after the auto-generated
110
+ # so it can override any of the settings
111
+ include_vagrantfile = File.expand_path("../include/_Vagrantfile", __FILE__)
112
+ load include_vagrantfile if File.exist?(include_vagrantfile)
113
+ EOF
114
+ end
115
+
116
+ <<-EOF.unindent
93
117
  Vagrant.configure("2") do |config|
94
118
  config.vm.provider :libvirt do |libvirt|
95
119
  libvirt.driver = "kvm"
96
- libvirt.host = ""
97
- libvirt.connect_via_ssh = false
98
- libvirt.storage_pool_name = "default"
99
120
  end
121
+ #{include_vagrantfile}
100
122
  end
101
-
102
- user_vagrantfile = File.expand_path('../_include/Vagrantfile', __FILE__)
103
- load user_vagrantfile if File.exists?(user_vagrantfile)
104
123
  EOF
105
124
  end
106
125
 
107
- def metadata_content(filesize)
108
- <<-EOF
126
+ def metadata_content_v1(filesize)
127
+ <<-EOF.unindent
109
128
  {
110
129
  "provider": "libvirt",
111
130
  "format": "qcow2",
@@ -114,8 +133,54 @@ module VagrantPlugins
114
133
  EOF
115
134
  end
116
135
 
136
+ def metadata_content_v2(disks)
137
+ data = {
138
+ "provider": "libvirt",
139
+ "format": "qcow2",
140
+ "disks": disks.each do |disk|
141
+ {'path': disk[:path]}
142
+ end
143
+ }
144
+ JSON.pretty_generate(data)
145
+ end
146
+
117
147
  protected
118
148
 
149
+ def sparsify_volume(volume_img)
150
+ `virt-sparsify --in-place #{volume_img}`
151
+ end
152
+
153
+ def sysprep_domain(domain_img)
154
+ # remove hw association with interface
155
+ # working for centos with lvs default disks
156
+ `virt-sysprep --no-logfile --operations #{@operations} -a #{domain_img} #{@options}`
157
+ end
158
+
159
+ def download_volume(env, volume, disk_path)
160
+ package_directory = env["package.directory"]
161
+ volume_img = package_directory + '/' + disk_path
162
+ env[:ui].info("Downloading #{volume.name} to #{volume_img}")
163
+ download_image(volume_img, env[:machine].provider_config.storage_pool_name,
164
+ volume.name, env) do |progress,image_size|
165
+ rewriting(env[:ui]) do |ui|
166
+ ui.clear_line
167
+ ui.report_progress(progress, image_size, false)
168
+ end
169
+ end
170
+ # Clear the line one last time since the progress meter doesn't
171
+ # disappear immediately.
172
+ rewriting(env[:ui]) {|ui| ui.clear_line}
173
+
174
+ # Prep domain disk
175
+ backing = `qemu-img info "#{volume_img}" | grep 'backing file:' | cut -d ':' -f2`.chomp
176
+ if backing
177
+ env[:ui].info('Image has backing image, copying image and rebasing ...')
178
+ `qemu-img rebase -p -b "" #{volume_img}`
179
+ end
180
+
181
+ return volume_img
182
+ end
183
+
119
184
  # Fog libvirt currently doesn't support downloading images from storage
120
185
  # pool volumes. Use ruby-libvirt client instead.
121
186
  def download_image(image_file, pool_name, volume_name, env)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'nokogiri'
2
4
  require 'socket'
3
5
  require 'timeout'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderLibvirt
3
5
  module Action
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'vagrant-libvirt/util/nfs'
2
4
  require 'yaml'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
 
3
5
  module VagrantPlugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
 
3
5
  module VagrantPlugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
  # require 'log4r/yamlconfigurator'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
 
3
5
  module VagrantPlugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
  require 'nokogiri'
3
5
 
@@ -86,9 +88,13 @@ module VagrantPlugins
86
88
 
87
89
  def search_network(nets, xml)
88
90
  str = '/domain/devices/interface'
89
- str += "[(@type='network' or @type='udp' or @type='bridge')"
91
+ str += "[(@type='network' or @type='udp' or @type='bridge' or @type='direct')"
90
92
  unless nets.empty?
91
- str += " and source[@network='#{nets.first['network']}']"
93
+ net = nets.first
94
+ network = net['network']
95
+ dev = net['dev']
96
+ str += " and source[@network='#{network}']" if network
97
+ str += " and source[@dev='#{dev}']" if dev
92
98
  end
93
99
  str += ']'
94
100
  @logger.debug(str)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'securerandom'
2
4
  module VagrantPlugins
3
5
  module ProviderLibvirt
@@ -46,7 +48,7 @@ module VagrantPlugins
46
48
  env[:root_path].basename.to_s.dup.concat('_')
47
49
  elsif config.default_prefix.empty?
48
50
  # don't have any prefix, not even "_"
49
- ''
51
+ String.new
50
52
  else
51
53
  config.default_prefix.to_s.dup
52
54
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
 
3
5
  require 'log4r'
@@ -0,0 +1,49 @@
1
+ require 'log4r'
2
+
3
+ module VagrantPlugins
4
+ module ProviderLibvirt
5
+ module Action
6
+ # Shutdown the domain.
7
+ class ShutdownDomain
8
+ def initialize(app, _env, target_state, source_state)
9
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::shutdown_domain')
10
+ @target_state = target_state
11
+ @source_state = source_state
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ timeout = env[:machine].config.vm.graceful_halt_timeout
17
+
18
+ start_time = Time.now
19
+
20
+ # call nested action first under the assumption it should try to
21
+ # handle shutdown via client capabilities
22
+ @app.call(env)
23
+
24
+ # return if successful, otherwise will ensure result is set to false
25
+ env[:result] = env[:machine].state.id == @target_state
26
+
27
+ return if env[:result]
28
+
29
+ current_time = Time.now
30
+
31
+ # if we've already exceeded the timeout
32
+ return if current_time - start_time >= timeout
33
+
34
+ # otherwise construct a new timeout.
35
+ timeout = timeout - (current_time - start_time)
36
+
37
+ domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
38
+ if env[:machine].state.id == @source_state
39
+ env[:ui].info(I18n.t('vagrant_libvirt.shutdown_domain'))
40
+ domain.shutdown
41
+ domain.wait_for(timeout) { !ready? }
42
+ end
43
+
44
+ env[:result] = env[:machine].state.id == @target_state
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
  require 'rexml/document'
3
5
 
@@ -56,7 +58,7 @@ module VagrantPlugins
56
58
  # disk_bus
57
59
  REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="vda"]') do |disk_target|
58
60
  next unless disk_target.attributes['bus'] != config.disk_bus
59
- @logger.debug "domain disk bus updated from '#{disk_target.attributes['bus']}' to '#{bus}'"
61
+ @logger.debug "domain disk bus updated from '#{disk_target.attributes['bus']}' to '#{config.disk_bus}'"
60
62
  descr_changed = true
61
63
  disk_target.attributes['bus'] = config.disk_bus
62
64
  disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
@@ -99,11 +101,13 @@ module VagrantPlugins
99
101
  if config.cpu_mode != 'host-passthrough'
100
102
  cpu_model = REXML::XPath.first(xml_descr, '/domain/cpu/model')
101
103
  if cpu_model.nil?
102
- @logger.debug "cpu_model updated from not set to '#{config.cpu_model}'"
103
- descr_changed = true
104
- cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
105
- cpu_model.attributes['fallback'] = 'allow'
106
- cpu_model.text = config.cpu_model
104
+ if config.cpu_model.strip != ''
105
+ @logger.debug "cpu_model updated from not set to '#{config.cpu_model}'"
106
+ descr_changed = true
107
+ cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
108
+ cpu_model.attributes['fallback'] = 'allow'
109
+ cpu_model.text = config.cpu_model
110
+ end
107
111
  else
108
112
  if (cpu_model.text or '').strip != config.cpu_model.strip
109
113
  @logger.debug "cpu_model text updated from #{cpu_model.text} to '#{config.cpu_model}'"
@@ -165,7 +169,7 @@ module VagrantPlugins
165
169
 
166
170
  # clock timers - because timers can be added/removed, just rebuild and then compare
167
171
  if !config.clock_timers.empty? || clock.has_elements?
168
- oldclock = ''
172
+ oldclock = String.new
169
173
  formatter.write(REXML::XPath.first(xml_descr, '/domain/clock'), oldclock)
170
174
  clock.delete_element('//timer')
171
175
  config.clock_timers.each do |clock_timer|
@@ -175,7 +179,7 @@ module VagrantPlugins
175
179
  end
176
180
  end
177
181
 
178
- newclock = ''
182
+ newclock = String.new
179
183
  formatter.write(clock, newclock)
180
184
  unless newclock.eql? oldclock
181
185
  @logger.debug "clock timers config changed"
@@ -320,10 +324,12 @@ module VagrantPlugins
320
324
  if config.initrd
321
325
  initrd = REXML::XPath.first(xml_descr, '/domain/os/initrd')
322
326
  if initrd.nil?
323
- @logger.debug "initrd updated from not set to '#{config.initrd}'"
324
- descr_changed = true
325
- initrd = REXML::Element.new('initrd', REXML::XPath.first(xml_descr, '/domain/os'))
326
- initrd.text = config.initrd
327
+ if config.initrd.strip != ''
328
+ @logger.debug "initrd updated from not set to '#{config.initrd}'"
329
+ descr_changed = true
330
+ initrd = REXML::Element.new('initrd', REXML::XPath.first(xml_descr, '/domain/os'))
331
+ initrd.text = config.initrd
332
+ end
327
333
  else
328
334
  if (initrd.text or '').strip != config.initrd
329
335
  @logger.debug "initrd updated from '#{initrd.text}' to '#{config.initrd}'"
@@ -337,22 +343,25 @@ module VagrantPlugins
337
343
  if descr_changed
338
344
  begin
339
345
  libvirt_domain.undefine
340
- new_descr = ''
346
+ new_descr = String.new
341
347
  xml_descr.write new_descr
342
- server = env[:machine].provider.driver.connection.servers.create(xml: new_descr)
348
+ env[:machine].provider.driver.connection.servers.create(xml: new_descr)
343
349
  rescue Fog::Errors::Error => e
344
- server = env[:machine].provider.driver.connection.servers.create(xml: descr)
350
+ env[:machine].provider.driver.connection.servers.create(xml: descr)
345
351
  raise Errors::FogCreateServerError, error_message: e.message
346
352
  end
347
353
  end
348
- rescue => e
354
+ rescue Errors::VagrantLibvirtError => e
349
355
  env[:ui].error("Error when updating domain settings: #{e.message}")
350
356
  end
351
357
  # Autostart with host if enabled in Vagrantfile
352
358
  libvirt_domain.autostart = config.autostart
359
+ @logger.debug {
360
+ "Starting Domain with XML:\n#{libvirt_domain.xml_desc}"
361
+ }
353
362
  # Actually start the domain
354
363
  domain.start
355
- rescue => e
364
+ rescue Fog::Errors::Error, Errors::VagrantLibvirtError => e
356
365
  raise Errors::FogError, message: e.message
357
366
  end
358
367
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
 
3
5
  module VagrantPlugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'log4r'
2
4
  require 'vagrant-libvirt/errors'
3
5
  require 'vagrant-libvirt/util/timer'
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'vagrant/action/builder'
2
4
  require 'log4r'
3
5
 
4
6
  module VagrantPlugins
5
7
  module ProviderLibvirt
6
8
  module Action
7
- # Include the built-in modules so we can use them as top-level things.
9
+ # Include the built-in & general modules so we can use them as top-level things.
8
10
  include Vagrant::Action::Builtin
11
+ include Vagrant::Action::General
9
12
  @logger = Log4r::Logger.new('vagrant_libvirt::action')
10
13
 
11
14
  # remove image from Libvirt storage pool
@@ -136,11 +139,23 @@ module VagrantPlugins
136
139
  b3.use ResumeDomain if env2[:result]
137
140
  end
138
141
 
142
+ # only perform shutdown if VM is running
139
143
  b2.use Call, IsRunning do |env2, b3|
140
144
  next unless env2[:result]
141
145
 
142
- # VM is running, halt it.
143
- b3.use HaltDomain
146
+ b3.use Call, Message, "Attempting nice shutdowns..." do |_, b4|
147
+ # ShutdownDomain will perform the domain shutdown on the out calls
148
+ # so it runs after the remaining actions in the same action builder.
149
+ b4.use ShutdownDomain, :shutoff, :running
150
+ b4.use GracefulHalt, :shutoff, :running
151
+ end
152
+
153
+ # Only force halt if previous actions insufficient.
154
+ b3.use Call, IsRunning do |env3, b4|
155
+ next unless env3[:result]
156
+
157
+ b4.use HaltDomain
158
+ end
144
159
  end
145
160
  end
146
161
  end
@@ -167,7 +182,18 @@ module VagrantPlugins
167
182
  def self.action_package
168
183
  Vagrant::Action::Builder.new.tap do |b|
169
184
  b.use ConfigValidate
170
- b.use PackageDomain
185
+ b.use Call, IsCreated do |env, b2|
186
+ unless env[:result]
187
+ b2.use MessageNotCreated
188
+ next
189
+ end
190
+
191
+ b2.use PackageSetupFolders
192
+ b2.use PackageSetupFiles
193
+ b2.use action_halt
194
+ b2.use Package
195
+ b2.use PackageDomain
196
+ end
171
197
  end
172
198
  end
173
199
 
@@ -334,6 +360,7 @@ module VagrantPlugins
334
360
  autoload :ForwardPorts, action_root.join('forward_ports')
335
361
  autoload :ClearForwardedPorts, action_root.join('forward_ports')
336
362
  autoload :HaltDomain, action_root.join('halt_domain')
363
+ autoload :ShutdownDomain, action_root.join('shutdown_domain')
337
364
  autoload :HandleBoxImage, action_root.join('handle_box_image')
338
365
  autoload :HandleStoragePool, action_root.join('handle_storage_pool')
339
366
  autoload :RemoveLibvirtImage, action_root.join('remove_libvirt_image')
@@ -366,6 +393,9 @@ module VagrantPlugins
366
393
  autoload :WaitTillUp, action_root.join('wait_till_up')
367
394
  autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids')
368
395
 
396
+ autoload :Package, 'vagrant/action/general/package'
397
+ autoload :PackageSetupFiles, 'vagrant/action/general/package_setup_files'
398
+ autoload :PackageSetupFolders, 'vagrant/action/general/package_setup_folders'
369
399
  autoload :SSHRun, 'vagrant/action/builtin/ssh_run'
370
400
  autoload :HandleBox, 'vagrant/action/builtin/handle_box'
371
401
  autoload :SyncedFolders, 'vagrant/action/builtin/synced_folders'