Update Heat autoscaling test

Heat OSTF test 'test autoscaling' was updated:
- Added new templates to use Ceilometer autoscaling resources
- Added checks to the test that Ceilometer is installed
- Removed now unnecessary Fedora17 image checks

Now in this test we will check Heat+Ceilometer autoscaling instead
of native one. So it is totally different test.

Closes-Bug: #1361576
Change-Id: I535709f0be8392d5aad988b461f996f43340d252
This commit is contained in:
Anastasia Kuznetsova 2014-11-24 12:31:42 +04:00
parent a35f516f16
commit 52fb4bf568
6 changed files with 218 additions and 362 deletions

View File

@ -1,133 +0,0 @@
heat_template_version: '2013-05-23'
description: AWS CloudFormation Sample Template
parameters:
InstanceType:
type: string
ImageId:
type: string
KeyName:
type: string
SecurityGroup:
type: string
resources:
CfnUser:
type: AWS::IAM::User
AKeys:
type: AWS::IAM::AccessKey
properties:
UserName: {Ref: CfnUser}
AGroup:
type: AWS::AutoScaling::AutoScalingGroup
properties:
AvailabilityZones: {'Fn::GetAZs': ""}
LaunchConfigurationName: {Ref: LaunchConfig}
MinSize: '1'
MaxSize: '2'
VPCZoneIdentifier: []
AScaleUpPolicy:
type: AWS::AutoScaling::ScalingPolicy
properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: {Ref: AGroup}
Cooldown: '60'
ScalingAdjustment: '1'
AScaleDownPolicy:
type: AWS::AutoScaling::ScalingPolicy
properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: {Ref: AGroup}
Cooldown: '60'
ScalingAdjustment: '-1'
CPUAlarmHigh:
type: OS::Heat::CWLiteAlarm
properties:
AlarmDescription: Scale-up if CPU > 50% for 1 minute
MetricName: CPUUtilization
Namespace: system/linux
Statistic: Average
Period: '60'
EvaluationPeriods: '1'
Threshold: '50'
AlarmActions:
- {Ref: AScaleUpPolicy}
Dimensions:
- Name: AutoScalingGroupName
Value: {Ref: AGroup}
ComparisonOperator: GreaterThanThreshold
CPUAlarmLow:
type: OS::Heat::CWLiteAlarm
properties:
AlarmDescription: Scale-down if CPU < 30% for 1 minute
MetricName: CPUUtilization
Namespace: system/linux
Statistic: Average
Period: '60'
EvaluationPeriods: '1'
Threshold: '30'
AlarmActions:
- {Ref: AScaleDownPolicy}
Dimensions:
- Name: AutoScalingGroupName
Value: {Ref: AGroup}
ComparisonOperator: LessThanThreshold
LaunchConfig:
type: AWS::AutoScaling::LaunchConfiguration
metadata:
AWS::CloudFormation::Init:
config:
files:
/etc/cfn/cfn-credentials:
content:
Fn::Join:
- ''
- - AWSAccessKeyId=
- {Ref: AKeys}
- '
'
- AWSSecretKey=
- get_attr: [AKeys, SecretAccessKey]
- '
'
mode: '000400'
owner: root
group: root
/tmp/stats-crontab.txt:
content:
Fn::Join:
- ''
- - 'MAIL=""
'
- '
'
- '* * * * * /opt/aws/bin/cfn-push-stats --watch '
- {Ref: CPUAlarmHigh}
- ' --cpu-util
'
- '* * * * * /opt/aws/bin/cfn-push-stats --watch '
- {Ref: CPUAlarmLow}
- ' --cpu-util
'
mode: '000600'
owner: root
group: root
properties:
ImageId: {Ref: ImageId}
InstanceType: {Ref: InstanceType}
KeyName: {Ref: KeyName}
SecurityGroups: [{Ref: SecurityGroup}]
UserData:
str_replace:
template: |
#!/bin/bash -v
/opt/aws/bin/cfn-init -s $stack_id -r LaunchConfig
# install crontab
crontab /tmp/stats-crontab.txt
touch /tmp/vm_ready.txt
params:
$stack_id: {get_param: "OS::stack_id"}

View File

