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,378 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
# Tasks that build the TinyCore ISO used by the integration test.
|
4
|
+
|
5
|
+
# Produces TinyCore spins.
|
6
|
+
#
|
7
|
+
# The implementation is heavily based on the official instructions at
|
8
|
+
# http://wiki.tinycorelinux.net/wiki:remastering
|
9
|
+
class TinyCore
|
10
|
+
# @return [String] path to the directory holding all the information
|
11
|
+
attr_reader :base
|
12
|
+
# @return [String] path to the vanilla Core ISO
|
13
|
+
attr_reader :iso
|
14
|
+
# @return [String] path to the unpacked version of the vanilla Core ISO
|
15
|
+
attr_reader :cd_fs
|
16
|
+
# @return [String] path to the directory caching extensions
|
17
|
+
attr_reader :pkg
|
18
|
+
# Path to the directory holding the contents of our remix's extension.
|
19
|
+
# @return [String]
|
20
|
+
attr_reader :ext_fs
|
21
|
+
# @return [String] path to the CPIO+GZ file-system in the unpacked Core ISO
|
22
|
+
attr_reader :fs_cpgz
|
23
|
+
# @return [String] path to the unpacked version of the re-mastered ISO
|
24
|
+
attr_reader :cd2_fs
|
25
|
+
# @return [String] path to the CPIO+GZ with our remix's extension.
|
26
|
+
attr_reader :ext_cpgz
|
27
|
+
# @return [String] path to the CPIO+GZ file-system in the remixed Core ISO
|
28
|
+
attr_reader :fs2_cpgz
|
29
|
+
# @return [String] path to the remixed ISO
|
30
|
+
attr_reader :iso2
|
31
|
+
|
32
|
+
def initialize(base_path)
|
33
|
+
@base = base_path
|
34
|
+
recompute_paths
|
35
|
+
end
|
36
|
+
|
37
|
+
# Computes all the working paths based on the base path.
|
38
|
+
def recompute_paths
|
39
|
+
@cd_fs = File.join base, 'cd'
|
40
|
+
@cd2_fs = File.join base, 'cd2'
|
41
|
+
@ext_fs = File.join base, 'ext'
|
42
|
+
@pkg = File.join base, 'pkg'
|
43
|
+
|
44
|
+
@iso = File.join base, 'core.iso'
|
45
|
+
@iso2 = File.join base, 'remix.iso'
|
46
|
+
@fs_cpgz = File.join cd_fs, 'boot/core.gz'
|
47
|
+
@fs2_cpgz = File.join cd2_fs, 'boot/core.gz'
|
48
|
+
@ext_cpgz = File.join pkg, 'remix_ext.gz'
|
49
|
+
end
|
50
|
+
private :recompute_paths
|
51
|
+
|
52
|
+
# Ensures that the TinyCore CD file-system is unpacked somewhere.
|
53
|
+
#
|
54
|
+
# This method might download the core ISO, and unpack it.
|
55
|
+
#
|
56
|
+
# @return [String] the same path returned by #ext_fs
|
57
|
+
def ensure_cd_fs_available
|
58
|
+
return cd_fs if File.exist? cd_fs
|
59
|
+
|
60
|
+
download_iso unless File.exist? iso
|
61
|
+
unpack_iso
|
62
|
+
ext_fs
|
63
|
+
end
|
64
|
+
|
65
|
+
# Downloads the core ISO.
|
66
|
+
#
|
67
|
+
# @raise [RuntimeError] if there's an error downloading the ISO
|
68
|
+
# @return [TinyCore] self, for easy call chaining
|
69
|
+
def download_iso
|
70
|
+
FileUtils.mkdir_p base
|
71
|
+
url = 'http://distro.ibiblio.org/tinycorelinux/4.x/x86/' +
|
72
|
+
'release/Core-current.iso'
|
73
|
+
download_file url, iso
|
74
|
+
self
|
75
|
+
end
|
76
|
+
private :download_iso
|
77
|
+
|
78
|
+
# Unpacks the core ISO.
|
79
|
+
#
|
80
|
+
# @raise [RuntimeError] if there's an error unpacking the ISO
|
81
|
+
# @return [TinyCore] self, for easy call chaining
|
82
|
+
def unpack_iso
|
83
|
+
FileUtils.mkdir_p cd_fs
|
84
|
+
unless shell "7z x -y -o#{cd_fs} #{iso}"
|
85
|
+
FileUtils.rm_rf cd_fs
|
86
|
+
raise "ISO unpacking failed"
|
87
|
+
end
|
88
|
+
self
|
89
|
+
end
|
90
|
+
private :unpack_iso
|
91
|
+
|
92
|
+
# Ensures that a TinyCore extension and its dependencies are downloaded.
|
93
|
+
#
|
94
|
+
# @param [String] package_name the name of the package (e.g., "openssh")
|
95
|
+
# @return [String] the file-system path where the package was downloaded
|
96
|
+
def ensure_package_available(package_name)
|
97
|
+
package_file = File.join pkg, "#{package_name}.tcz"
|
98
|
+
return package_file if File.exist?(package_file)
|
99
|
+
|
100
|
+
# NOTE: The check above relies on the dependencies being installed before
|
101
|
+
# the package itself.
|
102
|
+
package_deps(package_name).each do |dependency|
|
103
|
+
ensure_package_available dependency
|
104
|
+
end
|
105
|
+
|
106
|
+
package_url = 'http://distro.ibiblio.org/tinycorelinux/4.x/x86/tcz/' +
|
107
|
+
"#{package_name}.tcz"
|
108
|
+
FileUtils.mkdir_p pkg
|
109
|
+
download_file package_url, package_file
|
110
|
+
end
|
111
|
+
|
112
|
+
# The TinyCore packages that a package directly depends on.
|
113
|
+
#
|
114
|
+
# This method downloads the package's .dep file, if it doesn't already exist.
|
115
|
+
#
|
116
|
+
# @param [String] package_name the name of the package (e.g., "openssh")
|
117
|
+
# @return [Array<String>] names of the package's direct dependencies
|
118
|
+
def package_deps(package_name)
|
119
|
+
FileUtils.mkdir_p pkg
|
120
|
+
|
121
|
+
info_file = File.join pkg, "#{package_name}.tcz.dep"
|
122
|
+
unless File.exist? info_file
|
123
|
+
info_url = 'http://distro.ibiblio.org/tinycorelinux/4.x/x86/tcz/' +
|
124
|
+
"#{package_name}.tcz.dep"
|
125
|
+
download_file info_url, info_file
|
126
|
+
end
|
127
|
+
|
128
|
+
deps = []
|
129
|
+
File.read(info_file).split.map do |dep|
|
130
|
+
break unless /\.tcz\Z/ =~ dep
|
131
|
+
deps << dep.sub(/\.tcz\Z/, '')
|
132
|
+
end
|
133
|
+
deps
|
134
|
+
end
|
135
|
+
private :package_deps
|
136
|
+
|
137
|
+
# Installs a TinyCore package and its deps to the extension file-system.
|
138
|
+
#
|
139
|
+
# @param [String] package_name the name of the package (e.g., "openssh")
|
140
|
+
# @raise [RuntimeError] if some package download or decompression fails
|
141
|
+
# @return [TinyCore] the same path returned by #ext_fs
|
142
|
+
def install_package(package_name)
|
143
|
+
ensure_package_available package_name
|
144
|
+
package_transitive_deps(package_name).reverse.each do |dependency|
|
145
|
+
unpack_package dependency
|
146
|
+
end
|
147
|
+
ext_fs
|
148
|
+
end
|
149
|
+
|
150
|
+
# All the TinyCore packages that a package indirectly depends on.
|
151
|
+
#
|
152
|
+
# This method downloads the referenced .dep files, if they don't already
|
153
|
+
# exist.
|
154
|
+
#
|
155
|
+
# @param [String] package_name the name of the package (e.g., "openssh")
|
156
|
+
# @param [Hash<String, Boolean>] dependencies internal argument that tracks
|
157
|
+
# the dependency table as it is being built
|
158
|
+
# @return [Array<String>] names of all the packages that the given package
|
159
|
+
# indirectly depends on, including itself
|
160
|
+
def package_transitive_deps(package_name, dependencies = {})
|
161
|
+
dependencies[package_name] = true
|
162
|
+
package_deps(package_name).each do |dependency|
|
163
|
+
next if dependencies.has_key? dependency
|
164
|
+
package_transitive_deps dependency, dependencies
|
165
|
+
end
|
166
|
+
dependencies.keys
|
167
|
+
end
|
168
|
+
private :package_transitive_deps
|
169
|
+
|
170
|
+
# Extracts a TinyCore package to the extension file-system.
|
171
|
+
#
|
172
|
+
# @param [String] package_name the name of the package (e.g., "openssh")
|
173
|
+
# @raise [RuntimeError] if the package decompression fails
|
174
|
+
# @return [TinyCore] self, for easy call chaining
|
175
|
+
def unpack_package(package_name)
|
176
|
+
package_tcz = File.expand_path ensure_package_available(package_name)
|
177
|
+
FileUtils.mkdir_p ext_fs
|
178
|
+
out_dir = File.expand_path ext_fs
|
179
|
+
unless shell "unsquashfs -d #{out_dir} -f -n #{package_tcz}"
|
180
|
+
raise "TCZ unpacking failed"
|
181
|
+
end
|
182
|
+
self
|
183
|
+
end
|
184
|
+
private :unpack_package
|
185
|
+
|
186
|
+
# Runs a block of code in the root of the extension file-system.
|
187
|
+
# @return [Object] the return value of the given block
|
188
|
+
def change_ext_fs(&block)
|
189
|
+
FileUtils.mkdir_p ext_fs
|
190
|
+
Dir.chdir ext_fs do
|
191
|
+
yield self
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Adds an init.d service to the list of services to be started at boot.
|
196
|
+
# @return [TinyCore] self, for easy call chaining
|
197
|
+
def autoboot_service(service_name)
|
198
|
+
bootlocal = ensure_bootlocal_available
|
199
|
+
|
200
|
+
path = File.join '/usr/local/etc/init.d', service_name
|
201
|
+
unless File.exist?(ext_fs + path)
|
202
|
+
path = File.join '/etc/init.d', service_name
|
203
|
+
end
|
204
|
+
|
205
|
+
commands = File.read(bootlocal).split
|
206
|
+
return self if commands.any? { |command| /^#{path}/ =~ command }
|
207
|
+
File.open(bootlocal, 'ab') { |f| f.write "#{path} start\n" }
|
208
|
+
self
|
209
|
+
end
|
210
|
+
|
211
|
+
# Ensures that the bootlocal.sh file holding startup commands exists.
|
212
|
+
#
|
213
|
+
# If necessary, this method will create the file and its enclosing
|
214
|
+
# directories, and set correct permissions on everything.
|
215
|
+
#
|
216
|
+
# @return [String] the path to the bootlocal.sh file in the unpacked extension
|
217
|
+
# filesystem
|
218
|
+
def ensure_bootlocal_available
|
219
|
+
opt = File.join ext_fs, 'opt'
|
220
|
+
FileUtils.mkdir_p opt
|
221
|
+
File.chmod 02775, opt
|
222
|
+
|
223
|
+
bootlocal = File.join opt, 'bootlocal.sh'
|
224
|
+
unless File.exist? bootlocal
|
225
|
+
File.open(bootlocal, 'wb') { |f| f.write "#!/bin/sh\n" }
|
226
|
+
end
|
227
|
+
File.chmod 0775, bootlocal
|
228
|
+
bootlocal
|
229
|
+
end
|
230
|
+
private :ensure_bootlocal_available
|
231
|
+
|
232
|
+
|
233
|
+
def remaster
|
234
|
+
ensure_cd2_fs_available
|
235
|
+
build_initrd
|
236
|
+
config_bootldr
|
237
|
+
build_iso
|
238
|
+
end
|
239
|
+
|
240
|
+
# Ensures that the re-mastered CD file-system exists.
|
241
|
+
#
|
242
|
+
# If necessary, this method can download the Core ISO and unpack it, then
|
243
|
+
# copy the unpacked version over.
|
244
|
+
def ensure_cd2_fs_available
|
245
|
+
return cd2_fs if File.exist?(cd2_fs)
|
246
|
+
|
247
|
+
ensure_cd_fs_available
|
248
|
+
FileUtils.cp_r cd_fs, cd2_fs
|
249
|
+
end
|
250
|
+
private :ensure_cd2_fs_available
|
251
|
+
|
252
|
+
# Builds the file used as the initial RAM file-system by the remixed ISO.
|
253
|
+
#
|
254
|
+
# @return [String] the path to the initrd file.
|
255
|
+
def build_initrd
|
256
|
+
ensure_cd2_fs_available
|
257
|
+
build_extension
|
258
|
+
File.open fs2_cpgz, 'wb' do |f|
|
259
|
+
f.write File.read_binary(fs_cpgz)
|
260
|
+
f.write File.read_binary(ext_cpgz)
|
261
|
+
end
|
262
|
+
fs2_cpgz
|
263
|
+
end
|
264
|
+
private :build_initrd
|
265
|
+
|
266
|
+
# Builds a GZ+CPIO initramfs extension out of the extension file-system.
|
267
|
+
#
|
268
|
+
# @return [String] the path to the GZ+CPIO file
|
269
|
+
def build_extension
|
270
|
+
FileUtils.mkdir_p pkg
|
271
|
+
out_file = File.expand_path ext_cpgz
|
272
|
+
Dir.chdir ext_fs do
|
273
|
+
command = 'find . | cpio -o --format newc --owner=0:0 | ' +
|
274
|
+
"gzip -2 > #{out_file}"
|
275
|
+
unless shell command
|
276
|
+
raise "CP+GZ packing error"
|
277
|
+
end
|
278
|
+
unless shell "advdef --recompress --shrink-insane --quiet #{out_file}"
|
279
|
+
raise "CP+GZ re-compression error"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
ext_cpgz
|
283
|
+
end
|
284
|
+
private :build_extension
|
285
|
+
|
286
|
+
# Patches the boot-loader configuration on the remixed ISO.
|
287
|
+
#
|
288
|
+
# The patched configuration has the timeout removed, for instant VM booting.
|
289
|
+
#
|
290
|
+
# @return [String] the path to the patched configuration file.
|
291
|
+
def config_bootldr
|
292
|
+
ensure_cd2_fs_available
|
293
|
+
config_file = File.join cd2_fs, 'boot/isolinux/isolinux.cfg'
|
294
|
+
data = File.read config_file
|
295
|
+
data.gsub!(/^prompt .*$/, 'prompt 0')
|
296
|
+
data.gsub!(/^timeout .*$/, 'timeout 0')
|
297
|
+
File.open(config_file, 'w') { |f| f.write data }
|
298
|
+
config_file
|
299
|
+
end
|
300
|
+
|
301
|
+
# Creates an ISO image out of the unpacked remixed CD file-system.
|
302
|
+
#
|
303
|
+
# @return [String] the path to the newly created ISO file
|
304
|
+
def build_iso
|
305
|
+
ensure_cd2_fs_available
|
306
|
+
|
307
|
+
label = 'remixed-tc'
|
308
|
+
boot_loader = 'boot/isolinux/isolinux.bin'
|
309
|
+
boot_catalog = 'boot/isolinux/boot.cat'
|
310
|
+
|
311
|
+
command = "mkisofs -l -J -R -r -V #{label} -no-emul-boot " +
|
312
|
+
"-boot-load-size 4 -quiet " +
|
313
|
+
"-boot-info-table -b #{boot_loader} -c #{boot_catalog} " +
|
314
|
+
"-o #{iso2} #{cd2_fs}"
|
315
|
+
unless shell command
|
316
|
+
raise "ISO creation failed"
|
317
|
+
end
|
318
|
+
iso2
|
319
|
+
end
|
320
|
+
|
321
|
+
# Helper method for downloading a file over HTTP.
|
322
|
+
#
|
323
|
+
# @param [String] url the URL to download the file from
|
324
|
+
# @param [String] file the file-system path where the file will be saved
|
325
|
+
# @raise [RuntimeError] if there's an error downloading the file
|
326
|
+
# @return [String] the value of the "file argument"
|
327
|
+
def download_file(url, file)
|
328
|
+
return file if shell("curl -o #{file} #{url}")
|
329
|
+
|
330
|
+
File.unlink file if File.exist?(file)
|
331
|
+
raise "Download failed"
|
332
|
+
end
|
333
|
+
private :download_file
|
334
|
+
|
335
|
+
# Runs a shell command.
|
336
|
+
# @param [String] command the command to be executed
|
337
|
+
# @return [Boolean] true if the command's exit code was 0, false otherwise
|
338
|
+
def shell(command)
|
339
|
+
Kernel.system command
|
340
|
+
end
|
341
|
+
private :shell
|
342
|
+
|
343
|
+
# Runs a shell command with super-user privileges.
|
344
|
+
# @param [String] command the command to be executed
|
345
|
+
# @return [Boolean] true if the command's exit code was 0, false otherwise
|
346
|
+
def su_shell(command)
|
347
|
+
# TODO(pwnall): use shellwords responsibly
|
348
|
+
Kernel.system "sudo -i \"#{command}\""
|
349
|
+
end
|
350
|
+
private :su_shell
|
351
|
+
end
|
352
|
+
|
353
|
+
# All TinyCore files will be created here. This should be gitignored.
|
354
|
+
tinycore = TinyCore.new 'test/fixtures/tinycore'
|
355
|
+
|
356
|
+
file tinycore.iso2 do
|
357
|
+
tinycore.ensure_cd_fs_available
|
358
|
+
tinycore.install_package 'coreutils'
|
359
|
+
tinycore.install_package 'openssh'
|
360
|
+
tinycore.autoboot_service 'openssh'
|
361
|
+
|
362
|
+
tinycore.change_ext_fs do
|
363
|
+
FileUtils.cp 'usr/local/etc/ssh/ssh_config.example',
|
364
|
+
'usr/local/etc/ssh/ssh_config'
|
365
|
+
FileUtils.cp 'usr/local/etc/ssh/sshd_config.example',
|
366
|
+
'usr/local/etc/ssh/sshd_config'
|
367
|
+
data = File.read 'usr/local/etc/ssh/sshd_config'
|
368
|
+
data.gsub!(/^#PasswordAuthentication .*$/, 'PasswordAuthentication yes')
|
369
|
+
data.gsub!(/^#PermitEmptyPasswords .*$/, 'PermitEmptyPasswords yes')
|
370
|
+
File.open 'usr/local/etc/ssh/sshd_config', 'w' do |f|
|
371
|
+
f.write data
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
tinycore.remaster
|
376
|
+
end
|
377
|
+
|
378
|
+
task :fixtures => tinycore.iso2
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe VirtualBox::Board do
|
4
|
+
describe 'os_types' do
|
5
|
+
before do
|
6
|
+
@types = VirtualBox::Board.os_types
|
7
|
+
end
|
8
|
+
it 'should map linux 2.6' do
|
9
|
+
@types.must_include :linux26
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should include linux 2.6 ID' do
|
13
|
+
@types.must_include 'linux26'
|
14
|
+
@types.must_include 'Linux26'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should include linux 2.6 description' do
|
18
|
+
@types.must_include 'Linux 2.6'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'non-standard board specification' do
|
23
|
+
before do
|
24
|
+
@vm = VirtualBox::Vm.new :board => { :os => :fedora, :ram => 768,
|
25
|
+
:cpus => 2, :video_ram => 22, :boot_order => [:dvd, :net, :disk] }
|
26
|
+
@vm.register
|
27
|
+
end
|
28
|
+
after do
|
29
|
+
@vm.unregister
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should push/pull specification correctly' do
|
33
|
+
vm = VirtualBox::Vm.new :uid => @vm.uid
|
34
|
+
|
35
|
+
vm.pull_config
|
36
|
+
vm.board.to_hash.must_equal @vm.board.to_hash
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe 'CLI' do
|
4
|
+
describe 'run_command' do
|
5
|
+
describe 'with hello world echo' do
|
6
|
+
before do
|
7
|
+
@result = VirtualBox.run_command(['echo', 'Hello'])
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should report successful completion' do
|
11
|
+
@result.status.must_equal 0
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return echo output' do
|
15
|
+
@result.output.must_equal "Hello\n"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'with inline ruby script' do
|
20
|
+
before do
|
21
|
+
@result = VirtualBox.run_command(['ruby', '-e', 'print "Hi"; exit 1'])
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should return echo output' do
|
25
|
+
@result.output.must_equal 'Hi'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should report exit code 1' do
|
29
|
+
@result.status.must_equal 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe VirtualBox::Dhcp do
|
4
|
+
describe 'ip 192.168.0.1' do
|
5
|
+
before do
|
6
|
+
@dhcp = VirtualBox::Dhcp.new :ip => '192.168.0.1'
|
7
|
+
end
|
8
|
+
it 'has a default netmask' do
|
9
|
+
@dhcp.netmask.must_equal '255.255.255.0'
|
10
|
+
end
|
11
|
+
it 'computes the IP block start' do
|
12
|
+
@dhcp.start_ip.must_equal '192.168.0.2'
|
13
|
+
end
|
14
|
+
it 'computes the IP block end' do
|
15
|
+
@dhcp.end_ip.must_equal '192.168.0.254'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'startip 192.168.0.64' do
|
20
|
+
before do
|
21
|
+
@dhcp = VirtualBox::Dhcp.new :start_ip => '192.168.0.64'
|
22
|
+
end
|
23
|
+
it 'computes the server IP' do
|
24
|
+
@dhcp.ip.must_equal '192.168.0.1'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'random rule' do
|
29
|
+
before do
|
30
|
+
@dhcp = VirtualBox::Dhcp.new :net_name => 'rbxvbox0', :ip => '10.1.0.1'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'is not live' do
|
34
|
+
@dhcp.wont_be :live?
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'after being added' do
|
38
|
+
before { @dhcp.add }
|
39
|
+
after { @dhcp.remove }
|
40
|
+
|
41
|
+
it 'is live' do
|
42
|
+
@dhcp.must_be :live?
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'shows up on the list of live servers' do
|
46
|
+
servers = VirtualBox::Dhcp.all
|
47
|
+
dhcp = servers.find { |s| s.net_name == @dhcp.net_name }
|
48
|
+
dhcp.to_hash.must_equal @dhcp.to_hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe VirtualBox::Disk do
|
4
|
+
describe 'new with ISO' do
|
5
|
+
before do
|
6
|
+
@disk = VirtualBox::Disk.new :file => 'disk.iso'
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should guess media type' do
|
10
|
+
@disk.media.must_equal :dvd
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should guess image type' do
|
14
|
+
@disk.format.must_equal :raw
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'create' do
|
19
|
+
describe '16-megabyte VMDK flexible' do
|
20
|
+
let(:vmdk_path) { '/tmp/disk.vmdk' }
|
21
|
+
|
22
|
+
before do
|
23
|
+
File.unlink vmdk_path if File.exist?(vmdk_path)
|
24
|
+
@disk = VirtualBox::Disk.create :file => vmdk_path, :prealloc => false,
|
25
|
+
:size => 16 * 1024 * 1024
|
26
|
+
end
|
27
|
+
|
28
|
+
after do
|
29
|
+
File.unlink vmdk_path if File.exist?(vmdk_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should create a small file' do
|
33
|
+
File.stat(vmdk_path).size.must_be :<, 256 * 1024
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should return a Disk pointing to the file' do
|
37
|
+
@disk.file.must_equal vmdk_path
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should return a HDD Disk' do
|
41
|
+
@disk.media.must_equal :disk
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should return a VMDK Disk' do
|
45
|
+
@disk.format.must_equal :vmdk
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '16-megabyte preallocated' do
|
50
|
+
let(:vdi_path) { '/tmp/disk.vdi' }
|
51
|
+
|
52
|
+
before do
|
53
|
+
File.unlink vdi_path if File.exist?(vdi_path)
|
54
|
+
@disk = VirtualBox::Disk.create :file => vdi_path, :prealloc => true,
|
55
|
+
:size => 16 * 1024 * 1024
|
56
|
+
end
|
57
|
+
|
58
|
+
after do
|
59
|
+
File.unlink vdi_path if File.exist?(vdi_path)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should create a 16-megabyte file' do
|
63
|
+
(File.stat(vdi_path).size / (1024 * 1024)).must_equal 16
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should return a Disk pointing to the file' do
|
67
|
+
@disk.file.must_equal vdi_path
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return a HDD Disk' do
|
71
|
+
@disk.media.must_equal :disk
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should return a VDI Disk' do
|
75
|
+
@disk.format.must_equal :vdi
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'VM with a bunch of disks' do
|
81
|
+
let(:disk1_file) { '/tmp/disk1.vdi' }
|
82
|
+
let(:disk2_file) { '/tmp/disk2.vdi' }
|
83
|
+
let(:disk3_file) { '/tmp/disk3.vdi' }
|
84
|
+
let(:iso_file) { 'test/fixtures/tinycore/remix.iso' }
|
85
|
+
before do
|
86
|
+
[disk1_file, disk2_file, disk3_file].each do |file|
|
87
|
+
File.unlink file if File.exist?(file)
|
88
|
+
VirtualBox::Disk.create :file => file, :size => 16 * 1024 * 1024
|
89
|
+
end
|
90
|
+
|
91
|
+
@vm = VirtualBox::Vm.new :io_buses => [
|
92
|
+
{ :bus => :sata, :disks => [
|
93
|
+
{ :file => disk1_file, :port => 0, :device => 0 },
|
94
|
+
{ :file => disk2_file }
|
95
|
+
]},
|
96
|
+
{ :bus => :ide, :disks => [{ :file => iso_file }]},
|
97
|
+
{ :bus => :scsi, :disks => [{ :file => disk3_file }] }
|
98
|
+
]
|
99
|
+
@vm.register
|
100
|
+
end
|
101
|
+
|
102
|
+
after do
|
103
|
+
@vm.unregister
|
104
|
+
[disk1_file, disk2_file, disk3_file].each do |file|
|
105
|
+
File.unlink file if File.exist?(file)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should push/pull specs correctly' do
|
110
|
+
vm = VirtualBox::Vm.new :uid => @vm.uid
|
111
|
+
vm.pull_config
|
112
|
+
vm.io_buses.map { |io_bus| io_bus.to_hash }.
|
113
|
+
must_equal @vm.io_buses.map { |io_bus| io_bus.to_hash }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'net/ssh'
|
4
|
+
|
5
|
+
describe 'VirtualBox' do
|
6
|
+
before do
|
7
|
+
iso_file = 'test/fixtures/tinycore/remix.iso'
|
8
|
+
@net = VirtualBox::Net.new(:ip => '192.168.66.6',
|
9
|
+
:netmask => '255.255.255.0').add
|
10
|
+
@dhcp = VirtualBox::Dhcp.new(:net_name => @net.name,
|
11
|
+
:start_ip => '192.168.66.66').add
|
12
|
+
@vm = VirtualBox::Vm.new(
|
13
|
+
:board => { :ram => 256, :cpus => 1, :video_ram => 16,
|
14
|
+
:os => :linux26 },
|
15
|
+
:io_buses => [{ :bus => :ide,
|
16
|
+
:disks => [{ :file => iso_file, :port => 1 }] }],
|
17
|
+
:nics => [{ :mode => :host, :chip => :virtual,
|
18
|
+
:net_name => @net.if_name }]).register
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
@vm.unregister unless @vm.nil?
|
23
|
+
@dhcp.remove unless @dhcp.nil?
|
24
|
+
@net.remove unless @net.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'after VM start' do
|
28
|
+
before do
|
29
|
+
@vm.start
|
30
|
+
# Give the VM a chance to boot and generate SSH keys.
|
31
|
+
Kernel.sleep 3
|
32
|
+
end
|
33
|
+
|
34
|
+
after do
|
35
|
+
unless @vm.nil?
|
36
|
+
@vm.stop
|
37
|
+
# Let VirtualBox stop the VM, so that it can be unregistered.
|
38
|
+
Kernel.sleep 0.5
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should respond to a SSH connection' do
|
43
|
+
output = nil
|
44
|
+
Net::SSH.start '192.168.66.66', 'tc', :timeout => 10,
|
45
|
+
:global_known_hosts_file => [], :user_known_hosts_file => [],
|
46
|
+
:paranoid => false, :password => '' do |ssh|
|
47
|
+
output = ssh.exec!('ifconfig')
|
48
|
+
end
|
49
|
+
|
50
|
+
output.wont_be_nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|