Class: VagrantPlugins::ProviderKvm::Driver::Driver

Inherits:
Object
  • Object
show all
Includes:
Util
Defined in:
lib/vagrant-kvm/driver/driver.rb

Defined Under Namespace

Classes: VMNotFound

Constant Summary collapse

VM_STATE =

enum for states return by libvirt

[
:no_state,
:running,
:blocked,
:paused,
:shutdown,
:shutoff,
:crashed]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uuid = nil) ⇒ Driver

Returns a new instance of Driver.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/vagrant-kvm/driver/driver.rb', line 31

def initialize(uuid=nil)
  @logger = Log4r::Logger.new("vagrant::provider::kvm::driver")
  @uuid = uuid
  # This should be configurable
  @pool_name = "vagrant"
  @network_name = "vagrant"

  # Open a connection to the qemu driver
  begin
    @conn = Libvirt::open('qemu:///system')
  rescue Libvirt::Error => e
    if e.libvirt_code == 5
      # can't connect to hypervisor
      raise Vagrant::Errors::KvmNoConnection
    else
      raise e
    end
  end
  @version = read_version
  if (@version[:maj] == 1 && @version[:min] < 2) || @version[:maj] < 1
    raise Vagrant::Errors::KvmInvalidVersion
  end

  # Get storage pool if it exists
  begin
    @pool = @conn.lookup_storage_pool_by_name(@pool_name)
    @logger.info("Init storage pool #{@pool_name}")
  rescue Libvirt::RetrieveError
    # storage pool doesn't exist yet
  end

  if @uuid
    # Verify the VM exists, and if it doesn't, then don't worry
    # about it (mark the UUID as nil)
    raise VMNotFound if !vm_exists?(@uuid)
  end
end

Instance Attribute Details

#uuidObject (readonly)

The UUID of the virtual machine we represent



25
26
27
# File 'lib/vagrant-kvm/driver/driver.rb', line 25

def uuid
  @uuid
end

#versionObject (readonly)

The QEMU version XXX sufficient or have to check kvm and libvirt versions?



29
30
31
# File 'lib/vagrant-kvm/driver/driver.rb', line 29

def version
  @version
end

Instance Method Details

#create_network(config) ⇒ Object

Create network



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/vagrant-kvm/driver/driver.rb', line 144

def create_network(config)
  begin
    # Get the network if it exists
    @network = @conn.lookup_network_by_name(@network_name)
    definition = Util::NetworkDefinition.new(@network_name,
                                             @network.xml_desc)
    @network.destroy if @network.active?
    @network.undefine
  rescue Libvirt::RetrieveError
    # Network doesn't exist, create with defaults
    definition = Util::NetworkDefinition.new(@network_name)
  end
  definition.configure(config)
  @network = @conn.define_network_xml(definition.as_xml)
  @logger.info("Creating network #{@network_name}")
  @network.create
end

#deleteObject



69
70
71
72
73
74
75
76
77
78
79
# File 'lib/vagrant-kvm/driver/driver.rb', line 69

def delete
  domain = @conn.lookup_domain_by_uuid(@uuid)
  definition = Util::VmDefinition.new(domain.xml_desc, 'libvirt')
  volume = @pool.lookup_volume_by_path(definition.disk)
  volume.delete
  # XXX remove pool if empty?
  @pool.refresh
  # remove any saved state
  domain.managed_save_remove if domain.has_managed_save?
  domain.undefine
end

#haltObject

Halts the virtual machine



82
83
84
85
# File 'lib/vagrant-kvm/driver/driver.rb', line 82

def halt
  domain = @conn.lookup_domain_by_uuid(@uuid)
  domain.destroy
end

#import(xml, path) ⇒ String

Imports the VM

Parameters:

  • xml (String)

    Path to the libvirt XML file.

  • path (String)

    Destination path for the volume.

Returns:

  • (String)

    UUID of the imported VM.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/vagrant-kvm/driver/driver.rb', line 92

def import(xml, path)
  @logger.info("Importing VM")
  # create vm definition from xml
  definition = File.open(xml) { |f|
    Util::VmDefinition.new(f.read) }
  # copy volume to storage pool
  box_disk = definition.disk
  new_disk = File.basename(box_disk, File.extname(box_disk)) + "-" +
    Time.now.to_i.to_s + ".img"
  @logger.info("Copying volume #{box_disk} to #{new_disk}")
  old_path = File.join(File.dirname(xml), box_disk)
  new_path = File.join(path, new_disk)
  # we use qemu-img convert to preserve image size
  system("qemu-img convert -p #{old_path} -O raw #{new_path}")
  @pool.refresh
  volume = @pool.lookup_volume_by_name(new_disk)
  definition.disk = volume.path
  # create vm
  @logger.info("Creating new VM")
  domain = @conn.define_domain_xml(definition.as_libvirt)
  domain.uuid
end

#import_ovf(ovf, path) ⇒ String

Imports the VM from an OVF file. XXX should be fusioned with import

Parameters:

  • ovf (String)

    Path to the OVF file.

  • path (String)

    Destination path for the volume.

Returns:

  • (String)

    UUID of the imported VM.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/vagrant-kvm/driver/driver.rb', line 121

