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.
- data/Readme.md +9 -9
- data/VERSION +1 -1
- data/docs/GettingStarted.md +11 -11
- data/docs/WhatsNew.md +1 -1
- data/lib/virtualbox.rb +10 -1
- data/lib/virtualbox/abstract_model.rb +47 -29
- data/lib/virtualbox/abstract_model/attributable.rb +16 -16
- data/lib/virtualbox/abstract_model/dirty.rb +10 -10
- data/lib/virtualbox/abstract_model/relatable.rb +22 -22
- data/lib/virtualbox/abstract_model/validatable.rb +4 -4
- data/lib/virtualbox/attached_device.rb +23 -23
- data/lib/virtualbox/command.rb +9 -9
- data/lib/virtualbox/dvd.rb +7 -7
- data/lib/virtualbox/ext/subclass_listing.rb +1 -1
- data/lib/virtualbox/extra_data.rb +17 -17
- data/lib/virtualbox/forwarded_port.rb +23 -23
- data/lib/virtualbox/hard_drive.rb +27 -27
- data/lib/virtualbox/image.rb +25 -18
- data/lib/virtualbox/nic.rb +22 -22
- data/lib/virtualbox/proxies/collection.rb +4 -4
- data/lib/virtualbox/shared_folder.rb +25 -25
- data/lib/virtualbox/storage_controller.rb +16 -16
- data/lib/virtualbox/vm.rb +95 -42
- data/test/virtualbox/abstract_model/attributable_test.rb +28 -28
- data/test/virtualbox/abstract_model/dirty_test.rb +13 -13
- data/test/virtualbox/abstract_model/relatable_test.rb +36 -36
- data/test/virtualbox/abstract_model/validatable_test.rb +22 -22
- data/test/virtualbox/abstract_model_test.rb +46 -36
- data/test/virtualbox/attached_device_test.rb +47 -47
- data/test/virtualbox/command_test.rb +12 -12
- data/test/virtualbox/dvd_test.rb +15 -19
- data/test/virtualbox/ext/subclass_listing_test.rb +2 -2
- data/test/virtualbox/extra_data_test.rb +37 -37
- data/test/virtualbox/forwarded_port_test.rb +31 -31
- data/test/virtualbox/hard_drive_test.rb +40 -48
- data/test/virtualbox/image_test.rb +36 -33
- data/test/virtualbox/nic_test.rb +22 -22
- data/test/virtualbox/proxies/collection_test.rb +6 -6
- data/test/virtualbox/shared_folder_test.rb +36 -36
- data/test/virtualbox/storage_controller_test.rb +14 -14
- data/test/virtualbox/vm_test.rb +121 -70
- data/test/virtualbox_test.rb +19 -0
- data/virtualbox.gemspec +5 -3
- metadata +4 -2
data/lib/virtualbox/image.rb
CHANGED
@@ -16,11 +16,11 @@ module VirtualBox
|
|
16
16
|
# @abstract
|
17
17
|
class Image < AbstractModel
|
18
18
|
include SubclassListing
|
19
|
-
|
19
|
+
|
20
20
|
attribute :uuid, :readonly => true
|
21
21
|
attribute :location
|
22
22
|
attribute :accessible, :readonly => true
|
23
|
-
|
23
|
+
|
24
24
|
class <<self
|
25
25
|
# Parses the raw output of virtualbox into image objects. Used by
|
26
26
|
# subclasses to parse the output of their respective listing functions.
|
@@ -31,7 +31,7 @@ module VirtualBox
|
|
31
31
|
def parse_raw(raw)
|
32
32
|
parse_blocks(raw).collect { |v| new(v) }
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
# Parses the blocks of the output from virtualbox. VirtualBox outputs
|
36
36
|
# image listing in "blocks" which are then parsed down to their attributes.
|
37
37
|
#
|
@@ -41,8 +41,8 @@ module VirtualBox
|
|
41
41
|
def parse_blocks(raw)
|
42
42
|
raw.split(/\n\n/).collect { |v| parse_block(v.chomp) }.compact
|
43
43
|
end
|
44
|
-
|
45
|
-
# Parses a single block from VirtualBox output.
|
44
|
+
|
45
|
+
# Parses a single block from VirtualBox output.
|
46
46
|
#
|
47
47
|
# **This method typically won't be used except internally.**
|
48
48
|
#
|
@@ -57,14 +57,14 @@ module VirtualBox
|
|
57
57
|
next unless line =~ /^(.+?):\s+(.+?)$/
|
58
58
|
hd[$1.downcase.to_sym] = $2.to_s
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
# If we don't have a location but have a path, use that, as they
|
62
62
|
# are equivalent but not consistent.
|
63
63
|
hd[:location] = hd[:path] if hd.has_key?(:path)
|
64
|
-
|
64
|
+
|
65
65
|
hd
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
# Searches the subclasses which implement all method, searching for
|
69
69
|
# a matching UUID and returning that as the relationship.
|
70
70
|
#
|
@@ -74,17 +74,17 @@ module VirtualBox
|
|
74
74
|
def populate_relationship(caller, data)
|
75
75
|
return DVD.empty_drive if data[:medium] == "emptydrive"
|
76
76
|
return nil if data[:uuid].nil?
|
77
|
-
|
77
|
+
|
78
78
|
subclasses.each do |subclass|
|
79
79
|
next unless subclass.respond_to?(:all)
|
80
|
-
|
80
|
+
|
81
81
|
matching = subclass.all.find { |obj| obj.uuid == data[:uuid] }
|
82
82
|
return matching unless matching.nil?
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
nil
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
# Sets an image onto a relationship and/or removes it from a
|
89
89
|
# relationship. This method is automatically called by {Relatable}.
|
90
90
|
#
|
@@ -95,29 +95,29 @@ module VirtualBox
|
|
95
95
|
# We don't actually destroy any images using this method,
|
96
96
|
# so just return the new value as long as its a valid object
|
97
97
|
raise Exceptions::InvalidRelationshipObjectException.new if new_value && !new_value.is_a?(Image)
|
98
|
-
|
98
|
+
|
99
99
|
return new_value
|
100
100
|
end
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
# **This should never be called directly on {Image}.** Instead, initialize
|
104
104
|
# one of the subclasses.
|
105
105
|
def initialize(info=nil)
|
106
106
|
super()
|
107
|
-
|
107
|
+
|
108
108
|
populate_attributes(info) if info
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
# The image type as a string for the virtualbox command line. This
|
112
112
|
# method should be overridden by any subclass and is expected to
|
113
|
-
# return the type which is used in command line parameters for
|
113
|
+
# return the type which is used in command line parameters for
|
114
114
|
# attaching to storage controllers.
|
115
115
|
#
|
116
116
|
# @return [String]
|
117
117
|
def image_type
|
118
118
|
raise "This must be implemented by any subclasses"
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
# Returns boolean showing if empty drive or not. This method should be
|
122
122
|
# overriden by any subclass and is expected to return true of false
|
123
123
|
# showing if this image represents an empty drive of whatever type
|
@@ -127,5 +127,12 @@ module VirtualBox
|
|
127
127
|
def empty_drive?
|
128
128
|
false
|
129
129
|
end
|
130
|
+
|
131
|
+
# Returns the basename of the images location (the file name +extension)
|
132
|
+
#
|
133
|
+
# @return [String]
|
134
|
+
def filename
|
135
|
+
File.basename(location.to_s)
|
136
|
+
end
|
130
137
|
end
|
131
138
|
end
|
data/lib/virtualbox/nic.rb
CHANGED
@@ -18,7 +18,7 @@ module VirtualBox
|
|
18
18
|
#
|
19
19
|
# Properties of the model are exposed using standard ruby instance
|
20
20
|
# methods which are generated on the fly. Because of this, they are not listed
|
21
|
-
# below as available instance methods.
|
21
|
+
# below as available instance methods.
|
22
22
|
#
|
23
23
|
# These attributes can be accessed and modified via standard ruby-style
|
24
24
|
# `instance.attribute` and `instance.attribute=` methods. The attributes are
|
@@ -39,7 +39,7 @@ module VirtualBox
|
|
39
39
|
attribute :macaddress
|
40
40
|
attribute :cableconnected
|
41
41
|
attribute :bridgeadapter
|
42
|
-
|
42
|
+
|
43
43
|
class <<self
|
44
44
|
# Retrives the Nic data from human-readable vminfo. Since some data about
|
45
45
|
# nics is not exposed in the machine-readable virtual machine info, some
|
@@ -51,16 +51,16 @@ module VirtualBox
|
|
51
51
|
# @return [Hash]
|
52
52
|
def nic_data(vmname)
|
53
53
|
raw = VM.human_info(vmname)
|
54
|
-
|
54
|
+
|
55
55
|
# Complicated chain of methods just maps parse_nic over each line,
|
56
56
|
# removing invalid ones, and then converting it into a single hash.
|
57
57
|
raw.split("\n").collect { |v| parse_nic(v) }.compact.inject({}) do |acc, obj|
|
58
58
|
acc.merge({ obj[0] => obj[1] })
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
# Parses nic data out of a single line of the human readable output
|
63
|
-
# of vm info.
|
63
|
+
# of vm info.
|
64
64
|
#
|
65
65
|
# **This method typically won't be used except internally.**
|
66
66
|
#
|
@@ -68,18 +68,18 @@ module VirtualBox
|
|
68
68
|
def parse_nic(raw)
|
69
69
|
return unless raw =~ /^NIC\s(\d):\s+(.+?)$/
|
70
70
|
return if $2.to_s.strip == "disabled"
|
71
|
-
|
71
|
+
|
72
72
|
data = {}
|
73
73
|
nicname = "nic#{$1}"
|
74
74
|
$2.to_s.split(/,\s+/).each do |raw_property|
|
75
75
|
next unless raw_property =~ /^(.+?):\s+(.+?)$/
|
76
|
-
|
76
|
+
|
77
77
|
data[$1.downcase.to_sym] = $2.to_s
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
return nicname.to_sym, data
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
# Populates the nic relationship for anything which is related to it.
|
84
84
|
#
|
85
85
|
# **This method typically won't be used except internally.**
|
@@ -87,25 +87,25 @@ module VirtualBox
|
|
87
87
|
# @return [Array<Nic>]
|
88
88
|
def populate_relationship(caller, data)
|
89
89
|
nic_data = nic_data(caller.name)
|
90
|
-
|
90
|
+
|
91
91
|
relation = []
|
92
|
-
|
92
|
+
|
93
93
|
counter = 1
|
94
94
|
loop do
|
95
95
|
break unless data["nic#{counter}".to_sym]
|
96
|
-
|
96
|
+
|
97
97
|
nictype = nic_data["nic#{counter}".to_sym][:type] rescue nil
|
98
|
-
|
98
|
+
|
99
99
|
nic = new(counter, caller, data.merge({
|
100
100
|
"nictype#{counter}".to_sym => nictype
|
101
101
|
}))
|
102
102
|
relation.push(nic)
|
103
103
|
counter += 1
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
relation
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
# Saves the relationship. This simply calls {#save} on every
|
110
110
|
# member of the relationship.
|
111
111
|
#
|
@@ -117,32 +117,32 @@ module VirtualBox
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
end
|
120
|
-
|
121
|
-
# Since there is currently no way to create a _new_ nic, this is
|
120
|
+
|
121
|
+
# Since there is currently no way to create a _new_ nic, this is
|
122
122
|
# only used internally. Developers should NOT try to initialize their
|
123
123
|
# own nic objects.
|
124
124
|
def initialize(index, caller, data)
|
125
125
|
super()
|
126
|
-
|
126
|
+
|
127
127
|
@index = index
|
128
|
-
|
128
|
+
|
129
129
|
# Setup the index specific attributes
|
130
130
|
populate_data = {}
|
131
131
|
self.class.attributes.each do |name, options|
|
132
132
|
value = data["#{name}#{index}".to_sym]
|
133
133
|
populate_data[name] = value
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
populate_attributes(populate_data.merge({
|
137
137
|
:parent => caller
|
138
138
|
}))
|
139
139
|
end
|
140
|
-
|
140
|
+
|
141
141
|
# Saves a single attribute of the nic. This method is automatically
|
142
142
|
# called on {#save}.
|
143
143
|
#
|
144
144
|
# **This method typically won't be used except internally.**
|
145
|
-
def save_attribute(key, value, vmname)
|
145
|
+
def save_attribute(key, value, vmname)
|
146
146
|
Command.vboxmanage("modifyvm #{Command.shell_escape(vmname)} --#{key}#{@index} #{Command.shell_escape(value)}")
|
147
147
|
super
|
148
148
|
end
|
@@ -5,21 +5,21 @@ module VirtualBox
|
|
5
5
|
class Collection < Array
|
6
6
|
def initialize(parent)
|
7
7
|
super()
|
8
|
-
|
8
|
+
|
9
9
|
@parent = parent
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def <<(item)
|
13
13
|
item.added_to_relationship(@parent) if item.respond_to?(:added_to_relationship)
|
14
14
|
push(item)
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def clear
|
18
18
|
each do |item|
|
19
19
|
delete(item)
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def delete(item)
|
24
24
|
return unless super
|
25
25
|
item.removed_from_relationship(@parent) if item.respond_to?(:removed_from_relationship)
|
@@ -2,7 +2,7 @@ module VirtualBox
|
|
2
2
|
# Represents a shared folder in VirtualBox. In VirtualBox, shared folders are a
|
3
3
|
# method for basically "symlinking" a folder on the guest system to a folder which
|
4
4
|
# exists on the host system. This allows for sharing of files across the virtual
|
5
|
-
# machine.
|
5
|
+
# machine.
|
6
6
|
#
|
7
7
|
# **Note:** Whenever modifying shared folders on a VM, the changes won't take
|
8
8
|
# effect until a _cold reboot_ occurs. This means actually closing the virtual
|
@@ -36,7 +36,7 @@ module VirtualBox
|
|
36
36
|
# a local variable named `vm`.**
|
37
37
|
#
|
38
38
|
# Nothing tricky here: You treat existing shared folder objects just as if they
|
39
|
-
# were new ones. Assign a new name and/or a new path, then save.
|
39
|
+
# were new ones. Assign a new name and/or a new path, then save.
|
40
40
|
#
|
41
41
|
# folder = vm.shared_folders.first
|
42
42
|
# folder.name = "rufus"
|
@@ -65,7 +65,7 @@ module VirtualBox
|
|
65
65
|
#
|
66
66
|
# Properties of the model are exposed using standard ruby instance
|
67
67
|
# methods which are generated on the fly. Because of this, they are not listed
|
68
|
-
# below as available instance methods.
|
68
|
+
# below as available instance methods.
|
69
69
|
#
|
70
70
|
# These attributes can be accessed and modified via standard ruby-style
|
71
71
|
# `instance.attribute` and `instance.attribute=` methods. The attributes are
|
@@ -88,7 +88,7 @@ module VirtualBox
|
|
88
88
|
attribute :parent, :readonly => :readonly
|
89
89
|
attribute :name, :populate_key => "SharedFolderNameMachineMapping"
|
90
90
|
attribute :hostpath, :populate_key => "SharedFolderPathMachineMapping"
|
91
|
-
|
91
|
+
|
92
92
|
class <<self
|
93
93
|
# Populates the shared folder relationship for anything which is related to it.
|
94
94
|
#
|
@@ -97,19 +97,19 @@ module VirtualBox
|
|
97
97
|
# @return [Array<SharedFolder>]
|
98
98
|
def populate_relationship(caller, data)
|
99
99
|
relation = Proxies::Collection.new(caller)
|
100
|
-
|
100
|
+
|
101
101
|
counter = 1
|
102
102
|
loop do
|
103
103
|
break unless data["SharedFolderNameMachineMapping#{counter}".downcase.to_sym]
|
104
|
-
|
104
|
+
|
105
105
|
folder = new(counter, caller, data)
|
106
106
|
relation.push(folder)
|
107
107
|
counter += 1
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
relation
|
111
111
|
end
|
112
|
-
|
112
|
+
|
113
113
|
# Saves the relationship. This simply calls {#save} on every
|
114
114
|
# member of the relationship.
|
115
115
|
#
|
@@ -121,7 +121,7 @@ module VirtualBox
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
end
|
124
|
-
|
124
|
+
|
125
125
|
# @overload initialize(data={})
|
126
126
|
# Creates a new SharedFolder which is a new record. This
|
127
127
|
# should be attached to a VM and saved.
|
@@ -136,7 +136,7 @@ module VirtualBox
|
|
136
136
|
# to extract the relationship data.
|
137
137
|
def initialize(*args)
|
138
138
|
super()
|
139
|
-
|
139
|
+
|
140
140
|
if args.length == 3
|
141
141
|
initialize_for_relationship(*args)
|
142
142
|
elsif args.length == 1
|
@@ -147,7 +147,7 @@ module VirtualBox
|
|
147
147
|
raise NoMethodError.new
|
148
148
|
end
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
# Initializes the record for use in a relationship. This
|
152
152
|
# is automatically called by {#initialize} if it has three
|
153
153
|
# parameters.
|
@@ -161,12 +161,12 @@ module VirtualBox
|
|
161
161
|
value = data["#{key}#{index}".downcase.to_sym]
|
162
162
|
populate_data[key] = value
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
populate_attributes(populate_data.merge({
|
166
166
|
:parent => caller
|
167
167
|
}))
|
168
168
|
end
|
169
|
-
|
169
|
+
|
170
170
|
# Initializes a record with initial data but keeping it a "new
|
171
171
|
# record." This is called automatically if {#initialize} is given
|
172
172
|
# only a single parameter. View {#initialize} for documentation.
|
@@ -174,52 +174,52 @@ module VirtualBox
|
|
174
174
|
self.class.attributes.each do |name, options|
|
175
175
|
data[options[:populate_key]] = data[name]
|
176
176
|
end
|
177
|
-
|
177
|
+
|
178
178
|
populate_attributes(data)
|
179
179
|
new_record!
|
180
180
|
end
|
181
|
-
|
181
|
+
|
182
182
|
# Validates a shared folder.
|
183
183
|
def validate
|
184
184
|
super
|
185
|
-
|
185
|
+
|
186
186
|
validates_presence_of :parent
|
187
187
|
validates_presence_of :name
|
188
188
|
validates_presence_of :hostpath
|
189
189
|
end
|
190
|
-
|
190
|
+
|
191
191
|
# Saves or creates a shared folder.
|
192
192
|
#
|
193
193
|
# @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
|
194
194
|
# will be raised if the command failed.
|
195
195
|
# @return [Boolean] True if command was successful, false otherwise.
|
196
|
-
def save(raise_errors=false)
|
196
|
+
def save(raise_errors=false)
|
197
197
|
return true unless changed?
|
198
|
-
|
198
|
+
|
199
199
|
if !valid?
|
200
200
|
raise Exceptions::ValidationFailedException.new(errors) if raise_errors
|
201
201
|
return false
|
202
202
|
end
|
203
|
-
|
203
|
+
|
204
204
|
# If this isn't a new record, we destroy it first
|
205
205
|
destroy(raise_errors) if !new_record?
|
206
|
-
|
206
|
+
|
207
207
|
Command.vboxmanage("sharedfolder add #{Command.shell_escape(parent.name)} --name #{Command.shell_escape(name)} --hostpath #{Command.shell_escape(hostpath)}")
|
208
208
|
existing_record!
|
209
209
|
clear_dirty!
|
210
|
-
|
210
|
+
|
211
211
|
true
|
212
212
|
rescue Exceptions::CommandFailedException
|
213
213
|
raise if raise_errors
|
214
214
|
false
|
215
215
|
end
|
216
|
-
|
216
|
+
|
217
217
|
# Relationship callback when added to a collection. This is automatically
|
218
218
|
# called by any relationship collection when this object is added.
|
219
219
|
def added_to_relationship(parent)
|
220
220
|
write_attribute(:parent, parent)
|
221
221
|
end
|
222
|
-
|
222
|
+
|
223
223
|
# Destroys the shared folder. This doesn't actually delete the folder
|
224
224
|
# from the host system. Instead, it simply removes the mapping to the
|
225
225
|
# virtual machine, meaning it will no longer be possible to mount it
|
@@ -232,7 +232,7 @@ module VirtualBox
|
|
232
232
|
# If the name changed, we have to be sure to use the previous
|
233
233
|
# one.
|
234
234
|
name_value = name_changed? ? name_was : name
|
235
|
-
|
235
|
+
|
236
236
|
Command.vboxmanage("sharedfolder remove #{Command.shell_escape(parent.name)} --name #{Command.shell_escape(name_value)}")
|
237
237
|
true
|
238
238
|
rescue Exceptions::CommandFailedException
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module VirtualBox
|
2
2
|
# Represents a single storage controller which can be attached to a
|
3
3
|
# virtual machine.
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# **Currently, storage controllers can not be created from scratch.
|
6
6
|
# Therefore, the only way to use this model is through a relationship
|
7
7
|
# of a {VM} object.**
|
@@ -10,7 +10,7 @@ module VirtualBox
|
|
10
10
|
#
|
11
11
|
# Properties of the storage controller are exposed using standard ruby instance
|
12
12
|
# methods which are generated on the fly. Because of this, they are not listed
|
13
|
-
# below as available instance methods.
|
13
|
+
# below as available instance methods.
|
14
14
|
#
|
15
15
|
# These attributes can be accessed and modified via standard ruby-style
|
16
16
|
# `instance.attribute` and `instance.attribute=` methods. The attributes are
|
@@ -46,7 +46,7 @@ module VirtualBox
|
|
46
46
|
attribute :max_ports, :populate_key => :maxportcount
|
47
47
|
attribute :ports, :populate_key => :portcount
|
48
48
|
relationship :devices, AttachedDevice, :dependent => :destroy
|
49
|
-
|
49
|
+
|
50
50
|
class <<self
|
51
51
|
# Populates a relationship with another model.
|
52
52
|
#
|
@@ -55,7 +55,7 @@ module VirtualBox
|
|
55
55
|
# @return [Array<StorageController>]
|
56
56
|
def populate_relationship(caller, data)
|
57
57
|
relation = []
|
58
|
-
|
58
|
+
|
59
59
|
counter = 0
|
60
60
|
loop do
|
61
61
|
break unless data["storagecontrollername#{counter}".to_sym]
|
@@ -63,17 +63,17 @@ module VirtualBox
|
|
63
63
|
relation.push(nic)
|
64
64
|
counter += 1
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
relation
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# Destroys a relationship with another model.
|
71
71
|
#
|
72
72
|
# **This method typically won't be used except internally.**
|
73
73
|
def destroy_relationship(caller, data, *args)
|
74
74
|
data.each { |v| v.destroy(*args) }
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
# Saves the relationship. This simply calls {#save} on every
|
78
78
|
# member of the relationship.
|
79
79
|
#
|
@@ -84,16 +84,16 @@ module VirtualBox
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
# Since storage controllers still can't be created from scratch,
|
89
89
|
# this method shouldn't be called. Instead, storage controllers
|
90
90
|
# can be retrieved through relationships of other models such
|
91
91
|
# as {VM}.
|
92
92
|
def initialize(index, caller, data)
|
93
93
|
super()
|
94
|
-
|
94
|
+
|
95
95
|
@index = index
|
96
|
-
|
96
|
+
|
97
97
|
# Setup the index specific attributes
|
98
98
|
populate_data = {}
|
99
99
|
self.class.attributes.each do |name, options|
|
@@ -101,16 +101,16 @@ module VirtualBox
|
|
101
101
|
value = data["storagecontroller#{key}#{index}".to_sym]
|
102
102
|
populate_data[key] = value
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
# Make sure to merge in device data so those relationships will be
|
106
106
|
# setup properly
|
107
107
|
populate_data.merge!(extract_devices(index, data))
|
108
|
-
|
108
|
+
|
109
109
|
populate_attributes(populate_data.merge({
|
110
110
|
:parent => caller
|
111
111
|
}))
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
# Extracts related devices for a storage controller.
|
115
115
|
#
|
116
116
|
# **This method typically won't be used except internally.**
|
@@ -118,14 +118,14 @@ module VirtualBox
|
|
118
118
|
# @return [Hash]
|
119
119
|
def extract_devices(index, data)
|
120
120
|
name = data["storagecontrollername#{index}".downcase.to_sym].downcase
|
121
|
-
|
121
|
+
|
122
122
|
device_data = {}
|
123
123
|
data.each do |k,v|
|
124
124
|
next unless k.to_s =~ /^#{name}-/
|
125
|
-
|
125
|
+
|
126
126
|
device_data[k] = v
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
device_data
|
130
130
|
end
|
131
131
|
end
|