Class: Beaker::AwsSdk

Inherits:
Hypervisor
  • Object
show all
Defined in:
lib/beaker/hypervisor/aws_sdk.rb

Overview

This is an alternate EC2 driver that implements direct API access using Amazon’s AWS-SDK library: / SDK For Ruby

It is built for full control, to reduce any other layers beyond the pure vendor API.

Direct Known Subclasses

Ec2

Constant Summary collapse

ZOMBIE =

anything older than 3 hours is considered a zombie

3
PING_SECURITY_GROUP_NAME =
'beaker-ping'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hosts, options) ⇒ AwsSdk

Initialize AwsSdk hypervisor driver

Parameters:

  • hosts (Array<Beaker::Host>)

    Array of Beaker::Host objects

  • options (Hash<String, String>)

    Options hash



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 28

def initialize(hosts, options)
  @hosts = hosts
  @options = options
  @logger = options[:logger]
  @default_region = ENV['AWS_REGION'] || 'us-west-2'

  # Get AWS credentials
  creds = options[:use_fog_credentials] ? load_credentials() : nil

  config = {
    :credentials   => creds,
    :logger        => Logger.new($stdout),
    :log_level     => :debug,
    :log_formatter => Aws::Log::Formatter.colored,
    :retry_limit   => 12,
    :region        => ENV['AWS_REGION'] || 'us-west-2'
  }.delete_if{ |k,v| v.nil? }
  Aws.config.update(config)

  @client = {}
  @client.default_proc = proc do |hash, key|
    hash[key] = Aws::EC2::Client.new(:region => key)
  end

  test_split_install()
end

Instance Attribute Details

#default_regionObject (readonly)

Returns the value of attribute default_region.



22
23
24
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 22

def default_region
  @default_region
end

Instance Method Details

#add_ingress_rule(cl, sg_group, cidr_ip, from_port, to_port, protocol = 'tcp') ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Authorizes connections from certain CIDR to a range of ports

Parameters:

  • cl (Aws::EC2::Client)
  • sg_group (Aws::EC2::SecurityGroup)

    the AWS security group

  • cidr_ip (String)

    CIDR used for outbound security group rule

  • from_port (String)

    Starting Port number in the range

  • to_port (String)

    Ending Port number in the range



1054
1055
1056
1057
1058
1059
1060
1061
1062
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 1054

def add_ingress_rule(cl, sg_group, cidr_ip, from_port, to_port, protocol = 'tcp')
  cl.authorize_security_group_ingress(
    :cidr_ip     => cidr_ip,
    :ip_protocol => protocol,
    :from_port   => from_port,
    :to_port     => to_port,
    :group_id    => sg_group.group_id,
  )
end

#add_tagsvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Add metadata tags to all instances



539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 539

def add_tags
  @hosts.each do |host|
    instance = host['instance']

    # Define tags for the instance
    @logger.notify("aws-sdk: Add tags for #{host.name}")

    tags = [
      {
        :key   => 'jenkins_build_url',
        :value => @options[:jenkins_build_url],
      },
      {
        :key   => 'Name',
        :value => host.name,
      },
      {
        :key   => 'department',
        :value => @options[:department],
      },
      {
        :key   => 'project',
        :value => @options[:project],
      },
      {
        :key   => 'created_by',
        :value => @options[:created_by],
      },
    ]

    host[:host_tags].each do |name, val|
      tags << { :key => name.to_s, :value => val }
    end

    client.create_tags(
      :resources => [instance.instance_id],
      :tags      => tags.reject { |r| r[:value].nil? },
    )
  end

  nil
end

#backoff_sleep(tries) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Calculates and waits a back-off period based on the number of tries

Logs each backupoff time and retry value to the console.

Parameters:

  • tries (Number)

    number of tries to calculate back-off period



764
765
766
767
768
769
770
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 764

def backoff_sleep(tries)
  # Exponential with some randomization
  sleep_time = 2 ** tries
  @logger.notify("aws-sdk: Sleeping #{sleep_time} seconds for attempt #{tries}.")
  sleep sleep_time
  nil
end

#cleanupvoid

This method returns an undefined value.

Cleanup all earlier provisioned hosts on EC2 using the Aws::EC2 library

It goes without saying, but a #cleanup does nothing without a #provision method call first.



118
119
120
121
122
123
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 118

def cleanup
  # Provisioning should have set the host 'instance' values.
  kill_instances(@hosts.map{ |h| h['instance'] }.select{ |x| !x.nil? })
  delete_key_pair_all_regions()
  nil
end

#client(region = default_region) ⇒ Object



55
56
57
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 55

def client(region = default_region)
  @client[region]
end

#configure_hostsvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

f5 hosts are skipped since this isn’t a valid step there

This method returns an undefined value.

Configure /etc/hosts for each node



619
620
621
622
623
624
625
626
627
628
629
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 619

def configure_hosts
  non_netdev_windows_hosts = @hosts.select{ |h| !(h['platform'] =~ /f5-|netscaler|windows/) }
  non_netdev_windows_hosts.each do |host|
    host_entries = non_netdev_windows_hosts.map do |h|
      h == host ? etc_hosts_entry(h, :private_ip) : etc_hosts_entry(h)
    end
    host_entries.unshift "127.0.0.1\tlocalhost localhost.localdomain\n"
    set_etc_hosts(host, host_entries.join(''))
  end
  nil