@ -0,0 +1,76 @@
heat_template_version: 2013-05-23
parameters:
KeyName:
type: string
InstanceType:
type: string
ImageId:
type: string
SecurityGroup:
type: string
Subnet:
type: string
resources:
my_asg:
type: OS::Heat::AutoScalingGroup
properties:
resource:
type: OS::Nova::Server
properties:
metadata: {"metering.stack": {get_param: "OS::stack_id"}}
key_name: { get_param: KeyName }
image: { get_param: ImageId }
flavor: { get_param: InstanceType }
security_groups:
- get_param: SecurityGroup
networks:
- network: {get_param: Subnet}
min_size: 1
max_size: 2
scale_up_policy:
type: OS::Heat::ScalingPolicy
properties:
adjustment_type: change_in_capacity
auto_scaling_group_id: {get_resource: my_asg}
cooldown: 60
scaling_adjustment: 1
scale_down_policy:
type: OS::Heat::ScalingPolicy
properties:
adjustment_type: change_in_capacity
auto_scaling_group_id: {get_resource: my_asg}
cooldown: 60
scaling_adjustment: '-1'
cpu_alarm_high:
type: OS::Ceilometer::Alarm
properties:
description: Scale-up if the average CPU > 50% for 1 minute
meter_name: cpu_util
statistic: avg
period: 60
evaluation_periods: 1
threshold: 50
alarm_actions:
- {get_attr: [scale_up_policy, alarm_url]}
matching_metadata: {'metadata.user_metadata.stack': {get_param: "OS::stack_id"}}
comparison_operator: gt
cpu_alarm_low:
type: OS::Ceilometer::Alarm
properties:
description: Scale-down if the average CPU < 15% for 1 minutes
meter_name: cpu_util
statistic: avg
period: 60
evaluation_periods: 1
threshold: 15
alarm_actions:
- {get_attr: [scale_down_policy, alarm_url]}
matching_metadata: {'metadata.user_metadata.stack': {get_param: "OS::stack_id"}}
comparison_operator: lt

View File

@ -0,0 +1,72 @@
heat_template_version: 2013-05-23
parameters:
KeyName:
type: string
InstanceType:
type: string
ImageId:
type: string
SecurityGroup:
type: string
resources:
my_asg:
type: OS::Heat::AutoScalingGroup
properties:
resource:
type: OS::Nova::Server
properties:
metadata: {"metering.stack": {get_param: "OS::stack_id"}}
key_name: { get_param: KeyName }
image: { get_param: ImageId }
flavor: { get_param: InstanceType }
security_groups:
- get_param: SecurityGroup
min_size: 1
max_size: 2
scale_up_policy:
type: OS::Heat::ScalingPolicy
properties:
adjustment_type: change_in_capacity
auto_scaling_group_id: {get_resource: my_asg}
cooldown: 60
scaling_adjustment: 1
scale_down_policy:
type: OS::Heat::ScalingPolicy
properties:
adjustment_type: change_in_capacity
auto_scaling_group_id: {get_resource: my_asg}
cooldown: 60
scaling_adjustment: '-1'
cpu_alarm_high:
type: OS::Ceilometer::Alarm
properties:
description: Scale-up if the average CPU > 50% for 1 minute
meter_name: cpu_util
statistic: avg
period: 60
evaluation_periods: 1
threshold: 50
alarm_actions:
- {get_attr: [scale_up_policy, alarm_url]}
matching_metadata: {'metadata.user_metadata.stack': {get_param: "OS::stack_id"}}
comparison_operator: gt
cpu_alarm_low:
type: OS::Ceilometer::Alarm
properties:
description: Scale-down if the average CPU < 15% for 1 minutes
meter_name: cpu_util
statistic: avg
period: 60
evaluation_periods: 1
threshold: 15
alarm_actions:
- {get_attr: [scale_down_policy, alarm_url]}
matching_metadata: {'metadata.user_metadata.stack': {get_param: "OS::stack_id"}}
comparison_operator: lt

View File

