virtual_box 0.0.1 → 0.1.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/.autotest +9 -0
- data/.document +5 -0
- data/.project +12 -0
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +44 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +67 -0
- data/Rakefile +35 -22
- data/VERSION +1 -0
- data/lib/virtual_box/board.rb +287 -0
- data/lib/virtual_box/cli.rb +20 -0
- data/lib/virtual_box/dhcp.rb +149 -0
- data/lib/virtual_box/disk.rb +138 -0
- data/lib/virtual_box/io_bus.rb +261 -0
- data/lib/virtual_box/net.rb +144 -0
- data/lib/virtual_box/nic.rb +213 -0
- data/lib/virtual_box/version.rb +37 -24
- data/lib/virtual_box/vm.rb +289 -24
- data/lib/virtual_box.rb +14 -9
- data/test/helper.rb +24 -0
- data/test/tasks/tinycore.rake +378 -0
- data/test/virtual_box/board_test.rb +39 -0
- data/test/virtual_box/cli_test.rb +33 -0
- data/test/virtual_box/dhcp_test.rb +52 -0
- data/test/virtual_box/disk_test.rb +116 -0
- data/test/virtual_box/integration_test.rb +53 -0
- data/test/virtual_box/io_bus_test.rb +54 -0
- data/test/virtual_box/net_test.rb +80 -0
- data/test/virtual_box/nic_test.rb +50 -0
- data/test/virtual_box/version_test.rb +71 -0
- data/test/virtual_box/vm_test.rb +55 -0
- data/virtual_box.gemspec +81 -22
- metadata +208 -89
- data/CHANGELOG +0 -1
- data/LICENSE +0 -21
- data/Manifest +0 -17
- data/README.textile +0 -19
- data/lib/virtual_box/command_line.rb +0 -27
- data/lib/virtual_box/vm/general_settings.rb +0 -136
- data/lib/virtual_box/vm/identity.rb +0 -43
- data/lib/virtual_box/vm/lifecycle.rb +0 -42
- data/test/command_line_test.rb +0 -19
- data/test/general_settings_test.rb +0 -20
- data/test/lifecycle_test.rb +0 -64
- data/test/version_test.rb +0 -56
- data/testdata/golden_general_params.txt +0 -1
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module VirtualBox
|
4
|
+
|
5
|
+
# Configuration for a network card.
|
6
|
+
class Nic
|
7
|
+
# The kind of network emulation implemented on this card.
|
8
|
+
#
|
9
|
+
# Can be one of the following values:
|
10
|
+
# :nat:: uses VirtualBox internal NAT engine to hide under host OS
|
11
|
+
# :bridged:: bypasses host OS, connects directly to a network interface
|
12
|
+
# :host:: virtual network connecting guest to host
|
13
|
+
# :virtual:: virtual network connecting multiple guests
|
14
|
+
# @return [Symbol]
|
15
|
+
attr_accessor :mode
|
16
|
+
|
17
|
+
# The NIC controller chip.
|
18
|
+
#
|
19
|
+
# Can be one of the following values:
|
20
|
+
# :amd:: AMD PCNet FAST III (good default)
|
21
|
+
# :intel:: Intel PRO/1000 MT Server (for newer Windows systems)
|
22
|
+
# :intel_xp:: Intel PRO/1000 MT Server (for Windows XP)
|
23
|
+
# :virtio:: fake card optimized for virtualization (custom drivers needed)
|
24
|
+
# @return [Symbol]
|
25
|
+
attr_accessor :chip
|
26
|
+
|
27
|
+
# Name of the virtual network that the NIC is connected to.
|
28
|
+
#
|
29
|
+
# The identifier differs depending on the networking mode:
|
30
|
+
# :nat:: not applicable
|
31
|
+
# :bridged:: name of the bridge network interface on the host
|
32
|
+
# :host:: name of the host-only network interface
|
33
|
+
# :virtual:: virtual network name
|
34
|
+
# @return [Symbol]
|
35
|
+
attr_accessor :net_name
|
36
|
+
|
37
|
+
# MAC address for the network card, as a hexadecimal string.
|
38
|
+
#
|
39
|
+
# The format for specifying MACs is '0123456789AB'. A random MAC will be
|
40
|
+
# generated if one is not assigned.
|
41
|
+
# @return [String]
|
42
|
+
attr_accessor :mac
|
43
|
+
|
44
|
+
# Path to a file that logs a network trace for the VM.
|
45
|
+
#
|
46
|
+
# Can be null to disable tracing.
|
47
|
+
# @return [String]
|
48
|
+
attr_accessor :trace_file
|
49
|
+
|
50
|
+
undef :mode
|
51
|
+
def mode
|
52
|
+
@mode ||= :nat
|
53
|
+
end
|
54
|
+
|
55
|
+
undef :chip
|
56
|
+
def chip
|
57
|
+
@chip ||= (:mode == :virtual) ? :virtual : :amd
|
58
|
+
end
|
59
|
+
|
60
|
+
undef :mac
|
61
|
+
def mac
|
62
|
+
return @mac if @mac
|
63
|
+
@mac = SecureRandom.hex(6).upcase
|
64
|
+
@mac[1] = 'A' # Set the OUI bits to unicast and globally unique
|
65
|
+
@mac
|
66
|
+
end
|
67
|
+
undef :mac=
|
68
|
+
def mac=(new_mac)
|
69
|
+
@mac = new_mac && new_mac.upcase.gsub(/[^0-9A-F]/, '')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates a NIC with the given attributes.
|
73
|
+
#
|
74
|
+
# @param [Hash<Symbol, Object>] options ActiveRecord-style initial values for
|
75
|
+
# attributes; can be used together with Nic#to_hash to save and restore
|
76
|
+
def initialize(options = {})
|
77
|
+
options.each { |k, v| self.send :"#{k}=", v }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Arguments to "VBoxManage modifyvm" describing the NIC.
|
81
|
+
#
|
82
|
+
# @param [Number] nic_id the number of the card (1-4) connected to the host
|
83
|
+
# @return [Array<String>] arguments that can be concatenated to a "VBoxManage
|
84
|
+
# modifyvm" command to express this NIC specification
|
85
|
+
def to_params(nic_id)
|
86
|
+
params = []
|
87
|
+
|
88
|
+
params.push "--nic#{nic_id}"
|
89
|
+
case mode
|
90
|
+
when :nat
|
91
|
+
params.push 'nat'
|
92
|
+
when :bridged
|
93
|
+
params.push 'bridged', "--bridgeadapter#{nic_id}", net_name
|
94
|
+
when :virtual
|
95
|
+
params.push 'intnet', "--intnet#{nic_id}", net_name
|
96
|
+
when :host
|
97
|
+
params.push 'hostonly', "--hostonlyadapter#{nic_id}", net_name
|
98
|
+
else
|
99
|
+
params.push 'null'
|
100
|
+
end
|
101
|
+
|
102
|
+
params.push "--nictype#{nic_id}", case chip
|
103
|
+
when :amd
|
104
|
+
'Am79C973'
|
105
|
+
when :intel
|
106
|
+
'82545EM'
|
107
|
+
when :intel_xp
|
108
|
+
'82543GC'
|
109
|
+
when :virtual
|
110
|
+
'virtio'
|
111
|
+
end
|
112
|
+
|
113
|
+
params.push "--cableconnected#{nic_id}", 'on'
|
114
|
+
params.push "--macaddress#{nic_id}", mac if mac
|
115
|
+
|
116
|
+
params.push "--nictrace#{nic_id}"
|
117
|
+
if trace_file
|
118
|
+
params.push 'on', "--nictracefile#{nic_id}", trace_file
|
119
|
+
else
|
120
|
+
params.push 'off'
|
121
|
+
end
|
122
|
+
|
123
|
+
params
|
124
|
+
end
|
125
|
+
|
126
|
+
# Parses "VBoxManage showvminfo --machinereadable" output into this instance.
|
127
|
+
#
|
128
|
+
# @param [Hash<String, String>] params the "VBoxManage showvminfo" output,
|
129
|
+
# parsed by Vm.parse_machine_readble
|
130
|
+
# @param [Integer] nic_id the NIC's number in the VM
|
131
|
+
# @return [VirtualBox::Nic] self, for easy call chaining
|
132
|
+
def from_params(params, nic_id)
|
133
|
+
case params["nic#{nic_id}"]
|
134
|
+
when 'nat'
|
135
|
+
self.mode = :nat
|
136
|
+
when 'bridged'
|
137
|
+
self.mode = :bridged
|
138
|
+
self.net_name = params["bridgeadapter#{nic_id}"]
|
139
|
+
when 'intnet'
|
140
|
+
self.mode = :virtual
|
141
|
+
self.net_name = params["intnet#{nic_id}"]
|
142
|
+
when 'hostonly'
|
143
|
+
self.mode = :host
|
144
|
+
self.net_name = params["hostonlyadapter#{nic_id}"]
|
145
|
+
end
|
146
|
+
|
147
|
+
self.chip = case params["nictype#{nic_id}"]
|
148
|
+
when 'Am79C970A', 'Am79C973',
|
149
|
+
:amd
|
150
|
+
when '82543GC'
|
151
|
+
:intel_xp
|
152
|
+
when '82540OEM', '82545EM'
|
153
|
+
:intel
|
154
|
+
when 'virtio'
|
155
|
+
:virtual
|
156
|
+
else
|
157
|
+
(self.mode == :virtual) ? :virtual : :amd
|
158
|
+
end
|
159
|
+
|
160
|
+
self.mac = params["macaddress#{nic_id}"]
|
161
|
+
if params["nictrace#{nic_id}"] == 'on'
|
162
|
+
self.trace_file = params["nictracefile#{nic_id}"]
|
163
|
+
else
|
164
|
+
self.trace_file = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
# Hash capturing this specification. Can be passed to Nic#new.
|
171
|
+
#
|
172
|
+
# @return [Hash<Symbol, Object>] Ruby-friendly Hash that can be used to
|
173
|
+
# re-create this NIC specification
|
174
|
+
def to_hash
|
175
|
+
{ :mode => mode, :chip => chip, :net_name => net_name, :mac => mac,
|
176
|
+
:trace_file => trace_file }
|
177
|
+
end
|
178
|
+
|
179
|
+
# Information about the NICs attached to the computer.
|
180
|
+
#
|
181
|
+
# @return [Array<Hash<Symbol, Object>>] an array with one hash per NIC; hashes
|
182
|
+
# have the following keys:
|
183
|
+
# :id:: the inteface id (use when setting a Nic's net_id)
|
184
|
+
# :ip:: the IP address (check for 0.0.0.0 to see if it's live)
|
185
|
+
# :mask:: the netmask
|
186
|
+
# :mac:: the NICs MAC address
|
187
|
+
def self.host_nics
|
188
|
+
@host_nics ||= get_host_nics
|
189
|
+
end
|
190
|
+
|
191
|
+
# Queries VirtualBox for the network interfaces on the computer.
|
192
|
+
#
|
193
|
+
# @return (see .host_nics)
|
194
|
+
def self.get_host_nics
|
195
|
+
result = VirtualBox.run_command ['VBoxManage', '--nologo', 'list',
|
196
|
+
'--long', 'hostifs']
|
197
|
+
if result.status != 0
|
198
|
+
raise 'Unexpected error code returned by VirtualBox'
|
199
|
+
end
|
200
|
+
|
201
|
+
result.output.split("\n\n").map do |nic_info|
|
202
|
+
i = Hash[nic_info.split("\n").map { |line|
|
203
|
+
line.split(':', 2).map(&:strip)
|
204
|
+
}]
|
205
|
+
{
|
206
|
+
:id => i['Name'], :ip => i['IPAddress'], :mask => i['NetworkMask'],
|
207
|
+
:mac => i['HardwareAddress'].upcase.gsub(/[^0-9A-F]/, '')
|
208
|
+
}
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end # class VirtualBox::Nic
|
212
|
+
|
213
|
+
end # namespace VirtualBox
|
data/lib/virtual_box/version.rb
CHANGED
@@ -1,46 +1,59 @@
|
|
1
1
|
# VirtualBox version detection.
|
2
|
-
#
|
3
|
-
# Author:: Victor Costan
|
4
|
-
# Copyright:: Copyright (C) 2009 Zergling.Net
|
5
|
-
# License:: MIT
|
6
2
|
|
7
|
-
# :nodoc: namespace
|
8
3
|
module VirtualBox
|
9
4
|
@version_info = nil
|
10
5
|
|
11
6
|
# True if the installed VirtualBox is the open-source edition.
|
12
7
|
#
|
13
|
-
# The open-source edition of VirtualBox has some limitations, such as no
|
14
|
-
# for RDP and USB devices.
|
8
|
+
# The open-source edition of VirtualBox has some limitations, such as no
|
9
|
+
# support for RDP and USB devices.
|
15
10
|
def self.ose?
|
16
|
-
|
11
|
+
unless version.edition
|
12
|
+
raise 'VirtualBox is not installed on this machine.'
|
13
|
+
end
|
14
|
+
version.edition == 'OSE'
|
17
15
|
end
|
18
16
|
|
19
|
-
# Version information about the installed
|
17
|
+
# Version information about the VirtualBox package installed on this machine.
|
20
18
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
def self.
|
19
|
+
# @return [Hash<Symbol, Object>, Boolean] false if VirtualBox is not
|
20
|
+
# installed; otherwise, a hash with the following keys:
|
21
|
+
# :svn:: (number) the SVN revision that VirtualBox is built off of
|
22
|
+
# :edition:: the VirtualBox edition ('' for the personal edition, 'OSE'
|
23
|
+
# for the open-source edition)
|
24
|
+
# :release:: the public release number (e.g. '3.0.4')
|
25
|
+
def self.version
|
28
26
|
return @version_info unless @version_info.nil?
|
29
27
|
|
30
|
-
cmd_result =
|
31
|
-
|
28
|
+
cmd_result = run_command ['VBoxManage', '--version']
|
29
|
+
if cmd_result.status != 0
|
30
|
+
@version_info = Hashie::Mash.new
|
31
|
+
return @version_info
|
32
|
+
end
|
32
33
|
|
33
|
-
output = cmd_result
|
34
|
-
|
34
|
+
output = cmd_result.output.strip
|
35
|
+
|
35
36
|
if revision_offset = output.rindex('r')
|
36
|
-
|
37
|
+
revision = output[revision_offset + 1, output.length].to_i
|
37
38
|
output.slice! revision_offset..-1
|
39
|
+
else
|
40
|
+
revision = nil
|
38
41
|
end
|
42
|
+
|
39
43
|
if edition_offset = output.rindex('_')
|
40
|
-
|
44
|
+
edition = output[edition_offset + 1, output.length]
|
41
45
|
output.slice! edition_offset..-1
|
46
|
+
else
|
47
|
+
edition = ''
|
42
48
|
end
|
43
|
-
|
44
|
-
@version_info
|
49
|
+
|
50
|
+
@version_info = Hashie::Mash.new :release => output, :svn => revision,
|
51
|
+
:edition => edition
|
52
|
+
end
|
53
|
+
|
54
|
+
# Removes the cached information on the VirtualBox package version.
|
55
|
+
# @return <NilObject> nil
|
56
|
+
def self.reset_version_info!
|
57
|
+
@version_info = nil
|
45
58
|
end
|
46
59
|
end # namespace VirtualBox
|
data/lib/virtual_box/vm.rb
CHANGED
@@ -1,35 +1,300 @@
|
|
1
|
-
# Definition for the main VM class.
|
2
|
-
#
|
3
|
-
# Author:: Victor Costan
|
4
|
-
# Copyright:: Copyright (C) 2009 Zergling.Net
|
5
|
-
# License:: MIT
|
6
|
-
|
7
|
-
# :nodoc: namespace
|
8
1
|
module VirtualBox
|
2
|
+
|
3
|
+
# VirtualBox virtual machine.
|
4
|
+
class Vm
|
5
|
+
# The UUID used to register this virtual machine with VirtualBox.
|
6
|
+
#
|
7
|
+
# The UUID is used to identify the VM in many VirtualBox commands. It should
|
8
|
+
# not be changed once the VM is registered. In fact, the UUID should not
|
9
|
+
# be manually assigned under normal use.
|
10
|
+
# @return [String]
|
11
|
+
attr_accessor :uid
|
12
|
+
|
13
|
+
# A user-friendly name for this virtual machine.
|
14
|
+
#
|
15
|
+
# If not assigned, a unique name will be generated based on the VM's UUID.
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
attr_accessor :name
|
19
|
+
|
20
|
+
# The general VM configuration.
|
21
|
+
# @return [VirtualBox::Board]
|
22
|
+
attr_accessor :board
|
9
23
|
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
include Identity
|
14
|
-
include Lifecycle
|
24
|
+
# The IO controllers (and disks) connected to the VM.
|
25
|
+
# @return [Array<VirtualBox::IoBus>]
|
26
|
+
attr_accessor :io_buses
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
28
|
+
# The network cards connected to this virtual machine.
|
29
|
+
# @return [Array<VirtualBox::Nic>]
|
30
|
+
attr_accessor :nics
|
31
|
+
|
32
|
+
# If true, the VM's screen will be displayed in a GUI.
|
33
|
+
#
|
34
|
+
# This is only intended for manual testing. Many continuous integration
|
35
|
+
# servers cannot display the VirtualBox GUI, so this attribute should not be
|
36
|
+
# set to true in test suites.
|
37
|
+
# @return [Boolean]
|
38
|
+
attr_accessor :gui
|
39
|
+
|
40
|
+
undef :uid
|
41
|
+
def uid
|
42
|
+
@uid ||= UUID.generate
|
43
|
+
end
|
44
|
+
|
45
|
+
undef :name
|
46
|
+
def name
|
47
|
+
@name ||= 'rbx_' + uid.gsub('-', '')
|
19
48
|
end
|
20
49
|
|
21
|
-
|
50
|
+
undef :board=
|
51
|
+
def board=(new_board)
|
52
|
+
@board = if new_board.kind_of?(VirtualBox::Board)
|
53
|
+
new_board
|
54
|
+
else
|
55
|
+
VirtualBox::Board.new new_board
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
undef :io_buses=
|
60
|
+
def io_buses=(new_io_buses)
|
61
|
+
@io_buses = new_io_buses.map do |io_bus|
|
62
|
+
if io_bus.kind_of?(VirtualBox::IoBus)
|
63
|
+
io_bus
|
64
|
+
else
|
65
|
+
VirtualBox::IoBus.new io_bus
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
undef :nics=
|
71
|
+
def nics=(new_nics)
|
72
|
+
@nics = []
|
73
|
+
new_nics.each do |nic|
|
74
|
+
if nic.kind_of?(VirtualBox::Nic) || nic.nil?
|
75
|
+
@nics << nic
|
76
|
+
else
|
77
|
+
options = nic.dup
|
78
|
+
port = options.delete(:port) || @nics.length
|
79
|
+
@nics[port] = VirtualBox::Nic.new options
|
80
|
+
end
|
81
|
+
end
|
82
|
+
new_nics
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates a new virtual machine specification based on the given attributes.
|
22
86
|
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
87
|
+
# @param [Hash<Symbol, Object>] options ActiveRecord-style initial values for
|
88
|
+
# attributes; can be used together with Vm#to_hash to save and restore
|
89
|
+
def initialize(options = {})
|
90
|
+
self.board = {}
|
91
|
+
self.io_buses = []
|
92
|
+
self.nics = []
|
93
|
+
self.gui = false
|
94
|
+
options.each { |k, v| self.send :"#{k}=", v }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Hash capturing this specification. Can be passed to Vm#new.
|
98
|
+
#
|
99
|
+
# @return [Hash<Symbol, Object>] Ruby-friendly Hash that can be used to
|
100
|
+
# re-create this virtual machine specification
|
101
|
+
def to_hash
|
102
|
+
{
|
103
|
+
:name => name, :uid => uid, :gui => gui,
|
104
|
+
:board => board.to_hash, :io_buses => io_buses.map(&:to_hash),
|
105
|
+
:nics => nics.map.
|
106
|
+
with_index { |nic, i| nic && nic.to_hash.merge!(:port => i) }.
|
107
|
+
reject!(&:nil?)
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
# True if this VM has been registered with VirtualBox.
|
112
|
+
#
|
113
|
+
# @return [Boolean] true for VMs that are already registered with VirtualBox
|
114
|
+
def registered?
|
115
|
+
self.class.registered_uids.include? uid
|
116
|
+
end
|
117
|
+
|
118
|
+
# Registers this VM with VirtualBox.
|
119
|
+
#
|
120
|
+
# @return [VirtualBox::Vm] self, for easy call chaining
|
121
|
+
def register
|
122
|
+
unregister if registered?
|
123
|
+
|
124
|
+
result = VirtualBox.run_command ['VBoxManage', 'createvm',
|
125
|
+
'--name', name, '--uuid', uid, '--register']
|
126
|
+
if result.status != 0
|
127
|
+
raise 'Unexpected error code returned by VirtualBox'
|
128
|
+
end
|
129
|
+
|
130
|
+
begin
|
131
|
+
push_config
|
132
|
+
rescue
|
133
|
+
unregister
|
134
|
+
raise
|
135
|
+
end
|
136
|
+
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
# De-registers this VM from VirtualBox's database.
|
141
|
+
#
|
142
|
+
# @return [VirtualBox::Vm] self, for easy call chaining
|
143
|
+
def unregister
|
144
|
+
VirtualBox.run_command ['VBoxManage', 'unregistervm', uid, '--delete']
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
# Starts the virtual machine.
|
149
|
+
#
|
150
|
+
# @return [VirtualBox::Vm] self, for easy call chaining
|
151
|
+
def start
|
152
|
+
register unless registered?
|
153
|
+
|
154
|
+
result = VirtualBox.run_command ['VBoxManage', '--nologo', 'startvm', uid,
|
155
|
+
'--type', gui ? 'gui' : 'headless']
|
156
|
+
if result.status != 0
|
157
|
+
raise 'Unexpected error code returned by VirtualBox'
|
158
|
+
end
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
# Stops the virtual machine simulation.
|
163
|
+
#
|
164
|
+
# This is equivalent to pulling the power cord from a physical machine.
|
165
|
+
# @return [VirtualBox::Vm] self, for easy call chaining
|
166
|
+
def stop
|
167
|
+
control :kill
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
# Controls a started virtual machine.
|
172
|
+
#
|
173
|
+
# @param [Symbol] action the following actions are supported:
|
174
|
+
# :kill:: hard power-off (pulling the power cord from the machine)
|
175
|
+
# :power_button:: Power button press
|
176
|
+
# :nmi:: NMI (non-maskable interrupt)
|
177
|
+
# @return [VirtualBox::Vm] self, for easy call chaining
|
178
|
+
def control(action)
|
179
|
+
action = case action
|
180
|
+
when :kill
|
181
|
+
'poweroff'
|
182
|
+
when :power_button
|
183
|
+
'acpipowerbutton'
|
184
|
+
when :nmi
|
185
|
+
'injectnmi'
|
186
|
+
end
|
187
|
+
|
188
|
+
result = VirtualBox.run_command ['VBoxManage', '--nologo', 'controlvm', uid,
|
189
|
+
action]
|
190
|
+
if result.status != 0
|
191
|
+
raise 'Unexpected error code returned by VirtualBox'
|
192
|
+
end
|
193
|
+
self
|
194
|
+
end
|
195
|
+
|
196
|
+
# The UUIDs of all VMs that are registered with VirtualBox.
|
197
|
+
#
|
198
|
+
# @return [Array<String>] UUIDs for VMs that VirtualBox is aware of
|
199
|
+
def self.registered_uids
|
200
|
+
result = VirtualBox.run_command ['VBoxManage', '--nologo', 'list', 'vms']
|
201
|
+
if result.status != 0
|
202
|
+
raise 'Unexpected error code returned by VirtualBox'
|
203
|
+
end
|
204
|
+
result.output.split("\n").map do |id_info|
|
205
|
+
uid_offset = id_info.rindex(?{) + 1
|
206
|
+
uid = id_info[uid_offset...-1] # Exclude the closing }
|
207
|
+
end
|
26
208
|
end
|
27
209
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
210
|
+
# The UUIDs of all VirtualBox VMs that are started.
|
211
|
+
#
|
212
|
+
# @return [Array<String>] UUIDs for VMs that are running in VirtualBox
|
213
|
+
def self.started_uids
|
214
|
+
result = VirtualBox.run_command ['VBoxManage', '--nologo', 'list',
|
215
|
+
'runningvms']
|
216
|
+
if result.status != 0
|
217
|
+
raise 'Unexpected error code returned by VirtualBox'
|
218
|
+
end
|
219
|
+
result.output.split("\n").map do |id_info|
|
220
|
+
uid_offset = id_info.rindex(?{) + 1
|
221
|
+
uid = id_info[uid_offset...-1] # Exclude the closing }
|
222
|
+
end
|
32
223
|
end
|
33
|
-
end # class VM
|
34
224
|
|
225
|
+
# Updates the configuration in VirtualBox to reflect this VM's configuration.
|
226
|
+
#
|
227
|
+
# @return [VirtualBox::Vm] self, for easy call chaining
|
228
|
+
def push_config
|
229
|
+
command = ['VBoxManage', 'modifyvm', uid]
|
230
|
+
command.concat board.to_params
|
231
|
+
nics.each_with_index do |nic, index|
|
232
|
+
if nic.nil?
|
233
|
+
command.push "--nic#{index + 1}", 'none'
|
234
|
+
else
|
235
|
+
command.concat nic.to_params(index + 1)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
if VirtualBox.run_command(command).status != 0
|
239
|
+
raise 'Unexpected error code returned by VirtualBox'
|
240
|
+
end
|
241
|
+
|
242
|
+
io_buses.each { |bus| bus.add_to self }
|
243
|
+
|
244
|
+
self
|
245
|
+
end
|
246
|
+
|
247
|
+
# Updates this VM's configuration to reflect the VirtualBox configuration.
|
248
|
+
#
|
249
|
+
# @return [VirtualBox::Vm] self, for easy call chaining
|
250
|
+
def pull_config
|
251
|
+
result = VirtualBox.run_command ['VBoxManage', '--nologo', 'showvminfo',
|
252
|
+
'--machinereadable', uid]
|
253
|
+
if result.status != 0
|
254
|
+
raise 'Unexpected error code returned by VirtualBox'
|
255
|
+
end
|
256
|
+
|
257
|
+
config = self.class.parse_machine_readable result.output
|
258
|
+
|
259
|
+
self.name = config['name']
|
260
|
+
self.uid = config['UUID']
|
261
|
+
board.from_params config
|
262
|
+
|
263
|
+
nic_count = config.keys.select { |key| /^nic\d+$/ =~ key }.max[3..-1].to_i
|
264
|
+
1.upto nic_count do |index|
|
265
|
+
if config["nic#{index}"] == 'none'
|
266
|
+
nics[index - 1] = nil
|
267
|
+
else
|
268
|
+
nics[index - 1] ||= VirtualBox::Nic.new
|
269
|
+
nics[index - 1].from_params config, index
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
bus_count = 1 + (config.keys.select { |key|
|
274
|
+
/^storagecontrollername\d+$/ =~ key
|
275
|
+
}.max || "storagecontrollername-1")[21..-1].to_i
|
276
|
+
0.upto bus_count - 1 do |index|
|
277
|
+
io_buses[index] ||= VirtualBox::IoBus.new
|
278
|
+
io_buses[index].from_params config, index
|
279
|
+
end
|
280
|
+
|
281
|
+
self
|
282
|
+
end
|
283
|
+
|
284
|
+
# Parses the output of the 'VBoxManage showvminfo --machinereadable' command.
|
285
|
+
#
|
286
|
+
# @param [String] output the command output
|
287
|
+
# @return [Hash<String, Object>] a Hash whose keys are the strings on the left
|
288
|
+
# side of "=" on each line, and whose values are the strings on the right
|
289
|
+
# side
|
290
|
+
def self.parse_machine_readable(output)
|
291
|
+
Hash[output.split("\n").map { |line|
|
292
|
+
key, value = *line.split('=', 2)
|
293
|
+
key = key[1...-1] if key[0] == ?" # Remove string quotes ("").
|
294
|
+
value = value[1...-1] if value[0] == ?" # Remove string quotes ("").
|
295
|
+
[key, value]
|
296
|
+
}]
|
297
|
+
end
|
298
|
+
end # class VirtualBox::Vm
|
299
|
+
|
35
300
|
end # namespace VirtualBox
|
data/lib/virtual_box.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
# License:: MIT
|
1
|
+
# TODO(pwnall): documentation for the top-level VirtualBox module
|
2
|
+
module VirtualBox
|
3
|
+
|
4
|
+
end
|
6
5
|
|
7
|
-
require '
|
6
|
+
require 'hashie/mash'
|
7
|
+
require 'uuid'
|
8
|
+
|
9
|
+
require 'virtual_box/board.rb'
|
10
|
+
require 'virtual_box/cli.rb'
|
11
|
+
require 'virtual_box/dhcp.rb'
|
12
|
+
require 'virtual_box/disk.rb'
|
13
|
+
require 'virtual_box/io_bus.rb'
|
14
|
+
require 'virtual_box/net.rb'
|
15
|
+
require 'virtual_box/nic.rb'
|
8
16
|
require 'virtual_box/version.rb'
|
9
|
-
require 'virtual_box/vm/general_settings.rb'
|
10
|
-
require 'virtual_box/vm/identity.rb'
|
11
|
-
require 'virtual_box/vm/lifecycle.rb'
|
12
17
|
require 'virtual_box/vm.rb'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'minitest/unit'
|
11
|
+
require 'minitest/spec'
|
12
|
+
require 'mocha'
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
|
+
require 'virtual_box'
|
17
|
+
|
18
|
+
class MiniTest::Unit::TestCase
|
19
|
+
end
|
20
|
+
|
21
|
+
Dir[File.expand_path('helpers/**/*.rb', File.dirname(__FILE__))].
|
22
|
+
each { |h| require h }
|
23
|
+
|
24
|
+
MiniTest::Unit.autorun
|