veewee 0.0.2

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 (49) hide show
  1. data/.gitignore +7 -0
  2. data/.rvmrc +2 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +53 -0
  5. data/README.md +124 -0
  6. data/Rakefile +92 -0
  7. data/bin/veewee +79 -0
  8. data/iso/.gitignore +2 -0
  9. data/lib/vagrant_init.rb +1 -0
  10. data/lib/veewee/command.rb +66 -0
  11. data/lib/veewee/config.rb +5 -0
  12. data/lib/veewee/export.rb +81 -0
  13. data/lib/veewee/scancode.rb +108 -0
  14. data/lib/veewee/session.rb +712 -0
  15. data/lib/veewee/shell.rb +54 -0
  16. data/lib/veewee/ssh.rb +164 -0
  17. data/lib/veewee/transaction.rb +112 -0
  18. data/lib/veewee/version.rb +3 -0
  19. data/lib/veewee/web.rb +48 -0
  20. data/lib/veewee.rb +2 -0
  21. data/templates/CentOS-4.8-i386/definition.rb +14 -0
  22. data/templates/CentOS-4.8-i386/ks.cfg +45 -0
  23. data/templates/CentOS-4.8-i386/postinstall.sh +47 -0
  24. data/templates/CentOS-5.5-i386/definition.rb +16 -0
  25. data/templates/CentOS-5.5-i386/ks.cfg +45 -0
  26. data/templates/CentOS-5.5-i386/postinstall.sh +48 -0
  27. data/templates/CentOS-5.5-i386-netboot/definition.rb +17 -0
  28. data/templates/CentOS-5.5-i386-netboot/ks.cfg +45 -0
  29. data/templates/CentOS-5.5-i386-netboot/postinstall.sh +48 -0
  30. data/templates/ubuntu-10.04.1-server-amd64/definition.rb +25 -0
  31. data/templates/ubuntu-10.04.1-server-amd64/postinstall.sh +33 -0
  32. data/templates/ubuntu-10.04.1-server-amd64/preseed.cfg +87 -0
  33. data/templates/ubuntu-10.04.1-server-i386/definition.rb +25 -0
  34. data/templates/ubuntu-10.04.1-server-i386/postinstall.sh +43 -0
  35. data/templates/ubuntu-10.04.1-server-i386/postinstall2.sh +9 -0
  36. data/templates/ubuntu-10.04.1-server-i386/preseed.cfg +87 -0
  37. data/templates/ubuntu-10.10-server-amd64/definition.rb +25 -0
  38. data/templates/ubuntu-10.10-server-amd64/postinstall.sh +43 -0
  39. data/templates/ubuntu-10.10-server-amd64/postinstall2.sh +9 -0
  40. data/templates/ubuntu-10.10-server-amd64/preseed.cfg +87 -0
  41. data/templates/ubuntu-10.10-server-i386/definition.rb +25 -0
  42. data/templates/ubuntu-10.10-server-i386/postinstall.sh +43 -0
  43. data/templates/ubuntu-10.10-server-i386/postinstall2.sh +9 -0
  44. data/templates/ubuntu-10.10-server-i386/preseed.cfg +87 -0
  45. data/trials/docu-vbox.txt +83 -0
  46. data/trials/f.rb +29 -0
  47. data/trials/t.rb +15 -0
  48. data/veewee.gemspec +30 -0
  49. metadata +219 -0
