virtualbox 0.4.3 → 0.5.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 (44) hide show
  1. data/.gitignore +3 -3
  2. data/Gemfile +8 -8
  3. data/Rakefile +2 -0
  4. data/Readme.md +11 -2
  5. data/VERSION +1 -1
  6. data/docs/WhatsNew.md +40 -24
  7. data/lib/virtualbox.rb +9 -0
  8. data/lib/virtualbox/abstract_model.rb +80 -10
  9. data/lib/virtualbox/abstract_model/attributable.rb +61 -1
  10. data/lib/virtualbox/abstract_model/relatable.rb +88 -6
  11. data/lib/virtualbox/attached_device.rb +18 -10
  12. data/lib/virtualbox/command.rb +13 -0
  13. data/lib/virtualbox/dvd.rb +35 -0
  14. data/lib/virtualbox/exceptions.rb +1 -0
  15. data/lib/virtualbox/extra_data.rb +10 -21
  16. data/lib/virtualbox/global.rb +126 -0
  17. data/lib/virtualbox/hard_drive.rb +33 -9
  18. data/lib/virtualbox/image.rb +2 -3
  19. data/lib/virtualbox/media.rb +19 -0
  20. data/lib/virtualbox/nic.rb +28 -67
  21. data/lib/virtualbox/shared_folder.rb +7 -12
  22. data/lib/virtualbox/storage_controller.rb +8 -36
  23. data/lib/virtualbox/system_property.rb +55 -0
  24. data/lib/virtualbox/usb.rb +72 -0
  25. data/lib/virtualbox/vm.rb +126 -25
  26. data/test/test_helper.rb +118 -12
  27. data/test/virtualbox/abstract_model/attributable_test.rb +55 -5
  28. data/test/virtualbox/abstract_model/relatable_test.rb +66 -4
  29. data/test/virtualbox/abstract_model_test.rb +140 -8
  30. data/test/virtualbox/attached_device_test.rb +10 -7
  31. data/test/virtualbox/command_test.rb +13 -0
  32. data/test/virtualbox/dvd_test.rb +50 -28
  33. data/test/virtualbox/extra_data_test.rb +11 -51
  34. data/test/virtualbox/global_test.rb +78 -0
  35. data/test/virtualbox/hard_drive_test.rb +34 -57
  36. data/test/virtualbox/image_test.rb +0 -5
  37. data/test/virtualbox/nic_test.rb +11 -64
  38. data/test/virtualbox/shared_folder_test.rb +5 -5
  39. data/test/virtualbox/storage_controller_test.rb +15 -30
  40. data/test/virtualbox/system_property_test.rb +71 -0
  41. data/test/virtualbox/usb_test.rb +35 -0
  42. data/test/virtualbox/vm_test.rb +62 -121
  43. data/virtualbox.gemspec +15 -2
  44. metadata +23 -4
@@ -86,8 +86,8 @@ module VirtualBox
86
86
  #
87
87
  class SharedFolder < AbstractModel
88
88
  attribute :parent, :readonly => :readonly
89
- attribute :name, :populate_key => "SharedFolderNameMachineMapping"
90
- attribute :hostpath, :populate_key => "SharedFolderPathMachineMapping"
89
+ attribute :name
90
+ attribute :hostpath
91
91
 
92
92
  class <<self
93
93
  # Populates the shared folder relationship for anything which is related to it.
@@ -95,15 +95,12 @@ module VirtualBox
95
95
  # **This method typically won't be used except internally.**
96
96
  #
97
97
  # @return [Array<SharedFolder>]
98
- def populate_relationship(caller, data)
98
+ def populate_relationship(caller, doc)
99
99
  relation = Proxies::Collection.new(caller)
100
100
 
101
101
  counter = 1
102
- loop do
103
- break unless data["SharedFolderNameMachineMapping#{counter}".downcase.to_sym]
104
-
105
- folder = new(counter, caller, data)
106
- relation.push(folder)
102
+ doc.css("Hardware SharedFolders SharedFolder").each do |folder|
103
+ relation << new(counter, caller, folder)
107
104
  counter += 1
108
105
  end
109
106
 
@@ -156,10 +153,8 @@ module VirtualBox
156
153
  def initialize_for_relationship(index, caller, data)
157
154
  # Setup the index specific attributes
158
155
  populate_data = {}
159
- self.class.attributes.each do |name, options|
160
- key = options[:populate_key] || name
161
- value = data["#{key}#{index}".downcase.to_sym]
162
- populate_data[key] = value
156
+ data.attributes.each do |key, value|
157
+ populate_data[key.downcase.to_sym] = value.to_s
163
158
  end
