virtualbox 0.4.1 → 0.4.2

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/Readme.md +9 -9
  2. data/VERSION +1 -1
  3. data/docs/GettingStarted.md +11 -11
  4. data/docs/WhatsNew.md +1 -1
  5. data/lib/virtualbox.rb +10 -1
  6. data/lib/virtualbox/abstract_model.rb +47 -29
  7. data/lib/virtualbox/abstract_model/attributable.rb +16 -16
  8. data/lib/virtualbox/abstract_model/dirty.rb +10 -10
  9. data/lib/virtualbox/abstract_model/relatable.rb +22 -22
  10. data/lib/virtualbox/abstract_model/validatable.rb +4 -4
  11. data/lib/virtualbox/attached_device.rb +23 -23
  12. data/lib/virtualbox/command.rb +9 -9
  13. data/lib/virtualbox/dvd.rb +7 -7
  14. data/lib/virtualbox/ext/subclass_listing.rb +1 -1
  15. data/lib/virtualbox/extra_data.rb +17 -17
  16. data/lib/virtualbox/forwarded_port.rb +23 -23
  17. data/lib/virtualbox/hard_drive.rb +27 -27
  18. data/lib/virtualbox/image.rb +25 -18
  19. data/lib/virtualbox/nic.rb +22 -22
  20. data/lib/virtualbox/proxies/collection.rb +4 -4
  21. data/lib/virtualbox/shared_folder.rb +25 -25
  22. data/lib/virtualbox/storage_controller.rb +16 -16
  23. data/lib/virtualbox/vm.rb +95 -42
  24. data/test/virtualbox/abstract_model/attributable_test.rb +28 -28
  25. data/test/virtualbox/abstract_model/dirty_test.rb +13 -13
  26. data/test/virtualbox/abstract_model/relatable_test.rb +36 -36
  27. data/test/virtualbox/abstract_model/validatable_test.rb +22 -22
  28. data/test/virtualbox/abstract_model_test.rb +46 -36
  29. data/test/virtualbox/attached_device_test.rb +47 -47
  30. data/test/virtualbox/command_test.rb +12 -12
  31. data/test/virtualbox/dvd_test.rb +15 -19
  32. data/test/virtualbox/ext/subclass_listing_test.rb +2 -2
  33. data/test/virtualbox/extra_data_test.rb +37 -37
  34. data/test/virtualbox/forwarded_port_test.rb +31 -31
  35. data/test/virtualbox/hard_drive_test.rb +40 -48
  36. data/test/virtualbox/image_test.rb +36 -33
  37. data/test/virtualbox/nic_test.rb +22 -22
  38. data/test/virtualbox/proxies/collection_test.rb +6 -6
  39. data/test/virtualbox/shared_folder_test.rb +36 -36
  40. data/test/virtualbox/storage_controller_test.rb +14 -14
  41. data/test/virtualbox/vm_test.rb +121 -70
  42. data/test/virtualbox_test.rb +19 -0
  43. data/virtualbox.gemspec +5 -3
  44. metadata +4 -2
data/lib/virtualbox/vm.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module VirtualBox
2
2
  # Represents a single VirtualBox virtual machine. All attributes which are
3
- # not read-only can be modified and saved.
3
+ # not read-only can be modified and saved.
4
4
  #
5
5
  # # Finding Virtual Machines
6
6
  #
@@ -21,10 +21,10 @@ module VirtualBox
21
21
  # vm.save
22
22
  #
23
23
  # # Attributes and Relationships
24
- #
24
+ #
25
25
  # Properties of the virtual machine are exposed using standard ruby instance
26
26
  # methods which are generated on the fly. Because of this, they are not listed
27
- # below as available instance methods.
27
+ # below as available instance methods.
28
28
  #
29
29
  # These attributes can be accessed and modified via standard ruby-style
30
30
  # `instance.attribute` and `instance.attribute=` methods. The attributes are
@@ -33,7 +33,7 @@ module VirtualBox
33
33
  # Relationships are also accessed like attributes but can't be set. Instead,
34
34
  # they are typically references to other objects such as a {HardDrive} which
35
35
  # in turn have their own attributes which can be modified.
36
- #
36
+ #
37
37
  # ## Attributes
38
38
  #
39
39
  # This is copied directly from the class header, but lists all available
@@ -54,6 +54,7 @@ module VirtualBox
54
54
  # attribute :nestedpaging
55
55
  # attribute :vtxvpid
56
56
  # attribute :accelerate3d