@ -1,135 +0,0 @@
heat_template_version: '2013-05-23'
description: AWS CloudFormation Sample Template
parameters:
InstanceType:
type: string
ImageId:
type: string
Subnet:
type: string
KeyName:
type: string
SecurityGroup:
type: string
resources:
CfnUser:
type: AWS::IAM::User
AKeys:
type: AWS::IAM::AccessKey
properties:
UserName: {Ref: CfnUser}
AGroup:
type: AWS::AutoScaling::AutoScalingGroup
properties:
AvailabilityZones: {'Fn::GetAZs': ""}
LaunchConfigurationName: {Ref: LaunchConfig}
MinSize: '1'
MaxSize: '2'
VPCZoneIdentifier: [{Ref: Subnet}]
AScaleUpPolicy:
type: AWS::AutoScaling::ScalingPolicy
properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: {Ref: AGroup}
Cooldown: '60'
ScalingAdjustment: '1'
AScaleDownPolicy:
type: AWS::AutoScaling::ScalingPolicy
properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: {Ref: AGroup}
Cooldown: '60'
ScalingAdjustment: '-1'
CPUAlarmHigh:
type: OS::Heat::CWLiteAlarm
properties:
AlarmDescription: Scale-up if CPU > 50% for 1 minute
MetricName: CPUUtilization
Namespace: system/linux
Statistic: Average
Period: '60'
EvaluationPeriods: '1'
Threshold: '50'
AlarmActions:
- {Ref: AScaleUpPolicy}
Dimensions:
- Name: AutoScalingGroupName
Value: {Ref: AGroup}
ComparisonOperator: GreaterThanThreshold
CPUAlarmLow:
type: OS::Heat::CWLiteAlarm
properties:
AlarmDescription: Scale-down if CPU < 30% for 1 minute
MetricName: CPUUtilization
Namespace: system/linux
Statistic: Average
Period: '60'
EvaluationPeriods: '1'
Threshold: '30'
AlarmActions:
- {Ref: AScaleDownPolicy}
Dimensions:
- Name: AutoScalingGroupName
Value: {Ref: AGroup}
ComparisonOperator: LessThanThreshold
LaunchConfig:
type: AWS::AutoScaling::LaunchConfiguration
metadata:
AWS::CloudFormation::Init:
config:
files:
/etc/cfn/cfn-credentials:
content:
Fn::Join:
- ''
- - AWSAccessKeyId=
- {Ref: AKeys}
- '
'
- AWSSecretKey=
- get_attr: [AKeys, SecretAccessKey]
- '
'
mode: '000400'
owner: root
group: root
/tmp/stats-crontab.txt:
content:
Fn::Join:
- ''
- - 'MAIL=""
'
- '
'
- '* * * * * /opt/aws/bin/cfn-push-stats --watch '
- {Ref: CPUAlarmHigh}
- ' --cpu-util
'
- '* * * * * /opt/aws/bin/cfn-push-stats --watch '
- {Ref: CPUAlarmLow}
- ' --cpu-util
'
mode: '000600'
owner: root
group: root
properties:
ImageId: {Ref: ImageId}
InstanceType: {Ref: InstanceType}
KeyName: {Ref: KeyName}
SecurityGroups: [{Ref: SecurityGroup}]
UserData:
str_replace:
template: |
#!/bin/bash -v
/opt/aws/bin/cfn-init -s $stack_id -r LaunchConfig
# install crontab
crontab /tmp/stats-crontab.txt
touch /tmp/vm_ready.txt
params:
$stack_id: {get_param: "OS::stack_id"}

View File

