virtualbox 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,4 +2,5 @@ bin/*
2
2
  vendor/gems/*
3
3
  doc/*
4
4
  .yardoc/*
5
- pkg/*
5
+ pkg/*
6
+ test/coverage/*
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ docs/GettingStarted.md
3
+ TODO
data/Rakefile CHANGED
@@ -26,8 +26,22 @@ end
26
26
  begin
27
27
  require 'yard'
28
28
  YARD::Rake::YardocTask.new do |t|
29
- t.options = ['--main', 'Readme.md', '--markup', 'markdown', '--files', 'TODO']
29
+ t.options = ['--main', 'Readme.md', '--markup', 'markdown']
30
+ t.options += ['--title', 'VirtualBox Ruby Library Documentation']
30
31
  end
31
32
  rescue LoadError
32
33
  puts "Yard not available. Install it with: gem install yard"
34
+ end
35
+
36
+ begin
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |t|
39
+ t.libs << "test"
40
+ t.test_files = FileList["test/**/*_test.rb"]
41
+ t.output_dir = "test/coverage"
42
+ t.verbose = true
43
+ end
44
+ rescue LoadError
45
+ puts "Rcov not available. Coverage data tasks not available."
46
+ puts "Install it with: gem install rcov"
33
47
  end
data/Readme.md CHANGED
@@ -21,6 +21,9 @@ the gem, you must set the path to your `VBoxManage` binary:
21
21
  The virtualbox gem is modeled after ActiveRecord. If you've used ActiveRecord, you'll
22
22
  feel very comfortable using the virtualbox gem.
23
23
 
