vagrant-kvm 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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)