vagrant-kvm 0.1.4 → 0.1.5

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +14 -0
  5. data/CHANGELOG.md +29 -0
  6. data/DEVELOPMENT.md +87 -0
  7. data/Gemfile +1 -0
  8. data/INSTALL.md +229 -0
  9. data/LICENSE +2 -1
  10. data/README.md +154 -63
  11. data/Rakefile +24 -1
  12. data/example_box/README.md +8 -8
  13. data/lib/vagrant-kvm/action.rb +47 -5
  14. data/lib/vagrant-kvm/action/boot.rb +0 -4
  15. data/lib/vagrant-kvm/action/clear_forwarded_ports.rb +53 -0
  16. data/lib/vagrant-kvm/action/forward_ports.rb +104 -0
  17. data/lib/vagrant-kvm/action/import.rb +97 -18
  18. data/lib/vagrant-kvm/action/init_storage_pool.rb +3 -1
  19. data/lib/vagrant-kvm/action/message_not_running.rb +16 -0
  20. data/lib/vagrant-kvm/action/network.rb +12 -13
  21. data/lib/vagrant-kvm/action/package_vagrantfile.rb +3 -1
  22. data/lib/vagrant-kvm/action/prepare_gui.rb +20 -0
  23. data/lib/vagrant-kvm/action/prepare_nfs_settings.rb +8 -16
  24. data/lib/vagrant-kvm/action/prepare_nfs_valid_ids.rb +17 -0
  25. data/lib/vagrant-kvm/action/reset_image_permission.rb +23 -0
  26. data/lib/vagrant-kvm/action/resume_network.rb +28 -0
  27. data/lib/vagrant-kvm/action/share_folders.rb +6 -5
  28. data/lib/vagrant-kvm/action/suspend.rb +8 -1
  29. data/lib/vagrant-kvm/config.rb +103 -2
  30. data/lib/vagrant-kvm/driver/driver.rb +321 -99
  31. data/lib/vagrant-kvm/errors.rb +18 -0
  32. data/lib/vagrant-kvm/provider.rb +4 -1
  33. data/lib/vagrant-kvm/util.rb +3 -0
  34. data/lib/vagrant-kvm/util/commands.rb +23 -0
  35. data/lib/vagrant-kvm/util/definition_attributes.rb +33 -0
  36. data/lib/vagrant-kvm/util/disk_info.rb +48 -0
  37. data/lib/vagrant-kvm/util/network_definition.rb +44 -84
  38. data/lib/vagrant-kvm/util/vm_definition.rb +91 -103
  39. data/lib/vagrant-kvm/version.rb +1 -1
  40. data/locales/en.yml +8 -0
  41. data/locales/ja.yml +14 -0
  42. data/spec/acceptance/vagrant-kvm_spec.rb +80 -0
  43. data/spec/fedora/10.virt.rules +10 -0
  44. data/spec/fedora/50-vagrant-libvirt-access.pkla +6 -0
  45. data/spec/spec_helper.rb +30 -0
  46. data/spec/support/libvirt_helper.rb +37 -0
  47. data/spec/support/vagrant_kvm_helper.rb +39 -0
  48. data/spec/test_files/box.xml +74 -0
  49. data/spec/vagrant-kvm/config_spec.rb +56 -0
  50. data/spec/vagrant-kvm/driver/driver_spec.rb +36 -0
  51. data/spec/vagrant-kvm/errors_spec.rb +25 -0
  52. data/spec/vagrant-kvm/util/network_definition_spec.rb +60 -0
  53. data/spec/vagrant-kvm/util/vm_definition_spec.rb +76 -0
  54. data/templates/libvirt_domain.erb +34 -12
  55. data/templates/libvirt_network.erb +13 -0
  56. data/templates/package_Vagrantfile.erb +11 -0
  57. data/vagrant-kvm.gemspec +1 -2
  58. metadata +41 -42
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
- require 'rspec/core/rake_task'
4
3
 
5
4
  # Immediately sync all stdout so that tools like buildbot can
6
5
  # immediately load in the output.
