virtualbox 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ only :test do
4
4
  gem "contest", ">= 0.1.2"
5
5
  gem "mocha"
6
6
  gem "ruby-debug", ">= 0.10.3" if RUBY_VERSION < '1.9'
7
+ gem "ruby-debug19", ">= 0.11.6" if RUBY_VERSION >= '1.9'
7
8
  end
8
9
 
9
10
  # Makes sure that our code doesn't request gems outside
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Mitchell Hashimoto.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/Readme.md CHANGED
@@ -35,7 +35,7 @@ Below are some examples:
35
35
  # Let's first print out some basic info about the VM
36
36
  puts "Memory: #{vm.memory}"
37
37
 
38
- vm.storage_controllers.each do |sc
38
+ vm.storage_controllers.each do |sc|
39
39
  sc.attached_devices.each do |device|
40
40
  puts "Attached Device: #{device.uuid}"
41
41
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
@@ -1,41 +1,34 @@
1
- # What's New in 0.3.x?
1
+ # What's New in 0.4.x?
2
2
 
3
- ## Shared Folders
3
+ ## "Extra Data" on VMs / Global
4
4
 
5
- Shared folders are a great feature of VirtualBox which allows the host system
6
- to share data with guest systems easily using the native filesystem. Attaching,
7
- modifying, and removing these shared folders are now supported. A quick example
8
- below:
5
+ Extra data is persistent key-value storage which is available as a way to store any information
6
+ wanted. VirtualBox uses it for storing statistics and settings. You can use it for anything!
7
+ Setting extra data on virtual machines is now as easy as a ruby hash:
9
8
 
10
9
  vm = VirtualBox::VM.find("FooVM")
11
- folder = VirtualBox::SharedFolder.new
12
- folder.name = "hosthome"
13
- folder.hostpath = "/home/username"
14
- vm.shared_folders << folder
10
+ vm.extra_data["i_was_here"] = "yes!"
15
11
  vm.save
16
12
 
17
- For full documentation on this new feature, read about them at
18
- {VirtualBox::SharedFolder}.
13
+ Read more about extra data {VirtualBox::ExtraData here}.
19
14
 
20
- ## Validations
15
+ ## Port Forwarding
21
16
 
22
- Many of the models for the virtualbox library now come complete with data
23
- validations. These validations are performed within the library itself prior to
24
- calling the virtualbox commands. They work very much the same was as ActiveRecord
25
- validations:
17
+ If a VM is using NAT for its network, the host machine can't access any outward facing
18
+ services of the guest (for example: a web host, ftp server, etc.). Port forwarding is
19
+ one way to facilitate this need. Port forwarding is straight forward to setup:
26
20
 
27
- sf = VirtualBox::SharedFolder.new(hash_of_values)
28
- if !sf.valid?
29
- puts "#{sf.errors.length} errors with the folder"
30
- else
31
- sf.save
32
- end
21
+ vm = VirtualBox::VM.find("FooVM")
22
+ port = VirtualBox::ForwardedPort.new
23
+ port.name = "http"
24
+ port.guestport = 80
25
+ port.hostport = 8080
26
+ vm.forwarded_ports << port
27
+ vm.save
28
+
29
+ Read more about port forwarding {VirtualBox::ForwardedPort here}.
33
30
 
34
- In addition to `valid?` there is `errors` which returns a hash of all the errors,
35
- including errors on relationships. There is also the `validate` method which
36
- runs the validations, but you really shouldn't have the need to call that directly.
31
+ ## More Ruby Versions Supported!
37
32
 
38
- All validations are run automatically on `save`, which will return `false` if
39
- they fail. If you choose to raise errors on the save, a `ValidationFailedException`
40
- will be raised (in contrast to a `CommandFailedException`, which serves its own
41
- role).
33
+ Previously, virtualbox only supported 1.8.7. It now supports 1.8.6 and 1.9.x thanks
34
+ to AleksiP.
@@ -6,6 +6,8 @@ require 'virtualbox/proxies/collection'
6
6
  require 'virtualbox/image'
7
7
  require 'virtualbox/attached_device'
8
8
  require 'virtualbox/dvd'
9
+ require 'virtualbox/extra_data'
10
+ require 'virtualbox/forwarded_port'
9
11
  require 'virtualbox/hard_drive'
10
12
  require 'virtualbox/nic'
11
13
  require 'virtualbox/shared_folder'
@@ -96,8 +96,7 @@ module VirtualBox
96
96
  def relationship(name, klass, options = {})
97
97
  name = name.to_sym
98
98
 
99
- @relationships ||= {}
100
- @relationships[name] = { :klass => klass }.merge(options)
99
+ relationships << [name, { :klass => klass }.merge(options)]
101
100
 
102
101
  # Define the method to read the relationship
103
102
  define_method(name) { relationship_data[name] }
@@ -109,8 +108,22 @@ module VirtualBox
109
108
  # Returns a hash of all the relationships.
110
109
  #
111
110
  # @return [Hash]
111
+ def relationships_hash
112
+ Hash[*relationships.flatten]
113
+ end
114
+
115
+ # Returns an array of the relationships in order of being added.
116
+ #
117
+ # @return [Array]
112
118
  def relationships
113
- @relationships ||= {}
119
+ @relationships ||= []
120
+ end
121
+
122
+ # Returns a boolean of whether a relationship exists.
123
+ #
124
+ # @return [Boolean]
125
+ def has_relationship?(name)
126
+ !!relationships.detect { |r| r[0] == name }
114
127
  end
115
128
 
116
129
  # Used to propagate relationships to subclasses. This method makes sure that
@@ -166,7 +179,7 @@ module VirtualBox
166
179
  #
167
180
  # @param [Symbol] name The name of the relationship
168
181
  def destroy_relationship(name, *args)
169
- options = self.class.relationships[name]
182
+ options = self.class.relationships_hash[name]
170
183
  return unless options && options[:klass].respond_to?(:destroy_relationship)
171
184
  options[:klass].destroy_relationship(self, relationship_data[name], *args)
