virtualbox 0.5.4 → 0.6.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 (162) hide show
  1. data/.gitignore +2 -1
  2. data/Gemfile +1 -1
  3. data/Rakefile +1 -1
  4. data/Readme.md +5 -21
  5. data/VERSION +1 -1
  6. data/docs/WhatsNew.md +9 -47
  7. data/lib/virtualbox.rb +7 -30
  8. data/lib/virtualbox/abstract_model.rb +25 -5
  9. data/lib/virtualbox/abstract_model/attributable.rb +5 -1
  10. data/lib/virtualbox/abstract_model/dirty.rb +2 -0
  11. data/lib/virtualbox/abstract_model/interface_attributes.rb +96 -0
  12. data/lib/virtualbox/abstract_model/relatable.rb +19 -8
  13. data/lib/virtualbox/appliance.rb +59 -0
  14. data/lib/virtualbox/audio_adapter.rb +44 -0
  15. data/lib/virtualbox/bios.rb +44 -0
  16. data/lib/virtualbox/com.rb +23 -0
  17. data/lib/virtualbox/com/abstract_enum.rb +42 -0
  18. data/lib/virtualbox/com/abstract_implementer.rb +43 -0
  19. data/lib/virtualbox/com/abstract_interface.rb +165 -0
  20. data/lib/virtualbox/com/ffi/interface.rb +141 -0
  21. data/lib/virtualbox/com/ffi/interfaces.rb +42 -0
  22. data/lib/virtualbox/com/ffi/util.rb +101 -0
  23. data/lib/virtualbox/com/ffi/vboxxpcomc.rb +31 -0
  24. data/lib/virtualbox/com/ffi_interface.rb +65 -0
  25. data/lib/virtualbox/com/implementer/base.rb +52 -0
  26. data/lib/virtualbox/com/implementer/ffi.rb +350 -0
  27. data/lib/virtualbox/com/implementer/mscom.rb +165 -0
  28. data/lib/virtualbox/com/implementer/nil.rb +10 -0
  29. data/lib/virtualbox/com/interface/appliance.rb +20 -0
  30. data/lib/virtualbox/com/interface/audio_adapter.rb +13 -0
  31. data/lib/virtualbox/com/interface/audio_controller_type.rb +9 -0
  32. data/lib/virtualbox/com/interface/audio_driver_type.rb +9 -0
  33. data/lib/virtualbox/com/interface/bios_boot_menu_mode.rb +9 -0
  34. data/lib/virtualbox/com/interface/bios_settings.rb +19 -0
  35. data/lib/virtualbox/com/interface/clipboard_mode.rb +9 -0
  36. data/lib/virtualbox/com/interface/console.rb +48 -0
  37. data/lib/virtualbox/com/interface/cpu_property_type.rb +9 -0
  38. data/lib/virtualbox/com/interface/device_type.rb +9 -0
  39. data/lib/virtualbox/com/interface/dhcp_server.rb +20 -0
  40. data/lib/virtualbox/com/interface/firmware_type.rb +9 -0
  41. data/lib/virtualbox/com/interface/guest_os_type.rb +21 -0
  42. data/lib/virtualbox/com/interface/host.rb +40 -0
  43. data/lib/virtualbox/com/interface/host_network_interface.rb +28 -0
  44. data/lib/virtualbox/com/interface/host_network_interface_medium_type.rb +9 -0
  45. data/lib/virtualbox/com/interface/host_network_interface_status.rb +9 -0
  46. data/lib/virtualbox/com/interface/host_network_interface_type.rb +9 -0
  47. data/lib/virtualbox/com/interface/host_usb_device.rb +11 -0
  48. data/lib/virtualbox/com/interface/host_usb_device_filter.rb +11 -0
  49. data/lib/virtualbox/com/interface/hw_virt_ex_property_type.rb +9 -0
  50. data/lib/virtualbox/com/interface/machine.rb +103 -0
  51. data/lib/virtualbox/com/interface/machine_state.rb +12 -0
  52. data/lib/virtualbox/com/interface/medium.rb +48 -0
  53. data/lib/virtualbox/com/interface/medium_attachment.rb +16 -0
  54. data/lib/virtualbox/com/interface/medium_format.rb +16 -0
  55. data/lib/virtualbox/com/interface/medium_state.rb +9 -0
  56. data/lib/virtualbox/com/interface/medium_type.rb +9 -0
  57. data/lib/virtualbox/com/interface/medium_variant.rb +9 -0
  58. data/lib/virtualbox/com/interface/network_adapter.rb +28 -0
  59. data/lib/virtualbox/com/interface/network_adapter_type.rb +9 -0
  60. data/lib/virtualbox/com/interface/network_attachment_type.rb +9 -0
  61. data/lib/virtualbox/com/interface/nsiexception.rb +21 -0
  62. data/lib/virtualbox/com/interface/nsisupports.rb +13 -0
  63. data/lib/virtualbox/com/interface/parallel_port.rb +15 -0
  64. data/lib/virtualbox/com/interface/port_mode.rb +9 -0
  65. data/lib/virtualbox/com/interface/progress.rb +58 -0
  66. data/lib/virtualbox/com/interface/serial_port.rb +17 -0
  67. data/lib/virtualbox/com/interface/session.rb +16 -0
  68. data/lib/virtualbox/com/interface/session_state.rb +9 -0
  69. data/lib/virtualbox/com/interface/session_type.rb +9 -0
  70. data/lib/virtualbox/com/interface/shared_folder.rb +15 -0
  71. data/lib/virtualbox/com/interface/snapshot.rb +18 -0
  72. data/lib/virtualbox/com/interface/storage_bus.rb +9 -0
  73. data/lib/virtualbox/com/interface/storage_controller.rb +21 -0
  74. data/lib/virtualbox/com/interface/storage_controller_type.rb +9 -0
  75. data/lib/virtualbox/com/interface/system_properties.rb +35 -0
  76. data/lib/virtualbox/com/interface/usb_controller.rb +18 -0
  77. data/lib/virtualbox/com/interface/usb_device.rb +22 -0
  78. data/lib/virtualbox/com/interface/usb_device_filter.rb +21 -0
  79. data/lib/virtualbox/com/interface/usb_device_filter_action.rb +9 -0
  80. data/lib/virtualbox/com/interface/usb_device_state.rb +9 -0
  81. data/lib/virtualbox/com/interface/virtual_box_error_info.rb +15 -0
  82. data/lib/virtualbox/com/interface/virtual_system_description.rb +17 -0
  83. data/lib/virtualbox/com/interface/virtual_system_description_type.rb +12 -0
  84. data/lib/virtualbox/com/interface/virtual_system_description_value_type.rb +9 -0
  85. data/lib/virtualbox/com/interface/virtualbox.rb +54 -0
  86. data/lib/virtualbox/com/interface/vrdp_auth_type.rb +9 -0
  87. data/lib/virtualbox/com/interface/vrdp_server.rb +17 -0
  88. data/lib/virtualbox/com/mscom_interface.rb +22 -0
  89. data/lib/virtualbox/com/util.rb +18 -0
  90. data/lib/virtualbox/dvd.rb +7 -94
  91. data/lib/virtualbox/exceptions.rb +24 -0
  92. data/lib/virtualbox/ext/glob_loader.rb +22 -0
  93. data/lib/virtualbox/ext/logger.rb +38 -0
  94. data/lib/virtualbox/ext/platform.rb +1 -1
  95. data/lib/virtualbox/extra_data.rb +25 -37
  96. data/lib/virtualbox/forwarded_port.rb +35 -13
  97. data/lib/virtualbox/global.rb +22 -80
  98. data/lib/virtualbox/hard_drive.rb +30 -97
  99. data/lib/virtualbox/lib.rb +82 -0
  100. data/lib/virtualbox/media.rb +7 -6
  101. data/lib/virtualbox/medium.rb +138 -0
  102. data/lib/virtualbox/medium_attachment.rb +61 -0
  103. data/lib/virtualbox/network_adapter.rb +134 -0
  104. data/lib/virtualbox/shared_folder.rb +53 -78
  105. data/lib/virtualbox/storage_controller.rb +76 -20
  106. data/lib/virtualbox/system_properties.rb +74 -0
  107. data/lib/virtualbox/usb_controller.rb +55 -0
  108. data/lib/virtualbox/version.rb +15 -0
  109. data/lib/virtualbox/virtual_system_description.rb +47 -0
  110. data/lib/virtualbox/vm.rb +160 -272
  111. data/test/test_helper.rb +0 -108
  112. data/test/virtualbox/abstract_model/attributable_test.rb +7 -1
  113. data/test/virtualbox/abstract_model/dirty_test.rb +1 -1
  114. data/test/virtualbox/abstract_model/interface_attributes_test.rb +169 -0
  115. data/test/virtualbox/abstract_model/relatable_test.rb +20 -0
  116. data/test/virtualbox/abstract_model_test.rb +40 -5
  117. data/test/virtualbox/appliance_test.rb +152 -0
  118. data/test/virtualbox/audio_adapter_test.rb +83 -0
  119. data/test/virtualbox/bios_test.rb +83 -0
  120. data/test/virtualbox/com/abstract_enum_test.rb +48 -0
  121. data/test/virtualbox/com/abstract_implementer_test.rb +39 -0
  122. data/test/virtualbox/com/abstract_interface_test.rb +139 -0
  123. data/test/virtualbox/com/ffi/interface_test.rb +249 -0
  124. data/test/virtualbox/com/ffi/util_test.rb +86 -0
  125. data/test/virtualbox/com/ffi_interface_test.rb +42 -0
  126. data/test/virtualbox/com/implementer/base_test.rb +37 -0
  127. data/test/virtualbox/com/implementer/ffi_test.rb +519 -0
  128. data/test/virtualbox/com/implementer/mscom_test.rb +208 -0
  129. data/test/virtualbox/com/mscom_interface_test.rb +17 -0
  130. data/test/virtualbox/com/util_test.rb +17 -0
  131. data/test/virtualbox/dvd_test.rb +4 -95
  132. data/test/virtualbox/ext/platform_test.rb +8 -0
  133. data/test/virtualbox/extra_data_test.rb +78 -102
  134. data/test/virtualbox/forwarded_port_test.rb +57 -7
  135. data/test/virtualbox/global_test.rb +25 -115
  136. data/test/virtualbox/hard_drive_test.rb +49 -212
  137. data/test/virtualbox/lib_test.rb +93 -0
  138. data/test/virtualbox/medium_attachment_test.rb +147 -0
  139. data/test/virtualbox/medium_test.rb +192 -0
  140. data/test/virtualbox/network_adapter_test.rb +160 -0
  141. data/test/virtualbox/shared_folder_test.rb +144 -160
  142. data/test/virtualbox/storage_controller_test.rb +166 -45
  143. data/test/virtualbox/system_properties_test.rb +87 -0
  144. data/test/virtualbox/usb_controller_test.rb +104 -0
  145. data/test/virtualbox/version_test.rb +34 -0
  146. data/test/virtualbox/virtual_system_description_test.rb +61 -0
  147. data/test/virtualbox/vm_test.rb +288 -322
  148. data/test/virtualbox_test.rb +1 -9
  149. data/virtualbox.gemspec +139 -23
  150. metadata +143 -27
  151. data/lib/virtualbox/attached_device.rb +0 -249
  152. data/lib/virtualbox/command.rb +0 -109
  153. data/lib/virtualbox/image.rb +0 -137
  154. data/lib/virtualbox/nic.rb +0 -111
  155. data/lib/virtualbox/system_property.rb +0 -55
  156. data/lib/virtualbox/usb.rb +0 -72
  157. data/test/virtualbox/attached_device_test.rb +0 -303
  158. data/test/virtualbox/command_test.rb +0 -152
  159. data/test/virtualbox/image_test.rb +0 -190
  160. data/test/virtualbox/nic_test.rb +0 -76
  161. data/test/virtualbox/system_property_test.rb +0 -71
  162. data/test/virtualbox/usb_test.rb +0 -35
