Class: VirtualBox::VM

Inherits:
AbstractModel show all
Defined in:
lib/virtualbox/vm.rb

Overview

Represents a single VirtualBox virtual machine. All attributes which are not read-only can be modified and saved.

# Finding Virtual Machines

Two methods are used to find virtual machines: VM.all and VM.find. Each return a VM object. An example is shown below:

vm = VirtualBox::VM.find("MyWindowsXP")
puts vm.name # => "MyWindowsXP"

# Modifying Virtual Machines

Virtual machines can be modified a lot like [ActiveRecord](ar.rubyonrails.org/) objects. This is best shown through example:

vm = VirtualBox::VM.find("MyWindowsXP")
vm.memory_size = 256
vm.name = "WindowsXP"
vm.save

# Controlling Virtual Machines

Virtual machines can be controlled using the basic #start, #stop, etc. methods. The current state of the VM can also be retrieved via the #state method. An example of this use is shown below:

if vm.powered_off?
  vm.start
end

# Taking a Snapshot

Snapshots allow virtual machine states to be saved at a given point in time without having to stop the machine. This state can then be restored later. VirtualBox handles this by creating a differencing image which allows the hard drive to even retain its exact state. Taking a snapshot is extremely simple:

vm = VirtualBox::VM.find("MyWindowsXP")
vm.take_snapshot("My Snapshot", "A description of my snapshot")

# Traversing Snapshots

Snapshots are represented by a tree-like structure. There is a root snapshot and that snapshot has many children which may in turn have their own children. The easiest way to traverse this structure is to use the #root_snapshot VM method and traverse the structure like any tree structure:

vm = VirtualBox::VM.find("MyWindowsXP")
p vm.root_snapshot.children.length

# Finding Snapshots

While traversing the entire snapshot tree can be useful, it is often more useful to be able to simply find a snapshot by name. For this, use the #find_snapshot method:

vm = VirtualBox::VM.find("MyWindowsXP")
p vm.find_snapshot("PreSP3")

# Attributes and Relationships

Properties of the virtual machine are exposed using standard ruby instance methods which are generated on the fly. Because of this, they are not listed below as available instance methods.

These attributes can be accessed and modified via standard ruby-style ‘instance.attribute` and `instance.attribute=` methods. The attributes are listed below.

Relationships are also accessed like attributes but can’t be set. Instead, they are typically references to other objects such as a HardDrive which in turn have their own attributes which can be modified.

## Attributes

This is copied directly from the class header, but lists all available attributes. If you don’t understand what this means, read AbstractModel::Attributable.

attribute :uuid, :readonly => true, :property => :id
attribute :name
attribute :os_type_id
attribute :description
attribute :memory_size
attribute :memory_balloon_size
attribute :vram_size
attribute :cpu_count
attribute :accelerate_3d_enabled, :boolean => true
attribute :accelerate_2d_video_enabled, :boolean => true
attribute :clipboard_mode
attribute :monitor_count
attribute :state, :readonly => true
attribute :accessible, :readonly => true, :boolean => true
attribute :hardware_version
attribute :hardware_uuid
attribute :firmware_type
attribute :snapshot_folder
attribute :settings_file_path, :readonly => true
attribute :last_state_change, :readonly => true
attribute :state_file_path, :readonly => true
attribute :log_folder, :readonly => true
attribute :snapshot_count, :readonly => true
attribute :current_state_modified, :readonly => true
attribute :guest_property_notification_patterns
attribute :teleporter_enabled, :boolean => true
attribute :teleporter_port
attribute :teleporter_address
attribute :teleporter_password
attribute :interface, :readonly => true, :property => false

## Relationships

In addition to the basic attributes, a virtual machine is related to other things. The relationships are listed below. If you don’t understand this, read AbstractModel::Relatable.

relationship :audio_adapter, :AudioAdapter
relationship :bios, :BIOS
relationship :hw_virt, :HWVirtualization
relationship :cpu, :CPU
relationship :vrdp_server, :VRDPServer
relationship :storage_controllers, :StorageController, :dependent => :destroy
relationship :medium_attachments, :MediumAttachment
relationship :shared_folders, :SharedFolder
relationship :extra_data, :ExtraData
relationship :forwarded_ports, :ForwardedPort
relationship :network_adapters, :NetworkAdapter
relationship :usb_controller, :USBController
relationship :current_snapshot, :Snapshot

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from AbstractModel

#errors, errors_for_relationship, #existing_record!, #inspect, #lazy_attribute?, #lazy_relationship?, #new_record!, #new_record?, #parent_machine, #populate_attributes, #populate_relationship, #populate_relationships, reload!, #reload!, reload?, reloaded!, #save!, #save_attribute, #save_changed_interface_attributes, #save_interface_attribute, #set_relationship, #write_attribute

Methods included from AbstractModel::Validatable

#__validates_extract_options, #add_error, #clear_errors, #errors, #errors_on, #full_error_messages, #valid?, #validates_format_of, #validates_inclusion_of, #validates_numericality_of, #validates_presence_of

Methods included from AbstractModel::Relatable

#destroy_relationship, #destroy_relationships, #has_relationship?, included, #lazy_relationship?, #loaded_relationship?, #populate_relationship, #populate_relationships, #read_relationship, #relationship_class, #relationship_data, #save_relationship, #save_relationships, #set_relationship

Methods included from AbstractModel::VersionMatcher

#assert_version_match, #split_version, #version_match?

Methods included from AbstractModel::Dirty

#changed?, #changes, #clear_dirty!, #ignore_dirty, #method_missing, #set_dirty!

Methods included from AbstractModel::InterfaceAttributes

#load_interface_attribute, #load_interface_attributes, #save_interface_attribute, #save_interface_attributes, #spec_to_proc

Methods included from AbstractModel::Attributable

#attributes, #has_attribute?, included, #lazy_attribute?, #loaded_attribute?, #populate_attributes, #read_attribute, #readonly_attribute?, #write_attribute

Methods included from Logger

included, #logger, #logger_output=

Constructor Details

#initialize(imachine) ⇒ VM

Creates a new instance of a virtual machine.

**Currently can NOT be used to create a NEW virtual machine**. Support for creating new virtual machines will be added shortly. For now, this is only used by find and all to initialize the VMs.



245
246
247
248
249
250
# File 'lib/virtualbox/vm.rb', line 245

def initialize(imachine)
  super()

  write_attribute(:interface, imachine)
  initialize_attributes(imachine)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class VirtualBox::AbstractModel::Dirty

Class Method Details

.allArray<VM>

Returns an array of all available VMs.

Returns:

  • (Array<VM>)


187
188
189
# File 'lib/virtualbox/vm.rb', line 187

def all
  Global.global(true).vms
end

.find(name) ⇒ VM

Finds a VM by UUID or registered name and returns a new VM object. If the VM doesn’t exist, will return ‘nil`.