57
+ # attribute :accelerate2dvideo
57
58
  # attribute :biosbootmenu, :populate_key => :bootmenu
58
59
  # attribute :boot1
59
60
  # attribute :boot2
@@ -64,6 +65,7 @@ module VirtualBox
64
65
  # attribute :usb
65
66
  # attribute :audio
66
67
  # attribute :vrdp
68
+ # attribute :vrdpports
67
69
  # attribute :state, :populate_key => :vmstate, :readonly => true
68
70
  #
69
71
  # ## Relationships
@@ -94,6 +96,7 @@ module VirtualBox
94
96
  attribute :nestedpaging
95
97
  attribute :vtxvpid
96
98
  attribute :accelerate3d
99
+ attribute :accelerate2dvideo
97
100
  attribute :biosbootmenu, :populate_key => :bootmenu
98
101
  attribute :boot1
99
102
  attribute :boot2
@@ -104,13 +107,14 @@ module VirtualBox
104
107
  attribute :usb
105
108
  attribute :audio
106
109
  attribute :vrdp
110
+ attribute :vrdpports
107
111
  attribute :state, :populate_key => :vmstate, :readonly => true
108
112
  relationship :nics, Nic
109
113
  relationship :storage_controllers, StorageController, :dependent => :destroy
110
114
  relationship :shared_folders, SharedFolder
111
115
  relationship :extra_data, ExtraData
112
116
  relationship :forwarded_ports, ForwardedPort
113
-
117
+
114
118
  class <<self
115
119
  # Returns an array of all available VMs.
116
120
  #
@@ -119,16 +123,18 @@ module VirtualBox
119
123
  raw = Command.vboxmanage("list vms")
120
124
  parse_vm_list(raw)
121
125
  end
122
-
126
+
123
127
  # Finds a VM by UUID or registered name and returns a
124
128
  # new VM object. If the VM doesn't exist, will return `nil`.
125
129
  #
126
130
  # @return [VM]
127
131
  def find(name)
128
132
  new(raw_info(name))
133
+ rescue Exceptions::CommandFailedException
134
+ nil
129
135
  end
130
-
131
- # Imports a VM, blocking the entire thread during this time.
136
+
137
+ # Imports a VM, blocking the entire thread during this time.
132
138
  # When finished, on success, will return the VM object. This
133
139
  # VM object can be used to make any modifications necessary
134
140
  # (RAM, cpus, etc.).
@@ -137,10 +143,10 @@ module VirtualBox
137
143
  def import(source_path)
138
144
  raw = Command.vboxmanage("import #{Command.shell_escape(source_path)}")
139
145
  return nil unless raw
140
-
146
+
141
147
  find(parse_vm_name(raw))
142
148
  end
143
-
149
+
144
150
  # Gets the non-machine-readable info for a given VM and returns
145
151
  # it as a raw string.
146
152
  #
@@ -150,16 +156,16 @@ module VirtualBox
150
156
  def human_info(name)
151
157
  Command.vboxmanage("showvminfo #{Command.shell_escape(name)}")
152
158
  end
153
-
159
+
154
160
  # Gets the VM info (machine readable) for a given VM and returns it
155
- # as a hash.
161
+ # as a hash.
156
162
  #
157
163
  # @return [Hash] Parsed VM info.
158
164
  def raw_info(name)
159
165
  raw = Command.vboxmanage("showvminfo #{Command.shell_escape(name)} --machinereadable")
160
166
  parse_vm_info(raw)
161
167
  end
162
-
168
+
163
169
  # Parses the machine-readable format outputted by VBoxManage showvminfo
164
170
  # into a hash. Ignores lines which don't match the format.
165
171
  def parse_vm_info(raw)
@@ -172,7 +178,7 @@ module VirtualBox
172
178
 
173
179
  parsed
174
180
  end
175
-
181
+
176
182
  # Parses the list of VMs returned by the "list vms" command used
177
183
  # in {VM.all}.
178
184
  #
@@ -185,10 +191,10 @@ module VirtualBox
185
191
  next unless line =~ /^"(.+?)"\s+\{(.+?)\}$/
186
192
  results.push(find($1.to_s))
187
193
  end
188
-
194
+
189
195
  results
190
196
  end
191
-
197
+
192
198
  # Parses the vm name from the import results.
193
199
  #
194
200
  # **This method typically won't be used except internally.**
@@ -199,7 +205,7 @@ module VirtualBox
199
205
  $1.to_s
200
206
  end
201
207
  end
202
-
208
+
203
209
  # Creates a new instance of a virtual machine.