end

#create_group(region_or_vpc, ports, sg_cidr_ips = ['0.0.0.0/0']) ⇒ Aws::EC2::SecurityGroup

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Create a new security group

Accepts a region or VPC for group creation.

Parameters:

  • region_or_vpc (Aws::EC2::Region, Aws::EC2::VPC)

    the AWS region or vpc control object

  • ports (Array<Number>)

    an array of port numbers

  • sg_cidr_ips (Array<String>) (defaults to: ['0.0.0.0/0'])

    CIDRs used for outbound security group rule

Returns:

  • (Aws::EC2::SecurityGroup)

    created security group



1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 1017

def create_group(region_or_vpc, ports, sg_cidr_ips = ['0.0.0.0/0'])
  name = group_id(ports)
  @logger.notify("aws-sdk: Creating group #{name} for ports #{ports.to_s}")
  @logger.notify("aws-sdk: Creating group #{name} with CIDR IPs #{sg_cidr_ips.to_s}")
  cl = region_or_vpc.is_a?(String) ? client(region_or_vpc) : client

  params = {
    :description => "Custom Beaker security group for #{ports.to_a}",
    :group_name  => name,
  }

  params[:vpc_id] = region_or_vpc.vpc_id if region_or_vpc.is_a?(Aws::EC2::Types::Vpc)

  group = cl.create_security_group(params)

  unless ports.is_a? Set
    ports = Set.new(ports)
  end

  sg_cidr_ips.each do |cidr_ip|
    ports.each do |port|
      add_ingress_rule(cl, group, cidr_ip, port, port)
    end
  end

  group
end

#create_instance(host, ami_spec, subnet_id) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Create an EC2 instance for host, tag it, and return it.

Raises:

  • (RuntimeError)


264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 264

def create_instance(host, ami_spec, subnet_id)
  amitype = host['vmname'] || host['platform']
  amisize = host['amisize'] || 'm1.small'
  vpc_id = host['vpc_id'] || @options['vpc_id'] || nil
  host['sg_cidr_ips'] = host['sg_cidr_ips'] || '0.0.0.0/0';
  sg_cidr_ips = host['sg_cidr_ips'].split(',')

  if vpc_id && !subnet_id
    raise RuntimeError, "A subnet_id must be provided with a vpc_id"
  end

  # Use snapshot provided for this host
  image_type = host['snapshot']
  raise RuntimeError, "No snapshot/image_type provided for EC2 provisioning" unless image_type

  ami = ami_spec[amitype]
  ami_region = ami[:region]

  # Main region object for ec2 operations
  region = ami_region

  # If we haven't defined a vpc_id then we use the default vpc for the provided region
  unless vpc_id
    @logger.notify("aws-sdk: filtering available vpcs in region by 'isDefault'")

    default_vpcs = client(region).describe_vpcs(:filters => [{:name => 'isDefault', :values => ['true']}])
    vpc_id = if default_vpcs.vpcs.empty?
               nil
             else
               default_vpcs.vpcs.first.vpc_id
             end
  end

  # Grab the vpc object based upon provided id
  vpc = vpc_id ? client(region).describe_vpcs(:vpc_ids => [vpc_id]).vpcs.first : nil

  # Grab image object
  image_id = ami[:image][image_type.to_sym]
  @logger.notify("aws-sdk: Checking image #{image_id} exists and getting its root device")
  image = client(region).describe_images(:image_ids => [image_id]).images.first
  raise RuntimeError, "Image not found: #{image_id}" if image.nil?

  @logger.notify("Image Storage Type: #{image.root_device_type}")

  # Transform the images block_device_mappings output into a format
  # ready for a create.
  block_device_mappings = []
  if image.root_device_type == :ebs
    orig_bdm = image.block_device_mappings
    @logger.notify("aws-sdk: Image block_device_mappings: #{orig_bdm}")
    orig_bdm.each do |block_device|
      block_device_mappings << {
        :device_name => block_device.device_name,
        :ebs => {
          # Change the default size of the root volume.
          :volume_size => host['volume_size'] || block_device.ebs.volume_size,
          # This is required to override the images default for
          # delete_on_termination, forcing all volumes to be deleted once the
          # instance is terminated.
          :delete_on_termination => true,
        }
      }
    end
  end

  security_group = ensure_group(vpc || region, Beaker::EC2Helper.amiports(host), sg_cidr_ips)
  #check if ping is enabled
  ping_security_group = ensure_ping_group(vpc || region, sg_cidr_ips)

  msg = "aws-sdk: launching %p on %p using %p/%p%s" %
        [host.name, amitype, amisize, image_type,
         subnet_id ? ("in %p" % subnet_id) : '']
  @logger.notify(msg)
  config = {
    :max_count  => 1,
    :min_count  => 1,
    :image_id   => image_id,
    :monitoring => {
      :enabled => true,
    },
    :key_name => ensure_key_pair(region).key_pairs.first.key_name,
    :instance_type => amisize,
    :disable_api_termination => false,
    :instance_initiated_shutdown_behavior => "terminate",
    :network_interfaces => [{
      :subnet_id => subnet_id,
      :groups => [security_group.group_id, ping_security_group.group_id],
      :device_index => 0,
    }],
  }
  assoc_pub_ip_addr = host['associate_public_ip_address']
  config[:network_interfaces][0][:associate_public_ip_address] = assoc_pub_ip_addr unless assoc_pub_ip_addr.nil?
  config[:block_device_mappings] = block_device_mappings if image.root_device_type == :ebs
  reservation = client(region).run_instances(config)
  reservation.instances.first