Returns:



195
196
197
# File 'lib/virtualbox/vm.rb', line 195

def find(name)
  all.detect { |o| o.name == name || o.uuid == name }
end

.import(source_path, &block) ⇒ VM

Imports a VM, blocking the entire thread during this time. When finished, on success, will return the VM object. This VM object can be used to make any modifications necessary (RAM, cpus, etc.).

If there are multiple VMs in the OVF file being imported, the first virtual machine will be returned, although all will be imported.

If a block is given, it will be yielded with the progress of the import operation, so that the progress of the import can be tracked.

Returns:

  • (VM)

    The newly imported virtual machine



213
214
215
216
217
218
# File 'lib/virtualbox/vm.rb', line 213

def import(source_path, &block)
  appliance = Appliance.new(source_path)
  appliance.import(&block)

  find(appliance.virtual_systems.first.descriptions[:name][:auto])
end

.populate_array_relationship(caller, machines) ⇒ Object



228
229
230
231
232
233
234
235
236
# File 'lib/virtualbox/vm.rb', line 228

def populate_array_relationship(caller, machines)
  result = Proxies::Collection.new(caller)

  machines.each do |machine|
    result << new(machine)
  end

  result
end

.populate_relationship(caller, machines) ⇒ Object



220
221
222
# File 'lib/virtualbox/vm.rb', line 220

def populate_relationship(caller, machines)
  machines.is_a?(Array) ? populate_array_relationship(caller, machines) : populate_single_relationship(caller, machines)
end

.populate_single_relationship(caller, machine) ⇒ Object



224
225
226
# File 'lib/virtualbox/vm.rb', line 224

def populate_single_relationship(caller, machine)
  new(machine)
end

Instance Method Details

#aborted?Boolean

Returns true if the virtual machine state is aborted

Returns:

  • (Boolean)

    True if virtual machine state is aborted



621
622
623
# File 'lib/virtualbox/vm.rb', line 621

def aborted?
  state == :aborted
end

#control(command, *args) ⇒ Boolean

Controls the virtual machine. This method is used by #stop, #pause, #resume, and #save_state to control the virtual machine. Typically, you won’t ever have to call this method and should instead call those.

Parameters:

  • command (String)

    The command to run on controlvm

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



539
540
541
542
543
544
# File 'lib/virtualbox/vm.rb', line 539

