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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +29 -0
- data/DEVELOPMENT.md +87 -0
- data/Gemfile +1 -0
- data/INSTALL.md +229 -0
- data/LICENSE +2 -1
- data/README.md +154 -63
- data/Rakefile +24 -1
- data/example_box/README.md +8 -8
- data/lib/vagrant-kvm/action.rb +47 -5
- data/lib/vagrant-kvm/action/boot.rb +0 -4
- data/lib/vagrant-kvm/action/clear_forwarded_ports.rb +53 -0
- data/lib/vagrant-kvm/action/forward_ports.rb +104 -0
- data/lib/vagrant-kvm/action/import.rb +97 -18
- data/lib/vagrant-kvm/action/init_storage_pool.rb +3 -1
- data/lib/vagrant-kvm/action/message_not_running.rb +16 -0
- data/lib/vagrant-kvm/action/network.rb +12 -13
- data/lib/vagrant-kvm/action/package_vagrantfile.rb +3 -1
- data/lib/vagrant-kvm/action/prepare_gui.rb +20 -0
- data/lib/vagrant-kvm/action/prepare_nfs_settings.rb +8 -16
- data/lib/vagrant-kvm/action/prepare_nfs_valid_ids.rb +17 -0
- data/lib/vagrant-kvm/action/reset_image_permission.rb +23 -0
- data/lib/vagrant-kvm/action/resume_network.rb +28 -0
- data/lib/vagrant-kvm/action/share_folders.rb +6 -5
- data/lib/vagrant-kvm/action/suspend.rb +8 -1
- data/lib/vagrant-kvm/config.rb +103 -2
- data/lib/vagrant-kvm/driver/driver.rb +321 -99
- data/lib/vagrant-kvm/errors.rb +18 -0
- data/lib/vagrant-kvm/provider.rb +4 -1
- data/lib/vagrant-kvm/util.rb +3 -0
- data/lib/vagrant-kvm/util/commands.rb +23 -0
- data/lib/vagrant-kvm/util/definition_attributes.rb +33 -0
- data/lib/vagrant-kvm/util/disk_info.rb +48 -0
- data/lib/vagrant-kvm/util/network_definition.rb +44 -84
- data/lib/vagrant-kvm/util/vm_definition.rb +91 -103
- data/lib/vagrant-kvm/version.rb +1 -1
- data/locales/en.yml +8 -0
- data/locales/ja.yml +14 -0
- data/spec/acceptance/vagrant-kvm_spec.rb +80 -0
- data/spec/fedora/10.virt.rules +10 -0
- data/spec/fedora/50-vagrant-libvirt-access.pkla +6 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/libvirt_helper.rb +37 -0
- data/spec/support/vagrant_kvm_helper.rb +39 -0
- data/spec/test_files/box.xml +74 -0
- data/spec/vagrant-kvm/config_spec.rb +56 -0
- data/spec/vagrant-kvm/driver/driver_spec.rb +36 -0
- data/spec/vagrant-kvm/errors_spec.rb +25 -0
- data/spec/vagrant-kvm/util/network_definition_spec.rb +60 -0
- data/spec/vagrant-kvm/util/vm_definition_spec.rb +76 -0
- data/templates/libvirt_domain.erb +34 -12
- data/templates/libvirt_network.erb +13 -0
- data/templates/package_Vagrantfile.erb +11 -0
- data/vagrant-kvm.gemspec +1 -2
- metadata +41 -42
@@ -9,7 +9,9 @@ module VagrantPlugins
|
|
9
9
|
|
10
10
|
def call(env)
|
11
11
|
# Create a storage pool in tmp_path if it doesn't exist
|
12
|
-
|
12
|
+
userid = Process.uid.to_s
|
13
|
+
groupid = Process.gid.to_s
|
14
|
+
Driver::Driver.new.init_storage(env[:tmp_path], userid, groupid)
|
13
15
|
|
14
16
|
@app.call(env)
|
15
17
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module ProviderKvm
|
3
|
+
module Action
|
4
|
+
class MessageNotRunning
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
env[:ui].info I18n.t("vagrant.commands.common.vm_not_running")
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -15,28 +15,27 @@ module VagrantPlugins
|
|
15
15
|
# TODO: Validate network configuration prior to anything below
|
16
16
|
@env = env
|
17
17
|
|
18
|
-
options
|
18
|
+
options= {}
|
19
19
|
env[:machine].config.vm.networks.each do |type, network_options|
|
20
20
|
options = network_options if type == :private_network
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
options[:ip] = "192.168.123.10" unless options.has_key?(:ip)
|
24
|
+
addr = options[:ip].split(".")
|
25
|
+
addr[3] = "1"
|
26
|
+
base_ip = addr.join(".")
|
27
|
+
addr[3] = "100"
|
28
|
+
start_ip = addr.join(".")
|
29
|
+
addr[3] = "200"
|
30
|
+
end_ip = addr.join(".")
|
31
|
+
range = {
|
32
32
|
:start => start_ip,
|
33
33
|
:end => end_ip }
|
34
|
-
|
34
|
+
options = {
|
35
35
|
:base_ip => base_ip,
|
36
36
|
:netmask => "255.255.255.0",
|
37
37
|
:range => range
|
38
|
-
|
39
|
-
end
|
38
|
+
}.merge(options)
|
40
39
|
|
41
40
|
hosts = []
|
42
41
|
name = env[:machine].provider_config.name ?
|
@@ -19,7 +19,9 @@ module VagrantPlugins
|
|
19
19
|
# box. This Vagrantfile contains the MAC address so that the user doesn't
|
20
20
|
# have to worry about it.
|
21
21
|
def create_vagrantfile
|
22
|
-
|
22
|
+
tmp_dir = @env["export.temp_dir"]
|
23
|
+
tmp_dir = "/tmp" if !tmp_dir
|
24
|
+
File.open(File.join(tmp_dir, "Vagrantfile"), "w") do |f|
|
23
25
|
f.write(KvmTemplateRenderer.render("package_Vagrantfile", {
|
24
26
|
:base_mac => @env[:machine].provider.driver.read_mac_address
|
25
27
|
}))
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module ProviderKvm
|
3
|
+
module Action
|
4
|
+
class PrepareGui
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
config = env[:machine].provider_config
|
11
|
+
if config.gui
|
12
|
+
driver = env[:machine].provider.driver
|
13
|
+
driver.set_gui(config.vnc_port, config.vnc_autoport, config.vnc_password)
|
14
|
+
end
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -9,22 +9,11 @@ module VagrantPlugins
|
|
9
9
|
|
10
10
|
def call(env)
|
11
11
|
@app.call(env)
|
12
|
+
@machine = env[:machine]
|
12
13
|
|
13
|
-
|
14
|
-
env[:
|
15
|
-
if opts[:nfs]
|
16
|
-
using_nfs = true
|
17
|
-
break
|
18
|
-
end
|
19
|
-
end
|
14
|
+
env[:nfs_host_ip] = read_host_ip(env[:machine])
|
15
|
+
env[:nfs_machine_ip] = read_machine_ip(env[:machine])
|
20
16
|
|
21
|
-
if using_nfs
|
22
|
-
@logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP")
|
23
|
-
env[:nfs_host_ip] = read_host_ip(env[:machine])
|
24
|
-
env[:nfs_machine_ip] = read_machine_ip(env[:machine])
|
25
|
-
|
26
|
-
raise Vagrant::Errors::NFSNoHostonlyNetwork if !env[:nfs_machine_ip]
|
27
|
-
end
|
28
17
|
end
|
29
18
|
|
30
19
|
# Returns the IP address of the first host only network adapter
|
@@ -39,7 +28,8 @@ module VagrantPlugins
|
|
39
28
|
return base_ip.join(".")
|
40
29
|
end
|
41
30
|
|
42
|
-
|
31
|
+
# If no private network configuration, return default ip
|
32
|
+
"192.168.123.1"
|
43
33
|
end
|
44
34
|
|
45
35
|
# Returns the IP address of the guest by looking at the first
|
@@ -53,7 +43,9 @@ module VagrantPlugins
|
|
53
43
|
end
|
54
44
|
end
|
55
45
|
|
56
|
-
|
46
|
+
# XXX duplicated with network.rb default
|
47
|
+
# If no private network configuration, return default ip
|
48
|
+
"192.168.123.10"
|
57
49
|
end
|
58
50
|
end
|
59
51
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module ProviderKvm
|
3
|
+
module Action
|
4
|
+
class PrepareNFSValidIds
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
@logger = Log4r::Logger.new("vagrant::action::vm::nfs")
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
env[:nfs_valid_ids] = env[:machine].provider.driver.uuid
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module ProviderKvm
|
3
|
+
module Action
|
4
|
+
class ResetImagePermission
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
|
11
|
+
#current_state = env[:machine].state.id
|
12
|
+
#if current_state == :shutoff
|
13
|
+
userid = Process.uid.to_s
|
14
|
+
groupid = Process.gid.to_s
|
15
|
+
env[:machine].provider.driver.reset_volume_permission(userid, groupid)
|
16
|
+
#end
|
17
|
+
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module ProviderKvm
|
3
|
+
module Action
|
4
|
+
class ResumeNetwork
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
interfaces = env[:machine].provider.driver.read_network_interfaces
|
11
|
+
interfaces.each do |nic|
|
12
|
+
next unless nic[:type] == :network
|
13
|
+
|
14
|
+
network_name = nic[:network].to_s
|
15
|
+
state = env[:machine].provider.driver.network_state?(network_name)
|
16
|
+
unless state
|
17
|
+
# start network and related services such as forward and nfs
|
18
|
+
env[:machine].provider.driver.start_network(network_name)
|
19
|
+
env[:action_runner].run(ForwardPorts, env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
@app.call(env)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -28,11 +28,12 @@ module VagrantPlugins
|
|
28
28
|
# Ignore NFS shared folders
|
29
29
|
#next if data[:nfs]
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
unless data[:disabled]
|
32
|
+
# convert to NFS share
|
33
|
+
data[:nfs] = true
|
34
|
+
# This to prevent overwriting the actual shared folders data
|
35
|
+
result[id] = data.dup
|
36
|
+
end
|
36
37
|
end
|
37
38
|
end
|
38
39
|
end
|
@@ -9,7 +9,14 @@ module VagrantPlugins
|
|
9
9
|
def call(env)
|
10
10
|
if env[:machine].provider.state.id == :running
|
11
11
|
env[:ui].info I18n.t("vagrant.actions.vm.suspend.suspending")
|
12
|
-
env[:machine].
|
12
|
+
if env[:machine].provider_config.force_suspend
|
13
|
+
env[:machine].provider.driver.suspend
|
14
|
+
elsif env[:machine].provider.driver.can_save?
|
15
|
+
env[:machine].provider.driver.save
|
16
|
+
else
|
17
|
+
env[:ui].warn ("Suspend is not supported. use pause instead.")
|
18
|
+
env[:machine].provider.driver.suspend
|
19
|
+
end
|
13
20
|
end
|
14
21
|
|
15
22
|
@app.call(env)
|
data/lib/vagrant-kvm/config.rb
CHANGED
@@ -2,7 +2,7 @@ module VagrantPlugins
|
|
2
2
|
module ProviderKvm
|
3
3
|
class Config < Vagrant.plugin("2", :config)
|
4
4
|
# An array of customizations to make on the VM prior to booting it.
|
5
|
-
|
5
|
+
|
6
6
|
# @return [Array]
|
7
7
|
attr_reader :customizations
|
8
8
|
|
@@ -26,10 +26,61 @@ module VagrantPlugins
|
|
26
26
|
# @return [String]
|
27
27
|
attr_accessor :image_type
|
28
28
|
|
29
|
+
# VM image mode(clone or COW with backing file)
|
30
|
+
#
|
31
|
+
# @return [Boolean]
|
32
|
+
attr_reader :image_backing
|
33
|
+
|
34
|
+
# VM image mode(clone or COW with backing file)
|
35
|
+
#
|
36
|
+
# @return [String]
|
37
|
+
attr_accessor :image_mode
|
38
|
+
|
39
|
+
# path of qemu binary
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
attr_accessor :qemu_bin
|
43
|
+
|
44
|
+
# cpu model
|
45
|
+
#
|
46
|
+
# @return [String]: x86_64/i386
|
47
|
+
attr_accessor :cpu_model
|
48
|
+
|
49
|
+
# memory size in bytes
|
50
|
+
# default: defined in box
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
attr_accessor :memory_size
|
54
|
+
|
55
|
+
# core number of cpu
|
56
|
+
# default: defined in box
|
57
|
+
#
|
58
|
+
# @return [String]
|
59
|
+
attr_accessor :core_number
|
60
|
+
attr_accessor :vnc_port
|
61
|
+
attr_accessor :vnc_autoport
|
62
|
+
attr_accessor :vnc_password
|
63
|
+
attr_accessor :machine_type
|
64
|
+
attr_accessor :network_model
|
65
|
+
attr_accessor :video_model
|
66
|
+
attr_accessor :force_pause
|
67
|
+
|
29
68
|
def initialize
|
30
69
|
@name = UNSET_VALUE
|
31
70
|
@gui = UNSET_VALUE
|
32
71
|
@image_type = UNSET_VALUE
|
72
|
+
@image_mode = UNSET_VALUE
|
73
|
+
@qemu_bin = UNSET_VALUE
|
74
|
+
@cpu_model = UNSET_VALUE
|
75
|
+
@memory_size = UNSET_VALUE
|
76
|
+
@core_number = UNSET_VALUE
|
77
|
+
@vnc_port = UNSET_VALUE
|
78
|
+
@vnc_autoport = UNSET_VALUE
|
79
|
+
@vnc_password = UNSET_VALUE
|
80
|
+
@machine_type = UNSET_VALUE
|
81
|
+
@network_model = UNSET_VALUE
|
82
|
+
@video_model = UNSET_VALUE
|
83
|
+
@force_pause = UNSET_VALUE
|
33
84
|
end
|
34
85
|
|
35
86
|
# This is the hook that is called to finalize the object before it
|
@@ -40,7 +91,57 @@ module VagrantPlugins
|
|
40
91
|
# Default is to not show a GUI
|
41
92
|
@gui = false if @gui == UNSET_VALUE
|
42
93
|
# Default image type is a sparsed raw
|
43
|
-
@image_type = '
|
94
|
+
@image_type = 'qcow2' if @image_type == UNSET_VALUE
|
95
|
+
case @image_mode
|
96
|
+
when UNSET_VALUE
|
97
|
+
@image_backing = true
|
98
|
+
when 'clone'
|
99
|
+
@image_backing = false
|
100
|
+
when 'cow'
|
101
|
+
@image_backing = true
|
102
|
+
else
|
103
|
+
@image_backing = true
|
104
|
+
end
|
105
|
+
# Search qemu binary with the default behavior
|
106
|
+
@qemu_bin = nil if @qemu_bin == UNSET_VALUE
|
107
|
+
# Default cpu model is x86_64, acceptable only x86_64/i686
|
108
|
+
@cpu_model = 'x86_64' if @cpu_model == UNSET_VALUE
|
109
|
+
@cpu_model = 'x86_64' unless @cpu_model =~ /^(i686|x86_64)$/
|
110
|
+
# Process memory size directive
|
111
|
+
# accept the case
|
112
|
+
# integer recgnized as KiB
|
113
|
+
# <num>KiB/KB/kb/MiB/MB/mb/GiB/GB/gb
|
114
|
+
#
|
115
|
+
case @memory_size
|
116
|
+
when /^([0-9][0-9]*)(KiB|kib)$/
|
117
|
+
@memory_size = ("#{$1}".to_i * 1024).to_s
|
118
|
+
when /^([0-9][0-9]*)(KB|kb)$/
|
119
|
+
@memory_size = ("#{$1}".to_i * 1000).to_s
|
120
|
+
when /^([0-9][0-9]*)(m||MiB|mib|)$/
|
121
|
+
@memory_size = ("#{$1}".to_i * 1048576).to_s
|
122
|
+
when /^([0-9][0-9]*)(MB|mb|)$/
|
123
|
+
@memory_size = ("#{$1}".to_i * 1000000).to_s
|
124
|
+
when /^([0-9][0-9]*)(g||GiB|gib)$/
|
125
|
+
@memory_size = ("#{$1}".to_i * 1073741824).to_s
|
126
|
+
when /^([0-9][0-9]*)(GB|gb|)$/
|
127
|
+
@memory_size = ("#{$1}".to_i * 1000000000).to_s
|
128
|
+
when /^([0-9][0-9]*)$/
|
129
|
+
@memory_size = ("#{$1}".to_i * 1024).to_s
|
130
|
+
when UNSET_VALUE
|
131
|
+
@memory_size = nil
|
132
|
+
else
|
133
|
+
@memory_size = nil
|
134
|
+
end
|
135
|
+
# Default core number is 1
|
136
|
+
@core_number = 1 if @core_number == UNSET_VALUE
|
137
|
+
|
138
|
+
@vnc_autoport = false if @vnc_autoport == UNSET_VALUE
|
139
|
+
@vnc_password = nil if @vnc_password == UNSET_VALUE
|
140
|
+
@vnc_port = -1 if @vnc_port == UNSET_VALUE
|
141
|
+
@machine_type = "pc-1.2" if @machine_type == UNSET_VALUE
|
142
|
+
@network_model = "virtio" if @network_model == UNSET_VALUE
|
143
|
+
@video_model = "cirrus" if @video_model == UNSET_VALUE
|
144
|
+
@force_pause = false if @force_pause == UNSET_VALUE
|
44
145
|
end
|
45
146
|
end
|
46
147
|
end
|
@@ -11,6 +11,7 @@ module VagrantPlugins
|
|
11
11
|
|
12
12
|
include Util
|
13
13
|
include Errors
|
14
|
+
include Util::Commands
|
14
15
|
|
15
16
|
# enum for states return by libvirt
|
16
17
|
VM_STATE = [
|
@@ -32,16 +33,21 @@ module VagrantPlugins
|
|
32
33
|
# XXX sufficient or have to check kvm and libvirt versions?
|
33
34
|
attr_reader :version
|
34
35
|
|
35
|
-
|
36
|
+
# KVM support status
|
37
|
+
attr_reader :kvm
|
38
|
+
|
39
|
+
def initialize(uuid=nil, conn=nil)
|
36
40
|
@logger = Log4r::Logger.new("vagrant::provider::kvm::driver")
|
37
41
|
@uuid = uuid
|
38
42
|
# This should be configurable
|
39
43
|
@pool_name = "vagrant"
|
40
44
|
@network_name = "vagrant"
|
41
45
|
|
46
|
+
load_kvm_module!
|
47
|
+
|
42
48
|
# Open a connection to the qemu driver
|
43
49
|
begin
|
44
|
-
@conn = Libvirt::open('qemu:///system')
|
50
|
+
@conn = conn || Libvirt::open('qemu:///system')
|
45
51
|
rescue Libvirt::Error => e
|
46
52
|
if e.libvirt_code == 5
|
47
53
|
# can't connect to hypervisor
|
@@ -52,9 +58,9 @@ module VagrantPlugins
|
|
52
58
|
end
|
53
59
|
|
54
60
|
@version = read_version
|
55
|
-
if @version <
|
61
|
+
if @conn.version.to_i < 1001000
|
56
62
|
raise Errors::KvmInvalidVersion,
|
57
|
-
:actual => @version, :required => "
|
63
|
+
:actual => @version, :required => ">= 1.1.0"
|
58
64
|
end
|
59
65
|
|
60
66
|
# Get storage pool if it exists
|
@@ -72,10 +78,73 @@ module VagrantPlugins
|
|
72
78
|
end
|
73
79
|
end
|
74
80
|
|
81
|
+
# create empty volume in storage pool
|
82
|
+
# args: disk_name, capacity, path, image_type, box_pool, box_path,
|
83
|
+
# backing, owner, group, mode, label
|
84
|
+
def create_volume(args={})
|
85
|
+
args = { # default values
|
86
|
+
:owner => '-1',
|
87
|
+
:group => '-1',
|
88
|
+
:mode => '0744',
|
89
|
+
:label => 'virt_image_t',
|
90
|
+
:backing => false
|
91
|
+
}.merge(args)
|
92
|
+
msg = "Creating volume #{args[:disk_name]}"
|
93
|
+
msg += " backed by volume #{args[:box_path]}" if args[:backing]
|
94
|
+
capacity = args[:capacity]
|
95
|
+
@logger.info(msg)
|
96
|
+
storage_vol_xml = <<-EOF
|
97
|
+
<volume>
|
98
|
+
<name>#{args[:disk_name]}</name>
|
99
|
+
<allocation>0</allocation>
|
100
|
+
<capacity unit="#{capacity[:unit]}">#{capacity[:size]}</capacity>
|
101
|
+
<target>
|
102
|
+
<path>#{args[:path]}</path>
|
103
|
+
<format type='#{args[:image_type]}'/>
|
104
|
+
<permissions>
|
105
|
+
<owner>#{args[:owner]}</owner>
|
106
|
+
<group>#{args[:group]}</group>
|
107
|
+
<mode>#{args[:mode]}</mode>
|
108
|
+
<label>#{args[:label]}</label>
|
109
|
+
</permissions>
|
110
|
+
</target>
|
111
|
+
EOF
|
112
|
+
|
113
|
+
if args[:backing]
|
114
|
+
storage_vol_xml += <<-EOF
|
115
|
+
<backingStore>
|
116
|
+
<path>#{args[:box_path]}</path>
|
117
|
+
<format type='#{args[:image_type]}'/>
|
118
|
+
</backingStore>
|
119
|
+
EOF
|
120
|
+
end
|
121
|
+
storage_vol_xml += "</volume>"
|
122
|
+
|
123
|
+
@logger.debug "Creating volume with XML:\n#{storage_vol_xml}"
|
124
|
+
if args[:backing]
|
125
|
+
vol = @pool.create_volume_xml(storage_vol_xml)
|
126
|
+
else
|
127
|
+
pool = @conn.lookup_storage_pool_by_name(args[:box_pool])
|
128
|
+
clonevol = pool.lookup_volume_by_path(args[:box_path])
|
129
|
+
# create_volume_xml_from() can convert disk image type automatically.
|
130
|
+
vol = @pool.create_volume_xml_from(storage_vol_xml, clonevol)
|
131
|
+
end
|
132
|
+
@pool.refresh
|
133
|
+
end
|
134
|
+
|
135
|
+
def reset_volume_permission(userid, groupid)
|
136
|
+
@logger.info("Revert image owner to #{userid}:#{groupid}")
|
137
|
+
domain = @conn.lookup_domain_by_uuid(@uuid)
|
138
|
+
definition = Util::VmDefinition.new(domain.xml_desc)
|
139
|
+
volume_path = definition.attributes[:disk]
|
140
|
+
run_root_command("chown #{userid}:#{groupid} " + volume_path)
|
141
|
+
run_root_command("chmod 660 " + volume_path)
|
142
|
+
end
|
143
|
+
|
75
144
|
def delete
|
76
145
|
domain = @conn.lookup_domain_by_uuid(@uuid)
|
77
|
-
definition = Util::VmDefinition.new(domain.xml_desc
|
78
|
-
volume = @pool.lookup_volume_by_path(definition.disk)
|
146
|
+
definition = Util::VmDefinition.new(domain.xml_desc)
|
147
|
+
volume = @pool.lookup_volume_by_path(definition.attributes[:disk])
|
79
148
|
volume.delete
|
80
149
|
# XXX remove pool if empty?
|
81
150
|
@pool.refresh
|
@@ -84,6 +153,12 @@ module VagrantPlugins
|
|
84
153
|
domain.undefine
|
85
154
|
end
|
86
155
|
|
156
|
+
def find_box_disk(xml)
|
157
|
+
definition = File.open(xml) { |f|
|
158
|
+
Util::VmDefinition.new(f.read) }
|
159
|
+
definition.attributes[:disk]
|
160
|
+
end
|
161
|
+
|
87
162
|
# Halts the virtual machine
|
88
163
|
def halt
|
89
164
|
domain = @conn.lookup_domain_by_uuid(@uuid)
|
@@ -92,63 +167,26 @@ module VagrantPlugins
|
|
92
167
|
|
93
168
|
# Imports the VM
|
94
169
|
#
|
95
|
-
# @param [String]
|
96
|
-
# @param [String]
|
97
|
-
# @param [
|
98
|
-
|
99
|
-
|
100
|
-
@logger.info("Importing VM")
|
170
|
+
# @param [String] definition Path to the VM XML file.
|
171
|
+
# @param [String] volume_name Name of the imported volume
|
172
|
+
# @param [Hash] attributes
|
173
|
+
def import(definition, volume_name, args={})
|
174
|
+
@logger.info("Importing VM #{@name}")
|
101
175
|
# create vm definition from xml
|
102
|
-
definition = File.open(
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# we use qemu-img convert to preserve image size
|
112
|
-
system("qemu-img convert -p #{old_path} -O #{image_type} #{new_path}")
|
113
|
-
@pool.refresh
|
114
|
-
volume = @pool.lookup_volume_by_name(new_disk)
|
115
|
-
definition.disk = volume.path
|
116
|
-
definition.name = @name
|
117
|
-
definition.image_type = image_type
|
118
|
-
# create vm
|
119
|
-
@logger.info("Creating new VM")
|
120
|
-
domain = @conn.define_domain_xml(definition.as_libvirt)
|
121
|
-
domain.uuid
|
122
|
-
end
|
123
|
-
|
124
|
-
# Imports the VM from an OVF file.
|
125
|
-
# XXX should be fusioned with import
|
126
|
-
#
|
127
|
-
# @param [String] ovf Path to the OVF file.
|
128
|
-
# @param [String] path Destination path for the volume.
|
129
|
-
# @param [String] image_type An image type for the volume.
|
130
|
-
# @return [String] UUID of the imported VM.
|
131
|
-
def import_ovf(ovf, path, image_type)
|
132
|
-
@logger.info("Importing OVF definition for VM")
|
133
|
-
# create vm definition from ovf
|
134
|
-
definition = File.open(ovf) { |f|
|
135
|
-
Util::VmDefinition.new(f.read, 'ovf') }
|
136
|
-
# copy volume to storage pool
|
137
|
-
box_disk = definition.disk
|
138
|
-
new_disk = File.basename(box_disk, File.extname(box_disk)) + "-" +
|
139
|
-
Time.now.to_i.to_s + ".img"
|
140
|
-
@logger.info("Converting volume #{box_disk} to #{new_disk}")
|
141
|
-
old_path = File.join(File.dirname(ovf), box_disk)
|
142
|
-
new_path = File.join(path, new_disk)
|
143
|
-
system("qemu-img convert -p #{old_path} -O #{image_type} #{new_path}")
|
144
|
-
@pool.refresh
|
145
|
-
volume = @pool.lookup_volume_by_name(new_disk)
|
146
|
-
definition.disk = volume.path
|
147
|
-
definition.name = @name
|
148
|
-
definition.image_type = image_type
|
176
|
+
definition = File.open(definition) { |f| Util::VmDefinition.new(f.read) }
|
177
|
+
volume = @pool.lookup_volume_by_name(volume_name)
|
178
|
+
args = {
|
179
|
+
:image_type => "qcow2",
|
180
|
+
:qemu_bin => "/usr/bin/qemu",
|
181
|
+
:disk => volume.path,
|
182
|
+
:name => @name
|
183
|
+
}.merge(args)
|
184
|
+
definition.update(args)
|
149
185
|
# create vm
|
150
186
|
@logger.info("Creating new VM")
|
151
|
-
|
187
|
+
xml_definition = definition.as_xml
|
188
|
+
@logger.debug("Creating new VM with XML config:\n#{xml_definition}")
|
189
|
+
domain = @conn.define_domain_xml(xml_definition)
|
152
190
|
domain.uuid
|
153
191
|
end
|
154
192
|
|
@@ -159,44 +197,89 @@ module VagrantPlugins
|
|
159
197
|
@network = @conn.lookup_network_by_name(@network_name)
|
160
198
|
definition = Util::NetworkDefinition.new(@network_name,
|
161
199
|
@network.xml_desc)
|
162
|
-
@network.destroy if @network.active?
|
163
|
-
@network.undefine
|
164
200
|
rescue Libvirt::RetrieveError
|
165
201
|
# Network doesn't exist, create with defaults
|
166
202
|
definition = Util::NetworkDefinition.new(@network_name)
|
167
203
|
end
|
168
|
-
definition.
|
169
|
-
@network
|
170
|
-
|
171
|
-
|
204
|
+
definition.update(config)
|
205
|
+
if @network.nil?
|
206
|
+
@logger.info("Creating network #{@network_name}")
|
207
|
+
@network = define_network(definition)
|
208
|
+
else
|
209
|
+
# Only destroy existing network if config has changed. This is
|
210
|
+
# necessary because other VM could be currently using this network
|
211
|
+
# and will loose connectivity if the network is destroyed.
|
212
|
+
old_def = Util::NetworkDefinition.new(@network_name, @network.xml_desc)
|
213
|
+
if old_def == definition && @network.active?
|
214
|
+
@logger.info "Reusing existing configuration for #{@network_name}"
|
215
|
+
else
|
216
|
+
@logger.info "Recreating network config for #{@network_name}"
|
217
|
+
@logger.debug "Old definition was:\n#{@network.xml_desc}"
|
218
|
+
@network.destroy if @network.active?
|
219
|
+
@network.undefine
|
220
|
+
@network = define_network(definition)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def define_network(definition)
|
226
|
+
xml = definition.as_xml
|
227
|
+
@logger.debug "Defining new network with XML:\n#{xml}"
|
228
|
+
network = @conn.define_network_xml(xml)
|
229
|
+
network.create
|
230
|
+
network
|
172
231
|
end
|
173
232
|
|
174
233
|
# Initialize or create storage pool
|
175
|
-
def init_storage(base_path)
|
234
|
+
def init_storage(base_path, uid, gid)
|
235
|
+
# Storage pool doesn't exist so we create it
|
236
|
+
# create dir if it doesn't exist
|
237
|
+
# if we let libvirt create the dir it is owned by root
|
238
|
+
pool_path = base_path.join("storage-pool")
|
239
|
+
pool_path.mkpath unless Dir.exists?(pool_path)
|
240
|
+
@pool = init_storage_directory(
|
241
|
+
:pool_path => pool_path,
|
242
|
+
:pool_name => @pool_name,
|
243
|
+
:owner => uid, :group=>gid, :mode=>'755')
|
244
|
+
end
|
245
|
+
|
246
|
+
def init_storage_directory(args={})
|
176
247
|
begin
|
177
248
|
# Get the storage pool if it exists
|
178
|
-
|
179
|
-
@logger.info("Init storage pool #{
|
249
|
+
pool = @conn.lookup_storage_pool_by_name(args[:pool_name])
|
250
|
+
@logger.info("Init storage pool #{args[:pool_name]}")
|
180
251
|
rescue Libvirt::RetrieveError
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
@
|
196
|
-
|
252
|
+
@logger.info("Init storage pool with owner: #{args[:owner]}")
|
253
|
+
storage_pool_xml = <<-EOF
|
254
|
+
<pool type="dir">
|
255
|
+
<name>#{args[:pool_name]}</name>
|
256
|
+
<target>
|
257
|
+
<path>#{args[:pool_path]}</path>
|
258
|
+
<permissions>
|
259
|
+
<owner>#{args[:owner]}</owner>
|
260
|
+
<group>#{args[:group]}</group>
|
261
|
+
<mode>#{args[:mode]}</mode>
|
262
|
+
</permissions>
|
263
|
+
</target>
|
264
|
+
</pool>
|
265
|
+
EOF
|
266
|
+
pool = @conn.define_storage_pool_xml(storage_pool_xml)
|
267
|
+
pool.build
|
268
|
+
@logger.info("Creating storage pool #{args[:pool_name]} in #{args[:pool_path]}")
|
269
|
+
end
|
270
|
+
pool.create unless pool.active?
|
271
|
+
pool.refresh
|
272
|
+
pool
|
273
|
+
end
|
274
|
+
|
275
|
+
def free_storage_pool(pool_name)
|
276
|
+
begin
|
277
|
+
pool = @conn.lookup_storage_pool_by_name(pool_name)
|
278
|
+
pool.destroy
|
279
|
+
pool.free
|
280
|
+
rescue Libvirt::RetrieveError
|
281
|
+
@logger.info("fail to free storage pool #{pool_name}")
|
197
282
|
end
|
198
|
-
@pool.create unless @pool.active?
|
199
|
-
@pool.refresh
|
200
283
|
end
|
201
284
|
|
202
285
|
# Returns a list of network interfaces of the VM.
|
@@ -207,12 +290,36 @@ module VagrantPlugins
|
|
207
290
|
Util::VmDefinition.list_interfaces(domain.xml_desc)
|
208
291
|
end
|
209
292
|
|
293
|
+
def network_state?(network_name)
|
294
|
+
begin
|
295
|
+
network = @conn.lookup_network_by_name(network_name)
|
296
|
+
network.active?
|
297
|
+
rescue Libvirt::RetrieveError
|
298
|
+
false
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def start_network(network_name)
|
303
|
+
begin
|
304
|
+
network = @conn.lookup_network_by_name(network_name)
|
305
|
+
network.create unless network.active?
|
306
|
+
rescue Libvirt::RetrieveError
|
307
|
+
false
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
210
311
|
def read_state
|
211
312
|
domain = @conn.lookup_domain_by_uuid(@uuid)
|
212
313
|
state, reason = domain.state
|
213
314
|
# check if domain has been saved
|
214
|
-
|
215
|
-
|
315
|
+
case VM_STATE[state]
|
316
|
+
when :shutoff
|
317
|
+
if domain.has_managed_save?
|
318
|
+
return :saved
|
319
|
+
end
|
320
|
+
return :poweroff
|
321
|
+
when :shutdown
|
322
|
+
return :poweroff
|
216
323
|
end
|
217
324
|
VM_STATE[state]
|
218
325
|
end
|
@@ -228,6 +335,12 @@ module VagrantPlugins
|
|
228
335
|
"#{maj}.#{min}.#{rel}"
|
229
336
|
end
|
230
337
|
|
338
|
+
def read_mac_address
|
339
|
+
domain = @conn.lookup_domain_by_uuid(@uuid)
|
340
|
+
definition = Util::VmDefinition.new(domain.xml_desc)
|
341
|
+
definition.attributes[:mac]
|
342
|
+
end
|
343
|
+
|
231
344
|
# Resumes the previously paused virtual machine.
|
232
345
|
def resume
|
233
346
|
@logger.debug("Resuming paused VM...")
|
@@ -241,19 +354,32 @@ module VagrantPlugins
|
|
241
354
|
end
|
242
355
|
|
243
356
|
def set_mac_address(mac)
|
244
|
-
|
245
|
-
definition = Util::VmDefinition.new(domain.xml_desc, 'libvirt')
|
246
|
-
definition.set_mac(mac)
|
247
|
-
domain.undefine
|
248
|
-
@conn.define_domain_xml(definition.as_libvirt)
|
357
|
+
update_domain_xml(:mac => mac)
|
249
358
|
end
|
250
359
|
|
251
|
-
def set_gui
|
360
|
+
def set_gui(vnc_port, vnc_autoport, vnc_password)
|
361
|
+
@logger.debug("Enabling GUI")
|
362
|
+
update_domain_xml(
|
363
|
+
:gui => true,
|
364
|
+
:vnc_port => vnc_port,
|
365
|
+
:vnc_autoport => vnc_autoport,
|
366
|
+
:vnc_password => vnc_password)
|
367
|
+
end
|
368
|
+
|
369
|
+
def set_diskbus(disk_bus)
|
370
|
+
update_domain_xml(:disk_bus => disk_bus)
|
371
|
+
end
|
372
|
+
|
373
|
+
def update_domain_xml(options)
|
252
374
|
domain = @conn.lookup_domain_by_uuid(@uuid)
|
253
|
-
|
254
|
-
|
375
|
+
# Use DOMAIN_XML_SECURE to dump ALL options (including VNC password)
|
376
|
+
original_xml = domain.xml_desc(Libvirt::Domain::DOMAIN_XML_SECURE)
|
377
|
+
definition = Util::VmDefinition.new(original_xml)
|
378
|
+
definition.update(options)
|
255
379
|
domain.undefine
|
256
|
-
|
380
|
+
xml = definition.as_xml
|
381
|
+
@logger.debug("Updating domain xml\nFrom: #{original_xml}\nTo: #{xml}")
|
382
|
+
@conn.define_domain_xml(xml)
|
257
383
|
end
|
258
384
|
|
259
385
|
# Starts the virtual machine.
|
@@ -264,11 +390,48 @@ module VagrantPlugins
|
|
264
390
|
end
|
265
391
|
|
266
392
|
# Suspend the virtual machine and saves its states.
|
267
|
-
def
|
393
|
+
def save
|
268
394
|
domain = @conn.lookup_domain_by_uuid(@uuid)
|
269
395
|
domain.managed_save
|
270
396
|
end
|
271
397
|
|
398
|
+
def can_save?
|
399
|
+
domain = @conn.lookup_domain_by_uuid(@uuid)
|
400
|
+
definition = Util::VmDefinition.new(domain.xml_desc)
|
401
|
+
disk_bus = definition.get(:disk_bus)
|
402
|
+
return disk_bus != 'sata'
|
403
|
+
end
|
404
|
+
|
405
|
+
# Suspend the virtual machine temporaly
|
406
|
+
def suspend
|
407
|
+
domain = @conn.lookup_domain_by_uuid(@uuid)
|
408
|
+
domain.suspend
|
409
|
+
end
|
410
|
+
|
411
|
+
# Export
|
412
|
+
def export(xml_path)
|
413
|
+
@logger.info("FIXME: export has not tested yet.")
|
414
|
+
new_disk = 'disk.img'
|
415
|
+
# create new_disk
|
416
|
+
domain = @conn.lookup_domain_by_uuid(@uuid)
|
417
|
+
definition = Util::VmDefinition.new(domain.xml_desc)
|
418
|
+
disk_image = definition.attributes[:disk]
|
419
|
+
to_path = File.dirname(xml_path)
|
420
|
+
new_path = File.join(to_path, new_disk)
|
421
|
+
@logger.info("create disk image #{new_path}")
|
422
|
+
run_command("qemu-img convert -c -S 4k -O qcow2 #{disk_image} #{new_path}")
|
423
|
+
# write out box.xml
|
424
|
+
definition.update(:disk => new_disk,:gui => false,:uuid => nil)
|
425
|
+
File.open(xml_path,'w') do |f|
|
426
|
+
f.puts(definition.as_xml)
|
427
|
+
end
|
428
|
+
# write metadata.json
|
429
|
+
json_path=File.join(to_path, 'metadata.json')
|
430
|
+
File.open(json_path,'w') do |f|
|
431
|
+
f.puts('{"provider": "kvm"}')
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
272
435
|
# Verifies that the driver is ready and the connection is open
|
273
436
|
#
|
274
437
|
# This will raise a VagrantError if things are not ready.
|
@@ -289,6 +452,65 @@ module VagrantPlugins
|
|
289
452
|
false
|
290
453
|
end
|
291
454
|
end
|
455
|
+
|
456
|
+
# Checks which Linux OS variants
|
457
|
+
#
|
458
|
+
# host_redhat?
|
459
|
+
# host_debian?
|
460
|
+
# host_gentoo?
|
461
|
+
# host_arch?
|
462
|
+
# @return [Boolean]
|
463
|
+
def host_redhat?
|
464
|
+
release_file = Pathname.new("/etc/redhat-release")
|
465
|
+
|
466
|
+
if release_file.exist?
|
467
|
+
release_file.open("r:ISO-8859-1:UTF-8") do |f|
|
468
|
+
contents = f.gets
|
469
|
+
return true if contents =~ /^CentOS/ # CentOS
|
470
|
+
return true if contents =~ /^Fedora/ # Fedora
|
471
|
+
return true if contents =~ /^Korora/ # Korora
|
472
|
+
|
473
|
+
# Oracle Linux < 5.3
|
474
|
+
return true if contents =~ /^Enterprise Linux Enterprise Linux/
|
475
|
+
|
476
|
+
# Red Hat Enterprise Linux and Oracle Linux >= 5.3
|
477
|
+
return true if contents =~ /^Red Hat Enterprise Linux/
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
false
|
482
|
+
end
|
483
|
+
|
484
|
+
def host_debian?
|
485
|
+
File.exists?("/etc/debian_version")
|
486
|
+
end
|
487
|
+
|
488
|
+
def host_gentoo?
|
489
|
+
File.exists?("/etc/gentoo-release")
|
490
|
+
end
|
491
|
+
|
492
|
+
def host_arch?
|
493
|
+
File.exist?("/etc/arch-release")
|
494
|
+
end
|
495
|
+
|
496
|
+
private
|
497
|
+
def load_kvm_module!
|
498
|
+
@logger.info("Check KVM kernel modules")
|
499
|
+
kvm = File.readlines('/proc/modules').any? { |line| line =~ /kvm_(intel|amd)/ }
|
500
|
+
unless kvm
|
501
|
+
case File.read('/proc/cpuinfo')
|
502
|
+
when /vmx/
|
503
|
+
kvm = true if run_command("sudo /sbin/modprobe kvm-intel")
|
504
|
+
when /svm/
|
505
|
+
kvm = true if run_command("sudo /sbin/modprobe kvm-amd")
|
506
|
+
else
|
507
|
+
# looks like virtualization is not supported
|
508
|
+
end
|
509
|
+
end
|
510
|
+
# FIXME: see KVM/ARM project
|
511
|
+
raise Errors::VagrantKVMError, "KVM is unavailable" unless kvm
|
512
|
+
true
|
513
|
+
end
|
292
514
|
end
|
293
515
|
end
|
294
516
|
end
|