@ -129,10 +129,6 @@ class HeatBaseTest(fuel_health.nmanager.NovaNetworkScenarioTest):
self.wait_interval): self.wait_interval):
self.fail("Timed out waiting for stack to be deleted.") self.fail("Timed out waiting for stack to be deleted.")
def _find_heat_image(self, image_name):
return image_name in [i.name for i in
self.compute_client.images.list()]
def _wait_for_autoscaling(self, exp_count, def _wait_for_autoscaling(self, exp_count,
timeout, interval, reduced_stack_name): timeout, interval, reduced_stack_name):
LOG.info('expected count is {0}'.format(exp_count)) LOG.info('expected count is {0}'.format(exp_count))
@ -151,13 +147,14 @@ class HeatBaseTest(fuel_health.nmanager.NovaNetworkScenarioTest):
return fuel_health.test.call_until_true( return fuel_health.test.call_until_true(
count_instances, timeout, interval, reduced_stack_name) count_instances, timeout, interval, reduced_stack_name)
def _wait_for_cloudinit(self, conn_string, timeout, interval): def _wait_for_vm_ready_for_load(self, conn_string, timeout, interval):
""" """
Wait for fake file (described in the stack template) to be created Wait for fake file to be created on the instance
on the instance to make sure cloud-init procedure is completed. to make sure that vm is ready.
""" """
cmd = (conn_string + cmd = (conn_string +
" test -f /tmp/vm_ready.txt && echo -ne YES || echo -ne NO") " 'touch /tmp/ostf-heat.txt; "
"test -f /tmp/ostf-heat.txt && echo -ne YES || echo -ne NO'")
def check(): def check():
return self._run_ssh_cmd(cmd)[0] == "YES" return self._run_ssh_cmd(cmd)[0] == "YES"
@ -167,19 +164,23 @@ class HeatBaseTest(fuel_health.nmanager.NovaNetworkScenarioTest):
def _save_key_to_file(self, key): def _save_key_to_file(self, key):
return self._run_ssh_cmd( return self._run_ssh_cmd(
"KEY=`mktemp`; echo '%s' > $KEY; echo -ne $KEY;" % key)[0] "KEY=`mktemp`; echo '%s' > $KEY; "
"chmod 600 $KEY; echo -ne $KEY;" % key)[0]
def _delete_key_file(self, filepath): def _delete_key_file(self, filepath):
self._run_ssh_cmd("rm -f %s" % filepath) self._run_ssh_cmd("rm -f %s" % filepath)
def _load_vm_cpu(self, connection_string): def _load_vm_cpu(self, connection_string):
return self._run_ssh_cmd( self._run_ssh_cmd(connection_string + " 'rm -f /tmp/ostf-heat.txt'")
connection_string + " cat /dev/urandom | gzip -9 > /dev/null &")[0] return self._run_ssh_cmd(connection_string +
" 'cat /dev/urandom |"
" gzip -9 > /dev/null &'")[0]
def _release_vm_cpu(self, connection_string): def _release_vm_cpu(self, connection_string):
pid = self._run_ssh_cmd(connection_string + pid = self._run_ssh_cmd(connection_string +
" ps -ef | grep \"cat /dev/urandom\" " ' ps -ef | grep \"cat /dev/urandom\" '
"| grep -v grep | awk '{print $2}'")[0] '| grep -v grep | awk \"{print $1}\"')[0]
return self._run_ssh_cmd(connection_string + return self._run_ssh_cmd(connection_string +
" kill -9 %s" % pid.strip())[0] " kill -9 %s" % pid.strip())[0]

View File