204
210
  #
205
211
  # **Currently can NOT be used to create a NEW virtual machine**.
@@ -208,11 +214,11 @@ module VirtualBox
208
214
  # initialize the VMs.
209
215
  def initialize(data)
210
216
  super()
211
-
217
+
212
218
  populate_attributes(data)
213
219
  @original_name = data[:name]
214
220
  end
215
-
221
+
216
222
  # State of the virtual machine. Returns the state of the virtual
217
223
  # machine. This state will represent the state that was assigned
218
224
  # when the VM was found unless `reload` is set to `true`.
@@ -225,10 +231,10 @@ module VirtualBox
225
231
  info = self.class.raw_info(@original_name)
226
232
  write_attribute(:state, info[:vmstate])
227
233
  end
228
-
234
+
229
235
  read_attribute(:state)
230
236
  end
231
-
237
+
232
238
  # Saves the virtual machine if modified. This method saves any modified
233
239
  # attributes of the virtual machine. If any related attributes were saved
234
240
  # as well (such as storage controllers), those will be saved, too.
@@ -239,24 +245,24 @@ module VirtualBox
239
245
  save_attribute(:name, name)
240
246
  @original_name = name
241
247
  end
242
-
248
+
243
249
  super()
244
-
250
+
245
251
  true
246
252
  rescue Exceptions::CommandFailedException
247
253
  raise if raise_errors
248
254
  return false
249
255
  end
250
-
256
+
251
257
  # Saves a single attribute of the virtual machine. This should **not**
252
258
  # be called except interally. Instead, you're probably looking for {#save}.
253
259
  #
254
260
  # **This method typically won't be used except internally.**
255
261
  def save_attribute(key, value)
256
- Command.vboxmanage("modifyvm #{@original_name} --#{key} #{Command.shell_escape(value.to_s)}")
262
+ Command.vboxmanage("modifyvm #{Command.shell_escape(@original_name)} --#{key} #{Command.shell_escape(value.to_s)}")
257
263
  super
258
264
  end
259
-
265
+
260
266
  # Exports a virtual machine. The virtual machine will be exported
261
267
  # to the specified OVF file name. This directory will also have the
262
268
  # `mf` file which contains the file checksums and also the virtual
@@ -283,21 +289,21 @@ module VirtualBox
283
289
  options = options.inject([]) do |acc, kv|
284
290
  acc.push("--#{kv[0]} #{Command.shell_escape(kv[1])}")
285
291
  end
286
-
292
+
287
293
  options.unshift("--vsys 0") unless options.empty?
288
-
289
- raw = Command.vboxmanage("export #{@original_name} -o #{Command.shell_escape(filename)} #{options.join(" ")}".strip)
294
+
295
+ raw = Command.vboxmanage("export #{Command.shell_escape(@original_name)} -o #{Command.shell_escape(filename)} #{options.join(" ")}".strip)
290
296
  true
291
297
  rescue Exceptions::CommandFailedException
292
298
  raise if raise_error
293
299
  false
294
300
  end
295
-
301
+
296
302
  # Starts the virtual machine. The virtual machine can be started in a
297
303
  # variety of modes:
298
304
  #
299
305
  # * **gui** -- The VirtualBox GUI will open with the screen of the VM.
300
- # * **headless** -- The VM will run in the background. No GUI will be
306
+ # * **headless** -- The VM will run in the background. No GUI will be
301
307
  # present at all.
302
308
  #
303
309
  # All modes will start their processes and return almost immediately.
@@ -308,16 +314,28 @@ module VirtualBox
308
314
  # will be raised if the command failed.
309
315
  # @return [Boolean] True if command was successful, false otherwise.
310
316
  def start(mode=:gui, raise_errors=false)
311
- Command.vboxmanage("startvm #{@original_name} --type #{mode}")
317
+ Command.vboxmanage("startvm #{Command.shell_escape(@original_name)} --type #{mode}")
312
318
  true
313
319
  rescue Exceptions::CommandFailedException
314
320
  raise if raise_errors
315
321
  false
316
322
  end
317
-
323
+
324
+ # Shuts down the VM by directly calling "acpipowerbutton". Depending on the
325
+ # settings of the Virtual Machine, this may not work. For example, some linux
326
+ # installations don't respond to the ACPI power button at all. In such cases,
327
+ # {#stop} or {#save_state} may be used instead.
328
+ #
329
+ # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
330
+ # will be raised if the command failed.
331
+ # @return [Boolean] True if command was successful, false otherwise.
332
+ def shutdown(raise_errors=false)
333
+ control(:acpipowerbutton, raise_errors)
334
+ end
335
+
318
336
  # Stops the VM by directly calling "poweroff." Immediately halts the