end

#create_new_key_pair(region, pair_name) ⇒ Aws::EC2::KeyPair

Create a new key pair for a given Beaker run

Parameters:

  • region (Aws::EC2::Region)

    the region the key pair will be imported into

  • pair_name (String)

    the name of the key to be created

Returns:

  • (Aws::EC2::KeyPair)

    key pair created

Raises:

  • (RuntimeError)

    raised if AWS keypair not created



893
894
895
896
897
898
899
900
901
902
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 893

def create_new_key_pair(region, pair_name)
  @logger.debug("aws-sdk: importing new key pair: #{pair_name}")
  client(region).import_key_pair(:key_name => pair_name, :public_key_material => public_key)

  begin
    client(region).wait_until(:key_pair_exists, { :key_names => [pair_name] }, :max_attempts => 5, :delay => 2)
  rescue Aws::Waiters::Errors::WaiterFailed
    raise RuntimeError, "AWS key pair #{pair_name} can not be queried, even after import"
  end
end

#create_ping_group(region_or_vpc, sg_cidr_ips = ['0.0.0.0/0']) ⇒ Aws::EC2::SecurityGroup

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Create a new ping enabled security group

Accepts a region or VPC for group creation.

Parameters:

  • region_or_vpc (Aws::EC2::Region, Aws::EC2::VPC)

    the AWS region or vpc control object

  • sg_cidr_ips (Array<String>) (defaults to: ['0.0.0.0/0'])

    CIDRs used for outbound security group rule

Returns:

  • (Aws::EC2::SecurityGroup)

    created security group



982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 982

def create_ping_group(region_or_vpc, sg_cidr_ips = ['0.0.0.0/0'])
  @logger.notify("aws-sdk: Creating group #{PING_SECURITY_GROUP_NAME}")
  cl = region_or_vpc.is_a?(String) ? client(region_or_vpc) : client

  params = {
    :description => 'Custom Beaker security group to enable ping',
    :group_name  => PING_SECURITY_GROUP_NAME,
  }
  params[:vpc_id] = region_or_vpc.vpc_id if region_or_vpc.is_a?(Aws::EC2::Types::Vpc)

  group = cl.create_security_group(params)

  sg_cidr_ips.each do |cidr_ip|
    add_ingress_rule(
      cl,
      group,
      cidr_ip,
      '8', # 8 == ICMPv4 ECHO request
      '-1', # -1 == All ICMP codes
      'icmp',
    )
  end

  group
end

#delete_key_pair(region, pair_name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Deletes a given key pair

Parameters:

  • region (Aws::EC2::Region)

    the region the key belongs to

  • pair_name (String)

    the name of the key to be deleted



876
877
878
879
880
881
882
883
884
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 876

def delete_key_pair(region, pair_name)
  kp = client(region).describe_key_pairs(:key_names => [pair_name]).key_pairs.first
  unless kp.nil?
    @logger.debug("aws-sdk: delete key pair in region: #{region}")
    client(region).delete_key_pair(:key_name => pair_name)
  end
rescue Aws::EC2::Errors::InvalidKeyPairNotFound
  nil
end

#delete_key_pair_all_regions(keypair_name_filter = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Deletes key pairs from all regions

Parameters:

  • keypair_name_filter (String) (defaults to: nil)

    if given, will get all keypairs that match a simple String#start_with? filter. If no filter is given, the basic key name returned by #key_name will be used.

Returns:

  • nil



839
840
841
842
843
844
845
846
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 839

def delete_key_pair_all_regions(keypair_name_filter=nil)
  region_keypairs_hash = my_key_pairs(keypair_name_filter)
  region_keypairs_hash.each_pair do |region, keypair_name_array|
    keypair_name_array.each do |keypair_name|
      delete_key_pair(region, keypair_name)
    end
  end
end

#enable_root(host) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Enables root access for a host when username is not root



650
651
652
653
654
655
656
657
658
659
660
661
662
663
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 650

def enable_root(host)
  if host['user'] != 'root'
    if host['platform'] =~ /f5-/
      enable_root_f5(host)
    elsif host['platform'] =~ /netscaler/
      enable_root_netscaler(host)
    else
      copy_ssh_to_root(host, @options)
      (host, @options)
      host['user'] = 'root'
    end
    host.close
  end
end

#enable_root_f5(host) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

This method does not support other platforms

Enables root access for a host on an f5 platform

Returns:

  • nil



670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 670

def enable_root_f5(host)
  for tries in 1..10
    begin
      #This command is problematic as the F5 is not always done loading
      if host.exec(Command.new("modify sys db systemauth.disablerootlogin value false"), :acceptable_exit_codes => [0,1]).exit_code == 0 \
          and host.exec(Command.new("modify sys global-settings gui-setup disabled"), :acceptable_exit_codes => [0,1]).exit_code == 0 \
          and host.exec(Command.new("save sys config"), :acceptable_exit_codes => [0,1]).exit_code == 0
        backoff_sleep(tries)
        break
      elsif tries == 10
        raise "Instance was unable to be configured"
      end
    rescue Beaker::Host::CommandFailure => e
      @logger.debug("Instance not yet configured (#{e})")
    end
    backoff_sleep(tries)
  end
  host['user'] = 'root'
  host.close
  sha256 = Digest::SHA256.new
  password = sha256.hexdigest((1..50).map{(rand(86)+40).chr}.join.gsub(/\\/,'\&\&'))
  host['ssh'] = {:password => password}
  host.exec(Command.new("echo -e '#{password}\\n#{password}' | tmsh modify auth password admin"))
  @logger.notify("f5: Configured admin password to be #{password}")