@@ -0,0 +1,712 @@
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
+
10
+
11
+ module Veewee
12
+ class Session
13
+
14
+ attr_accessor :veewee_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
+ @veewee_dir=env[:veewee_dir]
23
+ @definition_dir=env[:definition_dir]
24
+ @template_dir=env[:template_dir]
25
+ @box_dir=env[:box_dir]
26
+ @iso_dir=env[:iso_dir]
27
+ @tmp_dir=env[:tmp_dir]
28
+ end
29
+
30
+ def self.declare(options)
31
+ defaults={
32
+ :cpu_count => '1', :memory_size=> '256',
33
+ :disk_size => '10140', :disk_format => 'VDI',:disk_size => '10240' ,
34
+ :os_type_id => 'Ubuntu',
35
+ :iso_file => "ubuntu-10.10-server-i386.iso", :iso_src => "", :iso_md5 => "", :iso_download_timeout => 1000,
36
+ :boot_wait => "10", :boot_cmd_sequence => [ "boot"],
37
+ :kickstart_port => "7122", :kickstart_ip => self.local_ip, :kickstart_timeout => 10000,:kickstart_file => "preseed.cfg",
38
+ :ssh_login_timeout => "100",:ssh_user => "vagrant", :ssh_password => "vagrant",:ssh_key => "",
39
+ :ssh_host_port => "2222", :ssh_guest_port => "22",
40
+ :sudo_cmd => "echo '%p'|sudo -S sh '%f'",
41
+ :shutdown_cmd => "shutdown -H",
42
+ :postinstall_files => [ "postinstall.sh"],:postinstall_timeout => 10000}
43
+
44
+ @definition=defaults.merge(options)
45
+
46
+ end
47
+
48
+ def self.define(boxname,template_name,options = {})
49
+ #Check if template_name exists
50
+
51
+ options = { "force" => false, "format" => "vagrant" }.merge(options)
52
+
53
+ if File.directory?(File.join(@template_dir,template_name))
54
+ else
55
+ puts "This template can not be found, use vagrant basebox templates to list all templates"
56
+ exit
57
+ end
58
+ if !File.exists?(@definition_dir)
59
+ FileUtils.mkdir(@definition_dir)
60
+ end
61
+
62
+ if File.directory?(File.join(@definition_dir,boxname))
63
+ if !options["force"]
64
+ puts "The definition for #{boxname} already exists. Use --force to overwrite"
65
+ exit
66
+ end
67
+ else
68
+ FileUtils.mkdir(File.join(@definition_dir,boxname))
69
+ end
70
+ FileUtils.cp_r(File.join(@template_dir,template_name,'.'),File.join(@definition_dir,boxname))
71
+ puts "The basebox '#{boxname}' has been succesfully created from the template ''#{template_name}'"
72
+ puts "You can now edit the definition files stored in definitions/#{boxname}"
73
+ puts "or build the box with:"
74
+ if (options["format"]=='vagrant')
75
+ puts "vagrant basebox build '#{boxname}'"
76
+ end
77
+ if (options["format"]=='veewee')
78
+ puts "veewee build '#{boxname}'"
79
+ end
80
+
81
+ end
82
+
83
+
84
+
85
+
86
+ def self.definition_exists?(boxname)
87
+ if File.directory?(File.join(@definition_dir,boxname))
88
+ if File.exists?(File.join(@definition_dir,boxname,'definition.rb'))
89
+ return true
90
+ else
91
+ return false
92
+ end
93
+ else
94
+ return false
95
+ end
96
+
97
+ end
98
+
99
+ def self.undefine(boxname)
100
+ name_dir=File.join(@definition_dir,boxname)
101
+ if File.directory?(name_dir)
102
+ #TODO: Needs to be more defensive!!
103
+ FileUtils.rm_rf(name_dir)
104
+ else
105
+ puts "Can not undefine , definition #{boxname} does not exist"
106
+ exit
107
+ end
108
+ end
109
+
110
+ def self.list_templates( options = { :format => 'vagrant'})
111
+ puts "The following templates are available:"
112
+ subdirs=Dir.glob("#{@template_dir}/*")
113
+ subdirs.each do |sub|
114
+ if File.directory?("#{sub}")
115
+ definition=Dir.glob("#{sub}/definition.rb")
116
+ if definition.length!=0
117
+ name=sub.sub(/#{@template_dir}\//,'')
118
+ if (options[:format]=='vagrant')
119
+ puts "vagrant basebox define '<boxname>' '#{name}'"
120
+ end
121
+ if (options[:format]=='veewee')
122
+ puts "veewee define '<boxname>' '#{name}'"
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def self.list_boxes
130
+ puts "Not yet implemented"
131
+ end
132
+
133
+ def self.list_definitions
134
+ puts "Not yet implemented"
135
+ end
136
+
137
+ def self.clean
138
+ puts "Not yet implemented"
139
+ end
140
+
141
+ def self.verify_iso(filename,autodownload = false)
142
+ if File.exists?(File.join(@iso_dir,filename))
143
+ puts
144
+ puts "Verifying the isofile #{filename} is ok."
145
+ else
146
+ full_path=File.join(@iso_dir,filename)
147
+ path1=Pathname.new(full_path)
148
+ path2=Pathname.new(Dir.pwd)
149
+ rel_path=path1.relative_path_from(path2).to_s
150
+
151
+ puts
152
+ puts "The isofile is not found. The definition provided the following information:"
153
+ puts "- Download url: #{@definition[:iso_src]}"
154
+ puts "- Md5 Checksum: #{@definition[:iso_md5]}"
155
+ puts ""
156
+
157
+ question=ask("Download? (Yes/No)") {|q| q.default="No"}
158
+ if question.downcase == "yes"
159
+ download_progress(@definition[:iso_src],full_path)
160
+ else
161
+ puts "You have choosen for manual download: "
162
+ puts "curl -C - -L '#{@definition[:iso_src]}' -o '#{rel_path}'"
163
+ puts "md5 '#{rel_path}' "
164
+ puts
165
+ exit
166
+ end
167
+
168
+
169
+ end
170
+
171
+ end
172
+
173
+ def self.export_box(boxname)
174
+ #Now we have to load the definition (reads definition.rb)
175
+ load_definition(boxname)
176
+
177
+ Veewee::Export.vagrant(boxname,@box_dir,@definition)
178
+ end
179
+
180
+ def self.remove_box(boxname)
181
+ puts "Not yet implemented"
182
+ end
183
+
184
+ def self.build(boxname,options)
185
+
186
+ options = { "force" => false, "format" => "vagrant" }.merge(options)
187
+
188
+ #Now we have to load the definition (reads definition.rb)
189
+ load_definition(boxname)
190
+
191
+ #Command to execute locally
192
+ @vboxcmd=determine_vboxcmd
193
+
194
+ ssh_options={ :user => @definition[:ssh_user], :port => @definition[:ssh_host_port], :password => @definition[:ssh_password],
195
+ :timeout => @definition[:ssh_timeout]}
196
+
197
+ #Suppress those annoying virtualbox messages
198
+ suppress_messages
199
+
200
+ verify_iso(@definition[:iso_file])
201
+
202
+ if (options["force"]==false)
203
+ else
204
+ puts "Forcing build by destroying #{boxname} machine"
205
+ destroy_vm(boxname)
206
+ end
207
+
208
+ checksums=calculate_checksums(@definition,boxname)
209
+
210
+ transaction(boxname,"0-initial-#{checksums[0]}",checksums) do
211
+
212
+ #Create the Virtualmachine and set all the memory and other stuff
213
+ create_vm(boxname)
214
+
215
+ #Create a disk with the same name as the boxname
216
+ create_disk(boxname)
217
+
218
+
219
+ #These command actually call the commandline of Virtualbox, I hope to use the virtualbox-ruby library in the future
220
+ add_ide_controller(boxname)
221
+ add_sata_controller(boxname)
222
+ attach_disk(boxname)
223
+ mount_isofile(boxname,@definition[:iso_file])
224
+ add_ssh_nat_mapping(boxname)
225
+
226
+ #Starting machine
227
+ #vm.start("vrdp")
228
+
229
+ start_vm(boxname,"gui")
230
+
231
+
232
+ #waiting for it to boot
233
+ puts "Waiting for the machine to boot"
234
+ sleep @definition[:boot_wait].to_i
235
+
236
+ Veewee::Scancode.send_sequence("#{@vboxcmd}","#{boxname}",@definition[:boot_cmd_sequence])
237
+
238
+ puts "Starting a webserver on port #{@definition[:kickstart_port]}"
239
+ #:kickstart_port => "7122", :kickstart_ip => self.local_ip, :kickstart_timeout => 1000,:kickstart_file => "preseed.cfg",
240
+ Veewee::Web.wait_for_request(@definition[:kickstart_file],{:port => @definition[:kickstart_port],
241
+ :host => @definition[:kickstart_ip], :timeout => @definition[:kickstart_timeout],
242
+ :web_dir => File.join(@definition_dir,boxname)})
243
+
244
+ Veewee::Ssh.when_ssh_login_works("localhost",ssh_options) do
245
+ #Transfer version of Virtualbox to $HOME/.vbox_version
246
+ versionfile=Tempfile.open("vbox.version")
247
+ versionfile.puts "#{VirtualBox::Global.global.lib.virtualbox.version}"
248
+ versionfile.rewind
249
+ Veewee::Ssh.transfer_file("localhost",versionfile.path,".vbox_version", ssh_options)
250
+ versionfile.close
251
+ versionfile.delete
252
+ end
253
+ end #initial Transaction
254
+
255
+
256
+ counter=1
257
+ @definition[:postinstall_files].each do |postinstall_file|
258
+
259
+
260
+ filename=File.join(@definition_dir,boxname,postinstall_file)
261
+
262
+ transaction(boxname,"#{counter}-#{postinstall_file}-#{checksums[counter]}",checksums) do
263
+
264
+ Veewee::Ssh.when_ssh_login_works("localhost",ssh_options) do
265
+
266
+ Veewee::Ssh.transfer_file("localhost",filename,ssh_options)
267
+ command=@definition[:sudo_cmd]
268
+ command.gsub!(/%p/,"#{@definition[:ssh_password]}")
269
+ command.gsub!(/%u/,"#{@definition[:ssh_user]}")
270
+ command.gsub!(/%f/,"#{postinstall_file}")
271
+
272
+ Veewee::Ssh.execute("localhost","#{command}",ssh_options)
273
+ end
274
+
275
+ end
276
+ counter+=1
277
+
278
+ end
279
+
280
+
281
+ end
282
+
283
+
284
+ def self.determine_vboxcmd
285
+ return "VBoxManage"
286
+ end
287
+
288
+ def self.start_vm(boxname,mode)
289
+ vm=VirtualBox::VM.find(boxname)
290
+ vm.start(mode)
291
+ end
292
+
293
+ def self.load_definition(boxname)
294
+ if definition_exists?(boxname)
295
+ definition_file=File.join(@definition_dir,boxname,"definition.rb")
296
+ begin
297
+ require definition_file
298
+ rescue LoadError
299
+ puts "Error loading definition of #{boxname}"
300
+ exit
301
+ end
302
+ end
303
+ end
304
+
305
+ def self.add_ssh_nat_mapping(boxname)
306
+ vm=VirtualBox::VM.find(boxname)
307
+ #Map SSH Ports
308
+ # command => "${vboxcmd} modifyvm '${vname}' --natpf1 'guestssh,tcp,,${hostsshport},,${guestsshport}'",
309
+ port = VirtualBox::NATForwardedPort.new
310
+ port.name = "guestssh"
311
+ port.guestport = @definition[:ssh_guest_port].to_i
312
+ port.hostport = @definition[:ssh_host_port].to_i
313
+ vm.network_adapters[0].nat_driver.forwarded_ports << port
314
+ port.save
315
+ vm.save
316
+ end
317
+
318
+ def self.destroy_vm(boxname)
319
+
320
+ load_definition(boxname)
321
+
322
+ #:destroy_medium => :delete, will delete machine + all media attachments
323
+ #vm.destroy(:destroy_medium => :delete)
324
+ ##vm.destroy(:destroy_image => true)
325
+
326
+ #VBoxManage unregistervm "test-machine" --delete
327
+ #because the destroy does remove the .vbox file on 4.0.x
328
+ #PDB
329
+ #vm.destroy()
330
+
331
+
332
+
333
+ vm=VirtualBox::VM.find(boxname)
334
+
335
+ if (!vm.nil? && !(vm.powered_off?))
336
+ puts "Shutting down vm #{boxname}"
337
+ #We force it here, maybe vm.shutdown is cleaner
338
+ vm.stop
339
+ end
340
+ sleep 3
341
+
342
+ command="#{@vboxcmd} unregistervm '#{boxname}' --delete"
343
+
344
+ puts "Deleting vm #{boxname}"
345
+
346
+ #Exec and system stop the execution here
347
+ Veewee::Shell.execute("#{command}")
348
+ sleep 1
349
+
350
+ #if the disk was not attached when the machine was destroyed we also need to delete the disk
351
+ location=boxname+"."+@definition[:disk_format].downcase
352
+ found=false
353
+ VirtualBox::HardDrive.all.each do |d|
354
+ if !d.location.match(/#{location}/).nil?
355
+
356
+ command="#{@vboxcmd} closemedium disk '#{d.location}' --delete"
357
+ puts "Deleting disk #{d.location}"
358
+ Veewee::Shell.execute("#{command}")
359
+ #v.3
360
+ #d.destroy(true)
361
+ break
362
+ end
363
+ end
364
+
365
+
366
+
367
+ end
368
+
369
+ def self.create_vm(boxname,force=false)
370
+
371
+ #Verifying the os.id with the :os_type_id specified
372
+ matchfound=false
373
+ VirtualBox::Global.global.lib.virtualbox.guest_os_types.collect { |os|
374
+ if @definition[:os_type_id] == os.id
375
+ matchfound=true
376
+ end
377
+ }
378
+ unless matchfound
379
+ puts "The ostype: #{@definition[:os_type_id]} is not available in your Virtualbox version"
380
+ exit
381
+ end
382
+
383
+
384
+ vm=VirtualBox::VM.find(boxname)
385
+
386
+ if (!vm.nil? && !(vm.powered_off?))
387
+ puts "shutting down box"
388
+ #We force it here, maybe vm.shutdown is cleaner
389
+ vm.stop
390
+ end
391
+
392
+ if !vm.nil?
393
+ puts "Box already exists"
394
+ #vm.stop
395
+ #vm.destroy
396
+ else
397
+ #TODO One day ruby-virtualbox will be able to handle this creation
398
+ #Box does not exist, we can start to create it
399
+
400
+ command="#{@vboxcmd} createvm --name '#{boxname}' --ostype '#{@definition[:os_type_id]}' --register"
401
+
402
+ #Exec and system stop the execution here
403
+ Veewee::Shell.execute("#{command}")
404
+
405
+ end
406
+
407
+ vm=VirtualBox::VM.find(boxname)
408
+ if vm.nil?
409
+ puts "we tried to create a box or a box was here before"
410
+ puts "but now it's gone"
411
+ exit
412
+ end
413
+
414
+ #Set all params we know
415
+ vm.memory_size=@definition[:memory_size].to_i
416
+ vm.os_type_id=@definition[:os_type_id]
417
+ vm.cpu_count=@definition[:cpu_count].to_i
418
+ vm.name=boxname
419
+
420
+ puts "Creating vm #{vm.name} : #{vm.memory_size}M - #{vm.cpu_count} CPU - #{vm.os_type_id}"
421
+ #setting bootorder
422
+ vm.boot_order[0]=:hard_disk
423
+ vm.boot_order[1]=:dvd
424
+ vm.boot_order[2]=:null
425
+ vm.boot_order[3]=:null
426
+ vm.validate
427
+ vm.save
428
+
429
+ end
430
+
431
+ def self.create_disk(boxname)
432
+ #Now check the disks
433
+ #Maybe one day we can use the name, now we have to check location
434
+ #disk=VirtualBox::HardDrive.find(boxname)
435
+ location=boxname+"."+@definition[:disk_format].downcase
436
+
437
+ found=false
438
+ VirtualBox::HardDrive.all.each do |d|
439
+ if !d.location.match(/#{location}/).nil?
440
+ found=true
441
+ break
442
+ end
443
+ end
444
+
445
+ if !found
446
+ puts "Creating new harddrive of size #{@definition[:disk_size].to_i} "
447
+
448
+ #newdisk=VirtualBox::HardDrive.new
449
+ #newdisk.format=@definition[:disk_format]
450
+ #newdisk.logical_size=@definition[:disk_size].to_i
451
+
452
+ #newdisk.location=location
453
+ ##PDB: again problems with the virtualbox GEM
454
+ ##VirtualBox::Global.global.max_vdi_size=1000000
455
+ #newdisk.save
456
+
457
+ command="VBoxManage list systemproperties|grep '^Default machine'|cut -d ':' -f 2|sed -e 's/^[ ]*//'"
458
+ results=IO.popen("#{command}")
459
+ place=results.gets.chop
460
+ results.close
461
+
462
+ command ="#{@vboxcmd} createhd --filename '#{place}/#{boxname}/#{boxname}.#{@definition[:disk_format]}' --size '#{@definition[:disk_size].to_i}' --format #{@definition[:disk_format]} > /dev/null"
463
+ Veewee::Shell.execute("#{command}")
464
+
465
+ end
466
+
467
+ end
468
+
469
+ def self.add_ide_controller(boxname)
470
+ #unless => "${vboxcmd} showvminfo '${vname}' | grep 'IDE Controller' "
471
+ command ="#{@vboxcmd} storagectl '#{boxname}' --name 'IDE Controller' --add ide"
472
+ Veewee::Shell.execute("#{command}")
473
+ end
474
+
475
+ def self.add_sata_controller(boxname)
476
+ #unless => "${vboxcmd} showvminfo '${vname}' | grep 'SATA Controller' ";
477
+ command ="#{@vboxcmd} storagectl '#{boxname}' --name 'SATA Controller' --add sata"
478
+ Veewee::Shell.execute("#{command}")
479
+ end
480
+
481
+
482
+ def self.attach_disk(boxname)
483
+ location=boxname+"."+@definition[:disk_format].downcase
484
+
485
+ command="VBoxManage list systemproperties|grep '^Default machine'|cut -d ':' -f 2|sed -e 's/^[ ]*//'"
486
+ results=IO.popen("#{command}")
487
+ place=results.gets.chop
488
+ results.close
489
+
490
+ location="#{place}/#{boxname}/"+location
491
+ puts "Attaching disk: #{location}"
492
+
493
+ #command => "${vboxcmd} storageattach '${vname}' --storagectl 'SATA Controller' --port 0 --device 0 --type hdd --medium '${vname}.vdi'",
494
+ command ="#{@vboxcmd} storageattach '#{boxname}' --storagectl 'SATA Controller' --port 0 --device 0 --type hdd --medium '#{location}'"
495
+ Veewee::Shell.execute("#{command}")
496
+
497
+ end
498
+
499
+ def self.mount_isofile(boxname,isofile)
500
+ full_iso_file=File.join(@iso_dir,isofile)
501
+ puts "Mounting cdrom: #{full_iso_file}"
502
+ #command => "${vboxcmd} storageattach '${vname}' --storagectl 'IDE Controller' --type dvddrive --port 1 --device 0 --medium '${isodst}' ";
503
+ command ="#{@vboxcmd} storageattach '#{boxname}' --storagectl 'IDE Controller' --type dvddrive --port 1 --device 0 --medium '#{full_iso_file}'"
504
+ Veewee::Shell.execute("#{command}")
505
+ end
506
+
507
+
508
+
509
+ def self.suppress_messages
510
+ #Setting this annoying messages to register
511
+ VirtualBox::ExtraData.global["GUI/RegistrationData"]="triesLeft=0"
512
+ VirtualBox::ExtraData.global["GUI/UpdateDate"]="1 d, 2009-09-20"
513
+ VirtualBox::ExtraData.global["GUI/SuppressMessages"]="confirmInputCapture,remindAboutAutoCapture,remindAboutMouseIntegrationOff"
514
+ VirtualBox::ExtraData.global["GUI/UpdateCheckCount"]="60"
515
+ update_date=Time.now+86400
516
+ VirtualBox::ExtraData.global["GUI/UpdateDate"]="1 d, #{update_date.year}-#{update_date.month}-#{update_date.day}, stable"
517
+
518
+ VirtualBox::ExtraData.global.save
519
+ end
520
+
521
+ def self.local_ip
522
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
523
+
524
+ UDPSocket.open do |s|
525
+ s.connect '64.233.187.99', 1
526
+ s.addr.last
527
+ end
528
+ ensure
529
+ Socket.do_not_reverse_lookup = orig
530
+ end
531
+
532
+ def self.list_ostypes
533
+ puts
534
+ puts "Available os types:"
535
+ VirtualBox::Global.global.lib.virtualbox.guest_os_types.collect { |os|
536
+ puts "#{os.id}: #{os.description}"
537
+ }
538
+ end
539
+
540
+
541
+ def self.calculate_checksums(definition,boxname)
542
+
543
+ #TODO: get rid of definitiondir and so one
544
+ initial=definition.clone
545
+
546
+ keys=[:postinstall_files,:sudo_cmd,:postinstall_timeout]
547
+ keys.each do |key|
548
+ initial.delete(key)
549
+ end
550
+
551
+ checksums=Array.new
552
+ checksums << Digest::MD5.hexdigest(initial.to_s)
553
+
554
+ postinstall_files=definition[:postinstall_files]
555
+ unless postinstall_files.nil?
556
+ for filename in postinstall_files
557
+ begin
558
+ full_filename=File.join(@definition_dir,boxname,filename)
559
+
560
+ checksums << Digest::MD5.hexdigest(File.read(full_filename))
561
+ rescue
562
+ puts "Error reading postinstall file #{filename} - checksum"
563
+ exit
564
+ end
565
+ end
566
+ end
567
+
568
+ return checksums
569
+
570
+ end
571
+
572
+ def self.download_progress(url,localfile)
573
+ pbar = nil
574
+ URI.parse(url).open(
575
+ :content_length_proc => lambda {|t|
576
+ if t && 0 < t
577
+ pbar = ProgressBar.new("Fetching file", t)
578
+ pbar.file_transfer_mode
579
+ end
580
+ },
581
+ :progress_proc => lambda {|s|
582
+ pbar.set s if pbar
583
+ }) { |src|
584
+ open("#{localfile}","wb") { |dst|
585
+ dst.write(src.read)
586
+ }
587
+ }
588
+
589
+ end
590
+
591
+ def self.transaction(boxname,step_name,checksums,&block)
592
+
593
+ current_step_nr=step_name.split("-")[0].to_i
594
+
595
+ vm=VirtualBox::VM.find(boxname)
596
+ snapnames=Array.new
597
+
598
+ #If vm exists , look for snapshots
599
+ if !vm.nil?
600
+ start_snapshot=vm.root_snapshot
601
+ snapshot=start_snapshot
602
+ counter=0
603
+
604
+ while (snapshot!=nil)
605
+ #puts "#{counter}:#{snapshot.name}"
606
+ snapnames[counter]=snapshot.name
607
+ counter=counter+1
608
+ snapshot=snapshot.children[0]
609
+ end
610
+ end
611
+
612
+ #find the last snapshot matching the state
613
+ counter=[snapnames.length, checksums.length].min-1
614
+ last_good_state=counter
615
+ for c in 0..counter do
616
+ #puts "#{c}- #{snapnames[c]} - #{checksums[c]}"
617
+ if !snapnames[c].match("#{c}.*-#{checksums[c]}")
618
+ # puts "we found a bad state"
619
+ last_good_state=c-1
620
+ break
621
+ end
622
+ end
623
+ #puts "Last good state: #{last_good_state}"
624
+
625
+ if (current_step_nr < last_good_state)
626
+ #puts "fast forwarding #{step_name}"
627
+ return
628
+ end
629
+
630
+ #puts "Current step: #{current_step_nr}"
631
+ if (current_step_nr == last_good_state)
632
+ if vm.running?
633
+ vm.stop
634
+ end
635
+
636
+ #invalidate later snapshots
637
+ #puts "remove old snapshots"
638
+
639
+ for s in (last_good_state+1)..(snapnames.length-1)
640
+ puts "Removing step [s] snapshot as it is no more valid"
641
+ snapshot=vm.find_snapshot(snapnames[s])
642
+ snapshot.destroy
643
+ #puts snapshot
644
+ end
645
+
646
+ vm.reload
647
+ puts "Loading step #{current_step_nr} snapshots as it has not changed"
648
+ sleep 2
649
+ goodsnap=vm.find_snapshot(snapnames[last_good_state])
650
+ goodsnap.restore
651
+ sleep 2
652
+ #TODO:Restore snapshot!!!
653
+ vm.start
654
+
655
+ end
656
+
657
+ #puts "last good state #{last_good_state}"
658
+
659
+
660
+ if (current_step_nr > last_good_state)
661
+
662
+ if (last_good_state==-1)
663
+ #no initial snapshot is found, clean machine!
664
+ vm=VirtualBox::VM.find(boxname)
665
+
666
+ if !vm.nil?
667
+ if vm.running?
668
+ puts "Stopping machine"
669
+ vm.stop
670
+ while vm.running?
671
+ sleep 1
672
+ end
673
+ end
674
+
675
+ #detaching cdroms (used to work in 3.x)
676
+ # vm.medium_attachments.each do |m|
677
+ # if m.type==:dvd
678
+ # #puts "Detaching dvd"
679
+ # m.detach
680
+ # end
681
+ # end
682
+
683
+ vm.reload
684
+ puts "We found no good state so we are destroying the previous machine+disks"
685
+ destroy_vm(boxname)
686
+ end
687
+
688
+ end
689
+
690
+ #puts "(re-)executing step #{step_name}"
691
+
692
+
693
+ yield
694
+
695
+ #Need to look it up again because if it was an initial load
696
+ vm=VirtualBox::VM.find(boxname)
697
+ puts "Step [#{current_step_nr}] was succesfull - saving state"
698
+ vm.save_state
699
+ sleep 2 #waiting for it to be ok
700
+ #puts "about to snapshot #{vm}"
701
+ #take snapshot after succesful execution
702
+ vm.take_snapshot(step_name,"snapshot taken by veewee")
703
+ sleep 2 #waiting for it to be started again
704
+ vm.start
705
+ end
706
+
707
+ #pp snapnames
708
+ end
709
+
710
+
711
+ end #End Class
712
+ end #End Module