vagrant-kvm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.gitignore +14 -0
  2. data/CHANGELOG.md +3 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE +8 -0
  5. data/README.md +90 -0
  6. data/Rakefile +15 -0
  7. data/example_box/README.md +18 -0
  8. data/example_box/box.xml +78 -0
  9. data/example_box/metadata.json +3 -0
  10. data/lib/vagrant-kvm.rb +20 -0
  11. data/lib/vagrant-kvm/action.rb +268 -0
  12. data/lib/vagrant-kvm/action/boot.rb +22 -0
  13. data/lib/vagrant-kvm/action/check_box.rb +36 -0
  14. data/lib/vagrant-kvm/action/check_created.rb +21 -0
  15. data/lib/vagrant-kvm/action/check_kvm.rb +23 -0
  16. data/lib/vagrant-kvm/action/check_running.rb +21 -0
  17. data/lib/vagrant-kvm/action/created.rb +20 -0
  18. data/lib/vagrant-kvm/action/destroy.rb +19 -0
  19. data/lib/vagrant-kvm/action/destroy_confirm.rb +17 -0
  20. data/lib/vagrant-kvm/action/export.rb +57 -0
  21. data/lib/vagrant-kvm/action/forced_halt.rb +21 -0
  22. data/lib/vagrant-kvm/action/import.rb +54 -0
  23. data/lib/vagrant-kvm/action/init_storage_pool.rb +19 -0
  24. data/lib/vagrant-kvm/action/is_paused.rb +20 -0
  25. data/lib/vagrant-kvm/action/is_running.rb +20 -0
  26. data/lib/vagrant-kvm/action/is_saved.rb +20 -0
  27. data/lib/vagrant-kvm/action/match_mac_address.rb +21 -0
  28. data/lib/vagrant-kvm/action/message_not_created.rb +16 -0
  29. data/lib/vagrant-kvm/action/message_will_not_destroy.rb +17 -0
  30. data/lib/vagrant-kvm/action/network.rb +69 -0
  31. data/lib/vagrant-kvm/action/package.rb +20 -0
  32. data/lib/vagrant-kvm/action/package_vagrantfile.rb +31 -0
  33. data/lib/vagrant-kvm/action/prepare_nfs_settings.rb +61 -0
  34. data/lib/vagrant-kvm/action/prune_nfs_exports.rb +20 -0
  35. data/lib/vagrant-kvm/action/resume.rb +25 -0
  36. data/lib/vagrant-kvm/action/setup_package_files.rb +51 -0
  37. data/lib/vagrant-kvm/action/share_folders.rb +76 -0
  38. data/lib/vagrant-kvm/action/suspend.rb +20 -0
  39. data/lib/vagrant-kvm/config.rb +31 -0
  40. data/lib/vagrant-kvm/driver/driver.rb +271 -0
  41. data/lib/vagrant-kvm/errors.rb +11 -0
  42. data/lib/vagrant-kvm/plugin.rb +73 -0
  43. data/lib/vagrant-kvm/provider.rb +104 -0
  44. data/lib/vagrant-kvm/util.rb +12 -0
  45. data/lib/vagrant-kvm/util/kvm_template_renderer.rb +20 -0
  46. data/lib/vagrant-kvm/util/network_definition.rb +106 -0
  47. data/lib/vagrant-kvm/util/vm_definition.rb +192 -0
  48. data/lib/vagrant-kvm/version.rb +5 -0
  49. data/locales/en.yml +4 -0
  50. data/templates/libvirt_domain.erb +64 -0
  51. data/vagrant-kvm.gemspec +58 -0
  52. metadata +191 -0