164
159
 
165
160
  populate_attributes(populate_data.merge({
@@ -43,7 +43,6 @@ module VirtualBox
43
43
  attribute :parent, :readonly => true
44
44
  attribute :name
45
45
  attribute :type
46
- attribute :max_ports, :populate_key => :maxportcount
47
46
  attribute :ports, :populate_key => :portcount
48
47
  relationship :devices, AttachedDevice, :dependent => :destroy
49
48
 
@@ -53,14 +52,12 @@ module VirtualBox
53
52
  # **This method typically won't be used except internally.**
54
53
  #
55
54
  # @return [Array<StorageController>]
56
- def populate_relationship(caller, data)
57
- relation = []
55
+ def populate_relationship(caller, doc)
56
+ relation = Proxies::Collection.new(caller)
58
57
 
59
58
  counter = 0
60
- loop do
61
- break unless data["storagecontrollername#{counter}".to_sym]
62
- nic = new(counter, caller, data)
63
- relation.push(nic)
59
+ doc.css("StorageControllers StorageController").each do |sc|
60
+ relation << new(counter, caller, sc)
64
61
  counter += 1
65
62
  end
66
63
 
@@ -96,37 +93,12 @@ module VirtualBox
96
93
 
97
94
  # Setup the index specific attributes
98
95
  populate_data = {}
99
- self.class.attributes.each do |name, options|
100
- key = options[:populate_key] || name
101
- value = data["storagecontroller#{key}#{index}".to_sym]
102
- populate_data[key] = value
96
+ data.attributes.each do |key,value|
97
+ populate_data[key.downcase.to_sym] = value.to_s
103
98
  end
104
99
 
105
- # Make sure to merge in device data so those relationships will be
106
- # setup properly
107
- populate_data.merge!(extract_devices(index, data))
108
-
109
- populate_attributes(populate_data.merge({
110
- :parent => caller
111
- }))
112
- end
113
-
114
- # Extracts related devices for a storage controller.
115
- #
116
- # **This method typically won't be used except internally.**
117
- #
118
- # @return [Hash]
119
- def extract_devices(index, data)
120
- name = data["storagecontrollername#{index}".downcase.to_sym].downcase
121
-
122
- device_data = {}
123
- data.each do |k,v|
124
- next unless k.to_s =~ /^#{name}-/
125
-
126
- device_data[k] = v
127
- end
128
-
129
- device_data
100
+ populate_attributes(populate_data.merge({:parent => caller}), :ignore_relationships => true)
101
+ populate_relationship(:devices, data)
130
102
  end
131
103
  end
132
104
  end
@@ -0,0 +1,55 @@
1
+ module VirtualBox
2
+ # Represents the system properties of the system which VirtualBox
3
+ # is running on. These system properties are immutable values which
4
+ # are typically limits or specs of the host system. Some examples
5
+ # of available properties are `Maximum guest RAM size` or
6
+ # `Maximum Devices per SATA Port`.
7
+ #
8
+ # # Retrieving the System Properties
9
+ #
10
+ # Retrieving the system properties is done by calling the {all} method.
11
+ # Since {SystemProperty} inherits from `Hash`, you can treat it just like
12
+ # one. The keys are simply the typical keys downcased with spaces replaced
13
+ # with underscores, and converted to a symbol. An example of accessing
14
+ # system properties is shown below:
15
+ #
16
+ # properties = VirtualBox::SystemProperty.all
17
+ # puts properties[:log_history_count]
18
+ # puts properties[:maximum_guest_ram_size]
19
+ #
20
+ # Since {SystemProperty} is simply a hash, you can also iterate over it,
21
+ # convert it easily to an array, etc.
22
+ class SystemProperty < Hash
23
+ class <<self
24
+ # Returns the hash of all system properties. Each call will invoke a
25
+ # system call to retrieve the properties (as in they're not cached
26
+ # on the class), so if you need to access them many times, please
27
+ # cache them yourself.
28
+ #
29
+ # @return [SystemProperty]
30
+ def all
31
+ raw = Command.vboxmanage("list", "systemproperties")
32
+ parse_raw(raw)
33
+ end
34
+
35
+ # Parses the raw output of vboxmanage. This parses the raw output from
36
+ # VBoxManage to parse the system properties.
37
+ #
38
+ # **This method typically won't be used except internally.**
39
+ #
40
+ # @param [String] data The raw output from vboxmanage.
41
+ # @return [SystemProperty]
42
+ def parse_raw(data)
43
+ result = new
44
+ data.split("\n").each do |line|
45
+ next unless line =~ /^(.+?):\s+(.+?)$/
46
+ value = $2.to_s
47
+ key = $1.to_s.downcase.gsub(/\s/, "_")
48
+ result[key.to_sym] = value
49
+ end
50
+
51
+ result
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,72 @@
1
+ module VirtualBox
2
+ # Represents a single USB device of a virtual machine.
3
+ #
4
+ # **Currently, new USB devices can't be created, so the only way to get this
5
+ # object is through a {VM}'s `usbs` relationship.**
6
+ #
7
+ # # Attributes
8
+ #
9
+ # Properties of the model are exposed using standard ruby instance
10
+ # methods which are generated on the fly. Because of this, they are not listed
11
+ # below as available instance methods.
12
+ #
13
+ # These attributes can be accessed and modified via standard ruby-style
14
+ # `instance.attribute` and `instance.attribute=` methods. The attributes are
15
+ # listed below. If you aren't sure what this means or you can't understand
16
+ # why the below is listed, please read {Attributable}.
17
+ #
18
+ # attribute :parent, :readonly => :readonly
19
+ # attribute :name
20
+ # attribute :active
21
+ # attribute :manufacturer
22
+ # attribute :product
23
+ # attribute :remote
24
+ #
25
+ class USB < AbstractModel
26
+ attribute :parent, :readonly => :readonly
27
+ attribute :name
28
+ attribute :active
29
+ attribute :manufacturer
30
+ attribute :product
31
+ attribute :remote
32
+
33
+ class <<self
34
+ # Populates the usb device relationship for anything which is related to it.
35
+ #
36
+ # **This method typically won't be used except internally.**
37
+ #
38
+ # @return [Array<USB>]
39
+ def populate_relationship(caller, doc)
40
+ relation = Proxies::Collection.new(caller)
41
+
42
+ doc.css("Hardware USBController DeviceFilter").each do |device|
43
+ relation << new(caller, device)
44
+ end
45
+
46
+ relation
47
+ end
48
+ end
49
+
50
+ # Since there is currently no way to create a _new_ usb device, this is
51
+ # only used internally. Developers should NOT try to initialize their
52
+ # own usb device objects.
53
+ def initialize(caller, data)
54
+ super()
55
+
56
+ # Set the parent
57
+ write_attribute(:parent, caller)
58
+
59
+ # Convert each attribute value to a string
60
+ attrs = {}
61
+
62
+ data.attributes.each do |key, value|
63
+ attrs[key.to_sym] = value.to_s
64
+ end
65
+
66
+ populate_attributes(attrs)
67
+
68
+ # Clear dirtiness
69
+ clear_dirty!
70
+ end
71
+ end
72
+ end
data/lib/virtualbox/vm.rb CHANGED
@@ -42,6 +42,7 @@ module VirtualBox
42
42
  # attribute :uuid, :readonly => true
43
43
  # attribute :name
44
44
  # attribute :ostype
45
+ # attribute :description, :readonly => true
45
46
  # attribute :memory
46
47
  # attribute :vram
47
48
  # attribute :acpi
@@ -63,9 +64,14 @@ module VirtualBox
63
64
  # attribute :clipboard
64
65
  # attribute :monitorcount
65
66
  # attribute :usb
67
+ # attribute :ehci
66
68
  # attribute :audio
69
+ # attribute :audiocontroller
70
+ # attribute :audiodriver
67
71
  # attribute :vrdp
68
- # attribute :vrdpports
72
+ # attribute :vrdpport
73
+ # attribute :vrdpauthtype
74
+ # attribute :vrdpauthtimeout
69
75
  # attribute :state, :populate_key => :vmstate, :readonly => true
70
76
  #
71
77
  # ## Relationships
@@ -75,6 +81,7 @@ module VirtualBox
75
81
  # understand this, read {Relatable}.
76
82
  #
77
83
  # relationship :nics, Nic
84
+ # relationship :usbs, USB
78
85
  # relationship :storage_controllers, StorageController, :dependent => :destroy
79
86
  # relationship :shared_folders, SharedFolder
80
87
  # relationship :extra_data, ExtraData
@@ -84,12 +91,13 @@ module VirtualBox
84
91
  attribute :uuid, :readonly => true
85
92
  attribute :name
86
93
  attribute :ostype
94
+ attribute :description, :readonly => true
87
95
  attribute :memory
88
96
  attribute :vram
89
97
  attribute :acpi
90
98
  attribute :ioapic
91
99
  attribute :cpus
92
- attribute :synthcpu
100
+ attribute :synthcpu, :lazy => true
93
101
  attribute :pae
94
102
  attribute :hwvirtex
95
103
  attribute :hwvirtexexcl
@@ -105,11 +113,17 @@ module VirtualBox
105
113
  attribute :clipboard
106
114
  attribute :monitorcount
107
115
  attribute :usb
116
+ attribute :ehci
108
117
  attribute :audio
118
+ attribute :audiocontroller
119
+ attribute :audiodriver
109
120
  attribute :vrdp
110
- attribute :vrdpports
111
- attribute :state, :populate_key => :vmstate, :readonly => true
121
+ attribute :vrdpport
122
+ attribute :vrdpauthtype
123
+ attribute :vrdpauthtimeout
124
+ attribute :state, :populate_key => :vmstate, :readonly => true, :lazy => true
112
125
  relationship :nics, Nic
126
+ relationship :usbs, USB
113
127
  relationship :storage_controllers, StorageController, :dependent => :destroy
114
128
  relationship :shared_folders, SharedFolder
115
129
  relationship :extra_data, ExtraData
@@ -119,9 +133,8 @@ module VirtualBox
119
133
  # Returns an array of all available VMs.
120
134
  #
121
135
  # @return [Array<VM>]
122
- def all
123
- raw = Command.vboxmanage("list", "vms")
124
- parse_vm_list(raw)
136
+ def all(reload=false)
137
+ Global.global(reload).vms
125
138
  end
126
139
 
127
140
  # Finds a VM by UUID or registered name and returns a
@@ -129,9 +142,20 @@ module VirtualBox
129
142
  #
130
143
  # @return [VM]
131
144
  def find(name)
132
- new(raw_info(name))
133
- rescue Exceptions::CommandFailedException
134
- nil
145
+ all(true).detect { |o| o.name == name || o.uuid == name }
146
+ end
147
+
148
+ # Loads a VM from its XML configuration file. All VMs managed
149
+ # by VirtualBox have an XML configuration file somewhere. If
150
+ # given the path, this will instantiate the VM that way. Typically
151
+ # this method will only be called internally. Users of the class
152
+ # should use {all} or {find} instead.
153
+ #
154
+ # @param [String] location Full path to the XML file.
155
+ # @return [VM]
156
+ def load_from_xml(location)
157
+ vm_doc = Command.parse_xml(location)
158
+ new(vm_doc)
135
159
  end
136
160
 
137
161
  # Imports a VM, blocking the entire thread during this time.
@@ -147,16 +171,6 @@ module VirtualBox
147
171
  find(parse_vm_name(raw))
148
172
  end
149
173
 
150
- # Gets the non-machine-readable info for a given VM and returns
151
- # it as a raw string.
152
- #
153
- # **This method typically won't be used except internally.**
154
- #
155
- # @return [String]
156
- def human_info(name)
157
- Command.vboxmanage("showvminfo", name)
158
- end
159
-
160
174
  # Gets the VM info (machine readable) for a given VM and returns it
161
175
  # as a hash.
162
176
  #
@@ -204,6 +218,17 @@ module VirtualBox
204
218
  return nil unless raw =~ /VM name "(.+?)"/
205
219
  $1.to_s
206
220
  end
221
+
222
+ def populate_relationship(caller, doc)
223
+ result = Proxies::Collection.new(caller)
224
+
225
+ doc.css("Global MachineRegistry MachineEntry").each do |entry|
226
+ location = Global.expand_path(entry[:src])
227
+ result << load_from_xml(location)
228
+ end
229
+
230
+ result
231
+ end
207
232
  end
208
233
 
209
234
  # Creates a new instance of a virtual machine.
@@ -215,8 +240,77 @@ module VirtualBox
215
240
  def initialize(data)
216
241
  super()
217
242
 
218
- populate_attributes(data)
219
- @original_name = data[:name]
243
+ initialize_attributes(data)
244
+ populate_relationships(data)
245
+ @original_name = name
246
+ end
247
+
248
+ def initialize_attributes(doc)
249
+ attribute_associations = {
250
+ :uuid => ["Machine", :uuid],
251
+ :name => ["Machine", :name],
252
+ :ostype => ["Machine", :OSType],
253
+ :description => ["Machine Description"],
254
+ :memory => ["Hardware Memory", :RAMSize],
255
+ :vram => ["Hardware Display", :VRAMSize],
256
+ :acpi => ["Hardware BIOS ACPI", :enabled],
257
+ :ioapic => ["Hardware BIOS IOAPIC", :enabled],
258
+ :cpus => ["Hardware CPU", :count],
259
+ :pae => ["Hardware CPU PAE", :enabled],
260
+ :hwvirtex => ["Hardware CPU HardwareVirtEx", :enabled],
261
+ :hwvirtexexcl => ["Hardware CPU HardwareVirtEx", :exclusive],
262
+ :nestedpaging => ["Hardware CPU HardwareVirtExNestedPaging", :enabled],
263
+ :vtxvpid => ["Hardware CPU HardwareVirtExVPID", :enabled],
264
+ :accelerate3d => ["Hardware Display", :accelerate3D],
265
+ :accelerate2dvideo => ["Hardware Display", :accelerate2DVideo],
266
+ :biosbootmenu => ["Hardware BIOS BootMenu", :mode],
267
+ :boot1 => ["Hardware Boot Order[position=\"1\"]", :device],
268
+ :boot2 => ["Hardware Boot Order[position=\"2\"]", :device],
269
+ :boot3 => ["Hardware Boot Order[position=\"3\"]", :device],
270
+ :boot4 => ["Hardware Boot Order[position=\"4\"]", :device],
271
+ :clipboard => ["Hardware Clipboard", :mode],
272
+ :monitorcount => ["Hardware Display", :monitorCount],
273
+ :usb => ["Hardware USBController", :enabled],
274
+ :ehci => ["Hardware USBController", :enabledEhci],
275
+ :audio => ["Hardware AudioAdapter", :enabled],
276
+ :audiocontroller => ["Hardware AudioAdapter", :controller],
277
+ :audiodriver => ["Hardware AudioAdapter", :driver],
278
+ :vrdp => ["Hardware RemoteDisplay", :enabled],
279
+ :vrdpport => ["Hardware RemoteDisplay", :port],
280
+ :vrdpauthtype => ["Hardware RemoteDisplay", :authType],
281
+ :vrdpauthtimeout => ["Hardware RemoteDisplay", :authTimeout],
282
+ }
283
+
284
+ attribute_associations.each do |name, search_data|
285
+ css, key = search_data
286
+ node = doc.css(css)[0]
287
+
288
+ # key is passed in for attributes, else you get the element inner text
289
+ value = (key ? node[key] : node.inner_text) if node
290
+
291
+ # Special cases
292
+ value = value[1..-2] if name == :uuid
293
+
294
+ write_attribute(name, value)
295
+ end
296
+
297
+ # Clear dirtiness, since this should only be called initially and
298
+ # therefore shouldn't affect dirtiness
299
+ clear_dirty!
300
+
301
+ # But this is an existing record
302
+ existing_record!
303
+ end
304
+
305
+ def load_attribute(name)
306
+ info = self.class.raw_info(@original_name)
307
+
308
+ if name == :state
309
+ # Simply force a state reload, and it'll write the attribute up
310
+ write_attribute(:state, info[:vmstate])
311
+ end
312
+
313
+ write_attribute(:synthcpu, info[:synthcpu]) unless loaded_attribute?(:synthcpu)
220
314
  end
221
315
 
222
316
  # State of the virtual machine. Returns the state of the virtual
@@ -228,8 +322,7 @@ module VirtualBox
228
322
  # @return [String] Virtual machine state.
229
323
  def state(reload=false)
230
324
  if reload
231
- info = self.class.raw_info(@original_name)
232
- write_attribute(:state, info[:vmstate])
325
+ load_attribute(:state)
233
326
  end
234
327
 
235
328
  read_attribute(:state)
@@ -248,6 +341,9 @@ module VirtualBox
248
341
 
249
342
  super()
250
343
 
344
+ # Force reload
345
+ Global.reload!
346
+
251
347
  true
252
348
  rescue Exceptions::CommandFailedException
253
349
  raise if raise_errors
@@ -420,7 +516,12 @@ module VirtualBox
420
516
  # unregistering a VM
421
517
  super
422
518
 
423
- Command.vboxmanage("unregistervm", @original_name, "--delete")
519
+ if Command.vboxmanage("unregistervm", @original_name, "--delete")
520
+ Global.reload!
521
+ return true
522
+ else
523
+ return false
524
+ end
424
525
  end
425
526
 
426
527
  # Returns true if the virtual machine state is running