def control(command, *args)
  with_open_session(:shared) do |session|
    result = session.console.send(command, *args)
    result.wait if result.is_a?(COM::Util.versioned_interface(:Progress))
  end
end

#destroy(opts = {}) ⇒ Object

Destroys the virtual machine. This method also removes all attached media (required by VirtualBox to destroy a VM). By default, this **will not** destroy attached hard drives, but will if given the ‘destroy_image` option.

Passes options to the destroy method.

Options Hash (opts):

  • :destroy_medium (Boolean) — default: false

    If true, will also unregister attached media. If set to ‘:delete`, it will not only unregister attached media, but will also physically remove their respective data.



557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
# File 'lib/virtualbox/vm.rb', line 557

def destroy(*args)
  # Do a full cleanup on the machine, then delete all the media attached
  media = interface.unregister(:full)

  if !media.empty?
    if Platform.windows? && !Platform.jruby?
      # The MSCOM interface in CRuby to delete media is broken,
      # so we do this ghettoness.
      path = interface.settings_file_path

      # A simple sanity check to make sure we don't attempt to delete
      # the root or something
      if path.length > 10
        Pathname.new(path).parent.rmtree
      end
    else
      interface.delete(media)

      # TODO: This sleep is silly. The progress object returned by the media
      # delete always fails to "wait" for some reason, so I do this. I hope
      # to solve this issue soon.
      sleep 1
    end
  end
end

#discard_stateBoolean

Discards any saved state on the current VM. The VM is not destroyed though and can still be started by calling #start.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



528
529
530
# File 'lib/virtualbox/vm.rb', line 528

def discard_state
  control(:discard_saved_state, true)
end

#export(filename, options = {}, &block) ⇒ Object

Exports a virtual machine. The virtual machine will be exported to the specified OVF file name. This directory will also have the ‘mf` file which contains the file checksums and also the virtual drives of the machine.

Export also supports an additional options hash which can contain information that will be embedded with the virtual machine. View below for more information on the available options.

This method will block until the export is complete, which takes about 60 to 90 seconds on my 2.2 GHz 2009 model MacBook Pro.

If a block is given to the method, then it will be yielded with the progress of the operation.

Parameters:

  • filename (String)

    The file (not directory) to save the exported OVF file. This directory will also receive the checksum file and virtual disks.



438
439
440
441
442
443
# File 'lib/virtualbox/vm.rb', line 438

def export(filename, options = {}, &block)
  app = Appliance.new
  app.path = filename
  app.add_machine(self, options)
  app.export(&block)
end

#find_snapshot(name) ⇒ Object

Find a snapshot by name or UUID. This allows you to find a snapshot by a given name, rather than having to resort to traversing the entire tree structure manually.



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/virtualbox/vm.rb', line 361

def find_snapshot(name)
  find_helper = lambda do |name, root|
    return nil if root.nil?
    return root if root.name == name || root.uuid == name

    root.children.each do |child|
      result = find_helper.call(name, child)
      return result unless result.nil?
    end

    nil
  end

  find_helper.call(name, root_snapshot)
end

#get_boot_order(interface, key) ⇒ Object

Loads the boot order for this virtual machine. This method should never be called directly. Instead, use the ‘boot_order` attribute to read and modify the boot order.



628
629
630
631
632
633
634
635
# File 'lib/virtualbox/vm.rb', line 628

def get_boot_order(interface, key)
  max_boot = Global.global.system_properties.max_boot_position

  (1..max_boot).inject([]) do |order, position|
    order << interface.get_boot_order(position)
    order
  end
end

#initialize_attributes(machine) ⇒ Object



252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/virtualbox/vm.rb', line 252

def initialize_attributes(machine)
  # Load the interface attributes
  load_interface_attributes(machine)

  # Setup the relationships
  populate_relationships(machine)

  # Clear dirtiness, since this should only be called initially and
  # therefore shouldn't affect dirtiness
  clear_dirty!

  # But this is an existing record
  existing_record!
end

#pauseBoolean

Pauses the VM, putting it on hold temporarily. The VM can be resumed again by calling #resume

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



505
506
507
# File 'lib/virtualbox/vm.rb', line 505

def pause
  control(:pause)
end

#paused?Boolean

Returns true if the virtual machine state is paused

Returns:

  • (Boolean)

    True if virtual machine state is paused



607
608
609
# File 'lib/virtualbox/vm.rb', line 607

def paused?
  state == :paused
end

#powered_off?Boolean

Returns true if the virtual machine state is powered off

Returns:

  • (Boolean)

    True if virtual machine state is powered off