@@ -0,0 +1,17 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ module Action
4
+ class MessageWillNotDestroy
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info I18n.t("vagrant.commands.destroy.will_not_destroy",
11
+ :name => env[:machine].name)
12
+ @app.call(env)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module ProviderKvm
5
+ module Action
6
+ # This middleware class configures networking
7
+ class Network
8
+
9
+ def initialize(app, env)
10
+ @logger = Log4r::Logger.new("vagrant::plugins::kvm::network")
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ # TODO: Validate network configuration prior to anything below
16
+ @env = env
17
+
18
+ options = nil
19
+ env[:machine].config.vm.networks.each do |type, network_options|
20
+ options = network_options if type == :private_network
21
+ end
22
+
23
+ if 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
+ :start => start_ip,
33
+ :end => end_ip }
34
+ options = {
35
+ :base_ip => base_ip,
36
+ :netmask => "255.255.255.0",
37
+ :range => range
38
+ }.merge(options)
39
+ end
40
+
41
+ hosts = []
42
+ name = env[:machine].provider_config.name ?
43
+ env[:machine].provider_config.name : "default"
44
+ hosts << {
45
+ :mac => format_mac(env[:machine].config.vm.base_mac),
46
+ :name => name,
47
+ :ip => options[:ip]
48
+ }
49
+ options[:hosts] = hosts
50
+
51
+ env[:ui].info I18n.t("vagrant.actions.vm.network.preparing")
52
+ env[:machine].provider.driver.create_network(options)
53
+
54
+ @app.call(env)
55
+ end
56
+
57
+ def format_mac(mac)
58
+ if mac.length == 12
59
+ mac = mac[0..1] + ":" + mac[2..3] + ":" +
60
+ mac[4..5] + ":" + mac[6..7] + ":" +
61
+ mac[8..9] + ":" + mac[10..11]
62
+ end
63
+ mac
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,20 @@
1
+ require 'vagrant/action/general/package'
2
+
3
+ module VagrantPlugins
4
+ module ProviderKvm
5
+ module Action
6
+ class Package < Vagrant::Action::General::Package
7
+ # Doing this so that we can test that the parent is properly
8
+ # called in the unit tests.
9
+ alias_method :general_call, :call
10
+ def call(env)
11
+ # Just match up a couple environmental variables so that
12
+ # the superclass will do the right thing. Then, call the
13
+ # superclass
14
+ env["package.directory"] = env["export.temp_dir"]
15
+ general_call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ module Action
4
+ class PackageVagrantfile
5
+
6
+ include ProviderKvm::Util
7
+
8
+ def initialize(app, env)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ @env = env
14
+ create_vagrantfile
15
+ @app.call(env)
16
+ end
17
+
18
+ # This method creates the auto-generated Vagrantfile at the root of the
19
+ # box. This Vagrantfile contains the MAC address so that the user doesn't
20
+ # have to worry about it.
21
+ def create_vagrantfile
22
+ File.open(File.join(@env["export.temp_dir"], "Vagrantfile"), "w") do |f|
23
+ f.write(KvmTemplateRenderer.render("package_Vagrantfile", {
24
+ :base_mac => @env[:machine].provider.driver.read_mac_address
25
+ }))
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,61 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ module Action
4
+ class PrepareNFSSettings
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
+ @app.call(env)
12
+
13
+ using_nfs = false
14
+ env[:machine].config.vm.synced_folders.each do |id, opts|
15
+ if opts[:nfs]
16
+ using_nfs = true
17
+ break
18
+ end
19
+ end
20
+
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
+ end
29
+
30
+ # Returns the IP address of the first host only network adapter
31
+ #
32
+ # @param [Machine] machine
33
+ # @return [String]
34
+ def read_host_ip(machine)
35
+ ip = read_machine_ip(machine)
36
+ if ip
37
+ base_ip = ip.split(".")
38
+ base_ip[3] = "1"
39
+ return base_ip.join(".")
40
+ end
41
+
42
+ nil
43
+ end
44
+
45
+ # Returns the IP address of the guest by looking at the first
46
+ # enabled host only network.
47
+ #
48
+ # @return [String]
49
+ def read_machine_ip(machine)
50
+ machine.config.vm.networks.each do |type, options|
51
+ if type == :private_network && options[:ip].is_a?(String)
52
+ return options[:ip]
53
+ end
54
+ end
55
+
56
+ nil
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ module Action
4
+ class PruneNFSExports
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ if env[:host]
11
+ uuid = env[:machine].provider.driver.uuid
12
+ env[:host].nfs_prune(uuid)
13
+ end
14
+
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ module Action
4
+ class Resume
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ current_state = env[:machine].provider.state.id
11
+
12
+ if current_state == :paused
13
+ env[:ui].info I18n.t("vagrant.actions.vm.resume.unpausing")
14
+ env[:machine].provider.driver.resume
15
+ elsif current_state == :saved
16
+ env[:ui].info I18n.t("vagrant.actions.vm.resume.resuming")
17
+ env[:action_runner].run(Boot, env)
18
+ end
19
+
20
+ @app.call(env)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ module Action
4
+ class SetupPackageFiles
5
+ def initialize(app, env)
6
+ @app = app
7
+
8
+ env["package.include"] ||= []
9
+ env["package.vagrantfile"] ||= nil
10
+ end
11
+
12
+ def call(env)
13
+ files = {}
14
+ env["package.include"].each do |file|
15
+ source = Pathname.new(file)
16
+ dest = nil
17
+
18
+ # If the source is relative then we add the file as-is to the include
19
+ # directory. Otherwise, we copy only the file into the root of the
20
+ # include directory. Kind of strange, but seems to match what people
21
+ # expect based on history.
22
+ if source.relative?
23
+ dest = source
24
+ else
25
+ dest = source.basename
26
+ end
27
+
28
+ # Assign the mapping
29
+ files[file] = dest
30
+ end
31
+
32
+ if env["package.vagrantfile"]
33
+ # Vagrantfiles are treated special and mapped to a specific file
34
+ files[env["package.vagrantfile"]] = "_Vagrantfile"
35
+ end
36
+
37
+ # Verify the mapping
38
+ files.each do |from, _|
39
+ raise Vagrant::Errors::PackageIncludeMissing,
40
+ :file => from if !File.exist?(from)
41
+ end
42
+
43
+ # Save the mapping
44
+ env["package.files"] = files
45
+
46
+ @app.call(env)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,76 @@
1
+ require "pathname"
2
+
3
+ require "log4r"
4
+
5
+ module VagrantPlugins
6
+ module ProviderKvm
7
+ module Action
8
+ class ShareFolders
9
+ def initialize(app, env)
10
+ @logger = Log4r::Logger.new("vagrant::action::vm::share_folders")
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ @env = env
16
+
17
+ prepare_folders
18
+ create_metadata
19
+
20
+ @app.call(env)
21
+ end
22
+
23
+ # This method returns an actual list of shared
24
+ # folders to create and their proper path.
25
+ def shared_folders
26
+ {}.tap do |result|
27
+ @env[:machine].config.vm.synced_folders.each do |id, data|
28
+ # Ignore NFS shared folders
29
+ #next if data[:nfs]
30
+
31
+ # convert to NFS share
32
+ data[:nfs] = true
33
+
34
+ # This to prevent overwriting the actual shared folders data
35
+ result[id] = data.dup
36
+ end
37
+ end
38
+ end
39
+
40
+ # Prepares the shared folders by verifying they exist and creating them
41
+ # if they don't.
42
+ def prepare_folders
43
+ shared_folders.each do |id, options|
44
+ hostpath = Pathname.new(options[:hostpath]).expand_path(@env[:root_path])
45
+
46
+ if !hostpath.directory? && options[:create]
47
+ # Host path doesn't exist, so let's create it.
48
+ @logger.debug("Host path doesn't exist, creating: #{hostpath}")
49
+
50
+ begin
51
+ hostpath.mkpath
52
+ rescue Errno::EACCES
53
+ raise Vagrant::Errors::SharedFolderCreateFailed,
54
+ :path => hostpath.to_s
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def create_metadata
61
+ @env[:ui].info I18n.t("vagrant.actions.vm.share_folders.creating")
62
+
63
+ folders = []
64
+ shared_folders.each do |id, data|
65
+ folders << {
66
+ :name => id,
67
+ :hostpath => File.expand_path(data[:hostpath], @env[:root_path]),
68
+ :transient => data[:transient]
69
+ }
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ module Action
4
+ class Suspend
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ if env[:machine].provider.state.id == :running
11
+ env[:ui].info I18n.t("vagrant.actions.vm.suspend.suspending")
12
+ env[:machine].provider.driver.suspend
13
+ end
14
+
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module ProviderKvm
3
+ class Config < Vagrant.plugin("2", :config)
4
+ # An array of customizations to make on the VM prior to booting it.
5
+ #
6
+ # @return [Array]
7
+ attr_reader :customizations
8
+
9
+ # This should be set to the name of the VM
10
+ #
11
+ # @return [String]
12
+ attr_accessor :name
13
+
14
+ # The defined network adapters.
15
+ #
16
+ # @return [Hash]
17
+ attr_reader :network_adapters
18
+
19
+ def initialize
20
+ @name = UNSET_VALUE
21
+ end
22
+
23
+ # This is the hook that is called to finalize the object before it
24
+ # is put into use.
25
+ def finalize!
26
+ # The default name is just nothing, and we default it
27
+ @name = nil if @name == UNSET_VALUE
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,271 @@
1
+ require 'libvirt'
2
+ require 'log4r'
3
+
4
+ module VagrantPlugins
5
+ module ProviderKvm
6
+ module Driver
7
+ class Driver
8
+ # This is raised if the VM is not found when initializing
9
+ # a driver with a UUID.
10
+ class VMNotFound < StandardError; end
11
+
12
+ include Util
13
+
14
+ # enum for states return by libvirt
15
+ VM_STATE = [
16
+ :no_state,
17
+ :running,
18
+ :blocked,
19
+ :paused,
20
+ :shutdown,
21
+ :shutoff,
22
+ :crashed]
23
+
24
+ # The UUID of the virtual machine we represent
25
+ attr_reader :uuid
26
+
27
+ # The QEMU version
28
+ # XXX sufficient or have to check kvm and libvirt versions?
29
+ attr_reader :version
30
+
31
+ def initialize(uuid=nil)
32
+ @logger = Log4r::Logger.new("vagrant::provider::kvm::driver")
33
+ @uuid = uuid
34
+ # This should be configurable
35
+ @pool_name = "vagrant"
36
+ @network_name = "vagrant"
37
+
38
+ # Open a connection to the qemu driver
39
+ begin
40
+ @conn = Libvirt::open('qemu:///system')
41
+ rescue Libvirt::Error => e
42
+ if e.libvirt_code == 5
43
+ # can't connect to hypervisor
44
+ raise Vagrant::Errors::KvmNoConnection
45
+ else
46
+ raise e
47
+ end
48
+ end
49
+ @version = read_version
50
+ if (@version[:maj] == 1 && @version[:min] < 2) || @version[:maj] < 1
51
+ raise Vagrant::Errors::KvmInvalidVersion
52
+ end
53
+
54
+ # Get storage pool if it exists
55
+ begin
56
+ @pool = @conn.lookup_storage_pool_by_name(@pool_name)
57
+ @logger.info("Init storage pool #{@pool_name}")
58
+ rescue Libvirt::RetrieveError
59
+ # storage pool doesn't exist yet
60
+ end
61
+
62
+ if @uuid
63
+ # Verify the VM exists, and if it doesn't, then don't worry
64
+ # about it (mark the UUID as nil)
65
+ raise VMNotFound if !vm_exists?(@uuid)
66
+ end
67
+ end
68
+
69
+ def delete
70
+ domain = @conn.lookup_domain_by_uuid(@uuid)
71
+ definition = Util::VmDefinition.new(domain.xml_desc, 'libvirt')
72
+ volume = @pool.lookup_volume_by_path(definition.disk)
73
+ volume.delete
74
+ # XXX remove pool if empty?
75
+ @pool.refresh
76
+ # remove any saved state
77
+ domain.managed_save_remove if domain.has_managed_save?
78
+ domain.undefine
79
+ end
80
+
81
+ # Halts the virtual machine
82
+ def halt
83
+ domain = @conn.lookup_domain_by_uuid(@uuid)
84
+ domain.destroy
85
+ end
86
+
87
+ # Imports the VM
88
+ #
89
+ # @param [String] xml Path to the libvirt XML file.
90
+ # @param [String] path Destination path for the volume.
91
+ # @return [String] UUID of the imported VM.
92
+ def import(xml, path)
93
+ @logger.info("Importing VM")
94
+ # create vm definition from xml
95
+ definition = File.open(xml) { |f|
96
+ Util::VmDefinition.new(f.read) }
97
+ # copy volume to storage pool
98
+ box_disk = definition.disk
99
+ new_disk = File.basename(box_disk, File.extname(box_disk)) + "-" +
100
+ Time.now.to_i.to_s + ".img"
101
+ @logger.info("Copying volume #{box_disk} to #{new_disk}")
102
+ old_path = File.join(File.dirname(xml), box_disk)
103
+ new_path = File.join(path, new_disk)
104
+ # we use qemu-img convert to preserve image size
105
+ system("qemu-img convert -p #{old_path} -O raw #{new_path}")
106
+ @pool.refresh
107
+ volume = @pool.lookup_volume_by_name(new_disk)
108
+ definition.disk = volume.path
109
+ # create vm
110
+ @logger.info("Creating new VM")
111
+ domain = @conn.define_domain_xml(definition.as_libvirt)
112
+ domain.uuid
113
+ end
114
+
115
+ # Imports the VM from an OVF file.
116
+ # XXX should be fusioned with import
117
+ #
118
+ # @param [String] ovf Path to the OVF file.
119
+ # @param [String] path Destination path for the volume.
120
+ # @return [String] UUID of the imported VM.
121
+ def import_ovf(ovf, path)
122
+ @logger.info("Importing OVF definition for VM")
123
+ # create vm definition from ovf
124
+ definition = File.open(ovf) { |f|
125
+ Util::VmDefinition.new(f.read, 'ovf') }
126
+ # copy volume to storage pool
127
+ box_disk = definition.disk
128
+ new_disk = File.basename(box_disk, File.extname(box_disk)) + "-" +
129
+ Time.now.to_i.to_s + ".img"
130
+ @logger.info("Converting volume #{box_disk} to #{new_disk}")
131
+ old_path = File.join(File.dirname(ovf), box_disk)
132
+ new_path = File.join(path, new_disk)
133
+ system("qemu-img convert -p #{old_path} -O raw #{new_path}")
134
+ @pool.refresh
135
+ volume = @pool.lookup_volume_by_name(new_disk)
136
+ definition.disk = volume.path
137
+ # create vm
138
+ @logger.info("Creating new VM")
139
+ domain = @conn.define_domain_xml(definition.as_libvirt)
140
+ domain.uuid
141
+ end
142
+
143
+ # Create network
144
+ def create_network(config)
145
+ begin
146
+ # Get the network if it exists
147
+ @network = @conn.lookup_network_by_name(@network_name)
148
+ definition = Util::NetworkDefinition.new(@network_name,
149
+ @network.xml_desc)
150
+ @network.destroy if @network.active?
151
+ @network.undefine
152
+ rescue Libvirt::RetrieveError
153
+ # Network doesn't exist, create with defaults
154
+ definition = Util::NetworkDefinition.new(@network_name)
155
+ end
156
+ definition.configure(config)
157
+ @network = @conn.define_network_xml(definition.as_xml)
158
+ @logger.info("Creating network #{@network_name}")
159
+ @network.create
160
+ end
161
+
162
+ # Initialize or create storage pool
163
+ def init_storage(base_path)
164
+ begin
165
+ # Get the storage pool if it exists
166
+ @pool = @conn.lookup_storage_pool_by_name(@pool_name)
167
+ @logger.info("Init storage pool #{@pool_name}")
168
+ rescue Libvirt::RetrieveError
169
+ # Storage pool doesn't exist so we create it
170
+ # create dir if it doesn't exist
171
+ # if we let libvirt create the dir it is owned by root
172
+ pool_path = base_path.join("storage-pool")
173
+ pool_path.mkpath unless Dir.exists?(pool_path)
174
+ storage_pool_xml = <<-EOF
175
+ <pool type="dir">
176
+ <name>#{@pool_name}</name>
177
+ <target>
178
+ <path>#{pool_path}</path>
179
+ </target>
180
+ </pool>
181
+ EOF
182
+ @pool = @conn.define_storage_pool_xml(storage_pool_xml)
183
+ @pool.build
184
+ @logger.info("Creating storage pool #{@pool_name} in #{pool_path}")
185
+ end
186
+ @pool.create unless @pool.active?
187
+ @pool.refresh
188
+ end
189
+
190
+ # Returns a list of network interfaces of the VM.
191
+ #
192
+ # @return [Hash]
193
+ def read_network_interfaces
194
+ domain = @conn.lookup_domain_by_uuid(@uuid)
195
+ Util::VmDefinition.list_interfaces(domain.xml_desc)
196
+ end
197
+
198
+ def read_state
199
+ domain = @conn.lookup_domain_by_uuid(@uuid)
200
+ state, reason = domain.state
201
+ # check if domain has been saved
202
+ if VM_STATE[state] == :shutoff and domain.has_managed_save?
203
+ return :saved
204
+ end
205
+ VM_STATE[state]
206
+ end
207
+
208
+ # Return the qemu version
209
+ #
210
+ # @return [Hash] with :maj and :min version numbers
211
+ def read_version
212
+ # libvirt returns a number like 1002002 for version 1.2.2
213
+ # we return just the major.minor part like this 1002
214
+ maj = @conn.version / 1000000
215
+ min = (@conn.version - maj*1000000) / 1000
216
+ { :maj => maj, :min => min }
217
+ end
218
+
219
+ # Resumes the previously paused virtual machine.
220
+ def resume
221
+ @logger.debug("Resuming paused VM...")
222
+ domain = @conn.lookup_domain_by_uuid(@uuid)
223
+ domain.resume
224
+ true
225
+ end
226
+
227
+ def set_mac_address(mac)
228
+ domain = @conn.lookup_domain_by_uuid(@uuid)
229
+ definition = Util::VmDefinition.new(domain.xml_desc, 'libvirt')
230
+ definition.set_mac(mac)
231
+ domain.undefine
232
+ @conn.define_domain_xml(definition.as_libvirt)
233
+ end
234
+
235
+ # Starts the virtual machine.
236
+ def start
237
+ domain = @conn.lookup_domain_by_uuid(@uuid)
238
+ domain.create
239
+ true
240
+ end
241
+
242
+ # Suspend the virtual machine and saves its states.
243
+ def suspend
244
+ domain = @conn.lookup_domain_by_uuid(@uuid)
245
+ domain.managed_save
246
+ end
247
+
248
+ # Verifies that the driver is ready and the connection is open
249
+ #
250
+ # This will raise a VagrantError if things are not ready.
251
+ def verify!
252
+ if @conn.closed?
253
+ raise Vagrant::Errors::KvmNoConnection
254
+ end
255
+ end
256
+
257
+ # Checks if a VM with the given UUID exists.
258
+ #
259
+ # @return [Boolean]
260
+ def vm_exists?(uuid)
261
+ begin
262
+ @logger.info("Check if VM #{uuid} exists")
263
+ @conn.lookup_domain_by_uuid(uuid)
264
+ rescue Libvirt::RetrieveError
265
+ false
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end