172
185
  end
@@ -183,7 +196,7 @@ module VirtualBox
183
196
  #
184
197
  # @return [Boolean]
185
198
  def has_relationship?(key)
186
- self.class.relationships.has_key?(key.to_sym)
199
+ self.class.has_relationship?(key.to_sym)
187
200
  end
188
201
 
189
202
  # Sets a relationship to the given value. This is not guaranteed to
@@ -201,7 +214,7 @@ module VirtualBox
201
214
  # @param [Object] value The new value of the relationship.
202
215
  def set_relationship(key, value)
203
216
  key = key.to_sym
204
- relationship = self.class.relationships[key]
217
+ relationship = self.class.relationships_hash[key]
205
218
  return unless relationship
206
219
 
207
220
  raise Exceptions::NonSettableRelationshipException.new unless relationship[:klass].respond_to?(:set_relationship)
@@ -0,0 +1,151 @@
1
+ module VirtualBox
2
+ # Represents "extra data" which can be set on a specific
3
+ # virtual machine or on VirtualBox as a whole. Extra data is persistent
4
+ # key-value storage which is available as a way to store any information
5
+ # wanted. VirtualBox uses it for storing statistics and settings. You can
6
+ # use it for anything!
7
+ #
8
+ # # Extra Data on a Virtual Machine
9
+ #
10
+ # Setting extra data on a virtual machine is easy. All {VM} objects have a
11
+ # `extra_data` relationship which is just a simple ruby hash, so you can treat
12
+ # it like one! Once the data is set, simply saving the VM will save the
13
+ # extra data. An example below:
14
+ #
15
+ # vm = VirtualBox::VM.find("FooVM")
16
+ # vm.extra_data["ruby-accessed"] = "yes, yes it was"
17
+ # vm.save
18
+ #
19
+ # Now, let's say you open up the VM again some other time:
20
+ #
21
+ # vm = VirtualBox::VM.find("FooVM")
22
+ # puts vm.extra_data["ruby-accessed"]
23
+ #
24
+ # It acts just like a hash!
25
+ #
26
+ # # Global Extra Data
27
+ #
28
+ # Extra data doesn't need to be tied to a specific virtual machine. It can also
29
+ # exist globally. Setting global extra-data is just as easy:
30
+ #
31
+ # VirtualBox::ExtraData.global["some-key"] = "some value"
32
+ # VirtualBox::ExtraData.global.save
33
+ #
34
+ class ExtraData < Hash
35
+ include AbstractModel::Dirty
36
+
37
+ attr_accessor :parent
38
+
39
+ @@global_data = nil
40
+
41
+ class <<self
42
+ # Gets the global extra data. This will "cache" the data for
43
+ # future use unless you set the `reload` paramter to true.
44
+ #
45
+ # @param [Boolean] reload If true, will reload new global data.
46
+ # @return [Array<ExtraData>]
47
+ def global(reload=false)
48
+ if !@@global_data || reload
49
+ raw = Command.vboxmanage("getextradata global enumerate")
50
+ @@global_data = parse_kv_pairs(raw)
51
+ end
52
+
53
+ @@global_data
54
+ end
55
+
56
+ # Parses the key-value pairs from the extra data enumerated
57
+ # output.
58
+ #
59
+ # @param [String] raw The raw output from enumerating extra data.
60
+ # @return [Hash]
61
+ def parse_kv_pairs(raw, parent=nil)
62
+ data = new(parent)
63
+ raw.split("\n").each do |line|
64
+ next unless line =~ /^Key: (.+?), Value: (.+?)$/i
65
+ data[$1.to_s] = $2.strip.to_s
66
+ end
67
+
68
+ data.clear_dirty!
69
+ data
70
+ end
71
+
72
+ # Populates a relationship with another model.
73
+ #
74
+ # **This method typically won't be used except internally.**
75
+ #
76
+ # @return [Array<ExtraData>]
77
+ def populate_relationship(caller, data)
78
+ raw = Command.vboxmanage("getextradata #{Command.shell_escape(caller.name)} enumerate")
79
+ parse_kv_pairs(raw, caller)
80
+ end
81
+
82
+ # Saves the relationship. This simply calls {#save} on every
83
+ # member of the relationship.
84
+ #
85
+ # **This method typically won't be used except internally.**
86
+ def save_relationship(caller, data)
87
+ data.save
88
+ end
89
+ end
90
+
91
+ # Initializes an extra data object.
92
+ #
93
+ # @param [Hash] data Initial attributes to populate.
94
+ def initialize(parent=nil)
95
+ @parent = parent || "global"
96
+ end
97
+
98
+ # Set an extradata key-value pair. Overrides ruby hash implementation
99
+ # to set dirty state. Otherwise that, behaves the same way.
100
+ def []=(key,value)
101
+ set_dirty!(key, self[key], value)
102
+ super
103
+ end
104
+
105
+ # Special accessor for parent name attribute. This returns
106
+ # either the parent name if its a VM object, otherwise
107
+ # just returns the default.
108
+ #
109
+ # @return [String]
110
+ def parent_name
111
+ if parent.is_a?(VM)
112
+ parent.name
113
+ else
114
+ parent
115
+ end
116
+ end
117
+
118
+ # Saves extra data. This method does the same thing for both new
119
+ # and existing extra data, since virtualbox will overwrite old data or
120
+ # create it if it doesn't exist.
121
+ #
122
+ # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
123
+ # will be raised if the command failed.
124
+ # @return [Boolean] True if command was successful, false otherwise.
125
+ def save(raise_errors=false)
126
+ changes.each do |key, value|
127
+ Command.vboxmanage("setextradata #{Command.shell_escape(parent_name)} #{Command.shell_escape(key)} #{Command.shell_escape(value[1])}")
128
+ clear_dirty!(key)
129
+ end
130
+
131
+ true
132
+ rescue Exceptions::CommandFailedException
133
+ raise if raise_errors
134
+ false
135
+ end
136
+
137
+ # Deletes the extra data.
138
+ #
139
+ # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
140
+ # will be raised if the command failed.
141
+ # @return [Boolean] True if command was successful, false otherwise.
142
+ def delete(key, raise_errors=false)
143
+ Command.vboxmanage("setextradata #{Command.shell_escape(parent_name)} #{Command.shell_escape(key)}")
144
+ super(key)
145
+ true
146
+ rescue Exceptions::CommandFailedException
147
+ raise if raise_errors
148
+ false
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,197 @@
1
+ module VirtualBox
2
+ # When a VM uses NAT as its NIC type, VirtualBox acts like its
3
+ # own private router for all virtual machines. Because of this,
4
+ # the host machine can't access services within the guest machine.
5
+ # To get around this, NAT supports port forwarding, which allows the
6
+ # guest machine services to be forwarded to some port on the host
7
+ # machine. Port forwarding is done completely through {ExtraData}, but
8
+ # is a complicated enough procedure that this class was made to
9
+ # faciliate it.
10
+ #
11
+ # **Note:** After changing any forwarded ports, the entire VirtualBox
12
+ # process must be restarted completely for them to take effect. When
13
+ # working with the ruby library, this isn't so much of an issue, but
14
+ # if you have any VMs running, they must all be shut down and restarted.
15
+ #
16
+ # # Adding a new Forwarded Port
17
+ #
18
+ # Since forwarded ports rely on being part of a {VM}, we're going to
19
+ # assume that `vm` points to a {VM} which has already been found.
20
+ #
21
+ # port = VirtualBox::ForwardedPort.new
22
+ # port.name = "apache" # This can be anything
23
+ # port.guestport = 80
24
+ # port.hostport = 8080
25
+ # vm.forwarded_ports << port
26
+ # port.save # Or vm.save
27
+ #
28
+ # # Modifying an Existing Forwarded Port
29
+ #
30
+ # This is assuming that `vm` is a local variable storing a {VM} object
31
+ # which has already been found.
32
+ #
33
+ # vm.forwarded_ports.first.hostport = 1919
34
+ # vm.save
35
+ #
36
+ # # Deleting a Forwarded Port
37
+ #
38
+ # To delete a forwarded port, you simply destroy it like any other model:
39
+ #
40
+ # vm.forwarded_ports.first.destroy
41
+ #
42
+ # # Attributes and Relationships
43
+ #
44
+ # Properties of the model are exposed using standard ruby instance
45
+ # methods which are generated on the fly. Because of this, they are not listed
46
+ # below as available instance methods.
47
+ #
48
+ # These attributes can be accessed and modified via standard ruby-style
49
+ # `instance.attribute` and `instance.attribute=` methods. The attributes are
50
+ # listed below.
51
+ #
52
+ # Relationships are also accessed like attributes but can't be set. Instead,
53
+ # they are typically references to other objects such as an {AttachedDevice} which
54
+ # in turn have their own attributes which can be modified.
55
+ #
56
+ # ## Attributes
57
+ #
58
+ # This is copied directly from the class header, but lists all available
59
+ # attributes. If you don't understand what this means, read {Attributable}.
60
+ #
61
+ # attribute :parent, :readonly => true
62
+ # attribute :name
63
+ # attribute :instance, :default => "0"
64
+ # attribute :device, :default => "pcnet"
65
+ # attribute :protocol, :default => "TCP"
66
+ # attribute :guestport
67
+ # attribute :hostport
68
+ #
69
+ class ForwardedPort < AbstractModel
70
+ attribute :parent, :readonly => true
71
+ attribute :name
72
+ attribute :instance, :default => "0"
73
+ attribute :device, :default => "pcnet"
74
+ attribute :protocol, :default => "TCP"
75
+ attribute :guestport
76
+ attribute :hostport
77
+
78
+ class <<self
79
+ # Populates a relationship with another model.
80
+ #
81
+ # **This method typically won't be used except internally.**
82
+ #
83
+ # @return [Array<ForwardedPort>]
84
+ def populate_relationship(caller, data)
85
+ relation = Proxies::Collection.new(caller)
86
+
87
+ caller.extra_data.each do |key, value|
88
+ next unless key =~ /^(VBoxInternal\/Devices\/(.+?)\/(.+?)\/LUN#0\/Config\/(.+?)\/)Protocol$/i
89
+
90
+ port = new({
91
+ :parent => caller,
92
+ :name => $4.to_s,
93
+ :instance => $3.to_s,
94
+ :device => $2.to_s,
95
+ :protocol => value,
96
+ :guestport => caller.extra_data["#{$1.to_s}GuestPort"],
97
+ :hostport => caller.extra_data["#{$1.to_s}HostPort"]
98
+ })
99
+
100
+ port.existing_record!
101
+
102
+ relation.push(port)
103
+ end
104
+
105
+ relation
106
+ end
107
+
108
+ # Saves the relationship. This simply calls {#save} on every
109
+ # member of the relationship.
110
+ #
111
+ # **This method typically won't be used except internally.**
112
+ def save_relationship(caller, data)
113
+ data.each do |fp|
114
+ fp.save
115
+ end
116
+ end
117
+ end
118
+
119
+ # @param [Hash] data The initial attributes to populate.
120
+ def initialize(data={})
121
+ super()
122
+ populate_attributes(data)
123
+ end
124
+
125
+ # Validates a forwarded port.
126
+ def validate
127
+ super
128
+
129
+ validates_presence_of :parent
130
+ validates_presence_of :name
131
+ validates_presence_of :guestport
132
+ validates_presence_of :hostport
133
+ end
134
+
135
+ # Saves the forwarded port.
136
+ #
137
+ # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
138
+ # will be raised if the command failed.
139
+ # @return [Boolean] True if command was successful, false otherwise.
140
+ def save(raise_errors=false)
141
+ return true if !new_record? && !changed?
142
+
143
+ if !valid?
144
+ raise Exceptions::ValidationFailedException.new(errors) if raise_errors
145
+ return false
146
+ end
147
+
148
+ destroy(raise_errors) if name_changed?
149
+
150
+ parent.extra_data["#{key_prefix}Protocol"] = protocol
151
+ parent.extra_data["#{key_prefix}GuestPort"] = guestport
152
+ parent.extra_data["#{key_prefix}HostPort"] = hostport
153
+ result = parent.extra_data.save(raise_errors)
154
+
155
+ clear_dirty!
156
+ existing_record!
157
+
158
+ result
159
+ end
160
+
161
+ # Destroys the port forwarding mapping.
162
+ #
163
+ # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
164
+ # will be raised if the command failed.
165
+ # @return [Boolean] True if command was successful, false otherwise.
166
+ def destroy(raise_errors=false)
167
+ results = []
168
+
169
+ if !new_record?
170
+ results << parent.extra_data.delete("#{key_prefix(true)}Protocol", raise_errors)
171
+ results << parent.extra_data.delete("#{key_prefix(true)}GuestPort", raise_errors)
172
+ results << parent.extra_data.delete("#{key_prefix(true)}HostPort", raise_errors)
173
+
174
+ new_record!
175
+ end
176
+
177
+ results.empty? || results.all? { |o| o == true }
178
+ end
179
+
180
+ # Relationship callback when added to a collection. This is automatically
181
+ # called by any relationship collection when this object is added.
182
+ def added_to_relationship(parent)
183
+ write_attribute(:parent, parent)
184
+ end
185
+
186
+ # Returns the prefix to be used for the extra data key. Forwarded ports
187
+ # are created by simply setting {ExtraData} on a {VM}. This class hides most
188
+ # of the inner workings of it, but it requires a common prefix. This method
189
+ # generates that.
190
+ #
191
+ # @return [String]
192
+ def key_prefix(old_name=false)
193
+ name_value = old_name && name_changed? ? name_was : name
194
+ "VBoxInternal\/Devices\/#{device}\/#{instance}\/LUN#0\/Config\/#{name_value}\/"
195
+ end
196
+ end
197
+ end
@@ -53,7 +53,7 @@ module VirtualBox
53
53
 
54
54
  # Parses each line which should be in the format:
55
55
  # KEY: VALUE
56
- block.lines.each do |line|
56
+ block.split("\n").each do |line|
57
57
  next unless line =~ /^(.+?):\s+(.+?)$/
58
58
  hd[$1.downcase.to_sym] = $2.to_s
59
59
  end
@@ -54,7 +54,7 @@ module VirtualBox
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
- raw.lines.collect { |v| parse_nic(v) }.compact.inject({}) do |acc, obj|
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
@@ -96,7 +96,7 @@ module VirtualBox
96
96
  #
97
97
  # @return [Array<SharedFolder>]
98
98
  def populate_relationship(caller, data)
99
- relation = []
99
+ relation = Proxies::Collection.new(caller)
100
100
 
101
101
  counter = 1
102
102
  loop do
@@ -79,7 +79,6 @@ module VirtualBox
79
79
  #
80
80
  # **This method typically won't be used except internally.**
81
81
  def save_relationship(caller, data)
82
- # Just call save on each nic with the VM
83
82
  data.each do |sc|
84
83
  sc.save
85
84
  end
@@ -75,6 +75,8 @@ module VirtualBox
75
75
  # relationship :nics, Nic
76
76
  # relationship :storage_controllers, StorageController, :dependent => :destroy
77
77
  # relationship :shared_folders, SharedFolder
78
+ # relationship :extra_data, ExtraData
79
+ # relationship :forwarded_ports, ForwardedPort
78
80
  #
79
81
  class VM < AbstractModel
80
82
  attribute :uuid, :readonly => true
@@ -106,6 +108,8 @@ module VirtualBox
106
108
  relationship :nics, Nic
107
109
  relationship :storage_controllers, StorageController, :dependent => :destroy
108
110
  relationship :shared_folders, SharedFolder
111
+ relationship :extra_data, ExtraData
112
+ relationship :forwarded_ports, ForwardedPort
109
113
 
110
114
  class <<self
111
115
  # Returns an array of all available VMs.
@@ -144,7 +148,7 @@ module VirtualBox
144
148
  #
145
149
  # @return [String]
146
150
  def human_info(name)
147
- Command.vboxmanage("showvminfo #{name}")
151
+ Command.vboxmanage("showvminfo #{Command.shell_escape(name)}")
148
152
  end
149
153
 
150
154
  # Gets the VM info (machine readable) for a given VM and returns it
@@ -152,7 +156,7 @@ module VirtualBox
152
156
  #
153
157
  # @return [Hash] Parsed VM info.
154
158
  def raw_info(name)
155
- raw = Command.vboxmanage("showvminfo #{name} --machinereadable")
159
+ raw = Command.vboxmanage("showvminfo #{Command.shell_escape(name)} --machinereadable")
156
160
  parse_vm_info(raw)
157
161
  end
158
162
 
@@ -160,7 +164,7 @@ module VirtualBox
160
164
  # into a hash. Ignores lines which don't match the format.
161
165
  def parse_vm_info(raw)
162
166
  parsed = {}
163
- raw.lines.each do |line|
167
+ raw.split("\n").each do |line|
164
168
  # Some lines aren't configuration, we just ignore them
165
169
  next unless line =~ /^"?(.+?)"?="?(.+?)"?$/
166
170
  parsed[$1.downcase.to_sym] = $2.strip
@@ -177,7 +181,7 @@ module VirtualBox
177
181
  # @return [Array] Array of virtual machines.
178
182
  def parse_vm_list(raw)
179
183
  results = []
180
- raw.lines.each do |line|
184
+ raw.split("\n").each do |line|
181
185
  next unless line =~ /^"(.+?)"\s+\{(.+?)\}$/
182
186
  results.push(find($1.to_s))
183
187
  end
@@ -22,6 +22,19 @@ class RelatableTest < Test::Unit::TestCase
22
22
  @data = {}
23
23
  end
24
24
 
25
+ context "class methods" do
26
+ should "read back relationships in order added" do
27
+ order = mock("order")
28
+ order_seq = sequence("order_seq")
29
+ order.expects(:foos).in_sequence(order_seq)
30
+ order.expects(:bars).in_sequence(order_seq)
31
+
32
+ RelatableModel.relationships.each do |name, options|
33
+ order.send(name)
34
+ end
35
+ end
36
+ end
37
+
25
38
  context "setting a relationship" do
26
39
  setup do
27
40
  @model = RelatableModel.new
@@ -55,7 +68,7 @@ class RelatableTest < Test::Unit::TestCase
55
68
 
56
69
  context "subclasses" do
57
70
  class SubRelatableModel < RelatableModel
58
- relationship :bars, Relatee
71
+ relationship :bars, RelatableTest::Relatee
59
72
  end
60
73
 
61
74
  setup do
@@ -63,12 +76,12 @@ class RelatableTest < Test::Unit::TestCase
63
76
  end
64
77
 
65
78
  should "inherit relationships of parent" do
66
- assert @relationships.has_key?(:foos)
67
- assert @relationships.has_key?(:bars)
79
+ assert SubRelatableModel.has_relationship?(:foos)
80
+ assert SubRelatableModel.has_relationship?(:bars)
68
81
  end
69
82
 
70
83
  should "inherit options of relationships" do
71
- assert_equal Relatee, @relationships[:foos][:klass]
84
+ assert_equal Relatee, SubRelatableModel.relationships_hash[:foos][:klass]
72
85
  end
73
86
  end
74
87
 
@@ -161,6 +174,11 @@ class RelatableTest < Test::Unit::TestCase
161
174
  @model = RelatableModel.new
162
175
  end
163
176
 
177
+ should "have a class method as well" do
178
+ assert RelatableModel.has_relationship?(:foos)
179
+ assert !RelatableModel.has_relationship?(:bazs)
180
+ end
181
+
164
182
  should "return true for existing relationships" do
165
183
  assert @model.has_relationship?(:foos)
166
184
  end
@@ -0,0 +1,218 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class ExtraDataTest < Test::Unit::TestCase
4
+ setup do
5
+ @raw = <<-raw
6
+ .
7
+ Key: GUI/LastVMSelected, Value: 02573b8e-f628-43ed-b688-e488414e07ac
8
+ Key: GUI/LastWindowPostion, Value: 99,457,770,550
9
+ Key: GUI/SUNOnlineData, Value: triesLeft=0
10
+ Key: GUI/SuppressMessages, Value: ,confirmInputCapture,remindAboutAutoCapture,confirmRemoveMedium,remindAboutInaccessibleMedia,confirmGoingFullscreen,remindAboutMouseIntegrationOn
11
+ Key: GUI/UpdateCheckCount, Value: 13
12
+ Key: GUI/UpdateDate, Value: 1 d, 2010-01-29, stable
13
+ raw
14
+
15
+ VirtualBox::Command.stubs(:execute)
16
+
17
+ @ed = VirtualBox::ExtraData.new
18
+ @ed["foo"] = "bar"
19
+ @ed.clear_dirty!
20
+ end
21
+
22
+ context "attributes" do
23
+ should "return parent name if its a VM object" do
24
+ vm = mock("vm")
25
+ vm.stubs(:is_a?).with(VirtualBox::VM).returns(true)
26
+ vm.stubs(:name).returns("FOO")
27
+
28
+ @ed.parent = vm
29
+ assert_equal "FOO", @ed.parent_name
30
+ end
31
+
32
+ should "return default otherwise" do
33
+ assert_equal "global", @ed.parent_name
34
+ end
35
+ end
36
+
37
+ context "relationships" do
38
+ setup do
39
+ @caller = mock("caller")
40
+ @caller.stubs(:name).returns("foocaller")
41
+
42
+ VirtualBox::Command.stubs(:vboxmanage).returns(@raw)
43
+ end
44
+
45
+ context "populating" do
46
+ should "call VBoxManage for the caller" do
47
+ VirtualBox::Command.expects(:vboxmanage).with("getextradata #{@caller.name} enumerate").returns(@raw)
48
+ VirtualBox::ExtraData.populate_relationship(@caller, {})
49
+ end
50
+
51
+ should "call pairs_to_objects with parent set to the caller" do
52
+ VirtualBox::ExtraData.expects(:parse_kv_pairs).with(@raw, @caller).once
53
+ VirtualBox::ExtraData.populate_relationship(@caller, {})
54
+ end
55
+
56
+ should "return an array of ExtraData objects" do
57
+ result = VirtualBox::ExtraData.populate_relationship(@caller, {})
58
+ assert result.is_a?(VirtualBox::ExtraData)
59
+ end
60
+ end
61
+
62
+ context "saving" do
63
+ should "call save on the ExtraData object" do
64
+ object = mock("object")
65
+ object.expects(:save).once
66
+
67
+ VirtualBox::ExtraData.save_relationship(@caller, object)
68
+ end
69
+ end
70
+ end
71
+
72
+ context "destroying (deleting)" do
73
+ setup do
74
+ @key = "foo"
75
+ end
76
+
77
+ should "call the proper vbox command" do
78
+ VirtualBox::Command.expects(:vboxmanage).with("setextradata global foo")
79
+ assert @ed.delete(@key)
80
+ end
81
+
82
+ should "remove the key from the hash" do
83
+ assert @ed.has_key?(@key)
84
+ assert @ed.delete(@key)
85
+ assert !@ed.has_key?(@key)
86
+ end
87
+
88
+ should "raise an exception if true sent to save and error occured" do
89
+ VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
90
+ assert_raises(VirtualBox::Exceptions::CommandFailedException) {
91
+ @ed.delete(@key, true)
92
+ }
93
+ end
94
+
95
+ should "return false if the command failed" do
96
+ VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
97
+ assert !@ed.delete(@key)
98
+ end
99
+ end
100
+
101
+ context "saving" do
102
+ setup do
103
+ @ed["foo"] = "BAR"
104
+ assert @ed.changed?
105
+ end
106
+
107
+ should "do nothing if there are no changes" do
108
+ @ed.clear_dirty!
109
+ VirtualBox::Command.expects(:vboxmanage).never
110
+ assert @ed.save
111
+ end
112
+
113
+ should "call the proper vbox command" do
114
+ VirtualBox::Command.expects(:vboxmanage).with("setextradata global foo BAR")
115
+ assert @ed.save
116
+ end
117
+
118
+ should "return false if the command failed" do
119
+ VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
120
+ assert !@ed.save
121
+ end
122
+
123
+ should "raise an exception if true sent to save and error occured" do
124
+ VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
125
+ assert_raises(VirtualBox::Exceptions::CommandFailedException) {
126
+ @ed.save(true)
127
+ }
128
+ end
129
+
130
+ should "clear dirty state" do
131
+ @ed["value"] = "rawr"
132
+ assert @ed.changed?
133
+ assert @ed.save
134
+ assert !@ed.changed?
135
+ end
136
+ end
137
+
138
+ context "setting dirty state" do
139
+ setup do
140
+ @ed = VirtualBox::ExtraData.new
141
+ end
142
+
143
+ should "not be dirty initially" do
144
+ assert !@ed.changed?
145
+ end
146
+
147
+ should "be dirty when setting a value" do
148
+ @ed["foo"] = "bar"
149
+ assert @ed.changed?
150
+ assert @ed.changes.has_key?("foo")
151
+ end
152
+ end
153
+
154
+ context "global extra data" do
155
+ setup do
156
+ get_seq = sequence("get_seq")
157
+ VirtualBox::Command.expects(:vboxmanage).with("getextradata global enumerate").once.in_sequence(get_seq)
158
+ VirtualBox::ExtraData.expects(:parse_kv_pairs).returns(@ed).once.in_sequence(get_seq)
159
+ @global = VirtualBox::ExtraData.global(true)
160
+ end
161
+
162
+ should "call the command, parse it, then turn it into objects" do
163
+ assert_equal "bar", @global["foo"]
164
+ end
165
+
166
+ should "return the same object if it exists for global data, rather than recreating it" do
167
+ VirtualBox::Command.expects(:vboxmanage).never
168
+ assert_equal @global, VirtualBox::ExtraData.global
169
+ end
170
+
171
+ should "return a new object if reload is true" do
172
+ VirtualBox::Command.expects(:vboxmanage).once
173
+ VirtualBox::ExtraData.expects(:parse_kv_pairs).returns(@ed.dup).once
174
+ assert !@global.equal?(VirtualBox::ExtraData.global(true))
175
+ end
176
+ end
177
+
178
+ context "constructor" do
179
+ should "set the parent with the given argument" do
180
+ ed = VirtualBox::ExtraData.new("JOEY")
181
+ assert_equal "JOEY", ed.parent
182
+ end
183
+
184
+ should "be global by default" do
185
+ ed = VirtualBox::ExtraData.new
186
+ assert_equal "global", ed.parent
187
+ end
188
+ end
189
+
190
+ context "parsing KV pairs" do
191
+ setup do
192
+ @data = VirtualBox::ExtraData.parse_kv_pairs(@raw)
193
+ end
194
+
195
+ should "return the proper number of items" do
196
+ # Shows that it skips over non-matching lines as well
197
+ assert_equal 6, @data.length
198
+ end
199
+
200
+ should "return as an ExtraData Hash" do
201
+ assert @data.is_a?(Hash)
202
+ assert @data.is_a?(VirtualBox::ExtraData)
203
+ end
204
+
205
+ should "return proper values, trimmed" do
206
+ assert_equal "1 d, 2010-01-29, stable", @data["GUI/UpdateDate"]
207
+ end
208
+
209
+ should "send the 2nd param as the parent to the ED object" do
210
+ @data = VirtualBox::ExtraData.parse_kv_pairs(@raw, "FOO")
211
+ assert_equal "FOO", @data.parent
212
+ end
213
+
214
+ should "return an unchanged ED object" do
215
+ assert !@data.changed?
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,206 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class ForwardedPortTest < Test::Unit::TestCase
4
+ setup do
5
+ @caller = mock("caller")
6
+ end
7
+
8
+ context "validations" do
9
+ setup do
10
+ @port = VirtualBox::ForwardedPort.new
11
+ @port.name = "foo"
12
+ @port.guestport = "22"
13
+ @port.hostport = "2222"
14
+ @port.added_to_relationship(@caller)
15
+ end
16
+
17
+ should "be valid with all fields" do
18
+ assert @port.valid?
19
+ end
20
+
21
+ should "be invalid with no name" do
22
+ @port.name = nil
23
+ assert !@port.valid?
24
+ end
25
+
26
+ should "be invalid with no guest port" do
27
+ @port.guestport = nil
28
+ assert !@port.valid?
29
+ end
30
+
31
+ should "be invalid with no host port" do
32
+ @port.hostport = nil
33
+ assert !@port.valid?
34
+ end
35
+
36
+ should "be invalid if not in a relationship" do
37
+ @port.write_attribute(:parent, nil)
38
+ assert !@port.valid?
39
+ end
40
+ end
41
+
42
+ context "with an instance" do
43
+ setup do
44
+ @port = VirtualBox::ForwardedPort.new({
45
+ :parent => @caller,
46
+ :name => "foo",
47
+ :guestport => "22",
48
+ :hostport => "2222"
49
+ })
50
+
51
+ @ed = mock("extradata")
52
+ @ed.stubs(:[]=)
53
+ @ed.stubs(:save)
54
+ @ed.stubs(:delete)
55
+ @caller.stubs(:extra_data).returns(@ed)
56
+ end
57
+
58
+ context "saving" do
59
+ context "an existing record" do
60
+ setup do
61
+ @port.existing_record!
62
+ end
63
+
64
+ should "not do anything and return true if its unchanged" do
65
+ @caller.expects(:extra_data).never
66
+ assert @port.save
67
+ end
68
+
69
+ should "clear the dirty state after saving" do
70
+ @port.name = "diff"
71
+ @port.save
72
+ assert !@port.changed?
73
+ end
74
+
75
+ should "call destroy if the name changed" do
76
+ @port.name = "diff"
77
+ @port.expects(:destroy).once
78
+ @port.save
79
+ end
80
+
81
+ should "not call destroy if the name didn't change" do
82
+ assert !@port.name_changed?
83
+ @port.expects(:destroy).never
84
+ @port.save
85
+ end
86
+ end
87
+
88
+ context "a new record" do
89
+ setup do
90
+ assert @port.new_record!
91
+ end
92
+
93
+ should "no longer be a new record after saving" do
94
+ @port.save
95
+ assert !@port.new_record?
96
+ end
97
+
98
+ should "return false and do nothing if invalid" do
99
+ @caller.expects(:extra_data).never
100
+ @port.expects(:valid?).returns(false)
101
+ assert !@port.save
102
+ end
103
+
104
+ should "raise a ValidationFailedException if invalid and raise_errors is true" do
105
+ @port.expects(:valid?).returns(false)
106
+ assert_raises(VirtualBox::Exceptions::ValidationFailedException) {
107
+ @port.save(true)
108
+ }
109
+ end
110
+
111
+ should "call save on the extra_data" do
112
+ @ed = mock("ed")
113
+ @ed.expects(:[]=).times(3)
114
+ @ed.expects(:save).once
115
+ @caller.expects(:extra_data).times(4).returns(@ed)
116
+ @port.save
117
+ end
118
+ end
119
+ end
120
+
121
+ context "key prefix" do
122
+ should "return a proper key prefix constructed with the attributes" do
123
+ assert_equal "VBoxInternal\/Devices\/#{@port.device}\/#{@port.instance}\/LUN#0\/Config\/#{@port.name}\/", @port.key_prefix
124
+ end
125
+
126
+ should "return with previous name if parameter is true" do
127
+ @port.name = "diff"
128
+ assert @port.name_changed?
129
+ assert_equal "VBoxInternal\/Devices\/#{@port.device}\/#{@port.instance}\/LUN#0\/Config\/#{@port.name_was}\/", @port.key_prefix(true)
130
+ end
131
+
132
+ should "not use previous name if parameter is true and name didn't change" do
133
+ assert !@port.name_changed?
134
+ assert_equal "VBoxInternal\/Devices\/#{@port.device}\/#{@port.instance}\/LUN#0\/Config\/#{@port.name}\/", @port.key_prefix(true)
135
+ end
136
+ end
137
+
138
+ context "destroying" do
139
+ setup do
140
+ @port.existing_record!
141
+ end
142
+
143
+ should "call delete on the extra data for each key" do
144
+ @ed = mock("ed")
145
+ @ed.expects(:delete).times(3)
146
+ @caller.expects(:extra_data).times(3).returns(@ed)
147
+ @port.destroy
148
+ end
149
+
150
+ should "do nothing if the record is new" do
151
+ @port.new_record!
152
+ @caller.expects(:extra_data).never
153
+ @port.destroy
154
+ end
155
+
156
+ should "be a new record after destroying" do
157
+ @port.destroy
158
+ assert @port.new_record?
159
+ end
160
+ end
161
+ end
162
+
163
+ context "relationships" do
164
+ context "saving" do
165
+ should "call #save on every object" do
166
+ objects = []
167
+ 5.times do |i|
168
+ object = mock("object#{i}")
169
+ object.expects(:save).once
170
+ objects.push(object)
171
+ end
172
+
173
+ VirtualBox::ForwardedPort.save_relationship(@caller, objects)
174
+ end
175
+ end
176
+
177
+ context "populating" do
178
+ setup do
179
+ @caller.stubs(:extra_data).returns({
180
+ "invalid" => "7",
181
+ "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/GuestPort" => "22",
182
+ "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/HostPort" => "2222",
183
+ "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/Protocol" => "TCP"
184
+ })
185
+
186
+ @objects = VirtualBox::ForwardedPort.populate_relationship(@caller, {})
187
+ end
188
+
189
+ should "return an array of ForwardedPorts" do
190
+ assert @objects.is_a?(VirtualBox::Proxies::Collection)
191
+ assert @objects.all? { |o| o.is_a?(VirtualBox::ForwardedPort) }
192
+ end
193
+
194
+ should "have the proper data" do
195
+ object = @objects.first
196
+ assert_equal "22", object.guestport
197
+ assert_equal "2222", object.hostport
198
+ assert_equal "TCP", object.protocol
199
+ end
200
+
201
+ should "be existing records" do
202
+ assert !@objects.first.new_record?
203
+ end
204
+ end
205
+ end
206
+ end
@@ -213,6 +213,10 @@ class SharedFolderTest < Test::Unit::TestCase
213
213
  setup do
214
214
  @value = VirtualBox::SharedFolder.populate_relationship(@caller, @data)
215
215
  end
216
+
217
+ should "be a 'collection'" do
218
+ assert @value.is_a?(VirtualBox::Proxies::Collection)
219
+ end
216
220
 
217
221
  should "create the correct amount of objects" do
218
222
  assert_equal 2, @value.length
@@ -89,6 +89,7 @@ showvminfo
89
89
  VirtualBox::Command.expects(:vboxmanage).with("showvminfo #{@name}").returns("")
90
90
  VirtualBox::Command.expects(:vboxmanage).with("list hdds").returns("")
91
91
  VirtualBox::Command.expects(:vboxmanage).with("list dvds").returns("")
92
+ VirtualBox::Command.expects(:vboxmanage).with("getextradata #{@name} enumerate").returns("")
92
93
  vm = VirtualBox::VM.find(@name)
93
94
  assert vm
94
95
  vm
@@ -99,6 +100,11 @@ showvminfo
99
100
  VirtualBox::Command.expects(:vboxmanage).with("showvminfo #{@name}").once
100
101
  VirtualBox::VM.human_info(@name)
101
102
  end
103
+
104
+ should "shell escape parameter" do
105
+ VirtualBox::Command.expects(:vboxmanage).with("showvminfo hello\\ world").once
106
+ VirtualBox::VM.human_info("hello world")
107
+ end
102
108
  end
103
109
 
104
110
  context "reading the VM state" do
@@ -359,6 +365,8 @@ raw
359
365
  VirtualBox::Nic.expects(:save_relationship).once
360
366
  VirtualBox::StorageController.expects(:save_relationship).once
361
367
  VirtualBox::SharedFolder.expects(:save_relationship).once
368
+ VirtualBox::ExtraData.expects(:save_relationship).once
369
+ VirtualBox::ForwardedPort.expects(:save_relationship).once
362
370
  assert @vm.save
363
371
  end
364
372
  end
@@ -379,6 +387,11 @@ raw
379
387
  assert @vm
380
388
  end
381
389
 
390
+ should "shell escape the VM name" do
391
+ VirtualBox::Command.expects(:vboxmanage).with("showvminfo hello\\ world --machinereadable").returns(@raw)
392
+ assert VirtualBox::VM.find("hello world")
393
+ end
394
+
382
395
  should "return a VM object with proper attributes" do
383
396
  @expected.each do |k,v|
384
397
  assert_equal v, @vm.read_attribute(k)
@@ -5,20 +5,22 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{virtualbox}
8
- s.version = "0.3.0"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Mitchell Hashimoto"]
12
- s.date = %q{2010-01-28}
12
+ s.date = %q{2010-01-29}
13
13
  s.description = %q{Create and modify virtual machines in VirtualBox using pure ruby.}
14
14
  s.email = %q{mitchell.hashimoto@gmail.com}
15
15
  s.extra_rdoc_files = [
16
- "TODO"
16
+ "LICENSE",
17
+ "TODO"
17
18
  ]
18
19
  s.files = [
19
20
  ".gitignore",
20
21
  ".yardopts",
21
22
  "Gemfile",
23
+ "LICENSE",
22
24
  "Rakefile",
23
25
  "Readme.md",
24
26
  "TODO",
@@ -36,6 +38,8 @@ Gem::Specification.new do |s|
36
38
  "lib/virtualbox/dvd.rb",
37
39
  "lib/virtualbox/exceptions.rb",
38
40
  "lib/virtualbox/ext/subclass_listing.rb",
41
+ "lib/virtualbox/extra_data.rb",
42
+ "lib/virtualbox/forwarded_port.rb",
39
43
  "lib/virtualbox/hard_drive.rb",
40
44
  "lib/virtualbox/image.rb",
41
45
  "lib/virtualbox/nic.rb",
@@ -53,6 +57,8 @@ Gem::Specification.new do |s|
53
57
  "test/virtualbox/command_test.rb",
54
58
  "test/virtualbox/dvd_test.rb",
55
59
  "test/virtualbox/ext/subclass_listing_test.rb",
60
+ "test/virtualbox/extra_data_test.rb",
61
+ "test/virtualbox/forwarded_port_test.rb",
56
62
  "test/virtualbox/hard_drive_test.rb",
57
63
  "test/virtualbox/image_test.rb",
58
64
  "test/virtualbox/nic_test.rb",
@@ -78,6 +84,8 @@ Gem::Specification.new do |s|
78
84
  "test/virtualbox/command_test.rb",
79
85
  "test/virtualbox/dvd_test.rb",
80
86
  "test/virtualbox/ext/subclass_listing_test.rb",
87
+ "test/virtualbox/extra_data_test.rb",
88
+ "test/virtualbox/forwarded_port_test.rb",
81
89
  "test/virtualbox/hard_drive_test.rb",
82
90
  "test/virtualbox/image_test.rb",
83
91
  "test/virtualbox/nic_test.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: virtualbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mitchell Hashimoto
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-28 00:00:00 -08:00
12
+ date: 2010-01-29 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -20,11 +20,13 @@ executables: []
20
20
  extensions: []
21
21
 
22
22
  extra_rdoc_files:
23
+ - LICENSE
23
24
  - TODO
24
25
  files:
25
26
  - .gitignore
26
27
  - .yardopts
27
28
  - Gemfile
29
+ - LICENSE
28
30
  - Rakefile
29
31
  - Readme.md
30
32
  - TODO
@@ -42,6 +44,8 @@ files:
42
44
  - lib/virtualbox/dvd.rb
43
45
  - lib/virtualbox/exceptions.rb
44
46
  - lib/virtualbox/ext/subclass_listing.rb
47
+ - lib/virtualbox/extra_data.rb
48
+ - lib/virtualbox/forwarded_port.rb
45
49
  - lib/virtualbox/hard_drive.rb
46
50
  - lib/virtualbox/image.rb
47
51
  - lib/virtualbox/nic.rb
@@ -59,6 +63,8 @@ files:
59
63
  - test/virtualbox/command_test.rb
60
64
  - test/virtualbox/dvd_test.rb
61
65
  - test/virtualbox/ext/subclass_listing_test.rb
66
+ - test/virtualbox/extra_data_test.rb
67
+ - test/virtualbox/forwarded_port_test.rb
62
68
  - test/virtualbox/hard_drive_test.rb
63
69
  - test/virtualbox/image_test.rb
64
70
  - test/virtualbox/nic_test.rb
@@ -106,6 +112,8 @@ test_files:
106
112
  - test/virtualbox/command_test.rb
107
113
  - test/virtualbox/dvd_test.rb
108
114
  - test/virtualbox/ext/subclass_listing_test.rb
115
+ - test/virtualbox/extra_data_test.rb
116
+ - test/virtualbox/forwarded_port_test.rb
109
117
  - test/virtualbox/hard_drive_test.rb
110
118
  - test/virtualbox/image_test.rb
111
119
  - test/virtualbox/nic_test.rb