beaker 2.10.0 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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]}"
|