beaker 2.10.0 → 2.11.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.
- checksums.yaml +8 -8
- data/HISTORY.md +292 -4
- data/acceptance/tests/base/host.rb +1 -0
- data/lib/beaker/answers/version30.rb +10 -10
- data/lib/beaker/cli.rb +10 -8
- data/lib/beaker/command.rb +1 -1
- data/lib/beaker/dsl/helpers/facter_helpers.rb +10 -1
- data/lib/beaker/dsl/helpers/hiera_helpers.rb +0 -11
- data/lib/beaker/dsl/helpers/host_helpers.rb +12 -3
- data/lib/beaker/dsl/helpers/puppet_helpers.rb +11 -3
- data/lib/beaker/dsl/helpers/tk_helpers.rb +0 -12
- data/lib/beaker/dsl/helpers/web_helpers.rb +0 -12
- data/lib/beaker/dsl/install_utils/module_utils.rb +9 -6
- data/lib/beaker/dsl/install_utils/pe_utils.rb +60 -8
- data/lib/beaker/dsl/install_utils/puppet_utils.rb +15 -2
- data/lib/beaker/host.rb +11 -145
- data/lib/beaker/host/mac.rb +3 -7
- data/lib/beaker/host/mac/pkg.rb +43 -0
- data/lib/beaker/host/pswindows.rb +1 -1
- data/lib/beaker/host/pswindows/exec.rb +83 -2
- data/lib/beaker/host/pswindows/pkg.rb +9 -6
- data/lib/beaker/host/unix/exec.rb +105 -0
- data/lib/beaker/host/unix/pkg.rb +22 -9
- data/lib/beaker/host/windows.rb +2 -1
- data/lib/beaker/host/windows/exec.rb +1 -1
- data/lib/beaker/host/windows/pkg.rb +4 -7
- data/lib/beaker/host_prebuilt_steps.rb +14 -14
- data/lib/beaker/hypervisor/aws_sdk.rb +198 -114
- data/lib/beaker/hypervisor/openstack.rb +48 -25
- data/lib/beaker/shared/host_manager.rb +11 -2
- data/lib/beaker/ssh_connection.rb +26 -0
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/answers_spec.rb +56 -0
- data/spec/beaker/cli_spec.rb +16 -12
- data/spec/beaker/command_spec.rb +3 -0
- data/spec/beaker/dsl/install_utils/module_utils_spec.rb +2 -2
- data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +71 -3
- data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +4 -1
- data/spec/beaker/host/unix/pkg_spec.rb +10 -10
- data/spec/beaker/host_prebuilt_steps_spec.rb +3 -1
- data/spec/beaker/host_spec.rb +8 -2
- data/spec/beaker/hypervisor/vagrant_spec.rb +1 -0
- metadata +3 -2
@@ -46,9 +46,6 @@ module Beaker
|
|
46
46
|
# Perform the main launch work
|
47
47
|
launch_all_nodes()
|
48
48
|
|
49
|
-
# Wait for each node to reach status :running
|
50
|
-
wait_for_status(:running)
|
51
|
-
|
52
49
|
# Add metadata tags to each instance
|
53
50
|
add_tags()
|
54
51
|
|
@@ -69,26 +66,30 @@ module Beaker
|
|
69
66
|
nil #void
|
70
67
|
end
|
71
68
|
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# It goes without saying, but a #cleanup does nothing without a #provision
|
75
|
-
# method call first.
|
69
|
+
# Kill all instances.
|
76
70
|
#
|
71
|
+
# @param instances [Enumerable<EC2::Instance>]
|
77
72
|
# @return [void]
|
78
|
-
def
|
79
|
-
|
80
|
-
@hosts.each do |host|
|
81
|
-
# This was set previously during provisioning
|
82
|
-
instance = host['instance']
|
83
|
-
|
84
|
-
# Only attempt a terminate if the instance actually is set by provision
|
85
|
-
# and the instance actually 'exists'.
|
73
|
+
def kill_instances(instances)
|
74
|
+
instances.each do |instance|
|
86
75
|
if !instance.nil? and instance.exists?
|
76
|
+
@logger.notify("aws-sdk: killing EC2 instance #{instance.id}")
|
87
77
|
instance.terminate
|
88
78
|
end
|
89
79
|
end
|
80
|
+
nil
|
81
|
+
end
|
90
82
|
|
91
|
-
|
83
|
+
# Cleanup all earlier provisioned hosts on EC2 using the AWS::EC2 library
|
84
|
+
#
|
85
|
+
# It goes without saying, but a #cleanup does nothing without a #provision
|
86
|
+
# method call first.
|
87
|
+
#
|
88
|
+
# @return [void]
|
89
|
+
def cleanup
|
90
|
+
# Provisioning should have set the host 'instance' values.
|
91
|
+
kill_instances(@hosts.map{|h| h['instance']}.select{|x| !x.nil?})
|
92
|
+
nil
|
92
93
|
end
|
93
94
|
|
94
95
|
# Print instances to the logger. Instances will be from all regions
|
@@ -220,125 +221,209 @@ module Beaker
|
|
220
221
|
|
221
222
|
end
|
222
223
|
|
223
|
-
#
|
224
|
-
#
|
225
|
-
# This is where the main launching work occurs for each node. Here we take
|
226
|
-
# care of feeding the information from the image required into the config
|
227
|
-
# for the new host, we perform the launch operation and ensure that the
|
228
|
-
# instance is properly tagged for identification.
|
224
|
+
# Create an EC2 instance for host, tag it, and return it.
|
229
225
|
#
|
230
|
-
# @return [
|
226
|
+
# @return [AWS::EC2::Instance)]
|
231
227
|
# @api private
|
232
|
-
def
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
# Iterate across all hosts and launch them, adding tags along the way
|
237
|
-
@logger.notify("aws-sdk: Iterate across all hosts in configuration and launch them")
|
238
|
-
@hosts.each do |host|
|
239
|
-
amitype = host['vmname'] || host['platform']
|
240
|
-
amisize = host['amisize'] || 'm1.small'
|
241
|
-
subnet_id = host['subnet_id'] || @options['subnet_id'] || nil
|
242
|
-
vpc_id = host['vpc_id'] || @options['vpc_id'] || nil
|
228
|
+
def create_instance(host, ami_spec, subnet_id)
|
229
|
+
amitype = host['vmname'] || host['platform']
|
230
|
+
amisize = host['amisize'] || 'm1.small'
|
231
|
+
vpc_id = host['vpc_id'] || @options['vpc_id'] || nil
|
243
232
|
|
244
|
-
|
245
|
-
|
246
|
-
|
233
|
+
if vpc_id and !subnet_id
|
234
|
+
raise RuntimeError, "A subnet_id must be provided with a vpc_id"
|
235
|
+
end
|
247
236
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
end
|
237
|
+
# Use snapshot provided for this host
|
238
|
+
image_type = host['snapshot']
|
239
|
+
if not image_type
|
240
|
+
raise RuntimeError, "No snapshot/image_type provided for EC2 provisioning"
|
241
|
+
end
|
242
|
+
ami = ami_spec[amitype]
|
243
|
+
ami_region = ami[:region]
|
244
|
+
|
245
|
+
# Main region object for ec2 operations
|
246
|
+
region = @ec2.regions[ami_region]
|
247
|
+
|
248
|
+
# If we haven't defined a vpc_id then we use the default vpc for the provided region
|
249
|
+
if !vpc_id
|
250
|
+
@logger.notify("aws-sdk: filtering available vpcs in region by 'isDefault")
|
251
|
+
filtered_vpcs = region.client.describe_vpcs(:filters => [{:name => 'isDefault', :values => ['true']}])
|
252
|
+
if !filtered_vpcs[:vpc_set].empty?
|
253
|
+
vpc_id = filtered_vpcs[:vpc_set].first[:vpc_id]
|
254
|
+
else #there's no default vpc, use nil
|
255
|
+
vpc_id = nil
|
268
256
|
end
|
257
|
+
end
|
269
258
|
|
270
|
-
|
271
|
-
|
259
|
+
# Grab the vpc object based upon provided id
|
260
|
+
vpc = vpc_id ? region.vpcs[vpc_id] : nil
|
272
261
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
262
|
+
# Grab image object
|
263
|
+
image_id = ami[:image][image_type.to_sym]
|
264
|
+
@logger.notify("aws-sdk: Checking image #{image_id} exists and getting its root device")
|
265
|
+
image = region.images[image_id]
|
266
|
+
if image.nil? and not image.exists?
|
267
|
+
raise RuntimeError, "Image not found: #{image_id}"
|
268
|
+
end
|
280
269
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
}
|
270
|
+
# Transform the images block_device_mappings output into a format
|
271
|
+
# ready for a create.
|
272
|
+
orig_bdm = image.block_device_mappings()
|
273
|
+
@logger.notify("aws-sdk: Image block_device_mappings: #{orig_bdm.to_hash}")
|
274
|
+
block_device_mappings = []
|
275
|
+
orig_bdm.each do |device_name, rest|
|
276
|
+
block_device_mappings << {
|
277
|
+
:device_name => device_name,
|
278
|
+
:ebs => {
|
279
|
+
# Change the default size of the root volume.
|
280
|
+
:volume_size => host['volume_size'] || rest[:volume_size],
|
281
|
+
# This is required to override the images default for
|
282
|
+
# delete_on_termination, forcing all volumes to be deleted once the
|
283
|
+
# instance is terminated.
|
284
|
+
:delete_on_termination => true,
|
297
285
|
}
|
298
|
-
end
|
299
|
-
|
300
|
-
security_group = ensure_group(vpc || region, Beaker::EC2Helper.amiports(host))
|
301
|
-
|
302
|
-
# Launch the node, filling in the blanks from previous work.
|
303
|
-
@logger.notify("aws-sdk: Launch instance")
|
304
|
-
config = {
|
305
|
-
:count => 1,
|
306
|
-
:image_id => image_id,
|
307
|
-
:monitoring_enabled => true,
|
308
|
-
:key_pair => ensure_key_pair(region),
|
309
|
-
:security_groups => [security_group],
|
310
|
-
:instance_type => amisize,
|
311
|
-
:disable_api_termination => false,
|
312
|
-
:instance_initiated_shutdown_behavior => "terminate",
|
313
|
-
:block_device_mappings => block_device_mappings,
|
314
|
-
:subnet => subnet_id,
|
315
286
|
}
|
316
|
-
|
287
|
+
end
|
317
288
|
|
318
|
-
|
319
|
-
# manipulated by 'cleanup' for example.
|
320
|
-
host['instance'] = instance
|
289
|
+
security_group = ensure_group(vpc || region, Beaker::EC2Helper.amiports(host))
|
321
290
|
|
322
|
-
|
291
|
+
msg = "aws-sdk: launching %p on %p using %p/%p%s" %
|
292
|
+
[host.name, amitype, amisize, image_type,
|
293
|
+
subnet_id ? ("in %p" % subnet_id) : '']
|
294
|
+
@logger.notify(msg)
|
295
|
+
config = {
|
296
|
+
:count => 1,
|
297
|
+
:image_id => image_id,
|
298
|
+
:monitoring_enabled => true,
|
299
|
+
:key_pair => ensure_key_pair(region),
|
300
|
+
:security_groups => [security_group],
|
301
|
+
:instance_type => amisize,
|
302
|
+
:disable_api_termination => false,
|
303
|
+
:instance_initiated_shutdown_behavior => "terminate",
|
304
|
+
:block_device_mappings => block_device_mappings,
|
305
|
+
:subnet => subnet_id,
|
306
|
+
}
|
307
|
+
region.instances.create(config)
|
308
|
+
end
|
309
|
+
|
310
|
+
# For each host, create an EC2 instance in one of the specified
|
311
|
+
# subnets and push it onto instances_created. Each subnet will be
|
312
|
+
# tried at most once for each host, and more than one subnet may
|
313
|
+
# be tried if capacity constraints are encountered. Each Hash in
|
314
|
+
# instances_created will contain an :instance and :host value.
|
315
|
+
#
|
316
|
+
# @param hosts [Enumerable<Host>]
|
317
|
+
# @param subnets [Enumerable<String>]
|
318
|
+
# @param ami_spec [Hash]
|
319
|
+
# @param instances_created Enumerable<Hash{Symbol=>EC2::Instance,Host}>
|
320
|
+
# @return [void]
|
321
|
+
# @api private
|
322
|
+
def launch_nodes_on_some_subnet(hosts, subnets, ami_spec, instances_created)
|
323
|
+
# Shuffle the subnets so we don't always hit the same one
|
324
|
+
# first, and cycle though the subnets independently of the
|
325
|
+
# host, so we stick with one that's working. Try each subnet
|
326
|
+
# once per-host.
|
327
|
+
if subnets.nil? or subnets.empty?
|
328
|
+
return
|
323
329
|
end
|
330
|
+
subnet_i = 0
|
331
|
+
shuffnets = subnets.shuffle
|
332
|
+
hosts.each do |host|
|
333
|
+
instance = nil
|
334
|
+
shuffnets.length.times do
|
335
|
+
begin
|
336
|
+
subnet_id = shuffnets[subnet_i]
|
337
|
+
instance = create_instance(host, ami_spec, subnet_id)
|
338
|
+
instances_created.push({:instance => instance, :host => host})
|
339
|
+
break
|
340
|
+
rescue AWS::EC2::Errors::InsufficientInstanceCapacity => ex
|
341
|
+
@logger.notify("aws-sdk: hit #{subnet_id} capacity limit; moving on")
|
342
|
+
subnet_i = (subnet_i + 1) % shuffnets.length
|
343
|
+
end
|
344
|
+
end
|
345
|
+
if instance.nil?
|
346
|
+
raise RuntimeError, "unable to launch host in any requested subnet"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
324
350
|
|
351
|
+
# Create EC2 instances for all hosts, tag them, and wait until
|
352
|
+
# they're running. When a host provides a subnet_id, create the
|
353
|
+
# instance in that subnet, otherwise prefer a CONFIG subnet_id.
|
354
|
+
# If neither are set but there is a CONFIG subnet_ids list,
|
355
|
+
# attempt to create the host in each specified subnet, which might
|
356
|
+
# fail due to capacity constraints, for example. Specifying both
|
357
|
+
# a CONFIG subnet_id and subnet_ids will provoke an error.
|
358
|
+
#
|
359
|
+
# @return [void]
|
360
|
+
# @api private
|
361
|
+
def launch_all_nodes
|
362
|
+
@logger.notify("aws-sdk: launch all hosts in configuration")
|
363
|
+
ami_spec = YAML.load_file(@options[:ec2_yaml])["AMI"]
|
364
|
+
global_subnet_id = @options['subnet_id']
|
365
|
+
global_subnets = @options['subnet_ids']
|
366
|
+
if global_subnet_id and global_subnets
|
367
|
+
raise RuntimeError, 'Config specifies both subnet_id and subnet_ids'
|
368
|
+
end
|
369
|
+
no_subnet_hosts = []
|
370
|
+
specific_subnet_hosts = []
|
371
|
+
some_subnet_hosts = []
|
372
|
+
@hosts.each do |host|
|
373
|
+
if global_subnet_id or host['subnet_id']
|
374
|
+
specific_subnet_hosts.push(host)
|
375
|
+
elsif global_subnets
|
376
|
+
some_subnet_hosts.push(host)
|
377
|
+
else
|
378
|
+
no_subnet_hosts.push(host)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
instances = [] # Each element is {:instance => i, :host => h}
|
382
|
+
begin
|
383
|
+
@logger.notify("aws-sdk: launch instances not particular about subnet")
|
384
|
+
launch_nodes_on_some_subnet(some_subnet_hosts, global_subnets, ami_spec,
|
385
|
+
instances)
|
386
|
+
@logger.notify("aws-sdk: launch instances requiring a specific subnet")
|
387
|
+
specific_subnet_hosts.each do |host|
|
388
|
+
subnet_id = host['subnet_id'] || global_subnet_id
|
389
|
+
instance = create_instance(host, ami_spec, subnet_id)
|
390
|
+
instances.push({:instance => instance, :host => host})
|
391
|
+
end
|
392
|
+
@logger.notify("aws-sdk: launch instances requiring no subnet")
|
393
|
+
no_subnet_hosts.each do |host|
|
394
|
+
instance = create_instance(host, ami_spec, nil)
|
395
|
+
instances.push({:instance => instance, :host => host})
|
396
|
+
end
|
397
|
+
wait_for_status(:running, instances)
|
398
|
+
rescue Exception => ex
|
399
|
+
@logger.notify("aws-sdk: exception - #{ex}")
|
400
|
+
kill_instances(instances.map{|x| x[:instance]})
|
401
|
+
raise ex
|
402
|
+
end
|
403
|
+
# At this point, all instances should be running since wait
|
404
|
+
# either returns on success or throws an exception.
|
405
|
+
if instances.empty?
|
406
|
+
raise RuntimeError, "Didn't manage to launch any EC2 instances"
|
407
|
+
end
|
408
|
+
# Assign the now known running instances to their hosts.
|
409
|
+
instances.each {|x| x[:host]['instance'] = x[:instance]}
|
325
410
|
nil
|
326
411
|
end
|
327
412
|
|
328
|
-
#
|
413
|
+
# Wait until all instances reach the desired state. Each Hash in
|
414
|
+
# instances must contain an :instance and :host value.
|
329
415
|
#
|
330
416
|
# @param status [Symbol] EC2 state to wait for, :running :stopped etc.
|
417
|
+
# @param instances Enumerable<Hash{Symbol=>EC2::Instance,Host}>
|
331
418
|
# @return [void]
|
332
419
|
# @api private
|
333
|
-
def wait_for_status(status)
|
420
|
+
def wait_for_status(status, instances)
|
334
421
|
# Wait for each node to reach status :running
|
335
|
-
@logger.notify("aws-sdk:
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
@logger.notify("aws-sdk: Wait for status #{status} for node #{name}")
|
341
|
-
|
422
|
+
@logger.notify("aws-sdk: Waiting for all hosts to be #{status}")
|
423
|
+
instances.each do |x|
|
424
|
+
name = x[:name]
|
425
|
+
instance = x[:instance]
|
426
|
+
@logger.notify("aws-sdk: Wait for node #{name} to be #{status}")
|
342
427
|
# Here we keep waiting for the machine state to reach ':running' with an
|
343
428
|
# exponential backoff for each poll.
|
344
429
|
# TODO: should probably be a in a shared method somewhere
|
@@ -356,7 +441,6 @@ module Beaker
|
|
356
441
|
end
|
357
442
|
backoff_sleep(tries)
|
358
443
|
end
|
359
|
-
|
360
444
|
end
|
361
445
|
end
|
362
446
|
|
@@ -13,8 +13,9 @@ module Beaker
|
|
13
13
|
#@option options [String] :openstack_username The username to access the OpenStack instance with (required)
|
14
14
|
#@option options [String] :openstack_auth_url The URL to access the OpenStack instance with (required)
|
15
15
|
#@option options [String] :openstack_tenant The tenant to access the OpenStack instance with (required)
|
16
|
+
#@option options [String] :openstack_region The region that each OpenStack instance should be provisioned on (optional)
|
16
17
|
#@option options [String] :openstack_network The network that each OpenStack instance should be contacted through (required)
|
17
|
-
#@option options [String] :openstack_keyname The name of an existing key pair that should be auto-loaded onto each
|
18
|
+
#@option options [String] :openstack_keyname The name of an existing key pair that should be auto-loaded onto each
|
18
19
|
# OpenStack instance (optional)
|
19
20
|
#@option options [String] :jenkins_build_url Added as metadata to each OpenStack instance
|
20
21
|
#@option options [String] :department Added as metadata to each OpenStack instance
|
@@ -27,25 +28,36 @@ module Beaker
|
|
27
28
|
@hosts = openstack_hosts
|
28
29
|
@vms = []
|
29
30
|
|
30
|
-
raise 'You must specify an Openstack API key (:
|
31
|
+
raise 'You must specify an Openstack API key (:openstack_api_key) for OpenStack instances!' unless @options[:openstack_api_key]
|
31
32
|
raise 'You must specify an Openstack username (:openstack_username) for OpenStack instances!' unless @options[:openstack_username]
|
32
33
|
raise 'You must specify an Openstack auth URL (:openstack_auth_url) for OpenStack instances!' unless @options[:openstack_auth_url]
|
33
34
|
raise 'You must specify an Openstack tenant (:openstack_tenant) for OpenStack instances!' unless @options[:openstack_tenant]
|
34
35
|
raise 'You must specify an Openstack network (:openstack_network) for OpenStack instances!' unless @options[:openstack_network]
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
|
37
|
+
optionhash = {}
|
38
|
+
optionhash[:provider] = :openstack
|
39
|
+
optionhash[:openstack_api_key] = @options[:openstack_api_key]
|
40
|
+
optionhash[:openstack_username] = @options[:openstack_username]
|
41
|
+
optionhash[:openstack_auth_url] = @options[:openstack_auth_url]
|
42
|
+
optionhash[:openstack_tenant] = @options[:openstack_tenant]
|
43
|
+
optionhash[:openstack_region] = @options[:openstack_region] if @options[:openstack_region]
|
44
|
+
|
45
|
+
@compute_client ||= Fog::Compute.new(optionhash)
|
46
|
+
|
40
47
|
if not @compute_client
|
41
48
|
raise "Unable to create OpenStack Compute instance (api key: #{@options[:openstack_api_key]}, username: #{@options[:openstack_username]}, auth_url: #{@options[:openstack_auth_url]}, tenant: #{@options[:openstack_tenant]})"
|
42
49
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
50
|
+
|
51
|
+
networkoptionhash = {}
|
52
|
+
networkoptionhash[:provider] = :openstack
|
53
|
+
networkoptionhash[:openstack_api_key] = @options[:openstack_api_key]
|
54
|
+
networkoptionhash[:openstack_username] = @options[:openstack_username]
|
55
|
+
networkoptionhash[:openstack_auth_url] = @options[:openstack_auth_url]
|
56
|
+
networkoptionhash[:openstack_tenant] = @options[:openstack_tenant]
|
57
|
+
networkoptionhash[:openstack_region] = @options[:openstack_region] if @options[:openstack_region]
|
58
|
+
|
59
|
+
@network_client ||= Fog::Network.new(networkoptionhash)
|
60
|
+
|
49
61
|
if not @network_client
|
50
62
|
|
51
63
|
raise "Unable to create OpenStack Network instance (api_key: #{@options[:openstack_api_key]}, username: #{@options[:openstack_username]}, auth_url: #{@options[:openstack_auth_url]}, tenant: #{@options[:openstack_tenant]})"
|
@@ -85,9 +97,9 @@ module Beaker
|
|
85
97
|
@logger.debug "Provisioning #{host.name} (#{host[:vmhostname]})"
|
86
98
|
options = {
|
87
99
|
:flavor_ref => flavor(host[:flavor]).id,
|
88
|
-
:image_ref
|
89
|
-
:nics
|
90
|
-
:name
|
100
|
+
:image_ref => image(host[:image]).id,
|
101
|
+
:nics => [ {'net_id' => network(@options[:openstack_network]).id } ],
|
102
|
+
:name => host[:vmhostname],
|
91
103
|
}
|
92
104
|
options[:key_name] = key_name(host)
|
93
105
|
vm = @compute_client.servers.create(options)
|
@@ -117,24 +129,35 @@ module Beaker
|
|
117
129
|
#
|
118
130
|
# Do we already have an address?
|
119
131
|
@logger.debug vm.addresses
|
120
|
-
|
132
|
+
address=nil
|
121
133
|
begin
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
rescue NoMethodError
|
126
|
-
@logger.debug "No current address retrievable from OpenStack data"
|
127
|
-
end
|
128
|
-
unless address
|
134
|
+
# Here we try and assign an address from a floating IP pool
|
135
|
+
# This seems to fail on some implementations (FloatingIpPoolNotFound)
|
129
136
|
ip = @compute_client.addresses.find { |ip| ip.instance_id.nil? }
|
130
|
-
|
131
137
|
if ip.nil?
|
132
138
|
@logger.debug "Creating IP for #{host.name} (#{host[:vmhostname]})"
|
133
139
|
ip = @compute_client.addresses.create
|
134
140
|
end
|
135
141
|
ip.server = vm
|
136
142
|
address = ip.ip
|
143
|
+
|
144
|
+
rescue Fog::Compute::OpenStack::NotFound
|
145
|
+
# Here, we fail to just trying to use an address that's already assigned if there is one
|
146
|
+
# There may be better logic, but this worked in the original implementation
|
147
|
+
# There might be an argument for checking whether an address is reachable a la
|
148
|
+
# port_open? logic in host.rb but maybe race conditions
|
149
|
+
|
150
|
+
begin
|
151
|
+
if vm.addresses[@options[:openstack_network]]
|
152
|
+
address = vm.addresses[@options[:openstack_network]].map{ |network| network['addr'] }.first
|
153
|
+
end
|
154
|
+
rescue NoMethodError
|
155
|
+
@logger.debug "No current address retrievable from OpenStack data"
|
156
|
+
end
|
157
|
+
|
137
158
|
end
|
159
|
+
|
160
|
+
raise 'Could not find or assign an address to the instance' unless address
|
138
161
|
host[:ip] = address
|
139
162
|
|
140
163
|
@logger.debug "OpenStack host #{host.name} (#{host[:vmhostname]}) assigned ip: #{host[:ip]}"
|