beaker 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +14 -0
  5. data/DOCUMENTING.md +167 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +17 -0
  8. data/README.md +332 -0
  9. data/Rakefile +121 -0
  10. data/beaker.gemspec +42 -0
  11. data/beaker.rb +10 -0
  12. data/bin/beaker +9 -0
  13. data/lib/beaker.rb +36 -0
  14. data/lib/beaker/answers.rb +29 -0
  15. data/lib/beaker/answers/version28.rb +104 -0
  16. data/lib/beaker/answers/version30.rb +194 -0
  17. data/lib/beaker/cli.rb +113 -0
  18. data/lib/beaker/command.rb +241 -0
  19. data/lib/beaker/command_factory.rb +21 -0
  20. data/lib/beaker/dsl.rb +85 -0
  21. data/lib/beaker/dsl/assertions.rb +87 -0
  22. data/lib/beaker/dsl/helpers.rb +625 -0
  23. data/lib/beaker/dsl/install_utils.rb +299 -0
  24. data/lib/beaker/dsl/outcomes.rb +99 -0
  25. data/lib/beaker/dsl/roles.rb +97 -0
  26. data/lib/beaker/dsl/structure.rb +63 -0
  27. data/lib/beaker/dsl/wrappers.rb +100 -0
  28. data/lib/beaker/host.rb +193 -0
  29. data/lib/beaker/host/aix.rb +15 -0
  30. data/lib/beaker/host/aix/file.rb +16 -0
  31. data/lib/beaker/host/aix/group.rb +35 -0
  32. data/lib/beaker/host/aix/user.rb +32 -0
  33. data/lib/beaker/host/unix.rb +54 -0
  34. data/lib/beaker/host/unix/exec.rb +15 -0
  35. data/lib/beaker/host/unix/file.rb +16 -0
  36. data/lib/beaker/host/unix/group.rb +40 -0
  37. data/lib/beaker/host/unix/pkg.rb +22 -0
  38. data/lib/beaker/host/unix/user.rb +32 -0
  39. data/lib/beaker/host/windows.rb +44 -0
  40. data/lib/beaker/host/windows/exec.rb +18 -0
  41. data/lib/beaker/host/windows/file.rb +15 -0
  42. data/lib/beaker/host/windows/group.rb +36 -0
  43. data/lib/beaker/host/windows/pkg.rb +26 -0
  44. data/lib/beaker/host/windows/user.rb +32 -0
  45. data/lib/beaker/hypervisor.rb +37 -0
  46. data/lib/beaker/hypervisor/aixer.rb +52 -0
  47. data/lib/beaker/hypervisor/blimper.rb +123 -0
  48. data/lib/beaker/hypervisor/fusion.rb +56 -0
  49. data/lib/beaker/hypervisor/solaris.rb +65 -0
  50. data/lib/beaker/hypervisor/vagrant.rb +118 -0
  51. data/lib/beaker/hypervisor/vcloud.rb +175 -0
  52. data/lib/beaker/hypervisor/vsphere.rb +80 -0
  53. data/lib/beaker/hypervisor/vsphere_helper.rb +200 -0
  54. data/lib/beaker/logger.rb +167 -0
  55. data/lib/beaker/network_manager.rb +73 -0
  56. data/lib/beaker/options_parsing.rb +323 -0
  57. data/lib/beaker/result.rb +55 -0
  58. data/lib/beaker/shared.rb +15 -0
  59. data/lib/beaker/shared/error_handler.rb +17 -0
  60. data/lib/beaker/shared/host_handler.rb +46 -0
  61. data/lib/beaker/shared/repetition.rb +28 -0
  62. data/lib/beaker/ssh_connection.rb +198 -0
  63. data/lib/beaker/test_case.rb +225 -0
  64. data/lib/beaker/test_config.rb +148 -0
  65. data/lib/beaker/test_suite.rb +288 -0
  66. data/lib/beaker/utils.rb +7 -0
  67. data/lib/beaker/utils/ntp_control.rb +42 -0
  68. data/lib/beaker/utils/repo_control.rb +92 -0
  69. data/lib/beaker/utils/setup_helper.rb +77 -0
  70. data/lib/beaker/utils/validator.rb +27 -0
  71. data/spec/beaker/command_spec.rb +94 -0
  72. data/spec/beaker/dsl/assertions_spec.rb +104 -0
  73. data/spec/beaker/dsl/helpers_spec.rb +230 -0
  74. data/spec/beaker/dsl/install_utils_spec.rb +70 -0
  75. data/spec/beaker/dsl/outcomes_spec.rb +43 -0
  76. data/spec/beaker/dsl/roles_spec.rb +86 -0
  77. data/spec/beaker/dsl/structure_spec.rb +60 -0
  78. data/spec/beaker/dsl/wrappers_spec.rb +52 -0
  79. data/spec/beaker/host_spec.rb +95 -0
  80. data/spec/beaker/logger_spec.rb +117 -0
  81. data/spec/beaker/options_parsing_spec.rb +37 -0
  82. data/spec/beaker/puppet_command_spec.rb +128 -0
  83. data/spec/beaker/ssh_connection_spec.rb +39 -0
  84. data/spec/beaker/test_case_spec.rb +6 -0
  85. data/spec/beaker/test_suite_spec.rb +44 -0
  86. data/spec/mocks_and_helpers.rb +34 -0
  87. data/spec/spec_helper.rb +15 -0
  88. metadata +359 -0
