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 +1 -0
- data/LICENSE +19 -0
- data/Readme.md +1 -1
- data/VERSION +1 -1
- data/docs/WhatsNew.md +23 -30
- data/lib/virtualbox.rb +2 -0
- data/lib/virtualbox/abstract_model/relatable.rb +19 -6
- data/lib/virtualbox/extra_data.rb +151 -0
- data/lib/virtualbox/forwarded_port.rb +197 -0
- data/lib/virtualbox/image.rb +1 -1
- data/lib/virtualbox/nic.rb +1 -1
- data/lib/virtualbox/shared_folder.rb +1 -1
- data/lib/virtualbox/storage_controller.rb +0 -1
- data/lib/virtualbox/vm.rb +8 -4
- data/test/virtualbox/abstract_model/relatable_test.rb +22 -4
- data/test/virtualbox/extra_data_test.rb +218 -0
- data/test/virtualbox/forwarded_port_test.rb +206 -0
- data/test/virtualbox/shared_folder_test.rb +4 -0
- data/test/virtualbox/vm_test.rb +13 -0
- data/virtualbox.gemspec +11 -3
- metadata +10 -2
data/Gemfile
CHANGED
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.
|
1
|
+
0.4.0
|
data/docs/WhatsNew.md
CHANGED
@@ -1,41 +1,34 @@
|
|
1
|
-
# What's New in 0.
|
1
|
+
# What's New in 0.4.x?
|
2
2
|
|
3
|
-
##
|
3
|
+
## "Extra Data" on VMs / Global
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
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
|
-
|
18
|
-
{VirtualBox::SharedFolder}.
|
13
|
+
Read more about extra data {VirtualBox::ExtraData here}.
|
19
14
|
|
20
|
-
##
|
15
|
+
## Port Forwarding
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
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.
|
data/lib/virtualbox.rb
CHANGED
@@ -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
|
-
|
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.
|
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.
|
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.
|
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
|
data/lib/virtualbox/image.rb
CHANGED
data/lib/virtualbox/nic.rb
CHANGED
@@ -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.
|
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
|
data/lib/virtualbox/vm.rb
CHANGED
@@ -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.
|
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.
|
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
|
67
|
-
assert
|
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,
|
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
|
data/test/virtualbox/vm_test.rb
CHANGED
@@ -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)
|
data/virtualbox.gemspec
CHANGED
@@ -5,20 +5,22 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{virtualbox}
|
8
|
-
s.version = "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-
|
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
|
-
"
|
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.
|
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-
|
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
|