@ -15,7 +15,6 @@
import logging import logging
from fuel_health import heatmanager from fuel_health import heatmanager
from fuel_health.common.utils import data_utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -24,9 +23,6 @@ LOG = logging.getLogger(__name__)
class HeatSmokeTests(heatmanager.HeatBaseTest): class HeatSmokeTests(heatmanager.HeatBaseTest):
""" """
Test class verifies Heat API calls, rollback and autoscaling use-cases. Test class verifies Heat API calls, rollback and autoscaling use-cases.
Special requirements:
1. Fedora-17 image with pre-installed cfntools and cloud-init packages
should be imported.
""" """
def setUp(self): def setUp(self):
super(HeatSmokeTests, self).setUp() super(HeatSmokeTests, self).setUp()
@ -201,86 +197,67 @@ class HeatSmokeTests(heatmanager.HeatBaseTest):
Target component: Heat Target component: Heat
Scenario: Scenario:
1. Check that image with cfntools package is imported. 1. Create a keypair.
2. Create a flavor. 2. Save generated private key to file on Controller node.
3. Create a keypair. 3. Create a security group.
4. Save generated private key to file on Controller node. 4. Create a stack.
5. Create a security group. 5. Wait for the stack status to change to 'CREATE_COMPLETE'.
6. Create a stack. 6. Create a floating IP.
7. Wait for the stack status to change to 'CREATE_COMPLETE'. 7. Assign the floating IP to the instance of the stack.
8. Create a floating ip. 8. Wait for instance is ready for load.
9. Assign the floating ip to the instance of the stack. 9. Load the instance CPU to initiate the stack scaling up.
10. Wait for cloud_init procedure to be completed on the instance. 10. Wait for the 2nd instance to be launched.
11. Load the instance CPU to initiate the stack scaling up. 11. Release the instance CPU to initiate the stack scaling down.
12. Wait for the 2nd instance to be launched. 12. Wait for the 2nd instance to be terminated.
13. Release the instance CPU to initiate the stack scaling down. 13. Delete the file with private key.
14. Wait for the 2nd instance to be terminated. 14. Delete the stack.
15. Delete the file with private key. 15. Wait for the stack to be deleted.
16. Delete the stack. Duration: 2100 s.
17. Wait for the stack to be deleted.
Duration: 3000 s.
""" """
msg = ("Autoscaling with native cloudwatch mechanism does " if not self.ceilometer_client:
"not work in Heat when used multi-engine architecture.") self.skipTest("This test can't be run in current configuration. "
"It checks Heat autoscaling using "
"Ceilometer resources, so Ceilometer "
"should be installed.")
if 'ha' in self.config.mode: self.check_image_exists()
LOG.debug(msg)
self.skipTest(msg)
image_name = "F17-x86_64-cfntools" keypair = self.verify(10, self._create_keypair, 1,
msg = ("Image with cfntools package wasn't "
"imported into Glance, please check "
"http://docs.mirantis.com/openstack/fuel/fuel"
"-5.0/user-guide.html#platform-tests-description")
image_available = self._find_heat_image(image_name)
if not image_available:
LOG.debug(msg)
self.skipTest(msg)
flavor_name = data_utils.rand_name('ostf-heat-flavor-')
flavor = self.verify(10, self.compute_client.flavors.create, 2,
"Flavor can not be created.", "flavor creation",
flavor_name, 512, 1, 12)
self.flavors.append(flavor)
keypair = self.verify(10, self._create_keypair, 3,
'Keypair can not be created.', 'Keypair can not be created.',
'keypair creation', 'keypair creation',
self.compute_client) self.compute_client)
path_to_key = self.verify(10, self._save_key_to_file, 4, path_to_key = self.verify(10, self._save_key_to_file, 2,
"Private key can not be saved to file.", "Private key can not be saved to file.",
"saving private key to the file", "saving private key to the file",
keypair.private_key) keypair.private_key)
sec_group = self.verify(10, self._create_security_group, 5, sec_group = self.verify(10, self._create_security_group, 3,
'Security group can not be created.', 'Security group can not be created.',
'security group creation', 'security group creation',
self.compute_client, 'ost1_test-sgroup') self.compute_client, 'ost1_test-sgroup')
parameters = { parameters = {
"KeyName": keypair.name, "KeyName": keypair.name,
"InstanceType": flavor.name, "InstanceType": self.testvm_flavor.name,
"ImageId": image_name, "ImageId": self.config.compute.image_name,
"SecurityGroup": sec_group.name "SecurityGroup": sec_group.name
} }
if 'neutron' in self.config.network.network_provider:
parameters['Subnet'] = self._get_subnet_id()
template = self._load_template('heat_autoscaling_template.yaml')
else:
template = self._load_template('heat_autoscale_nova.yaml')
# create stack if 'neutron' in self.config.network.network_provider:
parameters['Subnet'] = self.private_net
template = self._load_template('heat_autoscaling_neutron.yaml')
else:
template = self._load_template('heat_autoscaling_nova.yaml')
fail_msg = "Stack was not created properly." fail_msg = "Stack was not created properly."
stack = self.verify(20, self._create_stack, 6, stack = self.verify(20, self._create_stack, 4,
fail_msg, "stack creation", fail_msg, "stack creation",
self.heat_client, template, self.heat_client, template,
parameters=parameters) parameters=parameters)
self.verify(600, self._wait_for_stack_status, 7, self.verify(600, self._wait_for_stack_status, 5,
fail_msg, fail_msg,
"stack status becoming 'CREATE_COMPLETE'", "stack status becoming 'CREATE_COMPLETE'",
stack.id, 'CREATE_COMPLETE', 600, 15) stack.id, 'CREATE_COMPLETE', 600, 15)
@ -291,7 +268,7 @@ class HeatSmokeTests(heatmanager.HeatBaseTest):
# find just created instance # find just created instance
instance_list = self.compute_client.servers.list() instance_list = self.compute_client.servers.list()
LOG.info('servers list is {0}'.format(instance_list)) LOG.info('servers list is {0}'.format(instance_list))
LOG.info('expected img_name starts with {0}'.format( LOG.info('expected instance name starts with {0}'.format(
reduced_stack_name)) reduced_stack_name))
for server in instance_list: for server in instance_list:
@ -300,63 +277,61 @@ class HeatSmokeTests(heatmanager.HeatBaseTest):
self.instance.append(server) self.instance.append(server)
if not self.instance: if not self.instance:
self.fail("Failed step: 7 Instance for the {0} stack " self.fail("Failed step: 5 Instance for the {0} stack "
"was not created.".format(self.instance)) "was not created.".format(self.instance))
floating_ip = self.verify(10, self._create_floating_ip, 8, floating_ip = self.verify(10, self._create_floating_ip, 6,
"Floating IP can not be created.", "Floating IP can not be created.",
'floating IP creation') 'floating IP creation')
self.verify(10, self._assign_floating_ip_to_instance, 9, self.verify(10, self._assign_floating_ip_to_instance, 7,
"Floating IP can not be assigned.", "Floating IP can not be assigned.",
'assigning floating IP', 'assigning floating IP',
self.compute_client, self.instance[0], floating_ip) self.compute_client, self.instance[0], floating_ip)
vm_connection = "ssh -o StrictHostKeyChecking=no -i %s %s@%s" % ( vm_connection = "ssh -o StrictHostKeyChecking=no -i %s %s@%s" % (
path_to_key, "ec2-user", floating_ip.ip) path_to_key, "cirros", floating_ip.ip)
self.verify(1000, self._wait_for_cloudinit, 10, self.verify(120, self._wait_for_vm_ready_for_load, 8,
"Cloud-init script cannot finish within timeout.", "VM is not ready or connection can't be established",
"cloud-init script execution on VM", "test script execution on VM",
vm_connection, 1000, 15) vm_connection, 120, 15)
self.verify(60, self._load_vm_cpu, 11, self.verify(60, self._load_vm_cpu, 9,
"Cannot create a process to load VM CPU.", "Cannot create a process to load VM CPU.",
"loading VM CPU", "loading VM CPU",
vm_connection) vm_connection)
self.verify(500, self.verify(480,
self._wait_for_autoscaling, 12, self._wait_for_autoscaling, 10,
"Stack failed to launch the 2nd instance " "Stack failed to launch the 2nd instance "
"per autoscaling alarm.", "per autoscaling alarm.",
"launching the new instance per autoscaling alarm", "launching the new instance per autoscaling alarm",
len(self.instance) + 1, 500, 10, reduced_stack_name) len(self.instance) + 1, 480, 10, reduced_stack_name)
self.verify(180, self._release_vm_cpu, 13, self.verify(180, self._release_vm_cpu, 11,
"Cannot kill the process on VM to turn CPU load off.", "Cannot kill the process on VM to turn CPU load off.",
"turning off VM CPU load", "turning off VM CPU load",
vm_connection) vm_connection)
self.verify(500, self._wait_for_autoscaling, 14, self.verify(480, self._wait_for_autoscaling, 12,
"Stack failed to terminate the 2nd instance " "Stack failed to terminate the 2nd instance "
"per autoscaling alarm.", "per autoscaling alarm.",
"terminating the 2nd instance per autoscaling alarm", "terminating the 2nd instance per autoscaling alarm",
len(self.instance), 500, 10, reduced_stack_name) len(self.instance), 480, 10, reduced_stack_name)
# delete private key file self.verify(10, self._delete_key_file, 13,
self.verify(10, self._delete_key_file, 15,
"The file with private key cannot be deleted.", "The file with private key cannot be deleted.",
"deleting the file with private key", "deleting the file with private key",
path_to_key) path_to_key)
fail_msg = "Cannot delete stack." self.verify(20, self.heat_client.stacks.delete, 14,
self.verify(20, self.heat_client.stacks.delete, 16, "Cannot delete stack.",
fail_msg,
"deleting stack", "deleting stack",
stack.id) stack.id)
self.verify(100, self._wait_for_stack_deleted, 17, self.verify(100, self._wait_for_stack_deleted, 15,
fail_msg, "Cannot delete stack.",
"deleting stack", "deleting stack",
stack.id) stack.id)