data/.gitignore CHANGED
@@ -3,4 +3,5 @@ doc/*
3
3
  pkg/*
4
4
  test/coverage/*
5
5
  .bundle/*
6
- Gemfile.lock
6
+ Gemfile.lock
7
+ test.rb
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source :gemcutter
2
2
 
3
3
  # External Dependencies
4
- gem "nokogiri", "1.4.1"
4
+ gem "ffi"
5
5
 
6
6
  # Gems required for testing only.
7
7
  group :test do
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ begin
9
9
  gemspec.authors = ["Mitchell Hashimoto"]
10
10
  gemspec.executables = []
11
11
 
12
- gemspec.add_dependency('nokogiri', '>= 1.4.1')
12
+ gemspec.add_dependency('ffi', '>= 0.6.3')
13
13
  end
14
14
  Jeweler::GemcutterTasks.new
15
15
  rescue LoadError
data/Readme.md CHANGED
@@ -11,10 +11,9 @@ Windows, Linux, and OS X. After installation, install the gem:
11
11
 
12
12
  sudo gem install virtualbox
13
13
 
14
- The gem assumes that `VBoxManage` will be available on the `PATH`. If not, before using
15
- the gem, you must set the path to your `VBoxManage` binary:
16
-
17
- VirtualBox::Command.vboxmanage = "/path/to/my/VBoxManage"
14
+ The gem uses the native COM interface with VirtualBox provides to communicate with
15
+ VirtualBox. On Windows, this is globally available. On Linux-based machines, the gem
16
+ uses Ruby-FFI to talk to a dynamic library. No configuration should be necessary.
18
17
 
19
18
  ## Basic Usage
20
19
 
@@ -33,30 +32,15 @@ Below are some examples:
33
32
  vm = VirtualBox::VM.find("my-vm")
34
33
 
35
34
  # Let's first print out some basic info about the VM
36
- puts "Memory: #{vm.memory}"
37
-
38
- vm.storage_controllers.each do |sc|
39
- sc.attached_devices.each do |device|
40
- puts "Attached Device: #{device.uuid}"
41
- end
42
- end
35
+ puts "Memory: #{vm.memory_size}"
43
36
 
44
37
  # Let's modify the memory and name...
45
- vm.memory = 360
38
+ vm.memory_size = 360
46
39
  vm.name = "my-renamed-vm"
47
40
 
48
41
  # Save it!
49
42
  vm.save
50
43
 
51
- Or here is an example of creating a hard drive:
52
-
53
- require 'virtualbox'
54
-
55
- hd = VirtualBox::HardDrive.new
56
- hd.location = "foo.vdi"
57
- hd.size = 2000 # megabytes
58
- hd.save
59
-
60
44
  ## Known Issues or Uncompleted Features
61
45
 
62
46
  VirtualBox has a _ton_ of features! As such, this gem is still not totally complete.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.4
1
+ 0.6.0
data/docs/WhatsNew.md CHANGED
@@ -1,50 +1,12 @@
1
- # What's New in 0.5.x?
1
+ # What's New in 0.6.x?
2
2
 
3
- ## HUGE Speed Boost! Very few system calls!
3
+ ## Native Interface
4
4
 
5
- Most of the data retrieved by the virtualbox library now comes via XML parsing, rather
6
- than making calls to `VBoxManage`. This results in a drastic speedup. The few relationships or
7
- attributes which require a system call are typically _lazy loaded_ (covered below), so they
8
- don't incur a performance penalty unless they're used.
5
+ The VirtualBox gem no longer piggy-backs on top of `VBoxManage` or XML configuration files.
6
+ The gem now uses the native interface provided by VirtualBox to communicate. The result of
7
+ this is a _huge_ speed increase which simply would not have been possible otherwise, and
8
+ stability across versions. In addition to that, the entire VirtualBox API is now at your
9
+ disposal. While the gem itself doesn't yet support creating VMs and so on, the API is available
10
+ for you to do it manually (if you really wanted to!).
9
11
 
10
- The one caveat is that you now need to set the path to the global VirtualBox configuration
11
- XML. The virtualbox library will do its best to guess this path based on the operating
12
- system, but this is hardly foolproof. To set the virtualbox config path, it is a simple
13
- one-liner:
14
-
15
- # Remember, this won't be necessary MOST of the time
16
- VirtualBox::Global.vboxconfig = "~/path/to/VirtualBox.xml"
17
-
18
- ## Lazy Loading of Attributes and Relationships
19
-
20
- Although still not widely used (will be in future patch releases), some attributes and
21
- relationships are now _lazy loaded_. This means that since they're probably expensive
22
- to load (many system calls, heavy parsing, etc.) they aren't loaded initially. Instead,
23
- they are only loaded if they're used. This means that you don't incur the penalty cost
24
- of loading them unless you use it! Fantastic!
25
-
26
- There is no real "example code" for this feature since to the casual user, it happens
27
- transparently in the background and generally "just works." If you're _really_ curious,
28
- then feel free to check out any class which derives from {VirtualBox::AbstractModel}
29
- and any attribute or relationship with the `:lazy => true` option is lazy loaded!
30
-
31
- ## System Properties
32
-
33
- A small but meaningful update is the ability to view the system properties for the
34
- host system which VirtualBox is running. This is done via the {VirtualBox::SystemProperty}
35
- class, which is simply a `Hash`. System properties are immutable properties defined
36
- by the host system, which typically are limits imposed upon VirtualBox, such as
37
- maximum RAM size or default path to machine files. Retrieving the system properties
38
- is quite easy:
39
-
40
- properties = VirtualBox::SystemProperty.all
41
- properties.each do |key, value|
42
- puts "#{key} = #{value}"
43
- end
44
-
45
- ## USB Device Relationship on VMs
46
-
47
- Previously, {VirtualBox::VM VM} object would only be able to tell you if there
48
- were USB devices enabled or not. Now, `usbs` is a full-fledged relationship
49
- on VM. This relationship is access just like any other. For more information
50
- view the {VirtualBox::USB USB} class.
12
+ Future versions will support more and more of the API.
data/lib/virtualbox.rb CHANGED
@@ -1,34 +1,11 @@
1
- $:.unshift(File.expand_path(File.dirname(__FILE__)))
1
+ # Load the glob loader, which will handle the loading of all the other files
2
+ libdir = File.join(File.dirname(__FILE__), "virtualbox")
3
+ require File.expand_path("ext/glob_loader", libdir)
2
4
 
3
- # External Dependencies
4
- require 'nokogiri'
5
-
6
- # Internal Dependencies
7
- require 'virtualbox/ext/platform'
8
- require 'virtualbox/exceptions'
9
- require 'virtualbox/command'
10
- require 'virtualbox/abstract_model'
11
- require 'virtualbox/proxies/collection'
12
- require 'virtualbox/image'
13
- require 'virtualbox/attached_device'
14
- require 'virtualbox/dvd'
15
- require 'virtualbox/extra_data'
16
- require 'virtualbox/forwarded_port'
17
- require 'virtualbox/hard_drive'
18
- require 'virtualbox/nic'
19
- require 'virtualbox/usb'
20
- require 'virtualbox/shared_folder'
21
- require 'virtualbox/storage_controller'
22
- require 'virtualbox/vm'
23
- require 'virtualbox/media'
24
- require 'virtualbox/global'
25
- require 'virtualbox/system_property'
5
+ # Load them up
6
+ VirtualBox::GlobLoader.glob_require(libdir, %w{ext/logger ext/platform ext/subclass_listing com abstract_model medium})
26
7
 
8
+ # Setup the top-level module methods
27
9
  module VirtualBox
28
- class <<self
29
- # Returns installed VirtualBox version like '3.1.2r56127'.
30
- def version
31
- Command.vboxmanage("-v").chomp.strip
32
- end
33
- end
10
+ extend Version
34
11
  end
@@ -1,7 +1,10 @@
1
- require 'virtualbox/abstract_model/attributable'
2
- require 'virtualbox/abstract_model/dirty'
3
- require 'virtualbox/abstract_model/relatable'
4
- require 'virtualbox/abstract_model/validatable'
1
+ ['abstract_model/attributable',
2
+ 'abstract_model/interface_attributes',
3
+ 'abstract_model/dirty',
4
+ 'abstract_model/relatable',
5
+ 'abstract_model/validatable'].each do |lib|
6
+ require File.expand_path(lib, File.dirname(__FILE__))
7
+ end
5
8
 
6
9
  module VirtualBox
7
10
  # AbstractModel is the base class used for most of virtualbox's classes.
@@ -9,7 +12,10 @@ module VirtualBox
9
12
  #
10
13
  # @abstract
11
14
  class AbstractModel
15
+ include Logger
16
+
12
17
  include Attributable
18
+ include InterfaceAttributes
13
19
  include Dirty
14
20
  include Relatable
15
21
  include Validatable
@@ -122,6 +128,20 @@ module VirtualBox
122
128
  clear_dirty!(key)
123
129
  end
124
130
 
131
+ # Saves only changed interface attributes.
132
+ def save_changed_interface_attributes(interface)
133
+ changes.each do |key, values|
134
+ save_interface_attribute(key, interface)
135
+ end
136
+ end
137
+
138
+ # Overrides {InterfaceAttributes.save_interface_attribute} to clear the
139
+ # dirty state of the attribute.
140
+ def save_interface_attribute(key, interface)
141
+ super
142
+ clear_dirty!(key)
143
+ end
144
+
125
145
  # Overriding {Attributable#lazy_attribute?} to always return false for
126
146
  # new records, since new records shouldn't load lazy data.
127
147
  def lazy_attribute?(*args)
@@ -217,7 +237,7 @@ module VirtualBox
217
237
 
218
238
  self.class.attributes.each do |name, options|
219
239
  value = read_attribute(name)
220
- value = if value.is_a?(AbstractModel)
240
+ value = if value.is_a?(AbstractModel) || value.is_a?(COM::AbstractInterface)
221
241
  "#<#{value.class.name}>"
222
242
  else
223
243
  value.inspect
@@ -214,7 +214,11 @@ module VirtualBox
214
214
  load_attribute(name.to_sym)
215
215
  end
216
216
 
217
- attributes[name] || self.class.attributes[name][:default]
217
+ if attributes[name].nil?
218
+ self.class.attributes[name][:default]
219
+ else
220
+ attributes[name]
221
+ end
218
222
  end
219
223
  end
220
224
 
@@ -93,6 +93,8 @@ module VirtualBox
93
93
  # @param [Object] value The new value being set
94
94
  def set_dirty!(name, current, value)
95
95
  if current != value
96
+ name = name.to_sym
97
+
96
98
  # If its the first time this attribute has changed, store the
97
99
  # original value in the first field
98
100
  changes[name] ||= [current, nil]
@@ -0,0 +1,96 @@
1
+ module VirtualBox
2
+ class AbstractModel
3
+ # Module which can be included which defines helper methods to DRY out the
4
+ # code which handles attributes with {VirtualBox::COM} interfaces. This
5
+ # module works _alongside_ the {Attributable} module, so **both are required**.
6
+ module InterfaceAttributes
7
+ # Loads the attributes which have an interface getter and writes
8
+ # their values.
9
+ #
10
+ # @param [VirtualBox::COM::Interface] interface
11
+ def load_interface_attributes(interface)
12
+ self.class.attributes.each do |key, options|
13
+ load_interface_attribute(key, interface)
14
+ end
15
+ end
16
+
17
+ # Loads a single interface attribute.
18
+ #
19
+ # @param [Symbol] key The attribute to load
20
+ # @param [VirtualBox::COM::Interface] interface The interface
21
+ def load_interface_attribute(key, interface)
22
+ # Return unless we have a valid interface attribute with a getter
23
+ return unless has_attribute?(key)
24
+ options = self.class.attributes[key.to_sym]
25
+ return if options.has_key?(:property) && !options[:property]
26
+ getter = options[:property] || options[:property_getter] || key.to_sym
27
+ return unless getter
28
+
29
+ # Convert the getter to a proc and call it
30
+ getter = spec_to_proc(getter)
31
+ write_attribute(key, getter.call(interface))
32
+ end
33
+
34
+ # Saves all the attributes which have an interface setter.
35
+ def save_interface_attributes(interface)
36
+ self.class.attributes.each do |key, options|
37
+ save_interface_attribute(key, interface)
38
+ end
39
+ end
40
+
41
+ # Saves a single interface attribute
42
+ #
43
+ # @param [Symbol] key The attribute to write
44
+ # @param [VirtualBox::COM::Interface] interface The interface
45
+ # @param [Object] value The value to write
46
+ def save_interface_attribute(key, interface)
47
+ # Return unless we have a valid interface attribute with a setter
48
+ return unless has_attribute?(key)
49
+ options = self.class.attributes[key.to_sym]
50
+ return if options[:readonly]
51
+ return if options.has_key?(:property) && !options[:property]
52
+
53
+ setter = options[:property] || options[:property_setter] || "#{key}=".to_sym
54
+ return unless setter
55
+
56
+ # Convert the setter to a proc and call it
57
+ setter = spec_to_proc(setter)
58
+ setter.call(interface, read_attribute(key))
59
+ end
60
+
61
+ # Converts a getter/setter specification to a Proc which can be called
62
+ # to obtain or set a value. There are multiple ways to specify the getter
63
+ # and/or setter of an interface attribute:
64
+ #
65
+ # ## Symbol
66
+ #
67
+ # A symbol represents a method to call on the interface. An example of the
68
+ # declaration and resulting method call are shown below:
69
+ #
70
+ # attribute :foo, :property_getter => :get_foo
71
+ #
72
+ # Converts to:
73
+ #
74
+ # interface.get_foo
75
+ #
76
+ # ## Proc
77
+ #
78
+ # A proc is called with the interface and it is expected to return the value
79
+ # for a getter. For a setter, the interface and the value is sent in as
80
+ # parameters to the Proc.
81
+ #
82
+ # attribute :foo, :property_getter => Proc.new { |i| i.get_foo }
83
+ #
84
+ def spec_to_proc(spec)
85
+ # Return the spec as-is if its a proc
86
+ return spec if spec.is_a?(Proc)
87
+
88
+ if spec.is_a?(Symbol)
89
+ # For symbols, wrap up a method send in a Proc and return
90
+ # that
91
+ return Proc.new { |m, *args| m.send(spec, *args) }
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -209,8 +209,8 @@ module VirtualBox
209
209
  # calls `save_relationship` on the relationship class.
210
210
  def save_relationship(name, *args)
211
211
  options = self.class.relationships_hash[name]
212
- return unless options[:klass].respond_to?(:save_relationship)
213
- options[:klass].save_relationship(self, relationship_data[name], *args)
212
+ return unless relationship_class(name).respond_to?(:save_relationship)
213
+ relationship_class(name).save_relationship(self, relationship_data[name], *args)
214
214
  end
215
215
 
216
216
  # The equivalent to {Attributable#populate_attributes}, but with
@@ -224,8 +224,8 @@ module VirtualBox
224
224
  # Populate a single relationship.
225
225
  def populate_relationship(name, data)
226
226
  options = self.class.relationships_hash[name]
227
- return unless options[:klass].respond_to?(:populate_relationship)
228
- relationship_data[name] = options[:klass].populate_relationship(self, data)
227
+ return unless relationship_class(name).respond_to?(:populate_relationship)
228
+ relationship_data[name] = relationship_class(name).populate_relationship(self, data)
229
229
  end
230
230
 
231
231
  # Calls `destroy_relationship` on each of the relationships. Any
@@ -244,13 +244,13 @@ module VirtualBox
244
244
  # @param [Symbol] name The name of the relationship
245
245
  def destroy_relationship(name, *args)
246
246
  options = self.class.relationships_hash[name]
247
- return unless options && options[:klass].respond_to?(:destroy_relationship)
247
+ return unless options && relationship_class(name).respond_to?(:destroy_relationship)
248
248
 
249
249
  # Read relationship, which forces lazy relationships to load, which is
250
250
  # probably necessary for destroying
251
251
  read_relationship(name)
252
252
 
253
- options[:klass].destroy_relationship(self, relationship_data[name], *args)
253
+ relationship_class(name).destroy_relationship(self, relationship_data[name], *args)
254
254
  end
255
255
 
256
256
  # Hash to data associated with relationships. You should instead
@@ -281,6 +281,17 @@ module VirtualBox
281
281
  relationship_data.has_key?(key)
282
282
  end
283
283
 
284
+ # Returns the class for a given relationship. This method handles converting
285
+ # a string/symbol into the proper class.
286
+ #
287
+ # @return [Class]
288
+ def relationship_class(key)
289
+ options = self.class.relationships_hash[key.to_sym]
290
+ klass = options[:klass]
291
+ klass = Object.module_eval("#{klass}") unless klass.is_a?(Class)
292
+ klass
293
+ end
294
+
284
295
  # Sets a relationship to the given value. This is not guaranteed to
285
296
  # do anything, since "set_relationship" will be called on the class
286
297
  # that the relationship is associated with and its expected to return
@@ -299,8 +310,8 @@ module VirtualBox
299
310
  relationship = self.class.relationships_hash[key]
300
311
  return unless relationship
301
312
 
302
- raise Exceptions::NonSettableRelationshipException.new unless relationship[:klass].respond_to?(:set_relationship)
303
- relationship_data[key] = relationship[:klass].set_relationship(self, relationship_data[key], value)
313
+ raise Exceptions::NonSettableRelationshipException.new unless relationship_class(key).respond_to?(:set_relationship)
314
+ relationship_data[key] = relationship_class(key).set_relationship(self, relationship_data[key], value)
304
315
  end
305
316
  end
306
317
  end
@@ -0,0 +1,59 @@
1
+ module VirtualBox
2
+ # Represents an VirtualBox "appliance" which is an exported virtual machine or
3
+ # virtual machines. Appliances typically come with an OVF file and one or more
4
+ # compressed hard disks, and can be used to import directly into other VirtualBox
5
+ # installations. Appliances allow for virtual machine portability.
6
+ class Appliance < AbstractModel
7
+ attribute :path
8
+ attribute :interface, :readonly => true, :property => false
9
+ relationship :virtual_systems, :VirtualSystemDescription
10
+
11
+ def initialize(*args)
12
+ write_attribute(:interface, Lib.lib.virtualbox.create_appliance)
13
+
14
+ initialize_from_path(*args) if args.length == 1
15
+
16
+ clear_dirty!
17
+ end
18
+
19
+ # Initializes this Appliance instance from a path to an OVF file. This sets
20
+ # up the relationships and so on.
21
+ #
22
+ # @param [String] path Path to the OVF file.
23
+ def initialize_from_path(path)
24
+ # Read in the data from the path
25
+ interface.read(path).wait_for_completion(-1)
26
+
27
+ # Interpret the data to fill in the interface properties
28
+ interface.interpret
29
+
30
+ # Load the interface attributes
31
+ load_interface_attributes(interface)
32
+
33
+ # Fill in the virtual systems
34
+ populate_relationship(:virtual_systems, interface.virtual_system_descriptions)
35
+
36
+ # Should be an existing record
37
+ existing_record!
38
+ end
39
+
40
+ # Imports the machines associated with this appliance. If a block is given,
41
+ # it will be yielded every percent that the operation progresses. This can be
42
+ # done to check the progress of the import.
43
+ def import(&block)
44
+ interface.import_machines.wait(&block)
45
+ end
46
+
47
+ # Exports the machines to the given path. If a block is given, it will be yielded
48
+ # every percent that the operation progresses. This can be done to check the progress
49
+ # of the export in real-time.
50
+ def export(&block)
51
+ interface.write("ovf-1.0", path).wait(&block)
52
+ end
53
+
54
+ # Adds a VM to the appliance
55
+ def add_machine(vm)
56
+ vm.interface.export(interface)
57
+ end
58
+ end
59
+ end