virtual_box 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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