@@ -13,3 +12,27 @@ Dir.chdir(File.expand_path("../", __FILE__))
13
12
  # This installs the tasks that help with gem creation and
14
13
  # publishing.
15
14
  Bundler::GemHelper.install_tasks
15
+
16
+ require 'rspec/core/rake_task'
17
+ RSpec::Core::RakeTask.new
18
+
19
+ namespace :box do
20
+ desc 'Downloads and adds vagrant box for testing.'
21
+ task :add do
22
+ system 'bundle exec vagrant box add vagrant-kvm-specs http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box'
23
+ end
24
+
25
+ desc 'Prepares VirtualBox box for usage with KVM.'
26
+ task :prepare do
27
+ system 'bundle exec vagrant plugin install vagrant-mutate'
28
+ system 'bundle exec vagrant mutate vagrant-kvm-specs kvm'
29
+ system 'bundle exec vagrant box remove vagrant-kvm-specs virtualbox'
30
+ end
31
+
32
+ desc 'Removes testing vagrant box.'
33
+ task :remove do
34
+ system 'bundle exec vagrant box remove vagrant-kvm-specs kvm'
35
+ end
36
+ end
37
+
38
+ task :default => :spec
@@ -3,16 +3,16 @@
3
3
  Vagrant providers each require a custom provider-specific box format.
4
4
  This folder shows the example contents of a box for the `kvm` provider.
5
5
 
6
- There are two box formats for the `kvm` provider:
7
- 1. VirtualBox box - you need to change the provider to `kvm` in the
8
- `metadata.json` file, the box will be converted on the fly.
9
- 2. "Native" box - you need a box.xml file (libvirt domain format) and a raw
10
- image file (you can convert a .vmdk with qemu-img)
6
+ You need a box.xml file (libvirt domain format), you can use the example
7
+ supplied as a base or use one from a VM you created with other tools. Don't
8
+ forget to change the volume path to just the name of the volume. You should
9
+ use a qcow2 volume in most cases, raw is also supported if you have a good
10
+ reason to use this format.
11
11
 
12
- To turn this into a native box, you need to create a vagrant image and do:
12
+ To turn this into a native box, you need to create an tar archive:
13
13
 
14
- ```
14
+ ```bash
15
15
  $ tar cvzf kvm.box ./metadata.json ./Vagrantfile ./box.xml ./box-disk1.img
