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, #reset_relationships, #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, #read_interface_attribute, #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?, #reset_attributes, #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.



255
256
257
258
259
260
# File 'lib/virtualbox/vm.rb', line 255

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


192
193
194
# File 'lib/virtualbox/vm.rb', line 192

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:



200
201
202
203
204
205
206
207
# File 'lib/virtualbox/vm.rb', line 200

def find(name)
  new(Lib.lib.virtualbox.find_machine(name))
rescue Exceptions::ObjectNotFoundException
  nil
rescue Exceptions::COMException
  # NOTE: This only happens on JRuby/Windows
  nil
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



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

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



238
239
240
241
242
243
244
245
246
# File 'lib/virtualbox/vm.rb', line 238

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



230
231
232
# File 'lib/virtualbox/vm.rb', line 230

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



234
235
236
# File 'lib/virtualbox/vm.rb', line 234

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



641
642
643
# File 'lib/virtualbox/vm.rb', line 641

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.



558
559
560
561
562
563
# File 'lib/virtualbox/vm.rb', line 558

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.



576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/virtualbox/vm.rb', line 576

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

  if !media.empty?
    if Platform.windows?
      # The MSCOM interface in CRuby to delete media is broken,
      # so we do this ghettoness. Also, in JRuby, passing an array
      # to objects is broken. So once again, we do this.
      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.



547
548
549
# File 'lib/virtualbox/vm.rb', line 547

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.



457
458
459
460
461
462
# File 'lib/virtualbox/vm.rb', line 457

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.



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/virtualbox/vm.rb', line 380

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.



648
649
650
651
652
653
654
655
# File 'lib/virtualbox/vm.rb', line 648

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



262
263
264
265
266
267
268
269
# File 'lib/virtualbox/vm.rb', line 262

def initialize_attributes(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

#load_attribute(key) ⇒ Object



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

def load_attribute(key)
  read_interface_attribute(key, interface)
end

#load_relationship(name) ⇒ Object



275
276
277
278
# File 'lib/virtualbox/vm.rb', line 275

def load_relationship(name)
  # Load only the requested relationship
  populate_relationship(name, interface)
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.



524
525
526
# File 'lib/virtualbox/vm.rb', line 524

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



627
628
629
# File 'lib/virtualbox/vm.rb', line 627

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



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

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.



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

def reload
  # Reset relationships and attributes
  reset_attributes
  reset_relationships

  # Initialize again
  initialize_attributes(interface)

  self
end

#resumeBoolean

Resume a paused VM.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



531
532
533
# File 'lib/virtualbox/vm.rb', line 531

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:



369
370
371
372
373
374
375
# File 'lib/virtualbox/vm.rb', line 369

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



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

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.



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/virtualbox/vm.rb', line 343

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.



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

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



634
635
636
# File 'lib/virtualbox/vm.rb', line 634

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.



659
660
661
662
663
664
665
666
667
# File 'lib/virtualbox/vm.rb', line 659

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.



507
508
509
# File 'lib/virtualbox/vm.rb', line 507

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.



490
491
492
493
494
495
496
497
498
499
# File 'lib/virtualbox/vm.rb', line 490

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



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

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.



301
302
303
304
305
306
307
308
# File 'lib/virtualbox/vm.rb', line 301

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.



516
517
518
# File 'lib/virtualbox/vm.rb', line 516

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.



473
474
475
476
477
# File 'lib/virtualbox/vm.rb', line 473

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



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/virtualbox/vm.rb', line 311

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.



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
# File 'lib/virtualbox/vm.rb', line 401

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