virtual_box 0.1.2 → 0.1.4
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.lock +1 -1
- data/README.markdown +30 -0
- data/VERSION +1 -1
- data/lib/virtual_box/error.rb +1 -1
- data/lib/virtual_box/net.rb +36 -19
- data/lib/virtual_box/net/dhcp.rb +2 -2
- data/lib/virtual_box/vm.rb +34 -10
- data/lib/virtual_box/vm/disk.rb +10 -0
- data/test/helper.rb +3 -0
- data/test/virtual_box/integration_test.rb +2 -6
- data/test/virtual_box/net_test.rb +13 -13
- data/test/virtual_box/vm/disk_test.rb +8 -1
- data/test/virtual_box/vm_test.rb +27 -3
- data/virtual_box.gemspec +3 -3
- metadata +4 -4
data/Gemfile.lock
CHANGED
data/README.markdown
CHANGED
@@ -4,6 +4,36 @@ This gem is a Ruby API for VirtualBox, Sun's open-source virtualization software
|
|
4
4
|
that supports Linux, Mac OS X, and Windows.
|
5
5
|
|
6
6
|
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
virtual_box is mainly intended to help out with testing. The snippet below shows
|
10
|
+
a complete setup / teardown scenario.
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
setup do
|
14
|
+
vdi_file = '/tmp/disk.vdi'
|
15
|
+
@disk = VirtualBox::Vm::Disk.create :file => vdi_file,
|
16
|
+
:size => 16 * 1024 * 1024
|
17
|
+
iso_file = 'test/fixtures/tinycore/remix.iso'
|
18
|
+
@net = VirtualBox::Net.new(:ip => '192.168.66.6', :netmask => '255.255.255.0',
|
19
|
+
:dhcp => { :start_ip => '192.168.66.66' }).add
|
20
|
+
|
21
|
+
@vm = VirtualBox::Vm.new(
|
22
|
+
:board => { :ram => 256, :video_ram => 16, :os => :linux26 },
|
23
|
+
:io_buses => [{ :bus => :ide,
|
24
|
+
:disks => [{ :file => iso_file, :port => 1 }] },
|
25
|
+
{ :bus => :sata, :disks => [@disk] }],
|
26
|
+
:nics => [{ :mode => :host, :chip => :virtual,
|
27
|
+
:net_name => @net.name }]).register
|
28
|
+
end
|
29
|
+
|
30
|
+
teardown do
|
31
|
+
@vm.unregister
|
32
|
+
@net.remove
|
33
|
+
@disk.drop
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
7
37
|
## Features
|
8
38
|
|
9
39
|
Currently, the gem supports the following features:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
data/lib/virtual_box/error.rb
CHANGED
data/lib/virtual_box/net.rb
CHANGED
@@ -16,15 +16,17 @@ class Net
|
|
16
16
|
# attribute is read-only, and it is automatically set when the network is
|
17
17
|
# registered with VirtualBox.
|
18
18
|
# @return [String]
|
19
|
-
attr_reader :
|
19
|
+
attr_reader :name
|
20
20
|
|
21
21
|
# The name of the VirtualBox internal network.
|
22
22
|
#
|
23
|
+
# This is most likely not useful outside the VirtualBox API.
|
24
|
+
#
|
23
25
|
# VirtualBox's CLI does not provide a way to set the internal network name.
|
24
26
|
# Therefore, this attribute is read-only, and it is automatically set when the
|
25
27
|
# network is registered with VirtualBox.
|
26
28
|
# @return [String]
|
27
|
-
attr_reader :
|
29
|
+
attr_reader :vbox_name
|
28
30
|
|
29
31
|
# The MAC address of the host's virtual NIC that's connected to this network.
|
30
32
|
#
|
@@ -69,8 +71,8 @@ class Net
|
|
69
71
|
# True if this virtual network has been added to VirtualBox.
|
70
72
|
# @return [Boolean] true if this network exists, false otherwise
|
71
73
|
def live?
|
72
|
-
networks = self.class.all
|
73
|
-
network = networks.find { |net| net.
|
74
|
+
networks = self.class.all false
|
75
|
+
network = networks.find { |net| net.name == name }
|
74
76
|
network ? true : false
|
75
77
|
end
|
76
78
|
|
@@ -78,7 +80,7 @@ class Net
|
|
78
80
|
#
|
79
81
|
# @return [VirtualBox::Net] self, for easy call chaining
|
80
82
|
def add
|
81
|
-
unless
|
83
|
+
unless name.nil?
|
82
84
|
raise "Virtual network already added to VirtualBox"
|
83
85
|
end
|
84
86
|
|
@@ -88,17 +90,16 @@ class Net
|
|
88
90
|
unless match = /^interface\s+'(.*)'\s+.*created/i.match(output)
|
89
91
|
raise "VirtualBox output does not include interface name"
|
90
92
|
end
|
91
|
-
@
|
93
|
+
@name = match[1]
|
92
94
|
|
93
|
-
#
|
94
|
-
|
95
|
-
|
96
|
-
@name = network.name
|
95
|
+
# Query VirtualBox to pull the rest of the information.
|
96
|
+
network = self.class.named name
|
97
|
+
@vbox_name = network.vbox_name
|
97
98
|
@mac = network.mac
|
98
99
|
|
99
100
|
if (ip && ip != network.ip) || (netmask && netmask != network.netmask)
|
100
101
|
VirtualBox.run_command! ['VBoxManage', '--nologo', 'hostonlyif',
|
101
|
-
'ipconfig',
|
102
|
+
'ipconfig', name, '--ip', ip, '--netmask', netmask]
|
102
103
|
else
|
103
104
|
self.ip = network.ip
|
104
105
|
self.netmask = network.netmask
|
@@ -114,28 +115,44 @@ class Net
|
|
114
115
|
#
|
115
116
|
# @return [VirtualBox::Net] self, for easy call chaining
|
116
117
|
def remove
|
117
|
-
unless
|
118
|
+
unless name.nil?
|
118
119
|
dhcp.remove self if dhcp
|
119
|
-
VirtualBox.run_command ['VBoxManage', 'hostonlyif', 'remove',
|
120
|
+
VirtualBox.run_command ['VBoxManage', 'hostonlyif', 'remove', name]
|
120
121
|
end
|
121
122
|
self
|
122
123
|
end
|
123
124
|
|
124
125
|
# The virtual networks added to VirtualBox.
|
125
126
|
#
|
127
|
+
# @param [Boolean] with_dhcp if false, the returned VirtualBox::Net instances
|
128
|
+
# will have their dhcp property set to nil, even if they have DHCP
|
129
|
+
# servers; this saves a CLI call when DHCP information is not needed
|
126
130
|
# @return [Array<VirtualBox::Net>] all the DHCP servers added to VirtualBox
|
127
|
-
def self.all
|
128
|
-
dhcps = VirtualBox::Net::Dhcp.all
|
131
|
+
def self.all(with_dhcp = true)
|
132
|
+
dhcps = with_dhcp ? VirtualBox::Net::Dhcp.all : {}
|
129
133
|
|
130
134
|
output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'list',
|
131
135
|
'--long', 'hostonlyifs']
|
132
136
|
output.split("\n\n").map do |net_info|
|
133
|
-
net = new.from_net_info
|
134
|
-
net.dhcp = dhcps[net.
|
137
|
+
net = new.from_net_info net_info
|
138
|
+
net.dhcp = dhcps[net.vbox_name]
|
135
139
|
net
|
136
140
|
end
|
137
141
|
end
|
138
142
|
|
143
|
+
# The virtual network added to VirtualBox with a given name.
|
144
|
+
#
|
145
|
+
# This is a convenience for calling find on Net.all, so it's just as
|
146
|
+
# inefficient.
|
147
|
+
# @param [String] name the name to look for
|
148
|
+
# @param [Boolean] with_dhcp if false, the returned VirtualBox::Net instance
|
149
|
+
# will have its dhcp property set to nil, even if it has a DHCP
|
150
|
+
# server; this saves a CLI call when DHCP information is not needed
|
151
|
+
def self.named(name, with_dhcp = true)
|
152
|
+
networks = all with_dhcp
|
153
|
+
networks.find { |net| net.name == name }
|
154
|
+
end
|
155
|
+
|
139
156
|
# Parses information about a DHCP server returned by VirtualBox.
|
140
157
|
#
|
141
158
|
# The parsed information is used to replace this network's specification.
|
@@ -147,8 +164,8 @@ class Net
|
|
147
164
|
line.split(':', 2).map(&:strip)
|
148
165
|
}]
|
149
166
|
|
150
|
-
@
|
151
|
-
@
|
167
|
+
@name = info['Name']
|
168
|
+
@vbox_name = info['VBoxNetworkName']
|
152
169
|
@mac = info['HardwareAddress'].upcase.gsub(/[^0-9A-F]/, '')
|
153
170
|
self.ip = info['IPAddress']
|
154
171
|
self.netmask = info['NetworkMask']
|
data/lib/virtual_box/net/dhcp.rb
CHANGED
@@ -71,7 +71,7 @@ class Dhcp
|
|
71
71
|
'--netmask', netmask, '--lowerip', start_ip, '--upperip', end_ip,
|
72
72
|
'--enable']
|
73
73
|
if net_or_name.kind_of? VirtualBox::Net
|
74
|
-
command.push '--ifname', net_or_name.
|
74
|
+
command.push '--ifname', net_or_name.name
|
75
75
|
else
|
76
76
|
command.push '--netname', net_or_name
|
77
77
|
end
|
@@ -88,7 +88,7 @@ class Dhcp
|
|
88
88
|
def remove(net_or_name)
|
89
89
|
command = ['VBoxManage', 'dhcpserver', 'remove']
|
90
90
|
if net_or_name.kind_of? VirtualBox::Net
|
91
|
-
command.push '--ifname', net_or_name.
|
91
|
+
command.push '--ifname', net_or_name.name
|
92
92
|
else
|
93
93
|
command.push '--netname', net_or_name
|
94
94
|
end
|
data/lib/virtual_box/vm.rb
CHANGED
@@ -167,20 +167,44 @@ class Vm
|
|
167
167
|
# :nmi:: NMI (non-maskable interrupt)
|
168
168
|
# @return [VirtualBox::Vm] self, for easy call chaining
|
169
169
|
def control(action)
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
170
|
+
result = nil
|
171
|
+
|
172
|
+
3.times do
|
173
|
+
action_arg = case action
|
174
|
+
when :kill
|
175
|
+
'poweroff'
|
176
|
+
when :power_button
|
177
|
+
'acpipowerbutton'
|
178
|
+
when :nmi
|
179
|
+
'injectnmi'
|
180
|
+
else
|
181
|
+
action
|
182
|
+
end
|
178
183
|
|
179
|
-
|
180
|
-
|
184
|
+
result = VirtualBox.run_command ['VBoxManage', '--nologo', 'controlvm',
|
185
|
+
uid, action_arg]
|
186
|
+
return self if result[:status] == 0
|
187
|
+
|
188
|
+
# Perhaps the VM is already powered off?
|
189
|
+
if action == :kill || action == :power_button
|
190
|
+
return self unless live?
|
191
|
+
sleep 0.1
|
192
|
+
else
|
193
|
+
break
|
194
|
+
end
|
195
|
+
end
|
196
|
+
raise VirtualBox::Error, result
|
197
|
+
|
181
198
|
self
|
182
199
|
end
|
183
200
|
|
201
|
+
# True if this virtual machine is running inside VirtualBox.
|
202
|
+
#
|
203
|
+
# @return [Boolean] true if this VM is being simulated by VirtualBox
|
204
|
+
def live?
|
205
|
+
(uid && self.class.started_uids.include?(uid)) ? true : false
|
206
|
+
end
|
207
|
+
|
184
208
|
# The UUIDs of all VMs that are registered with VirtualBox.
|
185
209
|
#
|
186
210
|
# @return [Array<String>] UUIDs for VMs that VirtualBox is aware of
|
data/lib/virtual_box/vm/disk.rb
CHANGED
@@ -93,6 +93,16 @@ class Disk
|
|
93
93
|
new :file => path, :format => format, :media => :disk
|
94
94
|
end
|
95
95
|
|
96
|
+
# Removes the image file backing this disk.
|
97
|
+
#
|
98
|
+
# The method name is drop, as in "DROP TABLE". It doesn't remove the disk from
|
99
|
+
# any VM, it just removes the file.
|
100
|
+
# @return [VirtualBox::Vm::Disk] self, for easy call chaining
|
101
|
+
def drop
|
102
|
+
File.unlink @file if File.exist?(@file)
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
96
106
|
# Disk image format based on the extension in the file name.
|
97
107
|
def self.guess_image_format(image_file)
|
98
108
|
parts = File.basename(image_file).split('.')
|
data/test/helper.rb
CHANGED
@@ -11,6 +11,9 @@ require 'minitest/unit'
|
|
11
11
|
require 'minitest/spec'
|
12
12
|
require 'mocha'
|
13
13
|
|
14
|
+
require 'simplecov'
|
15
|
+
SimpleCov.start
|
16
|
+
|
14
17
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
18
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
19
|
require 'virtual_box'
|
@@ -15,7 +15,7 @@ describe 'VirtualBox' do
|
|
15
15
|
:io_buses => [{ :bus => :ide,
|
16
16
|
:disks => [{ :file => iso_file, :port => 1 }] }],
|
17
17
|
:nics => [{ :mode => :host, :chip => :virtual,
|
18
|
-
:net_name => @net.
|
18
|
+
:net_name => @net.name }]).register
|
19
19
|
end
|
20
20
|
|
21
21
|
after do
|
@@ -31,11 +31,7 @@ describe 'VirtualBox' do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
after do
|
34
|
-
unless @vm.nil?
|
35
|
-
@vm.stop
|
36
|
-
# Let VirtualBox stop the VM, so that it can be unregistered.
|
37
|
-
Kernel.sleep 0.5
|
38
|
-
end
|
34
|
+
@vm.stop unless @vm.nil?
|
39
35
|
end
|
40
36
|
|
41
37
|
it 'responds to a SSH connection' do
|
@@ -20,7 +20,9 @@ describe VirtualBox::Net do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'has its device name show up in ifconfig' do
|
23
|
-
|
23
|
+
# NOTE: on OSX, recent VirtualBox builds return user-friendly NIC names,
|
24
|
+
# such as "en0: Ethernet" instead of "en0"
|
25
|
+
`ifconfig -a`.must_include card[:name].split(':', 2).first
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -49,12 +51,12 @@ describe VirtualBox::Net do
|
|
49
51
|
end
|
50
52
|
|
51
53
|
it 'has an interface name that shows up in the ifconfig output' do
|
52
|
-
@net.
|
53
|
-
`ifconfig -a`.must_include @net.
|
54
|
+
@net.name.wont_be_nil
|
55
|
+
`ifconfig -a`.must_include @net.name
|
54
56
|
end
|
55
57
|
|
56
|
-
it 'has a name' do
|
57
|
-
@net.
|
58
|
+
it 'has a VirtualBox network name' do
|
59
|
+
@net.vbox_name.wont_be_nil
|
58
60
|
end
|
59
61
|
|
60
62
|
it 'has a MAC address' do
|
@@ -90,13 +92,13 @@ describe VirtualBox::Net do
|
|
90
92
|
end
|
91
93
|
|
92
94
|
it 'has an interface name that shows up in the ifconfig output' do
|
93
|
-
@net.
|
94
|
-
`ifconfig -a`.must_include @net.
|
95
|
+
@net.name.wont_be_nil
|
96
|
+
`ifconfig -a`.must_include @net.name
|
95
97
|
end
|
96
98
|
|
97
99
|
it 'shows up on the list of live networks' do
|
98
100
|
networks = VirtualBox::Net.all
|
99
|
-
network = networks.find { |n| n.
|
101
|
+
network = networks.find { |n| n.name == @net.name }
|
100
102
|
network.to_hash.must_equal @net.to_hash
|
101
103
|
end
|
102
104
|
end
|
@@ -124,14 +126,12 @@ describe VirtualBox::Net do
|
|
124
126
|
end
|
125
127
|
|
126
128
|
it 'has an interface name that shows up in the ifconfig output' do
|
127
|
-
@net.
|
128
|
-
`ifconfig -a`.must_include @net.
|
129
|
+
@net.name.wont_be_nil
|
130
|
+
`ifconfig -a`.must_include @net.name
|
129
131
|
end
|
130
132
|
|
131
133
|
it 'shows up on the list of live networks' do
|
132
|
-
|
133
|
-
network = networks.find { |n| n.if_name == @net.if_name }
|
134
|
-
network.to_hash.must_equal @net.to_hash
|
134
|
+
VirtualBox::Net.named(@net.name).to_hash.must_equal @net.to_hash
|
135
135
|
end
|
136
136
|
end
|
137
137
|
end
|
@@ -15,7 +15,7 @@ describe VirtualBox::Vm::Disk do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
describe 'create' do
|
18
|
+
describe '#create' do
|
19
19
|
describe '16-megabyte VMDK flexible' do
|
20
20
|
let(:vmdk_path) { '/tmp/disk.vmdk' }
|
21
21
|
|
@@ -44,6 +44,13 @@ describe VirtualBox::Vm::Disk do
|
|
44
44
|
it 'returns a VMDK Disk' do
|
45
45
|
@disk.format.must_equal :vmdk
|
46
46
|
end
|
47
|
+
|
48
|
+
describe '#drop' do
|
49
|
+
before { @disk.drop }
|
50
|
+
it 'removes the file' do
|
51
|
+
File.exist?(vmdk_path).must_equal false
|
52
|
+
end
|
53
|
+
end
|
47
54
|
end
|
48
55
|
|
49
56
|
describe '16-megabyte preallocated' do
|
data/test/virtual_box/vm_test.rb
CHANGED
@@ -18,7 +18,7 @@ describe VirtualBox::Vm do
|
|
18
18
|
@vm.wont_be :registered?
|
19
19
|
end
|
20
20
|
|
21
|
-
describe '
|
21
|
+
describe '#register' do
|
22
22
|
before do
|
23
23
|
@vm.register
|
24
24
|
end
|
@@ -36,19 +36,43 @@ describe VirtualBox::Vm do
|
|
36
36
|
uids.must_include @vm.uid
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
it 'is not live' do
|
40
|
+
@vm.live?.must_equal false
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#stop' do
|
44
|
+
it "doesn't crash" do
|
45
|
+
@vm.stop
|
46
|
+
@vm.live?.must_equal false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#start' do
|
40
51
|
before do
|
41
52
|
@vm.start
|
42
53
|
end
|
43
54
|
after do
|
44
55
|
@vm.stop
|
45
|
-
sleep 0.5 # VirtualBox will barf if we unregister the VM right away.
|
46
56
|
end
|
47
57
|
|
48
58
|
it 'shows up on the list of started VM UIDs' do
|
49
59
|
uids = VirtualBox::Vm.started_uids
|
50
60
|
uids.must_include @vm.uid
|
51
61
|
end
|
62
|
+
|
63
|
+
it 'is live' do
|
64
|
+
@vm.live?.must_equal true
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#stop' do
|
68
|
+
before do
|
69
|
+
@vm.stop
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'is no longer live' do
|
73
|
+
@vm.live?.must_equal false
|
74
|
+
end
|
75
|
+
end
|
52
76
|
end
|
53
77
|
end
|
54
78
|
end
|
data/virtual_box.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "virtual_box"
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Victor Costan"]
|
12
|
-
s.date = "2012-05-
|
12
|
+
s.date = "2012-05-07"
|
13
13
|
s.description = "Drives the VirtualBox command-line to manage VMs"
|
14
14
|
s.email = "victor@costan.us"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -55,7 +55,7 @@ Gem::Specification.new do |s|
|
|
55
55
|
s.homepage = "http://github.com/csail/police"
|
56
56
|
s.licenses = ["MIT"]
|
57
57
|
s.require_paths = ["lib"]
|
58
|
-
s.rubygems_version = "1.8.
|
58
|
+
s.rubygems_version = "1.8.23"
|
59
59
|
s.summary = "VirtualBox driver"
|
60
60
|
|
61
61
|
if s.respond_to? :specification_version then
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virtual_box
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: posix-spawn
|
@@ -228,7 +228,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
228
228
|
version: '0'
|
229
229
|
segments:
|
230
230
|
- 0
|
231
|
-
hash:
|
231
|
+
hash: 1577724543477993918
|
232
232
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
233
|
none: false
|
234
234
|
requirements:
|
@@ -237,7 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
237
237
|
version: '0'
|
238
238
|
requirements: []
|
239
239
|
rubyforge_project:
|
240
|
-
rubygems_version: 1.8.
|
240
|
+
rubygems_version: 1.8.23
|
241
241
|
signing_key:
|
242
242
|
specification_version: 3
|
243
243
|
summary: VirtualBox driver
|