319
337
  # virtual machine without saving state. This could result in a loss
320
- # of data.
338
+ # of data. To prevent data loss, see {#shutdown}
321
339
  #
322
340
  # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
323
341
  # will be raised if the command failed.
@@ -325,7 +343,7 @@ module VirtualBox
325
343
  def stop(raise_errors=false)
326
344
  control(:poweroff, raise_errors)
327
345
  end
328
-
346
+
329
347
  # Pauses the VM, putting it on hold temporarily. The VM can be resumed
330
348
  # again by calling {#resume}
331
349
  #
@@ -335,7 +353,7 @@ module VirtualBox
335
353
  def pause(raise_errors=false)
336
354
  control(:pause, raise_errors)
337
355
  end
338
-
356
+
339
357
  # Resume a paused VM.
340
358
  #
341
359
  # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
@@ -344,7 +362,7 @@ module VirtualBox
344
362
  def resume(raise_errors=false)
345
363
  control(:resume, raise_errors)
346
364
  end
347
-
365
+
348
366
  # Saves the state of a VM and stops it. The VM can be resumed
349
367
  # again by calling "start" again.
350
368
  #
@@ -354,7 +372,7 @@ module VirtualBox
354
372
  def save_state(raise_errors=false)
355
373
  control(:savestate, raise_errors)
356
374
  end
357
-
375
+
358
376
  # Controls the virtual machine. This method is used by {#stop},
359
377
  # {#pause}, {#resume}, and {#save_state} to control the virtual machine.
360
378
  # Typically, you won't ever have to call this method and should
@@ -365,13 +383,13 @@ module VirtualBox
365
383
  # will be raised if the command failed.
366
384
  # @return [Boolean] True if command was successful, false otherwise.
367
385
  def control(command, raise_errors=false)
368
- Command.vboxmanage("controlvm #{@original_name} #{command}")
386
+ Command.vboxmanage("controlvm #{Command.shell_escape(@original_name)} #{command}")
369
387
  true
370
388
  rescue Exceptions::CommandFailedException
371
389
  raise if raise_errors
372
390
  false
373
391
  end
374
-
392
+
375
393
  # Destroys the virtual machine. This method also removes all attached
376
394
  # media (required by VirtualBox to destroy a VM). By default,
377
395
  # this **will not** destroy attached hard drives, but will if given
@@ -386,8 +404,43 @@ module VirtualBox
386
404
  # Call super first to destroy relationships, necessary before
387
405
  # unregistering a VM
388
406
  super
389
-
390
- Command.vboxmanage("unregistervm #{@original_name} --delete")
407
+
408
+ Command.vboxmanage("unregistervm #{Command.shell_escape(@original_name)} --delete")
409
+ end
410
+
411
+ # Returns true if the virtual machine state is running
412
+ #
413
+ # @return [Boolean] True if virtual machine state is running
414
+ def running?
415
+ state == 'running'
416
+ end
417
+
418
+ # Returns true if the virtual machine state is powered off
419
+ #
420
+ # @return [Boolean] True if virtual machine state is powered off
421
+ def powered_off?
422
+ state == 'poweroff'
423
+ end
424
+
425
+ # Returns true if the virtual machine state is paused
426
+ #
427
+ # @return [Boolean] True if virtual machine state is paused
428
+ def paused?
429
+ state == 'paused'
430
+ end
431
+
432
+ # Returns true if the virtual machine state is saved
433
+ #
434
+ # @return [Boolean] True if virtual machine state is saved
435
+ def saved?
436
+ state == 'saved'
437
+ end
438
+
439
+ # Returns true if the virtual machine state is aborted
440
+ #
441
+ # @return [Boolean] True if virtual machine state is aborted
442
+ def aborted?
443
+ state == 'aborted'
391
444
  end
392
445
  end
393
446
  end
@@ -2,28 +2,28 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
2
 
3
3
  class AttributableTest < Test::Unit::TestCase
4
4
  class EmptyAttributeModel
5
- include VirtualBox::AbstractModel::Attributable
5
+ include VirtualBox::AbstractModel::Attributable
6
6
  end
7
-
7
+
8
8
  class AttributeModel < EmptyAttributeModel
9
9
  attribute :foo
10
10
  attribute :bar
11
-
11
+
12
12
  def initialize