24
+ There is a [quick getting started guide](http://mitchellh.github.com/virtualbox/file.GettingStarted.html) to
25
+ get you acquainted with the conventions of the virtualbox gem.
26
+
24
27
  Complete documentation can be found at [http://mitchellh.github.com/virtualbox](http://mitchellh.github.com/virtualbox).
25
28
 
26
29
  Below are some examples:
data/TODO CHANGED
@@ -4,9 +4,6 @@ Not all these features will make it into initial releases of virtualbox ruby gem
4
4
  But they will definitely all make it for version 1.0.
5
5
 
6
6
  * Creating a VM from scratch (non-import)
7
- * Pause/Resume VMs
8
- * Exporting a VM
9
- * Modifying attached devices
10
7
  * Parsing bridged IFs
11
8
  * Shared folders
12
9
  * Snapshots
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
@@ -0,0 +1,196 @@
1
+ # Getting Started with the VirtualBox Gem
2
+
3
+ * [Basic Conventions](#basic-conventions)
4
+ * [Finding Models](#bc-finding-models)
5
+ * [Accessing Models](#bc-accessing-models)
6
+ * [Modifying Models](#bc-modifying-models)
7
+ * [Saving Models](#bc-saving-models)
8
+
9
+ <a name="basic-conventions"></a>
10
+ # Basic Conventions
11
+
12
+ The entire virtualbox library follows a few conventions to make sure
13
+ things work uniformly across the entire codebase, and so that nothing
14
+ should surprise any developers once they understand these conventions.
15
+
16
+ When browsing the documentation, you'll probably notice that a lot of the
17
+ classes inherit from {VirtualBox::AbstractModel}. This just means that all
18
+ these classes act the same way! Every {VirtualBox::AbstractModel AbstractModel}
19
+ shares the following behaviors:
20
+
21
+ * Finding
22
+ * Accessing
23
+ * Modifying
24
+ * Saving
25
+
26
+ These behaviors should be similar if not the exact same across all
27
+ virtualbox models. Each of these behaviors is covered below.
28
+
29
+ <a name="bc-finding-models"></a>
30
+ ## Finding Models
31
+
32
+ All data models have a `find` or `all` method (or sometimes both!) These
33
+ methods do what you expect them to: `all` will return an array of all instances
34
+ of that model which is typically unordered. `find` will allow you to find a
35
+ specific instance of that model, typically by name or UUID. Below are a couple
36
+ examples of this.
37
+
38
+ ### All
39
+
40
+ This example uses {VirtualBox::HardDrive}. As you can see, its just an
41
+ unmodified ruby `Array` which is returned by `all`. This can be used find,
42
+ sort, enumerate, etc.
43
+
44
+ drives = VirtualBox::HardDrive.all
45
+ puts "You have #{drives.length} hard drives!"
46
+
47
+ drives.each do |drive|
48
+ puts "Drive: #{drive.uuid}"
49
+ end
50
+
51
+ In the case that `all` returns an empty array, this simply means that none
52
+ of that model exist.
53
+
54
+ ### Find
55
+
56
+ This example uses {VirtualBox::VM}, which will probably be the most common
57
+ model you search for.
58
+
59
+ vm = VirtualBox::VM.find("MyVM")
60
+ puts "This VM has #{vm.memory} MB of RAM allocated to it."
61
+
62
+ Find can also be used with UUIDs:
63
+
64
+ vm = VirtualBox::VM.find("3d0f87b4-50f7-4fc5-ad89-93375b1b32a3")
65
+ puts "This VM's name is: #{vm.name}"
66
+
67
+ When a find fails, it will return `nil`.
68
+
69
+ <a name="bc-accessing-models"></a>
70
+ ## Accessing Models
71
+
72
+ Every model has an _attribute list_ associated with it. These attributes are
73
+ what can be accessed on the model via the typical ruby attribute accessing
74
+ syntax with the `.` (dot) operator. Because these methods are generated
75
+ dynamically, they don't show up as methods in the documentation. Because of this,
76
+ attributes are listed for every model in their overviews. For examples, see the
77
+ overviews of {VirtualBox::VM}, {VirtualBox::HardDrive}, etc.
78
+
79
+ In addition to an attribute list, many models also have _relationships_.
80
+ Relationships are, for our purposes, similar enough to attributes that they
81
+ can be treated the same. Relationship accessing methods are also dynamically
82
+ generated, so they are listed within the overviews of the models as well (if they
83
+ have any). Relationships allow two models to show that they are connected in some
84
+ way, and can therefore be accessed through each other.
85
+
86
+ ### Attributes
87
+
88
+ Reading attributes is simple. Let's use a {VirtualBox::VM} as an example:
89
+
90
+ vm = VirtualBox::VM.find("FooVM")
91
+
92
+ # Accessing attributes:
93
+ vm.memory
94
+ vm.name
95
+ vm.boot1
96
+ vm.ioapic
97
+
98
+ ### Relationships
99
+
100
+ Relationships are read the exact same way as attributes. Again using a
101
+ {VirtualBox::VM} as an example:
102
+
103
+ vm = VirtualBox::VM.find("FooVM")
104
+
105
+ # storage_controllers is a relationship containing an array of all the
106
+ # storage controllers on this VM
107
+ vm.storage_controllers.each do |sc|
108
+ puts "Storage Controller: #{sc.uuid}"
109
+ end
110
+
111
+ The difference from an attribute is that while attributes are typically ruby
112
+ primitives such as `String` or `Boolean`, relationship objects are always other
113
+ virtualbox models such as {VirtualBox::StorageController}.
114
+
115
+ <a name="bc-modifying-models"></a>
116
+ ## Modifying Models
117
+
118
+ In addition to simply reading attributes and relationships, most can be modified
119
+ as well. I say "most" because some attributes are `readonly` and some relationships
120
+ simply don't support being directly modified (though their objects may, I'll get to
121
+ this in a moment). By looking at the attribute list it is easy to spot a readonly
122
+ attribute, which will have the `:readonly` option set to `true`. Below is an example
123
+ of what you might see in the overview of some model:
124
+
125
+ attribute :uuid, :readonly => true
126
+
127
+ In the above case, you could read the `uuid` attribute as normal, but it wouldn't support
128
+ modification (and you'll simply get a `NoMethodError` if you try to set it).
129
+
130
+ Relationships are a little bit trickier, since when discussing modifying a relationship,
131
+ it could either be taken to mean the items _in_ the relationship, or the relationship
132
+ itself. A good rule of thumb, assuming there exists a relationship `foos`,is if you ever
133
+ want to do `object.foos =` something, then you're _modifying the relationship_ and _not_
134
+ the objects. But if you ever do `object.foos[0].destroy`, then you're _modifying the
135
+ relationship objects_ and _not_ the relationship itself.
136
+
137
+ ### Attributes
138
+
139
+ Attributes which support modification are modified like standard ruby attributes. The
140
+ following example uses {VirtualBox::HardDrive}:
141
+
142
+ hd = VirtualBox::HardDrive.new
143
+ hd.size = 2000 # megabytes
144
+ hd.format = "VMDK"
145
+
146
+ As you can see, there is nothing sneaky going on here, and does what you expect.
147
+
148
+ ### Relationships
149
+
150
+ Modifying relationships, on the other hand, is a little different. If the model supports
151
+ modifying the relationship (which it'll note in its respective documentation), then
152
+ you can set it just like an attribute. Below, we use {VirtualBox::AttachedDevice} as
153
+ an example:
154
+
155
+ ad = VirtualBox::AttachedDevice.new
156
+
157
+ # Attached devices have an image relationship
158
+ ad.image = VirtualBox::DVD.empty_drive
159
+
160
+ If a relationship doesn't support setting it, it will raise a {VirtualBox::Exceptions::NonSettableRelationshipException}.
161
+
162
+ **Note**: Below is an example of modifying a relationship object, rather than a
163
+ relationship itself. The example below uses {VirtualBox::VM}.
164
+
165
+ vm = VirtualBox::VM.find("FooVM")
166
+ vm.storage_controllers[0].name = "Foo Controller"
167
+
168
+ <a name="bc-saving-models"></a>
169
+ ## Saving Models
170
+
171
+ Saving models is _really_ easy: you simply call `save`. That's all! Well, there are
172
+ some subtleties, but that's the basic idea. `save` will typically **also save relationships**
173
+ so if you modify a relationship object or relationship itself, calling `save` on the
174
+ parent object will typically save the relationships as well. `save` always returns
175
+ `true` or `false` depending on whether the operation was a success or not. If you'd like
176
+ instead to know why a `save` failed, you can call the method with a `true` parameter
177
+ which sets `raise_errors` to `true` and will raise a {VirtualBox::Exceptions::CommandFailedException}
178
+ if there is a failure. The message on this object contains the reason.
179
+
180
+ Below is an example of saving a simple {VirtualBox::VM} object:
181
+
182
+ vm = VirtualBox::VM.find("FooVM")
183
+
184
+ # Double the memory
185
+ vm.memory = vm.memory.to_i * 2
186
+
187
+ # This will return true/false depending on success
188
+ vm.save
189
+
190
+ Below is an example where an exception will be raised if an error occurs:
191
+
192
+ vm = VirtualBox::VM.find("FooVM")
193
+ vm.memory = "INVALID"
194
+
195
+ # This will raise an exception, since the memory is invalid
196
+ vm.save(true)
@@ -92,7 +92,21 @@ module VirtualBox
92
92
  # @option options [Symbol] :populate_key (attribute name) Specifies
93
93
  # a custom populate key to use for {Attributable#populate_attributes}
94
94
  def attribute(name, options = {})
95
- attributes[name.to_sym] = options
95
+ name = name.to_sym
96
+ attributes[name] = options
97
+
98
+ # Create the method for reading this attribute
99
+ define_method(name) { read_attribute(name) }
100
+
101
+ # Create the writer method for it unless the attribute is readonly,
102
+ # then remove the method if it exists
103
+ if !options[:readonly]
104
+ define_method("#{name}=") do |value|
105
+ write_attribute(name, value)
106
+ end
107
+ elsif method_defined?("#{name}=")
108
+ undef_method("#{name}=")
109
+ end
96
110
  end
97
111
 
98
112
  # Returns the hash of attributes and their associated options.
@@ -173,21 +187,6 @@ module VirtualBox
173
187
  name = name.to_sym
174
188
  has_attribute?(name) && self.class.attributes[name][:readonly]
175
189
  end
176
-
177
- # Method missing is used to add dynamic handlers for accessors and
178
- # setters. Due to the general ugliness of `method_missing`, this may
179
- # change in the near future, but for now it is what it is.
180
- def method_missing(meth, *args)
181
- meth_string = meth.to_s
182
-
183
- if has_attribute?(meth)
184
- read_attribute(meth)
185
- elsif meth_string =~ /^(.+?)=$/ && has_attribute?($1) && !readonly_attribute?($1)
186
- write_attribute($1.to_sym, *args)
187
- else
188
- super
189
- end
190
- end
191
190
  end
192
191
  end
193
192
  end
@@ -74,6 +74,13 @@ module VirtualBox
74
74
  # obj.clear_dirty!(:name)
75
75
  # obj.name_changed? # => false
76
76
  #
77
+ # If no specific field is speciied, `clear_dirty!` will clear the dirty
78
+ # status on the entire model.
79
+ #
80
+ # obj.changed? # => assume true
81
+ # obj.clear_dirty!
82
+ # obj.changed? # => false
83
+ #
77
84
  module Dirty
78
85
  # Manages dirty state for an attribute. This method will handle
79
86
  # setting the dirty state of an attribute (or even clearing it
@@ -104,8 +111,12 @@ module VirtualBox
104
111
  # Clears dirty state for a field.
105
112
  #
106
113
  # @param [Symbol] key The field to clear dirty state.
107
- def clear_dirty!(key)
108
- changes.delete(key)
114
+ def clear_dirty!(key=nil)
115
+ if key.nil?
116
+ @changed_attributes = {}
117
+ else
118
+ changes.delete(key)
119
+ end
109
120
  end
110
121
 
111
122
  # Ignores any dirty changes during the duration of the block.
@@ -39,6 +39,37 @@ module VirtualBox
39
39
  # # Accessing through an instance "instance"
40
40
  # instance.bacons # => whatever Bacon.populate_relationship created
41
41
  #
42
+ # # Settable Relationships
43
+ #
44
+ # It is often convenient that relationships become "settable." That is,
45
+ # for a relationship `foos`, there would exist a `foos=` method. This is
46
+ # possible by implementing the `set_relationship` method on the relationship
47
+ # class. Consider the following relationship:
48
+ #
49
+ # relationship :foos, Foo
50
+ #
51
+ # If `Foo` has the `set_relationship` method, then it will be called by
52
+ # `foos=`. It is expected to return the new value for the relationship. To
53
+ # facilitate this need, the `set_relationship` method is given three
54
+ # parameters: caller, old value, and new value. An example implementation,
55
+ # albeit a silly one, is below:
56
+ #
57
+ # class Foo
58
+ # def self.set_relationship(caller, old_value, new_value)
59
+ # return "Changed to: #{new_value}"
60
+ # end
61
+ # end
62
+ #
63
+ # In this case, the following behavior would occur:
64
+ #
65
+ # instance.foos # => assume "foo"
66
+ # instance.foos = "bar"
67
+ # instance.foos # => "Changed to: bar"
68
+ #
69
+ # If the relationship class _does not implement_ the `set_relationship`
70
+ # method, then a {Exceptions::NonSettableRelationshipException} will be raised if
71
+ # a user attempts to set that relationship.
72
+ #
42
73
  # # Dependent Relationships
43
74
  #
44
75
  # By setting `:dependent => :destroy` on relationships, {AbstractModel}
@@ -63,8 +94,16 @@ module VirtualBox
63
94
  # @option options [Symbol] :dependent (nil) - If set to `:destroy`
64
95
  # {AbstractModel#destroy} will propagate through to relationships.
65
96
  def relationship(name, klass, options = {})
97
+ name = name.to_sym
98
+
66
99
  @relationships ||= {}
67
100
  @relationships[name] = { :klass => klass }.merge(options)
101
+
102
+ # Define the method to read the relationship
103
+ define_method(name) { relationship_data[name] }
104
+
105
+ # Define the method to set the relationship
106
+ define_method("#{name}=") { |*args| set_relationship(name, *args) }
68
107
  end
69
108
 
70
109
  # Returns a hash of all the relationships.
@@ -147,16 +186,26 @@ module VirtualBox
147
186
  self.class.relationships.has_key?(key.to_sym)
148
187
  end
149
188
 
150
- # Method missing is used to add dynamic handlers for relationship
151
- # accessors.
152
- def method_missing(meth, *args)
153
- meth_string = meth.to_s
154
-
155
- if has_relationship?(meth)
156
- relationship_data[meth.to_sym]
157
- else
158
- super
159
- end
189
+ # Sets a relationship to the given value. This is not guaranteed to
190
+ # do anything, since "set_relationship" will be called on the class
191
+ # that the relationship is associated with and its expected to return
192
+ # the resulting relationship to set.
193
+ #
194
+ # If the relationship class doesn't respond to the set_relationship
195
+ # method, then an exception {Exceptions::NonSettableRelationshipException} will
196
+ # be raised.
197
+ #
198
+ # This method is called by the "magic" method of `relationship=`.
199
+ #
200
+ # @param [Symbol] key Relationship key.
201
+ # @param [Object] value The new value of the relationship.
202
+ def set_relationship(key, value)
203
+ key = key.to_sym
204
+ relationship = self.class.relationships[key]
205
+ return unless relationship
206
+
207
+ raise Exceptions::NonSettableRelationshipException.new unless relationship[:klass].respond_to?(:set_relationship)
208
+ relationship_data[key] = relationship[:klass].set_relationship(self, relationship_data[key], value)
160
209
  end
161
210
  end
162
211
  end
@@ -18,10 +18,24 @@ module VirtualBox
18
18
  # is {HardDrive#save} which will create a new hard drive if it didn't
19
19
  # previously exist, or save an old one if it did exist.
20
20
  def new_record?
21
- @new_record = true if @new_record.nil?
21
+ new_record! if @new_record.nil?
22
22
  @new_record
23
23
  end
24
24
 
25
+ # Explicitly resets the model to a new record. If you're using this
26
+ # method outside of virtualbox library core, you should really be
27
+ # asking yourself "why?"
28
+ def new_record!
29
+ @new_record = true
30
+ end
31
+
32
+ # Explicitly sets the model to not be a new record. If you're using
33
+ # this method outside of virtualbox library core, you should really
34
+ # be asking yourself "why?"
35
+ def existing_record!
36
+ @new_record = false
37
+ end
38
+
25
39
  # Saves the model attributes and relationships.
26
40
  #
27
41
  # The method can be passed any arbitrary arguments, which are
@@ -81,6 +95,14 @@ module VirtualBox
81
95
  super
82
96
  end
83
97
 
98
+ # Overwrites {Relatable#set_relationship} to set the dirty state of the
99
+ # relationship. See {Dirty#set_dirty!} as well.
100
+ def set_relationship(key, value)
101
+ existing = relationship_data[key]
102
+ new_value = super
103
+ set_dirty!(key, existing, new_value)
104
+ end
105
+
84
106
  # Destroys the model. The exact behaviour of this method is expected to be
85
107
  # defined on the subclasses. This method on AbstractModel simply
86
108
  # propagates the destroy to the dependent relationships. For more information
@@ -2,9 +2,39 @@ module VirtualBox
2
2
  # Represents an device which is attached to a storage controller. An example
3
3
  # of such a device would be a CD or hard drive attached to an IDE controller.
4
4
  #
5
- # **Currently, attached devices can not be created from scratch. The only way
6
- # to access them is through relationships with other models such as
7
- # {StorageController}.**
5
+ # # Creating a New Attached Device
6
+ #
7
+ # Creating a new attached device is simple. The following is a simple example
8
+ # of creating a DVD with an empty drive:
9
+ #
10
+ # ad = VirtualBox::AttachedDevice.new
11
+ # ad.port = 0
12
+ # ad.image = VirtualBox::DVD.empty_drive
13
+ # storage_controller.devices << ad
14
+ # ad.save
15
+ #
16
+ # The only quirk is that the attached device **must** be attached to a
17
+ # storage controller. The above assumes that `storage_controller` exists,
18
+ # which adds the device.
19
+ #
20
+ # Any {Image} subclass can be set to the `image` relationship.
21
+ #
22
+ # The following is an example using {VM.find}:
23
+ #
24
+ # # First creating the new device...
25
+ # ad = VirtualBox::AttachedDevice.new
26
+ # ad.port = 0
27
+ # ad.image = VirtualBox::DVD.empty_drive
28
+ #
29
+ # # Now attaching to existing VM
30
+ # vm = VirtualBox::VM.find("FooVM")
31
+ # vm.storage_controllers[0].devices << ad
32
+ # vm.save
33
+ #
34
+ # The interesting thing in this example is that the `save` method is called on
35
+ # the virtual machine rather than the AttachedDevice. This will actually work
36
+ # as expected! Saving a virtual machine automatically saves all it's relationships
37
+ # as well.
8
38
  #
9
39
  # # Attributes and Relationships
10
40
  #
@@ -40,8 +70,7 @@ module VirtualBox
40
70
  #
41
71
  class AttachedDevice < AbstractModel
42
72
  attribute :parent, :readonly => true
43
- attribute :uuid
44
- attribute :medium
73
+ attribute :uuid, :readonly => true
45
74
  attribute :port
46
75
  relationship :image, Image
47
76
 
@@ -52,7 +81,7 @@ module VirtualBox
52
81
  #
53
82
  # @return [Array<AttachedDevice>]
54
83
  def populate_relationship(caller, data)
55
- relation = []
84
+ relation = Proxies::Collection.new(caller)
56
85
 
57
86
  counter = 0
58
87
  loop do
@@ -71,20 +100,87 @@ module VirtualBox
71
100
  def destroy_relationship(caller, data, *args)
72
101
  data.each { |v| v.destroy(*args) }
73
102
  end
103
+
104
+ # Saves the relationship. This simply calls {#save} on every
105
+ # member of the relationship.
106
+ #
107
+ # **This method typically won't be used except internally.**
108
+ def save_relationship(caller, data)
109
+ # Just call save on each nic with the VM
110
+ data.each do |ad|
111
+ ad.save
112
+ end
113
+ end
74
114
  end
75
115
 
76
- # Since attached devices can not be created from scratch yet, this
77
- # method should never be called. Instead access attached devices
78
- # through relationships from other models such as {StorageController}.
79
- def initialize(index, caller, data)
116
+ # @overload initialize(data={})
117
+ # Creates a new AttachedDevice which is a new record. This
118
+ # should be attached to a storage controller and saved.
119
+ # @param [Hash] data (optional) A hash which contains initial attribute
120
+ # values for the AttachedDevice.
121
+ # @overload initialize(index, caller, data)
122
+ # Creates an AttachedDevice for a relationship. **This should
123
+ # never be called except internally.**
124
+ # @param [Integer] index Index of the port
125
+ # @param [Object] caller The parent
126
+ # @param [Hash] data A hash of data which must be used
127
+ # to extract the relationship data.
128
+ def initialize(*args)
80
129
  super()
81
130
 
82
- populate_attributes({
83
- :parent => caller,
84
- :port => index,
85
- :medium => data["#{caller.name}-#{index}-0".downcase.to_sym],
86
- :uuid => data["#{caller.name}-ImageUUID-#{index}-0".downcase.to_sym]
87
- })
131
+ if args.length == 3
132
+ populate_from_data(*args)
133
+ elsif args.length == 1
134
+ populate_attributes(*args)
135
+ new_record!
136
+ elsif args.empty?
137
+ return
138
+ else
139
+ raise NoMethodError.new
140
+ end
141
+ end
142
+
143
+ # Saves or creates an attached device.
144
+ #
145
+ # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
146
+ # will be raised if the command failed.
147
+ # @return [Boolean] True if command was successful, false otherwise.
148
+ def save(raise_errors=false)
149
+ raise Exceptions::NoParentException.new if parent.nil?
150
+ raise Exceptions::InvalidObjectException.new("Image must be set") if image.nil?
151
+ return true unless changed?
152
+
153
+ # If the port changed, we have to destroy the old one, then create
154
+ # a new one
155
+ destroy({:port => port_was}, raise_errors) if port_changed? && !port_was.nil?
156
+
157
+ Command.vboxmanage("storageattach #{Command.shell_escape(parent.parent.name)} --storagectl #{Command.shell_escape(parent.name)} --port #{port} --device 0 --type #{image.image_type} --medium #{medium}")
158
+ existing_record!
159
+ clear_dirty!
160
+
161
+ true
162
+ rescue Exceptions::CommandFailedException
163
+ raise if raise_errors
164
+ false
165
+ end
166
+
167
+ # Medium of the attached image. This attribute will be dependent
168
+ # on the attached image and will return one of the following values:
169
+ #
170
+ # * **none** - There is no attached image
171
+ # * **emptydrive** - An image with an empty drive is attached (see
172
+ # {DVD.empty_drive})
173
+ # * **image uuid** - The image's UUID
174
+ #
175
+ # @return [String]
176
+ def medium
177
+ if image.nil?
178
+ "none"
179
+ elsif image.empty_drive?
180
+ "emptydrive"
181
+ else
182
+ image.uuid
183
+ end
88
184
  end
89
185
 
90
186
  # Destroys the attached device. By default, this only removes any
@@ -94,11 +190,40 @@ module VirtualBox
94
190
  #
95
191
  # @option options [Boolean] :destroy_image (false) If true, will also
96
192
  # destroy the image associated with device.
97
- def destroy(options={})
193
+ # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
194
+ # will be raised if the command failed.
195
+ # @return [Boolean] True if command was successful, false otherwise.
196
+ def destroy(options={}, raise_errors=false)
98
197
  # parent = storagecontroller
99
198
  # parent.parent = vm
100
- Command.vboxmanage("storageattach #{Command.shell_escape(parent.parent.name)} --storagectl #{Command.shell_escape(parent.name)} --port #{port} --device 0 --medium none")
101
- image.destroy if options[:destroy_image] && image
199
+ destroy_port = options[:port] || port
200
+ Command.vboxmanage("storageattach #{Command.shell_escape(parent.parent.name)} --storagectl #{Command.shell_escape(parent.name)} --port #{destroy_port} --device 0 --medium none")
201
+ image.destroy(raise_errors) if options[:destroy_image] && image
202
+ rescue Exceptions::CommandFailedException
203
+ raise if raise_errors
204
+ false
205
+ end
206
+
207
+ # Relationship callback when added to a collection. This is automatically
208
+ # called by any relationship collection when this object is added.
209
+ def added_to_relationship(parent)
210
+ write_attribute(:parent, parent)
211
+ end
212
+
213
+ protected
214
+
215
+ # Populates the model based on data from a parsed vminfo. This
216
+ # method is used to create a model which already exists and is
217
+ # part of a relationship.
218
+ #
219
+ # **This method should never be called except internally.**
220
+ def populate_from_data(index, caller, data)
221
+ populate_attributes({
222
+ :parent => caller,
223
+ :port => index,
224
+ :medium => data["#{caller.name}-#{index}-0".downcase.to_sym],
225
+ :uuid => data["#{caller.name}-ImageUUID-#{index}-0".downcase.to_sym]
226
+ })
102
227
  end
103
228
  end
104
229
  end