600
601
602
# File 'lib/virtualbox/vm.rb', line 600

def powered_off?
  state == :powered_off
end

#reloadObject

Reload the model so that all the attributes and relationships are up to date. This method will automatically discard any changes to the VM and any of its relationships.



270
271
272
273
# File 'lib/virtualbox/vm.rb', line 270

def reload
  initialize_attributes(interface)
  self
end

#resumeBoolean

Resume a paused VM.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



512
513
514
# File 'lib/virtualbox/vm.rb', line 512

def resume
  control(:resume)
end

#root_snapshotSnapshot

Returns the root snapshot of this virtual machine. This root snapshot can be used to traverse the tree of snapshots.

Returns:



350
351
352
353
354
355
356
# File 'lib/virtualbox/vm.rb', line 350

def root_snapshot
  return nil if current_snapshot.nil?

  current = current_snapshot
  current = current.parent while current.parent != nil
  current
end

#running?Boolean

Returns true if the virtual machine state is running

Returns:

  • (Boolean)

    True if virtual machine state is running



593
594
595
# File 'lib/virtualbox/vm.rb', line 593

def running?
  state == :running
end

#saveObject

Saves the virtual machine if modified. This method saves any modified attributes of the virtual machine. If any related attributes were saved as well (such as storage controllers), those will be saved, too.



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/virtualbox/vm.rb', line 324

def save
  return false unless valid?
  raise Exceptions::ReadonlyVMStateException.new("VM must not be in saved state to modify.") if saved?

  with_open_session do |session|
    # Use setters to save the attributes on the locked machine and persist
    # the settings
    machine = session.machine

    # Save the boot order
    save_interface_attribute(:boot_order, machine)

    # Save all the attributes and relationships
    save_changed_interface_attributes(machine)

    # Save relationships, which may open their own sessions if necessary
    save_relationships
  end

  true
end

#save_stateBoolean

Saves the state of a VM and stops it. The VM can be resumed again by calling “#start” again.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



520
521
522
# File 'lib/virtualbox/vm.rb', line 520

def save_state
  control(:save_state)
end

#saved?Boolean

Returns true if the virtual machine state is saved

Returns:

  • (Boolean)

    True if virtual machine state is saved



614
615
616
# File 'lib/virtualbox/vm.rb', line 614

def saved?
  state == :saved
end

#set_boot_order(interface, key, value) ⇒ Object

Sets the boot order for this virtual machine. This method should never be called directly. Instead, modify the ‘boot_order` array.



639
640
641
642
643
644
645
646
647
# File 'lib/virtualbox/vm.rb', line 639

def set_boot_order(interface, key, value)
  max_boot = Global.global.system_properties.max_boot_position
  value = value.dup
  value.concat(Array.new(max_boot - value.size)) if value.size < max_boot

  (1..max_boot).each do |position|
    interface.set_boot_order(position, value[position - 1])
  end
end

#shutdownBoolean

Shuts down the VM by directly calling “acpipowerbutton”. Depending on the settings of the Virtual Machine, this may not work. For example, some linux installations don’t respond to the ACPI power button at all. In such cases, #stop or #save_state may be used instead.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



488
489
490
# File 'lib/virtualbox/vm.rb', line 488

def shutdown
  control(:power_button)
end

#start(mode = "gui") ⇒ Boolean

Starts the virtual machine. The virtual machine can be started in a variety of modes:

  • gui – The VirtualBox GUI will open with the screen of the VM.

  • vrdp – The VM will run in the background. No GUI will be present at all.

This method blocks while the external processes are starting.

Parameters:

  • mode (Symbol) (defaults to: "gui")

    Described above.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



471
472
473
474
475
476
477
478
479
480
# File 'lib/virtualbox/vm.rb', line 471

def start(mode="gui")
  return false if running?

  # Open a new remote session, this will automatically start the machine
  # as well
  session = Lib.lib.session
  interface.launch_vm_process(session, mode.to_s, "").wait
  session.unlock_machine if session.state == :open
  true
end

#starting?Boolean

Returns true if the virtual machine state is starting

Returns:

  • (Boolean)

    True if virtual machine state is starting



586
587
588
# File 'lib/virtualbox/vm.rb', line 586

def starting?
  state == :starting
end

#state(suppress_state_reload = false) ⇒ String

State of the virtual machine. Returns the state of the virtual machine. This state will represent the state that was assigned when the VM was found unless ‘reload` is set to `true`.

Parameters:

  • reload (Boolean)

    If true, will reload the state to current value.

Returns:

  • (String)

    Virtual machine state.