13
13
  super
14
-
14
+
15
15
  populate_attributes({
16
16
  :foo => "foo",
17
17
  :bar => "bar"
18
18
  })
19
19
  end
20
20
  end
21
-
21
+
22
22
  context "subclasses" do
23
23
  class SubModel < AttributeModel
24
24
  attribute :baz
25
25
  end
26
-
26
+
27
27
  should "have foo bar and baz" do
28
28
  attributes = SubModel.attributes
29
29
  assert attributes.has_key?(:foo)
@@ -31,118 +31,118 @@ class AttributableTest < Test::Unit::TestCase
31
31
  assert attributes.has_key?(:baz)
32
32
  end
33
33
  end
34
-
34
+
35
35
  context "attribute options" do
36
36
  context "custom populate keys" do
37
37
  class CustomPopulateModel < AttributeModel
38
38
  attribute :foo, :populate_key => :foo_key
39
39
  end
40
-
40
+
41
41
  setup do
42
42
  @model = CustomPopulateModel.new
43
43
  end
44
-
44
+
45
45
  should "use the populate key instead of the attribute name" do
46
46
  @model.populate_attributes({
47
47
  :foo => "not me!",
48
48
  :foo_key => "bar"
49
49
  })
50
-
50
+
51
51
  assert_equal "bar", @model.foo
52
52
  end
53
53
  end
54
-
54
+
55
55
  context "readonly attributes" do
56
56
  class ReadonlyModel < AttributeModel
57
57
  attribute :foo, :readonly => :readonly
58
-
58
+
59
59
  def initialize
60
60
  super
61
61
  populate_attributes({ :foo => "foo" })
62
62
  end
63
63
  end
64
-
64
+
65
65
  setup do
66
66
  @model = ReadonlyModel.new
67
67
  end
68
-
68
+
69
69
  should "be readonly" do
70
70
  assert @model.readonly_attribute?(:foo)
71
71
  end
72
-
72
+
73
73
  should "allow reading" do
74
74
  assert_equal "foo", @model.foo
75
75
  end
76
-
76
+
77
77
  should "not allow writing" do
78
78
  assert_raises(NoMethodError) { @model.foo = "YO" }
79
79
  end
80
80
  end
81
-
81
+
82
82
  context "default values" do
83
83
  class DefaultModel < EmptyAttributeModel
84
84
  attribute :foo, :default => "FOO!"
85
85
  attribute :bar
86
86
  end
87
-
87
+
88
88
  setup do
89
89
  @model = DefaultModel.new
90
90
  end
91
-
91
+
92
92
  should "read default values" do
93
93
  assert_equal "FOO!", @model.foo
94
94
  assert_nil @model.bar
95
95
  end
96
96
  end
97
97
  end
98
-
98
+
99
99
  context "populating attributes" do
100
100
  setup do
101
101
  @model = AttributeModel.new
102
102
  end
103
-
103
+
104
104
  should "write all valid attributes" do
105
105
  new_attributes = {
106
106
  :foo => "zxcv",
107
107
  :bar => "qwerty"
108
108
  }
109
-
109
+
110
110
  @model.populate_attributes(new_attributes)
111
111
  new_attributes.each do |k,v|
112
112
  assert_equal v, @model.send(k)
113
113
  end
114
114
  end
115
115
  end
116
-
116
+
117
117
  context "reading and writing attributes" do
118
118
  setup do
119
119
  @model = AttributeModel.new
120
120
  @checkstring = "HEY"
121
121
  end
122
-
122
+
123
123
  should "be able to read an entire hash of attributes" do
124
124
  atts = @model.attributes
125
125
  assert atts.is_a?(Hash)
126
126
  assert atts.has_key?(:foo)
127
127
  assert atts.has_key?(:bar)
128
128
  end
129
-
129
+
130
130
  should "be able to write defined attributes" do
131
131
  assert_nothing_raised {
132
132
  @model.foo = @check_string
133
133
  }
134
134
  end
135
-
135
+
136
136
  should "be able to read defined attributes" do
137
137
  assert_nothing_raised {
138
138
  assert_equal "foo", @model.foo
139
139
  }
140
140
  end
141
-
141
+
142
142
  should "raise an error if attempting to write an undefined attribute" do
143
143
  assert_raises(NoMethodError) { @model.baz = @check_string }
144
144
  end
145
-
145
+
146
146
  should "raise an error if attempting to read an undefined attribute" do
147
147
  assert_raises(NoMethodError) { @model.baz }
148
148
  end