end

#enable_root_netscaler(host) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

This method does not support other platforms

Enables root access for a host on an netscaler platform

Returns:

  • nil



701
702
703
704
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 701

def enable_root_netscaler(host)
  host['ssh'] = {:password => host['instance'].instance_id}
  @logger.notify("netscaler: nsroot password is #{host['instance'].instance_id}")
end

#enable_root_on_hostsvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Enables root for instances with custom username like ubuntu-amis



635
636
637
638
639
640
641
642
643
644
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 635

def enable_root_on_hosts
  @hosts.each do |host|
    if host['disable_root_ssh'] == true
      @logger.notify("aws-sdk: Not enabling root for instance as disable_root_ssh is set to 'true'.")
    else
      @logger.notify("aws-sdk: Enabling root ssh")
      enable_root(host)
    end
  end
end

#ensure_group(vpc, ports, sg_cidr_ips = ['0.0.0.0/0']) ⇒ Aws::EC2::SecurityGroup

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return an existing group, or create new one

Accepts a VPC as input for checking & creation.

Parameters:

  • vpc (Aws::EC2::VPC)

    the AWS vpc control object

  • ports (Array<Number>)

    an array of port numbers

  • sg_cidr_ips (Array<String>) (defaults to: ['0.0.0.0/0'])

    CIDRs used for outbound security group rule

Returns:

  • (Aws::EC2::SecurityGroup)

    created security group



956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 956

def ensure_group(vpc, ports, sg_cidr_ips = ['0.0.0.0/0'])
  @logger.notify("aws-sdk: Ensure security group exists for ports #{ports.to_s}, create if not")
  name = group_id(ports)

  group = client.describe_security_groups(
    :filters => [
      { :name => 'group-name', :values => [name] },
      { :name => 'vpc-id', :values => [vpc.vpc_id] },
    ]
  ).security_groups.first

  if group.nil?
    group = create_group(vpc, ports, sg_cidr_ips)
  end

  group
end

#ensure_key_pair(region) ⇒ Aws::EC2::KeyPair

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Creates the KeyPair for this test run

Parameters:

  • region (Aws::EC2::Region)

    region to create the key pair in

Returns:

  • (Aws::EC2::KeyPair)

    created key_pair



825
826
827
828
829
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 825

def ensure_key_pair(region)
  pair_name = key_name()
  delete_key_pair(region, pair_name)
  create_new_key_pair(region, pair_name)
end

#ensure_ping_group(vpc, sg_cidr_ips = ['0.0.0.0/0']) ⇒ Aws::EC2::SecurityGroup

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return an existing group, or create new one

Accepts a VPC as input for checking & creation.

Parameters:

  • vpc (Aws::EC2::VPC)

    the AWS vpc control object

  • sg_cidr_ips (Array<String>) (defaults to: ['0.0.0.0/0'])

    CIDRs used for outbound security group rule

Returns:

  • (Aws::EC2::SecurityGroup)

    created security group



930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 930

def ensure_ping_group(vpc, sg_cidr_ips = ['0.0.0.0/0'])
  @logger.notify("aws-sdk: Ensure security group exists that enables ping, create if not")

  group = client.describe_security_groups(
    :filters => [
      { :name => 'group-name', :values => [PING_SECURITY_GROUP_NAME] },
      { :name => 'vpc-id', :values => [vpc.vpc_id] },
    ]
  ).security_groups.first

  if group.nil?
    group = create_ping_group(vpc, sg_cidr_ips)
  end

  group
end

#etc_hosts_entry(host, interface = :ip) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return a valid /etc/hosts line for a given host

Parameters:

  • host (Beaker::Host)

    Beaker::Host object for generating /etc/hosts entry

  • interface (Symbol) (defaults to: :ip)

    Symbol identifies which ip should be used for host

Returns:

  • (String)

    formatted hosts entry for host



606
607
608
609
610
611
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 606

def etc_hosts_entry(host, interface = :ip)
  name = host.name
  domain = get_domain_name(host)
  ip = host[interface.to_s]
  "#{ip}\t#{name} #{name}.#{domain} #{host['dns_name']}\n"
end

#group_id(ports) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return a reproducable security group identifier based on input ports

Parameters:

  • ports (Array<Number>)

    array of port numbers

Returns:

  • (String)

    group identifier



909
910
911
912
913
914
915
916
917
918
919
920
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 909

def group_id(ports)
  if ports.nil? or ports.empty?
    raise ArgumentError, "Ports list cannot be nil or empty"
  end

  unless ports.is_a? Set
    ports = Set.new(ports)
  end

  # Lolwut, #hash is inconsistent between ruby processes
  "Beaker-#{Zlib.crc32(ports.inspect)}"
end

#instance_by_id(id) ⇒ Aws::EC2::Types::Instance

Provided an id return an instance object. Instance object will respond to methods described here: AWS Instance Object.