282
283
284
285
286
287
288
289
# File 'lib/virtualbox/vm.rb', line 282

def state(suppress_state_reload=false)
  if !suppress_state_reload
    load_interface_attribute(:state, interface)
    clear_dirty!(:state)
  end

  read_attribute(:state)
end

#stopBoolean

Stops the VM by directly calling “poweroff.” Immediately halts the virtual machine without saving state. This could result in a loss of data. To prevent data loss, see #shutdown

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



497
498
499
# File 'lib/virtualbox/vm.rb', line 497

def stop
  control(:power_down)
end

#take_snapshot(name, description = "", &block) ⇒ Object

Take a snapshot of the current state of the machine. This method can be called while the VM is running and also while it is powered off. This method will block while the snapshot is being taken.

If a block is given to this method, it will yield with a progress object which can be used to get the progress of the operation.

Parameters:

  • name (String)

    Name of the snapshot.

  • description (String) (defaults to: "")

    Description of the snapshot.



454
455
456
457
458
# File 'lib/virtualbox/vm.rb', line 454

def take_snapshot(name, description="", &block)
  with_open_session do |session|
    session.console.take_snapshot(name, description).wait(&block)
  end
end

#validateObject

Validates the virtual machine



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/virtualbox/vm.rb', line 292

def validate
  super

  validates_presence_of :name, :os_type_id, :memory_size, :vram_size, :cpu_count
  validates_numericality_of :memory_balloon_size, :monitor_count
  validates_inclusion_of :accelerate_3d_enabled, :accelerate_2d_video_enabled, :teleporter_enabled, :in => [true, false]

  if !errors_on(:name)
    # Only validate the name if the name has no errors already
    vms_of_same_name = self.class.find(name)
    add_error(:name, 'must not be used by another virtual machine.') if vms_of_same_name && vms_of_same_name.uuid != uuid
  end

  validates_inclusion_of :os_type_id, :in => VirtualBox::Global.global.lib.virtualbox.guest_os_types.collect { |os| os.id }

  min_guest_ram, max_guest_ram = Global.global.system_properties.min_guest_ram, Global.global.system_properties.max_guest_ram
  validates_inclusion_of :memory_size, :in => (min_guest_ram..max_guest_ram), :message => "must be between #{min_guest_ram} and #{max_guest_ram}."
  validates_inclusion_of :memory_balloon_size, :in => (0..max_guest_ram), :message => "must be between 0 and #{max_guest_ram}."

  min_guest_vram, max_guest_vram = Global.global.system_properties.min_guest_vram, Global.global.system_properties.max_guest_vram
  validates_inclusion_of :vram_size, :in => (min_guest_vram..max_guest_vram), :message => "must be between #{min_guest_vram} and #{max_guest_vram}."

  min_guest_cpu_count, max_guest_cpu_count = Global.global.system_properties.min_guest_cpu_count, Global.global.system_properties.max_guest_cpu_count
  validates_inclusion_of :cpu_count, :in => (min_guest_cpu_count..max_guest_cpu_count), :message => "must be between #{min_guest_cpu_count} and #{max_guest_cpu_count}."

  validates_inclusion_of :clipboard_mode, :in => COM::Util.versioned_interface(:ClipboardMode).map
  validates_inclusion_of :firmware_type, :in => COM::Util.versioned_interface(:FirmwareType).map
end

#with_open_session(mode = :write) ⇒ Object

Opens a direct session with the machine this VM represents and yields the session object to a block. Many of the VirtualBox’s settings can only be modified with an open session on a machine. An open session is similar to a write-lock. Once the session is completed, it must be closed, which this method does as well.



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/virtualbox/vm.rb', line 382

def with_open_session(mode=:write)
  # Set the session up
  session = Lib.lib.session

  close_session = false

  if session.state != :open
    # Open up a session for this virtual machine
    interface.lock_machine(session, mode)

    # Mark the session to be closed
    close_session = true
  end

  # Yield the block with the session
  yield session if block_given?

  # Close the session
  if close_session
    # Save these settings only if we're closing and only if the state
    # is not saved, since that doesn't allow the machine to be saved.
    session.machine.save_settings if mode == :write && session.machine.state != :saved

    # Close the session
    session.unlock_machine
  end
rescue Exception
  # Close the session so we don't get locked out. We use a rescue block
  # here instead of an "ensure" since we ONLY want this to occur if an
  # exception is raised. Otherwise, we may or may not close the session,
  # depending how deeply nested this call to `with_open_session` is.
  # (see close_session boolean above)
  session.unlock_machine if session.state == :open

  # Reraise the exception, we're not actually catching it to handle it
  raise
end