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