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 +2 -1
- data/.yardopts +3 -0
- data/Rakefile +15 -1
- data/Readme.md +3 -0
- data/TODO +0 -3
- data/VERSION +1 -1
- data/docs/GettingStarted.md +196 -0
- data/lib/virtualbox/abstract_model/attributable.rb +15 -16
- data/lib/virtualbox/abstract_model/dirty.rb +13 -2
- data/lib/virtualbox/abstract_model/relatable.rb +59 -10
- data/lib/virtualbox/abstract_model.rb +23 -1
- data/lib/virtualbox/attached_device.rb +144 -19
- data/lib/virtualbox/command.rb +11 -2
- data/lib/virtualbox/dvd.rb +52 -4
- data/lib/virtualbox/exceptions.rb +13 -0
- data/lib/virtualbox/hard_drive.rb +37 -8
- data/lib/virtualbox/image.rb +37 -0
- data/lib/virtualbox/nic.rb +1 -1
- data/lib/virtualbox/proxies/collection.rb +29 -0
- data/lib/virtualbox/storage_controller.rb +11 -0
- data/lib/virtualbox/vm.rb +105 -5
- data/lib/virtualbox.rb +2 -1
- data/test/virtualbox/abstract_model/dirty_test.rb +17 -0
- data/test/virtualbox/abstract_model/relatable_test.rb +49 -1
- data/test/virtualbox/abstract_model_test.rb +32 -1
- data/test/virtualbox/attached_device_test.rb +184 -2
- data/test/virtualbox/command_test.rb +36 -0
- data/test/virtualbox/dvd_test.rb +43 -0
- data/test/virtualbox/hard_drive_test.rb +52 -1
- data/test/virtualbox/image_test.rb +79 -0
- data/test/virtualbox/nic_test.rb +10 -0
- data/test/virtualbox/proxies/collection_test.rb +45 -0
- data/test/virtualbox/storage_controller_test.rb +12 -0
- data/test/virtualbox/vm_test.rb +118 -10
- data/virtualbox.gemspec +8 -3
- metadata +8 -3
- data/lib/virtualbox/errors.rb +0 -7
data/.gitignore
CHANGED
data/.yardopts
ADDED
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'
|
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
|
+
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
|
-
|
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
|
-
|
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
|
-
#
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
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
|
-
#
|
6
|
-
#
|
7
|
-
#
|
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
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
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
|
-
|
101
|
-
|
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
|