beaker 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.simplecov +14 -0
- data/DOCUMENTING.md +167 -0
- data/Gemfile +3 -0
- data/LICENSE +17 -0
- data/README.md +332 -0
- data/Rakefile +121 -0
- data/beaker.gemspec +42 -0
- data/beaker.rb +10 -0
- data/bin/beaker +9 -0
- data/lib/beaker.rb +36 -0
- data/lib/beaker/answers.rb +29 -0
- data/lib/beaker/answers/version28.rb +104 -0
- data/lib/beaker/answers/version30.rb +194 -0
- data/lib/beaker/cli.rb +113 -0
- data/lib/beaker/command.rb +241 -0
- data/lib/beaker/command_factory.rb +21 -0
- data/lib/beaker/dsl.rb +85 -0
- data/lib/beaker/dsl/assertions.rb +87 -0
- data/lib/beaker/dsl/helpers.rb +625 -0
- data/lib/beaker/dsl/install_utils.rb +299 -0
- data/lib/beaker/dsl/outcomes.rb +99 -0
- data/lib/beaker/dsl/roles.rb +97 -0
- data/lib/beaker/dsl/structure.rb +63 -0
- data/lib/beaker/dsl/wrappers.rb +100 -0
- data/lib/beaker/host.rb +193 -0
- data/lib/beaker/host/aix.rb +15 -0
- data/lib/beaker/host/aix/file.rb +16 -0
- data/lib/beaker/host/aix/group.rb +35 -0
- data/lib/beaker/host/aix/user.rb +32 -0
- data/lib/beaker/host/unix.rb +54 -0
- data/lib/beaker/host/unix/exec.rb +15 -0
- data/lib/beaker/host/unix/file.rb +16 -0
- data/lib/beaker/host/unix/group.rb +40 -0
- data/lib/beaker/host/unix/pkg.rb +22 -0
- data/lib/beaker/host/unix/user.rb +32 -0
- data/lib/beaker/host/windows.rb +44 -0
- data/lib/beaker/host/windows/exec.rb +18 -0
- data/lib/beaker/host/windows/file.rb +15 -0
- data/lib/beaker/host/windows/group.rb +36 -0
- data/lib/beaker/host/windows/pkg.rb +26 -0
- data/lib/beaker/host/windows/user.rb +32 -0
- data/lib/beaker/hypervisor.rb +37 -0
- data/lib/beaker/hypervisor/aixer.rb +52 -0
- data/lib/beaker/hypervisor/blimper.rb +123 -0
- data/lib/beaker/hypervisor/fusion.rb +56 -0
- data/lib/beaker/hypervisor/solaris.rb +65 -0
- data/lib/beaker/hypervisor/vagrant.rb +118 -0
- data/lib/beaker/hypervisor/vcloud.rb +175 -0
- data/lib/beaker/hypervisor/vsphere.rb +80 -0
- data/lib/beaker/hypervisor/vsphere_helper.rb +200 -0
- data/lib/beaker/logger.rb +167 -0
- data/lib/beaker/network_manager.rb +73 -0
- data/lib/beaker/options_parsing.rb +323 -0
- data/lib/beaker/result.rb +55 -0
- data/lib/beaker/shared.rb +15 -0
- data/lib/beaker/shared/error_handler.rb +17 -0
- data/lib/beaker/shared/host_handler.rb +46 -0
- data/lib/beaker/shared/repetition.rb +28 -0
- data/lib/beaker/ssh_connection.rb +198 -0
- data/lib/beaker/test_case.rb +225 -0
- data/lib/beaker/test_config.rb +148 -0
- data/lib/beaker/test_suite.rb +288 -0
- data/lib/beaker/utils.rb +7 -0
- data/lib/beaker/utils/ntp_control.rb +42 -0
- data/lib/beaker/utils/repo_control.rb +92 -0
- data/lib/beaker/utils/setup_helper.rb +77 -0
- data/lib/beaker/utils/validator.rb +27 -0
- data/spec/beaker/command_spec.rb +94 -0
- data/spec/beaker/dsl/assertions_spec.rb +104 -0
- data/spec/beaker/dsl/helpers_spec.rb +230 -0
- data/spec/beaker/dsl/install_utils_spec.rb +70 -0
- data/spec/beaker/dsl/outcomes_spec.rb +43 -0
- data/spec/beaker/dsl/roles_spec.rb +86 -0
- data/spec/beaker/dsl/structure_spec.rb +60 -0
- data/spec/beaker/dsl/wrappers_spec.rb +52 -0
- data/spec/beaker/host_spec.rb +95 -0
- data/spec/beaker/logger_spec.rb +117 -0
- data/spec/beaker/options_parsing_spec.rb +37 -0
- data/spec/beaker/puppet_command_spec.rb +128 -0
- data/spec/beaker/ssh_connection_spec.rb +39 -0
- data/spec/beaker/test_case_spec.rb +6 -0
- data/spec/beaker/test_suite_spec.rb +44 -0
- data/spec/mocks_and_helpers.rb +34 -0
- data/spec/spec_helper.rb +15 -0
- metadata +359 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Beaker
|
2
|
+
class Vsphere < Beaker::Hypervisor
|
3
|
+
|
4
|
+
def initialize(vsphere_hosts, options, config)
|
5
|
+
@options = options
|
6
|
+
@@config = config['CONFIG'].dup
|
7
|
+
@logger = options[:logger]
|
8
|
+
@vsphere_hosts = vsphere_hosts
|
9
|
+
require 'yaml' unless defined?(YAML)
|
10
|
+
vsphere_credentials = VsphereHelper.load_config
|
11
|
+
|
12
|
+
@logger.notify "Connecting to vSphere at #{vsphere_credentials[:server]}" +
|
13
|
+
" with credentials for #{vsphere_credentials[:user]}"
|
14
|
+
|
15
|
+
vsphere_helper = VsphereHelper.new( vsphere_credentials )
|
16
|
+
|
17
|
+
vsphere_vms = {}
|
18
|
+
@vsphere_hosts.each do |h|
|
19
|
+
name = h["vmname"] || h.name
|
20
|
+
vsphere_vms[name] = h["snapshot"]
|
21
|
+
end
|
22
|
+
vms = vsphere_helper.find_vms(vsphere_vms.keys)
|
23
|
+
vsphere_vms.each_pair do |name, snap|
|
24
|
+
unless vm = vms[name]
|
25
|
+
raise "Couldn't find VM #{name} in vSphere!"
|
26
|
+
end
|
27
|
+
|
28
|
+
snapshot = vsphere_helper.find_snapshot(vm, snap) or
|
29
|
+
raise "Could not find snapshot '#{snap}' for VM #{vm.name}!"
|
30
|
+
|
31
|
+
@logger.notify "Reverting #{vm.name} to snapshot '#{snap}'"
|
32
|
+
start = Time.now
|
33
|
+
# This will block for each snapshot...
|
34
|
+
# The code to issue them all and then wait until they are all done sucks
|
35
|
+
snapshot.RevertToSnapshot_Task.wait_for_completion
|
36
|
+
|
37
|
+
time = Time.now - start
|
38
|
+
@logger.notify "Spent %.2f seconds reverting" % time
|
39
|
+
|
40
|
+
unless vm.runtime.powerState == "poweredOn"
|
41
|
+
@logger.notify "Booting #{vm.name}"
|
42
|
+
start = Time.now
|
43
|
+
vm.PowerOnVM_Task.wait_for_completion
|
44
|
+
@logger.notify "Spent %.2f seconds booting #{vm.name}" % (Time.now - start)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
vsphere_helper.close
|
49
|
+
end
|
50
|
+
|
51
|
+
def cleanup
|
52
|
+
@logger.notify "Destroying vsphere boxes"
|
53
|
+
vsphere_credentials = VsphereHelper.load_config
|
54
|
+
|
55
|
+
@logger.notify "Connecting to vSphere at #{vsphere_credentials[:server]}" +
|
56
|
+
" with credentials for #{vsphere_credentials[:user]}"
|
57
|
+
|
58
|
+
vsphere_helper = VsphereHelper.new( vsphere_credentials )
|
59
|
+
|
60
|
+
vm_names = @vsphere_hosts.map {|h| h['vmname'] || h.name }
|
61
|
+
vms = vsphere_helper.find_vms vm_names
|
62
|
+
vm_names.each do |name|
|
63
|
+
unless vm = vms[name]
|
64
|
+
raise "Couldn't find VM #{name} in vSphere!"
|
65
|
+
end
|
66
|
+
|
67
|
+
if vm.runtime.powerState == "poweredOn"
|
68
|
+
@logger.notify "Shutting down #{vm.name}"
|
69
|
+
start = Time.now
|
70
|
+
vm.PowerOffVM_Task.wait_for_completion
|
71
|
+
@logger.notify(
|
72
|
+
"Spent %.2f seconds halting #{vm.name}" % (Time.now - start) )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
vsphere_helper.close
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'yaml' unless defined?(YAML)
|
2
|
+
require 'rubygems' unless defined?(Gem)
|
3
|
+
begin
|
4
|
+
require 'beaker/logger'
|
5
|
+
rescue LoadError
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'logger.rb'))
|
7
|
+
end
|
8
|
+
|
9
|
+
class VsphereHelper
|
10
|
+
def initialize vInfo = {}
|
11
|
+
@logger = vInfo[:logger] || Beaker::Logger.new
|
12
|
+
begin
|
13
|
+
require 'rbvmomi'
|
14
|
+
rescue LoadError
|
15
|
+
raise "Unable to load RbVmomi, please ensure its installed"
|
16
|
+
end
|
17
|
+
@connection = RbVmomi::VIM.connect :host => vInfo[:server],
|
18
|
+
:user => vInfo[:user],
|
19
|
+
:password => vInfo[:pass],
|
20
|
+
:insecure => true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.load_config
|
24
|
+
# support Fog/Cloud Provisioner layout
|
25
|
+
# (ie, someplace besides my made up conf)
|
26
|
+
vsphere_credentials = nil
|
27
|
+
if File.exists? '/etc/plharness/vsphere'
|
28
|
+
vsphere_credentials = load_legacy_credentials
|
29
|
+
|
30
|
+
elsif File.exists?( File.join(ENV['HOME'], '.fog') )
|
31
|
+
vsphere_credentials = load_fog_credentials
|
32
|
+
end
|
33
|
+
|
34
|
+
return vsphere_credentials
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load_fog_credentials
|
38
|
+
vInfo = YAML.load_file( File.join(ENV['HOME'], '.fog') )
|
39
|
+
|
40
|
+
vsphere_credentials = {}
|
41
|
+
vsphere_credentials[:server] = vInfo[:default][:vsphere_server]
|
42
|
+
vsphere_credentials[:user] = vInfo[:default][:vsphere_username]
|
43
|
+
vsphere_credentials[:pass] = vInfo[:default][:vsphere_password]
|
44
|
+
|
45
|
+
return vsphere_credentials
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.load_legacy_credentials
|
49
|
+
vInfo = YAML.load_file '/etc/plharness/vsphere'
|
50
|
+
|
51
|
+
puts(
|
52
|
+
"Use of /etc/plharness/vsphere as a config file is deprecated.\n" +
|
53
|
+
"Please use ~/.fog instead\n" +
|
54
|
+
"See http://docs.puppetlabs.com/pe/2.0/" +
|
55
|
+
"cloudprovisioner_configuring.html for format"
|
56
|
+
)
|
57
|
+
|
58
|
+
vsphere_credentials = {}
|
59
|
+
vsphere_credentials[:server] = vInfo['location']
|
60
|
+
vsphere_credentials[:user] = vInfo['user']
|
61
|
+
vsphere_credentials[:pass] = vInfo['pass']
|
62
|
+
|
63
|
+
return vsphere_credentials
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_snapshot vm, snapname
|
67
|
+
search_child_snaps vm.snapshot.rootSnapshotList, snapname
|
68
|
+
end
|
69
|
+
|
70
|
+
def search_child_snaps tree, snapname
|
71
|
+
snapshot = nil
|
72
|
+
tree.each do |child|
|
73
|
+
if child.name == snapname
|
74
|
+
snapshot ||= child.snapshot
|
75
|
+
else
|
76
|
+
snapshot ||= search_child_snaps child.childSnapshotList, snapname
|
77
|
+
end
|
78
|
+
end
|
79
|
+
snapshot
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_customization name
|
83
|
+
csm = @connection.serviceContent.customizationSpecManager
|
84
|
+
|
85
|
+
begin
|
86
|
+
customizationSpec = csm.GetCustomizationSpec({:name => name}).spec
|
87
|
+
rescue
|
88
|
+
customizationSpec = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
return customizationSpec
|
92
|
+
end
|
93
|
+
|
94
|
+
# an easier wrapper around the horrid PropertyCollector interface,
|
95
|
+
# necessary for searching VMs in all Datacenters that may be nested
|
96
|
+
# within folders of arbitrary depth
|
97
|
+
# returns a hash array of <name> => <VirtualMachine ManagedObjects>
|
98
|
+
def find_vms names, connection = @connection
|
99
|
+
names = names.is_a?(Array) ? names : [ names ]
|
100
|
+
containerView = get_base_vm_container_from connection
|
101
|
+
propertyCollector = connection.propertyCollector
|
102
|
+
|
103
|
+
objectSet = [{
|
104
|
+
:obj => containerView,
|
105
|
+
:skip => true,
|
106
|
+
:selectSet => [ RbVmomi::VIM::TraversalSpec.new({
|
107
|
+
:name => 'gettingTheVMs',
|
108
|
+
:path => 'view',
|
109
|
+
:skip => false,
|
110
|
+
:type => 'ContainerView'
|
111
|
+
}) ]
|
112
|
+
}]
|
113
|
+
|
114
|
+
propSet = [{
|
115
|
+
:pathSet => [ 'name' ],
|
116
|
+
:type => 'VirtualMachine'
|
117
|
+
}]
|
118
|
+
|
119
|
+
results = propertyCollector.RetrievePropertiesEx({
|
120
|
+
:specSet => [{
|
121
|
+
:objectSet => objectSet,
|
122
|
+
:propSet => propSet
|
123
|
+
}],
|
124
|
+
:options => { :maxObjects => nil }
|
125
|
+
})
|
126
|
+
|
127
|
+
vms = {}
|
128
|
+
results.objects.each do |result|
|
129
|
+
name = result.propSet.first.val
|
130
|
+
next unless names.include? name
|
131
|
+
vms[name] = result.obj
|
132
|
+
end
|
133
|
+
|
134
|
+
while results.token do
|
135
|
+
results = propertyCollector.ContinueRetrievePropertiesEx({:token => results.token})
|
136
|
+
results.objects.each do |result|
|
137
|
+
name = result.propSet.first.val
|
138
|
+
next unless names.include? name
|
139
|
+
vms[name] = result.obj
|
140
|
+
end
|
141
|
+
end
|
142
|
+
vms
|
143
|
+
end
|
144
|
+
|
145
|
+
def find_datastore datastorename
|
146
|
+
datacenter = @connection.serviceInstance.find_datacenter
|
147
|
+
datacenter.find_datastore(datastorename)
|
148
|
+
end
|
149
|
+
|
150
|
+
def find_folder foldername
|
151
|
+
datacenter = @connection.serviceInstance.find_datacenter
|
152
|
+
base = datacenter.vmFolder
|
153
|
+
folders = foldername.split('/')
|
154
|
+
folders.each do |folder|
|
155
|
+
case base
|
156
|
+
when RbVmomi::VIM::Folder
|
157
|
+
base = base.childEntity.find { |f| f.name == folder }
|
158
|
+
else
|
159
|
+
abort "Unexpected object type encountered (#{base.class}) while finding folder"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
base
|
164
|
+
end
|
165
|
+
|
166
|
+
def find_pool poolname
|
167
|
+
datacenter = @connection.serviceInstance.find_datacenter
|
168
|
+
base = datacenter.hostFolder
|
169
|
+
pools = poolname.split('/')
|
170
|
+
pools.each do |pool|
|
171
|
+
case base
|
172
|
+
when RbVmomi::VIM::Folder
|
173
|
+
base = base.childEntity.find { |f| f.name == pool }
|
174
|
+
when RbVmomi::VIM::ClusterComputeResource
|
175
|
+
base = base.resourcePool.resourcePool.find { |f| f.name == pool }
|
176
|
+
when RbVmomi::VIM::ResourcePool
|
177
|
+
base = base.resourcePool.find { |f| f.name == pool }
|
178
|
+
else
|
179
|
+
abort "Unexpected object type encountered (#{base.class}) while finding resource pool"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
base = base.resourcePool unless base.is_a?(RbVmomi::VIM::ResourcePool) and base.respond_to?(:resourcePool)
|
184
|
+
base
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_base_vm_container_from connection
|
188
|
+
viewManager = connection.serviceContent.viewManager
|
189
|
+
viewManager.CreateContainerView({
|
190
|
+
:container => connection.serviceContent.rootFolder,
|
191
|
+
:recursive => true,
|
192
|
+
:type => [ 'VirtualMachine' ]
|
193
|
+
})
|
194
|
+
end
|
195
|
+
|
196
|
+
def close
|
197
|
+
@connection.close
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Beaker
|
2
|
+
class Logger
|
3
|
+
NORMAL = "\e[00;00m"
|
4
|
+
BRIGHT_NORMAL = "\e[00;01m"
|
5
|
+
BLACK = "\e[00;30m"
|
6
|
+
RED = "\e[00;31m"
|
7
|
+
GREEN = "\e[00;32m"
|
8
|
+
YELLOW = "\e[00;33m"
|
9
|
+
BLUE = "\e[00;34m"
|
10
|
+
MAGENTA = "\e[00;35m"
|
11
|
+
CYAN = "\e[00;36m"
|
12
|
+
WHITE = "\e[00;37m"
|
13
|
+
GREY = "\e[01;30m"
|
14
|
+
BRIGHT_RED = "\e[01;31m"
|
15
|
+
BRIGHT_GREEN = "\e[01;32m"
|
16
|
+
BRIGHT_YELLOW = "\e[01;33m"
|
17
|
+
BRIGHT_BLUE = "\e[01;34m"
|
18
|
+
BRIGHT_MAGENTA = "\e[01;35m"
|
19
|
+
BRIGHT_CYAN = "\e[01;36m"
|
20
|
+
BRIGHT_WHITE = "\e[01;37m"
|
21
|
+
|
22
|
+
LOG_LEVELS = {
|
23
|
+
:debug => 1,
|
24
|
+
:warn => 2,
|
25
|
+
:normal => 3,
|
26
|
+
:info => 4
|
27
|
+
}
|
28
|
+
|
29
|
+
attr_accessor :color, :log_level, :destinations
|
30
|
+
|
31
|
+
def initialize(*args)
|
32
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
33
|
+
@color = options[:color]
|
34
|
+
@log_level = options[:debug] ? :debug : :normal
|
35
|
+
@destinations = []
|
36
|
+
|
37
|
+
dests = args
|
38
|
+
dests << STDOUT unless options[:quiet]
|
39
|
+
dests.uniq!
|
40
|
+
dests.each {|dest| add_destination(dest)}
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_destination(dest)
|
44
|
+
case dest
|
45
|
+
when IO
|
46
|
+
@destinations << dest
|
47
|
+
when String
|
48
|
+
@destinations << File.open(dest, 'w')
|
49
|
+
else
|
50
|
+
raise "Unsuitable log destination #{dest.inspect}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_destination(dest)
|
55
|
+
case dest
|
56
|
+
when IO
|
57
|
+
@destinations.delete(dest)
|
58
|
+
when String
|
59
|
+
@destinations.delete_if {|d| d.respond_to?(:path) and d.path == dest}
|
60
|
+
else
|
61
|
+
raise "Unsuitable log destination #{dest.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def is_debug?
|
66
|
+
LOG_LEVELS[@log_level] <= LOG_LEVELS[:debug]
|
67
|
+
end
|
68
|
+
|
69
|
+
def is_warn?
|
70
|
+
LOG_LEVELS[@log_level] <= LOG_LEVELS[:warn]
|
71
|
+
end
|
72
|
+
|
73
|
+
def host_output *args
|
74
|
+
return unless is_debug?
|
75
|
+
strings = strip_colors_from args
|
76
|
+
string = strings.join
|
77
|
+
optionally_color GREY, string, false
|
78
|
+
end
|
79
|
+
|
80
|
+
def debug *args
|
81
|
+
return unless is_debug?
|
82
|
+
optionally_color WHITE, args
|
83
|
+
end
|
84
|
+
|
85
|
+
def warn *args
|
86
|
+
return unless is_warn?
|
87
|
+
strings = args.map {|msg| "Warning: #{msg}" }
|
88
|
+
optionally_color YELLOW, strings
|
89
|
+
end
|
90
|
+
|
91
|
+
def success *args
|
92
|
+
optionally_color GREEN, args
|
93
|
+
end
|
94
|
+
|
95
|
+
def notify *args
|
96
|
+
optionally_color BRIGHT_WHITE, args
|
97
|
+
end
|
98
|
+
|
99
|
+
def error *args
|
100
|
+
optionally_color BRIGHT_RED, args
|
101
|
+
end
|
102
|
+
|
103
|
+
def strip_colors_from lines
|
104
|
+
Array(lines).map do |line|
|
105
|
+
line.gsub /\e\[(\d+;)?\d+m/, ''
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def optionally_color color_code, msg, add_newline = true
|
110
|
+
print_statement = add_newline ? :puts : :print
|
111
|
+
@destinations.each do |to|
|
112
|
+
to.print color_code if @color
|
113
|
+
to.send print_statement, msg
|
114
|
+
to.print NORMAL if @color
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# utility method to get the current call stack and format it
|
119
|
+
# to a human-readable string (which some IDEs/editors
|
120
|
+
# will recognize as links to the line numbers in the trace)
|
121
|
+
def pretty_backtrace backtrace = caller(1)
|
122
|
+
trace = purge_harness_files_from( Array( backtrace ) )
|
123
|
+
expand_symlinks( trace ).join "\n"
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
def expand_symlinks backtrace
|
128
|
+
backtrace.collect do |line|
|
129
|
+
file_path, line_num = line.split( ":" )
|
130
|
+
expanded_path = expand_symlink File.expand_path( file_path )
|
131
|
+
expanded_path.to_s + ":" + line_num.to_s
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def purge_harness_files_from backtrace
|
136
|
+
mostly_purged = backtrace.reject do |line|
|
137
|
+
# LOADED_FEATURES is an array of anything `require`d, i.e. everything
|
138
|
+
# but the test in question
|
139
|
+
$LOADED_FEATURES.any? do |require_path|
|
140
|
+
line.include? require_path
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# And remove lines that contain our program name in them
|
145
|
+
completely_purged = mostly_purged.reject {|line| line.include? $0 }
|
146
|
+
end
|
147
|
+
|
148
|
+
# utility method that takes a path as input, checks each component
|
149
|
+
# of the path to see if it is a symlink, and expands
|
150
|
+
# it if it is. returns the expanded path.
|
151
|
+
def expand_symlink file_path
|
152
|
+
file_path.split( "/" ).inject do |full_path, next_dir|
|
153
|
+
next_path = full_path + "/" + next_dir
|
154
|
+
if File.symlink? next_path
|
155
|
+
link = File.readlink next_path
|
156
|
+
next_path =
|
157
|
+
case link
|
158
|
+
when /^\// then link
|
159
|
+
else
|
160
|
+
File.expand_path( full_path + "/" + link )
|
161
|
+
end
|
162
|
+
end
|
163
|
+
next_path
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
%w(hypervisor).each do |lib|
|
2
|
+
begin
|
3
|
+
require "beaker/#{lib}"
|
4
|
+
rescue LoadError
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), lib))
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Beaker
|
10
|
+
class NetworkManager
|
11
|
+
HYPERVISOR_TYPES = ['solaris', 'blimpy', 'vsphere', 'fusion', 'aix', 'vcloud', 'vagrant']
|
12
|
+
|
13
|
+
def initialize(config, options, logger)
|
14
|
+
@logger = logger
|
15
|
+
@options = options
|
16
|
+
@hosts = []
|
17
|
+
@config = config
|
18
|
+
@virtual_machines = {}
|
19
|
+
@noprovision_machines = []
|
20
|
+
@config['HOSTS'].each_key do |name|
|
21
|
+
host_info = @config['HOSTS'][name]
|
22
|
+
#check to see if this host has a hypervisor
|
23
|
+
hypervisor = host_info['hypervisor']
|
24
|
+
#provision this box
|
25
|
+
# - only if we are running with --provision
|
26
|
+
# - only if we have a hypervisor
|
27
|
+
# - only if either the specific hosts has no specification or has 'provision' in its config
|
28
|
+
if @options[:provision] && hypervisor && (host_info.has_key?('provision') ? host_info['provision'] : true) #obey config file provision, defaults to provisioning vms
|
29
|
+
raise "Invalid hypervisor: #{hypervisor} (#{name})" unless HYPERVISOR_TYPES.include? hypervisor
|
30
|
+
@logger.debug "Hypervisor for #{name} is #{host_info['hypervisor'] || 'default' }, and I'm going to use #{hypervisor}"
|
31
|
+
@virtual_machines[hypervisor] = [] unless @virtual_machines[hypervisor]
|
32
|
+
@virtual_machines[hypervisor] << name
|
33
|
+
else #this is a non-provisioned machine, deal with it without hypervisors
|
34
|
+
@logger.debug "No hypervisor for #{name}, connecting to host without provisioning"
|
35
|
+
@noprovision_machines << name
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def provision
|
42
|
+
@provisioned_set = {}
|
43
|
+
@virtual_machines.each do |type, names|
|
44
|
+
hosts_for_type = []
|
45
|
+
#set up host objects for provisioned provisioned_set
|
46
|
+
names.each do |name|
|
47
|
+
host = Beaker::Host.create(name, @options, @config)
|
48
|
+
hosts_for_type << host
|
49
|
+
end
|
50
|
+
@provisioned_set[type] = Beaker::Hypervisor.create(type, hosts_for_type, @options, @config)
|
51
|
+
@hosts << hosts_for_type
|
52
|
+
end
|
53
|
+
@noprovision_machines.each do |name|
|
54
|
+
@hosts << Beaker::Host.create(name, @options, @config)
|
55
|
+
end
|
56
|
+
@hosts = @hosts.flatten
|
57
|
+
@hosts
|
58
|
+
end
|
59
|
+
|
60
|
+
def cleanup
|
61
|
+
#only cleanup if we aren't preserving hosts
|
62
|
+
#shut down connections
|
63
|
+
@hosts.each {|host| host.close }
|
64
|
+
|
65
|
+
if not @options[:preserve_hosts]
|
66
|
+
@provisioned_set.each_key do |type|
|
67
|
+
@provisioned_set[type].cleanup
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|