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.
Files changed (47) hide show
  1. data/.autotest +9 -0
  2. data/.document +5 -0
  3. data/.project +12 -0
  4. data/.rspec +1 -0
  5. data/Gemfile +14 -0
  6. data/Gemfile.lock +44 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.markdown +67 -0
  9. data/Rakefile +35 -22
  10. data/VERSION +1 -0
  11. data/lib/virtual_box/board.rb +287 -0
  12. data/lib/virtual_box/cli.rb +20 -0
  13. data/lib/virtual_box/dhcp.rb +149 -0
  14. data/lib/virtual_box/disk.rb +138 -0
  15. data/lib/virtual_box/io_bus.rb +261 -0
  16. data/lib/virtual_box/net.rb +144 -0
  17. data/lib/virtual_box/nic.rb +213 -0
  18. data/lib/virtual_box/version.rb +37 -24
  19. data/lib/virtual_box/vm.rb +289 -24
  20. data/lib/virtual_box.rb +14 -9
  21. data/test/helper.rb +24 -0
  22. data/test/tasks/tinycore.rake +378 -0
  23. data/test/virtual_box/board_test.rb +39 -0
  24. data/test/virtual_box/cli_test.rb +33 -0
  25. data/test/virtual_box/dhcp_test.rb +52 -0
  26. data/test/virtual_box/disk_test.rb +116 -0
  27. data/test/virtual_box/integration_test.rb +53 -0
  28. data/test/virtual_box/io_bus_test.rb +54 -0
  29. data/test/virtual_box/net_test.rb +80 -0
  30. data/test/virtual_box/nic_test.rb +50 -0
  31. data/test/virtual_box/version_test.rb +71 -0
  32. data/test/virtual_box/vm_test.rb +55 -0
  33. data/virtual_box.gemspec +81 -22
  34. metadata +208 -89
  35. data/CHANGELOG +0 -1
  36. data/LICENSE +0 -21
  37. data/Manifest +0 -17
  38. data/README.textile +0 -19
  39. data/lib/virtual_box/command_line.rb +0 -27
  40. data/lib/virtual_box/vm/general_settings.rb +0 -136
  41. data/lib/virtual_box/vm/identity.rb +0 -43
  42. data/lib/virtual_box/vm/lifecycle.rb +0 -42
  43. data/test/command_line_test.rb +0 -19
  44. data/test/general_settings_test.rb +0 -20
  45. data/test/lifecycle_test.rb +0 -64
  46. data/test/version_test.rb +0 -56
  47. 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