beaker 1.8.1 → 1.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/bin/beaker +1 -1
- data/lib/beaker.rb +1 -1
- data/lib/beaker/cli.rb +15 -35
- data/lib/beaker/host_prebuilt_steps.rb +335 -0
- data/lib/beaker/hypervisor.rb +53 -4
- data/lib/beaker/hypervisor/aixer.rb +2 -2
- data/lib/beaker/hypervisor/blimper.rb +5 -5
- data/lib/beaker/hypervisor/fusion.rb +3 -3
- data/lib/beaker/hypervisor/solaris.rb +2 -2
- data/lib/beaker/hypervisor/vagrant.rb +6 -16
- data/lib/beaker/hypervisor/vcloud.rb +6 -6
- data/lib/beaker/hypervisor/vcloud_pooled.rb +4 -4
- data/lib/beaker/hypervisor/vsphere.rb +3 -3
- data/lib/beaker/network_manager.rb +51 -37
- data/lib/beaker/options/presets.rb +1 -0
- data/lib/beaker/shared.rb +2 -2
- data/lib/beaker/shared/host_role_parser.rb +36 -0
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/host_prebuilt_steps_spec.rb +421 -0
- data/spec/beaker/hypervisor/google_compute.rb +23 -0
- data/spec/beaker/hypervisor/vagrant_spec.rb +5 -4
- data/spec/beaker/hypervisor/vcloud_pooled_spec.rb +2 -2
- data/spec/beaker/hypervisor/vcloud_spec.rb +2 -2
- data/spec/beaker/hypervisor/vsphere_spec.rb +2 -2
- data/spec/beaker/options/parser_spec.rb +1 -1
- data/spec/beaker/shared/host_role_parser_spec.rb +58 -0
- metadata +10 -18
- data/lib/beaker/shared/host_handler.rb +0 -51
- data/lib/beaker/utils.rb +0 -7
- data/lib/beaker/utils/ntp_control.rb +0 -57
- data/lib/beaker/utils/repo_control.rb +0 -90
- data/lib/beaker/utils/setup_helper.rb +0 -66
- data/lib/beaker/utils/validator.rb +0 -36
- data/spec/beaker/shared/host_handler_spec.rb +0 -104
- data/spec/beaker/utils/ntp_control_spec.rb +0 -70
- data/spec/beaker/utils/repo_control_spec.rb +0 -168
- data/spec/beaker/utils/setup_helper_spec.rb +0 -82
- data/spec/beaker/utils/validator_spec.rb +0 -91
data/lib/beaker/hypervisor.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
|
+
%w( host_prebuilt_steps ).each do |lib|
|
2
|
+
begin
|
3
|
+
require lib
|
4
|
+
rescue LoadError
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), lib))
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
1
9
|
module Beaker
|
10
|
+
#The Beaker class that interacts to all the supported hypervisors
|
2
11
|
class Hypervisor
|
12
|
+
include HostPrebuiltSteps
|
3
13
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
14
|
+
#Hypervisor creator method. Creates the appropriate hypervisor class object based upon
|
15
|
+
#the provided hypervisor type selected, then provisions hosts with hypervisor.
|
16
|
+
#@param [String] type The type of hypervisor to create - one of aix, solaris, vsphere, fusion,
|
17
|
+
# blimpy, vcloud or vagrant
|
18
|
+
#@param [Array<Host>] hosts_to_provision The hosts to be provisioned with the selected hypervisor
|
19
|
+
#@param [Hash] options options Options to alter execution
|
8
20
|
def self.create(type, hosts_to_provision, options)
|
9
21
|
@logger = options[:logger]
|
10
22
|
@logger.notify("Beaker::Hypervisor, found some #{type} boxes to create")
|
@@ -27,17 +39,54 @@ module Beaker
|
|
27
39
|
end
|
28
40
|
when /vagrant/
|
29
41
|
Beaker::Vagrant
|
42
|
+
when /none/
|
43
|
+
Beaker::Hypervisor
|
44
|
+
else
|
45
|
+
raise "Invalid hypervisor: #{type}"
|
30
46
|
end
|
47
|
+
|
31
48
|
hypervisor = hyper_class.new(hosts_to_provision, options)
|
32
49
|
hypervisor.provision
|
33
50
|
|
34
51
|
hypervisor
|
35
52
|
end
|
36
53
|
|
54
|
+
def initialize(hosts, options)
|
55
|
+
@hosts = hosts
|
56
|
+
@options = options
|
57
|
+
end
|
58
|
+
|
59
|
+
#Provisioning steps for be run for a given hypervisor. Default is nil.
|
37
60
|
def provision
|
38
61
|
nil
|
39
62
|
end
|
40
63
|
|
64
|
+
#Cleanup steps to be run for a given hypervisor. Default is nil.
|
65
|
+
def cleanup
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
#Default configuration steps to be run for a given hypervisor
|
70
|
+
def configure
|
71
|
+
if @options[:timesync]
|
72
|
+
timesync(@hosts, @options)
|
73
|
+
end
|
74
|
+
if @options[:root_keys]
|
75
|
+
sync_root_keys(@hosts, @options)
|
76
|
+
end
|
77
|
+
if @options[:add_el_extras]
|
78
|
+
add_el_extras(@hosts, @options)
|
79
|
+
end
|
80
|
+
if @options[:add_master_entry]
|
81
|
+
add_master_entry(@hosts, @options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
#Default validation steps to be run for a given hypervisor
|
86
|
+
def validate
|
87
|
+
validate_host(@hosts, @options)
|
88
|
+
end
|
89
|
+
|
41
90
|
end
|
42
91
|
end
|
43
92
|
|
@@ -4,7 +4,7 @@ module Beaker
|
|
4
4
|
def initialize(aix_hosts, options)
|
5
5
|
@options = options
|
6
6
|
@logger = options[:logger]
|
7
|
-
@
|
7
|
+
@hosts = aix_hosts
|
8
8
|
#aix machines are reverted to known state, not a snapshot
|
9
9
|
@fog_file = nil
|
10
10
|
if File.exists?( @options[:dot_fog] )
|
@@ -27,7 +27,7 @@ module Beaker
|
|
27
27
|
hypervisor[:user] = @fog_file[:default][:aix_hypervisor_username] || hypervisor[:user]
|
28
28
|
hypervisor[:ssh][:keys] = [@fog_file[:default][:aix_hypervisor_keyfile]] || hypervisor[:ssh][:keys]
|
29
29
|
|
30
|
-
@
|
30
|
+
@hosts.each do |host|
|
31
31
|
vm_name = host['vmname'] || host.name
|
32
32
|
|
33
33
|
@logger.notify "Reverting #{vm_name} to aix clean state"
|
@@ -27,7 +27,7 @@ module Beaker
|
|
27
27
|
def initialize(blimpy_hosts, options)
|
28
28
|
@options = options
|
29
29
|
@logger = options[:logger]
|
30
|
-
@
|
30
|
+
@hosts = blimpy_hosts
|
31
31
|
@blimpy = Blimpy
|
32
32
|
end
|
33
33
|
|
@@ -35,7 +35,7 @@ module Beaker
|
|
35
35
|
ami_spec= YAML.load_file(@options[:ec2_yaml])["AMI"]
|
36
36
|
|
37
37
|
fleet = @blimpy.fleet do |fleet|
|
38
|
-
@
|
38
|
+
@hosts.each do |host|
|
39
39
|
amitype = host['vmname'] || host['platform']
|
40
40
|
amisize = host['amisize'] || 'm1.small'
|
41
41
|
#use snapshot provided for this host
|
@@ -90,7 +90,7 @@ module Beaker
|
|
90
90
|
fleet.ships.each do |ship|
|
91
91
|
ship.wait_for_sshd
|
92
92
|
name = ship.name
|
93
|
-
host = @
|
93
|
+
host = @hosts.select { |host| host.name == name }[0]
|
94
94
|
host['ip'] = ship.dns
|
95
95
|
host.exec(Command.new("hostname #{name}"))
|
96
96
|
ip = get_ip(host)
|
@@ -99,7 +99,7 @@ module Beaker
|
|
99
99
|
end
|
100
100
|
|
101
101
|
# Send our hosts information to the nodes
|
102
|
-
@
|
102
|
+
@hosts.each do |host|
|
103
103
|
set_etc_hosts(host, etc_hosts)
|
104
104
|
end
|
105
105
|
|
@@ -107,7 +107,7 @@ module Beaker
|
|
107
107
|
|
108
108
|
def cleanup
|
109
109
|
fleet = @blimpy.fleet do |fleet|
|
110
|
-
@
|
110
|
+
@hosts.each do |host|
|
111
111
|
fleet.add(:aws) do |ship|
|
112
112
|
ship.name = host.name
|
113
113
|
end
|
@@ -10,9 +10,9 @@ module Beaker
|
|
10
10
|
end
|
11
11
|
@logger = options[:logger]
|
12
12
|
@options = options
|
13
|
-
@
|
13
|
+
@hosts = fusion_hosts
|
14
14
|
#check preconditions for fusion
|
15
|
-
@
|
15
|
+
@hosts.each do |host|
|
16
16
|
raise "You must specify a snapshot for Fusion instances, no snapshot defined for #{host.name}!" unless host["snapshot"]
|
17
17
|
end
|
18
18
|
@fission = Fission::VM
|
@@ -22,7 +22,7 @@ module Beaker
|
|
22
22
|
available = @fission.all.data.collect{|vm| vm.name}.sort.join(", ")
|
23
23
|
@logger.notify "Available VM names: #{available}"
|
24
24
|
|
25
|
-
@
|
25
|
+
@hosts.each do |host|
|
26
26
|
vm_name = host["vmname"] || host.name
|
27
27
|
vm = @fission.new vm_name
|
28
28
|
raise "Could not find VM '#{vm_name}' for #{host.name}!" unless vm.exists?
|
@@ -4,7 +4,7 @@ module Beaker
|
|
4
4
|
def initialize(solaris_hosts, options)
|
5
5
|
@options = options
|
6
6
|
@logger = options[:logger]
|
7
|
-
@
|
7
|
+
@hosts = solaris_hosts
|
8
8
|
@fog_file = nil
|
9
9
|
if File.exists?( @options[:dot_fog] )
|
10
10
|
@fog_file = YAML.load_file( @options[:dot_fog] )
|
@@ -28,7 +28,7 @@ module Beaker
|
|
28
28
|
hypervisor[:user] = @fog_file[:default][:solaris_hypervisor_username] || hypervisor[:user]
|
29
29
|
hypervisor[:ssh][:keys] = [@fog_file[:default][:solaris_hypervisor_keyfile]] || hypervisor[:ssh][:keys]
|
30
30
|
|
31
|
-
@
|
31
|
+
@hosts.each do |host|
|
32
32
|
vm_name = host['vmname'] || host.name
|
33
33
|
#use the snapshot provided for this host
|
34
34
|
snapshot = host['snapshot']
|
@@ -52,16 +52,6 @@ module Beaker
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
def copy_ssh_to_root host
|
56
|
-
#make is possible to log in as root by copying the ssh dir to root's account
|
57
|
-
@logger.debug "Give root a copy of vagrant's keys"
|
58
|
-
if host['platform'] =~ /windows/
|
59
|
-
host.exec(Command.new('sudo su -c "cp -r .ssh /home/Administrator/."'))
|
60
|
-
else
|
61
|
-
host.exec(Command.new('sudo su -c "cp -r .ssh /root/."'))
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
55
|
def set_ssh_config host, user
|
66
56
|
f = Tempfile.new("#{host.name}")
|
67
57
|
ssh_config = Dir.chdir(@vagrant_path) do
|
@@ -104,7 +94,7 @@ module Beaker
|
|
104
94
|
@options = options
|
105
95
|
@logger = options[:logger]
|
106
96
|
@temp_files = []
|
107
|
-
@
|
97
|
+
@hosts = vagrant_hosts
|
108
98
|
@vagrant_path = File.expand_path(File.join(File.basename(__FILE__), '..', '.vagrant', 'beaker_vagrant_files', File.basename(options[:hosts_file])))
|
109
99
|
FileUtils.mkdir_p(@vagrant_path)
|
110
100
|
@vagrant_file = File.expand_path(File.join(@vagrant_path, "Vagrantfile"))
|
@@ -117,29 +107,29 @@ module Beaker
|
|
117
107
|
#make sure that any old boxes are dead dead dead
|
118
108
|
vagrant_cmd("destroy --force") if File.file?(@vagrant_file)
|
119
109
|
|
120
|
-
make_vfile @
|
110
|
+
make_vfile @hosts, @options
|
121
111
|
|
122
112
|
vagrant_cmd("up")
|
123
113
|
else #set host ip of already up boxes
|
124
|
-
@
|
114
|
+
@hosts.each do |host|
|
125
115
|
host[:ip] = get_ip_from_vagrant_file(host.name)
|
126
116
|
end
|
127
117
|
end
|
128
118
|
|
129
119
|
@logger.debug "configure vagrant boxes (set ssh-config, switch to root user, hack etc/hosts)"
|
130
|
-
@
|
120
|
+
@hosts.each do |host|
|
131
121
|
default_user = host['user']
|
132
122
|
|
133
123
|
set_ssh_config host, 'vagrant'
|
134
124
|
|
135
|
-
copy_ssh_to_root host
|
125
|
+
copy_ssh_to_root host, @options
|
136
126
|
#shut down connection, will reconnect on next exec
|
137
127
|
host.close
|
138
128
|
|
139
129
|
set_ssh_config host, default_user
|
140
130
|
end
|
141
131
|
|
142
|
-
hack_etc_hosts @
|
132
|
+
hack_etc_hosts @hosts
|
143
133
|
|
144
134
|
end
|
145
135
|
|
@@ -7,7 +7,7 @@ module Beaker
|
|
7
7
|
def initialize(vcloud_hosts, options)
|
8
8
|
@options = options
|
9
9
|
@logger = options[:logger]
|
10
|
-
@
|
10
|
+
@hosts = vcloud_hosts
|
11
11
|
|
12
12
|
raise 'You must specify a datastore for vCloud instances!' unless @options['datastore']
|
13
13
|
raise 'You must specify a resource pool for vCloud instances!' unless @options['resourcepool']
|
@@ -101,7 +101,7 @@ module Beaker
|
|
101
101
|
|
102
102
|
start = Time.now
|
103
103
|
tasks = []
|
104
|
-
@
|
104
|
+
@hosts.each_with_index do |h, i|
|
105
105
|
# Generate a randomized hostname
|
106
106
|
h['vmhostname'] = generate_host_name
|
107
107
|
|
@@ -135,7 +135,7 @@ module Beaker
|
|
135
135
|
|
136
136
|
try = (Time.now - start) / 5
|
137
137
|
duration = run_and_report_duration do
|
138
|
-
@
|
138
|
+
@hosts.each_with_index do |h, i|
|
139
139
|
booting_host(h, try, attempts)
|
140
140
|
end
|
141
141
|
end
|
@@ -143,7 +143,7 @@ module Beaker
|
|
143
143
|
|
144
144
|
try = (Time.now - start) / 5
|
145
145
|
duration = run_and_report_duration do
|
146
|
-
@
|
146
|
+
@hosts.each_with_index do |h, i|
|
147
147
|
wait_for_dns_resolution(h, try, attempts)
|
148
148
|
end
|
149
149
|
end
|
@@ -158,8 +158,8 @@ module Beaker
|
|
158
158
|
@logger.notify "Destroying vCloud boxes"
|
159
159
|
connect_to_vsphere
|
160
160
|
|
161
|
-
vm_names = @
|
162
|
-
if @
|
161
|
+
vm_names = @hosts.map {|h| h['vmhostname'] }.compact
|
162
|
+
if @hosts.length != vm_names.length
|
163
163
|
@logger.warn "Some hosts did not have vmhostname set correctly! This likely means VM provisioning was not successful"
|
164
164
|
end
|
165
165
|
vms = @vsphere_helper.find_vms vm_names
|
@@ -19,7 +19,7 @@ module Beaker
|
|
19
19
|
def initialize(vcloud_hosts, options)
|
20
20
|
@options = options
|
21
21
|
@logger = options[:logger]
|
22
|
-
@
|
22
|
+
@hosts = vcloud_hosts
|
23
23
|
|
24
24
|
raise 'You must specify a datastore for vCloud instances!' unless @options['datastore']
|
25
25
|
raise 'You must specify a resource pool for vCloud instances!' unless @options['resourcepool']
|
@@ -54,7 +54,7 @@ module Beaker
|
|
54
54
|
def provision
|
55
55
|
start = Time.now
|
56
56
|
try = 1
|
57
|
-
@
|
57
|
+
@hosts.each_with_index do |h, i|
|
58
58
|
if not h['template']
|
59
59
|
raise ArgumentError, "You must specify a template name for #{h}"
|
60
60
|
end
|
@@ -97,8 +97,8 @@ module Beaker
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def cleanup
|
100
|
-
vm_names = @
|
101
|
-
if @
|
100
|
+
vm_names = @hosts.map {|h| h['vmhostname'] }.compact
|
101
|
+
if @hosts.length != vm_names.length
|
102
102
|
@logger.warn "Some hosts did not have vmhostname set correctly! This likely means VM provisioning was not successful"
|
103
103
|
end
|
104
104
|
|
@@ -6,7 +6,7 @@ module Beaker
|
|
6
6
|
def initialize(vsphere_hosts, options)
|
7
7
|
@options = options
|
8
8
|
@logger = options[:logger]
|
9
|
-
@
|
9
|
+
@hosts = vsphere_hosts
|
10
10
|
end
|
11
11
|
|
12
12
|
def provision
|
@@ -18,7 +18,7 @@ module Beaker
|
|
18
18
|
vsphere_helper = VsphereHelper.new( vsphere_credentials )
|
19
19
|
|
20
20
|
vsphere_vms = {}
|
21
|
-
@
|
21
|
+
@hosts.each do |h|
|
22
22
|
name = h["vmname"] || h.name
|
23
23
|
vsphere_vms[name] = h["snapshot"]
|
24
24
|
end
|
@@ -60,7 +60,7 @@ module Beaker
|
|
60
60
|
|
61
61
|
vsphere_helper = VsphereHelper.new( vsphere_credentials )
|
62
62
|
|
63
|
-
vm_names = @
|
63
|
+
vm_names = @hosts.map {|h| h['vmname'] || h.name }
|
64
64
|
vms = vsphere_helper.find_vms vm_names
|
65
65
|
vm_names.each do |name|
|
66
66
|
unless vm = vms[name]
|
@@ -7,15 +7,17 @@
|
|
7
7
|
end
|
8
8
|
|
9
9
|
module Beaker
|
10
|
+
#Object that holds all the provisioned and non-provisioned virtual machines.
|
11
|
+
#Controls provisioning, configuration, validation and cleanup of those virtual machines.
|
10
12
|
class NetworkManager
|
11
|
-
HYPERVISOR_TYPES = ['solaris', 'blimpy', 'vsphere', 'fusion', 'aix', 'vcloud', 'vagrant']
|
12
13
|
|
14
|
+
#Determine if a given host should be provisioned.
|
15
|
+
#Provision if:
|
16
|
+
# - only if we are running with ---provision
|
17
|
+
# - only if we have a hypervisor
|
18
|
+
# - only if either the specific hosts has no specification or has 'provision' in its config
|
19
|
+
# - always if it is a vagrant box (vagrant boxes are always provisioned as they always need ssh key hacking)
|
13
20
|
def provision? options, host
|
14
|
-
#provision this box
|
15
|
-
# - only if we are running with --provision
|
16
|
-
# - only if we have a hypervisor
|
17
|
-
# - only if either the specific hosts has no specification or has 'provision' in its config
|
18
|
-
# - always if it is a vagrant box (vagrant boxes are always provisioned as they always need ssh key hacking)
|
19
21
|
command_line_says = options[:provision]
|
20
22
|
host_says = host['hypervisor'] && (host.has_key?('provision') ? host['provision'] : true)
|
21
23
|
(command_line_says && host_says) or (host['hypervisor'] =~/vagrant/)
|
@@ -25,55 +27,67 @@ module Beaker
|
|
25
27
|
@logger = logger
|
26
28
|
@options = options
|
27
29
|
@hosts = []
|
28
|
-
@
|
29
|
-
@
|
30
|
+
@machines = {}
|
31
|
+
@hypervisors = nil
|
30
32
|
end
|
31
33
|
|
34
|
+
#Provision all virtual machines. Provision machines according to their set hypervisor, if no hypervisor
|
35
|
+
#is selected assume that the described hosts are already up and reachable and do no provisioning.
|
32
36
|
def provision
|
33
|
-
|
37
|
+
if @hypervisors
|
38
|
+
cleanup
|
39
|
+
end
|
40
|
+
@hypervisors = {}
|
41
|
+
#sort hosts by their hypervisor, use hypervisor 'none' if no hypervisor is specified
|
34
42
|
@options['HOSTS'].each_key do |name|
|
35
43
|
host = @options['HOSTS'][name]
|
36
|
-
hypervisor = host['hypervisor']
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@virtual_machines[hypervisor] << name
|
42
|
-
else #this is a non-provisioned machine, deal with it without hypervisors
|
43
|
-
@logger.debug "No hypervisor for #{name}, connecting to host without provisioning"
|
44
|
-
@noprovision_machines << name
|
45
|
-
end
|
44
|
+
hypervisor = host['hypervisor']
|
45
|
+
hypervisor = provision?(@options, host) ? host['hypervisor'] : 'none'
|
46
|
+
@logger.debug "Hypervisor for #{name} is #{hypervisor}"
|
47
|
+
@machines[hypervisor] = [] unless @machines[hypervisor]
|
48
|
+
@machines[hypervisor] << Beaker::Host.create(name, @options)
|
46
49
|
end
|
47
50
|
|
48
|
-
@
|
49
|
-
|
50
|
-
|
51
|
-
#set up host objects for provisioned provisioned_set
|
52
|
-
names.each do |name|
|
53
|
-
host = Beaker::Host.create(name, @options)
|
54
|
-
hosts_for_type << host
|
55
|
-
end
|
56
|
-
@provisioned_set[type] = Beaker::Hypervisor.create(type, hosts_for_type, @options)
|
57
|
-
@hosts << hosts_for_type
|
58
|
-
end
|
59
|
-
@noprovision_machines.each do |name|
|
60
|
-
@hosts << Beaker::Host.create(name, @options)
|
51
|
+
@machines.each_key do |type|
|
52
|
+
@hypervisors[type] = Beaker::Hypervisor.create(type, @machines[type], @options)
|
53
|
+
@hosts << @machines[type]
|
61
54
|
end
|
62
55
|
@hosts = @hosts.flatten
|
63
56
|
@hosts
|
64
57
|
end
|
65
58
|
|
59
|
+
#Validate all provisioned machines, ensure that required packages are installed - if they are missing
|
60
|
+
#attempt to add them.
|
61
|
+
#@raise [Exception] Raise an exception if virtual machines fail to be validated
|
62
|
+
def validate
|
63
|
+
if @hypervisors
|
64
|
+
@hypervisors.each_key do |type|
|
65
|
+
@hypervisors[type].validate
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#Configure all provisioned machines, adding any packages or settings required for SUTs
|
71
|
+
#@raise [Exception] Raise an exception if virtual machines fail to be configured
|
72
|
+
def configure
|
73
|
+
if @hypervisors
|
74
|
+
@hypervisors.each_key do |type|
|
75
|
+
@hypervisors[type].configure
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#Shut down network connections and revert all provisioned virtual machines
|
66
81
|
def cleanup
|
67
82
|
#shut down connections
|
68
83
|
@hosts.each {|host| host.close }
|
69
84
|
|
70
|
-
if @
|
71
|
-
@
|
72
|
-
|
73
|
-
@provisioned_set[type].cleanup
|
74
|
-
end
|
85
|
+
if @hypervisors
|
86
|
+
@hypervisors.each_key do |type|
|
87
|
+
@hypervisors[type].cleanup
|
75
88
|
end
|
76
89
|
end
|
90
|
+
@hypervisors = nil
|
77
91
|
end
|
78
92
|
|
79
93
|
end
|