@@ -0,0 +1,123 @@
1
+ module Beaker
2
+ class Blimper < Beaker::Hypervisor
3
+
4
+ def amiports(host)
5
+ roles = host['roles']
6
+ ports = [22]
7
+
8
+ if roles.include? 'database'
9
+ ports << 8080
10
+ ports << 8081
11
+ end
12
+
13
+ if roles.include? 'master'
14
+ ports << 8140
15
+ end
16
+
17
+ if roles.include? 'dashboard'
18
+ ports << 443
19
+ end
20
+
21
+ ports
22
+ end
23
+
24
+ def initialize(blimpy_hosts, options, config)
25
+ @options = options
26
+ @config = config['CONFIG'].dup
27
+ @logger = options[:logger]
28
+ @blimpy_hosts = blimpy_hosts
29
+ require 'rubygems' unless defined?(Gem)
30
+ require 'yaml' unless defined?(YAML)
31
+ begin
32
+ require 'blimpy'
33
+ rescue LoadError
34
+ raise "Unable to load Blimpy, please ensure its installed"
35
+ end
36
+ ami_spec= YAML.load_file('config/image_templates/ec2.yaml')["AMI"]
37
+
38
+ fleet = Blimpy.fleet do |fleet|
39
+ @blimpy_hosts.each do |host|
40
+ amitype = host['vmname'] || host['platform']
41
+ amisize = host['amisize'] || 'm1.small'
42
+ #use snapshot provided for this host
43
+ image_type = host['snapshot']
44
+ if not image_type
45
+ raise "No snapshot/image_type provided for blimpy provisioning"
46
+ end
47
+ ami = ami_spec[amitype]
48
+ fleet.add(:aws) do |ship|
49
+ ship.name = host.name
50
+ ship.ports = amiports(host)
51
+ ship.image_id = ami[:image][image_type.to_sym]
52
+ if not ship.image_id
53
+ raise "No image_id found for host #{ship.name} (#{amitype}:#{amisize}) using snapshot/image_type #{image_type}"
54
+ end
55
+ ship.flavor = amisize
56
+ ship.region = ami[:region]
57
+ ship.username = 'root'
58
+ end
59
+ @logger.debug "Added #{host.name} (#{amitype}:#{amisize}) using snapshot/image_type #{image_type} to blimpy fleet"
60
+ end
61
+ end
62
+
63
+ # Attempt to start the fleet, we wrap it with some error handling that deals
64
+ # with generic Fog errors and retrying in case these errors are transient.
65
+ fleet_retries = 0
66
+ begin
67
+ fleet.start
68
+ rescue Fog::Errors::Error => ex
69
+ fleet_retries += 1
70
+ if fleet_retries <= 3
71
+ sleep_time = rand(10) + 10
72
+ @logger.notify("Calling fleet.destroy, sleeping #{sleep_time} seconds and retrying fleet.start due to Fog::Errors::Error (#{
73
+ ex.message}), retry attempt #{fleet_retries}.")
74
+ begin
75
+ timeout(30) do
76
+ fleet.destroy
77
+ end
78
+ rescue
79
+ end
80
+ sleep sleep_time
81
+ retry
82
+ else
83
+ @logger.error("Retried Fog #{fleet_retries} times, giving up and throwing the exception")
84
+ raise ex
85
+ end
86
+ end
87
+
88
+ # Configure our nodes to match the blimp fleet
89
+ # Also generate hosts entries for the fleet, since we're iterating
90
+ etc_hosts = "127.0.0.1\tlocalhost localhost.localdomain\n"
91
+ fleet.ships.each do |ship|
92
+ ship.wait_for_sshd
93
+ name = ship.name
94
+ host = @blimpy_hosts.select { |host| host.name == name }[0]
95
+ host['ip'] = ship.dns
96
+ host.exec(Command.new("hostname #{name}"))
97
+ ip = get_ip(host)
98
+ domain = get_domain_name(host)
99
+ etc_hosts += "#{ip}\t#{name}\t#{name}.#{domain}\n"
100
+ end
101
+
102
+ # Send our hosts information to the nodes
103
+ @blimpy_hosts.each do |host|
104
+ set_etc_hosts(host, etc_hosts)
105
+ end
106
+
107
+ end #revert_blimpy
108
+
109
+ def cleanup
110
+ fleet = Blimpy.fleet do |fleet|
111
+ @blimpy_hosts.each do |host|
112
+ fleet.add(:aws) do |ship|
113
+ ship.name = host.name
114
+ end
115
+ end
116
+ end
117
+
118
+ @logger.notify "Destroying Blimpy boxes"
119
+ fleet.destroy
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,56 @@
1
+ module Beaker
2
+ class Fusion < Beaker::Hypervisor
3
+
4
+ def initialize(fusion_hosts, options, config)
5
+ require 'rubygems' unless defined?(Gem)
6
+ begin
7
+ require 'fission'
8
+ rescue LoadError
9
+ raise "Unable to load fission, please ensure it is installed!"
10
+ end
11
+ @logger = options[:logger]
12
+ @options = options
13
+ @config = config['CONFIG'].dup
14
+ @fusion_hosts = fusion_hosts
15
+
16
+
17
+ available = Fission::VM.all.data.collect{|vm| vm.name}.sort.join(", ")
18
+ @logger.notify "Available VM names: #{available}"
19
+
20
+ @fusion_hosts.each do |host|
21
+ vm_name = host["vmname"] || host.name
22
+ vm = Fission::VM.new vm_name
23
+ raise "Could not find VM '#{vm_name}' for #{host.name}!" unless vm.exists?
24
+
25
+ available_snapshots = vm.snapshots.data.sort.join(", ")
26
+ @logger.notify "Available snapshots for #{host.name}: #{available_snapshots}"
27
+ snap_name = host["snapshot"]
28
+ raise "No snapshot specified for #{host.name}" unless snap_name
29
+ raise "Could not find snapshot '#{snap_name}' for host #{host.name}!" unless vm.snapshots.data.include? snap_name
30
+
31
+ @logger.notify "Reverting #{host.name} to snapshot '#{snap_name}'"
32
+ start = Time.now
33
+ vm.revert_to_snapshot snap_name
34
+ while vm.running?.data
35
+ sleep 1
36
+ end
37
+ time = Time.now - start
38
+ @logger.notify "Spent %.2f seconds reverting" % time
39
+
40
+ @logger.notify "Resuming #{host.name}"
41
+ start = Time.now
42
+ vm.start :headless => true
43
+ until vm.running?.data
44
+ sleep 1
45
+ end
46
+ time = Time.now - start
47
+ @logger.notify "Spent %.2f seconds resuming VM" % time
48
+ end
49
+ end #revert_fusion
50
+
51
+ def cleanup
52
+ @logger.notify "No cleanup for fusion boxes"
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,65 @@
1
+ module Beaker
2
+ class Solaris < Beaker::Hypervisor
3
+
4
+ def initialize(solaris_hosts, options, config)
5
+ @options = options
6
+ @config = config['CONFIG'].dup
7
+ @logger = options[:logger]
8
+ @solaris_hosts = solaris_hosts
9
+ fog_file = nil
10
+ if File.exists?( File.join(ENV['HOME'], '.fog') )
11
+ fog_file = YAML.load_file( File.join(ENV['HOME'], '.fog') )
12
+ end
13
+ raise "Cant load ~/.fog config" unless fog_file
14
+
15
+ hypername = fog_file[:default][:solaris_hypervisor_server]
16
+ vmpath = fog_file[:default][:solaris_hypervisor_vmpath]
17
+ snappaths = fog_file[:default][:solaris_hypervisor_snappaths]
18
+
19
+ hyperconf = {
20
+ 'HOSTS' => {
21
+ hypername => { 'platform' => 'solaris-11-sparc' }
22
+ },
23
+ 'CONFIG' => {
24
+ 'user' => fog_file[:default][:solaris_hypervisor_username] || ENV['USER'],
25
+ 'ssh' => {
26
+ :keys => fog_file[:default][:solaris_hypervisor_keyfile] || "#{ENV['HOME']}/.ssh/id_rsa"
27
+ }
28
+ }
29
+ }
30
+
31
+ hyperconfig = Beaker::TestConfig.new( hyperconf, @options )
32
+
33
+ @logger.notify "Connecting to hypervisor at #{hypername}"
34
+ hypervisor = Beaker::Host.create( hypername, @options, hyperconfig )
35
+
36
+ @solaris_hosts.each do |host|
37
+ vm_name = host['vmname'] || host.name
38
+ #use the snapshot provided for this host
39
+ snapshot = host['snapshot']
40
+
41
+
42
+ @logger.notify "Reverting #{vm_name} to snapshot #{snapshot}"
43
+ start = Time.now
44
+ hypervisor.exec(Command.new("sudo /sbin/zfs rollback -Rf #{vmpath}/#{vm_name}@#{snapshot}"))
45
+ snappaths.each do |spath|
46
+ @logger.notify "Reverting #{vm_name}/#{spath} to snapshot #{snapshot}"
47
+ hypervisor.exec(Command.new("sudo /sbin/zfs rollback -Rf #{vmpath}/#{vm_name}/#{spath}@#{snapshot}"))
48
+ end
49
+ time = Time.now - start
50
+ @logger.notify "Spent %.2f seconds reverting" % time
51
+
52
+ @logger.notify "Booting #{vm_name}"
53
+ start = Time.now
54
+ hypervisor.exec(Command.new("sudo /sbin/zoneadm -z #{vm_name} boot"))
55
+ @logger.notify "Spent %.2f seconds booting #{vm_name}" % (Time.now - start)
56
+ end
57
+ hypervisor.close
58
+ end
59
+
60
+ def cleanup
61
+ @logger.notify "No cleanup for solaris boxes"
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,118 @@
1
+ module Beaker
2
+ class Vagrant < Beaker::Hypervisor
3
+
4
+ # Return a random mac address
5
+ #
6
+ # @return [String] a random mac address
7
+ def randmac
8
+ "080027" + (1..3).map{"%0.2X"%rand(256)}.join
9
+ end
10
+
11
+ def rand_chunk
12
+ (1 + rand(253)).to_s #don't want a 0 or a 255
13
+ end
14
+
15
+ def randip
16
+ "192.168.#{rand_chunk}.#{rand_chunk}"
17
+ end
18
+
19
+ def make_vfile hosts
20
+ #HACK HACK HACK - add checks here to ensure that we have box + box_url
21
+ #generate the VagrantFile
22
+ vagrant_file = "Vagrant.configure(\"2\") do |c|\n"
23
+ hosts.each do |host|
24
+ host['ip'] ||= randip #use the existing ip, otherwise default to a random ip
25
+ vagrant_file << " c.vm.define '#{host.name}' do |v|\n"
26
+ vagrant_file << " v.vm.hostname = '#{host.name}'\n"
27
+ vagrant_file << " v.vm.box = '#{host['box']}'\n"
28
+ vagrant_file << " v.vm.box_url = '#{host['box_url']}'\n" unless host['box_url'].nil?
29
+ vagrant_file << " v.vm.base_mac = '#{randmac}'\n"
30
+ vagrant_file << " v.vm.network :private_network, ip: \"#{host['ip'].to_s}\"\n"
31
+ vagrant_file << " end\n"
32
+ @logger.debug "created Vagrantfile for VagrantHost #{host.name}"
33
+ end
34
+ vagrant_file << " c.vm.provider :virtualbox do |vb|\n"
35
+ vagrant_file << " vb.customize [\"modifyvm\", :id, \"--memory\", \"1024\"]\n"
36
+ vagrant_file << " end\n"
37
+ vagrant_file << "end\n"
38
+ f = File.open("Vagrantfile", 'w')
39
+ f.write(vagrant_file)
40
+ f.close()
41
+ end
42
+
43
+ def hack_etc_hosts hosts
44
+ etc_hosts = "127.0.0.1\tlocalhost localhost.localdomain\n"
45
+ hosts.each do |host|
46
+ etc_hosts += "#{host['ip'].to_s}\t#{host.name}\n"
47
+ end
48
+ hosts.each do |host|
49
+ set_etc_hosts(host, etc_hosts)
50
+ end
51
+ end
52
+
53
+ def copy_ssh_to_root host
54
+ #make is possible to log in as root by copying the ssh dir to root's account
55
+ @logger.debug "Give root a copy of vagrant's keys"
56
+ if host['platform'] =~ /windows/
57
+ host.exec(Command.new('sudo su -c "cp -r .ssh /home/Administrator/."'))
58
+ else
59
+ host.exec(Command.new('sudo su -c "cp -r .ssh /root/."'))
60
+ end
61
+ end
62
+
63
+ def set_ssh_config host, user
64
+ f = Tempfile.new("#{host.name}")
65
+ ssh_config = `vagrant ssh-config #{host.name}`
66
+ #replace hostname with ip
67
+ ssh_config = ssh_config.gsub(/#{host.name}/, host['ip'])
68
+ #set the user
69
+ ssh_config = ssh_config.gsub(/User vagrant/, "User #{user}")
70
+ f.write(ssh_config)
71
+ f.rewind
72
+ host['ssh'] = {:config => f.path()}
73
+ host['user'] = user
74
+ @temp_files << f
75
+ end
76
+
77
+ def initialize(vagrant_hosts, options, config)
78
+ require 'tempfile'
79
+ @options = options
80
+ @config = config['CONFIG'].dup
81
+ @logger = options[:logger]
82
+ @temp_files = []
83
+ @vagrant_hosts = vagrant_hosts
84
+
85
+ make_vfile @vagrant_hosts
86
+
87
+ #stop anything currently running, that way vagrant up will re-do networking on existing boxes
88
+ system("vagrant halt")
89
+ system("vagrant up")
90
+
91
+ @logger.debug "configure vagrant boxes (set ssh-config, switch to root user, hack etc/hosts)"
92
+ @vagrant_hosts.each do |host|
93
+ default_user = host['user']
94
+
95
+ set_ssh_config host, 'vagrant'
96
+
97
+ copy_ssh_to_root host
98
+ #shut down connection, will reconnect on next exec
99
+ host.close
100
+
101
+ set_ssh_config host, default_user
102
+
103
+ end
104
+
105
+ hack_etc_hosts @vagrant_hosts
106
+ end
107
+
108
+ def cleanup
109
+ @logger.debug "removing temporory ssh-config files per-vagrant box"
110
+ @temp_files.each do |f|
111
+ f.close()
112
+ end
113
+ @logger.notify "Destroying vagrant boxes"
114
+ system("vagrant destroy --force")
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,175 @@
1
+ module Beaker
2
+ class Vcloud < Beaker::Hypervisor
3
+
4
+ def initialize(vcloud_hosts, options, config)
5
+ @options = options
6
+ @config = config['CONFIG'].dup
7
+ @logger = options[:logger]
8
+ @vcloud_hosts = vcloud_hosts
9
+ require 'yaml' unless defined?(YAML)
10
+
11
+ raise 'You must specify a datastore for vCloud instances!' unless @config['datastore']
12
+ raise 'You must specify a resource pool for vCloud instances!' unless @config['resourcepool']
13
+ raise 'You must specify a folder for vCloud instances!' unless @config['folder']
14
+
15
+ vsphere_credentials = VsphereHelper.load_config
16
+
17
+ @logger.notify "Connecting to vSphere at #{vsphere_credentials[:server]}" +
18
+ " with credentials for #{vsphere_credentials[:user]}"
19
+
20
+ vsphere_helper = VsphereHelper.new( vsphere_credentials )
21
+ vsphere_vms = {}
22
+
23
+ attempts = 10
24
+
25
+ start = Time.now
26
+ @vcloud_hosts.each_with_index do |h, i|
27
+ # Generate a randomized hostname
28
+ o = [('a'..'z'),('0'..'9')].map{|r| r.to_a}.flatten
29
+ h['vmhostname'] = o[rand(25)] + (0...14).map{o[rand(o.length)]}.join
30
+
31
+ if h['template'] =~ /\//
32
+ templatefolders = h['template'].split('/')
33
+ h['template'] = templatefolders.pop
34
+ end
35
+
36
+ @logger.notify "Deploying #{h['vmhostname']} (#{h.name}) to #{@config['folder']} from template '#{h['template']}'"
37
+
38
+ vm = {}
39
+
40
+ if templatefolders
41
+ vm[h['template']] = vsphere_helper.find_folder(templatefolders.join('/')).find(h['template'])
42
+ else
43
+ vm = vsphere_helper.find_vms(h['template'])
44
+ end
45
+
46
+ if vm.length == 0
47
+ raise "Unable to find template '#{h['template']}'!"
48
+ end
49
+
50
+ # Add VM annotation
51
+ configSpec = RbVmomi::VIM.VirtualMachineConfigSpec(
52
+ :annotation =>
53
+ 'Base template: ' + h['template'] + "\n" +
54
+ 'Creation time: ' + Time.now.strftime("%Y-%m-%d %H:%M") + "\n\n" +
55
+ 'CI build link: ' + ( ENV['BUILD_URL'] || 'Deployed independently of CI' )
56
+ )
57
+
58
+ # Are we using a customization spec?
59
+ customizationSpec = vsphere_helper.find_customization( h['template'] )
60
+
61
+ if customizationSpec
62
+ # Print a logger message if using a customization spec
63
+ @logger.notify "Found customization spec for '#{h['template']}', will apply after boot"
64
+
65
+ # Using a customization spec takes longer, set a longer timeout
66
+ attempts = attempts * 2
67
+ end
68
+
69
+ # Put the VM in the specified folder and resource pool
70
+ relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(
71
+ :datastore => vsphere_helper.find_datastore(@config['datastore']),
72
+ :pool => vsphere_helper.find_pool(@config['resourcepool']),
73
+ :diskMoveType => :moveChildMostDiskBacking
74
+ )
75
+
76
+ # Create a clone spec
77
+ spec = RbVmomi::VIM.VirtualMachineCloneSpec(
78
+ :config => configSpec,
79
+ :location => relocateSpec,
80
+ :customization => customizationSpec,
81
+ :powerOn => true,
82
+ :template => false
83
+ )
84
+
85
+ # Deploy from specified template
86
+ if (@vcloud_hosts.length == 1) or (i == @vcloud_hosts.length - 1)
87
+ vm[h['template']].CloneVM_Task( :folder => vsphere_helper.find_folder(@config['folder']), :name => h['vmhostname'], :spec => spec ).wait_for_completion
88
+ else
89
+ vm[h['template']].CloneVM_Task( :folder => vsphere_helper.find_folder(@config['folder']), :name => h['vmhostname'], :spec => spec )
90
+ end
91
+ end
92
+ @logger.notify 'Spent %.2f seconds deploying VMs' % (Time.now - start)
93
+
94
+ start = Time.now
95
+ @vcloud_hosts.each_with_index do |h, i|
96
+ @logger.notify "Booting #{h['vmhostname']} (#{h.name}) and waiting for it to register with vSphere"
97
+ try = 1
98
+ last_wait = 0
99
+ wait = 1
100
+ until
101
+ vsphere_helper.find_vms(h['vmhostname'])[h['vmhostname']].summary.guest.toolsRunningStatus == 'guestToolsRunning' and
102
+ vsphere_helper.find_vms(h['vmhostname'])[h['vmhostname']].summary.guest.ipAddress != nil
103
+ if try <= attempts
104
+ sleep wait
105
+ (last_wait, wait) = wait, last_wait + wait
106
+ try += 1
107
+ else
108
+ raise "vSphere registration failed after #{wait} seconds"
109
+ end
110
+ end
111
+ end
112
+ @logger.notify "Spent %.2f seconds booting and waiting for vSphere registration" % (Time.now - start)
113
+
114
+ start = Time.now
115
+ @vcloud_hosts.each_with_index do |h, i|
116
+ @logger.notify "Waiting for #{h['vmhostname']} DNS resolution"
117
+ try = 1
118
+ last_wait = 0
119
+ wait = 3
120
+
121
+ begin
122
+ Socket.getaddrinfo(h['vmhostname'], nil)
123
+ rescue
124
+ if try <= attempts
125
+ sleep wait
126
+ (last_wait, wait) = wait, last_wait + wait
127
+ try += 1
128
+
129
+ retry
130
+ else
131
+ raise "DNS resolution failed after #{wait} seconds"
132
+ end
133
+ end
134
+ end
135
+ @logger.notify "Spent %.2f seconds waiting for DNS resolution" % (Time.now - start)
136
+
137
+ vsphere_helper.close
138
+ end
139
+
140
+ def cleanup
141
+ @logger.notify "Destroying vCloud boxes"
142
+ vsphere_credentials = VsphereHelper.load_config
143
+
144
+ @logger.notify "Connecting to vSphere at #{vsphere_credentials[:server]}" +
145
+ " with credentials for #{vsphere_credentials[:user]}"
146
+
147
+ vsphere_helper = VsphereHelper.new( vsphere_credentials )
148
+
149
+ vm_names = @vcloud_hosts.map {|h| h['vmhostname'] }.compact
150
+ if @vcloud_hosts.length != vm_names.length
151
+ @logger.warn "Some hosts did not have vmhostname set correctly! This likely means VM provisioning was not successful"
152
+ end
153
+ vms = vsphere_helper.find_vms vm_names
154
+ vm_names.each do |name|
155
+ unless vm = vms[name]
156
+ raise "Couldn't find VM #{name} in vSphere!"
157
+ end
158
+
159
+ if vm.runtime.powerState == 'poweredOn'
160
+ @logger.notify "Shutting down #{vm.name}"
161
+ start = Time.now
162
+ vm.PowerOffVM_Task.wait_for_completion
163
+ @logger.notify "Spent %.2f seconds halting #{vm.name}" % (Time.now - start)
164
+ end
165
+
166
+ start = Time.now
167
+ vm.Destroy_Task
168
+ @logger.notify "Spent %.2f seconds destroying #{vm.name}" % (Time.now - start)
169
+ end
170
+
171
+ vsphere_helper.close
172
+ end
173
+
174
+ end
175
+ end