vbox 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +14 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +68 -0
- data/README.md +5 -0
- data/Rakefile +91 -0
- data/bin/vbox +73 -0
- data/lib/java/README.txt +5 -0
- data/lib/java/dir2floppy.jar +0 -0
- data/lib/java/dir2floppy.java +134 -0
- data/lib/vagrant_init.rb +6 -0
- data/lib/vbox.rb +2 -0
- data/lib/vbox/command.rb +82 -0
- data/lib/vbox/config.rb +5 -0
- data/lib/vbox/export.rb +65 -0
- data/lib/vbox/scancode.rb +208 -0
- data/lib/vbox/session.rb +893 -0
- data/lib/vbox/shell.rb +54 -0
- data/lib/vbox/ssh.rb +193 -0
- data/lib/vbox/transaction.rb +122 -0
- data/lib/vbox/utils.rb +26 -0
- data/lib/vbox/version.rb +3 -0
- data/lib/vbox/web.rb +48 -0
- data/templates/CentOS-5.7-i386-netboot/definition.rb +16 -0
- data/templates/CentOS-5.7-i386-netboot/ks.cfg +45 -0
- data/templates/CentOS-5.7-i386-netboot/postinstall.sh +53 -0
- data/templates/CentOS-5.7-x86_64-netboot/definition.rb +16 -0
- data/templates/CentOS-5.7-x86_64-netboot/ks.cfg +45 -0
- data/templates/CentOS-5.7-x86_64-netboot/postinstall.sh +60 -0
- data/templates/CentOS-6.0-i386-netboot/definition.rb +16 -0
- data/templates/CentOS-6.0-i386-netboot/ks.cfg +52 -0
- data/templates/CentOS-6.0-i386-netboot/postinstall.sh +30 -0
- data/templates/CentOS-6.0-i386/definition.rb +17 -0
- data/templates/CentOS-6.0-i386/ks.cfg +47 -0
- data/templates/CentOS-6.0-i386/postinstall.sh +48 -0
- data/templates/CentOS-6.0-x86_64-netboot/definition.rb +16 -0
- data/templates/CentOS-6.0-x86_64-netboot/ks.cfg +52 -0
- data/templates/CentOS-6.0-x86_64-netboot/postinstall.sh +30 -0
- data/templates/CentOS-6.0-x86_64/definition.rb +17 -0
- data/templates/CentOS-6.0-x86_64/ks.cfg +47 -0
- data/templates/CentOS-6.0-x86_64/postinstall.sh +48 -0
- data/templates/Debian-6.0.3-amd64-netboot/definition.rb +42 -0
- data/templates/Debian-6.0.3-amd64-netboot/postinstall.sh +80 -0
- data/templates/Debian-6.0.3-amd64-netboot/preseed.cfg +42 -0
- data/templates/Debian-6.0.3-i386-netboot/definition.rb +44 -0
- data/templates/Debian-6.0.3-i386-netboot/postinstall.sh +80 -0
- data/templates/Debian-6.0.3-i386-netboot/preseed.cfg +42 -0
- data/templates/Fedora-15-i386-netboot/definition.rb +17 -0
- data/templates/Fedora-15-i386-netboot/ks.cfg +82 -0
- data/templates/Fedora-15-i386-netboot/postinstall.sh +18 -0
- data/templates/Fedora-15-i386/definition.rb +17 -0
- data/templates/Fedora-15-i386/ks.cfg +64 -0
- data/templates/Fedora-15-i386/postinstall.sh +33 -0
- data/templates/Fedora-15-x86_64-netboot/definition.rb +29 -0
- data/templates/Fedora-15-x86_64-netboot/ks.cfg +64 -0
- data/templates/Fedora-15-x86_64-netboot/postinstall.sh +33 -0
- data/templates/Fedora-15-x86_64/definition.rb +17 -0
- data/templates/Fedora-15-x86_64/ks.cfg +64 -0
- data/templates/Fedora-15-x86_64/postinstall.sh +33 -0
- data/templates/Sysrescuecd-2.0.0-experimental/autorun0 +3 -0
- data/templates/Sysrescuecd-2.0.0-experimental/definition.rb +20 -0
- data/templates/archlinux-i386-netboot/aif.cfg +34 -0
- data/templates/archlinux-i386-netboot/definition.rb +29 -0
- data/templates/archlinux-i386-netboot/postinstall.sh +87 -0
- data/templates/archlinux-i386-netboot/postinstall2.sh +28 -0
- data/templates/archlinux-i386/aif.cfg +33 -0
- data/templates/archlinux-i386/definition.rb +29 -0
- data/templates/archlinux-i386/postinstall.sh +106 -0
- data/templates/archlinux-x86_64-netboot/aif.cfg +34 -0
- data/templates/archlinux-x86_64-netboot/definition.rb +29 -0
- data/templates/archlinux-x86_64-netboot/postinstall.sh +90 -0
- data/templates/archlinux-x86_64-netboot/postinstall2.sh +28 -0
- data/templates/archlinux-x86_64/aif.cfg +33 -0
- data/templates/archlinux-x86_64/definition.rb +29 -0
- data/templates/archlinux-x86_64/postinstall.sh +90 -0
- data/templates/archlinux-x86_64/postinstall2.sh +38 -0
- data/templates/freebsd-8.2-experimental/definition.rb +19 -0
- data/templates/freebsd-8.2-experimental/postinstall.sh +191 -0
- data/templates/freebsd-8.2-pcbsd-i386-netboot/definition.rb +35 -0
- data/templates/freebsd-8.2-pcbsd-i386-netboot/pcinstall.fbg.cfg +58 -0
- data/templates/freebsd-8.2-pcbsd-i386-netboot/postinstall.sh +93 -0
- data/templates/freebsd-8.2-pcbsd-i386/definition.rb +31 -0
- data/templates/freebsd-8.2-pcbsd-i386/pcinstall.fbg.cfg +57 -0
- data/templates/freebsd-8.2-pcbsd-i386/postinstall.sh +93 -0
- data/templates/gentoo-latest-i386-experimental/definition.rb +29 -0
- data/templates/gentoo-latest-i386-experimental/postinstall.sh +184 -0
- data/templates/openSUSE-11.4-DVD-i586/autoinst_de.xml +1284 -0
- data/templates/openSUSE-11.4-DVD-i586/autoinst_en.xml +1284 -0
- data/templates/openSUSE-11.4-DVD-i586/definition.rb +28 -0
- data/templates/openSUSE-11.4-DVD-i586/postinstall.sh +43 -0
- data/templates/openSUSE-11.4-DVD-x86_64/autoinst_de.xml +1459 -0
- data/templates/openSUSE-11.4-DVD-x86_64/autoinst_en.xml +1459 -0
- data/templates/openSUSE-11.4-DVD-x86_64/definition.rb +28 -0
- data/templates/openSUSE-11.4-DVD-x86_64/postinstall.sh +43 -0
- data/templates/openSUSE-11.4-NET-i586/autoinst_de.xml +1278 -0
- data/templates/openSUSE-11.4-NET-i586/autoinst_en.xml +1278 -0
- data/templates/openSUSE-11.4-NET-i586/definition.rb +28 -0
- data/templates/openSUSE-11.4-NET-i586/postinstall.sh +43 -0
- data/templates/openSUSE-11.4-NET-x86_64/autoinst_de.xml +1453 -0
- data/templates/openSUSE-11.4-NET-x86_64/autoinst_en.xml +1453 -0
- data/templates/openSUSE-11.4-NET-x86_64/definition.rb +28 -0
- data/templates/openSUSE-11.4-NET-x86_64/postinstall.sh +43 -0
- data/templates/solaris-11-express-i386/auto_install/ai.dtd +58 -0
- data/templates/solaris-11-express-i386/auto_install/ai_manifest.xml +241 -0
- data/templates/solaris-11-express-i386/auto_install/configuration.dtd +44 -0
- data/templates/solaris-11-express-i386/auto_install/default.xml +124 -0
- data/templates/solaris-11-express-i386/auto_install/default.xml.orig +124 -0
- data/templates/solaris-11-express-i386/auto_install/sc_profiles/static_network.xml +105 -0
- data/templates/solaris-11-express-i386/auto_install/software.dtd +105 -0
- data/templates/solaris-11-express-i386/auto_install/target.dtd +196 -0
- data/templates/solaris-11-express-i386/default.xml +121 -0
- data/templates/solaris-11-express-i386/definition.rb +65 -0
- data/templates/solaris-11-express-i386/postinstall.sh +98 -0
- data/templates/ubuntu-10.04.3-server-amd64/definition.rb +54 -0
- data/templates/ubuntu-10.04.3-server-amd64/postinstall.sh +90 -0
- data/templates/ubuntu-10.04.3-server-amd64/preseed.cfg +87 -0
- data/templates/ubuntu-10.04.3-server-i386/definition.rb +24 -0
- data/templates/ubuntu-10.04.3-server-i386/postinstall.sh +91 -0
- data/templates/ubuntu-10.04.3-server-i386/preseed.cfg +87 -0
- data/templates/ubuntu-11.10-server-amd64/definition.rb +35 -0
- data/templates/ubuntu-11.10-server-amd64/postinstall.sh +90 -0
- data/templates/ubuntu-11.10-server-amd64/preseed.cfg +87 -0
- data/templates/ubuntu-11.10-server-i386/definition.rb +35 -0
- data/templates/ubuntu-11.10-server-i386/postinstall.sh +90 -0
- data/templates/ubuntu-11.10-server-i386/preseed.cfg +87 -0
- data/validation/features/steps/ssh_steps.rb +169 -0
- data/validation/support/env.rb +1 -0
- data/validation/vagrant-private.key +27 -0
- data/validation/vagrant.feature +52 -0
- data/validation/vagrant.pub +1 -0
- data/vbox.gemspec +32 -0
- metadata +338 -0
data/lib/vbox/config.rb
ADDED
data/lib/vbox/export.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
module Vbox
|
|
3
|
+
class Export
|
|
4
|
+
|
|
5
|
+
def self.vagrant(boxname,boxdir,definition)
|
|
6
|
+
|
|
7
|
+
#Check if box already exists
|
|
8
|
+
vm=VirtualBox::VM.find(boxname)
|
|
9
|
+
if vm.nil?
|
|
10
|
+
puts "#{boxname} is not found, maybe you need to build first?"
|
|
11
|
+
exit
|
|
12
|
+
end
|
|
13
|
+
#We need to shutdown first
|
|
14
|
+
if vm.running?
|
|
15
|
+
puts "Vagrant requires the box to be shutdown, before it can export"
|
|
16
|
+
puts "Sudo also needs to work for user #{definition[:ssh_user]}"
|
|
17
|
+
puts "Performing a clean shutdown now."
|
|
18
|
+
ssh_options={ :user => definition[:ssh_user], :port => definition[:ssh_host_port], :password => definition[:ssh_password],
|
|
19
|
+
:timeout => definition[:ssh_timeout]}
|
|
20
|
+
|
|
21
|
+
Vbox::Ssh.execute("localhost","sudo #{definition[:shutdown_cmd]}",ssh_options)
|
|
22
|
+
|
|
23
|
+
#Wait for state poweroff
|
|
24
|
+
while (vm.running?) do
|
|
25
|
+
print '.'
|
|
26
|
+
sleep 1
|
|
27
|
+
end
|
|
28
|
+
puts
|
|
29
|
+
puts "Machine #{boxname} is powered off cleanly"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#Vagrant requires a relative path for output of boxes
|
|
33
|
+
|
|
34
|
+
#4.0.x. not using boxes as a subdir
|
|
35
|
+
boxdir=Pathname.new(Dir.pwd)
|
|
36
|
+
|
|
37
|
+
full_path=File.join(boxdir,boxname+".box")
|
|
38
|
+
path1=Pathname.new(full_path)
|
|
39
|
+
path2=Pathname.new(Dir.pwd)
|
|
40
|
+
box_path=path1.relative_path_from(path2).to_s
|
|
41
|
+
|
|
42
|
+
if File.exists?("#{box_path}")
|
|
43
|
+
puts "box #{boxname}.box already exists"
|
|
44
|
+
exit
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
puts "Excuting vagrant voodoo:"
|
|
48
|
+
export_command="vagrant package --base '#{boxname}' --output '#{box_path}'"
|
|
49
|
+
puts "#{export_command}"
|
|
50
|
+
Vbox::Shell.execute("#{export_command}") #hmm, needs to get the gem_home set?
|
|
51
|
+
puts
|
|
52
|
+
|
|
53
|
+
#add_ssh_nat_mapping back!!!!
|
|
54
|
+
|
|
55
|
+
puts "To import it into vagrant type:"
|
|
56
|
+
puts "vagrant box add '#{boxname}' '#{box_path}'"
|
|
57
|
+
puts ""
|
|
58
|
+
puts "To use it:"
|
|
59
|
+
puts "vagrant init '#{boxname}'"
|
|
60
|
+
puts "vagrant up"
|
|
61
|
+
puts "vagrant ssh"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
module Vbox
|
|
2
|
+
class Scancode
|
|
3
|
+
|
|
4
|
+
def self.send_sequence(vboxcmd,vname,sequence,webport)
|
|
5
|
+
puts
|
|
6
|
+
counter=0
|
|
7
|
+
sequence.each { |s|
|
|
8
|
+
counter=counter+1
|
|
9
|
+
|
|
10
|
+
s.gsub!(/%IP%/,Vbox::Session.local_ip);
|
|
11
|
+
s.gsub!(/%PORT%/,webport);
|
|
12
|
+
s.gsub!(/%NAME%/, vname);
|
|
13
|
+
puts "Typing:[#{counter}]: "+s
|
|
14
|
+
|
|
15
|
+
keycodes=string_to_keycode(s)
|
|
16
|
+
|
|
17
|
+
# VBox seems to have issues with sending the scancodes as one big
|
|
18
|
+
# .join()-ed string. It seems to get them out or order or ignore some.
|
|
19
|
+
# A workaround is to send the scancodes one-by-one.
|
|
20
|
+
codes=""
|
|
21
|
+
for keycode in keycodes.split(' ') do
|
|
22
|
+
|
|
23
|
+
unless keycode=="wait"
|
|
24
|
+
send_keycode(vboxcmd,vname,keycode)
|
|
25
|
+
sleep 0.01
|
|
26
|
+
else
|
|
27
|
+
sleep 1
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
#sleep after each sequence (needs to be param)
|
|
31
|
+
sleep 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
puts "Done typing."
|
|
35
|
+
puts
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.send_keycode(vboxcmd,vname,keycode)
|
|
40
|
+
command= "#{vboxcmd} controlvm '#{vname}' keyboardputscancode #{keycode}"
|
|
41
|
+
#puts "#{command}"
|
|
42
|
+
IO.popen("#{command}") { |f| print '' }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.string_to_keycode(thestring)
|
|
46
|
+
|
|
47
|
+
#http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
|
|
48
|
+
|
|
49
|
+
k=Hash.new
|
|
50
|
+
#numericals
|
|
51
|
+
k['1'] = '02 82';
|
|
52
|
+
k['2'] = '03 83';
|
|
53
|
+
k['3'] = '04 84';
|
|
54
|
+
k['4'] = '05 85';
|
|
55
|
+
k['5'] = '06 86';
|
|
56
|
+
k['6'] = '07 87';
|
|
57
|
+
k['7'] = '08 88';
|
|
58
|
+
k['8'] = '09 89';
|
|
59
|
+
k['9'] = '0a 8a';
|
|
60
|
+
k['0'] = '0b 8b';
|
|
61
|
+
|
|
62
|
+
#symbols
|
|
63
|
+
k['-'] = '0c 8c';
|
|
64
|
+
k['='] = '0d 8d';
|
|
65
|
+
k[';'] = '27 a7';
|
|
66
|
+
k['"'] = '2a 28 aa a8';
|
|
67
|
+
k['\''] = '28 a8';
|
|
68
|
+
k['\\'] = '2b ab';
|
|
69
|
+
k['|'] = '2a 2b aa 8b';
|
|
70
|
+
k['['] = '1a 9a';
|
|
71
|
+
k[']'] = '1b 9b';
|
|
72
|
+
k['<'] = '2a 33 aa b3';
|
|
73
|
+
k['>'] = '2a 34 aa b4';
|
|
74
|
+
k['?'] = '2a 35 aa b5';
|
|
75
|
+
k['$'] = '2a 05 aa 85';
|
|
76
|
+
k['+'] = '2a 0d aa 8d';
|
|
77
|
+
k[','] = '33 b3' ;
|
|
78
|
+
k['.'] = '34 b4';
|
|
79
|
+
k['/'] = '35 b5' ;
|
|
80
|
+
k[':'] = '2a 27 aa a7';
|
|
81
|
+
k['%'] = '2a 06 aa 86';
|
|
82
|
+
k['_'] = '2a 0c aa 8c';
|
|
83
|
+
k['&'] = '2a 08 aa 88';
|
|
84
|
+
k['('] = '2a 0a aa 8a';
|
|
85
|
+
k[')'] = '2a 0b aa 8b';
|
|
86
|
+
|
|
87
|
+
#delimiters
|
|
88
|
+
k['Tab'] = '0f 8f';
|
|
89
|
+
|
|
90
|
+
#alphabet small
|
|
91
|
+
k['a'] = '1e 9e';
|
|
92
|
+
k['b'] = '30 b0';
|
|
93
|
+
k['c'] = '2e ae';
|
|
94
|
+
k['d'] = '20 a0';
|
|
95
|
+
k['e'] = '12 92';
|
|
96
|
+
k['f'] = '21 a1';
|
|
97
|
+
k['g'] = '22 a2';
|
|
98
|
+
k['h'] = '23 a3';
|
|
99
|
+
k['i'] = '17 97';
|
|
100
|
+
k['j'] = '24 a4';
|
|
101
|
+
k['k'] = '25 a5';
|
|
102
|
+
k['l'] = '26 a6';
|
|
103
|
+
k['m'] = '32 b2';
|
|
104
|
+
k['n'] = '31 b1';
|
|
105
|
+
k['o'] = '18 98';
|
|
106
|
+
k['p'] = '19 99';
|
|
107
|
+
k['q'] = '10 90';
|
|
108
|
+
k['r'] = '13 93';
|
|
109
|
+
k['s'] = '1f 9f';
|
|
110
|
+
k['t'] = '14 94';
|
|
111
|
+
k['u'] = '16 96';
|
|
112
|
+
k['v'] = '2f af';
|
|
113
|
+
k['w'] = '11 91';
|
|
114
|
+
k['x'] = '2d ad';
|
|
115
|
+
k['y'] = '15 95';
|
|
116
|
+
k['z'] = '2c ac';
|
|
117
|
+
|
|
118
|
+
#alphabet big
|
|
119
|
+
k['A'] = '2a 1e aa 9e';
|
|
120
|
+
k['B'] = '2a 30 aa b0';
|
|
121
|
+
k['C'] = '2a 2e aa ae';
|
|
122
|
+
k['D'] = '2a 20 aa a0';
|
|
123
|
+
k['E'] = '2a 12 aa';
|
|
124
|
+
k['F'] = '2a 21 aa a1';
|
|
125
|
+
k['G'] = '2a 22 aa a2';
|
|
126
|
+
k['H'] = '2a 23 aa a3';
|
|
127
|
+
k['I'] = '2a 17 aa';
|
|
128
|
+
k['J'] = '2a 24 aa a4';
|
|
129
|
+
k['K'] = '2a 25 aa a5';
|
|
130
|
+
k['L'] = '2a 26 aa a6';
|
|
131
|
+
k['M'] = '2a 32 aa b2';
|
|
132
|
+
k['N'] = '2a 31 aa b1';
|
|
133
|
+
k['O'] = '2a 18 aa';
|
|
134
|
+
k['P'] = '2a 19 aa';
|
|
135
|
+
k['Q'] = '2a 10 aa';
|
|
136
|
+
k['R'] = '2a 13 aa';
|
|
137
|
+
k['S'] = '2a 1f aa 9f';
|
|
138
|
+
k['T'] = '2a 14 aa';
|
|
139
|
+
k['U'] = '2a 16 aa';
|
|
140
|
+
k['V'] = '2a 2f aa af';
|
|
141
|
+
k['W'] = '2a 11 aa';
|
|
142
|
+
k['X'] = '2a 2d aa ad';
|
|
143
|
+
k['Y'] = '2a 15 aa';
|
|
144
|
+
k['Z'] = '2a 2c aa ac';
|
|
145
|
+
|
|
146
|
+
special=Hash.new;
|
|
147
|
+
special['<Enter>'] = '1c 9c';
|
|
148
|
+
special['<Backspace>'] = '0e 8e';
|
|
149
|
+
special['<Spacebar>'] = '39 b9';
|
|
150
|
+
special['<Return>'] = '1c 9c'
|
|
151
|
+
special['<Esc>'] = '01 81';
|
|
152
|
+
special['<Tab>'] = '0f 8f';
|
|
153
|
+
special['<KillX>'] = '1d 38 0e';
|
|
154
|
+
special['<Wait>'] = 'wait';
|
|
155
|
+
special['<Up>'] = '48 c8';
|
|
156
|
+
special['<Down>'] = '50 d0';
|
|
157
|
+
special['<PageUp>'] = '49 c9';
|
|
158
|
+
special['<PageDown>'] = '51 d1';
|
|
159
|
+
special['<End>'] = '4f cf';
|
|
160
|
+
special['<Insert>'] = '52 d2';
|
|
161
|
+
special['<Delete>'] = '53 d3';
|
|
162
|
+
special['<Left>'] = '4b cb';
|
|
163
|
+
special['<Right>'] = '4d cd';
|
|
164
|
+
special['<Home>'] = '47 c7';
|
|
165
|
+
|
|
166
|
+
special['<F1>'] = '3b';
|
|
167
|
+
special['<F2>'] = '3c';
|
|
168
|
+
special['<F3>'] = '3d';
|
|
169
|
+
special['<F4>'] = '3e';
|
|
170
|
+
special['<F5>'] = '3f';
|
|
171
|
+
special['<F6>'] = '40';
|
|
172
|
+
special['<F7>'] = '41';
|
|
173
|
+
special['<F8>'] = '42';
|
|
174
|
+
special['<F9>'] = '43';
|
|
175
|
+
special['<F10>'] = '44';
|
|
176
|
+
|
|
177
|
+
keycodes=''
|
|
178
|
+
thestring.gsub!(/ /,"<Spacebar>")
|
|
179
|
+
|
|
180
|
+
until thestring.length == 0
|
|
181
|
+
nospecial=true;
|
|
182
|
+
special.keys.each { |key|
|
|
183
|
+
if thestring.start_with?(key)
|
|
184
|
+
#take thestring
|
|
185
|
+
#check if it starts with a special key + pop special string
|
|
186
|
+
keycodes=keycodes+special[key]+' ';
|
|
187
|
+
thestring=thestring.slice(key.length,thestring.length-key.length)
|
|
188
|
+
nospecial=false;
|
|
189
|
+
break;
|
|
190
|
+
end
|
|
191
|
+
}
|
|
192
|
+
if nospecial
|
|
193
|
+
code=k[thestring.slice(0,1)]
|
|
194
|
+
if !code.nil?
|
|
195
|
+
keycodes=keycodes+code+' '
|
|
196
|
+
else
|
|
197
|
+
puts "no scan code for #{thestring.slice(0,1)}"
|
|
198
|
+
end
|
|
199
|
+
#pop one
|
|
200
|
+
thestring=thestring.slice(1,thestring.length-1)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
return keycodes
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
end
|
|
208
|
+
end
|
data/lib/vbox/session.rb
ADDED
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
require 'socket'
|
|
3
|
+
require 'net/scp'
|
|
4
|
+
require 'pp'
|
|
5
|
+
require 'open-uri'
|
|
6
|
+
require 'progressbar'
|
|
7
|
+
require 'highline/import'
|
|
8
|
+
require 'tempfile'
|
|
9
|
+
require 'virtualbox'
|
|
10
|
+
|
|
11
|
+
module Vbox
|
|
12
|
+
class Session
|
|
13
|
+
|
|
14
|
+
attr_accessor :vbox_dir
|
|
15
|
+
attr_accessor :definition_dir
|
|
16
|
+
attr_accessor :template_dir
|
|
17
|
+
attr_accessor :iso_dir
|
|
18
|
+
attr_accessor :name
|
|
19
|
+
attr_accessor :definition
|
|
20
|
+
|
|
21
|
+
def self.setenv(env)
|
|
22
|
+
@vbox_dir=env[:vbox_dir]
|
|
23
|
+
@definition_dir=env[:definition_dir]
|
|
24
|
+
@template_dir=env[:template_dir]
|
|
25
|
+
@validation_dir=env[:validation_dir]
|
|
26
|
+
@box_dir=env[:box_dir]
|
|
27
|
+
@iso_dir=env[:iso_dir]
|
|
28
|
+
@tmp_dir=env[:tmp_dir]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.declare(options)
|
|
32
|
+
defaults={
|
|
33
|
+
:cpu_count => '1',
|
|
34
|
+
:memory_size=> '256',
|
|
35
|
+
:disk_size => '10240',
|
|
36
|
+
:disk_format => 'VDI',
|
|
37
|
+
:hostiocache => 'off' ,
|
|
38
|
+
:os_type_id => 'Ubuntu',
|
|
39
|
+
:iso_file => "ubuntu-10.10-server-i386.iso",
|
|
40
|
+
:iso_src => "", :iso_md5 => "",
|
|
41
|
+
:iso_download_timeout => 1000,
|
|
42
|
+
:boot_wait => "10",
|
|
43
|
+
:boot_cmd_sequence => [ "boot"],
|
|
44
|
+
:kickstart_port => "7122",
|
|
45
|
+
:kickstart_ip => self.local_ip,
|
|
46
|
+
:kickstart_timeout => 10000,
|
|
47
|
+
:ssh_login_timeout => "100",
|
|
48
|
+
:ssh_user => "vagrant",
|
|
49
|
+
:ssh_password => "vagrant",
|
|
50
|
+
:ssh_key => "",
|
|
51
|
+
:ssh_host_port => "2222",
|
|
52
|
+
:ssh_guest_port => "22",
|
|
53
|
+
:sudo_cmd => "echo '%p'|sudo -S sh '%f'",
|
|
54
|
+
:shutdown_cmd => "shutdown -h now",
|
|
55
|
+
:postinstall_files => [ "postinstall.sh"],
|
|
56
|
+
:postinstall_timeout => 10000}
|
|
57
|
+
|
|
58
|
+
@definition=defaults.merge(options)
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.define(boxname,template_name,options = {})
|
|
63
|
+
#Check if template_name exists
|
|
64
|
+
|
|
65
|
+
options = { "force" => false, "format" => "vagrant" }.merge(options)
|
|
66
|
+
|
|
67
|
+
if File.directory?(File.join(@template_dir,template_name))
|
|
68
|
+
else
|
|
69
|
+
puts "This template can not be found, use vagrant vbox templates to list all templates"
|
|
70
|
+
exit
|
|
71
|
+
end
|
|
72
|
+
if !File.exists?(@definition_dir)
|
|
73
|
+
FileUtils.mkdir(@definition_dir)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if File.directory?(File.join(@definition_dir,boxname))
|
|
77
|
+
if !options["force"]
|
|
78
|
+
puts "The definition for #{boxname} already exists. Use --force to overwrite"
|
|
79
|
+
exit
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
FileUtils.mkdir(File.join(@definition_dir,boxname))
|
|
83
|
+
end
|
|
84
|
+
FileUtils.cp_r(File.join(@template_dir,template_name,'.'),File.join(@definition_dir,boxname))
|
|
85
|
+
puts "The vbox '#{boxname}' has been successfully created from the template ''#{template_name}'"
|
|
86
|
+
puts "You can now edit the definition files stored in definitions/#{boxname}"
|
|
87
|
+
puts "or build the box with:"
|
|
88
|
+
if (options["format"]=='vagrant')
|
|
89
|
+
puts "vagrant vbox build '#{boxname}'"
|
|
90
|
+
end
|
|
91
|
+
if (options["format"]=='vbox')
|
|
92
|
+
puts "vbox build '#{boxname}'"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def self.definition_exists?(boxname)
|
|
98
|
+
if File.directory?(File.join(@definition_dir,boxname))
|
|
99
|
+
if File.exists?(File.join(@definition_dir,boxname,'definition.rb'))
|
|
100
|
+
return true
|
|
101
|
+
else
|
|
102
|
+
return false
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
return false
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.undefine(boxname)
|
|
111
|
+
name_dir=File.join(@definition_dir,boxname)
|
|
112
|
+
if File.directory?(name_dir)
|
|
113
|
+
#TODO: Needs to be more defensive!!
|
|
114
|
+
puts "Removing definition #{boxname}"
|
|
115
|
+
FileUtils.rm_rf(name_dir)
|
|
116
|
+
else
|
|
117
|
+
puts "Can not undefine , definition #{boxname} does not exist"
|
|
118
|
+
exit
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def self.list_templates( options = { :format => 'vagrant'})
|
|
123
|
+
puts "The following templates are available:"
|
|
124
|
+
subdirs=Dir.glob("#{@template_dir}/*").sort_by {|f| File.basename f}
|
|
125
|
+
subdirs.each do |sub|
|
|
126
|
+
if File.directory?("#{sub}")
|
|
127
|
+
definition=Dir.glob("#{sub}/definition.rb")
|
|
128
|
+
if definition.length!=0
|
|
129
|
+
name=sub.sub(/#{@template_dir}\//,'')
|
|
130
|
+
if (options[:format]=='vagrant')
|
|
131
|
+
puts "vagrant vbox define '<boxname>' '#{name}'"
|
|
132
|
+
end
|
|
133
|
+
if (options[:format]=='vbox')
|
|
134
|
+
puts "vbox define '<boxname>' '#{name}'"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.list_boxes
|
|
142
|
+
puts "Not yet implemented"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.list_definitions
|
|
146
|
+
puts "The following defined vboxes exist:"
|
|
147
|
+
subdirs=Dir.glob("#{@definition_dir}/*")
|
|
148
|
+
subdirs.each do |sub|
|
|
149
|
+
puts "- "+File.basename(sub)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def self.clean
|
|
154
|
+
puts "Not yet implemented"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def self.verify_iso(filename,autodownload = false)
|
|
158
|
+
if File.exists?(File.join(@iso_dir,filename))
|
|
159
|
+
puts
|
|
160
|
+
puts "Verifying the isofile #{filename} is ok."
|
|
161
|
+
else
|
|
162
|
+
|
|
163
|
+
full_path=File.join(@iso_dir,filename)
|
|
164
|
+
path1=Pathname.new(full_path)
|
|
165
|
+
path2=Pathname.new(Dir.pwd)
|
|
166
|
+
rel_path=path1.relative_path_from(path2).to_s
|
|
167
|
+
|
|
168
|
+
puts
|
|
169
|
+
puts "We did not find an isofile in <currentdir>/iso. \n\nThe definition provided the following download information:"
|
|
170
|
+
unless "#{@definition[:iso_src]}"==""
|
|
171
|
+
puts "- Download url: #{@definition[:iso_src]}"
|
|
172
|
+
end
|
|
173
|
+
puts "- Md5 Checksum: #{@definition[:iso_md5]}"
|
|
174
|
+
puts "#{@definition[:iso_download_instructions]}"
|
|
175
|
+
puts
|
|
176
|
+
|
|
177
|
+
if @definition[:iso_src] == ""
|
|
178
|
+
puts "Please follow the instructions above:"
|
|
179
|
+
puts "- to get the ISO"
|
|
180
|
+
puts" - put it in <currentdir>/iso"
|
|
181
|
+
puts "- then re-run the command"
|
|
182
|
+
puts
|
|
183
|
+
exit
|
|
184
|
+
else
|
|
185
|
+
|
|
186
|
+
question=ask("Download? (Yes/No)") {|q| q.default="No"}
|
|
187
|
+
if question.downcase == "yes"
|
|
188
|
+
if !File.exists?(@iso_dir)
|
|
189
|
+
puts "Creating an iso directory"
|
|
190
|
+
FileUtils.mkdir(@iso_dir)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
download_progress(@definition[:iso_src],full_path)
|
|
194
|
+
else
|
|
195
|
+
puts "You have choosen for manual download: "
|
|
196
|
+
puts "curl -C - -L '#{@definition[:iso_src]}' -o '#{rel_path}'"
|
|
197
|
+
puts "md5 '#{rel_path}' "
|
|
198
|
+
puts
|
|
199
|
+
exit
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def self.export_box(boxname)
|
|
208
|
+
#Now we have to load the definition (reads definition.rb)
|
|
209
|
+
load_definition(boxname)
|
|
210
|
+
|
|
211
|
+
Vbox::Export.vagrant(boxname,@box_dir,@definition)
|
|
212
|
+
#vagrant removes the mapping
|
|
213
|
+
#we need to restore it in order to be able to login again
|
|
214
|
+
add_ssh_nat_mapping(boxname)
|
|
215
|
+
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def self.remove_box(boxname)
|
|
219
|
+
puts "Not yet implemented"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def self.build(boxname,options)
|
|
223
|
+
|
|
224
|
+
options = { "force" => false, "format" => "vagrant", "nogui" => false }.merge(options)
|
|
225
|
+
|
|
226
|
+
#Now we have to load the definition (reads definition.rb)
|
|
227
|
+
load_definition(boxname)
|
|
228
|
+
|
|
229
|
+
#Command to execute locally
|
|
230
|
+
@vboxcmd=determine_vboxcmd
|
|
231
|
+
|
|
232
|
+
ssh_options={ :user => @definition[:ssh_user], :port => @definition[:ssh_host_port], :password => @definition[:ssh_password],
|
|
233
|
+
:timeout => @definition[:ssh_timeout]}
|
|
234
|
+
|
|
235
|
+
#Suppress those annoying virtualbox messages
|
|
236
|
+
suppress_messages
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
vm=VirtualBox::VM.find(boxname)
|
|
240
|
+
|
|
241
|
+
if (!vm.nil? && (vm.saved?))
|
|
242
|
+
puts "Removing save state"
|
|
243
|
+
vm.discard_state
|
|
244
|
+
vm.reload
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
if (!vm.nil? && !(vm.powered_off?))
|
|
248
|
+
puts "Shutting down vm #{boxname}"
|
|
249
|
+
#We force it here, maybe vm.shutdown is cleaner
|
|
250
|
+
begin
|
|
251
|
+
|
|
252
|
+
vm.stop
|
|
253
|
+
rescue VirtualBox::Exceptions::InvalidVMStateException
|
|
254
|
+
puts "There was problem sending the stop command because the machine is in an Invalid state"
|
|
255
|
+
puts "Please verify leftovers from a previous build in your vm folder"
|
|
256
|
+
exit
|
|
257
|
+
end
|
|
258
|
+
sleep 3
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
verify_iso(@definition[:iso_file])
|
|
263
|
+
|
|
264
|
+
if (options["force"]==false)
|
|
265
|
+
else
|
|
266
|
+
puts "Forcing build by destroying #{boxname} machine"
|
|
267
|
+
destroy_vm(boxname)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
if Vbox::Utils.is_port_open?("localhost", @definition[:ssh_host_port])
|
|
271
|
+
puts "Hmm, the port #{@definition[:ssh_host_port]} is open. And we shut down?"
|
|
272
|
+
exit
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
checksums=calculate_checksums(@definition,boxname)
|
|
276
|
+
|
|
277
|
+
transaction(boxname,"0-initial-#{checksums[0]}",checksums) do
|
|
278
|
+
|
|
279
|
+
#Create the Virtualmachine and set all the memory and other stuff
|
|
280
|
+
create_vm(boxname)
|
|
281
|
+
|
|
282
|
+
#Create a disk with the same name as the boxname
|
|
283
|
+
create_disk(boxname)
|
|
284
|
+
|
|
285
|
+
#These command actually call the commandline of Virtualbox, I hope to use the virtualbox-ruby library in the future
|
|
286
|
+
add_ide_controller(boxname)
|
|
287
|
+
add_sata_controller(boxname)
|
|
288
|
+
attach_disk(boxname)
|
|
289
|
+
mount_isofile(boxname,@definition[:iso_file])
|
|
290
|
+
add_ssh_nat_mapping(boxname)
|
|
291
|
+
|
|
292
|
+
#Starting machine
|
|
293
|
+
|
|
294
|
+
if (options["nogui"]==true)
|
|
295
|
+
start_vm(boxname,"vrdp")
|
|
296
|
+
else
|
|
297
|
+
start_vm(boxname,"gui")
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
#waiting for it to boot
|
|
301
|
+
puts "Waiting for the machine to boot"
|
|
302
|
+
sleep @definition[:boot_wait].to_i
|
|
303
|
+
|
|
304
|
+
Vbox::Scancode.send_sequence("#{@vboxcmd}","#{boxname}",@definition[:boot_cmd_sequence],@definition[:kickstart_port])
|
|
305
|
+
|
|
306
|
+
kickstartfile=@definition[:kickstart_file]
|
|
307
|
+
if kickstartfile.nil? || kickstartfile.length == 0
|
|
308
|
+
puts "Skipping webserver as no kickstartfile was specified"
|
|
309
|
+
else
|
|
310
|
+
puts "Starting a webserver on port #{@definition[:kickstart_port]}"
|
|
311
|
+
#:kickstart_port => "7122", :kickstart_ip => self.local_ip, :kickstart_timeout => 1000,:kickstart_file => "preseed.cfg",
|
|
312
|
+
if kickstartfile.is_a? String
|
|
313
|
+
Vbox::Web.wait_for_request(kickstartfile,{:port => @definition[:kickstart_port],
|
|
314
|
+
:host => @definition[:kickstart_ip], :timeout => @definition[:kickstart_timeout],
|
|
315
|
+
:web_dir => File.join(@definition_dir,boxname)})
|
|
316
|
+
end
|
|
317
|
+
if kickstartfile.is_a? Array
|
|
318
|
+
kickstartfiles=kickstartfile
|
|
319
|
+
kickstartfiles.each do |kickfile|
|
|
320
|
+
Vbox::Web.wait_for_request(kickfile,{:port => @definition[:kickstart_port],
|
|
321
|
+
:host => @definition[:kickstart_ip], :timeout => @definition[:kickstart_timeout],
|
|
322
|
+
:web_dir => File.join(@definition_dir,boxname)})
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
Vbox::Ssh.when_ssh_login_works("localhost",ssh_options) do
|
|
329
|
+
#Transfer version of Virtualbox to $HOME/.vbox_version
|
|
330
|
+
versionfile=Tempfile.open("vbox.version")
|
|
331
|
+
versionfile.puts "#{VirtualBox::Global.global.lib.virtualbox.version.split('_')[0]}"
|
|
332
|
+
versionfile.rewind
|
|
333
|
+
begin
|
|
334
|
+
Vbox::Ssh.transfer_file("localhost",versionfile.path,".vbox_version", ssh_options)
|
|
335
|
+
rescue RuntimeError
|
|
336
|
+
puts "error transfering file, possible not enough permissions to write?"
|
|
337
|
+
exit
|
|
338
|
+
end
|
|
339
|
+
puts ""
|
|
340
|
+
versionfile.close
|
|
341
|
+
versionfile.delete
|
|
342
|
+
end
|
|
343
|
+
end #initial Transaction
|
|
344
|
+
|
|
345
|
+
counter=1
|
|
346
|
+
@definition[:postinstall_files].each do |postinstall_file|
|
|
347
|
+
|
|
348
|
+
filename=File.join(@definition_dir,boxname,postinstall_file)
|
|
349
|
+
|
|
350
|
+
transaction(boxname,"#{counter}-#{postinstall_file}-#{checksums[counter]}",checksums) do
|
|
351
|
+
|
|
352
|
+
Vbox::Ssh.when_ssh_login_works("localhost",ssh_options) do
|
|
353
|
+
begin
|
|
354
|
+
Vbox::Ssh.transfer_file("localhost",filename,File.basename(filename),ssh_options)
|
|
355
|
+
rescue RuntimeError
|
|
356
|
+
puts "error transferring file, possible not enough permissions to write?"
|
|
357
|
+
exit
|
|
358
|
+
end
|
|
359
|
+
command=@definition[:sudo_cmd]
|
|
360
|
+
newcommand=command.gsub(/%p/,"#{@definition[:ssh_password]}")
|
|
361
|
+
newcommand.gsub!(/%u/,"#{@definition[:ssh_user]}")
|
|
362
|
+
newcommand.gsub!(/%f/,"#{postinstall_file}")
|
|
363
|
+
puts "***#{newcommand}"
|
|
364
|
+
Vbox::Ssh.execute("localhost","#{newcommand}",ssh_options)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
end
|
|
368
|
+
counter+=1
|
|
369
|
+
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
puts "#{boxname} was built successfully. "
|
|
373
|
+
puts ""
|
|
374
|
+
puts "Now you can: "
|
|
375
|
+
puts "- verify your box by running : vagrant vbox validate #{boxname}"
|
|
376
|
+
puts "- export your vm to a .box file by running : vagrant vbox export #{boxname}"
|
|
377
|
+
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def self.determine_vboxcmd
|
|
381
|
+
return "VBoxManage"
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def self.start_vm(boxname,mode)
|
|
385
|
+
vm=VirtualBox::VM.find(boxname)
|
|
386
|
+
vm.start(mode)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def self.load_definition(boxname)
|
|
390
|
+
|
|
391
|
+
if definition_exists?(boxname)
|
|
392
|
+
definition_file=File.join(@definition_dir,boxname,"definition.rb")
|
|
393
|
+
begin
|
|
394
|
+
require definition_file
|
|
395
|
+
rescue LoadError
|
|
396
|
+
puts "Error loading definition of #{boxname}"
|
|
397
|
+
exit
|
|
398
|
+
end
|
|
399
|
+
else
|
|
400
|
+
puts "Error: definition for vbox '#{boxname}' does not exist."
|
|
401
|
+
list_definitions
|
|
402
|
+
exit
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def self.add_ssh_nat_mapping(boxname)
|
|
407
|
+
vm=VirtualBox::VM.find(boxname)
|
|
408
|
+
#Map SSH Ports
|
|
409
|
+
# command => "${vboxcmd} modifyvm '${vname}' --natpf1 'guestssh,tcp,,${hostsshport},,${guestsshport}'",
|
|
410
|
+
port = VirtualBox::NATForwardedPort.new
|
|
411
|
+
port.name = "guestssh"
|
|
412
|
+
port.guestport = @definition[:ssh_guest_port].to_i
|
|
413
|
+
port.hostport = @definition[:ssh_host_port].to_i
|
|
414
|
+
vm.network_adapters[0].nat_driver.forwarded_ports << port
|
|
415
|
+
port.save
|
|
416
|
+
vm.save
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def self.destroy_vm(boxname)
|
|
420
|
+
|
|
421
|
+
load_definition(boxname)
|
|
422
|
+
@vboxcmd=determine_vboxcmd
|
|
423
|
+
#:destroy_medium => :delete, will delete machine + all media attachments
|
|
424
|
+
#vm.destroy(:destroy_medium => :delete)
|
|
425
|
+
##vm.destroy(:destroy_image => true)
|
|
426
|
+
|
|
427
|
+
#VBoxManage unregistervm "test-machine" --delete
|
|
428
|
+
#because the destroy does remove the .vbox file on 4.0.x
|
|
429
|
+
#PDB
|
|
430
|
+
#vm.destroy()
|
|
431
|
+
|
|
432
|
+
vm=VirtualBox::VM.find(boxname)
|
|
433
|
+
|
|
434
|
+
if (!vm.nil? && !(vm.powered_off?))
|
|
435
|
+
puts "Shutting down vm #{boxname}"
|
|
436
|
+
#We force it here, maybe vm.shutdown is cleaner
|
|
437
|
+
begin
|
|
438
|
+
vm.stop
|
|
439
|
+
rescue VirtualBox::Exceptions::InvalidVMStateException
|
|
440
|
+
puts "There was problem sending the stop command because the machine is in an Invalid state"
|
|
441
|
+
puts "Please verify leftovers from a previous build in your vm folder"
|
|
442
|
+
exit
|
|
443
|
+
end
|
|
444
|
+
sleep 3
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
command="#{@vboxcmd} unregistervm '#{boxname}' --delete"
|
|
448
|
+
puts command
|
|
449
|
+
puts "Deleting vm #{boxname}"
|
|
450
|
+
|
|
451
|
+
#Exec and system stop the execution here
|
|
452
|
+
Vbox::Shell.execute("#{command}")
|
|
453
|
+
sleep 1
|
|
454
|
+
|
|
455
|
+
#if the disk was not attached when the machine was destroyed we also need to delete the disk
|
|
456
|
+
location=boxname+"."+@definition[:disk_format].downcase
|
|
457
|
+
found=false
|
|
458
|
+
VirtualBox::HardDrive.all.each do |d|
|
|
459
|
+
if d.location.match(/#{location}/)
|
|
460
|
+
|
|
461
|
+
if File.exists?(d.location)
|
|
462
|
+
command="#{@vboxcmd} closemedium disk '#{d.location}' --delete"
|
|
463
|
+
else
|
|
464
|
+
command="#{@vboxcmd} closemedium disk '#{d.location}'"
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
#command="#{@vboxcmd} closemedium disk '#{d.location}' --delete"
|
|
468
|
+
puts "Deleting disk #{d.location}"
|
|
469
|
+
puts "#{command}"
|
|
470
|
+
|
|
471
|
+
Vbox::Shell.execute("#{command}")
|
|
472
|
+
|
|
473
|
+
if File.exists?(d.location)
|
|
474
|
+
puts "We tried to delete the disk file via virtualbox '#{d.location} but failed"
|
|
475
|
+
puts "Removing it manually"
|
|
476
|
+
FileUtils.rm(d.location)
|
|
477
|
+
exit
|
|
478
|
+
end
|
|
479
|
+
#v.3
|
|
480
|
+
#d.destroy(true)
|
|
481
|
+
break
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def self.create_vm(boxname,force=false)
|
|
487
|
+
|
|
488
|
+
#Verifying the os.id with the :os_type_id specified
|
|
489
|
+
matchfound=false
|
|
490
|
+
VirtualBox::Global.global.lib.virtualbox.guest_os_types.collect { |os|
|
|
491
|
+
if @definition[:os_type_id] == os.id
|
|
492
|
+
matchfound=true
|
|
493
|
+
end
|
|
494
|
+
}
|
|
495
|
+
unless matchfound
|
|
496
|
+
puts "The ostype: #{@definition[:os_type_id]} is not available in your Virtualbox version"
|
|
497
|
+
exit
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
vm=VirtualBox::VM.find(boxname)
|
|
502
|
+
|
|
503
|
+
if (!vm.nil? && !(vm.powered_off?))
|
|
504
|
+
puts "shutting down box"
|
|
505
|
+
#We force it here, maybe vm.shutdown is cleaner
|
|
506
|
+
vm.stop
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
if !vm.nil?
|
|
510
|
+
puts "Box already exists"
|
|
511
|
+
#vm.stop
|
|
512
|
+
#vm.destroy
|
|
513
|
+
else
|
|
514
|
+
#TODO One day ruby-virtualbox will be able to handle this creation
|
|
515
|
+
#Box does not exist, we can start to create it
|
|
516
|
+
|
|
517
|
+
command="#{@vboxcmd} createvm --name '#{boxname}' --ostype '#{@definition[:os_type_id]}' --register"
|
|
518
|
+
|
|
519
|
+
#Exec and system stop the execution here
|
|
520
|
+
Vbox::Shell.execute("#{command}")
|
|
521
|
+
|
|
522
|
+
# Modify the vm to enable or disable hw virtualization extensions
|
|
523
|
+
vm_flags=%w{pagefusion acpi ioapic pae hpet hwvirtex hwvirtexcl nestedpaging largepages vtxvpid synthxcpu rtcuseutc}
|
|
524
|
+
|
|
525
|
+
vm_flags.each do |vm_flag|
|
|
526
|
+
unless @definition[vm_flag.to_sym].nil?
|
|
527
|
+
puts "Setting VM Flag #{vm_flag} to #{@definition[vm_flag.to_sym]}"
|
|
528
|
+
command="#{@vboxcmd} modifyvm #{boxname} --#{vm_flag.to_s} #{@definition[vm_flag.to_sym]}"
|
|
529
|
+
Vbox::Shell.execute("#{command}")
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# Todo Check for java
|
|
534
|
+
# Todo check output of commands
|
|
535
|
+
|
|
536
|
+
# Check for floppy
|
|
537
|
+
unless @definition[:floppy_files].nil?
|
|
538
|
+
require 'tmpdir'
|
|
539
|
+
temp_dir=Dir.tmpdir
|
|
540
|
+
@definition[:floppy_files].each do |filename|
|
|
541
|
+
full_filename=full_filename=File.join(@definition_dir,boxname,filename)
|
|
542
|
+
FileUtils.cp("#{full_filename}","#{temp_dir}")
|
|
543
|
+
end
|
|
544
|
+
javacode_dir=File.expand_path(File.join(__FILE__,'..','..','java'))
|
|
545
|
+
floppy_file=File.join(@definition_dir,boxname,"virtualfloppy.vfd")
|
|
546
|
+
command="java -jar #{javacode_dir}/dir2floppy.jar '#{temp_dir}' '#{floppy_file}'"
|
|
547
|
+
puts "#{command}"
|
|
548
|
+
Vbox::Shell.execute("#{command}")
|
|
549
|
+
|
|
550
|
+
# Create floppy controller
|
|
551
|
+
command="#{@vboxcmd} storagectl '#{boxname}' --name 'Floppy Controller' --add floppy"
|
|
552
|
+
puts "#{command}"
|
|
553
|
+
Vbox::Shell.execute("#{command}")
|
|
554
|
+
|
|
555
|
+
# Attach floppy to machine (the vfd extension is crucial to detect msdos type floppy)
|
|
556
|
+
command="#{@vboxcmd} storageattach '#{boxname}' --storagectl 'Floppy Controller' --port 0 --device 0 --type fdd --medium '#{floppy_file}'"
|
|
557
|
+
puts "#{command}"
|
|
558
|
+
Vbox::Shell.execute("#{command}")
|
|
559
|
+
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
#Exec and system stop the execution here
|
|
563
|
+
Vbox::Shell.execute("#{command}")
|
|
564
|
+
|
|
565
|
+
command="#{@vboxcmd} sharedfolder add '#{boxname}' --name 'vbox-validation' --hostpath '#{File.expand_path(@validation_dir)}' --automount"
|
|
566
|
+
|
|
567
|
+
Vbox::Shell.execute("#{command}")
|
|
568
|
+
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
vm=VirtualBox::VM.find(boxname)
|
|
572
|
+
if vm.nil?
|
|
573
|
+
puts "we tried to create a box or a box was here before"
|
|
574
|
+
puts "but now it's gone"
|
|
575
|
+
exit
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
#Set all params we know
|
|
579
|
+
vm.memory_size=@definition[:memory_size].to_i
|
|
580
|
+
vm.os_type_id=@definition[:os_type_id]
|
|
581
|
+
vm.cpu_count=@definition[:cpu_count].to_i
|
|
582
|
+
vm.name=boxname
|
|
583
|
+
|
|
584
|
+
puts "Creating vm #{vm.name} : #{vm.memory_size}M - #{vm.cpu_count} CPU - #{vm.os_type_id}"
|
|
585
|
+
#setting bootorder
|
|
586
|
+
vm.boot_order[0]=:hard_disk
|
|
587
|
+
vm.boot_order[1]=:dvd
|
|
588
|
+
vm.boot_order[2]=:null
|
|
589
|
+
vm.boot_order[3]=:null
|
|
590
|
+
vm.validate
|
|
591
|
+
vm.save
|
|
592
|
+
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def self.create_disk(boxname)
|
|
596
|
+
#Now check the disks
|
|
597
|
+
#Maybe one day we can use the name, now we have to check location
|
|
598
|
+
#disk=VirtualBox::HardDrive.find(boxname)
|
|
599
|
+
location=boxname+"."+@definition[:disk_format].downcase
|
|
600
|
+
|
|
601
|
+
found=false
|
|
602
|
+
VirtualBox::HardDrive.all.each do |d|
|
|
603
|
+
if !d.location.match(/#{location}/).nil?
|
|
604
|
+
found=true
|
|
605
|
+
break
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
@vboxcmd=determine_vboxcmd
|
|
610
|
+
|
|
611
|
+
if !found
|
|
612
|
+
puts "Creating new harddrive of size #{@definition[:disk_size].to_i} "
|
|
613
|
+
|
|
614
|
+
#newdisk=VirtualBox::HardDrive.new
|
|
615
|
+
#newdisk.format=@definition[:disk_format]
|
|
616
|
+
#newdisk.logical_size=@definition[:disk_size].to_i
|
|
617
|
+
|
|
618
|
+
#newdisk.location=location
|
|
619
|
+
##PDB: again problems with the virtualbox GEM
|
|
620
|
+
##VirtualBox::Global.global.max_vdi_size=1000000
|
|
621
|
+
#newdisk.save
|
|
622
|
+
|
|
623
|
+
command="#{@vboxcmd} list systemproperties|grep '^Default machine'|cut -d ':' -f 2|sed -e 's/^[ ]*//'"
|
|
624
|
+
results=IO.popen("#{command}")
|
|
625
|
+
place=results.gets.chop
|
|
626
|
+
results.close
|
|
627
|
+
|
|
628
|
+
command ="#{@vboxcmd} createhd --filename '#{place}/#{boxname}/#{boxname}.#{@definition[:disk_format].downcase}' --size '#{@definition[:disk_size].to_i}' --format #{@definition[:disk_format].downcase} > /dev/null"
|
|
629
|
+
puts "#{command}"
|
|
630
|
+
Vbox::Shell.execute("#{command}")
|
|
631
|
+
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def self.add_ide_controller(boxname)
|
|
637
|
+
#unless => "${vboxcmd} showvminfo '${vname}' | grep 'IDE Controller' "
|
|
638
|
+
command ="#{@vboxcmd} storagectl '#{boxname}' --name 'IDE Controller' --add ide"
|
|
639
|
+
Vbox::Shell.execute("#{command}")
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
def self.add_sata_controller(boxname)
|
|
643
|
+
#unless => "${vboxcmd} showvminfo '${vname}' | grep 'SATA Controller' ";
|
|
644
|
+
command ="#{@vboxcmd} storagectl '#{boxname}' --name 'SATA Controller' --add sata --sataportcount 1 --hostiocache #{@definition[:hostiocache]}"
|
|
645
|
+
Vbox::Shell.execute("#{command}")
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
def self.attach_disk(boxname)
|
|
649
|
+
location=boxname+"."+@definition[:disk_format].downcase
|
|
650
|
+
|
|
651
|
+
@vboxcmd=determine_vboxcmd
|
|
652
|
+
|
|
653
|
+
command="#{@vboxcmd} list systemproperties|grep '^Default machine'|cut -d ':' -f 2|sed -e 's/^[ ]*//'"
|
|
654
|
+
results=IO.popen("#{command}")
|
|
655
|
+
place=results.gets.chop
|
|
656
|
+
results.close
|
|
657
|
+
|
|
658
|
+
location="#{place}/#{boxname}/"+location
|
|
659
|
+
puts "Attaching disk: #{location}"
|
|
660
|
+
|
|
661
|
+
#command => "${vboxcmd} storageattach '${vname}' --storagectl 'SATA Controller' --port 0 --device 0 --type hdd --medium '${vname}.vdi'",
|
|
662
|
+
command ="#{@vboxcmd} storageattach '#{boxname}' --storagectl 'SATA Controller' --port 0 --device 0 --type hdd --medium '#{location}'"
|
|
663
|
+
Vbox::Shell.execute("#{command}")
|
|
664
|
+
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def self.mount_isofile(boxname,isofile)
|
|
668
|
+
full_iso_file=File.join(@iso_dir,isofile)
|
|
669
|
+
puts "Mounting cdrom: #{full_iso_file}"
|
|
670
|
+
#command => "${vboxcmd} storageattach '${vname}' --storagectl 'IDE Controller' --type dvddrive --port 1 --device 0 --medium '${isodst}' ";
|
|
671
|
+
command ="#{@vboxcmd} storageattach '#{boxname}' --storagectl 'IDE Controller' --type dvddrive --port 1 --device 0 --medium '#{full_iso_file}'"
|
|
672
|
+
Vbox::Shell.execute("#{command}")
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
def self.suppress_messages
|
|
676
|
+
#Setting this annoying messages to register
|
|
677
|
+
VirtualBox::ExtraData.global["GUI/RegistrationData"]="triesLeft=0"
|
|
678
|
+
VirtualBox::ExtraData.global["GUI/UpdateDate"]="1 d, 2009-09-20"
|
|
679
|
+
VirtualBox::ExtraData.global["GUI/SuppressMessages"]="confirmInputCapture,remindAboutAutoCapture,remindAboutMouseIntegrationOff"
|
|
680
|
+
VirtualBox::ExtraData.global["GUI/UpdateCheckCount"]="60"
|
|
681
|
+
update_date=Time.now+86400
|
|
682
|
+
VirtualBox::ExtraData.global["GUI/UpdateDate"]="1 d, #{update_date.year}-#{update_date.month}-#{update_date.day}, stable"
|
|
683
|
+
|
|
684
|
+
VirtualBox::ExtraData.global.save
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
def self.local_ip
|
|
688
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
|
689
|
+
|
|
690
|
+
UDPSocket.open do |s|
|
|
691
|
+
s.connect '64.233.187.99', 1
|
|
692
|
+
s.addr.last
|
|
693
|
+
end
|
|
694
|
+
ensure
|
|
695
|
+
Socket.do_not_reverse_lookup = orig
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
def self.validate_box(boxname,options)
|
|
699
|
+
require 'cucumber'
|
|
700
|
+
|
|
701
|
+
require 'cucumber/rspec/disable_option_parser'
|
|
702
|
+
require 'cucumber/cli/main'
|
|
703
|
+
|
|
704
|
+
ENV['vbox_user']=options[:user]
|
|
705
|
+
feature_path=File.join(File.dirname(__FILE__),"..","..","validation","vagrant.feature")
|
|
706
|
+
|
|
707
|
+
features=Array.new
|
|
708
|
+
features[0]=feature_path
|
|
709
|
+
|
|
710
|
+
begin
|
|
711
|
+
# The dup is to keep ARGV intact, so that tools like ruby-debug can respawn.
|
|
712
|
+
failure = Cucumber::Cli::Main.execute(features.dup)
|
|
713
|
+
Kernel.exit(failure ? 1 : 0)
|
|
714
|
+
rescue SystemExit => e
|
|
715
|
+
Kernel.exit(e.status)
|
|
716
|
+
rescue Exception => e
|
|
717
|
+
STDERR.puts("#{e.message} (#{e.class})")
|
|
718
|
+
STDERR.puts(e.backtrace.join("\n"))
|
|
719
|
+
Kernel.exit(1)
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
def self.list_ostypes
|
|
725
|
+
puts
|
|
726
|
+
puts "Available os types:"
|
|
727
|
+
VirtualBox::Global.global.lib.virtualbox.guest_os_types.collect { |os|
|
|
728
|
+
puts "#{os.id}: #{os.description}"
|
|
729
|
+
}
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
def self.calculate_checksums(definition,boxname)
|
|
733
|
+
|
|
734
|
+
#TODO: get rid of definitiondir and so one
|
|
735
|
+
initial=definition.clone
|
|
736
|
+
|
|
737
|
+
keys=[:postinstall_files,:sudo_cmd,:postinstall_timeout]
|
|
738
|
+
keys.each do |key|
|
|
739
|
+
initial.delete(key)
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
checksums=Array.new
|
|
743
|
+
checksums << Digest::MD5.hexdigest(initial.to_s)
|
|
744
|
+
|
|
745
|
+
postinstall_files=definition[:postinstall_files]
|
|
746
|
+
unless postinstall_files.nil?
|
|
747
|
+
for filename in postinstall_files
|
|
748
|
+
begin
|
|
749
|
+
full_filename=File.join(@definition_dir,boxname,filename)
|
|
750
|
+
|
|
751
|
+
checksums << Digest::MD5.hexdigest(File.read(full_filename))
|
|
752
|
+
rescue
|
|
753
|
+
puts "Error reading postinstall file #{filename} - checksum"
|
|
754
|
+
exit
|
|
755
|
+
end
|
|
756
|
+
end
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
return checksums
|
|
760
|
+
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
def self.download_progress(url,localfile)
|
|
764
|
+
pbar = nil
|
|
765
|
+
URI.parse(url).open(
|
|
766
|
+
:content_length_proc => lambda {|t|
|
|
767
|
+
if t && 0 < t
|
|
768
|
+
pbar = ProgressBar.new("Fetching file", t)
|
|
769
|
+
pbar.file_transfer_mode
|
|
770
|
+
end
|
|
771
|
+
},
|
|
772
|
+
:progress_proc => lambda {|s|
|
|
773
|
+
pbar.set s if pbar
|
|
774
|
+
}) { |src|
|
|
775
|
+
open("#{localfile}","wb") { |dst|
|
|
776
|
+
dst.write(src.read)
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
def self.transaction(boxname,step_name,checksums,&block)
|
|
783
|
+
|
|
784
|
+
current_step_nr=step_name.split("-")[0].to_i
|
|
785
|
+
|
|
786
|
+
vm=VirtualBox::VM.find(boxname)
|
|
787
|
+
snapnames=Array.new
|
|
788
|
+
|
|
789
|
+
#If vm exists , look for snapshots
|
|
790
|
+
if !vm.nil?
|
|
791
|
+
start_snapshot=vm.root_snapshot
|
|
792
|
+
snapshot=start_snapshot
|
|
793
|
+
counter=0
|
|
794
|
+
|
|
795
|
+
while (snapshot!=nil)
|
|
796
|
+
#puts "#{counter}:#{snapshot.name}"
|
|
797
|
+
snapnames[counter]=snapshot.name
|
|
798
|
+
counter=counter+1
|
|
799
|
+
snapshot=snapshot.children[0]
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
#find the last snapshot matching the state
|
|
804
|
+
counter=[snapnames.length, checksums.length].min-1
|
|
805
|
+
last_good_state=counter
|
|
806
|
+
for c in 0..counter do
|
|
807
|
+
#puts "#{c}- #{snapnames[c]} - #{checksums[c]}"
|
|
808
|
+
if !snapnames[c].match("#{c}.*-#{checksums[c]}")
|
|
809
|
+
# puts "we found a bad state"
|
|
810
|
+
last_good_state=c-1
|
|
811
|
+
break
|
|
812
|
+
end
|
|
813
|
+
end
|
|
814
|
+
#puts "Last good state: #{last_good_state}"
|
|
815
|
+
|
|
816
|
+
if (current_step_nr < last_good_state)
|
|
817
|
+
#puts "fast forwarding #{step_name}"
|
|
818
|
+
return
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
#puts "Current step: #{current_step_nr}"
|
|
822
|
+
if (current_step_nr == last_good_state)
|
|
823
|
+
if vm.running?
|
|
824
|
+
vm.stop
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
#invalidate later snapshots
|
|
828
|
+
#puts "remove old snapshots"
|
|
829
|
+
|
|
830
|
+
for s in (last_good_state+1)..(snapnames.length-1)
|
|
831
|
+
puts "Removing step [#{s}] snapshot as it is no more valid"
|
|
832
|
+
snapshot=vm.find_snapshot(snapnames[s])
|
|
833
|
+
snapshot.destroy
|
|
834
|
+
#puts snapshot
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
vm.reload
|
|
838
|
+
puts "Loading step #{current_step_nr} snapshots as it has not changed"
|
|
839
|
+
sleep 2
|
|
840
|
+
goodsnap=vm.find_snapshot(snapnames[last_good_state])
|
|
841
|
+
goodsnap.restore
|
|
842
|
+
sleep 2
|
|
843
|
+
#TODO:Restore snapshot!!!
|
|
844
|
+
vm.start
|
|
845
|
+
sleep 4
|
|
846
|
+
puts "Starting machine"
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
#puts "last good state #{last_good_state}"
|
|
850
|
+
|
|
851
|
+
if (current_step_nr > last_good_state)
|
|
852
|
+
|
|
853
|
+
if (last_good_state==-1)
|
|
854
|
+
#no initial snapshot is found, clean machine!
|
|
855
|
+
vm=VirtualBox::VM.find(boxname)
|
|
856
|
+
|
|
857
|
+
if !vm.nil?
|
|
858
|
+
if vm.running?
|
|
859
|
+
puts "Stopping machine"
|
|
860
|
+
vm.stop
|
|
861
|
+
while vm.running?
|
|
862
|
+
sleep 1
|
|
863
|
+
end
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
vm.reload
|
|
867
|
+
puts "We found no good state so we are destroying the previous machine+disks"
|
|
868
|
+
destroy_vm(boxname)
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
#puts "(re-)executing step #{step_name}"
|
|
874
|
+
|
|
875
|
+
yield
|
|
876
|
+
|
|
877
|
+
#Need to look it up again because if it was an initial load
|
|
878
|
+
vm=VirtualBox::VM.find(boxname)
|
|
879
|
+
puts "Step [#{current_step_nr}] was successfully - saving state"
|
|
880
|
+
vm.save_state
|
|
881
|
+
sleep 2 #waiting for it to be ok
|
|
882
|
+
#puts "about to snapshot #{vm}"
|
|
883
|
+
#take snapshot after successful execution
|
|
884
|
+
vm.take_snapshot(step_name,"snapshot taken by vbox")
|
|
885
|
+
sleep 2 #waiting for it to be started again
|
|
886
|
+
vm.start
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
#pp snapnames
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
end #End Class
|
|
893
|
+
end #End Module
|