virtualbox 0.3.0 → 0.4.0

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/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