Parameters:

  • id (String)

    The id of the instance to return

Returns:



156
157
158
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 156

def instance_by_id(id)
  client.describe_instances(:instance_ids => [id]).reservations.first.instances.first
end

#instancesArray<Aws::Ec2::Types::Instance>

Return all instances currently on ec2.

Returns:

  • (Array<Aws::Ec2::Types::Instance>)

    An array of Aws::EC2 instance objects

See Also:



163
164
165
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 163

def instances
  client.describe_instances.reservations.map(&:instances).flatten
end

#key_nameString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generate a reusable key name from the local hosts hostname

Returns:

  • (String)

    safe key name for current host



808
809
810
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 808

def key_name
  "#{key_name_prefix}-#{@options[:aws_keyname_modifier]}-#{@options[:timestamp].strftime("%F_%H_%M_%S_%N")}"
end

#key_name_prefixString

Note:

This is the part of the key that will stay static between Beaker runs on the same host.

Generate a key prefix for key pair names

Returns:

  • (String)

    Beaker key pair name based on sanitized hostname



799
800
801
802
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 799

def key_name_prefix
  safe_hostname = Socket.gethostname.gsub('.', '-')
  "Beaker-#{local_user}-#{safe_hostname}"
end

#kill_instances(instances) ⇒ void

This method returns an undefined value.

Kill all instances.

Parameters:



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 98

def kill_instances(instances)
  running_instances = instances.compact.select do |instance|
    instance_by_id(instance.instance_id).state.name == 'running'
  end
  instance_ids = running_instances.map(&:instance_id)

  return nil if instance_ids.empty?

  @logger.notify("aws-sdk: killing EC2 instance(s) #{instance_ids.join(', ')}")
  client.terminate_instances(:instance_ids => instance_ids)

  nil
end

#kill_zombie_volumesObject

Destroy any volumes marked ‘available’, INCLUDING THOSE YOU DON’T OWN! Use with care.



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 233

def kill_zombie_volumes
  # Occasionaly, tearing down ec2 instances leaves orphaned EBS volumes behind -- these stack up quickly.
  # This simply looks for EBS volumes that are not in use
  @logger.notify("aws-sdk: Kill Zombie Volumes!")
  volume_count = 0

  regions.each do |region|
    @logger.debug "Reviewing: #{region}"
    available_volumes = client(region).describe_volumes(
      :filters => [
        { :name => 'status', :values => ['available'], }
      ]
    ).volumes

    available_volumes.each do |volume|
      begin
        client(region).delete_volume(:volume_id => volume.id)
        volume_count += 1
      rescue Aws::EC2::Errors::InvalidVolume::NotFound => e
        @logger.debug "Failed to remove volume: #{volume.id} #{e}"
      end
    end
  end

  @logger.notify "Freed #{volume_count} volume(s)"
end

#kill_zombies(max_age = ZOMBIE, key = key_name) ⇒ Object

Shutdown and destroy ec2 instances idenfitied by key that have been alive longer than ZOMBIE hours.

Parameters:

  • max_age (Integer) (defaults to: ZOMBIE)

    The age in hours that a machine needs to be older than to be considered a zombie

  • key (String) (defaults to: key_name)

    The key_name to match for



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 202

