virtualbox 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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