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.
Files changed (131) hide show
  1. data/.gitignore +14 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +68 -0
  4. data/README.md +5 -0
  5. data/Rakefile +91 -0
  6. data/bin/vbox +73 -0
  7. data/lib/java/README.txt +5 -0
  8. data/lib/java/dir2floppy.jar +0 -0
  9. data/lib/java/dir2floppy.java +134 -0
  10. data/lib/vagrant_init.rb +6 -0
  11. data/lib/vbox.rb +2 -0
  12. data/lib/vbox/command.rb +82 -0
  13. data/lib/vbox/config.rb +5 -0
  14. data/lib/vbox/export.rb +65 -0
  15. data/lib/vbox/scancode.rb +208 -0
  16. data/lib/vbox/session.rb +893 -0
  17. data/lib/vbox/shell.rb +54 -0
  18. data/lib/vbox/ssh.rb +193 -0
  19. data/lib/vbox/transaction.rb +122 -0
  20. data/lib/vbox/utils.rb +26 -0
  21. data/lib/vbox/version.rb +3 -0
  22. data/lib/vbox/web.rb +48 -0
  23. data/templates/CentOS-5.7-i386-netboot/definition.rb +16 -0
  24. data/templates/CentOS-5.7-i386-netboot/ks.cfg +45 -0
  25. data/templates/CentOS-5.7-i386-netboot/postinstall.sh +53 -0
  26. data/templates/CentOS-5.7-x86_64-netboot/definition.rb +16 -0
  27. data/templates/CentOS-5.7-x86_64-netboot/ks.cfg +45 -0
  28. data/templates/CentOS-5.7-x86_64-netboot/postinstall.sh +60 -0
  29. data/templates/CentOS-6.0-i386-netboot/definition.rb +16 -0
  30. data/templates/CentOS-6.0-i386-netboot/ks.cfg +52 -0
  31. data/templates/CentOS-6.0-i386-netboot/postinstall.sh +30 -0
  32. data/templates/CentOS-6.0-i386/definition.rb +17 -0
  33. data/templates/CentOS-6.0-i386/ks.cfg +47 -0
  34. data/templates/CentOS-6.0-i386/postinstall.sh +48 -0
  35. data/templates/CentOS-6.0-x86_64-netboot/definition.rb +16 -0
  36. data/templates/CentOS-6.0-x86_64-netboot/ks.cfg +52 -0
  37. data/templates/CentOS-6.0-x86_64-netboot/postinstall.sh +30 -0
  38. data/templates/CentOS-6.0-x86_64/definition.rb +17 -0
  39. data/templates/CentOS-6.0-x86_64/ks.cfg +47 -0
  40. data/templates/CentOS-6.0-x86_64/postinstall.sh +48 -0
  41. data/templates/Debian-6.0.3-amd64-netboot/definition.rb +42 -0
  42. data/templates/Debian-6.0.3-amd64-netboot/postinstall.sh +80 -0
  43. data/templates/Debian-6.0.3-amd64-netboot/preseed.cfg +42 -0
  44. data/templates/Debian-6.0.3-i386-netboot/definition.rb +44 -0
  45. data/templates/Debian-6.0.3-i386-netboot/postinstall.sh +80 -0
  46. data/templates/Debian-6.0.3-i386-netboot/preseed.cfg +42 -0
  47. data/templates/Fedora-15-i386-netboot/definition.rb +17 -0
  48. data/templates/Fedora-15-i386-netboot/ks.cfg +82 -0
  49. data/templates/Fedora-15-i386-netboot/postinstall.sh +18 -0
  50. data/templates/Fedora-15-i386/definition.rb +17 -0
  51. data/templates/Fedora-15-i386/ks.cfg +64 -0
  52. data/templates/Fedora-15-i386/postinstall.sh +33 -0
  53. data/templates/Fedora-15-x86_64-netboot/definition.rb +29 -0
  54. data/templates/Fedora-15-x86_64-netboot/ks.cfg +64 -0
  55. data/templates/Fedora-15-x86_64-netboot/postinstall.sh +33 -0
  56. data/templates/Fedora-15-x86_64/definition.rb +17 -0
  57. data/templates/Fedora-15-x86_64/ks.cfg +64 -0
  58. data/templates/Fedora-15-x86_64/postinstall.sh +33 -0
  59. data/templates/Sysrescuecd-2.0.0-experimental/autorun0 +3 -0
  60. data/templates/Sysrescuecd-2.0.0-experimental/definition.rb +20 -0
  61. data/templates/archlinux-i386-netboot/aif.cfg +34 -0
  62. data/templates/archlinux-i386-netboot/definition.rb +29 -0
  63. data/templates/archlinux-i386-netboot/postinstall.sh +87 -0
  64. data/templates/archlinux-i386-netboot/postinstall2.sh +28 -0
  65. data/templates/archlinux-i386/aif.cfg +33 -0
  66. data/templates/archlinux-i386/definition.rb +29 -0
  67. data/templates/archlinux-i386/postinstall.sh +106 -0
  68. data/templates/archlinux-x86_64-netboot/aif.cfg +34 -0
  69. data/templates/archlinux-x86_64-netboot/definition.rb +29 -0
  70. data/templates/archlinux-x86_64-netboot/postinstall.sh +90 -0
  71. data/templates/archlinux-x86_64-netboot/postinstall2.sh +28 -0
  72. data/templates/archlinux-x86_64/aif.cfg +33 -0
  73. data/templates/archlinux-x86_64/definition.rb +29 -0
  74. data/templates/archlinux-x86_64/postinstall.sh +90 -0
  75. data/templates/archlinux-x86_64/postinstall2.sh +38 -0
  76. data/templates/freebsd-8.2-experimental/definition.rb +19 -0
  77. data/templates/freebsd-8.2-experimental/postinstall.sh +191 -0
  78. data/templates/freebsd-8.2-pcbsd-i386-netboot/definition.rb +35 -0
  79. data/templates/freebsd-8.2-pcbsd-i386-netboot/pcinstall.fbg.cfg +58 -0
  80. data/templates/freebsd-8.2-pcbsd-i386-netboot/postinstall.sh +93 -0
  81. data/templates/freebsd-8.2-pcbsd-i386/definition.rb +31 -0
  82. data/templates/freebsd-8.2-pcbsd-i386/pcinstall.fbg.cfg +57 -0
  83. data/templates/freebsd-8.2-pcbsd-i386/postinstall.sh +93 -0
  84. data/templates/gentoo-latest-i386-experimental/definition.rb +29 -0
  85. data/templates/gentoo-latest-i386-experimental/postinstall.sh +184 -0
  86. data/templates/openSUSE-11.4-DVD-i586/autoinst_de.xml +1284 -0
  87. data/templates/openSUSE-11.4-DVD-i586/autoinst_en.xml +1284 -0
  88. data/templates/openSUSE-11.4-DVD-i586/definition.rb +28 -0
  89. data/templates/openSUSE-11.4-DVD-i586/postinstall.sh +43 -0
  90. data/templates/openSUSE-11.4-DVD-x86_64/autoinst_de.xml +1459 -0
  91. data/templates/openSUSE-11.4-DVD-x86_64/autoinst_en.xml +1459 -0
  92. data/templates/openSUSE-11.4-DVD-x86_64/definition.rb +28 -0
  93. data/templates/openSUSE-11.4-DVD-x86_64/postinstall.sh +43 -0
  94. data/templates/openSUSE-11.4-NET-i586/autoinst_de.xml +1278 -0
  95. data/templates/openSUSE-11.4-NET-i586/autoinst_en.xml +1278 -0
  96. data/templates/openSUSE-11.4-NET-i586/definition.rb +28 -0
  97. data/templates/openSUSE-11.4-NET-i586/postinstall.sh +43 -0
  98. data/templates/openSUSE-11.4-NET-x86_64/autoinst_de.xml +1453 -0
  99. data/templates/openSUSE-11.4-NET-x86_64/autoinst_en.xml +1453 -0
  100. data/templates/openSUSE-11.4-NET-x86_64/definition.rb +28 -0
  101. data/templates/openSUSE-11.4-NET-x86_64/postinstall.sh +43 -0
  102. data/templates/solaris-11-express-i386/auto_install/ai.dtd +58 -0
  103. data/templates/solaris-11-express-i386/auto_install/ai_manifest.xml +241 -0
  104. data/templates/solaris-11-express-i386/auto_install/configuration.dtd +44 -0
  105. data/templates/solaris-11-express-i386/auto_install/default.xml +124 -0
  106. data/templates/solaris-11-express-i386/auto_install/default.xml.orig +124 -0
  107. data/templates/solaris-11-express-i386/auto_install/sc_profiles/static_network.xml +105 -0
  108. data/templates/solaris-11-express-i386/auto_install/software.dtd +105 -0
  109. data/templates/solaris-11-express-i386/auto_install/target.dtd +196 -0
  110. data/templates/solaris-11-express-i386/default.xml +121 -0
  111. data/templates/solaris-11-express-i386/definition.rb +65 -0
  112. data/templates/solaris-11-express-i386/postinstall.sh +98 -0
  113. data/templates/ubuntu-10.04.3-server-amd64/definition.rb +54 -0
  114. data/templates/ubuntu-10.04.3-server-amd64/postinstall.sh +90 -0
  115. data/templates/ubuntu-10.04.3-server-amd64/preseed.cfg +87 -0
  116. data/templates/ubuntu-10.04.3-server-i386/definition.rb +24 -0
  117. data/templates/ubuntu-10.04.3-server-i386/postinstall.sh +91 -0
  118. data/templates/ubuntu-10.04.3-server-i386/preseed.cfg +87 -0
  119. data/templates/ubuntu-11.10-server-amd64/definition.rb +35 -0
  120. data/templates/ubuntu-11.10-server-amd64/postinstall.sh +90 -0
  121. data/templates/ubuntu-11.10-server-amd64/preseed.cfg +87 -0
  122. data/templates/ubuntu-11.10-server-i386/definition.rb +35 -0
  123. data/templates/ubuntu-11.10-server-i386/postinstall.sh +90 -0
  124. data/templates/ubuntu-11.10-server-i386/preseed.cfg +87 -0
  125. data/validation/features/steps/ssh_steps.rb +169 -0
  126. data/validation/support/env.rb +1 -0
  127. data/validation/vagrant-private.key +27 -0
  128. data/validation/vagrant.feature +52 -0
  129. data/validation/vagrant.pub +1 -0
  130. data/vbox.gemspec +32 -0
  131. metadata +338 -0
@@ -0,0 +1,5 @@
1
+ module Vbox
2
+ class BaseBoxConfig < Vagrant::Config::Base
3
+ configures :vbox
4
+ end
5
+ end
@@ -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
@@ -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