def kill_zombies(max_age = ZOMBIE, key = key_name)
  @logger.notify("aws-sdk: Kill Zombies! (keyname: #{key}, age: #{max_age} hrs)")

  instances_to_kill = []

  time_now = Time.now.getgm #ec2 uses GM time

  #examine all available regions
  regions.each do |region|
    @logger.debug "Reviewing: #{region}"

    client(region).describe_instances.reservations.each do |reservation|
      reservation.instances.each do |instance|
        if (instance.key_name =~ /#{key}/)
          @logger.debug "Examining #{instance.instance_id} (keyname: #{instance.key_name}, launch time: #{instance.launch_time}, state: #{instance.state.name})"
          if ((time_now - instance.launch_time) >  max_age*60*60) and instance.state.name !~ /terminated/
            @logger.debug "Kill! #{instance.instance_id}: #{instance.key_name} (Current status: #{instance.state.name})"
            instances_to_kill << instance
          end
        end
      end
    end
  end

  kill_instances(instances_to_kill)
  delete_key_pair_all_regions(key_name_prefix)

  @logger.notify "#{key}: Killed #{instances_to_kill.length} instance(s)"
end

#launch_all_nodesvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Create EC2 instances for all hosts, tag them, and wait until they’re running. When a host provides a subnet_id, create the instance in that subnet, otherwise prefer a CONFIG subnet_id. If neither are set but there is a CONFIG subnet_ids list, attempt to create the host in each specified subnet, which might fail due to capacity constraints, for example. Specifying both a CONFIG subnet_id and subnet_ids will provoke an error.



412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 412

def launch_all_nodes
  @logger.notify("aws-sdk: launch all hosts in configuration")
  ami_spec = YAML.load_file(@options[:ec2_yaml])["AMI"]
  global_subnet_id = @options['subnet_id']
  global_subnets = @options['subnet_ids']
  if global_subnet_id and global_subnets
    raise RuntimeError, 'Config specifies both subnet_id and subnet_ids'
  end
  no_subnet_hosts = []
  specific_subnet_hosts = []
  some_subnet_hosts = []
  @hosts.each do |host|
    if global_subnet_id or host['subnet_id']
      specific_subnet_hosts.push(host)
    elsif global_subnets
      some_subnet_hosts.push(host)
    else
      no_subnet_hosts.push(host)
    end
  end
  instances = [] # Each element is {:instance => i, :host => h}
  begin
    @logger.notify("aws-sdk: launch instances not particular about subnet")
    launch_nodes_on_some_subnet(some_subnet_hosts, global_subnets, ami_spec,
                                instances)
    @logger.notify("aws-sdk: launch instances requiring a specific subnet")
    specific_subnet_hosts.each do |host|
      subnet_id = host['subnet_id'] || global_subnet_id
      instance = create_instance(host, ami_spec, subnet_id)
      instances.push({:instance => instance, :host => host})
    end
    @logger.notify("aws-sdk: launch instances requiring no subnet")
    no_subnet_hosts.each do |host|
      instance = create_instance(host, ami_spec, nil)
      instances.push({:instance => instance, :host => host})
    end
    wait_for_status(:running, instances)
  rescue Exception => ex
    @logger.notify("aws-sdk: exception #{ex.class}: #{ex}")
    kill_instances(instances.map{|x| x[:instance]})
    raise ex
  end
  # At this point, all instances should be running since wait
  # either returns on success or throws an exception.
  if instances.empty?
    raise RuntimeError, "Didn't manage to launch any EC2 instances"
  end
  # Assign the now known running instances to their hosts.
  instances.each {|x| x[:host]['instance'] = x[:instance]}
  nil
end

#launch_nodes_on_some_subnet(hosts, subnets, ami_spec, instances_created) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

For each host, create an EC2 instance in one of the specified subnets and push it onto instances_created. Each subnet will be tried at most once for each host, and more than one subnet may be tried if capacity constraints are encountered. Each Hash in instances_created will contain an :instance and :host value.

Parameters:

  • hosts (Enumerable<Host>)
  • subnets (Enumerable<String>)
  • ami_spec (Hash)
  • instances_created

    Enumerable<HashSymbol=>EC2::Instance,Host>



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 373

def launch_nodes_on_some_subnet(hosts, subnets, ami_spec, instances_created)
  # Shuffle the subnets so we don't always hit the same one
  # first, and cycle though the subnets independently of the
  # host, so we stick with one that's working.  Try each subnet
  # once per-host.
  if subnets.nil? or subnets.empty?
    return
  end
  subnet_i = 0
  shuffnets = subnets.shuffle
  hosts.each do |host|
    instance = nil
    shuffnets.length.times do
      begin
        subnet_id = shuffnets[subnet_i]
        instance = create_instance(host, ami_spec, subnet_id)
        instances_created.push({:instance => instance, :host => host})
        break
      rescue Aws::EC2::Errors::InsufficientInstanceCapacity
        @logger.notify("aws-sdk: hit #{subnet_id} capacity limit; moving on")
        subnet_i = (subnet_i + 1) % shuffnets.length
      end
    end
    if instance.nil?
      raise RuntimeError, "unable to launch host in any requested subnet"
    end
  end
end

#load_credentialsHash<Symbol, String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return a hash containing AWS credentials

Returns:

  • (Hash<Symbol, String>)

    AWS credentials



1068
1069
1070
1071
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 1068

def load_credentials
  return load_env_credentials if load_env_credentials.set?
  load_fog_credentials(@options[:dot_fog])
end

#load_env_credentials(prefix = 'AWS') ⇒ Aws::Credentials

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return AWS credentials loaded from environment variables

Parameters:

  • prefix (String) (defaults to: 'AWS')

    environment variable prefix

Returns:

  • (Aws::Credentials)

    ec2 credentials



1078
1079
1080
1081
1082
1083
1084
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 1078

def load_env_credentials(prefix='AWS')
  Aws::Credentials.new(
    ENV["#{prefix}_ACCESS_KEY_ID"],
    ENV["#{prefix}_SECRET_ACCESS_KEY"],
    ENV["#{prefix}_SESSION_TOKEN"]
  )
end

#load_fog_credentials(dot_fog = '.fog') ⇒ Aws::Credentials

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return a hash containing the fog credentials for EC2

Parameters:

  • dot_fog (String) (defaults to: '.fog')

    dot fog path

Returns:

  • (Aws::Credentials)

    ec2 credentials



1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 1091

def load_fog_credentials(dot_fog = '.fog')
  default = get_fog_credentials(dot_fog)

  raise "You must specify an aws_access_key_id in your .fog file (#{dot_fog}) for ec2 instances!" unless default[:aws_access_key_id]
  raise "You must specify an aws_secret_access_key in your .fog file (#{dot_fog}) for ec2 instances!" unless default[:aws_secret_access_key]

  Aws::Credentials.new(
    default[:aws_access_key_id],
    default[:aws_secret_access_key],
    default[:aws_session_token]
  )
end

#local_userString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the local user running this tool

Returns:

  • (String)

    username of local user



816
817
818
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 816

def local_user
  ENV['USER']
end

#log_instances(key = key_name, status = /running/) ⇒ Object

Print instances to the logger. Instances will be from all regions associated with provided key name and limited by regex compared to instance status. Defaults to running instances.

Parameters:

  • key (String) (defaults to: key_name)

    The key_name to match for

  • status (Regex) (defaults to: /running/)

    The regular expression to match against the instance’s status



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 131

def log_instances(key = key_name, status = /running/)
  instances = []
  regions.each do |region|
    @logger.debug "Reviewing: #{region}"
    client(region).describe_instances.reservations.each do |reservation|
      reservation.instances.each do |instance|
        if (instance.key_name =~ /#{key}/) and (instance.state.name =~ status)
          instances << instance
        end
      end
    end
  end
  output = ""
  instances.each do |instance|
    dns_name = instance.public_dns_name || instance.private_dns_name
    output << "#{instance.instance_id} keyname: #{instance.key_name}, dns name: #{dns_name}, private ip: #{instance.private_ip_address}, ip: #{instance.public_ip_address}, launch time #{instance.launch_time}, status: #{instance.state.name}\n"
  end
  @logger.notify("aws-sdk: List instances (keyname: #{key})")
  @logger.notify("#{output}")
end

#my_key_pairs(name_filter = nil) ⇒ Hash{String=>Array[String]}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Gets the Beaker user’s keypairs by region

Parameters:

  • name_filter (String) (defaults to: nil)

    if given, will get all keypairs that match a simple String#start_with? filter. If no filter is given, the basic key name returned by #key_name will be used.

Returns:

  • (Hash{String=>Array[String]})

    a hash of region name to an array of the keypair names that match for the filter



857
858
859
860
861
862
863
864
865
866
867
868
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 857

def my_key_pairs(name_filter=nil)
  keypairs_by_region = {}
  key_name_filter = name_filter ? "#{name_filter}-*" : key_name

  regions.each do |region|
    keypairs_by_region[region] = client(region).describe_key_pairs(
      :filters => [{ :name => 'key-name', :values => [key_name_filter] }]
    ).key_pairs.map(&:key_name)
  end

  keypairs_by_region
end

#populate_dnsvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Populate the hosts IP address from the EC2 dns_name



586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 586

def populate_dns
  # Obtain the IP addresses and dns_name for each host
  @hosts.each do |host|
    @logger.notify("aws-sdk: Populate DNS for #{host.name}")
    instance = host['instance']
    host['ip'] = instance.public_ip_address || instance.private_ip_address
    host['private_ip'] = instance.private_ip_address
    host['dns_name'] = instance.public_dns_name || instance.private_dns_name
    @logger.notify("aws-sdk: name: #{host.name} ip: #{host['ip']} private_ip: #{host['private_ip']} dns_name: #{host['dns_name']}")
  end

  nil
end

#provisionvoid

This method returns an undefined value.

Provision all hosts on EC2 using the Aws::EC2 API



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 62

def provision
  start_time = Time.now

  # Perform the main launch work
  launch_all_nodes()

  wait_for_status_netdev()

  # Add metadata tags to each instance
  add_tags()

  # Grab the ip addresses and dns from EC2 for each instance to use for ssh
  populate_dns()

  #enable root if user is not root
  enable_root_on_hosts()

  # Set the hostname for each box
  set_hostnames()

  # Configure /etc/hosts on each host
  configure_hosts()

  @logger.notify("aws-sdk: Provisioning complete in #{Time.now - start_time} seconds")

  nil #void
end

#public_keyString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Retrieve the public key locally from the executing users ~/.ssh directory

Returns:

  • (String)

    contents of public key



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 776

def public_key
  keys = Array(@options[:ssh][:keys])
  keys << '~/.ssh/id_rsa'
  keys << '~/.ssh/id_dsa'
  key_file = keys.find do |key|
    key_pub = key + '.pub'
    File.exist?(File.expand_path(key_pub)) && File.exist?(File.expand_path(key))
  end

  if key_file
    @logger.debug("Using public key: #{key_file}")
  else
    raise RuntimeError, "Expected to find a public key, but couldn't in #{keys}"
  end
  File.read(File.expand_path(key_file + '.pub'))
end

#regionsObject



90
91
92
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 90

def regions
  @regions ||= client.describe_regions.regions.map(&:region_name)
end

#security_group_by_id(id) ⇒ Aws::EC2::Types::SecurityGroup

Provided an id return a security group object Security object will respond to methods described here: AWS SecurityGroup Object.

Parameters:

  • id (String)

    The id of the security group to return

Returns:

  • (Aws::EC2::Types::SecurityGroup)

    An Aws::EC2 security group object



186
187
188
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 186

def security_group_by_id(id)
  client.describe_security_groups(:group_ids => [id]).security_groups.first
end

#security_groupsArray<Aws::EC2::Types::SecurityGroup>

Return all security groups currently on ec2.

Returns:

  • (Array<Aws::EC2::Types::SecurityGroup>)

    An array of Aws::EC2 security group objects

See Also:

  • AwsSdk#security_goup_by_id


193
194
195
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 193

def security_groups
  client.describe_security_groups.security_groups
end

#set_hostnames@hosts

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Set the :vmhostname for each host object to be the dns_name, which is accessible publicly. Then configure each ec2 machine to that dns_name, so that when facter is installed the facts for hostname and domain match the dns_name.

if :use_beaker_hostnames: is true, set the :vmhostname and hostname of each ec2 machine to the host from the beaker hosts file.

Returns:

  • (@hosts)


715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 715

def set_hostnames
  if @options[:use_beaker_hostnames]
    @hosts.each do |host|
      host[:vmhostname] = host.name
      if host['platform'] =~ /el-7/
        # on el-7 hosts, the hostname command doesn't "stick" randomly
        host.exec(Command.new("hostnamectl set-hostname #{host.name}"))
      elsif host['platform'] =~ /windows/
        @logger.notify('aws-sdk: Change hostname on windows is not supported.')
      else
        next if host['platform'] =~ /netscaler/
        host.exec(Command.new("hostname #{host.name}"))
        if host['vmname'] =~ /^amazon/
          # Amazon Linux requires this to preserve host name changes across reboots.
          # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-hostname.html
          # Also note that without an elastic ip set, while this will
          # preserve the hostname across a full shutdown/startup of the vm
          # (as opposed to a reboot) -- the ip address will have changed.
          host.exec(Command.new("sed -ie '/^HOSTNAME/ s/=.*/=#{host.name}/' /etc/sysconfig/network"))
        end
      end
    end
  else
    @hosts.each do |host|
      host[:vmhostname] = host[:dns_name]
      if host['platform'] =~ /el-7/
        # on el-7 hosts, the hostname command doesn't "stick" randomly
        host.exec(Command.new("hostnamectl set-hostname #{host.hostname}"))
      elsif host['platform'] =~ /windows/
        @logger.notify('aws-sdk: Change hostname on windows is not supported.')
      else
        next if host['platform'] =~ /netscaler/
        host.exec(Command.new("hostname #{host.hostname}"))
        if host['vmname'] =~ /^amazon/
          # See note above
          host.exec(Command.new("sed -ie '/^HOSTNAME/ s/=.*/=#{host.hostname}/' /etc/sysconfig/network"))
        end
      end
    end
  end
end

#test_split_installObject

Adds port 8143 to host if master, database and dashboard are not on same instance



1106
1107
1108
1109
1110
1111
1112
1113
1114
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 1106

def test_split_install
  @hosts.each do |host|
    mono_roles = ['master', 'database', 'dashboard']
    roles_intersection = host[:roles] & mono_roles
    if roles_intersection.size != 3 && roles_intersection.any?
      host[:additional_ports] ? host[:additional_ports].push(8143) : host[:additional_ports] = [8143]
    end
  end
end

#vpc_by_id(id) ⇒ Aws::EC2::Types::Vpc

Provided an id return a VPC object. VPC object will respond to methods described here: AWS VPC Object.

Parameters:

  • id (String)

    The id of the VPC to return

Returns:

  • (Aws::EC2::Types::Vpc)

    An Aws::EC2 vpc object



171
172
173
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 171

def vpc_by_id(id)
  client.describe_vpcs(:vpc_ids => [id]).vpcs.first
end

#vpcsArray<Aws::EC2::Types::Vpc>

Return all VPCs currently on ec2.

Returns:

  • (Array<Aws::EC2::Types::Vpc>)

    An array of Aws::EC2 vpc objects

See Also:



178
179
180
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 178

def vpcs
  client.describe_vpcs.vpcs
end

#wait_for_status(state_name, instances, &block) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Wait until all instances reach the desired state. Each Hash in instances must contain an :instance and :host value.

FIXME: rename to #wait_for_state

Parameters:

  • state_name (String)

    EC2 state to wait for, ‘running’, ‘stopped’, etc.

  • instances

    Enumerable<HashSymbol=>EC2::Instance,Host>

  • block (Proc)

    more complex checks can be made by passing a block in. This overrides the status parameter. EC2::Instance objects from the hosts will be yielded to the passed block



476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 476

def wait_for_status(state_name, instances, &block)
  # Wait for each node to reach status :running
  @logger.notify("aws-sdk: Waiting for all hosts to be #{state_name}")
  instances.each do |x|
    name = x[:host].name
    instance = x[:instance]
    @logger.notify("aws-sdk: Wait for node #{name} to be #{state_name}")
    # Here we keep waiting for the machine state to reach 'running' with an
    # exponential backoff for each poll.
    # TODO: should probably be a in a shared method somewhere
    for tries in 1..10
      refreshed_instance = instance_by_id(instance.instance_id)

      if refreshed_instance.nil?
        @logger.debug("Instance #{name} not yet available (#{e})")
      else
        if block_given?
          test_result = yield refreshed_instance
        else
          test_result = refreshed_instance.state.name.to_s == state_name.to_s
        end
        if test_result
          x[:instance] = refreshed_instance
          # Always sleep, so the next command won't cause a throttle
          backoff_sleep(tries)
          break
        elsif tries == 10
          raise "Instance never reached state #{state_name}"
        end
      end

      backoff_sleep(tries)
    end
  end
end

#wait_for_status_netdevvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

if any host is an netdev one, these checks will happen once across all of the hosts, and then we’ll exit

This method returns an undefined value.

Handles special checks needed for netdev platforms.



519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
# File 'lib/beaker/hypervisor/aws_sdk.rb', line 519

def wait_for_status_netdev()
  @hosts.each do |host|
    if host['platform'] =~ /f5-|netscaler/
      wait_for_status(:running, @hosts)

      wait_for_status(nil, @hosts) do |instance|
        instance_status_collection = instance.client.describe_instance_status({:instance_ids => [instance.instance_id]})
        first_instance = instance_status_collection.reservations.first.instances.first
        first_instance[:system_status][:status] == "ok"
      end

      break
    end
  end
end