def import_ovf(ovf, path)
  @logger.info("Importing OVF definition for VM")
  # create vm definition from ovf
  definition = File.open(ovf) { |f|
    Util::VmDefinition.new(f.read, 'ovf') }
  # copy volume to storage pool
  box_disk = definition.disk
  new_disk = File.basename(box_disk, File.extname(box_disk)) + "-" +
    Time.now.to_i.to_s + ".img"
  @logger.info("Converting volume #{box_disk} to #{new_disk}")
  old_path = File.join(File.dirname(ovf), box_disk)
  new_path = File.join(path, new_disk)
  system("qemu-img convert -p #{old_path} -O raw #{new_path}")
  @pool.refresh
  volume = @pool.lookup_volume_by_name(new_disk)
  definition.disk = volume.path
  # create vm
  @logger.info("Creating new VM")
  domain = @conn.define_domain_xml(definition.as_libvirt)
  domain.uuid
end

#init_storage(base_path) ⇒ Object

Initialize or create storage pool



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/vagrant-kvm/driver/driver.rb', line 163

def init_storage(base_path)
  begin
    # Get the storage pool if it exists
    @pool = @conn.lookup_storage_pool_by_name(@pool_name)
    @logger.info("Init storage pool #{@pool_name}")
  rescue Libvirt::RetrieveError
    # Storage pool doesn't exist so we create it
    # create dir if it doesn't exist
    # if we let libvirt create the dir it is owned by root
    pool_path = base_path.join("storage-pool")
    pool_path.mkpath unless Dir.exists?(pool_path)
    storage_pool_xml = <<-EOF
  <pool type="dir">
    <name>#{@pool_name}</name>
    <target>
      <path>#{pool_path}</path>
    </target>
  </pool>
    EOF
    @pool = @conn.define_storage_pool_xml(storage_pool_xml)
    @pool.build
    @logger.info("Creating storage pool #{@pool_name} in #{pool_path}")
  end
  @pool.create unless @pool.active?
  @pool.refresh
end

#read_network_interfacesHash

Returns a list of network interfaces of the VM.

Returns:

  • (Hash)


193
194
195
196
# File 'lib/vagrant-kvm/driver/driver.rb', line 193

def read_network_interfaces
  domain = @conn.lookup_domain_by_uuid(@uuid)
  Util::VmDefinition.list_interfaces(domain.xml_desc)
end

#read_stateObject



198
199
200
201
202
203
204
205
206
# File 'lib/vagrant-kvm/driver/driver.rb', line 198

def read_state
  domain = @conn.lookup_domain_by_uuid(@uuid)
  state, reason = domain.state
  # check if domain has been saved
  if VM_STATE[state] == :shutoff and domain.has_managed_save?
    return :saved
  end
  VM_STATE[state]
end

#read_versionHash

Return the qemu version

Returns:

  • (Hash)

    with :maj and :min version numbers



211
212
213
214
215
216
217
# File 'lib/vagrant-kvm/driver/driver.rb', line 211

def read_version
  # libvirt returns a number like 1002002 for version 1.2.2
  # we return just the major.minor part like this 1002
  maj = @conn.version / 1000000
  min = (@conn.version - maj*1000000) / 1000
  { :maj => maj, :min => min }
end

#resumeObject

Resumes the previously paused virtual machine.



220
221
222
223
224
225
# File 'lib/vagrant-kvm/driver/driver.rb', line 220

def resume
  @logger.debug("Resuming paused VM...")
  domain = @conn.lookup_domain_by_uuid(@uuid)
  domain.resume
  true
end

#set_mac_address(mac) ⇒ Object



227
228
229
230
231
232
233
# File 'lib/vagrant-kvm/driver/driver.rb', line 227

def set_mac_address(mac)
  domain = @conn.lookup_domain_by_uuid(@uuid)
  definition = Util::VmDefinition.new(domain.xml_desc, 'libvirt')
  definition.set_mac(mac)
  domain.undefine
  @conn.define_domain_xml(definition.as_libvirt)
end

#startObject

Starts the virtual machine.



236
237
238
239
240
# File 'lib/vagrant-kvm/driver/driver.rb', line 236

def start
  domain = @conn.lookup_domain_by_uuid(@uuid)
  domain.create
  true
end

#suspendObject

Suspend the virtual machine and saves its states.



243
244
245
246
# File 'lib/vagrant-kvm/driver/driver.rb', line 243

def suspend
  domain = @conn.lookup_domain_by_uuid(@uuid)
  domain.managed_save
end

#verify!Object

Verifies that the driver is ready and the connection is open

This will raise a VagrantError if things are not ready.



251
252
253
254
255
# File 'lib/vagrant-kvm/driver/driver.rb', line 251

def verify!
  if @conn.closed?
    raise Vagrant::Errors::KvmNoConnection
  end
end

#vm_exists?(uuid) ⇒ Boolean

Checks if a VM with the given UUID exists.

Returns:

  • (Boolean)


260
261
262
263
264
265
266
267
# File 'lib/vagrant-kvm/driver/driver.rb', line 260

def vm_exists?(uuid)
  begin
    @logger.info("Check if VM #{uuid} exists")
    @conn.lookup_domain_by_uuid(uuid)
  rescue Libvirt::RetrieveError
    false
  end
end