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.
- 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
|