16
16
  ```
17
17
 
18
- You need a base MAC address and a private network like in the example.
18
+ You need a base MAC address like in the example.
@@ -15,12 +15,25 @@ module VagrantPlugins
15
15
  Vagrant::Action::Builder.new.tap do |b|
16
16
  b.use Network
17
17
  b.use Provision
18
- b.use PruneNFSExports
19
- b.use NFS
20
- b.use PrepareNFSSettings
18
+ b.use Vagrant::Action::Builtin::HandleForwardedPortCollisions
19
+ if Vagrant::VERSION < "1.4.0"
20
+ b.use PruneNFSExports
21
+ b.use NFS
22
+ b.use PrepareNFSSettings
23
+ else
24
+ #FIXME
25
+ b.use PrepareNFSValidIds
26
+ b.use SyncedFolderCleanup
27
+ b.use SyncedFolders
28
+ b.use PrepareNFSSettings
29
+ end
21
30
  b.use SetHostname
22
31
  #b.use Customize
32
+ b.use ForwardPorts
23
33
  b.use Boot
34
+ if Vagrant::VERSION >= "1.3.0"
35
+ b.use WaitForCommunicator, [:running]
36
+ end
24
37
  b.use ShareFolders
25
38
  end
26
39
  end
@@ -41,7 +54,13 @@ module VagrantPlugins
41
54
  b3.use ConfigValidate
42
55
  b3.use EnvSet, :force_halt => true
43
56
  b3.use action_halt
44
- b3.use PruneNFSExports
57
+ if Vagrant::VERSION < "1.4.0"
58
+ b3.use PruneNFSExports
59
+ else
60
+ b3.use PrepareNFSSettings
61
+ b3.use PrepareNFSValidIds
62
+ b3.use SyncedFolderCleanup
63
+ end
45
64
  b3.use Destroy
46
65
  else
47
66
  b3.use MessageWillNotDestroy
@@ -63,10 +82,12 @@ module VagrantPlugins
63
82
  b3.use Resume
64
83
  end
65
84
 
85
+ b2.use ClearForwardedPorts
66
86
  b2.use Call, GracefulHalt, :shutoff, :running do |env2, b3|
67
87
  if !env2[:result]
68
88
  b3.use ForcedHalt
69
89
  end
90
+ b3.use ResetImagePermission
70
91
  end
71
92
  else
72
93
  b2.use MessageNotCreated
@@ -87,7 +108,14 @@ module VagrantPlugins
87
108
 
88
109
  b2.use SetupPackageFiles
89
110
  b2.use action_halt
90
- #b2.use Export
111
+ if Vagrant::VERSION < "1.4.0"
112
+ b2.use PruneNFSExports
113
+ else
114
+ b2.use PrepareNFSSettings
115
+ b2.use PrepareNFSValidIds
116
+ b2.use SyncedFolderCleanup
117
+ end
118
+ b2.use Export
91
119
  b2.use PackageVagrantfile
92
120
  b2.use Package
93
121
  end
@@ -143,6 +171,7 @@ module VagrantPlugins
143
171
  b.use CheckKvm
144
172
  b.use Call, Created do |env, b2|
145
173
  if env[:result]
174
+ b2.use ResumeNetwork
146
175
  b2.use Resume
147
176
  else
148
177
  b2.use MessageNotCreated
@@ -190,12 +219,14 @@ module VagrantPlugins
190
219
 
191
220
  b3.use Call, IsPaused do |env3, b4|
192
221
  if env3[:result]
222
+ b4.use ResumeNetwork
193
223
  b4.use Resume
194
224
  next
195
225
  end
196
226
 
197
227
  # The VM is not saved, so we must have to boot it up
198
228
  # like normal. Boot!
229
+ b4.use PrepareGui
199
230
  b4.use action_boot
200
231
  end
201
232
  end
@@ -230,6 +261,7 @@ module VagrantPlugins
230
261
  # If the VM is NOT created yet, then do the setup steps
231
262
  if !env[:result]
232
263
  b2.use CheckBox
264
+ b2.use SetName
233
265
  b2.use Import
234
266
  b2.use MatchMACAddress
235
267
  end
@@ -245,10 +277,13 @@ module VagrantPlugins
245
277
  autoload :CheckCreated, action_root.join("check_created")
246
278
  autoload :CheckKvm, action_root.join("check_kvm")
247
279
  autoload :CheckRunning, action_root.join("check_running")
280
+ autoload :ClearForwardedPorts, action_root.join("clear_forwarded_ports")
248
281
  autoload :Created, action_root.join("created")
249
282
  autoload :Destroy, action_root.join("destroy")
250
283
  autoload :DestroyConfirm, action_root.join("destroy_confirm")
284
+ autoload :Export, action_root.join("export")
251
285
  autoload :ForcedHalt, action_root.join("forced_halt")
286
+ autoload :ForwardPorts, action_root.join("forward_ports")
252
287
  autoload :Import, action_root.join("import")
253
288
  autoload :InitStoragePool, action_root.join("init_storage_pool")
254
289
  autoload :IsPaused, action_root.join("is_paused")
@@ -256,11 +291,18 @@ module VagrantPlugins
256
291
  autoload :IsSaved, action_root.join("is_saved")
257
292
  autoload :MatchMACAddress, action_root.join("match_mac_address")
258
293
  autoload :MessageNotCreated, action_root.join("message_not_created")
294
+ autoload :MessageNotRunning, action_root.join("message_not_running")
259
295
  autoload :MessageWillNotDestroy, action_root.join("message_will_not_destroy")
260
296
  autoload :Network, action_root.join("network")
297
+ autoload :PackageVagrantfile, action_root.join("package_vagrantfile")
298
+ autoload :Package, action_root.join("package")
299
+ autoload :PrepareGui, action_root.join("prepare_gui")
261
300
  autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings")
301
+ autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids")
262
302
  autoload :PruneNFSExports, action_root.join("prune_nfs_exports")
303
+ autoload :ResetImagePermission, action_root.join("reset_image_permission")
263
304
  autoload :Resume, action_root.join("resume")
305
+ autoload :ResumeNetwork, action_root.join("resume_network")
264
306
  autoload :SetName, action_root.join("set_name")
265
307
  autoload :SetupPackageFiles, action_root.join("setup_package_files")
266
308
  autoload :ShareFolders, action_root.join("share_folders")
@@ -9,10 +9,6 @@ module VagrantPlugins
9
9
  def call(env)
10
10
  @env = env
11
11
 
12
- if @env[:machine].provider_config.gui
13
- env[:machine].provider.driver.set_gui
14
- end
15
-
16
12
  # Start up the VM
17
13
  env[:ui].info I18n.t("vagrant.actions.vm.boot.booting")
18
14
  env[:machine].provider.driver.start
@@ -0,0 +1,53 @@
1
+ #
2
+ # borrowed from vagrant-lxc
3
+
4
+ module VagrantPlugins
5
+ module ProviderKvm
6
+ module Action
7
+ class ClearForwardedPorts
8
+ include Util::Commands
9
+
10
+ def initialize(app, env)
11
+ @app = app
12
+ @logger = Log4r::Logger.new("vagrant::kvm::action::clear_forwarded_ports")
13
+ end
14
+
15
+ def call(env)
16
+ @env = env
17
+
18
+ if redir_pids.any?
19
+ env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting")
20
+ redir_pids.each do |pid|
21
+ next unless is_redir_pid?(pid)
22
+ @logger.debug "Killing pid #{pid}"
23
+ run_command "pkill -TERM -P #{pid}"
24
+ end
25
+
26
+ remove_redir_pids
27
+ end
28
+
29
+ @app.call env
30
+ end
31
+
32
+ protected
33
+
34
+ def redir_pids
35
+ @redir_pids = Dir[@env[:machine].data_dir.join('pids').to_s + "/redir_*.pid"].map do |file|
36
+ File.read(file).strip.chomp
37
+ end
38
+ end
39
+
40
+ def is_redir_pid?(pid)
41
+ `ps -o cmd= #{pid}`.strip.chomp =~ /redir/
42
+ end
43
+
44
+ def remove_redir_pids
45
+ Dir[@env[:machine].data_dir.join('pids').to_s + "/redir_*.pid"].each do |file|
46
+ File.delete file
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,104 @@
1
+ #
2
+ # borrowed from vagrant-lxc
3
+
4
+ module VagrantPlugins
5
+ module ProviderKvm
6
+ module Action
7
+ class ForwardPorts
8
+ include Util::Commands
9
+
10
+ def initialize(app, env)
11
+ @app = app
12
+ @logger = Log4r::Logger.new("vagrant::kvm::action::forward_ports")
13
+ end
14
+
15
+ def call(env)
16
+ @env = env
17
+
18
+ # Get the ports we're forwarding
19
+ env[:forwarded_ports] = compile_forwarded_ports(env[:machine].config)
20
+
21
+ if @env[:forwarded_ports].any? and not redir_installed?
22
+ raise Errors::RedirNotInstalled
23
+ end
24
+
25
+ # Warn if we're port forwarding to any privileged ports
26
+ env[:forwarded_ports].each do |fp|
27
+ if fp[:host] <= 1024
28
+ env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports")
29
+ break
30
+ end
31
+ end
32
+
33
+ # Continue, we need the VM to be booted in order to grab its IP
34
+ @app.call env
35
+
36
+ if @env[:forwarded_ports].any?
37
+ env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
38
+ forward_ports
39
+ end
40
+ end
41
+
42
+ def forward_ports
43
+ @env[:forwarded_ports].each do |fp|
44
+ message_attributes = {
45
+ # TODO: Add support for multiple adapters
46
+ :adapter => 'eth0',
47
+ :guest_port => fp[:guest],
48
+ :host_port => fp[:host]
49
+ }
50
+
51
+ # TODO: Remove adapter from logging
52
+ @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
53
+ message_attributes))
54
+
55
+ redir_pid = redirect_port(
56
+ fp[:host_ip],
57
+ fp[:host],
58
+ fp[:guest_ip] || @env[:machine].provider.ssh_info[:host],
59
+ fp[:guest]
60
+ )
61
+ store_redir_pid(fp[:host], redir_pid)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def compile_forwarded_ports(config)
68
+ mappings = {}
69
+
70
+ config.vm.networks.each do |type, options|
71
+ if type == :forwarded_port && options[:id] != 'ssh'
72
+ mappings[options[:host]] = options
73
+ end
74
+ end
75
+
76
+ mappings.values
77
+ end
78
+
79
+ def redirect_port(host_ip, host_port, guest_ip, guest_port)
80
+ params = %W( --lport=#{host_port} --caddr=#{guest_ip} --cport=#{guest_port} )
81
+ params.unshift "--laddr=#{host_ip}" if host_ip
82
+ params << '--syslog' if ENV['REDIR_LOG']
83
+ redir_cmd = "redir #{params.join(' ')} 2>/dev/null"
84
+
85
+ @logger.debug "Forwarding port with `#{redir_cmd}`"
86
+ spawn redir_cmd
87
+ end
88
+
89
+ def store_redir_pid(host_port, redir_pid)
90
+ data_dir = @env[:machine].data_dir.join('pids')
91
+ data_dir.mkdir unless data_dir.directory?
92
+
93
+ data_dir.join("redir_#{host_port}.pid").open('w') do |pid_file|
94
+ pid_file.write(redir_pid)
95
+ end
96
+ end
97
+
98
+ def redir_installed?
99
+ run_command "which redir > /dev/null"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -1,44 +1,123 @@
1
+ require 'etc'
2
+
1
3
  module VagrantPlugins
2
4
  module ProviderKvm
3
5
  module Action
4
6
  class Import
7
+ include Util
8
+ include Util::Commands
9
+
5
10
  def initialize(app, env)
6
11
  @app = app
12
+ @logger = Log4r::Logger.new("vagrant::kvm::action::import")
7
13
  end
8
14
 
9
15
  def call(env)
10
- env[:ui].info I18n.t("vagrant.actions.vm.import.importing",
16
+ @env = env
17
+ @env[:ui].info I18n.t("vagrant.actions.vm.import.importing",
11
18
  :name => env[:machine].box.name)
12
19
 
20
+ provider_config = @env[:machine].provider_config
21
+
13
22
  # Ignore unsupported image types
14
- image_type = env[:machine].provider_config.image_type
15
- image_type = 'raw' unless image_type == 'qcow2'
16
-
17
- # Import the virtual machine (ovf or libvirt)
18
- # if a libvirt XML definition is present we use it
19
- # otherwise we convert the OVF
20
- storage_path = File.join(env[:tmp_path],"/storage-pool")
21
- box_file = env[:machine].box.directory.join("box.xml").to_s
22
- if File.file?(box_file)
23
- env[:machine].id = env[:machine].provider.driver.import(
24
- box_file, storage_path, image_type)
25
- else
26
- box_file = env[:machine].box.directory.join("box.ovf").to_s
27
- env[:machine].id = env[:machine].provider.driver.import_ovf(
28
- box_file, storage_path, image_type)
23
+ args={:image_type => provider_config.image_type}
24
+ args[:image_type] = 'qcow2' unless args[:image_type] == 'raw'
25
+ # Add memory attribute when specified
26
+ if provider_config.memory_size
27
+ args[:memory] = provider_config.memory_size
29
28
  end
30
29
 
30
+ # import arguments
31
+ args = {
32
+ :image_backing => provider_config.image_backing,
33
+ :qemu_bin => provider_config.qemu_bin,
34
+ :cpus => provider_config.core_number,
35
+ :cpu_model => provider_config.cpu_model,
36
+ :machine_type => provider_config.machine_type,
37
+ :network_model => provider_config.network_model,
38
+ :video_model => provider_config.video_model
39
+ }.merge(args)
40
+
41
+ # Import the virtual machine
42
+ storage_path = File.join(@env[:tmp_path],"/storage-pool")
43
+ box_file = @env[:machine].box.directory.join("box.xml").to_s
44
+ raise Errors::KvmBadBoxFormat unless File.file?(box_file)
45
+
46
+ # import box volume
47
+ volume_name = import_volume(storage_path, box_file, args)
48
+
49
+ # import the box to a new vm
50
+ env[:machine].id = @env[:machine].provider.driver.import(box_file, volume_name, args)
51
+
31
52
  # If we got interrupted, then the import could have been
32
53
  # interrupted and its not a big deal. Just return out.
33
- return if env[:interrupted]
54
+ return if @env[:interrupted]
34
55
 
35
56
  # Flag as erroneous and return if import failed
36
- raise Vagrant::Errors::VMImportFailure if !env[:machine].id
57
+ raise Vagrant::Errors::VMImportFailure if !@env[:machine].id
37
58
 
38
59
  # Import completed successfully. Continue the chain
39
60
  @app.call(env)
40
61
  end
41
62
 
63
+ def import_volume(storage_path, box_file, args)
64
+ @logger.debug "Importing volume. Storage path: #{storage_path} " +
65
+ "Image Type: #{args[:image_type]}"
66
+
67
+ box_disk = @env[:machine].provider.driver.find_box_disk(box_file)
68
+ new_disk = File.basename(box_disk, File.extname(box_disk)) + "-" +
69
+ Time.now.to_i.to_s + ".img"
70
+ old_path = File.join(File.dirname(box_file), box_disk)
71
+ new_path = File.join(storage_path, new_disk)
72
+
73
+ # for backward compatibility, we handle both raw and qcow2 box format
74
+ box = Util::DiskInfo.new(old_path)
75
+ if box.type == 'raw' || args[:image_type] == 'raw'
76
+ args[:image_baking] = false
77
+ @logger.info "Disable disk image with box image as backing file"
78
+ end
79
+
80
+ if args[:image_type] == 'qcow2' || args[:image_type] == 'raw'
81
+ # create volume
82
+ box_name = @env[:machine].config.vm.box
83
+ driver = @env[:machine].provider.driver
84
+ userid = Process.uid.to_s
85
+ groupid = Process.gid.to_s
86
+ modes = {:dir => '0775', :file => '0664'}
87
+ label = 'virt_image_t'
88
+ if driver.host_redhat?
89
+ # on Redhat/Fedora, permission is controlled
90
+ # with only SELinux
91
+ modes = {:dir => '0777',:file => '0666'}
92
+ elsif driver.host_debian?
93
+ groupid = Etc.getgrnam('kvm').gid.to_s
94
+ else
95
+ # XXX: default
96
+ end
97
+ pool_name = 'vagrant_' + userid + '_' + box_name
98
+ driver.init_storage_directory(
99
+ :pool_path => File.dirname(old_path), :pool_name => pool_name,
100
+ :owner => userid, :group => groupid, :mode => modes[:dir])
101
+ driver.create_volume(
102
+ :disk_name => new_disk,
103
+ :capacity => box.capacity,
104
+ :path => new_path,
105
+ :image_type => args[:image_type],
106
+ :box_pool => pool_name,
107
+ :box_path => old_path,
108
+ :backing => args[:image_backing],
109
+ :owner => userid,
110
+ :group => groupid,
111
+ :mode => modes[:file],
112
+ :label => label)
113
+ driver.free_storage_pool(pool_name)
114
+ else
115
+ @logger.info "Image type #{args[:image_type]} is not supported"
116
+ end
117
+ # TODO cleanup if interrupted
118
+ new_disk
119
+ end
120
+
42
121
  def recover(env)
43
122
  if env[:machine].provider.state.id != :not_created
44
123
  return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)