Merge "Increase gabbi API test retry time"
diff --git a/.zuul.yaml b/.zuul.yaml
new file mode 100644
index 0000000..61c94c3
--- /dev/null
+++ b/.zuul.yaml
@@ -0,0 +1,13 @@
+- project:
+ check:
+ jobs:
+ - heat-functional-orig-mysql-lbaasv2
+ - heat-functional-convg-mysql-lbaasv2
+ - heat-functional-convg-mysql-lbaasv2-non-apache
+ - heat-functional-convg-mysql-lbaasv2-py35
+ gate:
+ jobs:
+ - heat-functional-orig-mysql-lbaasv2
+ - heat-functional-convg-mysql-lbaasv2
+ - heat-functional-convg-mysql-lbaasv2-non-apache
+ - heat-functional-convg-mysql-lbaasv2-py35
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..68c771a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,176 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
diff --git a/heat_tempest_plugin/common/test.py b/heat_tempest_plugin/common/test.py
index a0a8b5f..4fdfb8e 100644
--- a/heat_tempest_plugin/common/test.py
+++ b/heat_tempest_plugin/common/test.py
@@ -29,8 +29,8 @@
from heat_tempest_plugin.common import exceptions
from heat_tempest_plugin.common import remote_client
-from heat_tempest_plugin import config
from heat_tempest_plugin.services import clients
+from tempest import config
LOG = logging.getLogger(__name__)
_LOG_FORMAT = "%(levelname)8s [%(name)s] %(message)s"
diff --git a/heat_tempest_plugin/config.py b/heat_tempest_plugin/config.py
index 761eddc..d658a98 100644
--- a/heat_tempest_plugin/config.py
+++ b/heat_tempest_plugin/config.py
@@ -12,8 +12,6 @@
from oslo_config import cfg
-CONF = None
-
service_available_group = cfg.OptGroup(name="service_available",
title="Available OpenStack Services")
@@ -67,6 +65,10 @@
"is used"),
cfg.StrOpt('region',
help="The region name to use"),
+ cfg.StrOpt('endpoint_type',
+ default='public',
+ choices=['public', 'admin', 'internal'],
+ help="The endpoint type to use for the orchestration service."),
cfg.StrOpt('instance_type',
help="Instance type for tests. Needs to be big enough for a "
"full OS plus the test workload"),
@@ -120,9 +122,6 @@
default=60,
help="Timeout in seconds to wait for output from ssh "
"channel."),
- cfg.IntOpt('tenant_network_mask_bits',
- default=28,
- help="The mask bits for tenant ipv4 subnets"),
cfg.BoolOpt('skip_scenario_tests',
default=False,
help="Skip all scenario tests"),
@@ -151,15 +150,6 @@
default=120,
help="Timeout in seconds to wait for connectivity to "
"server."),
- cfg.IntOpt('sighup_timeout',
- default=120,
- help="Timeout in seconds to wait for adding or removing child "
- "process after receiving of sighup signal"),
- cfg.IntOpt('sighup_config_edit_retries',
- default=10,
- help='Count of retries to edit config file during sighup. If '
- 'another worker already edit config file, file can be '
- 'busy, so need to wait and try edit file again.'),
cfg.StrOpt('heat_config_notify_script',
default=('heat-config-notify'),
help="Path to the script heat-config-notify"),
diff --git a/heat_tempest_plugin/plugin.py b/heat_tempest_plugin/plugin.py
index 0938b43..acf4fc7 100644
--- a/heat_tempest_plugin/plugin.py
+++ b/heat_tempest_plugin/plugin.py
@@ -34,7 +34,6 @@
heat_config.ServiceAvailableGroup)
config.register_opt_group(conf, heat_config.heat_group,
heat_config.HeatGroup)
- heat_config.CONF = config.CONF
def get_opt_lists(self):
return [(heat_config.heat_group.name,
diff --git a/heat_tempest_plugin/services/clients.py b/heat_tempest_plugin/services/clients.py
index ca79374..4567968 100644
--- a/heat_tempest_plugin/services/clients.py
+++ b/heat_tempest_plugin/services/clients.py
@@ -47,10 +47,12 @@
def project_id(self):
return self.auth_plugin.get_project_id(self.session)
- def get_endpoint_url(self, service_type, region=None):
+ def get_endpoint_url(self, service_type, region=None,
+ endpoint_type='public'):
kwargs = {
'service_type': service_type,
- 'region_name': region}
+ 'region_name': region,
+ 'interface': endpoint_type}
return self.auth_ref.service_catalog.url_for(**kwargs)
@@ -108,7 +110,8 @@
try:
if endpoint is None:
endpoint = self.identity_client.get_endpoint_url(
- 'orchestration', self.conf.region)
+ 'orchestration', region=self.conf.region,
+ endpoint_type=self.conf.endpoint_type)
except kc_exceptions.EndpointNotFound:
return None
else:
@@ -151,7 +154,7 @@
self.NOVA_API_VERSION,
session=self.identity_client.session,
service_type='compute',
- endpoint_type='publicURL',
+ endpoint_type=self.conf.endpoint_type,
region_name=self.conf.region,
os_cache=False,
http_log_debug=True)
@@ -162,13 +165,13 @@
session=self.identity_client.session,
service_type='network',
region_name=self.conf.region,
- endpoint_type='publicURL')
+ endpoint_type=self.conf.endpoint_type)
def _get_volume_client(self):
return cinder_client.Client(
self.CINDERCLIENT_VERSION,
session=self.identity_client.session,
- endpoint_type='publicURL',
+ endpoint_type=self.conf.endpoint_type,
region_name=self.conf.region,
http_log_debug=True)
@@ -176,7 +179,7 @@
args = {
'auth_version': self.auth_version,
'session': self.identity_client.session,
- 'os_options': {'endpoint_type': 'publicURL',
+ 'os_options': {'endpoint_type': self.conf.endpoint_type,
'region_name': self.conf.region,
'service_type': 'object-store'},
}
@@ -184,7 +187,7 @@
def _get_metric_client(self):
- adapter_options = {'interface': 'public',
+ adapter_options = {'interface': self.conf.endpoint_type,
'region_name': self.conf.region}
args = {
'session': self.identity_client.session,
diff --git a/heat_tempest_plugin/tests/api/test_heat_api.py b/heat_tempest_plugin/tests/api/test_heat_api.py
index 3c8bd77..bf86839 100644
--- a/heat_tempest_plugin/tests/api/test_heat_api.py
+++ b/heat_tempest_plugin/tests/api/test_heat_api.py
@@ -19,8 +19,8 @@
from six.moves.urllib import parse as urlparse
from heat_tempest_plugin.common import test
-from heat_tempest_plugin import config
from heat_tempest_plugin.services import clients
+from tempest import config
TESTS_DIR = 'gabbits'
@@ -35,7 +35,7 @@
return
manager = clients.ClientManager(conf)
endpoint = manager.identity_client.get_endpoint_url(
- 'orchestration', conf.region)
+ 'orchestration', region=conf.region, endpoint_type=conf.endpoint_type)
host = urlparse.urlparse(endpoint).hostname
os.environ['OS_TOKEN'] = manager.identity_client.auth_token
os.environ['PREFIX'] = test.rand_name('api')
diff --git a/heat_tempest_plugin/tests/functional/test_admin_actions.py b/heat_tempest_plugin/tests/functional/test_admin_actions.py
deleted file mode 100644
index 757b03c..0000000
--- a/heat_tempest_plugin/tests/functional/test_admin_actions.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-# Simple stack
-test_template = {
- 'heat_template_version': '2013-05-23',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1'
- }
- }
- }
-}
-
-# Nested stack
-rsg_template = {
- 'heat_template_version': '2013-05-23',
- 'resources': {
- 'random_group': {
- 'type': 'OS::Heat::ResourceGroup',
- 'properties': {
- 'count': 2,
- 'resource_def': {
- 'type': 'OS::Heat::RandomString',
- 'properties': {
- 'length': 30,
- 'salt': 'initial'
- }
- }
- }
- }
- }
-}
-
-
-class AdminActionsTest(functional_base.FunctionalTestsBase):
-
- def setUp(self):
- super(AdminActionsTest, self).setUp()
- if not self.conf.admin_username or not self.conf.admin_password:
- self.skipTest('No admin creds found, skipping')
-
- def create_stack_setup_admin_client(self, template=test_template):
- # Create the stack with the default user
- self.stack_identifier = self.stack_create(template=template)
-
- # Setup admin clients
- self.setup_clients_for_admin()
-
- def test_admin_simple_stack_actions(self):
- self.create_stack_setup_admin_client()
-
- updated_template = test_template.copy()
- props = updated_template['resources']['test1']['properties']
- props['value'] = 'new_value'
-
- # Update, suspend and resume stack
- self.update_stack(self.stack_identifier,
- template=updated_template)
- self.stack_suspend(self.stack_identifier)
- self.stack_resume(self.stack_identifier)
-
- # List stack resources
- initial_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(self.stack_identifier))
- # Delete stack
- self._stack_delete(self.stack_identifier)
-
- def test_admin_complex_stack_actions(self):
- self.create_stack_setup_admin_client(template=rsg_template)
-
- updated_template = rsg_template.copy()
- props = updated_template['resources']['random_group']['properties']
- props['count'] = 3
-
- # Update, suspend and resume stack
- self.update_stack(self.stack_identifier,
- template=updated_template)
- self.stack_suspend(self.stack_identifier)
- self.stack_resume(self.stack_identifier)
-
- # List stack resources
- resources = {'random_group': 'OS::Heat::ResourceGroup'}
- self.assertEqual(resources,
- self.list_resources(self.stack_identifier))
- # Delete stack
- self._stack_delete(self.stack_identifier)
diff --git a/heat_tempest_plugin/tests/functional/test_autoscaling.py b/heat_tempest_plugin/tests/functional/test_autoscaling.py
deleted file mode 100644
index d266646..0000000
--- a/heat_tempest_plugin/tests/functional/test_autoscaling.py
+++ /dev/null
@@ -1,752 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-import json
-
-from heatclient import exc
-from oslo_log import log as logging
-import six
-from testtools import matchers
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-LOG = logging.getLogger(__name__)
-
-
-class AutoscalingGroupTest(functional_base.FunctionalTestsBase):
-
- template = '''
-{
- "AWSTemplateFormatVersion" : "2010-09-09",
- "Description" : "Template to create multiple instances.",
- "Parameters" : {"size": {"Type": "String", "Default": "1"},
- "AZ": {"Type": "String", "Default": "nova"},
- "image": {"Type": "String"},
- "flavor": {"Type": "String"},
- "user_data": {"Type": "String", "Default": "jsconfig data"}},
- "Resources": {
- "JobServerGroup": {
- "Type" : "AWS::AutoScaling::AutoScalingGroup",
- "Properties" : {
- "AvailabilityZones" : [{"Ref": "AZ"}],
- "LaunchConfigurationName" : { "Ref" : "JobServerConfig" },
- "MinSize" : {"Ref": "size"},
- "MaxSize" : "20"
- }
- },
-
- "JobServerConfig" : {
- "Type" : "AWS::AutoScaling::LaunchConfiguration",
- "Metadata": {"foo": "bar"},
- "Properties": {
- "ImageId" : {"Ref": "image"},
- "InstanceType" : {"Ref": "flavor"},
- "SecurityGroups" : [ "sg-1" ],
- "UserData" : {"Ref": "user_data"}
- }
- }
- },
- "Outputs": {
- "InstanceList": {"Value": {
- "Fn::GetAtt": ["JobServerGroup", "InstanceList"]}},
- "JobServerConfigRef": {"Value": {
- "Ref": "JobServerConfig"}}
- }
-}
-'''
-
- instance_template = '''
-heat_template_version: 2013-05-23
-parameters:
- ImageId: {type: string}
- InstanceType: {type: string}
- SecurityGroups: {type: comma_delimited_list}
- UserData: {type: string}
- Tags: {type: comma_delimited_list, default: "x,y"}
-
-resources:
- random1:
- type: OS::Heat::RandomString
- properties:
- salt: {get_param: UserData}
-outputs:
- PublicIp: {value: {get_attr: [random1, value]}}
- AvailabilityZone: {value: 'not-used11'}
- PrivateDnsName: {value: 'not-used12'}
- PublicDnsName: {value: 'not-used13'}
- PrivateIp: {value: 'not-used14'}
-'''
-
- # This is designed to fail.
- bad_instance_template = '''
-heat_template_version: 2013-05-23
-parameters:
- ImageId: {type: string}
- InstanceType: {type: string}
- SecurityGroups: {type: comma_delimited_list}
- UserData: {type: string}
- Tags: {type: comma_delimited_list, default: "x,y"}
-
-resources:
- random1:
- type: OS::Heat::RandomString
- depends_on: waiter
- ready_poster:
- type: AWS::CloudFormation::WaitConditionHandle
- waiter:
- type: AWS::CloudFormation::WaitCondition
- properties:
- Handle: {get_resource: ready_poster}
- Timeout: 1
-outputs:
- PublicIp:
- value: {get_attr: [random1, value]}
-'''
-
- def setUp(self):
- super(AutoscalingGroupTest, self).setUp()
- if not self.conf.minimal_image_ref:
- raise self.skipException("No minimal image configured to test")
- if not self.conf.instance_type:
- raise self.skipException("No flavor configured to test")
-
- def assert_instance_count(self, stack, expected_count):
- inst_list = self._stack_output(stack, 'InstanceList')
- self.assertEqual(expected_count, len(inst_list.split(',')))
-
- def _assert_instance_state(self, nested_identifier,
- num_complete, num_failed):
- for res in self.client.resources.list(nested_identifier):
- if 'COMPLETE' in res.resource_status:
- num_complete = num_complete - 1
- elif 'FAILED' in res.resource_status:
- num_failed = num_failed - 1
- self.assertEqual(0, num_failed)
- self.assertEqual(0, num_complete)
-
-
-class AutoscalingGroupBasicTest(AutoscalingGroupTest):
-
- def test_basic_create_works(self):
- """Make sure the working case is good.
-
- Note this combines test_override_aws_ec2_instance into this test as
- well, which is:
- If AWS::EC2::Instance is overridden, AutoScalingGroup will
- automatically use that overridden resource type.
- """
-
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 4,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
- stack_identifier = self.stack_create(template=self.template,
- files=files, environment=env)
- initial_resources = {
- 'JobServerConfig': 'AWS::AutoScaling::LaunchConfiguration',
- 'JobServerGroup': 'AWS::AutoScaling::AutoScalingGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 4)
-
- def test_size_updates_work(self):
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 2,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- stack_identifier = self.stack_create(template=self.template,
- files=files,
- environment=env)
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 2)
-
- # Increase min size to 5
- env2 = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 5,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
- self.update_stack(stack_identifier, self.template,
- environment=env2, files=files)
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 5)
-
- def test_update_group_replace(self):
- """Test case for ensuring non-updatable props case a replacement.
-
- Make sure that during a group update the non-updatable
- properties cause a replacement.
- """
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry':
- {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': '1',
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- stack_identifier = self.stack_create(template=self.template,
- files=files,
- environment=env)
- rsrc = self.client.resources.get(stack_identifier, 'JobServerGroup')
- orig_asg_id = rsrc.physical_resource_id
-
- env2 = {'resource_registry':
- {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': '1',
- 'AZ': 'wibble',
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type,
- 'user_data': 'new data'}}
- self.update_stack(stack_identifier, self.template,
- environment=env2, files=files)
-
- # replacement will cause the resource physical_resource_id to change.
- rsrc = self.client.resources.get(stack_identifier, 'JobServerGroup')
- self.assertNotEqual(orig_asg_id, rsrc.physical_resource_id)
-
- def test_create_instance_error_causes_group_error(self):
- """Test create failing a resource in the instance group.
-
- If a resource in an instance group fails to be created, the instance
- group itself will fail and the broken inner resource will remain.
- """
- stack_name = self._stack_rand_name()
- files = {'provider.yaml': self.bad_instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 2,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- self.client.stacks.create(
- stack_name=stack_name,
- template=self.template,
- files=files,
- disable_rollback=True,
- parameters={},
- environment=env
- )
- self.addCleanup(self._stack_delete, stack_name)
- stack = self.client.stacks.get(stack_name)
- stack_identifier = '%s/%s' % (stack_name, stack.id)
- self._wait_for_stack_status(stack_identifier, 'CREATE_FAILED')
- initial_resources = {
- 'JobServerConfig': 'AWS::AutoScaling::LaunchConfiguration',
- 'JobServerGroup': 'AWS::AutoScaling::AutoScalingGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
- self._assert_instance_state(nested_ident, 0, 2)
-
- def test_update_instance_error_causes_group_error(self):
- """Test update failing a resource in the instance group.
-
- If a resource in an instance group fails to be created during an
- update, the instance group itself will fail and the broken inner
- resource will remain.
- """
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 2,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- stack_identifier = self.stack_create(template=self.template,
- files=files,
- environment=env)
- initial_resources = {
- 'JobServerConfig': 'AWS::AutoScaling::LaunchConfiguration',
- 'JobServerGroup': 'AWS::AutoScaling::AutoScalingGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 2)
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
- self._assert_instance_state(nested_ident, 2, 0)
- initial_list = [res.resource_name
- for res in self.client.resources.list(nested_ident)]
-
- env['parameters']['size'] = 3
- files2 = {'provider.yaml': self.bad_instance_template}
- self.client.stacks.update(
- stack_id=stack_identifier,
- template=self.template,
- files=files2,
- disable_rollback=True,
- parameters={},
- environment=env
- )
- self._wait_for_stack_status(stack_identifier, 'UPDATE_FAILED')
-
- # assert that there are 3 bad instances
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
-
- # 2 resources should be in update failed, and one create failed.
- for res in self.client.resources.list(nested_ident):
- if res.resource_name in initial_list:
- self._wait_for_resource_status(nested_ident,
- res.resource_name,
- 'UPDATE_FAILED')
- else:
- self._wait_for_resource_status(nested_ident,
- res.resource_name,
- 'CREATE_FAILED')
-
- def test_group_suspend_resume(self):
-
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 4,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
- stack_identifier = self.stack_create(template=self.template,
- files=files, environment=env)
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
-
- self.stack_suspend(stack_identifier)
- self._wait_for_all_resource_status(nested_ident, 'SUSPEND_COMPLETE')
-
- self.stack_resume(stack_identifier)
- self._wait_for_all_resource_status(nested_ident, 'RESUME_COMPLETE')
-
-
-class AutoscalingGroupUpdatePolicyTest(AutoscalingGroupTest):
-
- def ig_tmpl_with_updt_policy(self):
- templ = json.loads(copy.deepcopy(self.template))
- up = {"AutoScalingRollingUpdate": {
- "MinInstancesInService": "1",
- "MaxBatchSize": "2",
- "PauseTime": "PT1S"}}
- templ['Resources']['JobServerGroup']['UpdatePolicy'] = up
- return templ
-
- def update_instance_group(self, updt_template,
- num_updates_expected_on_updt,
- num_creates_expected_on_updt,
- num_deletes_expected_on_updt):
-
- # setup stack from the initial template
- files = {'provider.yaml': self.instance_template}
- size = 10
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': size,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
- stack_name = self._stack_rand_name()
- stack_identifier = self.stack_create(
- stack_name=stack_name,
- template=self.ig_tmpl_with_updt_policy(),
- files=files,
- environment=env)
- stack = self.client.stacks.get(stack_identifier)
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
-
- # test that physical resource name of launch configuration is used
- conf_name = self._stack_output(stack, 'JobServerConfigRef')
- conf_name_pattern = '%s-JobServerConfig-[a-zA-Z0-9]+$' % stack_name
- self.assertThat(conf_name,
- matchers.MatchesRegex(conf_name_pattern))
-
- # test the number of instances created
- self.assert_instance_count(stack, size)
- # saves info from initial list of instances for comparison later
- init_instances = self.client.resources.list(nested_ident)
- init_names = [inst.resource_name for inst in init_instances]
-
- # test stack update
- self.update_stack(stack_identifier, updt_template,
- environment=env, files=files)
- updt_stack = self.client.stacks.get(stack_identifier)
-
- # test that the launch configuration is replaced
- updt_conf_name = self._stack_output(updt_stack, 'JobServerConfigRef')
- self.assertThat(updt_conf_name,
- matchers.MatchesRegex(conf_name_pattern))
- self.assertNotEqual(conf_name, updt_conf_name)
-
- # test that the group size are the same
- updt_instances = self.client.resources.list(nested_ident)
- updt_names = [inst.resource_name for inst in updt_instances]
- self.assertEqual(len(init_names), len(updt_names))
- for res in updt_instances:
- self.assertEqual('UPDATE_COMPLETE', res.resource_status)
-
- # test that the appropriate number of instance names are the same
- matched_names = set(updt_names) & set(init_names)
- self.assertEqual(num_updates_expected_on_updt, len(matched_names))
-
- # test that the appropriate number of new instances are created
- self.assertEqual(num_creates_expected_on_updt,
- len(set(updt_names) - set(init_names)))
-
- # test that the appropriate number of instances are deleted
- self.assertEqual(num_deletes_expected_on_updt,
- len(set(init_names) - set(updt_names)))
-
- # test that the older instances are the ones being deleted
- if num_deletes_expected_on_updt > 0:
- deletes_expected = init_names[:num_deletes_expected_on_updt]
- self.assertNotIn(deletes_expected, updt_names)
-
- def test_instance_group_update_replace(self):
- """Test simple update replace.
-
- Test update replace with no conflict in batch size and minimum
- instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- grp = updt_template['Resources']['JobServerGroup']
- policy = grp['UpdatePolicy']['AutoScalingRollingUpdate']
- policy['MinInstancesInService'] = '1'
- policy['MaxBatchSize'] = '3'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=10,
- num_creates_expected_on_updt=0,
- num_deletes_expected_on_updt=0)
-
- def test_instance_group_update_replace_with_adjusted_capacity(self):
- """Test update replace with capacity adjustment.
-
- Test update replace with capacity adjustment due to conflict in batch
- size and minimum instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- grp = updt_template['Resources']['JobServerGroup']
- policy = grp['UpdatePolicy']['AutoScalingRollingUpdate']
- policy['MinInstancesInService'] = '8'
- policy['MaxBatchSize'] = '4'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=8,
- num_creates_expected_on_updt=2,
- num_deletes_expected_on_updt=2)
-
- def test_instance_group_update_replace_huge_batch_size(self):
- """Test update replace with a huge batch size."""
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['AutoScalingRollingUpdate']
- policy['MinInstancesInService'] = '0'
- policy['MaxBatchSize'] = '20'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=10,
- num_creates_expected_on_updt=0,
- num_deletes_expected_on_updt=0)
-
- def test_instance_group_update_replace_huge_min_in_service(self):
- """Update replace with huge number of minimum instances in service."""
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['AutoScalingRollingUpdate']
- policy['MinInstancesInService'] = '20'
- policy['MaxBatchSize'] = '1'
- policy['PauseTime'] = 'PT0S'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=9,
- num_creates_expected_on_updt=1,
- num_deletes_expected_on_updt=1)
-
- def test_instance_group_update_no_replace(self):
- """Test simple update only and no replace.
-
- Test simple update only and no replace (i.e. updated instance flavor
- in Launch Configuration) with no conflict in batch size and
- minimum instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['AutoScalingRollingUpdate']
- policy['MinInstancesInService'] = '1'
- policy['MaxBatchSize'] = '3'
- policy['PauseTime'] = 'PT0S'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['InstanceType'] = self.conf.minimal_instance_type
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=10,
- num_creates_expected_on_updt=0,
- num_deletes_expected_on_updt=0)
-
- def test_instance_group_update_no_replace_with_adjusted_capacity(self):
- """Test update only and no replace with capacity adjustment.
-
- Test update only and no replace (i.e. updated instance flavor in
- Launch Configuration) with capacity adjustment due to conflict in
- batch size and minimum instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['AutoScalingRollingUpdate']
- policy['MinInstancesInService'] = '8'
- policy['MaxBatchSize'] = '4'
- policy['PauseTime'] = 'PT0S'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['InstanceType'] = self.conf.minimal_instance_type
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=8,
- num_creates_expected_on_updt=2,
- num_deletes_expected_on_updt=2)
-
-
-class AutoScalingSignalTest(AutoscalingGroupTest):
-
- template = '''
-{
- "AWSTemplateFormatVersion" : "2010-09-09",
- "Description" : "Template to create multiple instances.",
- "Parameters" : {"size": {"Type": "String", "Default": "1"},
- "AZ": {"Type": "String", "Default": "nova"},
- "image": {"Type": "String"},
- "flavor": {"Type": "String"}},
- "Resources": {
- "custom_lb": {
- "Type": "AWS::EC2::Instance",
- "Properties": {
- "ImageId": {"Ref": "image"},
- "InstanceType": {"Ref": "flavor"},
- "UserData": "foo",
- "SecurityGroups": [ "sg-1" ],
- "Tags": []
- },
- "Metadata": {
- "IPs": {"Fn::GetAtt": ["JobServerGroup", "InstanceList"]}
- }
- },
- "JobServerGroup": {
- "Type" : "AWS::AutoScaling::AutoScalingGroup",
- "Properties" : {
- "AvailabilityZones" : [{"Ref": "AZ"}],
- "LaunchConfigurationName" : { "Ref" : "JobServerConfig" },
- "DesiredCapacity" : {"Ref": "size"},
- "MinSize" : "0",
- "MaxSize" : "20"
- }
- },
- "JobServerConfig" : {
- "Type" : "AWS::AutoScaling::LaunchConfiguration",
- "Metadata": {"foo": "bar"},
- "Properties": {
- "ImageId" : {"Ref": "image"},
- "InstanceType" : {"Ref": "flavor"},
- "SecurityGroups" : [ "sg-1" ],
- "UserData" : "jsconfig data"
- }
- },
- "ScaleUpPolicy" : {
- "Type" : "AWS::AutoScaling::ScalingPolicy",
- "Properties" : {
- "AdjustmentType" : "ChangeInCapacity",
- "AutoScalingGroupName" : { "Ref" : "JobServerGroup" },
- "Cooldown" : "0",
- "ScalingAdjustment": "1"
- }
- },
- "ScaleDownPolicy" : {
- "Type" : "AWS::AutoScaling::ScalingPolicy",
- "Properties" : {
- "AdjustmentType" : "ChangeInCapacity",
- "AutoScalingGroupName" : { "Ref" : "JobServerGroup" },
- "Cooldown" : "0",
- "ScalingAdjustment" : "-2"
- }
- }
- },
- "Outputs": {
- "InstanceList": {"Value": {
- "Fn::GetAtt": ["JobServerGroup", "InstanceList"]}}
- }
-}
-'''
-
- lb_template = '''
-heat_template_version: 2013-05-23
-parameters:
- ImageId: {type: string}
- InstanceType: {type: string}
- SecurityGroups: {type: comma_delimited_list}
- UserData: {type: string}
- Tags: {type: comma_delimited_list, default: "x,y"}
-
-resources:
-outputs:
- PublicIp: {value: "not-used"}
- AvailabilityZone: {value: 'not-used1'}
- PrivateDnsName: {value: 'not-used2'}
- PublicDnsName: {value: 'not-used3'}
- PrivateIp: {value: 'not-used4'}
-
-'''
-
- def setUp(self):
- super(AutoScalingSignalTest, self).setUp()
- self.build_timeout = self.conf.build_timeout
- self.build_interval = self.conf.build_interval
- self.files = {'provider.yaml': self.instance_template,
- 'lb.yaml': self.lb_template}
- self.env = {'resource_registry':
- {'resources':
- {'custom_lb': {'AWS::EC2::Instance': 'lb.yaml'}},
- 'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 2,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- def check_instance_count(self, stack_identifier, expected):
- md = self.client.resources.metadata(stack_identifier, 'custom_lb')
- actual_md = len(md['IPs'].split(','))
- if actual_md != expected:
- LOG.warning('check_instance_count exp:%d, meta:%s' % (expected,
- md['IPs']))
- return False
-
- stack = self.client.stacks.get(stack_identifier)
- inst_list = self._stack_output(stack, 'InstanceList')
- actual = len(inst_list.split(','))
- if actual != expected:
- LOG.warning('check_instance_count exp:%d, act:%s' % (expected,
- inst_list))
- return actual == expected
-
- def test_scaling_meta_update(self):
- """Use heatclient to signal the up and down policy.
-
- Then confirm that the metadata in the custom_lb is updated each
- time.
- """
- stack_identifier = self.stack_create(template=self.template,
- files=self.files,
- environment=self.env)
-
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 2))
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
- # Scale up one, Trigger alarm
- self.client.resources.signal(stack_identifier, 'ScaleUpPolicy')
- self._wait_for_stack_status(nested_ident, 'UPDATE_COMPLETE')
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 3))
-
- # Scale down two, Trigger alarm
- self.client.resources.signal(stack_identifier, 'ScaleDownPolicy')
- self._wait_for_stack_status(nested_ident, 'UPDATE_COMPLETE')
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 1))
-
- def test_signal_with_policy_update(self):
- """Prove that an updated policy is used in the next signal."""
-
- stack_identifier = self.stack_create(template=self.template,
- files=self.files,
- environment=self.env)
-
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 2))
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
- # Scale up one, Trigger alarm
- self.client.resources.signal(stack_identifier, 'ScaleUpPolicy')
- self._wait_for_stack_status(nested_ident, 'UPDATE_COMPLETE')
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 3))
-
- # increase the adjustment to "+2" and remove the DesiredCapacity
- # so we don't go from 3 to 2.
- new_template = self.template.replace(
- '"ScalingAdjustment": "1"',
- '"ScalingAdjustment": "2"').replace(
- '"DesiredCapacity" : {"Ref": "size"},', '')
-
- self.update_stack(stack_identifier, template=new_template,
- environment=self.env, files=self.files)
-
- # Scale up two, Trigger alarm
- self.client.resources.signal(stack_identifier, 'ScaleUpPolicy')
- self._wait_for_stack_status(nested_ident, 'UPDATE_COMPLETE')
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 5))
-
- def test_signal_during_suspend(self):
- """Prove that a signal will fail when the stack is in suspend."""
-
- stack_identifier = self.stack_create(template=self.template,
- files=self.files,
- environment=self.env)
-
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 2))
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
-
- # suspend the top level stack.
- self.client.actions.suspend(stack_id=stack_identifier)
-
- # Wait for stack to reach SUSPEND_COMPLETE
- self._wait_for_stack_status(stack_identifier, 'SUSPEND_COMPLETE')
-
- # Send a signal and an exception will raise
- ex = self.assertRaises(exc.BadRequest,
- self.client.resources.signal,
- stack_identifier, 'ScaleUpPolicy')
-
- error_msg = 'Signal resource during SUSPEND is not supported'
- self.assertIn(error_msg, six.text_type(ex))
- ev = self.wait_for_event_with_reason(
- stack_identifier,
- reason='Cannot signal resource during SUSPEND',
- rsrc_name='ScaleUpPolicy')
- self.assertEqual('SUSPEND_COMPLETE', ev[0].resource_status)
-
- # still SUSPEND_COMPLETE (not gone to UPDATE_COMPLETE)
- self._wait_for_stack_status(nested_ident, 'SUSPEND_COMPLETE')
- self._wait_for_stack_status(stack_identifier, 'SUSPEND_COMPLETE')
- # still 2 instances.
- self.assertTrue(test.call_until_true(
- self.build_timeout, self.build_interval,
- self.check_instance_count, stack_identifier, 2))
diff --git a/heat_tempest_plugin/tests/functional/test_aws_stack.py b/heat_tempest_plugin/tests/functional/test_aws_stack.py
deleted file mode 100644
index 03beb1f..0000000
--- a/heat_tempest_plugin/tests/functional/test_aws_stack.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import hashlib
-import json
-import random
-
-from six.moves.urllib import parse
-from swiftclient import utils as swiftclient_utils
-import yaml
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class AwsStackTest(functional_base.FunctionalTestsBase):
- test_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- the_nested:
- Type: AWS::CloudFormation::Stack
- Properties:
- TemplateURL: the.yaml
- Parameters:
- KeyName: foo
-Outputs:
- output_foo:
- Value: {"Fn::GetAtt": [the_nested, Outputs.Foo]}
-'''
-
- nested_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- KeyName:
- Type: String
-Outputs:
- Foo:
- Value: bar
-'''
-
- update_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- KeyName:
- Type: String
-Outputs:
- Foo:
- Value: foo
-'''
-
- nested_with_res_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- KeyName:
- Type: String
-Resources:
- NestedResource:
- Type: OS::Heat::RandomString
-Outputs:
- Foo:
- Value: {"Fn::GetAtt": [NestedResource, value]}
-'''
-
- def setUp(self):
- super(AwsStackTest, self).setUp()
- if not self.is_service_available('object-store'):
- self.skipTest('object-store service not available, skipping')
- self.object_container_name = test.rand_name()
- self.project_id = self.identity_client.project_id
- self.swift_key = hashlib.sha224(
- str(random.getrandbits(256)).encode('ascii')).hexdigest()[:32]
- key_header = 'x-container-meta-temp-url-key'
- self.object_client.put_container(self.object_container_name,
- {key_header: self.swift_key})
- self.addCleanup(self.object_client.delete_container,
- self.object_container_name)
-
- def publish_template(self, contents, cleanup=True):
- oc = self.object_client
-
- # post the object
- oc.put_object(self.object_container_name, 'template.yaml', contents)
- if cleanup:
- self.addCleanup(oc.delete_object,
- self.object_container_name,
- 'template.yaml')
- path = '/v1/AUTH_%s/%s/%s' % (self.project_id,
- self.object_container_name,
- 'template.yaml')
- timeout = self.conf.build_timeout * 10
- tempurl = swiftclient_utils.generate_temp_url(path, timeout,
- self.swift_key, 'GET')
- sw_url = parse.urlparse(oc.url)
- return '%s://%s%s' % (sw_url.scheme, sw_url.netloc, tempurl)
-
- def test_nested_stack_create(self):
- url = self.publish_template(self.nested_template)
- self.template = self.test_template.replace('the.yaml', url)
- stack_identifier = self.stack_create(template=self.template)
- stack = self.client.stacks.get(stack_identifier)
- self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
- self.assertEqual('bar', self._stack_output(stack, 'output_foo'))
-
- def test_nested_stack_create_with_timeout(self):
- url = self.publish_template(self.nested_template)
- self.template = self.test_template.replace('the.yaml', url)
- timeout_template = yaml.safe_load(self.template)
- props = timeout_template['Resources']['the_nested']['Properties']
- props['TimeoutInMinutes'] = '50'
-
- stack_identifier = self.stack_create(
- template=timeout_template)
- stack = self.client.stacks.get(stack_identifier)
- self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
- self.assertEqual('bar', self._stack_output(stack, 'output_foo'))
-
- def test_nested_stack_adopt_ok(self):
- url = self.publish_template(self.nested_with_res_template)
- self.template = self.test_template.replace('the.yaml', url)
- adopt_data = {
- "resources": {
- "the_nested": {
- "resource_id": "test-res-id",
- "resources": {
- "NestedResource": {
- "type": "OS::Heat::RandomString",
- "resource_id": "test-nested-res-id",
- "resource_data": {"value": "goopie"}
- }
- }
- }
- },
- "environment": {"parameters": {}},
- "template": yaml.safe_load(self.template)
- }
-
- stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data))
-
- self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
- stack = self.client.stacks.get(stack_identifier)
- self.assertEqual('goopie', self._stack_output(stack, 'output_foo'))
-
- def test_nested_stack_adopt_fail(self):
- url = self.publish_template(self.nested_with_res_template)
- self.template = self.test_template.replace('the.yaml', url)
- adopt_data = {
- "resources": {
- "the_nested": {
- "resource_id": "test-res-id",
- "resources": {
- }
- }
- },
- "environment": {"parameters": {}},
- "template": yaml.safe_load(self.template)
- }
-
- stack_identifier = self.stack_adopt(adopt_data=json.dumps(adopt_data),
- wait_for_status='ADOPT_FAILED')
- rsrc = self.client.resources.get(stack_identifier, 'the_nested')
- self.assertEqual('ADOPT_FAILED', rsrc.resource_status)
-
- def test_nested_stack_update(self):
- url = self.publish_template(self.nested_template)
- self.template = self.test_template.replace('the.yaml', url)
- stack_identifier = self.stack_create(template=self.template)
- original_nested_id = self.assert_resource_is_a_stack(
- stack_identifier, 'the_nested')
- stack = self.client.stacks.get(stack_identifier)
- self.assertEqual('bar', self._stack_output(stack, 'output_foo'))
-
- new_template = yaml.safe_load(self.template)
- props = new_template['Resources']['the_nested']['Properties']
- props['TemplateURL'] = self.publish_template(self.update_template,
- cleanup=False)
-
- self.update_stack(stack_identifier, new_template)
-
- # Expect the physical resource name staying the same after update,
- # so that the nested was actually updated instead of replaced.
- new_nested_id = self.assert_resource_is_a_stack(
- stack_identifier, 'the_nested')
- self.assertEqual(original_nested_id, new_nested_id)
- updt_stack = self.client.stacks.get(stack_identifier)
- self.assertEqual('foo', self._stack_output(updt_stack, 'output_foo'))
-
- def test_nested_stack_suspend_resume(self):
- url = self.publish_template(self.nested_template)
- self.template = self.test_template.replace('the.yaml', url)
- stack_identifier = self.stack_create(template=self.template)
- self.stack_suspend(stack_identifier)
- self.stack_resume(stack_identifier)
diff --git a/heat_tempest_plugin/tests/functional/test_cancel_update.py b/heat_tempest_plugin/tests/functional/test_cancel_update.py
deleted file mode 100644
index 4a8938a..0000000
--- a/heat_tempest_plugin/tests/functional/test_cancel_update.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class CancelUpdateTest(functional_base.FunctionalTestsBase):
-
- template = '''
-heat_template_version: '2013-05-23'
-parameters:
- InstanceType:
- type: string
- ImageId:
- type: string
- network:
- type: string
-resources:
- port:
- type: OS::Neutron::Port
- properties:
- network: {get_param: network}
- Server:
- type: OS::Nova::Server
- properties:
- flavor_update_policy: REPLACE
- image: {get_param: ImageId}
- flavor: {get_param: InstanceType}
- networks:
- - port: {get_resource: port}
-'''
-
- def setUp(self):
- super(CancelUpdateTest, self).setUp()
- if not self.conf.minimal_image_ref:
- raise self.skipException("No minimal image configured to test")
- if not self.conf.minimal_instance_type:
- raise self.skipException("No minimal flavor configured to test.")
-
- def test_cancel_update_server_with_port(self):
- parameters = {'InstanceType': self.conf.minimal_instance_type,
- 'ImageId': self.conf.minimal_image_ref,
- 'network': self.conf.fixed_network_name}
-
- stack_identifier = self.stack_create(template=self.template,
- parameters=parameters)
- parameters['InstanceType'] = self.conf.instance_type
- self.update_stack(stack_identifier, self.template,
- parameters=parameters,
- expected_status='UPDATE_IN_PROGRESS')
-
- self.cancel_update_stack(stack_identifier)
diff --git a/heat_tempest_plugin/tests/functional/test_conditional_exposure.py b/heat_tempest_plugin/tests/functional/test_conditional_exposure.py
deleted file mode 100644
index dd4329b..0000000
--- a/heat_tempest_plugin/tests/functional/test_conditional_exposure.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heatclient import exc
-import keystoneclient
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class ServiceBasedExposureTest(functional_base.FunctionalTestsBase):
- # NOTE(pas-ha) if we ever decide to install Sahara on Heat
- # functional gate, this must be changed to other not-installed
- # but in principle supported service
- unavailable_service = 'Sahara'
- unavailable_template = """
-heat_template_version: 2015-10-15
-parameters:
- instance_type:
- type: string
-resources:
- not_available:
- type: OS::Sahara::NodeGroupTemplate
- properties:
- plugin_name: fake
- hadoop_version: 0.1
- flavor: {get_param: instance_type}
- node_processes: []
-"""
-
- def setUp(self):
- super(ServiceBasedExposureTest, self).setUp()
- # check that Sahara endpoint is available
- if self._is_sahara_deployed():
- self.skipTest("Sahara is actually deployed, "
- "can not run negative tests on "
- "Sahara resources availability.")
-
- def _is_sahara_deployed(self):
- try:
- self.identity_client.get_endpoint_url('data-processing',
- self.conf.region)
- except keystoneclient.exceptions.EndpointNotFound:
- return False
- return True
-
- def test_unavailable_resources_not_listed(self):
- resources = self.client.resource_types.list()
- self.assertFalse(any(self.unavailable_service in r.resource_type
- for r in resources))
-
- def test_unavailable_resources_not_created(self):
- stack_name = self._stack_rand_name()
- parameters = {'instance_type': self.conf.minimal_instance_type}
- ex = self.assertRaises(exc.HTTPBadRequest,
- self.client.stacks.create,
- stack_name=stack_name,
- parameters=parameters,
- template=self.unavailable_template)
- self.assertIn('ResourceTypeUnavailable', ex.message.decode('utf-8'))
- self.assertIn('OS::Sahara::NodeGroupTemplate',
- ex.message.decode('utf-8'))
-
-
-class RoleBasedExposureTest(functional_base.FunctionalTestsBase):
-
- fl_tmpl = """
-heat_template_version: 2015-10-15
-
-resources:
- not4everyone:
- type: OS::Nova::Flavor
- properties:
- ram: 20000
- vcpus: 10
-"""
-
- cvt_tmpl = """
-heat_template_version: 2015-10-15
-
-resources:
- cvt:
- type: OS::Cinder::VolumeType
- properties:
- name: cvt_test
-"""
-
- host_aggr_tmpl = """
-heat_template_version: 2015-10-15
-parameters:
- az:
- type: string
- default: nova
-resources:
- cvt:
- type: OS::Nova::HostAggregate
- properties:
- name: aggregate_test
- availability_zone: {get_param: az}
-"""
-
- scenarios = [
- ('r_nova_flavor', dict(
- stack_name='s_nova_flavor',
- template=fl_tmpl,
- forbidden_r_type="OS::Nova::Flavor",
- test_creation=True)),
- ('r_nova_host_aggregate', dict(
- stack_name='s_nova_ost_aggregate',
- template=host_aggr_tmpl,
- forbidden_r_type="OS::Nova::HostAggregate",
- test_creation=True)),
- ('r_cinder_vtype', dict(
- stack_name='s_cinder_vtype',
- template=cvt_tmpl,
- forbidden_r_type="OS::Cinder::VolumeType",
- test_creation=True)),
- ('r_cinder_vtype_encrypt', dict(
- forbidden_r_type="OS::Cinder::EncryptedVolumeType",
- test_creation=False)),
- ('r_neutron_qos', dict(
- forbidden_r_type="OS::Neutron::QoSPolicy",
- test_creation=False)),
- ('r_neutron_qos_bandwidth_limit', dict(
- forbidden_r_type="OS::Neutron::QoSBandwidthLimitRule",
- test_creation=False)),
- ('r_manila_share_type', dict(
- forbidden_r_type="OS::Manila::ShareType",
- test_creation=False))
- ]
-
- def test_non_admin_forbidden_create_resources(self):
- """Fail to create resource w/o admin role.
-
- Integration tests job runs as normal OpenStack user,
- and the resources above are configured to require
- admin role in default policy file of Heat.
- """
- if self.test_creation:
- ex = self.assertRaises(exc.Forbidden,
- self.client.stacks.create,
- stack_name=self.stack_name,
- template=self.template)
- self.assertIn(self.forbidden_r_type, ex.message.decode('utf-8'))
-
- def test_forbidden_resource_not_listed(self):
- resources = self.client.resource_types.list()
- self.assertNotIn(self.forbidden_r_type,
- (r.resource_type for r in resources))
diff --git a/heat_tempest_plugin/tests/functional/test_conditions.py b/heat_tempest_plugin/tests/functional/test_conditions.py
deleted file mode 100644
index 106a4ca..0000000
--- a/heat_tempest_plugin/tests/functional/test_conditions.py
+++ /dev/null
@@ -1,619 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-cfn_template = '''
-AWSTemplateFormatVersion: 2010-09-09
-Parameters:
- env_type:
- Default: test
- Type: String
- AllowedValues: [prod, test]
- zone:
- Type: String
- Default: beijing
-Conditions:
- Prod: {"Fn::Equals" : [{Ref: env_type}, "prod"]}
- Test:
- Fn::Not:
- - Fn::Equals:
- - Ref: env_type
- - prod
- Beijing_Prod:
- Fn::And:
- - Fn::Equals:
- - Ref: env_type
- - prod
- - Fn::Equals:
- - Ref: zone
- - beijing
- Xian_Zone:
- Fn::Equals:
- - Ref: zone
- - xian
- Xianyang_Zone:
- Fn::Equals:
- - Ref: zone
- - xianyang
- Fujian_Zone:
- Fn::Or:
- - Fn::Equals:
- - Ref: zone
- - fuzhou
- - Fn::Equals:
- - Ref: zone
- - xiamen
- Fujian_Prod:
- Fn::And:
- - Fujian_Zone
- - Prod
- Shannxi_Provice:
- Fn::Or:
- - Xian_Zone
- - Xianyang_Zone
- Not_Shannxi:
- Fn::Not: [Shannxi_Provice]
-Resources:
- test_res:
- Type: OS::Heat::TestResource
- Properties:
- value: {"Fn::If": ["Prod", "env_is_prod", "env_is_test"]}
- prod_res:
- Type: OS::Heat::TestResource
- Properties:
- value: prod_res
- Condition: Prod
- test_res1:
- Type: OS::Heat::TestResource
- Properties:
- value: just in test env
- Condition: Test
- beijing_prod_res:
- Type: OS::Heat::TestResource
- Properties:
- value: beijing_prod_res
- Condition: Beijing_Prod
- fujian_res:
- Type: OS::Heat::TestResource
- Condition: Fujian_Zone
- Properties:
- value: fujian_res
- fujian_prod_res:
- Type: OS::Heat::TestResource
- Condition: Fujian_Prod
- Properties:
- value: fujian_prod_res
- shannxi_res:
- Type: OS::Heat::TestResource
- Condition: Shannxi_Provice
- Properties:
- value: shannxi_res
- not_shannxi_res:
- Type: OS::Heat::TestResource
- Condition: Not_Shannxi
- Properties:
- value: not_shannxi_res
-Outputs:
- res_value:
- Value: {"Fn::GetAtt": [prod_res, output]}
- Condition: Prod
- test_res_value:
- Value: {"Fn::GetAtt": [test_res, output]}
- prod_resource:
- Value: {"Fn::If": [Prod, {Ref: prod_res}, 'no_prod_res']}
- test_res1_value:
- Value: {"Fn::If": [Test, {"Fn::GetAtt": [test_res1, output]},
- 'no_test_res1']}
- beijing_prod_res:
- Value: {"Fn::If": [Beijing_Prod, {Ref: beijing_prod_res}, 'no_prod_res']}
-'''
-
-hot_template = '''
-heat_template_version: 2016-10-14
-parameters:
- env_type:
- default: test
- type: string
- constraints:
- - allowed_values: [prod, test]
- zone:
- type: string
- default: beijing
-conditions:
- prod: {equals : [{get_param: env_type}, "prod"]}
- test:
- not:
- equals:
- - get_param: env_type
- - prod
- beijing_prod:
- and:
- - equals:
- - get_param: zone
- - beijing
- - equals:
- - get_param: env_type
- - prod
- xian_zone:
- equals:
- - get_param: zone
- - xian
- xianyang_zone:
- equals:
- - get_param: zone
- - xianyang
- fujian_zone:
- or:
- - equals:
- - get_param: zone
- - fuzhou
- - equals:
- - get_param: zone
- - xiamen
- fujian_prod:
- and:
- - fujian_zone
- - prod
- shannxi_provice:
- or:
- - xian_zone
- - xianyang_zone
- not_shannxi:
- not: shannxi_provice
-resources:
- test_res:
- type: OS::Heat::TestResource
- properties:
- value: {if: ["prod", "env_is_prod", "env_is_test"]}
- prod_res:
- type: OS::Heat::TestResource
- properties:
- value: prod_res
- condition: prod
- test_res1:
- type: OS::Heat::TestResource
- properties:
- value: just in test env
- condition: test
- beijing_prod_res:
- type: OS::Heat::TestResource
- properties:
- value: beijing_prod_res
- condition: beijing_prod
- fujian_res:
- type: OS::Heat::TestResource
- condition: fujian_zone
- properties:
- value: fujian_res
- fujian_prod_res:
- type: OS::Heat::TestResource
- condition: fujian_prod
- properties:
- value: fujian_prod_res
- shannxi_res:
- type: OS::Heat::TestResource
- condition: shannxi_provice
- properties:
- value: shannxi_res
- not_shannxi_res:
- type: OS::Heat::TestResource
- condition: not_shannxi
- properties:
- value: not_shannxi_res
-outputs:
- res_value:
- value: {get_attr: [prod_res, output]}
- condition: prod
- test_res_value:
- value: {get_attr: [test_res, output]}
- prod_resource:
- value: {if: [prod, {get_resource: prod_res}, 'no_prod_res']}
- test_res1_value:
- value: {if: [test, {get_attr: [test_res1, output]}, 'no_test_res1']}
- beijing_prod_res:
- value: {if: [beijing_prod, {get_resource: beijing_prod_res},
- 'no_prod_res']}
-'''
-
-before_rename_tmpl = '''
-heat_template_version: 2016-10-14
-parameters:
- env_type:
- default: test
- type: string
-conditions:
- cd1: {equals : [{get_param: env_type}, "prod"]}
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value: {if: [cd1, 'prod', 'test']}
-'''
-
-after_rename_tmpl = '''
-heat_template_version: 2016-10-14
-parameters:
- env_type:
- default: prod
- type: string
-conditions:
- cd2: {equals : [{get_param: env_type}, "prod"]}
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value: {if: [cd2, 'prod', 'test']}
- test2:
- type: OS::Heat::TestResource
- properties:
- value: {if: [cd2, 'prod', 'test']}
-'''
-
-fail_rename_tmpl = '''
-heat_template_version: 2016-10-14
-parameters:
- env_type:
- default: prod
- type: string
-conditions:
- cd3: {equals : [{get_param: env_type}, "prod"]}
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value: {if: [cd3, 'prod', 'test']}
- test2:
- type: OS::Heat::TestResource
- properties:
- value: {if: [cd3, 'prod', 'test']}
- test_fail:
- type: OS::Heat::TestResource
- properties:
- fail: True
- depends_on: [test, test2]
-'''
-
-recover_rename_tmpl = '''
-heat_template_version: 2016-10-14
-parameters:
- env_type:
- default: prod
- type: string
-conditions:
- cd3: {equals : [{get_param: env_type}, "prod"]}
-resources:
- test2:
- type: OS::Heat::TestResource
- properties:
- value: {if: [cd3, 'prod', 'test']}
- test_fail:
- type: OS::Heat::TestResource
- properties:
- fail: False
- depends_on: [test2]
-'''
-
-
-class CreateUpdateResConditionTest(functional_base.FunctionalTestsBase):
-
- def res_assert_for_prod(self, resources, bj_prod=True, fj_zone=False,
- shannxi_provice=False):
- res_names = [res.resource_name for res in resources]
- if bj_prod:
- self.assertEqual(4, len(resources))
- self.assertIn('beijing_prod_res', res_names)
- self.assertIn('not_shannxi_res', res_names)
- elif fj_zone:
- self.assertEqual(5, len(resources))
- self.assertIn('fujian_res', res_names)
- self.assertNotIn('beijing_prod_res', res_names)
- self.assertIn('not_shannxi_res', res_names)
- self.assertIn('fujian_prod_res', res_names)
- elif shannxi_provice:
- self.assertEqual(3, len(resources))
- self.assertIn('shannxi_res', res_names)
- else:
- self.assertEqual(3, len(resources))
- self.assertIn('not_shannxi_res', res_names)
- self.assertIn('prod_res', res_names)
- self.assertIn('test_res', res_names)
-
- def res_assert_for_test(self, resources, fj_zone=False,
- shannxi_provice=False):
- res_names = [res.resource_name for res in resources]
-
- if fj_zone:
- self.assertEqual(4, len(resources))
- self.assertIn('fujian_res', res_names)
- self.assertIn('not_shannxi_res', res_names)
- elif shannxi_provice:
- self.assertEqual(3, len(resources))
- self.assertNotIn('fujian_res', res_names)
- self.assertIn('shannxi_res', res_names)
- else:
- self.assertEqual(3, len(resources))
- self.assertIn('not_shannxi_res', res_names)
- self.assertIn('test_res', res_names)
- self.assertIn('test_res1', res_names)
- self.assertNotIn('prod_res', res_names)
-
- def output_assert_for_prod(self, stack_id, bj_prod=True):
- output = self.client.stacks.output_show(stack_id,
- 'res_value')['output']
- self.assertEqual('prod_res', output['output_value'])
-
- test_res_value = self.client.stacks.output_show(
- stack_id, 'test_res_value')['output']
- self.assertEqual('env_is_prod', test_res_value['output_value'])
-
- prod_resource = self.client.stacks.output_show(
- stack_id, 'prod_resource')['output']
- self.assertNotEqual('no_prod_res', prod_resource['output_value'])
-
- test_res_output = self.client.stacks.output_show(
- stack_id, 'test_res1_value')['output']
- self.assertEqual('no_test_res1', test_res_output['output_value'])
-
- beijing_prod_res = self.client.stacks.output_show(
- stack_id, 'beijing_prod_res')['output']
- if bj_prod:
- self.assertNotEqual('no_prod_res',
- beijing_prod_res['output_value'])
- else:
- self.assertEqual('no_prod_res', beijing_prod_res['output_value'])
-
- def output_assert_for_test(self, stack_id):
- output = self.client.stacks.output_show(stack_id,
- 'res_value')['output']
- self.assertIsNone(output['output_value'])
-
- test_res_value = self.client.stacks.output_show(
- stack_id, 'test_res_value')['output']
- self.assertEqual('env_is_test', test_res_value['output_value'])
-
- prod_resource = self.client.stacks.output_show(
- stack_id, 'prod_resource')['output']
- self.assertEqual('no_prod_res', prod_resource['output_value'])
-
- test_res_output = self.client.stacks.output_show(
- stack_id, 'test_res1_value')['output']
- self.assertEqual('just in test env',
- test_res_output['output_value'])
-
- beijing_prod_res = self.client.stacks.output_show(
- stack_id, 'beijing_prod_res')['output']
- self.assertEqual('no_prod_res', beijing_prod_res['output_value'])
-
- def test_stack_create_update_cfn_template_test_to_prod(self):
- stack_identifier = self.stack_create(template=cfn_template)
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'zone': 'fuzhou'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources, fj_zone=True)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'zone': 'xianyang'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources, shannxi_provice=True)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'env_type': 'prod'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources)
- self.output_assert_for_prod(stack_identifier)
-
- parms = {'env_type': 'prod',
- 'zone': 'shanghai'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, False)
- self.output_assert_for_prod(stack_identifier, False)
-
- parms = {'env_type': 'prod',
- 'zone': 'xiamen'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, bj_prod=False, fj_zone=True)
- self.output_assert_for_prod(stack_identifier, False)
-
- parms = {'env_type': 'prod',
- 'zone': 'xianyang'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, bj_prod=False, fj_zone=False,
- shannxi_provice=True)
- self.output_assert_for_prod(stack_identifier, False)
-
- def test_stack_create_update_cfn_template_prod_to_test(self):
- parms = {'env_type': 'prod'}
- stack_identifier = self.stack_create(template=cfn_template,
- parameters=parms)
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources)
- self.output_assert_for_prod(stack_identifier)
-
- parms = {'zone': 'xiamen',
- 'env_type': 'prod'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, bj_prod=False, fj_zone=True)
- self.output_assert_for_prod(stack_identifier, bj_prod=False)
-
- parms = {'zone': 'xianyang',
- 'env_type': 'prod'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, bj_prod=False, fj_zone=False,
- shannxi_provice=True)
- self.output_assert_for_prod(stack_identifier, bj_prod=False)
-
- parms = {'zone': 'shanghai',
- 'env_type': 'prod'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, bj_prod=False, fj_zone=False,
- shannxi_provice=False)
- self.output_assert_for_prod(stack_identifier, bj_prod=False)
-
- parms = {'env_type': 'test'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'env_type': 'test',
- 'zone': 'fuzhou'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources, fj_zone=True)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'env_type': 'test',
- 'zone': 'xianyang'}
- self.update_stack(stack_identifier,
- template=cfn_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources, fj_zone=False,
- shannxi_provice=True)
- self.output_assert_for_test(stack_identifier)
-
- def test_stack_create_update_hot_template_test_to_prod(self):
- stack_identifier = self.stack_create(template=hot_template)
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'zone': 'xianyang'}
- self.update_stack(stack_identifier,
- template=hot_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources, shannxi_provice=True)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'env_type': 'prod'}
- self.update_stack(stack_identifier,
- template=hot_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources)
- self.output_assert_for_prod(stack_identifier)
-
- parms = {'env_type': 'prod',
- 'zone': 'shanghai'}
- self.update_stack(stack_identifier,
- template=hot_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, False)
- self.output_assert_for_prod(stack_identifier, False)
-
- parms = {'env_type': 'prod',
- 'zone': 'xianyang'}
- self.update_stack(stack_identifier,
- template=hot_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, False, shannxi_provice=True)
- self.output_assert_for_prod(stack_identifier, False)
-
- def test_stack_create_update_hot_template_prod_to_test(self):
- parms = {'env_type': 'prod'}
- stack_identifier = self.stack_create(template=hot_template,
- parameters=parms)
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources)
- self.output_assert_for_prod(stack_identifier)
-
- parms = {'env_type': 'prod',
- 'zone': 'xianyang'}
- self.update_stack(stack_identifier,
- template=hot_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_prod(resources, False, shannxi_provice=True)
- self.output_assert_for_prod(stack_identifier, False)
-
- parms = {'env_type': 'test'}
- self.update_stack(stack_identifier,
- template=hot_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources)
- self.output_assert_for_test(stack_identifier)
-
- parms = {'env_type': 'test',
- 'zone': 'xianyang'}
- self.update_stack(stack_identifier,
- template=hot_template,
- parameters=parms)
-
- resources = self.client.resources.list(stack_identifier)
- self.res_assert_for_test(resources, fj_zone=False,
- shannxi_provice=True)
- self.output_assert_for_test(stack_identifier)
-
- def test_condition_rename(self):
- stack_identifier = self.stack_create(template=before_rename_tmpl)
- self.update_stack(stack_identifier, template=after_rename_tmpl)
- self.update_stack(stack_identifier, template=fail_rename_tmpl,
- expected_status='UPDATE_FAILED')
- self.update_stack(stack_identifier, template=recover_rename_tmpl)
diff --git a/heat_tempest_plugin/tests/functional/test_create_update.py b/heat_tempest_plugin/tests/functional/test_create_update.py
deleted file mode 100644
index 46b1837..0000000
--- a/heat_tempest_plugin/tests/functional/test_create_update.py
+++ /dev/null
@@ -1,710 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-import copy
-import json
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-test_template_one_resource = {
- 'heat_template_version': 'pike',
- 'description': 'Test template to create one instance.',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 1,
- 'action_wait_secs': {'create': 1},
- 'client_name': 'nova',
- 'entity_name': 'servers',
- }
- }
- }
-}
-
-test_template_two_resource = {
- 'heat_template_version': 'pike',
- 'description': 'Test template to create two instance.',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0,
- 'action_wait_secs': {'update': 1}
- }
- },
- 'test2': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0
- }
- }
- }
-}
-
-
-def _change_rsrc_properties(template, rsrcs, values):
- modified_template = copy.deepcopy(template)
- for rsrc_name in rsrcs:
- rsrc_prop = modified_template['resources'][
- rsrc_name]['properties']
- for prop in rsrc_prop:
- if prop in values:
- rsrc_prop[prop] = values[prop]
- return modified_template
-
-
-class CreateStackTest(functional_base.FunctionalTestsBase):
- def test_create_rollback(self):
- values = {'fail': True, 'value': 'test_create_rollback'}
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'], values)
-
- self.stack_create(
- template=template,
- expected_status='ROLLBACK_COMPLETE',
- disable_rollback=False)
-
-
-class UpdateStackTest(functional_base.FunctionalTestsBase):
-
- provider_template = {
- 'heat_template_version': '2013-05-23',
- 'description': 'foo',
- 'resources': {
- 'test1': {
- 'type': 'My::TestResource'
- }
- }
- }
-
- provider_group_template = '''
-heat_template_version: 2013-05-23
-parameters:
- count:
- type: number
- default: 2
-resources:
- test_group:
- type: OS::Heat::ResourceGroup
- properties:
- count: {get_param: count}
- resource_def:
- type: My::TestResource
-'''
-
- update_userdata_template = '''
-heat_template_version: 2014-10-16
-parameters:
- flavor:
- type: string
- user_data:
- type: string
- image:
- type: string
- network:
- type: string
-
-resources:
- server:
- type: OS::Nova::Server
- properties:
- image: {get_param: image}
- flavor: {get_param: flavor}
- networks: [{network: {get_param: network} }]
- user_data_format: SOFTWARE_CONFIG
- user_data: {get_param: user_data}
-'''
-
- fail_param_template = '''
-heat_template_version: 2014-10-16
-parameters:
- do_fail:
- type: boolean
- default: False
-resources:
- aresource:
- type: OS::Heat::TestResource
- properties:
- value: Test
- fail: {get_param: do_fail}
- wait_secs: 1
-'''
-
- def test_stack_update_nochange(self):
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'],
- {'value': 'test_no_change'})
- stack_identifier = self.stack_create(
- template=template)
- expected_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(expected_resources,
- self.list_resources(stack_identifier))
-
- # Update with no changes, resources should be unchanged
- self.update_stack(stack_identifier, template)
- self.assertEqual(expected_resources,
- self.list_resources(stack_identifier))
-
- def test_stack_in_place_update(self):
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'],
- {'value': 'test_in_place'})
- stack_identifier = self.stack_create(
- template=template)
- expected_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(expected_resources,
- self.list_resources(stack_identifier))
- resource = self.client.resources.list(stack_identifier)
- initial_phy_id = resource[0].physical_resource_id
-
- tmpl_update = _change_rsrc_properties(
- test_template_one_resource, ['test1'],
- {'value': 'test_in_place_update'})
- # Update the Value
- self.update_stack(stack_identifier, tmpl_update)
- resource = self.client.resources.list(stack_identifier)
- # By default update_in_place
- self.assertEqual(initial_phy_id,
- resource[0].physical_resource_id)
-
- def test_stack_update_replace(self):
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'],
- {'value': 'test_replace'})
- stack_identifier = self.stack_create(
- template=template)
- expected_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(expected_resources,
- self.list_resources(stack_identifier))
- resource = self.client.resources.list(stack_identifier)
- initial_phy_id = resource[0].physical_resource_id
-
- # Update the value and also set update_replace prop
- tmpl_update = _change_rsrc_properties(
- test_template_one_resource, ['test1'],
- {'value': 'test_in_place_update', 'update_replace': True})
- self.update_stack(stack_identifier, tmpl_update)
- resource = self.client.resources.list(stack_identifier)
- # update Replace
- self.assertNotEqual(initial_phy_id,
- resource[0].physical_resource_id)
-
- def test_stack_update_add_remove(self):
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'],
- {'value': 'test_add_remove'})
- stack_identifier = self.stack_create(
- template=template)
- initial_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- tmpl_update = _change_rsrc_properties(
- test_template_two_resource, ['test1', 'test2'],
- {'value': 'test_add_remove_update'})
- # Add one resource via a stack update
- self.update_stack(stack_identifier, tmpl_update)
- updated_resources = {'test1': 'OS::Heat::TestResource',
- 'test2': 'OS::Heat::TestResource'}
- self.assertEqual(updated_resources,
- self.list_resources(stack_identifier))
-
- # Then remove it by updating with the original template
- self.update_stack(stack_identifier, template)
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- def test_stack_update_rollback(self):
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'],
- {'value': 'test_update_rollback'})
- stack_identifier = self.stack_create(
- template=template)
- initial_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- tmpl_update = _change_rsrc_properties(
- test_template_two_resource, ['test1', 'test2'],
- {'value': 'test_update_rollback', 'fail': True})
- # stack update, also set failure
- self.update_stack(stack_identifier, tmpl_update,
- expected_status='ROLLBACK_COMPLETE',
- disable_rollback=False)
- # since stack update failed only the original resource is present
- updated_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(updated_resources,
- self.list_resources(stack_identifier))
-
- def test_stack_update_from_failed(self):
- # Prove it's possible to update from an UPDATE_FAILED state
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'],
- {'value': 'test_update_failed'})
- stack_identifier = self.stack_create(
- template=template)
- initial_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- tmpl_update = _change_rsrc_properties(
- test_template_one_resource, ['test1'], {'fail': True})
- # Update with bad template, we should fail
- self.update_stack(stack_identifier, tmpl_update,
- expected_status='UPDATE_FAILED')
- # but then passing a good template should succeed
- self.update_stack(stack_identifier, test_template_two_resource)
- updated_resources = {'test1': 'OS::Heat::TestResource',
- 'test2': 'OS::Heat::TestResource'}
- self.assertEqual(updated_resources,
- self.list_resources(stack_identifier))
-
- def test_stack_update_provider(self):
- template = _change_rsrc_properties(
- test_template_one_resource, ['test1'],
- {'value': 'test_provider_template'})
- files = {'provider.template': json.dumps(template)}
- env = {'resource_registry':
- {'My::TestResource': 'provider.template'}}
- stack_identifier = self.stack_create(
- template=self.provider_template,
- files=files,
- environment=env
- )
-
- initial_resources = {'test1': 'My::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- # Prove the resource is backed by a nested stack, save the ID
- nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
- 'test1')
- nested_id = nested_identifier.split('/')[-1]
-
- # Then check the expected resources are in the nested stack
- nested_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
- tmpl_update = _change_rsrc_properties(
- test_template_two_resource, ['test1', 'test2'],
- {'value': 'test_provider_template'})
- # Add one resource via a stack update by changing the nested stack
- files['provider.template'] = json.dumps(tmpl_update)
- self.update_stack(stack_identifier, self.provider_template,
- environment=env, files=files)
-
- # Parent resources should be unchanged and the nested stack
- # should have been updated in-place without replacement
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
- rsrc = self.client.resources.get(stack_identifier, 'test1')
- self.assertEqual(rsrc.physical_resource_id, nested_id)
-
- # Then check the expected resources are in the nested stack
- nested_resources = {'test1': 'OS::Heat::TestResource',
- 'test2': 'OS::Heat::TestResource'}
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
-
- def test_stack_update_alias_type(self):
- env = {'resource_registry':
- {'My::TestResource': 'OS::Heat::RandomString',
- 'My::TestResource2': 'OS::Heat::RandomString'}}
- stack_identifier = self.stack_create(
- template=self.provider_template,
- environment=env
- )
- p_res = self.client.resources.get(stack_identifier, 'test1')
- self.assertEqual('My::TestResource', p_res.resource_type)
-
- initial_resources = {'test1': 'My::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
- res = self.client.resources.get(stack_identifier, 'test1')
- # Modify the type of the resource alias to My::TestResource2
- tmpl_update = copy.deepcopy(self.provider_template)
- tmpl_update['resources']['test1']['type'] = 'My::TestResource2'
- self.update_stack(stack_identifier, tmpl_update, environment=env)
- res_a = self.client.resources.get(stack_identifier, 'test1')
- self.assertEqual(res.physical_resource_id, res_a.physical_resource_id)
- self.assertEqual(res.attributes['value'], res_a.attributes['value'])
-
- def test_stack_update_alias_changes(self):
- env = {'resource_registry':
- {'My::TestResource': 'OS::Heat::RandomString'}}
- stack_identifier = self.stack_create(
- template=self.provider_template,
- environment=env
- )
- p_res = self.client.resources.get(stack_identifier, 'test1')
- self.assertEqual('My::TestResource', p_res.resource_type)
-
- initial_resources = {'test1': 'My::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
- res = self.client.resources.get(stack_identifier, 'test1')
- # Modify the resource alias to point to a different type
- env = {'resource_registry':
- {'My::TestResource': 'OS::Heat::TestResource'}}
- self.update_stack(stack_identifier, template=self.provider_template,
- environment=env)
- res_a = self.client.resources.get(stack_identifier, 'test1')
- self.assertNotEqual(res.physical_resource_id,
- res_a.physical_resource_id)
-
- def test_stack_update_provider_type(self):
- template = _change_rsrc_properties(
- test_template_one_resource, ['test1'],
- {'value': 'test_provider_template'})
- files = {'provider.template': json.dumps(template)}
- env = {'resource_registry':
- {'My::TestResource': 'provider.template',
- 'My::TestResource2': 'provider.template'}}
- stack_identifier = self.stack_create(
- template=self.provider_template,
- files=files,
- environment=env
- )
- p_res = self.client.resources.get(stack_identifier, 'test1')
- self.assertEqual('My::TestResource', p_res.resource_type)
-
- initial_resources = {'test1': 'My::TestResource'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- # Prove the resource is backed by a nested stack, save the ID
- nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
- 'test1')
- nested_id = nested_identifier.split('/')[-1]
-
- # Then check the expected resources are in the nested stack
- nested_resources = {'test1': 'OS::Heat::TestResource'}
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
- n_res = self.client.resources.get(nested_identifier, 'test1')
-
- # Modify the type of the provider resource to My::TestResource2
- tmpl_update = copy.deepcopy(self.provider_template)
- tmpl_update['resources']['test1']['type'] = 'My::TestResource2'
- self.update_stack(stack_identifier, tmpl_update,
- environment=env, files=files)
- p_res = self.client.resources.get(stack_identifier, 'test1')
- self.assertEqual('My::TestResource2', p_res.resource_type)
-
- # Parent resources should be unchanged and the nested stack
- # should have been updated in-place without replacement
- self.assertEqual({u'test1': u'My::TestResource2'},
- self.list_resources(stack_identifier))
- rsrc = self.client.resources.get(stack_identifier, 'test1')
- self.assertEqual(rsrc.physical_resource_id, nested_id)
-
- # Then check the expected resources are in the nested stack
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
- n_res2 = self.client.resources.get(nested_identifier, 'test1')
- self.assertEqual(n_res.physical_resource_id,
- n_res2.physical_resource_id)
-
- def test_stack_update_provider_group(self):
- """Test two-level nested update."""
-
- # Create a ResourceGroup (which creates a nested stack),
- # containing provider resources (which create a nested
- # stack), thus exercising an update which traverses
- # two levels of nesting.
- template = _change_rsrc_properties(
- test_template_one_resource, ['test1'],
- {'value': 'test_provider_group_template'})
- files = {'provider.template': json.dumps(template)}
- env = {'resource_registry':
- {'My::TestResource': 'provider.template'}}
-
- stack_identifier = self.stack_create(
- template=self.provider_group_template,
- files=files,
- environment=env
- )
-
- initial_resources = {'test_group': 'OS::Heat::ResourceGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- # Prove the resource is backed by a nested stack, save the ID
- nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
- 'test_group')
-
- # Then check the expected resources are in the nested stack
- nested_resources = {'0': 'My::TestResource',
- '1': 'My::TestResource'}
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
-
- for n_rsrc in nested_resources:
- rsrc = self.client.resources.get(nested_identifier, n_rsrc)
- provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
- provider_identifier = '%s/%s' % (provider_stack.stack_name,
- provider_stack.id)
- provider_resources = {u'test1': u'OS::Heat::TestResource'}
- self.assertEqual(provider_resources,
- self.list_resources(provider_identifier))
-
- tmpl_update = _change_rsrc_properties(
- test_template_two_resource, ['test1', 'test2'],
- {'value': 'test_provider_group_template'})
- # Add one resource via a stack update by changing the nested stack
- files['provider.template'] = json.dumps(tmpl_update)
- self.update_stack(stack_identifier, self.provider_group_template,
- environment=env, files=files)
-
- # Parent resources should be unchanged and the nested stack
- # should have been updated in-place without replacement
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- # Resource group stack should also be unchanged (but updated)
- nested_stack = self.client.stacks.get(nested_identifier)
- self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
-
- for n_rsrc in nested_resources:
- rsrc = self.client.resources.get(nested_identifier, n_rsrc)
- provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
- provider_identifier = '%s/%s' % (provider_stack.stack_name,
- provider_stack.id)
- provider_resources = {'test1': 'OS::Heat::TestResource',
- 'test2': 'OS::Heat::TestResource'}
- self.assertEqual(provider_resources,
- self.list_resources(provider_identifier))
-
- def test_stack_update_with_replacing_userdata(self):
- """Test case for updating userdata of instance.
-
- Confirm that we can update userdata of instance during updating stack
- by the user of member role.
-
- Make sure that a resource that inherits from StackUser can be deleted
- during updating stack.
- """
- if not self.conf.minimal_image_ref:
- raise self.skipException("No minimal image configured to test")
- if not self.conf.minimal_instance_type:
- raise self.skipException("No flavor configured to test")
-
- parms = {'flavor': self.conf.minimal_instance_type,
- 'image': self.conf.minimal_image_ref,
- 'network': self.conf.fixed_network_name,
- 'user_data': ''}
-
- stack_identifier = self.stack_create(
- template=self.update_userdata_template,
- parameters=parms
- )
-
- parms_updated = parms
- parms_updated['user_data'] = 'two'
- self.update_stack(
- stack_identifier,
- template=self.update_userdata_template,
- parameters=parms_updated)
-
- def test_stack_update_provider_group_patch(self):
- '''Test two-level nested update with PATCH'''
- template = _change_rsrc_properties(
- test_template_one_resource, ['test1'],
- {'value': 'test_provider_group_template'})
- files = {'provider.template': json.dumps(template)}
- env = {'resource_registry':
- {'My::TestResource': 'provider.template'}}
-
- stack_identifier = self.stack_create(
- template=self.provider_group_template,
- files=files,
- environment=env
- )
-
- initial_resources = {'test_group': 'OS::Heat::ResourceGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- # Prove the resource is backed by a nested stack, save the ID
- nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
- 'test_group')
-
- # Then check the expected resources are in the nested stack
- nested_resources = {'0': 'My::TestResource',
- '1': 'My::TestResource'}
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
-
- # increase the count, pass only the paramter, no env or template
- params = {'count': 3}
- self.update_stack(stack_identifier, parameters=params, existing=True)
-
- # Parent resources should be unchanged and the nested stack
- # should have been updated in-place without replacement
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- # Resource group stack should also be unchanged (but updated)
- nested_stack = self.client.stacks.get(nested_identifier)
- self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
- # Add a resource, as we should have added one
- nested_resources['2'] = 'My::TestResource'
- self.assertEqual(nested_resources,
- self.list_resources(nested_identifier))
-
- def test_stack_update_from_failed_patch(self):
- '''Test PATCH update from a failed state.'''
-
- # Start with empty template
- stack_identifier = self.stack_create(
- template='heat_template_version: 2014-10-16')
-
- # Update with a good template, but bad parameter
- self.update_stack(stack_identifier,
- template=self.fail_param_template,
- parameters={'do_fail': True},
- expected_status='UPDATE_FAILED')
-
- # PATCH update, only providing the parameter
- self.update_stack(stack_identifier,
- parameters={'do_fail': False},
- existing=True)
- self.assertEqual({u'aresource': u'OS::Heat::TestResource'},
- self.list_resources(stack_identifier))
-
- def test_stack_update_with_new_env(self):
- """Update handles new resource types in the environment.
-
- If a resource type appears during an update and the update fails,
- retrying the update is able to find the type properly in the
- environment.
- """
- stack_identifier = self.stack_create(
- template=test_template_one_resource)
-
- # Update with a new resource and make the update fails
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'], {'fail': True})
- template['resources']['test2'] = {'type': 'My::TestResource'}
- template['resources']['test1']['depends_on'] = 'test2'
- env = {'resource_registry':
- {'My::TestResource': 'OS::Heat::TestResource'}}
- self.update_stack(stack_identifier,
- template=template,
- environment=env,
- expected_status='UPDATE_FAILED')
-
- # Fixing the template should fix the stack
- template = _change_rsrc_properties(template,
- ['test1'], {'fail': False})
- self.update_stack(stack_identifier,
- template=template,
- environment=env)
- self.assertEqual({'test1': 'OS::Heat::TestResource',
- 'test2': 'My::TestResource'},
- self.list_resources(stack_identifier))
-
- def test_stack_update_with_new_version(self):
- """Update handles new template version in failure.
-
- If a stack update fails while changing the template version, update is
- able to handle the new version fine.
- """
- stack_identifier = self.stack_create(
- template=test_template_one_resource)
-
- # Update with a new function and make the update fails
- template = _change_rsrc_properties(test_template_two_resource,
- ['test1'], {'fail': True})
-
- template['heat_template_version'] = '2015-10-15'
- template['resources']['test2']['properties']['value'] = {
- 'list_join': [',', ['a'], ['b']]}
- self.update_stack(stack_identifier,
- template=template,
- expected_status='UPDATE_FAILED')
-
- template = _change_rsrc_properties(template,
- ['test2'], {'value': 'Test2'})
- self.update_stack(stack_identifier,
- template=template,
- expected_status='UPDATE_FAILED')
- self._stack_delete(stack_identifier)
-
- def test_stack_update_with_old_version(self):
- """Update handles old template version in failure.
-
- If a stack update fails while changing the template version, update is
- able to handle the old version fine.
- """
- template = _change_rsrc_properties(
- test_template_one_resource,
- ['test1'], {'value': {'list_join': [',', ['a'], ['b']]}})
- template['heat_template_version'] = '2015-10-15'
- stack_identifier = self.stack_create(
- template=template)
-
- # Update with a new function and make the update fails
- template = _change_rsrc_properties(test_template_one_resource,
- ['test1'], {'fail': True})
- self.update_stack(stack_identifier,
- template=template,
- expected_status='UPDATE_FAILED')
- self._stack_delete(stack_identifier)
-
- def test_stack_update_with_conditions(self):
- """Update manages new conditions added.
-
- When a new resource is added during updates, the stacks handles the new
- conditions correctly, and doesn't fail to load them while the update is
- still in progress.
- """
- stack_identifier = self.stack_create(
- template=test_template_one_resource)
-
- updated_template = copy.deepcopy(test_template_two_resource)
- updated_template['conditions'] = {'cond1': True}
- updated_template['resources']['test3'] = {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': {'if': ['cond1', 'val3', 'val4']}
- }
- }
- test2_props = updated_template['resources']['test2']['properties']
- test2_props['action_wait_secs'] = {'create': 30}
-
- self.update_stack(stack_identifier,
- template=updated_template,
- expected_status='UPDATE_IN_PROGRESS')
-
- def check_resources():
- resources = self.list_resources(stack_identifier)
- if len(resources) < 2:
- return False
- self.assertIn('test3', resources)
- return True
-
- self.assertTrue(test.call_until_true(20, 2, check_resources))
diff --git a/heat_tempest_plugin/tests/functional/test_create_update_neutron_port.py b/heat_tempest_plugin/tests/functional/test_create_update_neutron_port.py
index 72cd47c..eeeabf3 100644
--- a/heat_tempest_plugin/tests/functional/test_create_update_neutron_port.py
+++ b/heat_tempest_plugin/tests/functional/test_create_update_neutron_port.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -62,6 +64,7 @@
port_mac = self._stack_output(stack, 'mac_address')
return port_id[0], port_ip, port_mac
+ @decorators.idempotent_id('8e37933b-345c-4ce5-a523-e67bdf198d73')
def test_update_remove_ip(self):
# create with defined ip_address
stack_identifier = self.stack_create(template=test_template)
@@ -77,6 +80,7 @@
self.assertEqual(_id, new_id)
self.assertEqual(_mac, new_mac)
+ @decorators.idempotent_id('df1bf0fb-115e-42f3-b6cc-e12b5d506c98')
def test_update_with_mac_address(self):
if not self.conf.admin_username or not self.conf.admin_password:
self.skipTest('No admin creds found, skipping')
diff --git a/heat_tempest_plugin/tests/functional/test_create_update_neutron_subnet.py b/heat_tempest_plugin/tests/functional/test_create_update_neutron_subnet.py
index 345a740..9a10ba2 100644
--- a/heat_tempest_plugin/tests/functional/test_create_update_neutron_subnet.py
+++ b/heat_tempest_plugin/tests/functional/test_create_update_neutron_subnet.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -41,6 +43,7 @@
output = self._stack_output(stack, output_key)
return output
+ @decorators.idempotent_id('af43fc6d-58ba-4b5f-bd68-07b29f0a96bc')
def test_update_allocation_pools(self):
stack_identifier = self.stack_create(template=test_template)
alloc_pools = self.get_outputs(stack_identifier, 'alloc_pools')
@@ -57,6 +60,7 @@
self.assertEqual([{'start': '11.11.11.10', 'end': '11.11.11.100'}],
new_alloc_pools)
+ @decorators.idempotent_id('2f1d3b04-2183-4e50-8aa3-fa0fbff5c861')
def test_update_allocation_pools_to_empty(self):
stack_identifier = self.stack_create(template=test_template)
alloc_pools = self.get_outputs(stack_identifier, 'alloc_pools')
@@ -72,6 +76,7 @@
# new_alloc_pools should be []
self.assertEqual([], new_alloc_pools)
+ @decorators.idempotent_id('6ec51150-6f83-4179-9faa-a3aee91ea150')
def test_update_to_no_allocation_pools(self):
stack_identifier = self.stack_create(template=test_template)
alloc_pools = self.get_outputs(stack_identifier, 'alloc_pools')
@@ -87,6 +92,7 @@
# last_alloc_pools should be []
self.assertEqual([], last_alloc_pools)
+ @decorators.idempotent_id('ffb3eaf8-044b-4229-99f9-5fd983ccc9d9')
def test_update_gateway_ip(self):
stack_identifier = self.stack_create(template=test_template)
gw_ip = self.get_outputs(stack_identifier, 'gateway_ip')
@@ -100,6 +106,7 @@
# the gateway_ip should be the new one
self.assertEqual('11.11.11.9', new_gw_ip)
+ @decorators.idempotent_id('e7b37a6b-e883-4c0c-b6a3-9ece1843e902')
def test_update_gateway_ip_to_empty(self):
stack_identifier = self.stack_create(template=test_template)
gw_ip = self.get_outputs(stack_identifier, 'gateway_ip')
@@ -113,6 +120,7 @@
# new gateway_ip should be None
self.assertIsNone(new_gw_ip)
+ @decorators.idempotent_id('373bab03-1f66-4883-b1de-2ecc39efe17e')
def test_update_to_no_gateway_ip(self):
stack_identifier = self.stack_create(template=test_template)
gw_ip = self.get_outputs(stack_identifier, 'gateway_ip')
diff --git a/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py b/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py
index d572405..ff5bcaf 100644
--- a/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py
+++ b/heat_tempest_plugin/tests/functional/test_create_update_neutron_trunk.py
@@ -15,6 +15,8 @@
import copy
import yaml
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -94,6 +96,7 @@
sub_ports_set = {frozenset(d.items()) for d in new_sub_ports}
return sub_ports_set
+ @decorators.idempotent_id('5572b0ac-fdb2-4c68-a49e-024771814471')
def test_add_first_sub_port(self):
stack_identifier = self.stack_create(template=test_template)
@@ -117,6 +120,7 @@
self.assertEqual(self._sub_ports_dict_to_set(new_sub_port),
self._sub_ports_dict_to_set(trunk_sub_port))
+ @decorators.idempotent_id('c3f52330-01b7-4649-99fd-43700e6bbda3')
def test_add_a_second_sub_port(self):
parsed_template = yaml.safe_load(test_template)
sub_ports = [{'port': {'get_resource': 'sub_port_one'},
@@ -153,6 +157,7 @@
self.assertEqual(self._sub_ports_dict_to_set(expected_sub_ports),
self._sub_ports_dict_to_set(trunk_sub_ports))
+ @decorators.idempotent_id('cb59363e-5517-42f9-8b93-9ad700e2ef4c')
def test_remove_sub_port_from_trunk(self):
sub_ports = [{'port': {'get_resource': 'sub_port_one'},
'segmentation_type': 'vlan',
@@ -189,6 +194,7 @@
self.assertEqual(self._sub_ports_dict_to_set(expected_sub_ports),
self._sub_ports_dict_to_set(trunk_sub_ports))
+ @decorators.idempotent_id('7f0836c7-1d35-4d11-bcdc-e0e19ca68b68')
def test_remove_last_sub_port_from_trunk(self):
sub_ports = [{'port': {'get_resource': 'sub_port_one'},
'segmentation_type': 'vlan',
@@ -224,6 +230,7 @@
'The returned sub ports (%s) in trunk_details is '
'not empty!' % trunk_sub_ports)
+ @decorators.idempotent_id('e9296d6f-009c-4530-9aaf-84a0f8281bcb')
def test_update_existing_sub_port_on_trunk(self):
sub_ports = [{'port': {'get_resource': 'sub_port_one'},
'segmentation_type': 'vlan',
@@ -253,6 +260,7 @@
self.assertEqual(self._sub_ports_dict_to_set([updated_sub_port]),
self._sub_ports_dict_to_set(trunk_sub_ports))
+ @decorators.idempotent_id('912044d8-e0c1-4c6c-ab4e-bc1cadc46c18')
def test_update_trunk_name_and_description(self):
new_name = 'pineapple'
new_description = 'This is a test trunk'
diff --git a/heat_tempest_plugin/tests/functional/test_default_parameters.py b/heat_tempest_plugin/tests/functional/test_default_parameters.py
deleted file mode 100644
index 24aa6e4..0000000
--- a/heat_tempest_plugin/tests/functional/test_default_parameters.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import yaml
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class DefaultParametersTest(functional_base.FunctionalTestsBase):
-
- template = '''
-heat_template_version: 2013-05-23
-parameters:
- length:
- type: string
- default: 40
-resources:
- random1:
- type: nested_random.yaml
- random2:
- type: OS::Heat::RandomString
- properties:
- length: {get_param: length}
-outputs:
- random1:
- value: {get_attr: [random1, random1_value]}
- random2:
- value: {get_resource: random2}
-'''
- nested_template = '''
-heat_template_version: 2013-05-23
-parameters:
- length:
- type: string
- default: 50
-resources:
- random1:
- type: OS::Heat::RandomString
- properties:
- length: {get_param: length}
-outputs:
- random1_value:
- value: {get_resource: random1}
-'''
-
- scenarios = [
- ('none', dict(param=None, default=None, temp_def=True,
- expect1=50, expect2=40)),
- ('default', dict(param=None, default=12, temp_def=True,
- expect1=12, expect2=12)),
- ('both', dict(param=15, default=12, temp_def=True,
- expect1=12, expect2=15)),
- ('no_temp_default', dict(param=None, default=12, temp_def=False,
- expect1=12, expect2=12)),
- ]
-
- def test_defaults(self):
- env = {'parameters': {}, 'parameter_defaults': {}}
- if self.param:
- env['parameters'] = {'length': self.param}
- if self.default:
- env['parameter_defaults'] = {'length': self.default}
-
- if not self.temp_def:
- # remove the default from the parameter in the nested template.
- ntempl = yaml.safe_load(self.nested_template)
- del ntempl['parameters']['length']['default']
- nested_template = yaml.safe_dump(ntempl)
- else:
- nested_template = self.nested_template
-
- stack_identifier = self.stack_create(
- template=self.template,
- files={'nested_random.yaml': nested_template},
- environment=env
- )
-
- stack = self.client.stacks.get(stack_identifier)
- for out in stack.outputs:
- if out['output_key'] == 'random1':
- self.assertEqual(self.expect1, len(out['output_value']))
- if out['output_key'] == 'random2':
- self.assertEqual(self.expect2, len(out['output_value']))
diff --git a/heat_tempest_plugin/tests/functional/test_delete.py b/heat_tempest_plugin/tests/functional/test_delete.py
deleted file mode 100644
index 20266d6..0000000
--- a/heat_tempest_plugin/tests/functional/test_delete.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import time
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class DeleteInProgressTest(functional_base.FunctionalTestsBase):
-
- root_template = '''
-heat_template_version: 2013-05-23
-resources:
- rg:
- type: OS::Heat::ResourceGroup
- properties:
- count: 125
- resource_def:
- type: empty.yaml
-'''
-
- empty_template = '''
-heat_template_version: 2013-05-23
-resources:
-'''
-
- def test_delete_nested_stacks_create_in_progress(self):
- files = {'empty.yaml': self.empty_template}
- identifier = self.stack_create(template=self.root_template,
- files=files,
- expected_status='CREATE_IN_PROGRESS')
- time.sleep(20)
- self._stack_delete(identifier)
diff --git a/heat_tempest_plugin/tests/functional/test_encrypted_parameter.py b/heat_tempest_plugin/tests/functional/test_encrypted_parameter.py
index bfbd77b..0e7483e 100644
--- a/heat_tempest_plugin/tests/functional/test_encrypted_parameter.py
+++ b/heat_tempest_plugin/tests/functional/test_encrypted_parameter.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -43,6 +45,7 @@
value: { get_param: foo }
'''
+ @decorators.idempotent_id('d5bbf763-8eb9-4657-ad3f-e97405b6e1f2')
def test_db_encryption(self):
# Create a stack with the value of 'foo' to be encrypted
foo_param = 'my_encrypted_foo'
diff --git a/heat_tempest_plugin/tests/functional/test_encryption_vol_type.py b/heat_tempest_plugin/tests/functional/test_encryption_vol_type.py
index 2b98022..c0dd884 100644
--- a/heat_tempest_plugin/tests/functional/test_encryption_vol_type.py
+++ b/heat_tempest_plugin/tests/functional/test_encryption_vol_type.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
from heat_tempest_plugin.tests.functional import functional_base
@@ -73,6 +74,7 @@
self.assertEqual(my_encrypted_vol_type_tmpl_prop['control_location'],
phy_rsrc_specs.control_location)
+ @decorators.idempotent_id('a98de4ce-aa59-478b-a8ce-b5d818c76a68')
def test_create_update(self):
stack_identifier = self.stack_create(
template=test_encryption_vol_type)
diff --git a/heat_tempest_plugin/tests/functional/test_env_merge.py b/heat_tempest_plugin/tests/functional/test_env_merge.py
deleted file mode 100644
index 68ec035..0000000
--- a/heat_tempest_plugin/tests/functional/test_env_merge.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-TEMPLATE = '''
- heat_template_version: 2015-04-30
- parameters:
- p0:
- type: string
- default: CORRECT
- p1:
- type: string
- default: INCORRECT
- p2:
- type: string
- default: INCORRECT
- resources:
- r1:
- type: test::R1
- r2:
- type: test::R2
- r3a:
- type: test::R3
- r3b:
- type: test::R3
-'''
-
-ENV_1 = '''
- parameters:
- p1: CORRECT
- p2: INCORRECT-E1
- resource_registry:
- test::R1: OS::Heat::RandomString
- test::R2: BROKEN
- test::R3: OS::Heat::None
-'''
-
-ENV_2 = '''
- parameters:
- p2: CORRECT
- resource_registry:
- test::R2: OS::Heat::RandomString
- resources:
- r3b:
- test::R3: OS::Heat::RandomString
-'''
-
-
-class EnvironmentMergingTests(functional_base.FunctionalTestsBase):
-
- def test_server_environment_merging(self):
-
- # Setup
- files = {'env1.yaml': ENV_1, 'env2.yaml': ENV_2}
- environment_files = ['env1.yaml', 'env2.yaml']
-
- # Test
- stack_id = self.stack_create(stack_name='env_merge',
- template=TEMPLATE,
- files=files,
- environment_files=environment_files)
-
- # Verify
-
- # Since there is no environment show, the registry overriding
- # is partially verified by there being no error. If it wasn't
- # working, test::R2 would remain mapped to BROKEN in env1.
-
- # Sanity check
- resources = self.list_resources(stack_id)
- self.assertEqual(4, len(resources))
-
- # Verify the parameters are correctly set
- stack = self.client.stacks.get(stack_id)
- self.assertEqual('CORRECT', stack.parameters['p0'])
- self.assertEqual('CORRECT', stack.parameters['p1'])
- self.assertEqual('CORRECT', stack.parameters['p2'])
-
- # Verify that r3b has been overridden into a RandomString
- # by checking to see that it has a value
- r3b = self.client.resources.get(stack_id, 'r3b')
- r3b_attrs = r3b.attributes
- self.assertIn('value', r3b_attrs)
diff --git a/heat_tempest_plugin/tests/functional/test_event_sinks.py b/heat_tempest_plugin/tests/functional/test_event_sinks.py
index a6e59eb..7cb1d7b 100644
--- a/heat_tempest_plugin/tests/functional/test_event_sinks.py
+++ b/heat_tempest_plugin/tests/functional/test_event_sinks.py
@@ -12,6 +12,7 @@
import uuid
+from tempest.lib import decorators
from zaqarclient.queues.v2 import client as zaqarclient
from heat_tempest_plugin.common import test
@@ -28,6 +29,7 @@
value: ok
'''
+ @decorators.idempotent_id('d0b72695-e97d-4aa1-bfaf-31c14b09ac87')
def test_events(self):
queue_id = str(uuid.uuid4())
environment = {'event_sinks': [{'type': 'zaqar-queue',
diff --git a/heat_tempest_plugin/tests/functional/test_external_ref.py b/heat_tempest_plugin/tests/functional/test_external_ref.py
index c54e453..d6a73ac 100644
--- a/heat_tempest_plugin/tests/functional/test_external_ref.py
+++ b/heat_tempest_plugin/tests/functional/test_external_ref.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -32,6 +34,7 @@
value: {get_resource: test1}
'''
+ @decorators.idempotent_id('45449bad-18ba-4148-82e6-a6bc1e9a9b04')
def test_create_with_external_ref(self):
stack_name = self._stack_rand_name()
stack_identifier = self.stack_create(
@@ -55,6 +58,7 @@
'output_key': 'str',
'output_value': 'foobar'}], stack.outputs)
+ @decorators.idempotent_id('fb16477c-e981-4ef9-a83b-c0acc162343a')
def test_update_with_external_ref(self):
stack_name = self._stack_rand_name()
stack_identifier = self.stack_create(
diff --git a/heat_tempest_plugin/tests/functional/test_heat_autoscaling.py b/heat_tempest_plugin/tests/functional/test_heat_autoscaling.py
deleted file mode 100644
index a83c7fe..0000000
--- a/heat_tempest_plugin/tests/functional/test_heat_autoscaling.py
+++ /dev/null
@@ -1,215 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class HeatAutoscalingTest(functional_base.FunctionalTestsBase):
- template = '''
-heat_template_version: 2014-10-16
-
-resources:
- random_group:
- type: OS::Heat::AutoScalingGroup
- properties:
- cooldown: 0
- desired_capacity: 3
- max_size: 5
- min_size: 2
- resource:
- type: OS::Heat::RandomString
-
- scale_up_policy:
- type: OS::Heat::ScalingPolicy
- properties:
- adjustment_type: change_in_capacity
- auto_scaling_group_id: { get_resource: random_group }
- scaling_adjustment: 1
-
- scale_down_policy:
- type: OS::Heat::ScalingPolicy
- properties:
- adjustment_type: change_in_capacity
- auto_scaling_group_id: { get_resource: random_group }
- scaling_adjustment: -1
-
-outputs:
- all_values:
- value: {get_attr: [random_group, outputs_list, value]}
- value_0:
- value: {get_attr: [random_group, resource.0.value]}
- value_1:
- value: {get_attr: [random_group, resource.1.value]}
- value_2:
- value: {get_attr: [random_group, resource.2.value]}
- asg_size:
- value: {get_attr: [random_group, current_size]}
-'''
-
- template_nested = '''
-heat_template_version: 2014-10-16
-
-resources:
- random_group:
- type: OS::Heat::AutoScalingGroup
- properties:
- desired_capacity: 3
- max_size: 5
- min_size: 2
- resource:
- type: randomstr.yaml
-
-outputs:
- all_values:
- value: {get_attr: [random_group, outputs_list, random_str]}
- value_0:
- value: {get_attr: [random_group, resource.0.random_str]}
- value_1:
- value: {get_attr: [random_group, resource.1.random_str]}
- value_2:
- value: {get_attr: [random_group, resource.2.random_str]}
-'''
-
- template_randomstr = '''
-heat_template_version: 2013-05-23
-
-resources:
- random_str:
- type: OS::Heat::RandomString
-
-outputs:
- random_str:
- value: {get_attr: [random_str, value]}
-'''
-
- def _assert_output_values(self, stack_id):
- stack = self.client.stacks.get(stack_id)
- all_values = self._stack_output(stack, 'all_values')
- self.assertEqual(3, len(all_values))
- self.assertEqual(all_values[0], self._stack_output(stack, 'value_0'))
- self.assertEqual(all_values[1], self._stack_output(stack, 'value_1'))
- self.assertEqual(all_values[2], self._stack_output(stack, 'value_2'))
-
- def test_asg_scale_up_max_size(self):
- stack_id = self.stack_create(template=self.template,
- expected_status='CREATE_COMPLETE')
- stack = self.client.stacks.get(stack_id)
- asg_size = self._stack_output(stack, 'asg_size')
- # Ensure that initial desired capacity is met
- self.assertEqual(3, asg_size)
-
- # send scale up signals and ensure that asg honors max_size
- asg = self.client.resources.get(stack_id, 'random_group')
- max_size = 5
- for num in range(asg_size+1, max_size+2):
- expected_resources = num if num <= max_size else max_size
- self.client.resources.signal(stack_id, 'scale_up_policy')
- self.assertTrue(
- test.call_until_true(self.conf.build_timeout,
- self.conf.build_interval,
- self.check_autoscale_complete,
- asg.physical_resource_id,
- expected_resources, stack_id,
- 'scale_up_policy'))
-
- def test_asg_scale_down_min_size(self):
- stack_id = self.stack_create(template=self.template,
- expected_status='CREATE_COMPLETE')
- stack = self.client.stacks.get(stack_id)
- asg_size = self._stack_output(stack, 'asg_size')
- # Ensure that initial desired capacity is met
- self.assertEqual(3, asg_size)
-
- # send scale down signals and ensure that asg honors min_size
- asg = self.client.resources.get(stack_id, 'random_group')
- min_size = 2
- for num in range(asg_size-1, 0, -1):
- expected_resources = num if num >= min_size else min_size
- self.client.resources.signal(stack_id, 'scale_down_policy')
- self.assertTrue(
- test.call_until_true(self.conf.build_timeout,
- self.conf.build_interval,
- self.check_autoscale_complete,
- asg.physical_resource_id,
- expected_resources, stack_id,
- 'scale_down_policy'))
-
- def test_asg_cooldown(self):
- cooldown_tmpl = self.template.replace('cooldown: 0',
- 'cooldown: 60')
- stack_id = self.stack_create(template=cooldown_tmpl,
- expected_status='CREATE_COMPLETE')
- stack = self.client.stacks.get(stack_id)
- asg_size = self._stack_output(stack, 'asg_size')
- # Ensure that initial desired capacity is met
- self.assertEqual(3, asg_size)
-
- # send scale up signal.
- # Since cooldown is in effect, number of resources should not change
- asg = self.client.resources.get(stack_id, 'random_group')
- expected_resources = 3
- self.client.resources.signal(stack_id, 'scale_up_policy')
- self.assertTrue(
- test.call_until_true(self.conf.build_timeout,
- self.conf.build_interval,
- self.check_autoscale_complete,
- asg.physical_resource_id,
- expected_resources, stack_id,
- 'scale_up_policy'))
-
- def test_path_attrs(self):
- stack_id = self.stack_create(template=self.template)
- expected_resources = {'random_group': 'OS::Heat::AutoScalingGroup',
- 'scale_up_policy': 'OS::Heat::ScalingPolicy',
- 'scale_down_policy': 'OS::Heat::ScalingPolicy'}
- self.assertEqual(expected_resources, self.list_resources(stack_id))
- self._assert_output_values(stack_id)
-
- def test_path_attrs_nested(self):
- files = {'randomstr.yaml': self.template_randomstr}
- stack_id = self.stack_create(template=self.template_nested,
- files=files)
- expected_resources = {'random_group': 'OS::Heat::AutoScalingGroup'}
- self.assertEqual(expected_resources, self.list_resources(stack_id))
- self._assert_output_values(stack_id)
-
-
-class AutoScalingGroupUpdateWithNoChanges(functional_base.FunctionalTestsBase):
-
- template = '''
-heat_template_version: 2013-05-23
-
-resources:
- test_group:
- type: OS::Heat::AutoScalingGroup
- properties:
- desired_capacity: 0
- max_size: 0
- min_size: 0
- resource:
- type: OS::Heat::RandomString
- test_policy:
- type: OS::Heat::ScalingPolicy
- properties:
- adjustment_type: change_in_capacity
- auto_scaling_group_id: { get_resource: test_group }
- scaling_adjustment: 1
-'''
-
- def test_as_group_update_without_resource_changes(self):
- stack_identifier = self.stack_create(template=self.template)
- new_template = self.template.replace(
- 'scaling_adjustment: 1',
- 'scaling_adjustment: 2')
-
- self.update_stack(stack_identifier, template=new_template)
diff --git a/heat_tempest_plugin/tests/functional/test_hooks.py b/heat_tempest_plugin/tests/functional/test_hooks.py
index 78c58f3..91351fa 100644
--- a/heat_tempest_plugin/tests/functional/test_hooks.py
+++ b/heat_tempest_plugin/tests/functional/test_hooks.py
@@ -12,6 +12,8 @@
import yaml
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -27,6 +29,7 @@
'foo_step3': {'type': 'OS::Heat::RandomString',
'depends_on': 'foo_step2'}}}
+ @decorators.idempotent_id('f14cbe86-0392-474a-9f28-bb561745698d')
def test_hook_pre_create(self):
env = {'resource_registry':
{'resources':
@@ -58,6 +61,7 @@
stack_identifier, 'foo_step2', 'CREATE_COMPLETE')
self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
+ @decorators.idempotent_id('75009076-96eb-43af-b4df-f002704737d2')
def test_hook_pre_update_nochange(self):
env = {'resource_registry':
{'resources':
@@ -99,6 +103,7 @@
self.assertEqual(res_before.physical_resource_id,
res_after.physical_resource_id)
+ @decorators.idempotent_id('9f03d1e0-a2d2-42fc-a9c6-47db05b9c8ed')
def test_hook_pre_update_replace(self):
env = {'resource_registry':
{'resources':
@@ -141,6 +146,7 @@
self.assertNotEqual(res_before.physical_resource_id,
res_after.physical_resource_id)
+ @decorators.idempotent_id('15c4f97d-5e06-40c6-83af-0e57d5afaff9')
def test_hook_pre_update_in_place(self):
env = {'resource_registry':
{'resources':
@@ -190,6 +196,7 @@
self.assertEqual(res_before.physical_resource_id,
res_after.physical_resource_id)
+ @decorators.idempotent_id('7d96b5f4-4aba-493d-b098-4beea4654a2b')
def test_hook_pre_create_nested(self):
files = {'nested.yaml': yaml.safe_dump(self.template)}
env = {'resource_registry':
@@ -231,6 +238,7 @@
nested_identifier, 'foo_step2', 'CREATE_COMPLETE')
self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
+ @decorators.idempotent_id('cffc0783-3add-4f0c-ac66-03c90f6feeb0')
def test_hook_pre_create_wildcard(self):
env = {'resource_registry':
{'resources':
diff --git a/heat_tempest_plugin/tests/functional/test_immutable_parameters.py b/heat_tempest_plugin/tests/functional/test_immutable_parameters.py
deleted file mode 100644
index f031c01..0000000
--- a/heat_tempest_plugin/tests/functional/test_immutable_parameters.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-from heatclient import exc as heat_exceptions
-
-
-class ImmutableParametersTest(functional_base.FunctionalTestsBase):
-
- template_param_has_no_immutable_field = '''
-heat_template_version: 2014-10-16
-parameters:
- param1:
- type: string
- default: default_value
-outputs:
- param1_output:
- description: 'parameter 1 details'
- value: { get_param: param1 }
-'''
-
- template_param_has_immutable_field = '''
-heat_template_version: 2014-10-16
-parameters:
- param1:
- type: string
- default: default_value
- immutable: false
-outputs:
- param1_output:
- description: 'parameter 1 details'
- value: { get_param: param1 }
-'''
-
- def test_no_immutable_param_field(self):
- param1_create_value = 'value1'
- create_parameters = {"param1": param1_create_value}
-
- stack_identifier = self.stack_create(
- template=self.template_param_has_no_immutable_field,
- parameters=create_parameters
- )
- stack = self.client.stacks.get(stack_identifier)
-
- # Verify the value of the parameter
- self.assertEqual(param1_create_value,
- self._stack_output(stack, 'param1_output'))
-
- param1_update_value = 'value2'
- update_parameters = {"param1": param1_update_value}
-
- self.update_stack(
- stack_identifier,
- template=self.template_param_has_no_immutable_field,
- parameters=update_parameters)
-
- stack = self.client.stacks.get(stack_identifier)
-
- # Verify the value of the updated parameter
- self.assertEqual(param1_update_value,
- self._stack_output(stack, 'param1_output'))
-
- def test_immutable_param_field_allowed(self):
- param1_create_value = 'value1'
- create_parameters = {"param1": param1_create_value}
-
- stack_identifier = self.stack_create(
- template=self.template_param_has_immutable_field,
- parameters=create_parameters
- )
- stack = self.client.stacks.get(stack_identifier)
-
- # Verify the value of the parameter
- self.assertEqual(param1_create_value,
- self._stack_output(stack, 'param1_output'))
-
- param1_update_value = 'value2'
- update_parameters = {"param1": param1_update_value}
-
- self.update_stack(
- stack_identifier,
- template=self.template_param_has_immutable_field,
- parameters=update_parameters)
- stack = self.client.stacks.get(stack_identifier)
-
- # Verify the value of the updated parameter
- self.assertEqual(param1_update_value,
- self._stack_output(stack, 'param1_output'))
-
- # Ensure stack is not in a failed state
- self.assertEqual('UPDATE_COMPLETE', stack.stack_status)
-
- def test_immutable_param_field_error(self):
- param1_create_value = 'value1'
- create_parameters = {"param1": param1_create_value}
-
- # Toggle the immutable field to preclude updating
- immutable_true = self.template_param_has_immutable_field.replace(
- 'immutable: false', 'immutable: true')
-
- stack_identifier = self.stack_create(
- template=immutable_true,
- parameters=create_parameters
- )
- stack = self.client.stacks.get(stack_identifier)
-
- param1_update_value = 'value2'
- update_parameters = {"param1": param1_update_value}
-
- # Verify the value of the parameter
- self.assertEqual(param1_create_value,
- self._stack_output(stack, 'param1_output'))
-
- # Attempt to update the stack with a new parameter value
- try:
- self.update_stack(
- stack_identifier,
- template=immutable_true,
- parameters=update_parameters)
- except heat_exceptions.HTTPBadRequest as exc:
- exp = ('The following parameters are immutable and may not be '
- 'updated: param1')
- self.assertIn(exp, str(exc))
-
- stack = self.client.stacks.get(stack_identifier)
-
- # Ensure stack is not in a failed state
- self.assertEqual('CREATE_COMPLETE', stack.stack_status)
-
- # Ensure immutable parameter has not changed
- self.assertEqual(param1_create_value,
- self._stack_output(stack, 'param1_output'))
diff --git a/heat_tempest_plugin/tests/functional/test_instance_group.py b/heat_tempest_plugin/tests/functional/test_instance_group.py
deleted file mode 100644
index c0bb7e8..0000000
--- a/heat_tempest_plugin/tests/functional/test_instance_group.py
+++ /dev/null
@@ -1,500 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-import json
-
-from testtools import matchers
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class InstanceGroupTest(functional_base.FunctionalTestsBase):
-
- template = '''
-{
- "AWSTemplateFormatVersion" : "2010-09-09",
- "Description" : "Template to create multiple instances.",
- "Parameters" : {"size": {"Type": "String", "Default": "1"},
- "AZ": {"Type": "String", "Default": "nova"},
- "image": {"Type": "String"},
- "flavor": {"Type": "String"},
- "user_data": {"Type": "String", "Default": "jsconfig data"}},
- "Resources": {
- "JobServerGroup": {
- "Type": "OS::Heat::InstanceGroup",
- "Properties": {
- "LaunchConfigurationName" : {"Ref": "JobServerConfig"},
- "Size" : {"Ref": "size"},
- "AvailabilityZones" : [{"Ref": "AZ"}]
- }
- },
-
- "JobServerConfig" : {
- "Type" : "AWS::AutoScaling::LaunchConfiguration",
- "Metadata": {"foo": "bar"},
- "Properties": {
- "ImageId" : {"Ref": "image"},
- "InstanceType" : {"Ref": "flavor"},
- "SecurityGroups" : [ "sg-1" ],
- "UserData" : {"Ref": "user_data"}
- }
- }
- },
- "Outputs": {
- "InstanceList": {"Value": {
- "Fn::GetAtt": ["JobServerGroup", "InstanceList"]}},
- "JobServerConfigRef": {"Value": {
- "Ref": "JobServerConfig"}}
- }
-}
-'''
-
- instance_template = '''
-heat_template_version: 2013-05-23
-parameters:
- ImageId: {type: string}
- InstanceType: {type: string}
- SecurityGroups: {type: comma_delimited_list}
- UserData: {type: string}
- Tags: {type: comma_delimited_list}
-
-resources:
- random1:
- type: OS::Heat::RandomString
- properties:
- salt: {get_param: UserData}
-outputs:
- PublicIp:
- value: {get_attr: [random1, value]}
-'''
-
- # This is designed to fail.
- bad_instance_template = '''
-heat_template_version: 2013-05-23
-parameters:
- ImageId: {type: string}
- InstanceType: {type: string}
- SecurityGroups: {type: comma_delimited_list}
- UserData: {type: string}
- Tags: {type: comma_delimited_list}
-
-resources:
- random1:
- type: OS::Heat::RandomString
- depends_on: waiter
- ready_poster:
- type: AWS::CloudFormation::WaitConditionHandle
- waiter:
- type: AWS::CloudFormation::WaitCondition
- properties:
- Handle: {Ref: ready_poster}
- Timeout: 1
-outputs:
- PublicIp:
- value: {get_attr: [random1, value]}
-'''
-
- def setUp(self):
- super(InstanceGroupTest, self).setUp()
- if not self.conf.minimal_image_ref:
- raise self.skipException("No minimal image configured to test")
- if not self.conf.instance_type:
- raise self.skipException("No flavor configured to test")
-
- def assert_instance_count(self, stack, expected_count):
- inst_list = self._stack_output(stack, 'InstanceList')
- self.assertEqual(expected_count, len(inst_list.split(',')))
-
- def _assert_instance_state(self, nested_identifier,
- num_complete, num_failed):
- for res in self.client.resources.list(nested_identifier):
- if 'COMPLETE' in res.resource_status:
- num_complete = num_complete - 1
- elif 'FAILED' in res.resource_status:
- num_failed = num_failed - 1
- self.assertEqual(0, num_failed)
- self.assertEqual(0, num_complete)
-
-
-class InstanceGroupBasicTest(InstanceGroupTest):
-
- def test_basic_create_works(self):
- """Make sure the working case is good.
-
- Note this combines test_override_aws_ec2_instance into this test as
- well, which is:
- If AWS::EC2::Instance is overridden, InstanceGroup will automatically
- use that overridden resource type.
- """
-
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 4,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
- stack_identifier = self.stack_create(template=self.template,
- files=files, environment=env)
- initial_resources = {
- 'JobServerConfig': 'AWS::AutoScaling::LaunchConfiguration',
- 'JobServerGroup': 'OS::Heat::InstanceGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 4)
-
- def test_size_updates_work(self):
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 2,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- stack_identifier = self.stack_create(template=self.template,
- files=files,
- environment=env)
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 2)
-
- # Increase min size to 5
- env2 = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 5,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
- self.update_stack(stack_identifier, self.template,
- environment=env2, files=files)
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 5)
-
- def test_update_group_replace(self):
- """Test case for ensuring non-updatable props case a replacement.
-
- Make sure that during a group update the non-updatable properties cause
- a replacement.
- """
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry':
- {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 1,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- stack_identifier = self.stack_create(template=self.template,
- files=files,
- environment=env)
- rsrc = self.client.resources.get(stack_identifier, 'JobServerGroup')
- orig_asg_id = rsrc.physical_resource_id
-
- env2 = {'resource_registry':
- {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': '2',
- 'AZ': 'wibble',
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type,
- 'user_data': 'new data'}}
- self.update_stack(stack_identifier, self.template,
- environment=env2, files=files)
-
- # replacement will cause the resource physical_resource_id to change.
- rsrc = self.client.resources.get(stack_identifier, 'JobServerGroup')
- self.assertNotEqual(orig_asg_id, rsrc.physical_resource_id)
-
- def test_create_instance_error_causes_group_error(self):
- """Test create failing a resource in the instance group.
-
- If a resource in an instance group fails to be created, the instance
- group itself will fail and the broken inner resource will remain.
- """
- stack_name = self._stack_rand_name()
- files = {'provider.yaml': self.bad_instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 2,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- self.client.stacks.create(
- stack_name=stack_name,
- template=self.template,
- files=files,
- disable_rollback=True,
- parameters={},
- environment=env
- )
- self.addCleanup(self._stack_delete, stack_name)
- stack = self.client.stacks.get(stack_name)
- stack_identifier = '%s/%s' % (stack_name, stack.id)
- self._wait_for_stack_status(stack_identifier, 'CREATE_FAILED')
- initial_resources = {
- 'JobServerConfig': 'AWS::AutoScaling::LaunchConfiguration',
- 'JobServerGroup': 'OS::Heat::InstanceGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
- self._assert_instance_state(nested_ident, 0, 2)
-
- def test_update_instance_error_causes_group_error(self):
- """Test update failing a resource in the instance group.
-
- If a resource in an instance group fails to be created during an
- update, the instance group itself will fail and the broken inner
- resource will remain.
- """
- files = {'provider.yaml': self.instance_template}
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': 2,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.instance_type}}
-
- stack_identifier = self.stack_create(template=self.template,
- files=files,
- environment=env)
- initial_resources = {
- 'JobServerConfig': 'AWS::AutoScaling::LaunchConfiguration',
- 'JobServerGroup': 'OS::Heat::InstanceGroup'}
- self.assertEqual(initial_resources,
- self.list_resources(stack_identifier))
-
- stack = self.client.stacks.get(stack_identifier)
- self.assert_instance_count(stack, 2)
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
- self._assert_instance_state(nested_ident, 2, 0)
- initial_list = [res.resource_name
- for res in self.client.resources.list(nested_ident)]
-
- env['parameters']['size'] = 3
- files2 = {'provider.yaml': self.bad_instance_template}
- self.client.stacks.update(
- stack_id=stack_identifier,
- template=self.template,
- files=files2,
- disable_rollback=True,
- parameters={},
- environment=env
- )
- self._wait_for_stack_status(stack_identifier, 'UPDATE_FAILED')
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
- # assert that there are 3 bad instances
- # 2 resources should be in update failed, and one create failed.
- for res in self.client.resources.list(nested_ident):
- if res.resource_name in initial_list:
- self._wait_for_resource_status(nested_ident,
- res.resource_name,
- 'UPDATE_FAILED')
- else:
- self._wait_for_resource_status(nested_ident,
- res.resource_name,
- 'CREATE_FAILED')
-
-
-class InstanceGroupUpdatePolicyTest(InstanceGroupTest):
-
- def ig_tmpl_with_updt_policy(self):
- templ = json.loads(copy.deepcopy(self.template))
- up = {"RollingUpdate": {
- "MinInstancesInService": "1",
- "MaxBatchSize": "2",
- "PauseTime": "PT1S"}}
- templ['Resources']['JobServerGroup']['UpdatePolicy'] = up
- return templ
-
- def update_instance_group(self, updt_template,
- num_updates_expected_on_updt,
- num_creates_expected_on_updt,
- num_deletes_expected_on_updt,
- update_replace):
-
- # setup stack from the initial template
- files = {'provider.yaml': self.instance_template}
- size = 5
- env = {'resource_registry': {'AWS::EC2::Instance': 'provider.yaml'},
- 'parameters': {'size': size,
- 'image': self.conf.minimal_image_ref,
- 'flavor': self.conf.minimal_instance_type}}
- stack_name = self._stack_rand_name()
- stack_identifier = self.stack_create(
- stack_name=stack_name,
- template=self.ig_tmpl_with_updt_policy(),
- files=files,
- environment=env)
- stack = self.client.stacks.get(stack_identifier)
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'JobServerGroup')
-
- # test that physical resource name of launch configuration is used
- conf_name = self._stack_output(stack, 'JobServerConfigRef')
- conf_name_pattern = '%s-JobServerConfig-[a-zA-Z0-9]+$' % stack_name
- self.assertThat(conf_name,
- matchers.MatchesRegex(conf_name_pattern))
-
- # test the number of instances created
- self.assert_instance_count(stack, size)
- # saves info from initial list of instances for comparison later
- init_instances = self.client.resources.list(nested_ident)
- init_names = [inst.resource_name for inst in init_instances]
-
- # test stack update
- self.update_stack(stack_identifier, updt_template,
- environment=env, files=files)
- updt_stack = self.client.stacks.get(stack_identifier)
-
- # test that the launch configuration is replaced
- updt_conf_name = self._stack_output(updt_stack, 'JobServerConfigRef')
- self.assertThat(updt_conf_name,
- matchers.MatchesRegex(conf_name_pattern))
- self.assertNotEqual(conf_name, updt_conf_name)
-
- # test that the group size are the same
- updt_instances = self.client.resources.list(nested_ident)
- updt_names = [inst.resource_name for inst in updt_instances]
- self.assertEqual(len(init_names), len(updt_names))
- for res in updt_instances:
- self.assertEqual('UPDATE_COMPLETE', res.resource_status)
-
- # test that the appropriate number of instance names are the same
- matched_names = set(updt_names) & set(init_names)
- self.assertEqual(num_updates_expected_on_updt, len(matched_names))
-
- # test that the appropriate number of new instances are created
- self.assertEqual(num_creates_expected_on_updt,
- len(set(updt_names) - set(init_names)))
-
- # test that the appropriate number of instances are deleted
- self.assertEqual(num_deletes_expected_on_updt,
- len(set(init_names) - set(updt_names)))
-
- # test that the older instances are the ones being deleted
- if num_deletes_expected_on_updt > 0:
- deletes_expected = init_names[:num_deletes_expected_on_updt]
- self.assertNotIn(deletes_expected, updt_names)
-
- def test_instance_group_update_replace(self):
- """Test simple update replace with no conflict.
-
- Test simple update replace with no conflict in batch size and
- minimum instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- grp = updt_template['Resources']['JobServerGroup']
- policy = grp['UpdatePolicy']['RollingUpdate']
- policy['MinInstancesInService'] = '1'
- policy['MaxBatchSize'] = '3'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=5,
- num_creates_expected_on_updt=0,
- num_deletes_expected_on_updt=0,
- update_replace=True)
-
- def test_instance_group_update_replace_with_adjusted_capacity(self):
- """Test update replace with capacity adjustment.
-
- Test update replace with capacity adjustment due to conflict in
- batch size and minimum instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- grp = updt_template['Resources']['JobServerGroup']
- policy = grp['UpdatePolicy']['RollingUpdate']
- policy['MinInstancesInService'] = '4'
- policy['MaxBatchSize'] = '4'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=2,
- num_creates_expected_on_updt=3,
- num_deletes_expected_on_updt=3,
- update_replace=True)
-
- def test_instance_group_update_replace_huge_batch_size(self):
- """Test update replace with a huge batch size."""
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['RollingUpdate']
- policy['MinInstancesInService'] = '0'
- policy['MaxBatchSize'] = '20'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=5,
- num_creates_expected_on_updt=0,
- num_deletes_expected_on_updt=0,
- update_replace=True)
-
- def test_instance_group_update_replace_huge_min_in_service(self):
- """Update replace with huge number of minimum instances in service."""
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['RollingUpdate']
- policy['MinInstancesInService'] = '20'
- policy['MaxBatchSize'] = '2'
- policy['PauseTime'] = 'PT0S'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['UserData'] = 'new data'
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=3,
- num_creates_expected_on_updt=2,
- num_deletes_expected_on_updt=2,
- update_replace=True)
-
- def test_instance_group_update_no_replace(self):
- """Test simple update only and no replace with no conflict.
-
- Test simple update only and no replace (i.e. updated instance flavor
- in Launch Configuration) with no conflict in batch size and
- minimum instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['RollingUpdate']
- policy['MinInstancesInService'] = '1'
- policy['MaxBatchSize'] = '3'
- policy['PauseTime'] = 'PT0S'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['InstanceType'] = self.conf.instance_type
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=5,
- num_creates_expected_on_updt=0,
- num_deletes_expected_on_updt=0,
- update_replace=False)
-
- def test_instance_group_update_no_replace_with_adjusted_capacity(self):
- """Test update only and no replace with capacity adjustment.
-
- Test update only and no replace (i.e. updated instance flavor in
- Launch Configuration) with capacity adjustment due to conflict in
- batch size and minimum instances in service.
- """
- updt_template = self.ig_tmpl_with_updt_policy()
- group = updt_template['Resources']['JobServerGroup']
- policy = group['UpdatePolicy']['RollingUpdate']
- policy['MinInstancesInService'] = '4'
- policy['MaxBatchSize'] = '4'
- policy['PauseTime'] = 'PT0S'
- config = updt_template['Resources']['JobServerConfig']
- config['Properties']['InstanceType'] = self.conf.instance_type
-
- self.update_instance_group(updt_template,
- num_updates_expected_on_updt=2,
- num_creates_expected_on_updt=3,
- num_deletes_expected_on_updt=3,
- update_replace=False)
diff --git a/heat_tempest_plugin/tests/functional/test_lbaasv2.py b/heat_tempest_plugin/tests/functional/test_lbaasv2.py
index e33c5b9..3439179 100644
--- a/heat_tempest_plugin/tests/functional/test_lbaasv2.py
+++ b/heat_tempest_plugin/tests/functional/test_lbaasv2.py
@@ -11,6 +11,8 @@
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -88,6 +90,7 @@
if not self.is_network_extension_supported('lbaasv2'):
self.skipTest('LBaasv2 extension not available, skipping')
+ @decorators.idempotent_id('2f4a476c-cba7-448b-a7c1-85f7284f0293')
def test_create_update_loadbalancer(self):
parameters = {
'subnet': self.conf.fixed_subnet_name,
@@ -125,6 +128,7 @@
self.assertEqual(7777, output['connection_limit'])
self.assertEqual('updatedListener', output['description'])
+ @decorators.idempotent_id('104f59ae-a3c8-4c12-98e5-a7bc0007878d')
def test_add_delete_poolmember(self):
parameters = {
'subnet': self.conf.fixed_subnet_name,
diff --git a/heat_tempest_plugin/tests/functional/test_nested_get_attr.py b/heat_tempest_plugin/tests/functional/test_nested_get_attr.py
deleted file mode 100644
index 57f65e2..0000000
--- a/heat_tempest_plugin/tests/functional/test_nested_get_attr.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-# Using nested get_attr functions isn't a good idea - in particular, this
-# actually working depends on correct dependencies between the two resources
-# whose attributes are being fetched, and these dependencies are non-local to
-# where the get_attr calls are used. Nevertheless, it did sort-of work, and
-# this test will help keep it that way.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-initial_template = '''
-heat_template_version: ocata
-resources:
- dict_resource:
- type: OS::Heat::Value
- properties:
- value:
- blarg: wibble
- foo: bar
- baz: quux
- fred: barney
- # These dependencies are required because we only want to read the
- # attribute values for a given resource once, and therefore we do so in
- # dependency order. This is necessarily true for a convergence traversal,
- # but also happens when we're fetching the resource attributes e.g. to show
- # the output values. The key1/key2 attribute values must be stored before
- # we attempt to calculate the dep_attrs for dict_resource in order to
- # correctly determine which attributes of dict_resource are used.
- depends_on:
- - key1
- - key2
- - indirect_key3_dep
- key1:
- type: OS::Heat::Value
- properties:
- value: blarg
- key2:
- type: OS::Heat::Value
- properties:
- value: foo
- key3:
- type: OS::Heat::Value
- properties:
- value: fred
- value1:
- type: OS::Heat::Value
- properties:
- value:
- get_attr:
- - dict_resource
- - value
- - {get_attr: [key1, value]}
- indirect_key3_dep:
- type: OS::Heat::Value
- properties:
- value: ignored
- depends_on: key3
-outputs:
- value1:
- value: {get_attr: [value1, value]}
- value2:
- value: {get_attr: [dict_resource, value, {get_attr: [key2, value]}]}
- value3:
- value: {get_attr: [dict_resource, value, {get_attr: [key3, value]}]}
-'''
-
-update_template = '''
-heat_template_version: ocata
-resources:
- dict_resource:
- type: OS::Heat::Value
- properties:
- value:
- blarg: wibble
- foo: bar
- baz: quux
- fred: barney
- depends_on:
- - key1
- - key2
- - indirect_key3_dep
- - key4
- key1:
- type: OS::Heat::Value
- properties:
- value: foo
- key2:
- type: OS::Heat::Value
- properties:
- value: fred
- key3:
- type: OS::Heat::Value
- properties:
- value: blarg
- key4:
- type: OS::Heat::Value
- properties:
- value: baz
- value1:
- type: OS::Heat::Value
- properties:
- value:
- get_attr:
- - dict_resource
- - value
- - {get_attr: [key1, value]}
- value4:
- type: OS::Heat::Value
- properties:
- value:
- get_attr:
- - dict_resource
- - value
- - {get_attr: [key4, value]}
- indirect_key3_dep:
- type: OS::Heat::Value
- properties:
- value: ignored
- depends_on: key3
-outputs:
- value1:
- value: {get_attr: [value1, value]}
- value2:
- value: {get_attr: [dict_resource, value, {get_attr: [key2, value]}]}
- value3:
- value: {get_attr: [dict_resource, value, {get_attr: [key3, value]}]}
- value4:
- value: {get_attr: [value4, value]}
-'''
-
-
-class NestedGetAttrTest(functional_base.FunctionalTestsBase):
- def assertOutput(self, value, stack_identifier, key):
- op = self.client.stacks.output_show(stack_identifier, key)['output']
- self.assertEqual(key, op['output_key'])
- if 'output_error' in op:
- raise Exception(op['output_error'])
- self.assertEqual(value, op['output_value'])
-
- def test_nested_get_attr_create(self):
- stack_identifier = self.stack_create(template=initial_template)
-
- self.assertOutput('wibble', stack_identifier, 'value1')
- self.assertOutput('bar', stack_identifier, 'value2')
- self.assertOutput('barney', stack_identifier, 'value3')
-
- def test_nested_get_attr_update(self):
- stack_identifier = self.stack_create(template=initial_template)
- self.update_stack(stack_identifier, template=update_template)
-
- self.assertOutput('bar', stack_identifier, 'value1')
- self.assertOutput('barney', stack_identifier, 'value2')
- self.assertOutput('wibble', stack_identifier, 'value3')
- self.assertOutput('quux', stack_identifier, 'value4')
diff --git a/heat_tempest_plugin/tests/functional/test_notifications.py b/heat_tempest_plugin/tests/functional/test_notifications.py
deleted file mode 100644
index 7a74cdb..0000000
--- a/heat_tempest_plugin/tests/functional/test_notifications.py
+++ /dev/null
@@ -1,194 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import kombu
-from oslo_config import cfg
-from oslo_messaging._drivers import common
-from oslo_messaging import transport
-import requests
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-BASIC_NOTIFICATIONS = [
- 'orchestration.stack.create.start',
- 'orchestration.stack.create.end',
- 'orchestration.stack.update.start',
- 'orchestration.stack.update.end',
- 'orchestration.stack.suspend.start',
- 'orchestration.stack.suspend.end',
- 'orchestration.stack.resume.start',
- 'orchestration.stack.resume.end',
- 'orchestration.stack.delete.start',
- 'orchestration.stack.delete.end'
-]
-
-ASG_NOTIFICATIONS = [
- 'orchestration.autoscaling.start',
- 'orchestration.autoscaling.end'
-]
-
-
-def get_url(conf):
- conf = conf.oslo_messaging_rabbit
- return 'amqp://%s:%s@%s:%s/' % (conf.rabbit_userid,
- conf.rabbit_password,
- conf.rabbit_host,
- conf.rabbit_port)
-
-
-class NotificationHandler(object):
- def __init__(self, stack_id, events=None):
- self._notifications = []
- self.stack_id = stack_id
- self.events = events
-
- def process_message(self, body, message):
- notification = common.deserialize_msg(body)
- if notification['payload']['stack_name'] == self.stack_id:
- if self.events is not None:
- if notification['event_type'] in self.events:
- self.notifications.append(notification['event_type'])
- else:
- self.notifications.append(notification['event_type'])
- message.ack()
-
- def clear(self):
- self._notifications = []
-
- @property
- def notifications(self):
- return self._notifications
-
-
-class NotificationTest(functional_base.FunctionalTestsBase):
-
- basic_template = '''
-heat_template_version: 2013-05-23
-resources:
- random1:
- type: OS::Heat::RandomString
-'''
- update_basic_template = '''
-heat_template_version: 2013-05-23
-resources:
- random1:
- type: OS::Heat::RandomString
- random2:
- type: OS::Heat::RandomString
-'''
-
- asg_template = '''
-heat_template_version: 2013-05-23
-resources:
- asg:
- type: OS::Heat::AutoScalingGroup
- properties:
- resource:
- type: OS::Heat::RandomString
- min_size: 1
- desired_capacity: 2
- max_size: 3
-
- scale_up_policy:
- type: OS::Heat::ScalingPolicy
- properties:
- adjustment_type: change_in_capacity
- auto_scaling_group_id: {get_resource: asg}
- cooldown: 0
- scaling_adjustment: 1
-
- scale_down_policy:
- type: OS::Heat::ScalingPolicy
- properties:
- adjustment_type: change_in_capacity
- auto_scaling_group_id: {get_resource: asg}
- cooldown: 0
- scaling_adjustment: '-1'
-
-outputs:
- scale_up_url:
- value: {get_attr: [scale_up_policy, alarm_url]}
- scale_dn_url:
- value: {get_attr: [scale_down_policy, alarm_url]}
-'''
-
- def setUp(self):
- super(NotificationTest, self).setUp()
- self.exchange = kombu.Exchange('heat', 'topic', durable=False)
- queue = kombu.Queue(exchange=self.exchange,
- routing_key='notifications.info',
- exclusive=True)
- self.conn = kombu.Connection(get_url(
- transport.get_transport(cfg.CONF).conf))
- self.ch = self.conn.channel()
- self.queue = queue(self.ch)
- self.queue.declare()
-
- def consume_events(self, handler, count):
- self.conn.drain_events()
- return len(handler.notifications) == count
-
- def test_basic_notifications(self):
- # disable cleanup so we can call _stack_delete() directly.
- stack_identifier = self.stack_create(template=self.basic_template,
- enable_cleanup=False)
- self.update_stack(stack_identifier,
- template=self.update_basic_template)
- self.stack_suspend(stack_identifier)
- self.stack_resume(stack_identifier)
- self._stack_delete(stack_identifier)
-
- handler = NotificationHandler(stack_identifier.split('/')[0])
-
- with self.conn.Consumer(self.queue,
- callbacks=[handler.process_message],
- auto_declare=False):
- try:
- while True:
- self.conn.drain_events(timeout=1)
- except Exception:
- pass
-
- for n in BASIC_NOTIFICATIONS:
- self.assertIn(n, handler.notifications)
-
- def test_asg_notifications(self):
- stack_identifier = self.stack_create(template=self.asg_template)
-
- for output in self.client.stacks.get(stack_identifier).outputs:
- if output['output_key'] == 'scale_dn_url':
- scale_down_url = output['output_value']
- else:
- scale_up_url = output['output_value']
-
- notifications = []
- handler = NotificationHandler(stack_identifier.split('/')[0],
- ASG_NOTIFICATIONS)
-
- with self.conn.Consumer(self.queue,
- callbacks=[handler.process_message],
- auto_declare=False):
-
- requests.post(scale_up_url, verify=self.verify_cert)
- self.assertTrue(
- test.call_until_true(20, 0, self.consume_events, handler, 2))
- notifications += handler.notifications
-
- handler.clear()
- requests.post(scale_down_url, verify=self.verify_cert)
- self.assertTrue(
- test.call_until_true(20, 0, self.consume_events, handler, 2))
- notifications += handler.notifications
-
- self.assertEqual(2, notifications.count(ASG_NOTIFICATIONS[0]))
- self.assertEqual(2, notifications.count(ASG_NOTIFICATIONS[1]))
diff --git a/heat_tempest_plugin/tests/functional/test_nova_server_networks.py b/heat_tempest_plugin/tests/functional/test_nova_server_networks.py
index c0baeb4..32ed27a 100644
--- a/heat_tempest_plugin/tests/functional/test_nova_server_networks.py
+++ b/heat_tempest_plugin/tests/functional/test_nova_server_networks.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -90,6 +92,82 @@
- port: {get_resource: port}
'''
+server_with_multiple_subnets_no_ports_template = '''
+heat_template_version: 2016-04-08
+description: Test template to test nova server network updates.
+parameters:
+ flavor:
+ type: string
+ image:
+ type: string
+resources:
+ net:
+ type: OS::Neutron::Net
+ properties:
+ name: the_net
+ subnet_a:
+ type: OS::Neutron::Subnet
+ properties:
+ network: {get_resource: net}
+ cidr: 11.11.11.0/24
+ name: subnet_a
+ subnet_b:
+ type: OS::Neutron::Subnet
+ properties:
+ network: {get_resource: net}
+ cidr: 12.12.12.0/24
+ name: subnet_b
+ server:
+ type: OS::Nova::Server
+ properties:
+ image: {get_param: image}
+ flavor: {get_param: flavor}
+ networks: $NETWORKS
+outputs:
+ networks:
+ value: {get_attr: [server, networks]}
+'''
+
+server_with_no_nets_template = '''
+heat_template_version: 2016-04-08
+description: Test template to test nova server network updates.
+parameters:
+ flavor:
+ type: string
+ image:
+ type: string
+resources:
+ net:
+ type: OS::Neutron::Net
+ properties:
+ name: the_net
+ subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ name: the_subnet
+ network: {get_resource: net}
+ cidr: 11.11.11.0/24
+ server:
+ type: OS::Nova::Server
+ properties:
+ image: {get_param: image}
+ flavor: {get_param: flavor}
+ networks: $NETWORKS
+outputs:
+ port0_id:
+ value: {get_attr: [server, addresses, the_net, 0, port]}
+ port1_id:
+ value: {get_attr: [server, addresses, the_net, 1, port]}
+ port2_id:
+ value: {get_attr: [server, addresses, the_net, 2, port]}
+ port0_ip_addr:
+ value: {get_attr: [server, addresses, the_net, 0, addr]}
+ port1_ip_addr:
+ value: {get_attr: [server, addresses, the_net, 1, addr]}
+ port2_ip_addr:
+ value: {get_attr: [server, addresses, the_net, 2, addr]}
+'''
+
class CreateServerTest(functional_base.FunctionalTestsBase):
@@ -97,6 +175,7 @@
stack = self.client.stacks.get(stack_identifier)
return self._stack_output(stack, output_key)
+ @decorators.idempotent_id('58ccf0aa-7531-4eaa-8ed5-38663a4defaa')
def test_create_server_with_subnet_fixed_ip_sec_group(self):
parms = {'flavor': self.conf.minimal_instance_type,
'image': self.conf.minimal_image_ref}
@@ -114,30 +193,31 @@
server = self.compute_client.servers.get(server_id)
self.assertEqual([{"name": "the_sg"}], server.security_groups)
+ @decorators.idempotent_id('12185eaa-927f-43f3-a525-0424c4eb9b5d')
def test_create_update_server_with_subnet(self):
parms = {'flavor': self.conf.minimal_instance_type,
'image': self.conf.minimal_image_ref}
- template = server_with_sub_fixed_ip_template.replace(
- 'fixed_ip: 11.11.11.11',
- 'fixed_ip: 11.11.11.22').replace(
- 'name: my_net', 'name: your_net')
+ template = server_with_multiple_subnets_no_ports_template.replace(
+ '$NETWORKS', ('[{subnet: {get_resource: subnet_a}}]'))
stack_identifier = self.stack_create(
template=template,
stack_name='create_server_with_sub_ip',
parameters=parms)
networks = self.get_outputs(stack_identifier, 'networks')
- self.assertEqual(['11.11.11.22'], networks['your_net'])
+ self.assertIn('11.11.11', networks['the_net'][0])
- # update the server only with subnet, we won't pass
+ # update the server using a different subnet, we won't pass
# both port_id and net_id to attach interface, then update success
- template_only_subnet = template.replace(
- 'fixed_ip: 11.11.11.22', '')
+ template = server_with_multiple_subnets_no_ports_template.replace(
+ '$NETWORKS', ('[{subnet: {get_resource: subnet_b}}]'))
self.update_stack(stack_identifier,
- template_only_subnet,
+ template,
parameters=parms)
new_networks = self.get_outputs(stack_identifier, 'networks')
- self.assertNotEqual(['11.11.11.22'], new_networks['your_net'])
+ self.assertNotIn('11.11.11', new_networks['the_net'][0])
+ self.assertIn('12.12.12', new_networks['the_net'][0])
+ @decorators.idempotent_id('19479c15-6b25-4865-8889-658566608bd9')
def test_create_server_with_port(self):
parms = {'flavor': self.conf.minimal_instance_type,
'image': self.conf.minimal_image_ref}
@@ -147,3 +227,212 @@
template=server_with_port_template,
stack_name='server_with_port',
parameters=parms)
+
+
+class UpdateServerNetworksTest(functional_base.FunctionalTestsBase):
+ def setUp(self):
+ super(UpdateServerNetworksTest, self).setUp()
+ self.params = {'flavor': self.conf.minimal_instance_type,
+ 'image': self.conf.minimal_image_ref}
+
+ def get_outputs(self, stack_identifier, output_key):
+ stack = self.client.stacks.get(stack_identifier)
+ return self._stack_output(stack, output_key)
+
+ @decorators.idempotent_id('c1a22dbf-3160-41b7-8d3f-62ca33fc35a8')
+ def test_create_update_server_swap_network_subnet(self):
+ '''Test updating stack with:
+
+ old_snippet
+ networks:
+ - network: {get_resource: net}
+ new_snippet
+ networks:
+ - subnet: {get_resource: subnet}
+ '''
+ template = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{network: {get_resource: net}}]')
+ stack_identifier = self.stack_create(
+ template=template,
+ stack_name='swap_network_subnet',
+ parameters=self.params)
+ port0 = self.get_outputs(stack_identifier, 'port0_id')
+ template_update = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{subnet: {get_resource: subnet}}]')
+ self.update_stack(stack_identifier, template_update,
+ parameters=self.params)
+ self.assertEqual(port0, self.get_outputs(stack_identifier, 'port0_id'))
+
+ @decorators.idempotent_id('cccfe612-1ab7-401f-a4c5-63372826a780')
+ def test_create_update_server_swap_network_port(self):
+ '''Test updating stack with:
+
+ old_snippet
+ networks:
+ - network: {get_resource: net}
+ new_snippet
+ networks:
+ - port: <the_port_created_on_stack_create>
+ '''
+ template = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{network: {get_resource: net}}]')
+ stack_identifier = self.stack_create(
+ template=template,
+ stack_name='swap_network_port',
+ parameters=self.params)
+ port0 = self.get_outputs(stack_identifier, 'port0_id')
+ template_update = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{port: ' + port0 + '}]')
+ self.update_stack(stack_identifier, template_update,
+ parameters=self.params)
+ self.assertEqual(port0, self.get_outputs(stack_identifier, 'port0_id'))
+
+ @decorators.idempotent_id('3eeb0dff-5d2d-4178-a4e6-06e4c26ce23a')
+ def test_create_update_server_swap_subnet_network(self):
+ '''Test updating stack with:
+
+ old_snippet
+ networks:
+ - subnet: {get_resource: subnet}
+ new_snippet
+ networks:
+ - network: {get_resource: net}
+ '''
+ template = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{subnet: {get_resource: subnet}}]')
+ stack_identifier = self.stack_create(
+ template=template,
+ stack_name='swap_subnet_network',
+ parameters=self.params)
+ port0 = self.get_outputs(stack_identifier, 'port0_id')
+ template_update = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{network: {get_resource: net}}]')
+ self.update_stack(stack_identifier, template_update,
+ parameters=self.params)
+ self.assertEqual(port0, self.get_outputs(stack_identifier, 'port0_id'))
+
+ @decorators.idempotent_id('647fda5d-fc0c-4eb1-9ce3-c4c537461324')
+ def test_create_update_server_add_subnet(self):
+ '''Test updating stack with:
+
+ old_snippet
+ networks:
+ - network: {get_resource: net}
+ new_snippet
+ networks:
+ - network: {get_resource: net}
+ subnet: {get_resource: subnet}
+ '''
+ template = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{network: {get_resource: net}}]')
+ stack_identifier = self.stack_create(
+ template=template,
+ stack_name='add_subnet',
+ parameters=self.params)
+ port0 = self.get_outputs(stack_identifier, 'port0_id')
+ template_update = server_with_no_nets_template.replace(
+ '$NETWORKS',
+ '[{network: {get_resource: net}, subnet: {get_resource: subnet}}]')
+ self.update_stack(stack_identifier, template_update,
+ parameters=self.params)
+ self.assertEqual(port0, self.get_outputs(stack_identifier, 'port0_id'))
+
+ @decorators.idempotent_id('01c0f1cd-25b2-49b9-b4ac-fc4dd8937e42')
+ def test_create_update_server_add_same_fixed_ip(self):
+ '''Test updating stack with:
+
+ old_snippet
+ networks:
+ - network: {get_resource: net}
+ new_snippet
+ networks:
+ - network: {get_resource: net}
+ fixed_ip: <the_same_ip_already_allocated>
+ '''
+ template = server_with_no_nets_template.replace(
+ '$NETWORKS',
+ '[{network: {get_resource: net}}]')
+ stack_identifier = self.stack_create(
+ template=template,
+ stack_name='same_fixed_ip',
+ parameters=self.params)
+ port0 = self.get_outputs(stack_identifier, 'port0_id')
+ port0_ip = self.get_outputs(stack_identifier, 'port0_ip_addr')
+ template_update = server_with_no_nets_template.replace(
+ '$NETWORKS',
+ '[{network: {get_resource: net}, fixed_ip: ' + port0_ip + '}]')
+ self.update_stack(stack_identifier, template_update,
+ parameters=self.params)
+ self.assertEqual(port0, self.get_outputs(stack_identifier, 'port0_id'))
+
+ @decorators.idempotent_id('abc39cd6-7745-4314-ac04-85df532dd7c9')
+ def test_create_update_server_add_network(self):
+ '''Test updating stack with:
+
+ old_snippet
+ networks:
+ - subnet: {get_resource: subnet}
+ new_snippet
+ networks:
+ - network: {get_resource: net}
+ subnet: {get_resource: subnet}
+ '''
+ template = server_with_no_nets_template.replace(
+ '$NETWORKS', '[{subnet: {get_resource: subnet}}]')
+ stack_identifier = self.stack_create(
+ template=template,
+ stack_name='add_network',
+ parameters=self.params)
+ port0 = self.get_outputs(stack_identifier, 'port0_id')
+ template_update = server_with_no_nets_template.replace(
+ '$NETWORKS',
+ '[{network: {get_resource: net}, subnet: {get_resource: subnet}}]')
+ self.update_stack(stack_identifier, template_update,
+ parameters=self.params)
+ self.assertEqual(port0, self.get_outputs(stack_identifier, 'port0_id'))
+
+ @decorators.idempotent_id('3f729e7e-a698-4ee3-8a5e-0db84f16d1e1')
+ def test_create_update_server_multi_networks_swaps(self):
+ '''Test updating stack with:
+
+ old_snippet:
+ networks:
+ - network: {get_resource: net}
+ - network: {get_resource: net}
+ fixed_ip: 11.11.11.33
+ - subnet: {get_resource: subnet}
+ new_snippet:
+ networks:
+ - subnet: {get_resource: subnet}
+ - network: {get_resource: net}
+ - network: {get_resource: net}
+ subnet: {get_resource: subnet}
+ '''
+ old_snippet = """
+ - network: {get_resource: net}
+ - network: {get_resource: net}
+ fixed_ip: 11.11.11.33
+ - subnet: {get_resource: subnet}
+"""
+ new_snippet = """
+ - subnet: {get_resource: subnet}
+ - network: {get_resource: net}
+ - network: {get_resource: net}
+ subnet: {get_resource: subnet}
+"""
+ template = server_with_no_nets_template.replace(
+ '$NETWORKS', old_snippet)
+ stack_identifier = self.stack_create(
+ template=template,
+ stack_name='multi_networks_swaps',
+ parameters=self.params)
+ port0 = self.get_outputs(stack_identifier, 'port0_id')
+ port1 = self.get_outputs(stack_identifier, 'port1_id')
+ port2 = self.get_outputs(stack_identifier, 'port2_id')
+ template_update = server_with_no_nets_template.replace(
+ '$NETWORKS', new_snippet)
+ self.update_stack(stack_identifier, template_update,
+ parameters=self.params)
+ self.assertEqual(port0, self.get_outputs(stack_identifier, 'port0_id'))
+ self.assertEqual(port1, self.get_outputs(stack_identifier, 'port1_id'))
+ self.assertEqual(port2, self.get_outputs(stack_identifier, 'port2_id'))
diff --git a/heat_tempest_plugin/tests/functional/test_os_wait_condition.py b/heat_tempest_plugin/tests/functional/test_os_wait_condition.py
index e19be4f..dc78341 100644
--- a/heat_tempest_plugin/tests/functional/test_os_wait_condition.py
+++ b/heat_tempest_plugin/tests/functional/test_os_wait_condition.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -99,6 +101,7 @@
if not self.conf.minimal_instance_type:
raise self.skipException("No minimal flavor configured to test")
+ @decorators.idempotent_id('cc54ca6e-b91d-4ddd-80cc-24a886dfaaa0')
def test_create_stack_with_multi_signal_waitcondition(self):
params = {'flavor': self.conf.minimal_instance_type,
'image': self.conf.minimal_image_ref,
diff --git a/heat_tempest_plugin/tests/functional/test_preview.py b/heat_tempest_plugin/tests/functional/test_preview.py
index b1779dc..f1a6d72 100644
--- a/heat_tempest_plugin/tests/functional/test_preview.py
+++ b/heat_tempest_plugin/tests/functional/test_preview.py
@@ -10,9 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from heat_tempest_plugin.tests.functional import functional_base
from heatclient import exc
import six
+from tempest.lib import decorators
+
+from heat_tempest_plugin.tests.functional import functional_base
class StackPreviewTest(functional_base.FunctionalTestsBase):
@@ -88,6 +90,7 @@
self.assertEqual(stack_name, result['parameters']['OS::stack_name'])
self.assertEqual('abc', result['parameters']['incomming'])
+ @decorators.idempotent_id('4c2c062d-4723-42c0-a113-3307dd4ceb12')
def test_basic_pass(self):
stack_name = self._stack_rand_name()
result = self.client.stacks.preview(
@@ -114,6 +117,7 @@
self.assertEqual('abc', res['properties']['value'])
self.assertEqual([], res['required_by'])
+ @decorators.idempotent_id('54e69a54-762d-479e-a4f4-1fbf21af01c6')
def test_basic_fail(self):
stack_name = self._stack_rand_name()
@@ -131,6 +135,7 @@
': The Parameter (missing) was not provided.',
six.text_type(excp))
+ @decorators.idempotent_id('0449113c-ff90-4f2b-8825-27ea35c1983f')
def test_nested_pass(self):
"""Nested stacks need to recurse down the stacks."""
main_template = '''
@@ -187,6 +192,7 @@
self.assertEqual('abc', res['properties']['value'])
self.assertEqual([], res['required_by'])
+ @decorators.idempotent_id('6ca8ddfc-106f-4ecc-83f7-fca31d0c85ca')
def test_res_group_with_nested_template(self):
main_template = '''
heat_template_version: 2015-04-30
diff --git a/heat_tempest_plugin/tests/functional/test_preview_update.py b/heat_tempest_plugin/tests/functional/test_preview_update.py
deleted file mode 100644
index a4d293f..0000000
--- a/heat_tempest_plugin/tests/functional/test_preview_update.py
+++ /dev/null
@@ -1,298 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-test_template_one_resource = {
- 'heat_template_version': '2013-05-23',
- 'description': 'Test template to create one instance.',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0
- }
- }
- }
-}
-
-test_template_two_resource = {
- 'heat_template_version': '2013-05-23',
- 'description': 'Test template to create two instance.',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0
- }
- },
- 'test2': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0
- }
- }
- }
-}
-
-
-class UpdatePreviewBase(functional_base.FunctionalTestsBase):
-
- def assert_empty_sections(self, changes, empty_sections):
- for section in empty_sections:
- self.assertEqual([], changes[section])
-
-
-class UpdatePreviewStackTest(UpdatePreviewBase):
-
- def test_add_resource(self):
- self.stack_identifier = self.stack_create(
- template=test_template_one_resource)
- result = self.preview_update_stack(self.stack_identifier,
- test_template_two_resource)
- changes = result['resource_changes']
-
- unchanged = changes['unchanged'][0]['resource_name']
- self.assertEqual('test1', unchanged)
-
- added = changes['added'][0]['resource_name']
- self.assertEqual('test2', added)
-
- self.assert_empty_sections(changes, ['updated', 'replaced', 'deleted'])
-
- def test_no_change(self):
- self.stack_identifier = self.stack_create(
- template=test_template_one_resource)
- result = self.preview_update_stack(self.stack_identifier,
- test_template_one_resource)
- changes = result['resource_changes']
-
- unchanged = changes['unchanged'][0]['resource_name']
- self.assertEqual('test1', unchanged)
-
- self.assert_empty_sections(
- changes, ['updated', 'replaced', 'deleted', 'added'])
-
- def test_update_resource(self):
- self.stack_identifier = self.stack_create(
- template=test_template_one_resource)
- test_template_updated_resource = {
- 'heat_template_version': '2013-05-23',
- 'description': 'Test template to create one instance.',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1 foo',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0
- }
- }
- }
- }
-
- result = self.preview_update_stack(self.stack_identifier,
- test_template_updated_resource)
- changes = result['resource_changes']
-
- updated = changes['updated'][0]['resource_name']
- self.assertEqual('test1', updated)
-
- self.assert_empty_sections(
- changes, ['added', 'unchanged', 'replaced', 'deleted'])
-
- def test_replaced_resource(self):
- self.stack_identifier = self.stack_create(
- template=test_template_one_resource)
- new_template = {
- 'heat_template_version': '2013-05-23',
- 'description': 'Test template to create one instance.',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'update_replace': True,
- }
- }
- }
- }
-
- result = self.preview_update_stack(self.stack_identifier, new_template)
- changes = result['resource_changes']
-
- replaced = changes['replaced'][0]['resource_name']
- self.assertEqual('test1', replaced)
-
- self.assert_empty_sections(
- changes, ['added', 'unchanged', 'updated', 'deleted'])
-
- def test_delete_resource(self):
- self.stack_identifier = self.stack_create(
- template=test_template_two_resource)
- result = self.preview_update_stack(self.stack_identifier,
- test_template_one_resource)
- changes = result['resource_changes']
-
- unchanged = changes['unchanged'][0]['resource_name']
- self.assertEqual('test1', unchanged)
-
- deleted = changes['deleted'][0]['resource_name']
- self.assertEqual('test2', deleted)
-
- self.assert_empty_sections(changes, ['updated', 'replaced', 'added'])
-
-
-class UpdatePreviewStackTestNested(UpdatePreviewBase):
- template_nested_parent = '''
-heat_template_version: 2016-04-08
-resources:
- nested1:
- type: nested1.yaml
-'''
-
- template_nested1 = '''
-heat_template_version: 2016-04-08
-resources:
- nested2:
- type: nested2.yaml
-'''
-
- template_nested2 = '''
-heat_template_version: 2016-04-08
-resources:
- random:
- type: OS::Heat::RandomString
-'''
-
- template_nested2_2 = '''
-heat_template_version: 2016-04-08
-resources:
- random:
- type: OS::Heat::RandomString
- random2:
- type: OS::Heat::RandomString
-'''
-
- def _get_by_resource_name(self, changes, name, action):
- filtered_l = [x for x in changes[action]
- if x['resource_name'] == name]
- self.assertEqual(1, len(filtered_l))
- return filtered_l[0]
-
- def test_nested_resources_nochange(self):
- files = {'nested1.yaml': self.template_nested1,
- 'nested2.yaml': self.template_nested2}
- self.stack_identifier = self.stack_create(
- template=self.template_nested_parent, files=files)
- result = self.preview_update_stack(
- self.stack_identifier,
- template=self.template_nested_parent,
- files=files, show_nested=True)
- changes = result['resource_changes']
-
- # The nested random resource should be unchanged, but we always
- # update nested stacks even when there are no changes
- self.assertEqual(1, len(changes['unchanged']))
- self.assertEqual('random', changes['unchanged'][0]['resource_name'])
- self.assertEqual('nested2', changes['unchanged'][0]['parent_resource'])
-
- self.assertEqual(2, len(changes['updated']))
- u_nested1 = self._get_by_resource_name(changes, 'nested1', 'updated')
- self.assertNotIn('parent_resource', u_nested1)
- u_nested2 = self._get_by_resource_name(changes, 'nested2', 'updated')
- self.assertEqual('nested1', u_nested2['parent_resource'])
-
- self.assert_empty_sections(changes, ['replaced', 'deleted', 'added'])
-
- def test_nested_resources_add(self):
- files = {'nested1.yaml': self.template_nested1,
- 'nested2.yaml': self.template_nested2}
- self.stack_identifier = self.stack_create(
- template=self.template_nested_parent, files=files)
- files['nested2.yaml'] = self.template_nested2_2
- result = self.preview_update_stack(
- self.stack_identifier,
- template=self.template_nested_parent,
- files=files, show_nested=True)
- changes = result['resource_changes']
-
- # The nested random resource should be unchanged, but we always
- # update nested stacks even when there are no changes
- self.assertEqual(1, len(changes['unchanged']))
- self.assertEqual('random', changes['unchanged'][0]['resource_name'])
- self.assertEqual('nested2', changes['unchanged'][0]['parent_resource'])
-
- self.assertEqual(1, len(changes['added']))
- self.assertEqual('random2', changes['added'][0]['resource_name'])
- self.assertEqual('nested2', changes['added'][0]['parent_resource'])
-
- self.assert_empty_sections(changes, ['replaced', 'deleted'])
-
- def test_nested_resources_delete(self):
- files = {'nested1.yaml': self.template_nested1,
- 'nested2.yaml': self.template_nested2_2}
- self.stack_identifier = self.stack_create(
- template=self.template_nested_parent, files=files)
- files['nested2.yaml'] = self.template_nested2
- result = self.preview_update_stack(
- self.stack_identifier,
- template=self.template_nested_parent,
- files=files, show_nested=True)
- changes = result['resource_changes']
-
- # The nested random resource should be unchanged, but we always
- # update nested stacks even when there are no changes
- self.assertEqual(1, len(changes['unchanged']))
- self.assertEqual('random', changes['unchanged'][0]['resource_name'])
- self.assertEqual('nested2', changes['unchanged'][0]['parent_resource'])
-
- self.assertEqual(1, len(changes['deleted']))
- self.assertEqual('random2', changes['deleted'][0]['resource_name'])
- self.assertEqual('nested2', changes['deleted'][0]['parent_resource'])
-
- self.assert_empty_sections(changes, ['replaced', 'added'])
-
- def test_nested_resources_replace(self):
- files = {'nested1.yaml': self.template_nested1,
- 'nested2.yaml': self.template_nested2}
- self.stack_identifier = self.stack_create(
- template=self.template_nested_parent, files=files)
- parent_none = self.template_nested_parent.replace(
- 'nested1.yaml', 'OS::Heat::None')
- result = self.preview_update_stack(
- self.stack_identifier,
- template=parent_none,
- show_nested=True)
- changes = result['resource_changes']
-
- # The nested random resource should be unchanged, but we always
- # update nested stacks even when there are no changes
- self.assertEqual(1, len(changes['replaced']))
- self.assertEqual('nested1', changes['replaced'][0]['resource_name'])
-
- self.assertEqual(2, len(changes['deleted']))
- d_random = self._get_by_resource_name(changes, 'random', 'deleted')
- self.assertEqual('nested2', d_random['parent_resource'])
- d_nested2 = self._get_by_resource_name(changes, 'nested2', 'deleted')
- self.assertEqual('nested1', d_nested2['parent_resource'])
-
- self.assert_empty_sections(changes, ['updated', 'unchanged', 'added'])
diff --git a/heat_tempest_plugin/tests/functional/test_purge.py b/heat_tempest_plugin/tests/functional/test_purge.py
deleted file mode 100644
index 1dd4ff8..0000000
--- a/heat_tempest_plugin/tests/functional/test_purge.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import time
-
-from oslo_concurrency import processutils
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class PurgeTest(functional_base.FunctionalTestsBase):
- template = '''
-heat_template_version: 2014-10-16
-parameters:
-resources:
- test_resource:
- type: OS::Heat::TestResource
-'''
-
- def test_purge(self):
- stack_identifier = self.stack_create(template=self.template)
- self._stack_delete(stack_identifier)
- stacks = dict((stack.id, stack) for stack in
- self.client.stacks.list(show_deleted=True))
- self.assertIn(stack_identifier.split('/')[1], stacks)
- time.sleep(1)
- cmd = "heat-manage purge_deleted 0"
- processutils.execute(cmd, shell=True)
- stacks = dict((stack.id, stack) for stack in
- self.client.stacks.list(show_deleted=True))
- self.assertNotIn(stack_identifier.split('/')[1], stacks)
-
- # Test with tags
- stack_identifier = self.stack_create(template=self.template,
- tags="foo,bar")
- self._stack_delete(stack_identifier)
- time.sleep(1)
- cmd = "heat-manage purge_deleted 0"
- processutils.execute(cmd, shell=True)
- stacks = dict((stack.id, stack) for stack in
- self.client.stacks.list(show_deleted=True))
- self.assertNotIn(stack_identifier.split('/')[1], stacks)
diff --git a/heat_tempest_plugin/tests/functional/test_reload_on_sighup.py b/heat_tempest_plugin/tests/functional/test_reload_on_sighup.py
deleted file mode 100644
index 81da958..0000000
--- a/heat_tempest_plugin/tests/functional/test_reload_on_sighup.py
+++ /dev/null
@@ -1,142 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import re
-import subprocess
-import time
-
-import eventlet
-
-from oslo_concurrency import processutils
-from six.moves import configparser
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class ReloadOnSighupTest(functional_base.FunctionalTestsBase):
-
- def setUp(self):
- self.config_file = "/etc/heat/heat.conf"
- super(ReloadOnSighupTest, self).setUp()
-
- def _is_mod_wsgi_daemon(self, service):
- process = ''.join(['wsgi:',
- service[:9]]).replace('_', '-').encode('utf-8')
- s = subprocess.Popen(["ps", "ax"], stdout=subprocess.PIPE)
- for x in s.stdout:
- if re.search(process, x):
- return True
-
- def _set_config_value(self, service, key, value):
- config = configparser.ConfigParser()
-
- # NOTE(prazumovsky): If there are several workers, there can be
- # situation, when one thread opens self.config_file for writing
- # (so config_file erases with opening), in that moment other thread
- # intercepts to this file and try to set config option value, i.e.
- # write to file, which is already erased by first thread, so,
- # NoSectionError raised. So, should wait until first thread writes to
- # config_file.
- retries_count = self.conf.sighup_config_edit_retries
- while True:
- config.read(self.config_file)
- try:
- config.set(service, key, str(value))
- except configparser.NoSectionError:
- if retries_count <= 0:
- raise
- retries_count -= 1
- eventlet.sleep(1)
- else:
- break
-
- with open(self.config_file, 'w') as f:
- config.write(f)
-
- def _get_config_value(self, service, key):
- config = configparser.ConfigParser()
- config.read(self.config_file)
- val = config.get(service, key)
- return val
-
- def _get_heat_api_pids(self, service):
- # get the pids of all heat-api processes
- if service == "heat_api":
- process = "heat-api|grep -Ev 'grep|cloudwatch|cfn'"
- else:
- process = "%s|grep -Ev 'grep'" % service.replace('_', '-')
- cmd = "ps -ef|grep %s|awk '{print $2}'" % process
- out, err = processutils.execute(cmd, shell=True)
- self.assertIsNotNone(out, "heat-api service not running. %s" % err)
- pids = filter(None, out.split('\n'))
-
- # get the parent pids of all heat-api processes
- cmd = "ps -ef|grep %s|awk '{print $3}'" % process
- out, _ = processutils.execute(cmd, shell=True)
- parent_pids = filter(None, out.split('\n'))
-
- heat_api_parent = list(set(pids) & set(parent_pids))[0]
- heat_api_children = list(set(pids) - set(parent_pids))
-
- return heat_api_parent, heat_api_children
-
- def _change_config(self, service, old_workers, new_workers):
- pre_reload_parent, pre_reload_children = self._get_heat_api_pids(
- service)
- self.assertEqual(old_workers, len(pre_reload_children))
-
- # change the config values
- self._set_config_value(service, 'workers', new_workers)
- cmd = "kill -HUP %s" % pre_reload_parent
- processutils.execute(cmd, shell=True)
-
- # wait till heat-api reloads
- start_time = time.time()
- while time.time() - start_time < self.conf.sighup_timeout:
- post_reload_parent, post_reload_children = self._get_heat_api_pids(
- service)
- intersect = set(post_reload_children) & set(pre_reload_children)
- if (new_workers == len(post_reload_children)
- and pre_reload_parent == post_reload_parent
- and intersect == set()):
- break
- eventlet.sleep(1)
- self.assertEqual(pre_reload_parent, post_reload_parent)
- self.assertEqual(new_workers, len(post_reload_children))
- # test if all child processes are newly created
- self.assertEqual(set(post_reload_children) & set(pre_reload_children),
- set())
-
- def _reload(self, service):
- old_workers = int(self._get_config_value(service, 'workers'))
- new_workers = old_workers + 1
- self.addCleanup(self._set_config_value, service, 'workers',
- old_workers)
-
- self._change_config(service, old_workers, new_workers)
- # revert all the changes made
- self._change_config(service, new_workers, old_workers)
-
- def _reload_on_sighup(self, service):
- if not self._is_mod_wsgi_daemon(service):
- self._reload(service)
- else:
- self.skipTest('Skipping Test, Service running under httpd.')
-
- def test_api_reload_on_sighup(self):
- self._reload_on_sighup('heat_api')
-
- def test_api_cfn_reload_on_sighup(self):
- self._reload_on_sighup('heat_api_cfn')
-
- def test_api_cloudwatch_on_sighup(self):
- self._reload_on_sighup('heat_api_cloudwatch')
diff --git a/heat_tempest_plugin/tests/functional/test_remote_stack.py b/heat_tempest_plugin/tests/functional/test_remote_stack.py
index 4c53933..5e778e7 100644
--- a/heat_tempest_plugin/tests/functional/test_remote_stack.py
+++ b/heat_tempest_plugin/tests/functional/test_remote_stack.py
@@ -13,6 +13,7 @@
from heatclient import exc
import six
+from tempest.lib import decorators
from heat_tempest_plugin.tests.functional import functional_base
@@ -49,6 +50,7 @@
self.template = self.template.replace('RegionOne',
self.conf.region)
+ @decorators.idempotent_id('7e735c40-24fb-4ef8-8585-d1c68b344476')
def test_remote_stack_alone(self):
stack_id = self.stack_create(template=self.remote_template)
expected_resources = {'random1': 'OS::Heat::RandomString'}
@@ -57,6 +59,7 @@
output_value = self._stack_output(stack, 'remote_key')
self.assertEqual(32, len(output_value))
+ @decorators.idempotent_id('aeed20d1-ebda-4544-9ace-16b0c8fc24f6')
def test_stack_create(self):
files = {'remote_stack.yaml': self.remote_template}
stack_id = self.stack_create(files=files)
@@ -80,6 +83,7 @@
remote_resources = {'random1': 'OS::Heat::RandomString'}
self.assertEqual(remote_resources, self.list_resources(remote_id))
+ @decorators.idempotent_id('830bfeae-6d8a-4cb2-823d-d8b6c3a740ad')
def test_stack_create_bad_region(self):
tmpl_bad_region = self.template.replace(self.conf.region, 'DARKHOLE')
files = {'remote_stack.yaml': self.remote_template}
@@ -94,6 +98,7 @@
'orchestration service in DARKHOLE region not found"')
self.assertEqual(error_msg, six.text_type(ex))
+ @decorators.idempotent_id('b2190dfc-d223-4595-b168-6c42b0f3a3e5')
def test_stack_resource_validation_fail(self):
tmpl_bad_format = self.remote_template.replace('resources', 'resource')
files = {'remote_stack.yaml': tmpl_bad_format}
@@ -106,6 +111,7 @@
'invalid: resource"') % self.conf.region
self.assertEqual(error_msg, six.text_type(ex))
+ @decorators.idempotent_id('141f0478-121b-4e61-bde7-d5551bfd1fc2')
def test_stack_update(self):
files = {'remote_stack.yaml': self.remote_template}
stack_id = self.stack_create(files=files)
@@ -137,6 +143,7 @@
self.assertEqual(remote_resources,
self.list_resources(rstack.id))
+ @decorators.idempotent_id('50841af8-bdf5-4df6-a075-dc061ada6833')
def test_stack_suspend_resume(self):
files = {'remote_stack.yaml': self.remote_template}
stack_id = self.stack_create(files=files)
diff --git a/heat_tempest_plugin/tests/functional/test_replace_deprecated.py b/heat_tempest_plugin/tests/functional/test_replace_deprecated.py
deleted file mode 100644
index 0eee3f1..0000000
--- a/heat_tempest_plugin/tests/functional/test_replace_deprecated.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import yaml
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class ReplaceDeprecatedResourceTest(functional_base.FunctionalTestsBase):
- template = '''
-heat_template_version: "2013-05-23"
-parameters:
- flavor:
- type: string
- image:
- type: string
- network:
- type: string
-
-resources:
- config:
- type: OS::Heat::SoftwareConfig
- properties:
- config: xxxx
-
- server:
- type: OS::Nova::Server
- properties:
- image: {get_param: image}
- flavor: {get_param: flavor}
- networks: [{network: {get_param: network} }]
- user_data_format: SOFTWARE_CONFIG
- dep:
- type: OS::Heat::SoftwareDeployments
- properties:
- config: {get_resource: config}
- servers: {'0': {get_resource: server}}
- signal_transport: NO_SIGNAL
-outputs:
- server:
- value: {get_resource: server}
-'''
-
- deployment_group_snippet = '''
-type: OS::Heat::SoftwareDeploymentGroup
-properties:
- config: {get_resource: config}
- servers: {'0': {get_resource: server}}
- signal_transport: NO_SIGNAL
-'''
- enable_cleanup = True
-
- def test_replace_software_deployments(self):
- parms = {'flavor': self.conf.minimal_instance_type,
- 'network': self.conf.fixed_network_name,
- 'image': self.conf.minimal_image_ref
- }
- deployments_template = yaml.safe_load(self.template)
- stack_identifier = self.stack_create(
- parameters=parms,
- template=deployments_template,
- enable_cleanup=self.enable_cleanup)
- expected_resources = {'config': 'OS::Heat::SoftwareConfig',
- 'dep': 'OS::Heat::SoftwareDeployments',
- 'server': 'OS::Nova::Server'}
- resource = self.client.resources.get(stack_identifier, 'server')
- self.assertEqual(expected_resources,
- self.list_resources(stack_identifier))
- initial_phy_id = resource.physical_resource_id
- resources = deployments_template['resources']
- resources['dep'] = yaml.safe_load(self.deployment_group_snippet)
- self.update_stack(
- stack_identifier,
- deployments_template,
- parameters=parms)
- resource = self.client.resources.get(stack_identifier, 'server')
- self.assertEqual(initial_phy_id,
- resource.physical_resource_id)
- expected_new_resources = {'config': 'OS::Heat::SoftwareConfig',
- 'dep': 'OS::Heat::SoftwareDeploymentGroup',
- 'server': 'OS::Nova::Server'}
- self.assertEqual(expected_new_resources,
- self.list_resources(stack_identifier))
diff --git a/heat_tempest_plugin/tests/functional/test_resource_chain.py b/heat_tempest_plugin/tests/functional/test_resource_chain.py
deleted file mode 100644
index 06ec8ff..0000000
--- a/heat_tempest_plugin/tests/functional/test_resource_chain.py
+++ /dev/null
@@ -1,167 +0,0 @@
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-TEMPLATE_SIMPLE = '''
-heat_template_version: 2016-04-08
-parameters:
- string-length:
- type: number
-resources:
- my-chain:
- type: OS::Heat::ResourceChain
- properties:
- resources: ['OS::Heat::RandomString', 'OS::Heat::RandomString']
- resource_properties:
- length: { get_param: string-length }
-outputs:
- resource-ids:
- value: { get_attr: [my-chain, refs] }
- resource-0-value:
- value: { get_attr: [my-chain, resource.0, value] }
- all-resource-attrs:
- value: { get_attr: [my-chain, attributes, value] }
-'''
-
-TEMPLATE_PARAM_DRIVEN = '''
-heat_template_version: 2016-04-08
-parameters:
- chain-types:
- type: comma_delimited_list
-resources:
- my-chain:
- type: OS::Heat::ResourceChain
- properties:
- resources: { get_param: chain-types }
-'''
-
-
-class ResourceChainTests(functional_base.FunctionalTestsBase):
-
- def test_create(self):
- # Test
- params = {'string-length': 8}
- stack_id = self.stack_create(template=TEMPLATE_SIMPLE,
- parameters=params)
-
- # Verify
- stack = self.client.stacks.get(stack_id)
- self.assertIsNotNone(stack)
-
- # Top-level resource for chain
- expected = {'my-chain': 'OS::Heat::ResourceChain'}
- found = self.list_resources(stack_id)
- self.assertEqual(expected, found)
-
- # Nested stack exists and has two resources
- nested_id = self.group_nested_identifier(stack_id, 'my-chain')
- expected = {'0': 'OS::Heat::RandomString',
- '1': 'OS::Heat::RandomString'}
- found = self.list_resources(nested_id)
- self.assertEqual(expected, found)
-
- # Outputs
- resource_ids = self._stack_output(stack, 'resource-ids')
- self.assertIsNotNone(resource_ids)
- self.assertEqual(2, len(resource_ids))
-
- resource_value = self._stack_output(stack, 'resource-0-value')
- self.assertIsNotNone(resource_value)
- self.assertEqual(8, len(resource_value)) # from parameter
-
- resource_attrs = self._stack_output(stack, 'all-resource-attrs')
- self.assertIsNotNone(resource_attrs)
- self.assertIsInstance(resource_attrs, dict)
- self.assertEqual(2, len(resource_attrs))
- self.assertEqual(8, len(resource_attrs['0']))
- self.assertEqual(8, len(resource_attrs['1']))
-
- def test_update(self):
- # Setup
- params = {'string-length': 8}
- stack_id = self.stack_create(template=TEMPLATE_SIMPLE,
- parameters=params)
-
- update_tmpl = '''
- heat_template_version: 2016-04-08
- parameters:
- string-length:
- type: number
- resources:
- my-chain:
- type: OS::Heat::ResourceChain
- properties:
- resources: ['OS::Heat::None']
- '''
-
- # Test
- self.update_stack(stack_id, template=update_tmpl, parameters=params)
-
- # Verify
- # Nested stack only has the None resource
- nested_id = self.group_nested_identifier(stack_id, 'my-chain')
- expected = {'0': 'OS::Heat::None'}
- found = self.list_resources(nested_id)
- self.assertEqual(expected, found)
-
- def test_update_resources(self):
- params = {'chain-types': 'OS::Heat::None'}
-
- stack_id = self.stack_create(template=TEMPLATE_PARAM_DRIVEN,
- parameters=params)
-
- nested_id = self.group_nested_identifier(stack_id, 'my-chain')
- expected = {'0': 'OS::Heat::None'}
- found = self.list_resources(nested_id)
- self.assertEqual(expected, found)
-
- params = {'chain-types': 'OS::Heat::None,OS::Heat::None'}
- self.update_stack(stack_id, template=TEMPLATE_PARAM_DRIVEN,
- parameters=params)
-
- expected = {'0': 'OS::Heat::None', '1': 'OS::Heat::None'}
- found = self.list_resources(nested_id)
- self.assertEqual(expected, found)
-
- def test_resources_param_driven(self):
- # Setup
- params = {'chain-types':
- 'OS::Heat::None,OS::Heat::RandomString,OS::Heat::None'}
-
- # Test
- stack_id = self.stack_create(template=TEMPLATE_PARAM_DRIVEN,
- parameters=params)
-
- # Verify
- nested_id = self.group_nested_identifier(stack_id, 'my-chain')
- expected = {'0': 'OS::Heat::None',
- '1': 'OS::Heat::RandomString',
- '2': 'OS::Heat::None'}
- found = self.list_resources(nested_id)
- self.assertEqual(expected, found)
-
- def test_resources_env_defined(self):
- # Setup
- env = {'parameters': {'chain-types': 'OS::Heat::None'}}
-
- # Test
- stack_id = self.stack_create(template=TEMPLATE_PARAM_DRIVEN,
- environment=env)
-
- # Verify
- nested_id = self.group_nested_identifier(stack_id, 'my-chain')
- expected = {'0': 'OS::Heat::None'}
- found = self.list_resources(nested_id)
- self.assertEqual(expected, found)
diff --git a/heat_tempest_plugin/tests/functional/test_resource_group.py b/heat_tempest_plugin/tests/functional/test_resource_group.py
deleted file mode 100644
index aea1bda..0000000
--- a/heat_tempest_plugin/tests/functional/test_resource_group.py
+++ /dev/null
@@ -1,695 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-import json
-
-from heatclient import exc
-import six
-import yaml
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class ResourceGroupTest(functional_base.FunctionalTestsBase):
- template = '''
-heat_template_version: 2013-05-23
-resources:
- random_group:
- type: OS::Heat::ResourceGroup
- properties:
- count: 0
- resource_def:
- type: My::RandomString
- properties:
- length: 30
- salt: initial
-outputs:
- random1:
- value: {get_attr: [random_group, resource.0.value]}
- random2:
- value: {get_attr: [random_group, resource.1.value]}
- all_values:
- value: {get_attr: [random_group, value]}
-'''
-
- def test_resource_group_zero_novalidate(self):
- # Nested resources should be validated only when size > 0
- # This allows features to be disabled via size=0 without
- # triggering validation of nested resource custom constraints
- # e.g images etc in the nested schema.
- nested_template_fail = '''
-heat_template_version: 2013-05-23
-parameters:
- length:
- type: string
- default: 50
- salt:
- type: string
- default: initial
-resources:
- random:
- type: OS::Heat::RandomString
- properties:
- length: BAD
-'''
-
- files = {'provider.yaml': nested_template_fail}
- env = {'resource_registry':
- {'My::RandomString': 'provider.yaml'}}
- stack_identifier = self.stack_create(
- template=self.template,
- files=files,
- environment=env
- )
-
- self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
- self.list_resources(stack_identifier))
-
- # Check we created an empty nested stack
- nested_identifier = self.group_nested_identifier(stack_identifier,
- 'random_group')
- self.assertEqual({}, self.list_resources(nested_identifier))
-
- # Prove validation works for non-zero create/update
- template_two_nested = self.template.replace("count: 0", "count: 2")
- expected_err = ("resources.random_group<nested_stack>.resources."
- "0<provider.yaml>.resources.random: : "
- "Value 'BAD' is not an integer")
- ex = self.assertRaises(exc.HTTPBadRequest, self.update_stack,
- stack_identifier, template_two_nested,
- environment=env, files=files)
- self.assertIn(expected_err, six.text_type(ex))
-
- ex = self.assertRaises(exc.HTTPBadRequest, self.stack_create,
- template=template_two_nested,
- environment=env, files=files)
- self.assertIn(expected_err, six.text_type(ex))
-
- def _validate_resources(self, stack_identifier, expected_count):
- resources = self.list_group_resources(stack_identifier,
- 'random_group')
- self.assertEqual(expected_count, len(resources))
- expected_resources = dict(
- (str(idx), 'My::RandomString')
- for idx in range(expected_count))
-
- self.assertEqual(expected_resources, resources)
-
- def test_create(self):
- def validate_output(stack, output_key, length):
- output_value = self._stack_output(stack, output_key)
- self.assertEqual(length, len(output_value))
- return output_value
- # verify that the resources in resource group are identically
- # configured, resource names and outputs are appropriate.
- env = {'resource_registry':
- {'My::RandomString': 'OS::Heat::RandomString'}}
- create_template = self.template.replace("count: 0", "count: 2")
- stack_identifier = self.stack_create(template=create_template,
- environment=env)
- self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
- self.list_resources(stack_identifier))
-
- # validate count, type and name of resources in a resource group.
- self._validate_resources(stack_identifier, 2)
-
- # validate outputs
- stack = self.client.stacks.get(stack_identifier)
- outputs = []
- outputs.append(validate_output(stack, 'random1', 30))
- outputs.append(validate_output(stack, 'random2', 30))
- self.assertEqual(outputs, self._stack_output(stack, 'all_values'))
-
- def test_update_increase_decrease_count(self):
- # create stack with resource group count 2
- env = {'resource_registry':
- {'My::RandomString': 'OS::Heat::RandomString'}}
- create_template = self.template.replace("count: 0", "count: 2")
- stack_identifier = self.stack_create(template=create_template,
- environment=env)
- self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
- self.list_resources(stack_identifier))
- # verify that the resource group has 2 resources
- self._validate_resources(stack_identifier, 2)
-
- # increase the resource group count to 5
- update_template = self.template.replace("count: 0", "count: 5")
- self.update_stack(stack_identifier, update_template, environment=env)
- # verify that the resource group has 5 resources
- self._validate_resources(stack_identifier, 5)
-
- # decrease the resource group count to 3
- update_template = self.template.replace("count: 0", "count: 3")
- self.update_stack(stack_identifier, update_template, environment=env)
- # verify that the resource group has 3 resources
- self._validate_resources(stack_identifier, 3)
-
- def test_update_removal_policies(self):
- rp_template = '''
-heat_template_version: 2014-10-16
-resources:
- random_group:
- type: OS::Heat::ResourceGroup
- properties:
- count: 5
- removal_policies: []
- resource_def:
- type: OS::Heat::RandomString
-'''
-
- # create stack with resource group, initial count 5
- stack_identifier = self.stack_create(template=rp_template)
- self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
- self.list_resources(stack_identifier))
- group_resources = self.list_group_resources(stack_identifier,
- 'random_group')
- expected_resources = {u'0': u'OS::Heat::RandomString',
- u'1': u'OS::Heat::RandomString',
- u'2': u'OS::Heat::RandomString',
- u'3': u'OS::Heat::RandomString',
- u'4': u'OS::Heat::RandomString'}
- self.assertEqual(expected_resources, group_resources)
-
- # Remove three, specifying the middle resources to be removed
- update_template = rp_template.replace(
- 'removal_policies: []',
- 'removal_policies: [{resource_list: [\'1\', \'2\', \'3\']}]')
- self.update_stack(stack_identifier, update_template)
- group_resources = self.list_group_resources(stack_identifier,
- 'random_group')
- expected_resources = {u'0': u'OS::Heat::RandomString',
- u'4': u'OS::Heat::RandomString',
- u'5': u'OS::Heat::RandomString',
- u'6': u'OS::Heat::RandomString',
- u'7': u'OS::Heat::RandomString'}
- self.assertEqual(expected_resources, group_resources)
-
- def test_props_update(self):
- """Test update of resource_def properties behaves as expected."""
-
- env = {'resource_registry':
- {'My::RandomString': 'OS::Heat::RandomString'}}
- template_one = self.template.replace("count: 0", "count: 1")
- stack_identifier = self.stack_create(template=template_one,
- environment=env)
- self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
- self.list_resources(stack_identifier))
-
- initial_nested_ident = self.group_nested_identifier(stack_identifier,
- 'random_group')
- self.assertEqual({'0': 'My::RandomString'},
- self.list_resources(initial_nested_ident))
- # get the resource id
- res = self.client.resources.get(initial_nested_ident, '0')
- initial_res_id = res.physical_resource_id
-
- # change the salt (this should replace the RandomString but
- # not the nested stack or resource group.
- template_salt = template_one.replace("salt: initial", "salt: more")
- self.update_stack(stack_identifier, template_salt, environment=env)
- updated_nested_ident = self.group_nested_identifier(stack_identifier,
- 'random_group')
- self.assertEqual(initial_nested_ident, updated_nested_ident)
-
- # compare the resource id, we expect a change.
- res = self.client.resources.get(updated_nested_ident, '0')
- updated_res_id = res.physical_resource_id
- self.assertNotEqual(initial_res_id, updated_res_id)
-
- def test_update_nochange(self):
- """Test update with no properties change."""
-
- env = {'resource_registry':
- {'My::RandomString': 'OS::Heat::RandomString'}}
- template_one = self.template.replace("count: 0", "count: 2")
- stack_identifier = self.stack_create(template=template_one,
- environment=env)
- self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
- self.list_resources(stack_identifier))
-
- initial_nested_ident = self.group_nested_identifier(stack_identifier,
- 'random_group')
- self.assertEqual({'0': 'My::RandomString', '1': 'My::RandomString'},
- self.list_resources(initial_nested_ident))
- # get the output
- stack0 = self.client.stacks.get(stack_identifier)
- initial_rand = self._stack_output(stack0, 'random1')
-
- template_copy = copy.deepcopy(template_one)
- self.update_stack(stack_identifier, template_copy, environment=env)
- updated_nested_ident = self.group_nested_identifier(stack_identifier,
- 'random_group')
- self.assertEqual(initial_nested_ident, updated_nested_ident)
-
- # compare the random number, we expect no change.
- stack1 = self.client.stacks.get(stack_identifier)
- updated_rand = self._stack_output(stack1, 'random1')
- self.assertEqual(initial_rand, updated_rand)
-
- def test_update_nochange_resource_needs_update(self):
- """Test update when the resource definition has changed.
-
- Test the scenario when the ResourceGroup update happens without
- any changed properties, this can happen if the definition of
- a contained provider resource changes (files map changes), then
- the group and underlying nested stack should end up updated.
- """
-
- random_templ1 = '''
-heat_template_version: 2013-05-23
-parameters:
- length:
- type: string
- default: not-used
- salt:
- type: string
- default: not-used
-resources:
- random1:
- type: OS::Heat::RandomString
- properties:
- salt: initial
-outputs:
- value:
- value: {get_attr: [random1, value]}
-'''
- files1 = {'my_random.yaml': random_templ1}
-
- random_templ2 = random_templ1.replace('salt: initial',
- 'salt: more')
- files2 = {'my_random.yaml': random_templ2}
-
- env = {'resource_registry':
- {'My::RandomString': 'my_random.yaml'}}
-
- template_one = self.template.replace("count: 0", "count: 2")
- stack_identifier = self.stack_create(template=template_one,
- environment=env,
- files=files1)
- self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'},
- self.list_resources(stack_identifier))
- self.assertEqual(files1, self.client.stacks.files(stack_identifier))
-
- initial_nested_ident = self.group_nested_identifier(stack_identifier,
- 'random_group')
- self.assertEqual({'0': 'My::RandomString', '1': 'My::RandomString'},
- self.list_resources(initial_nested_ident))
- # get the output
- stack0 = self.client.stacks.get(stack_identifier)
- initial_rand = self._stack_output(stack0, 'random1')
-
- # change the environment so we use a different TemplateResource.
- # note "files2".
- self.update_stack(stack_identifier, template_one,
- environment=env, files=files2)
- updated_nested_ident = self.group_nested_identifier(stack_identifier,
- 'random_group')
- self.assertEqual(initial_nested_ident, updated_nested_ident)
- self.assertEqual(files2, self.client.stacks.files(stack_identifier))
-
- # compare the output, we expect a change.
- stack1 = self.client.stacks.get(stack_identifier)
- updated_rand = self._stack_output(stack1, 'random1')
- self.assertNotEqual(initial_rand, updated_rand)
-
-
-class ResourceGroupTestNullParams(functional_base.FunctionalTestsBase):
- template = '''
-heat_template_version: 2013-05-23
-parameters:
- param:
- type: empty
-resources:
- random_group:
- type: OS::Heat::ResourceGroup
- properties:
- count: 1
- resource_def:
- type: My::RandomString
- properties:
- param: {get_param: param}
-outputs:
- val:
- value: {get_attr: [random_group, val]}
-'''
-
- nested_template_file = '''
-heat_template_version: 2013-05-23
-parameters:
- param:
- type: empty
-outputs:
- val:
- value: {get_param: param}
-'''
-
- scenarios = [
- ('string_empty', dict(
- param='',
- p_type='string',
- )),
- ('boolean_false', dict(
- param=False,
- p_type='boolean',
- )),
- ('number_zero', dict(
- param=0,
- p_type='number',
- )),
- ('comma_delimited_list', dict(
- param=[],
- p_type='comma_delimited_list',
- )),
- ('json_empty', dict(
- param={},
- p_type='json',
- )),
- ]
-
- def test_create_pass_zero_parameter(self):
- templ = self.template.replace('type: empty',
- 'type: %s' % self.p_type)
- n_t_f = self.nested_template_file.replace('type: empty',
- 'type: %s' % self.p_type)
- files = {'provider.yaml': n_t_f}
- env = {'resource_registry':
- {'My::RandomString': 'provider.yaml'}}
- stack_identifier = self.stack_create(
- template=templ,
- files=files,
- environment=env,
- parameters={'param': self.param}
- )
- stack = self.client.stacks.get(stack_identifier)
- self.assertEqual(self.param, self._stack_output(stack, 'val')[0])
-
-
-class ResourceGroupAdoptTest(functional_base.FunctionalTestsBase):
- """Prove that we can do resource group adopt."""
-
- main_template = '''
-heat_template_version: "2013-05-23"
-resources:
- group1:
- type: OS::Heat::ResourceGroup
- properties:
- count: 2
- resource_def:
- type: OS::Heat::RandomString
-outputs:
- test0:
- value: {get_attr: [group1, resource.0.value]}
- test1:
- value: {get_attr: [group1, resource.1.value]}
-'''
-
- def _yaml_to_json(self, yaml_templ):
- return yaml.safe_load(yaml_templ)
-
- def test_adopt(self):
- data = {
- "resources": {
- "group1": {
- "status": "COMPLETE",
- "name": "group1",
- "resource_data": {},
- "metadata": {},
- "resource_id": "test-group1-id",
- "action": "CREATE",
- "type": "OS::Heat::ResourceGroup",
- "resources": {
- "0": {
- "status": "COMPLETE",
- "name": "0",
- "resource_data": {"value": "goopie"},
- "resource_id": "ID-0",
- "action": "CREATE",
- "type": "OS::Heat::RandomString",
- "metadata": {}
- },
- "1": {
- "status": "COMPLETE",
- "name": "1",
- "resource_data": {"value": "different"},
- "resource_id": "ID-1",
- "action": "CREATE",
- "type": "OS::Heat::RandomString",
- "metadata": {}
- }
- }
- }
- },
- "environment": {"parameters": {}},
- "template": yaml.safe_load(self.main_template)
- }
- stack_identifier = self.stack_adopt(
- adopt_data=json.dumps(data))
-
- self.assert_resource_is_a_stack(stack_identifier, 'group1')
- stack = self.client.stacks.get(stack_identifier)
- self.assertEqual('goopie', self._stack_output(stack, 'test0'))
- self.assertEqual('different', self._stack_output(stack, 'test1'))
-
-
-class ResourceGroupErrorResourceTest(functional_base.FunctionalTestsBase):
- template = '''
-heat_template_version: "2013-05-23"
-resources:
- group1:
- type: OS::Heat::ResourceGroup
- properties:
- count: 2
- resource_def:
- type: fail.yaml
-'''
- nested_templ = '''
-heat_template_version: "2013-05-23"
-resources:
- oops:
- type: OS::Heat::TestResource
- properties:
- fail: true
- wait_secs: 2
-'''
-
- def test_fail(self):
- stack_identifier = self.stack_create(
- template=self.template,
- files={'fail.yaml': self.nested_templ},
- expected_status='CREATE_FAILED',
- enable_cleanup=False)
- stack = self.client.stacks.get(stack_identifier)
-
- self.assertEqual('CREATE_FAILED', stack.stack_status)
- self.client.stacks.delete(stack_identifier)
- self._wait_for_stack_status(
- stack_identifier, 'DELETE_COMPLETE',
- success_on_not_found=True)
-
-
-class ResourceGroupUpdatePolicyTest(functional_base.FunctionalTestsBase):
-
- template = '''
-heat_template_version: '2015-04-30'
-resources:
- random_group:
- type: OS::Heat::ResourceGroup
- update_policy:
- rolling_update:
- min_in_service: 1
- max_batch_size: 2
- pause_time: 1
- properties:
- count: 10
- resource_def:
- type: OS::Heat::TestResource
- properties:
- value: initial
- update_replace: False
-'''
-
- def update_resource_group(self, update_template,
- updated, created, deleted):
- stack_identifier = self.stack_create(template=self.template)
- group_resources = self.list_group_resources(stack_identifier,
- 'random_group',
- minimal=False)
-
- init_names = [res.physical_resource_id for res in group_resources]
-
- self.update_stack(stack_identifier, update_template)
- group_resources = self.list_group_resources(stack_identifier,
- 'random_group',
- minimal=False)
-
- updt_names = [res.physical_resource_id for res in group_resources]
-
- matched_names = set(updt_names) & set(init_names)
-
- self.assertEqual(updated, len(matched_names))
-
- self.assertEqual(created, len(set(updt_names) - set(init_names)))
-
- self.assertEqual(deleted, len(set(init_names) - set(updt_names)))
-
- def test_resource_group_update(self):
- """Test rolling update with no conflict.
-
- Simple rolling update with no conflict in batch size
- and minimum instances in service.
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '1'
- policy['max_batch_size'] = '3'
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
-
- self.update_resource_group(updt_template,
- updated=10,
- created=0,
- deleted=0)
-
- def test_resource_group_update_replace(self):
- """Test rolling update(replace)with no conflict.
-
- Simple rolling update replace with no conflict in batch size
- and minimum instances in service.
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '1'
- policy['max_batch_size'] = '3'
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
- res_def['properties']['update_replace'] = True
-
- self.update_resource_group(updt_template,
- updated=0,
- created=10,
- deleted=10)
-
- def test_resource_group_update_scaledown(self):
- """Test rolling update with scaledown.
-
- Simple rolling update with reduced size.
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '1'
- policy['max_batch_size'] = '3'
- grp['properties']['count'] = 6
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
-
- self.update_resource_group(updt_template,
- updated=6,
- created=0,
- deleted=4)
-
- def test_resource_group_update_scaleup(self):
- """Test rolling update with scaleup.
-
- Simple rolling update with increased size.
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '1'
- policy['max_batch_size'] = '3'
- grp['properties']['count'] = 12
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
-
- self.update_resource_group(updt_template,
- updated=10,
- created=2,
- deleted=0)
-
- def test_resource_group_update_adjusted(self):
- """Test rolling update with enough available resources
-
- Update with capacity adjustment with enough resources.
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '8'
- policy['max_batch_size'] = '4'
- grp['properties']['count'] = 6
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
-
- self.update_resource_group(updt_template,
- updated=6,
- created=0,
- deleted=4)
-
- def test_resource_group_update_with_adjusted_capacity(self):
- """Test rolling update with capacity adjustment.
-
- Rolling update with capacity adjustment due to conflict in
- batch size and minimum instances in service.
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '8'
- policy['max_batch_size'] = '4'
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
-
- self.update_resource_group(updt_template,
- updated=10,
- created=0,
- deleted=0)
-
- def test_resource_group_update_huge_batch_size(self):
- """Test rolling update with huge batch size.
-
- Rolling Update with a huge batch size(more than
- current size).
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '0'
- policy['max_batch_size'] = '20'
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
- self.update_resource_group(updt_template,
- updated=10,
- created=0,
- deleted=0)
-
- def test_resource_group_update_huge_min_in_service(self):
- """Test rolling update with huge minimum capacity.
-
- Rolling Update with a huge number of minimum instances
- in service.
- """
- updt_template = yaml.safe_load(copy.deepcopy(self.template))
- grp = updt_template['resources']['random_group']
- policy = grp['update_policy']['rolling_update']
- policy['min_in_service'] = '20'
- policy['max_batch_size'] = '1'
- res_def = grp['properties']['resource_def']
- res_def['properties']['value'] = 'updated'
-
- self.update_resource_group(updt_template,
- updated=10,
- created=0,
- deleted=0)
diff --git a/heat_tempest_plugin/tests/functional/test_resources_list.py b/heat_tempest_plugin/tests/functional/test_resources_list.py
index 0a182ad..16c941f 100644
--- a/heat_tempest_plugin/tests/functional/test_resources_list.py
+++ b/heat_tempest_plugin/tests/functional/test_resources_list.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -35,6 +37,7 @@
class ResourcesList(functional_base.FunctionalTestsBase):
+ @decorators.idempotent_id('b65b5c82-68b0-42c9-82a0-c0e74e9ad906')
def test_filtering_with_depend(self):
stack_identifier = self.stack_create(template=test_template_depend)
[test2] = self.client.resources.list(stack_identifier,
@@ -42,6 +45,7 @@
self.assertEqual('CREATE_COMPLETE', test2.resource_status)
+ @decorators.idempotent_id('97a65d53-b449-4a43-8283-42d43b165756')
def test_required_by(self):
stack_identifier = self.stack_create(template=test_template_depend)
[test1] = self.client.resources.list(stack_identifier,
diff --git a/heat_tempest_plugin/tests/functional/test_simultaneous_update.py b/heat_tempest_plugin/tests/functional/test_simultaneous_update.py
deleted file mode 100644
index 3cfbeaa..0000000
--- a/heat_tempest_plugin/tests/functional/test_simultaneous_update.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-import copy
-import time
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-_test_template = {
- 'heat_template_version': 'pike',
- 'description': 'Test template to create two resources.',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0,
- }
- },
- 'test2': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'fail': False,
- 'update_replace': False,
- 'wait_secs': 0,
- 'action_wait_secs': {
- 'create': 30,
- }
- },
- 'depends_on': ['test1']
- }
- }
-}
-
-
-def get_templates(fail=False, delay_s=None):
- before = copy.deepcopy(_test_template)
-
- after = copy.deepcopy(before)
- for r in after['resources'].values():
- r['properties']['value'] = 'Test2'
-
- before_props = before['resources']['test2']['properties']
- before_props['fail'] = fail
- if delay_s is not None:
- before_props['action_wait_secs']['create'] = delay_s
-
- return before, after
-
-
-class SimultaneousUpdateStackTest(functional_base.FunctionalTestsBase):
-
- @test.requires_convergence
- def test_retrigger_success(self):
- before, after = get_templates()
- stack_id = self.stack_create(template=before,
- expected_status='CREATE_IN_PROGRESS')
- time.sleep(10)
-
- self.update_stack(stack_id, after)
-
- @test.requires_convergence
- def test_retrigger_failure(self):
- before, after = get_templates(fail=True)
- stack_id = self.stack_create(template=before,
- expected_status='CREATE_IN_PROGRESS')
- time.sleep(10)
-
- self.update_stack(stack_id, after)
-
- @test.requires_convergence
- def test_retrigger_timeout(self):
- before, after = get_templates(delay_s=70)
- stack_id = self.stack_create(template=before,
- expected_status='CREATE_IN_PROGRESS',
- timeout=1)
- time.sleep(50)
-
- self.update_stack(stack_id, after)
diff --git a/heat_tempest_plugin/tests/functional/test_snapshot_restore.py b/heat_tempest_plugin/tests/functional/test_snapshot_restore.py
deleted file mode 100644
index 3616c8f..0000000
--- a/heat_tempest_plugin/tests/functional/test_snapshot_restore.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class StackSnapshotRestoreTest(functional_base.FunctionalTestsBase):
-
- def setUp(self):
- super(StackSnapshotRestoreTest, self).setUp()
- if not self.conf.minimal_image_ref:
- raise self.skipException("No image configured to test")
-
- if not self.conf.minimal_instance_type:
- raise self.skipException(
- "No minimal_instance_type configured to test")
-
- self.assign_keypair()
-
- def test_stack_snapshot_restore(self):
- template = '''
-heat_template_version: ocata
-parameters:
- keyname:
- type: string
- flavor:
- type: string
- image:
- type: string
- network:
- type: string
-resources:
- my_port:
- type: OS::Neutron::Port
- properties:
- network: {get_param: network}
- my_server:
- type: OS::Nova::Server
- properties:
- image: {get_param: image}
- flavor: {get_param: flavor}
- key_name: {get_param: keyname}
- networks: [{port: {get_resource: my_port} }]
-
-'''
-
- def get_server_image(server_id):
- server = self.compute_client.servers.get(server_id)
- return server.image['id']
-
- parameters = {'keyname': self.keypair_name,
- 'flavor': self.conf.minimal_instance_type,
- 'image': self.conf.minimal_image_ref,
- 'network': self.conf.fixed_network_name}
- stack_identifier = self.stack_create(template=template,
- parameters=parameters)
- server_resource = self.client.resources.get(
- stack_identifier, 'my_server')
- server_id = server_resource.physical_resource_id
- prev_image_id = get_server_image(server_id)
-
- # Do snapshot and restore
- snapshot_id = self.stack_snapshot(stack_identifier)
- self.stack_restore(stack_identifier, snapshot_id)
-
- self.assertNotEqual(prev_image_id, get_server_image(server_id))
diff --git a/heat_tempest_plugin/tests/functional/test_software_config.py b/heat_tempest_plugin/tests/functional/test_software_config.py
index 2f31b29..f034096 100644
--- a/heat_tempest_plugin/tests/functional/test_software_config.py
+++ b/heat_tempest_plugin/tests/functional/test_software_config.py
@@ -20,6 +20,7 @@
import yaml
from oslo_utils import timeutils
+from tempest.lib import decorators
from heat_tempest_plugin.common import exceptions
from heat_tempest_plugin.common import test
@@ -69,6 +70,7 @@
enable_cleanup = True
+ @decorators.idempotent_id('8ee231ff-f80a-4a17-a860-5cda87e18ad0')
def test_deployments_metadata(self):
parms = {'flavor': self.conf.minimal_instance_type,
'network': self.conf.fixed_network_name,
@@ -102,6 +104,7 @@
for config_stack in config_stacks:
self._wait_for_stack_status(config_stack, 'CREATE_COMPLETE')
+ @decorators.idempotent_id('bd539232-b999-4bec-b47d-ff4822fc8b82')
def test_deployments_timeout_failed(self):
parms = {'flavor': self.conf.minimal_instance_type,
'network': self.conf.fixed_network_name,
@@ -226,6 +229,7 @@
queue_id = %(queue_id)s
'''
+ @decorators.idempotent_id('3af97ced-bead-4629-b78a-97762719e990')
def test_signal_queues(self):
parms = {'flavor': self.conf.minimal_instance_type,
'network': self.conf.fixed_network_name,
diff --git a/heat_tempest_plugin/tests/functional/test_software_deployment_group.py b/heat_tempest_plugin/tests/functional/test_software_deployment_group.py
deleted file mode 100644
index a298419..0000000
--- a/heat_tempest_plugin/tests/functional/test_software_deployment_group.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class SoftwareDeploymentGroupTest(functional_base.FunctionalTestsBase):
- sd_template = '''
-heat_template_version: 2016-10-14
-
-parameters:
- input:
- type: string
- default: foo_input
-
-resources:
- config:
- type: OS::Heat::SoftwareConfig
- properties:
- group: script
- inputs:
- - name: foo
-
- deployment:
- type: OS::Heat::SoftwareDeploymentGroup
- properties:
- config: {get_resource: config}
- input_values:
- foo: {get_param: input}
- servers:
- '0': dummy0
- '1': dummy1
- '2': dummy2
- '3': dummy3
-'''
-
- sd_template_with_upd_policy = '''
-heat_template_version: 2016-10-14
-
-parameters:
- input:
- type: string
- default: foo_input
-
-resources:
- config:
- type: OS::Heat::SoftwareConfig
- properties:
- group: script
- inputs:
- - name: foo
-
- deployment:
- type: OS::Heat::SoftwareDeploymentGroup
- update_policy:
- rolling_update:
- max_batch_size: 2
- pause_time: 1
- properties:
- config: {get_resource: config}
- input_values:
- foo: {get_param: input}
- servers:
- '0': dummy0
- '1': dummy1
- '2': dummy2
- '3': dummy3
-'''
- enable_cleanup = True
-
- def deployment_crud(self, template):
- stack_identifier = self.stack_create(
- template=template,
- enable_cleanup=self.enable_cleanup,
- expected_status='CREATE_IN_PROGRESS')
- self._wait_for_resource_status(
- stack_identifier, 'deployment', 'CREATE_IN_PROGRESS')
-
- # Wait for all deployment resources to become IN_PROGRESS, since only
- # IN_PROGRESS resources get signalled
- nested_identifier = self.assert_resource_is_a_stack(
- stack_identifier, 'deployment')
- self._wait_for_stack_status(nested_identifier, 'CREATE_IN_PROGRESS')
- self._wait_for_all_resource_status(nested_identifier,
- 'CREATE_IN_PROGRESS')
- group_resources = self.list_group_resources(
- stack_identifier, 'deployment', minimal=False)
-
- self.assertEqual(4, len(group_resources))
- self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE',
- signal_required=True,
- resources_to_signal=group_resources)
-
- created_group_resources = self.list_group_resources(
- stack_identifier, 'deployment', minimal=False)
- self.assertEqual(4, len(created_group_resources))
- self.check_input_values(created_group_resources, 'foo', 'foo_input')
-
- self.update_stack(stack_identifier,
- template=template,
- environment={'parameters': {'input': 'input2'}},
- expected_status='UPDATE_IN_PROGRESS')
- nested_identifier = self.assert_resource_is_a_stack(
- stack_identifier, 'deployment')
- self._wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE',
- signal_required=True,
- resources_to_signal=group_resources)
-
- self.check_input_values(created_group_resources, 'foo', 'input2')
-
- # We explicitly test delete here, vs just via cleanup and check
- # the nested stack is gone
- self._stack_delete(stack_identifier)
- self._wait_for_stack_status(
- nested_identifier, 'DELETE_COMPLETE',
- success_on_not_found=True)
-
- def test_deployment_crud(self):
- self.deployment_crud(self.sd_template)
-
- def test_deployment_crud_with_rolling_update(self):
- self.deployment_crud(self.sd_template_with_upd_policy)
-
- def test_deployments_create_delete_in_progress(self):
- stack_identifier = self.stack_create(
- template=self.sd_template,
- enable_cleanup=self.enable_cleanup,
- expected_status='CREATE_IN_PROGRESS')
- self._wait_for_resource_status(
- stack_identifier, 'deployment', 'CREATE_IN_PROGRESS')
- nested_identifier = self.assert_resource_is_a_stack(
- stack_identifier, 'deployment')
- group_resources = self.list_group_resources(
- stack_identifier, 'deployment', minimal=False)
-
- self.assertEqual(4, len(group_resources))
- # Now test delete while the stacks are still IN_PROGRESS
- self._stack_delete(stack_identifier)
- self._wait_for_stack_status(
- nested_identifier, 'DELETE_COMPLETE',
- success_on_not_found=True)
diff --git a/heat_tempest_plugin/tests/functional/test_stack_cancel.py b/heat_tempest_plugin/tests/functional/test_stack_cancel.py
deleted file mode 100644
index ba5e989..0000000
--- a/heat_tempest_plugin/tests/functional/test_stack_cancel.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-import eventlet
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-template = {
- 'heat_template_version': 'pike',
- 'resources': {
- 'test1': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- 'action_wait_secs': {
- 'update': 30,
- }
- }
- },
- 'test2': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': 'Test1',
- },
- 'depends_on': ['test1']
- },
- }
-}
-
-
-def get_templates(delay_s=None):
- before = copy.deepcopy(template)
- after = copy.deepcopy(before)
- for r in after['resources'].values():
- r['properties']['value'] = 'Test2'
-
- if delay_s:
- before_props = before['resources']['test1']['properties']
- before_props['action_wait_secs']['create'] = delay_s
- return before, after
-
-
-class StackCancelTest(functional_base.FunctionalTestsBase):
-
- def _test_cancel_update(self, rollback=True,
- expected_status='ROLLBACK_COMPLETE'):
- before, after = get_templates()
- stack_id = self.stack_create(template=before)
- self.update_stack(stack_id, template=after,
- expected_status='UPDATE_IN_PROGRESS')
- self._wait_for_resource_status(stack_id, 'test1', 'UPDATE_IN_PROGRESS')
- self.cancel_update_stack(stack_id, rollback, expected_status)
- return stack_id
-
- def test_cancel_update_with_rollback(self):
- self._test_cancel_update()
-
- def test_cancel_update_without_rollback(self):
- stack_id = self._test_cancel_update(rollback=False,
- expected_status='UPDATE_FAILED')
- self.assertTrue(test.call_until_true(
- 60, 2, self.verify_resource_status,
- stack_id, 'test1', 'UPDATE_COMPLETE'))
- eventlet.sleep(2)
- self.assertTrue(self.verify_resource_status(stack_id, 'test2',
- 'CREATE_COMPLETE'))
-
- def test_cancel_create_without_rollback(self):
- before, after = get_templates(delay_s=30)
- stack_id = self.stack_create(template=before,
- expected_status='CREATE_IN_PROGRESS')
- self._wait_for_resource_status(stack_id, 'test1', 'CREATE_IN_PROGRESS')
- self.cancel_update_stack(stack_id, rollback=False,
- expected_status='CREATE_FAILED')
- self.assertTrue(test.call_until_true(
- 60, 2, self.verify_resource_status,
- stack_id, 'test1', 'CREATE_COMPLETE'))
- eventlet.sleep(2)
- self.assertTrue(self.verify_resource_status(stack_id, 'test2',
- 'INIT_COMPLETE'))
diff --git a/heat_tempest_plugin/tests/functional/test_stack_events.py b/heat_tempest_plugin/tests/functional/test_stack_events.py
index 0db852e..185ca4f 100644
--- a/heat_tempest_plugin/tests/functional/test_stack_events.py
+++ b/heat_tempest_plugin/tests/functional/test_stack_events.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -42,6 +44,7 @@
self.assertIsNotNone(event.resource_status_reason)
self.assertIsNotNone(event.id)
+ @decorators.idempotent_id('620f4f7c-74f8-48a4-a8b0-d06d0337f133')
def test_event(self):
parameters = {}
diff --git a/heat_tempest_plugin/tests/functional/test_stack_outputs.py b/heat_tempest_plugin/tests/functional/test_stack_outputs.py
index f629a97..1b20b7b 100644
--- a/heat_tempest_plugin/tests/functional/test_stack_outputs.py
+++ b/heat_tempest_plugin/tests/functional/test_stack_outputs.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -35,6 +37,7 @@
value: { get_attr: [test_resource_b, output] }
'''
+ @decorators.idempotent_id('a886dd67-4506-4a37-82ae-43f0a7d83f35')
def test_outputs(self):
stack_identifier = self.stack_create(
template=self.template
@@ -89,6 +92,7 @@
value: {get_attr: [test_resource_b, output]}
'''
+ @decorators.idempotent_id('aea0e495-4c77-4033-8c43-3351e9cb7b48')
def test_outputs_update_new_resource(self):
stack_identifier = self.stack_create(template=self.before_template)
self.update_stack(stack_identifier, template=self.after_template)
@@ -133,6 +137,7 @@
value: { get_param: foo }
'''
+ @decorators.idempotent_id('993a403c-c6e2-475d-a65d-a82b8c9e0c22')
def test_output_error_nested(self):
stack_identifier = self.stack_create(
template=self.nested_template,
diff --git a/heat_tempest_plugin/tests/functional/test_stack_tags.py b/heat_tempest_plugin/tests/functional/test_stack_tags.py
index a270950..7d3d52a 100644
--- a/heat_tempest_plugin/tests/functional/test_stack_tags.py
+++ b/heat_tempest_plugin/tests/functional/test_stack_tags.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -31,6 +33,7 @@
value: {get_param: input}
'''
+ @decorators.idempotent_id('67332e51-b427-42d5-ad8d-fd2ec334f361')
def test_stack_tag(self):
# Stack create with stack tags
tags = 'foo,bar'
@@ -66,6 +69,7 @@
empty_tags_stack = self.client.stacks.get(stack_identifier)
self.assertIsNone(empty_tags_stack.tags)
+ @decorators.idempotent_id('5ed79584-0684-4f9c-ae8e-44a8f874ec79')
def test_hidden_stack(self):
# Stack create with hidden stack tag
tags = 'foo,hidden'
diff --git a/heat_tempest_plugin/tests/functional/test_swiftsignal_update.py b/heat_tempest_plugin/tests/functional/test_swiftsignal_update.py
deleted file mode 100644
index 604e592..0000000
--- a/heat_tempest_plugin/tests/functional/test_swiftsignal_update.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-test_template = '''
-heat_template_version: 2014-10-16
-
-resources:
- signal_handle:
- type: "OS::Heat::SwiftSignalHandle"
-
-outputs:
- signal_curl:
- value: { get_attr: ['signal_handle', 'curl_cli'] }
- description: Swift signal cURL
-
- signal_url:
- value: { get_attr: ['signal_handle', 'endpoint'] }
- description: Swift signal URL
-'''
-
-
-class SwiftSignalHandleUpdateTest(functional_base.FunctionalTestsBase):
-
- def test_stack_update_same_template_replace_no_url(self):
- if not self.is_service_available('object-store'):
- self.skipTest('object-store service not available, skipping')
- stack_identifier = self.stack_create(template=test_template)
- stack = self.client.stacks.get(stack_identifier)
- orig_url = self._stack_output(stack, 'signal_url')
- orig_curl = self._stack_output(stack, 'signal_curl')
- self.update_stack(stack_identifier, test_template)
- stack = self.client.stacks.get(stack_identifier)
- self.assertEqual(orig_url, self._stack_output(stack, 'signal_url'))
- self.assertEqual(orig_curl, self._stack_output(stack, 'signal_curl'))
diff --git a/heat_tempest_plugin/tests/functional/test_template_resource.py b/heat_tempest_plugin/tests/functional/test_template_resource.py
deleted file mode 100644
index a7cc808..0000000
--- a/heat_tempest_plugin/tests/functional/test_template_resource.py
+++ /dev/null
@@ -1,982 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import json
-
-from heatclient import exc as heat_exceptions
-import six
-import yaml
-
-from heat_tempest_plugin.common import test
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class TemplateResourceTest(functional_base.FunctionalTestsBase):
- """Prove that we can use the registry in a nested provider."""
-
- template = '''
-heat_template_version: 2013-05-23
-resources:
- secret1:
- type: OS::Heat::RandomString
-outputs:
- secret-out:
- value: { get_attr: [secret1, value] }
-'''
- nested_templ = '''
-heat_template_version: 2013-05-23
-resources:
- secret2:
- type: OS::Heat::RandomString
-outputs:
- value:
- value: { get_attr: [secret2, value] }
-'''
-
- env_templ = '''
-resource_registry:
- "OS::Heat::RandomString": nested.yaml
-'''
-
- def test_nested_env(self):
- main_templ = '''
-heat_template_version: 2013-05-23
-resources:
- secret1:
- type: My::NestedSecret
-outputs:
- secret-out:
- value: { get_attr: [secret1, value] }
-'''
-
- nested_templ = '''
-heat_template_version: 2013-05-23
-resources:
- secret2:
- type: My::Secret
-outputs:
- value:
- value: { get_attr: [secret2, value] }
-'''
-
- env_templ = '''
-resource_registry:
- "My::Secret": "OS::Heat::RandomString"
- "My::NestedSecret": nested.yaml
-'''
-
- stack_identifier = self.stack_create(
- template=main_templ,
- files={'nested.yaml': nested_templ},
- environment=env_templ)
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'secret1')
- # prove that resource.parent_resource is populated.
- sec2 = self.client.resources.get(nested_ident, 'secret2')
- self.assertEqual('secret1', sec2.parent_resource)
-
- def test_no_infinite_recursion(self):
- """Prove that we can override a python resource.
-
- And use that resource within the template resource.
- """
- stack_identifier = self.stack_create(
- template=self.template,
- files={'nested.yaml': self.nested_templ},
- environment=self.env_templ)
- self.assert_resource_is_a_stack(stack_identifier, 'secret1')
-
- def test_nested_stack_delete_then_delete_parent_stack(self):
- """Check the robustness of stack deletion.
-
- This tests that if you manually delete a nested
- stack, the parent stack is still deletable.
- """
- # disable cleanup so we can call _stack_delete() directly.
- stack_identifier = self.stack_create(
- template=self.template,
- files={'nested.yaml': self.nested_templ},
- environment=self.env_templ,
- enable_cleanup=False)
-
- nested_ident = self.assert_resource_is_a_stack(stack_identifier,
- 'secret1')
-
- self._stack_delete(nested_ident)
- self._stack_delete(stack_identifier)
-
- def test_change_in_file_path(self):
- stack_identifier = self.stack_create(
- template=self.template,
- files={'nested.yaml': self.nested_templ},
- environment=self.env_templ)
- stack = self.client.stacks.get(stack_identifier)
- secret_out1 = self._stack_output(stack, 'secret-out')
-
- nested_templ_2 = '''
-heat_template_version: 2013-05-23
-resources:
- secret2:
- type: OS::Heat::RandomString
-outputs:
- value:
- value: freddy
-'''
- env_templ_2 = '''
-resource_registry:
- "OS::Heat::RandomString": new/nested.yaml
-'''
- self.update_stack(stack_identifier,
- template=self.template,
- files={'new/nested.yaml': nested_templ_2},
- environment=env_templ_2)
- stack = self.client.stacks.get(stack_identifier)
- secret_out2 = self._stack_output(stack, 'secret-out')
- self.assertNotEqual(secret_out1, secret_out2)
- self.assertEqual('freddy', secret_out2)
-
-
-class NestedAttributesTest(functional_base.FunctionalTestsBase):
- """Prove that we can use the template resource references."""
-
- main_templ = '''
-heat_template_version: 2014-10-16
-resources:
- secret2:
- type: My::NestedSecret
-outputs:
- old_way:
- value: { get_attr: [secret2, nested_str]}
- test_attr1:
- value: { get_attr: [secret2, resource.secret1, value]}
- test_attr2:
- value: { get_attr: [secret2, resource.secret1.value]}
- test_ref:
- value: { get_resource: secret2 }
-'''
-
- env_templ = '''
-resource_registry:
- "My::NestedSecret": nested.yaml
-'''
-
- def test_stack_ref(self):
- nested_templ = '''
-heat_template_version: 2014-10-16
-resources:
- secret1:
- type: OS::Heat::RandomString
-outputs:
- nested_str:
- value: {get_attr: [secret1, value]}
-'''
- stack_identifier = self.stack_create(
- template=self.main_templ,
- files={'nested.yaml': nested_templ},
- environment=self.env_templ)
- self.assert_resource_is_a_stack(stack_identifier, 'secret2')
- stack = self.client.stacks.get(stack_identifier)
- test_ref = self._stack_output(stack, 'test_ref')
- self.assertIn('arn:openstack:heat:', test_ref)
-
- def test_transparent_ref(self):
- """Test using nested resource more transparently.
-
- With the addition of OS::stack_id we can now use the nested resource
- more transparently.
- """
-
- nested_templ = '''
-heat_template_version: 2014-10-16
-resources:
- secret1:
- type: OS::Heat::RandomString
-outputs:
- OS::stack_id:
- value: {get_resource: secret1}
- nested_str:
- value: {get_attr: [secret1, value]}
-'''
- stack_identifier = self.stack_create(
- template=self.main_templ,
- files={'nested.yaml': nested_templ},
- environment=self.env_templ)
- self.assert_resource_is_a_stack(stack_identifier, 'secret2')
- stack = self.client.stacks.get(stack_identifier)
- test_ref = self._stack_output(stack, 'test_ref')
- test_attr = self._stack_output(stack, 'old_way')
-
- self.assertNotIn('arn:openstack:heat', test_ref)
- self.assertEqual(test_attr, test_ref)
-
- def test_nested_attributes(self):
- nested_templ = '''
-heat_template_version: 2014-10-16
-resources:
- secret1:
- type: OS::Heat::RandomString
-outputs:
- nested_str:
- value: {get_attr: [secret1, value]}
-'''
- stack_identifier = self.stack_create(
- template=self.main_templ,
- files={'nested.yaml': nested_templ},
- environment=self.env_templ)
- self.assert_resource_is_a_stack(stack_identifier, 'secret2')
- stack = self.client.stacks.get(stack_identifier)
- old_way = self._stack_output(stack, 'old_way')
- test_attr1 = self._stack_output(stack, 'test_attr1')
- test_attr2 = self._stack_output(stack, 'test_attr2')
-
- self.assertEqual(old_way, test_attr1)
- self.assertEqual(old_way, test_attr2)
-
-
-class TemplateResourceFacadeTest(functional_base.FunctionalTestsBase):
- """Prove that we can use ResourceFacade in a HOT template."""
-
- main_template = '''
-heat_template_version: 2013-05-23
-resources:
- the_nested:
- type: the.yaml
- metadata:
- foo: bar
-outputs:
- value:
- value: {get_attr: [the_nested, output]}
-'''
-
- nested_templ = '''
-heat_template_version: 2013-05-23
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value: {"Fn::Select": [foo, {resource_facade: metadata}]}
-outputs:
- output:
- value: {get_attr: [test, output]}
- '''
-
- def test_metadata(self):
- stack_identifier = self.stack_create(
- template=self.main_template,
- files={'the.yaml': self.nested_templ})
- stack = self.client.stacks.get(stack_identifier)
- value = self._stack_output(stack, 'value')
- self.assertEqual('bar', value)
-
-
-class TemplateResourceUpdateTest(functional_base.FunctionalTestsBase):
- """Prove that we can do template resource updates."""
-
- main_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- the_nested:
- Type: the.yaml
- Properties:
- one: my_name
- two: your_name
-Outputs:
- identifier:
- Value: {Ref: the_nested}
- value:
- Value: {'Fn::GetAtt': [the_nested, the_str]}
-'''
-
- main_template_change_prop = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- the_nested:
- Type: the.yaml
- Properties:
- one: updated_name
- two: your_name
-
-Outputs:
- identifier:
- Value: {Ref: the_nested}
- value:
- Value: {'Fn::GetAtt': [the_nested, the_str]}
-'''
-
- main_template_add_prop = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- the_nested:
- Type: the.yaml
- Properties:
- one: my_name
- two: your_name
- three: third_name
-
-Outputs:
- identifier:
- Value: {Ref: the_nested}
- value:
- Value: {'Fn::GetAtt': [the_nested, the_str]}
-'''
-
- main_template_remove_prop = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- the_nested:
- Type: the.yaml
- Properties:
- one: my_name
-
-Outputs:
- identifier:
- Value: {Ref: the_nested}
- value:
- Value: {'Fn::GetAtt': [the_nested, the_str]}
-'''
-
- initial_tmpl = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: foo
- Type: String
- two:
- Default: bar
- Type: String
-
-Resources:
- NestedResource:
- Type: OS::Heat::RandomString
- Properties:
- salt: {Ref: one}
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [NestedResource, value]}
-'''
-
- prop_change_tmpl = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: yikes
- Type: String
- two:
- Default: foo
- Type: String
-Resources:
- NestedResource:
- Type: OS::Heat::RandomString
- Properties:
- salt: {Ref: two}
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [NestedResource, value]}
-'''
-
- prop_add_tmpl = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: yikes
- Type: String
- two:
- Default: foo
- Type: String
- three:
- Default: bar
- Type: String
-
-Resources:
- NestedResource:
- Type: OS::Heat::RandomString
- Properties:
- salt: {Ref: three}
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [NestedResource, value]}
-'''
-
- prop_remove_tmpl = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: yikes
- Type: String
-
-Resources:
- NestedResource:
- Type: OS::Heat::RandomString
- Properties:
- salt: {Ref: one}
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [NestedResource, value]}
-'''
-
- attr_change_tmpl = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: foo
- Type: String
- two:
- Default: bar
- Type: String
-
-Resources:
- NestedResource:
- Type: OS::Heat::RandomString
- Properties:
- salt: {Ref: one}
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [NestedResource, value]}
- something_else:
- Value: just_a_string
-'''
-
- content_change_tmpl = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: foo
- Type: String
- two:
- Default: bar
- Type: String
-
-Resources:
- NestedResource:
- Type: OS::Heat::RandomString
- Properties:
- salt: yum
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [NestedResource, value]}
-'''
-
- EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
- scenarios = [
- ('no_changes', dict(template=main_template,
- provider=initial_tmpl,
- expect=NOCHANGE)),
- ('main_tmpl_change', dict(template=main_template_change_prop,
- provider=initial_tmpl,
- expect=UPDATE)),
- ('provider_change', dict(template=main_template,
- provider=content_change_tmpl,
- expect=UPDATE)),
- ('provider_props_change', dict(template=main_template,
- provider=prop_change_tmpl,
- expect=UPDATE)),
- ('provider_props_add', dict(template=main_template_add_prop,
- provider=prop_add_tmpl,
- expect=UPDATE)),
- ('provider_props_remove', dict(template=main_template_remove_prop,
- provider=prop_remove_tmpl,
- expect=NOCHANGE)),
- ('provider_attr_change', dict(template=main_template,
- provider=attr_change_tmpl,
- expect=NOCHANGE)),
- ]
-
- def test_template_resource_update_template_schema(self):
- stack_identifier = self.stack_create(
- template=self.main_template,
- files={'the.yaml': self.initial_tmpl})
- stack = self.client.stacks.get(stack_identifier)
- initial_id = self._stack_output(stack, 'identifier')
- initial_val = self._stack_output(stack, 'value')
-
- self.update_stack(stack_identifier,
- self.template,
- files={'the.yaml': self.provider})
- stack = self.client.stacks.get(stack_identifier)
- self.assertEqual(initial_id,
- self._stack_output(stack, 'identifier'))
- if self.expect == self.NOCHANGE:
- self.assertEqual(initial_val,
- self._stack_output(stack, 'value'))
- else:
- self.assertNotEqual(initial_val,
- self._stack_output(stack, 'value'))
-
-
-class TemplateResourceUpdateFailedTest(functional_base.FunctionalTestsBase):
- """Prove that we can do updates on a nested stack to fix a stack."""
-
- main_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- keypair:
- Type: OS::Nova::KeyPair
- Properties:
- name: replace-this
- save_private_key: false
- server:
- Type: server_fail.yaml
- DependsOn: keypair
-'''
- nested_templ = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- RealRandom:
- Type: OS::Heat::RandomString
-'''
-
- def setUp(self):
- super(TemplateResourceUpdateFailedTest, self).setUp()
- self.assign_keypair()
-
- def test_update_on_failed_create(self):
- # create a stack with "server" dependent on "keypair", but
- # keypair fails, so "server" is not created properly.
- # We then fix the template and it should succeed.
- broken_templ = self.main_template.replace('replace-this',
- self.keypair_name)
- stack_identifier = self.stack_create(
- template=broken_templ,
- files={'server_fail.yaml': self.nested_templ},
- expected_status='CREATE_FAILED')
-
- fixed_templ = self.main_template.replace('replace-this',
- test.rand_name())
- self.update_stack(stack_identifier,
- fixed_templ,
- files={'server_fail.yaml': self.nested_templ})
-
-
-class TemplateResourceAdoptTest(functional_base.FunctionalTestsBase):
- """Prove that we can do template resource adopt/abandon."""
-
- main_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- the_nested:
- Type: the.yaml
- Properties:
- one: my_name
-Outputs:
- identifier:
- Value: {Ref: the_nested}
- value:
- Value: {'Fn::GetAtt': [the_nested, the_str]}
-'''
-
- nested_templ = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: foo
- Type: String
-Resources:
- RealRandom:
- Type: OS::Heat::RandomString
- Properties:
- salt: {Ref: one}
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [RealRandom, value]}
-'''
-
- def _yaml_to_json(self, yaml_templ):
- return yaml.safe_load(yaml_templ)
-
- def test_abandon(self):
- stack_identifier = self.stack_create(
- template=self.main_template,
- files={'the.yaml': self.nested_templ},
- enable_cleanup=False
- )
-
- info = self.stack_abandon(stack_id=stack_identifier)
- self.assertEqual(self._yaml_to_json(self.main_template),
- info['template'])
- self.assertEqual(self._yaml_to_json(self.nested_templ),
- info['resources']['the_nested']['template'])
- # TODO(james combs): Implement separate test cases for export
- # once export REST API is available. Also test reverse order
- # of invocation: export -> abandon AND abandon -> export
-
- def test_adopt(self):
- data = {
- 'resources': {
- 'the_nested': {
- "type": "the.yaml",
- "resources": {
- "RealRandom": {
- "type": "OS::Heat::RandomString",
- 'resource_data': {'value': 'goopie'},
- 'resource_id': 'froggy'
- }
- }
- }
- },
- "environment": {"parameters": {}},
- "template": yaml.safe_load(self.main_template)
- }
-
- stack_identifier = self.stack_adopt(
- adopt_data=json.dumps(data),
- files={'the.yaml': self.nested_templ})
-
- self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
- stack = self.client.stacks.get(stack_identifier)
- self.assertEqual('goopie', self._stack_output(stack, 'value'))
-
-
-class TemplateResourceCheckTest(functional_base.FunctionalTestsBase):
- """Prove that we can do template resource check."""
-
- main_template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- the_nested:
- Type: the.yaml
- Properties:
- one: my_name
-Outputs:
- identifier:
- Value: {Ref: the_nested}
- value:
- Value: {'Fn::GetAtt': [the_nested, the_str]}
-'''
-
- nested_templ = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Parameters:
- one:
- Default: foo
- Type: String
-Resources:
- RealRandom:
- Type: OS::Heat::RandomString
- Properties:
- salt: {Ref: one}
-Outputs:
- the_str:
- Value: {'Fn::GetAtt': [RealRandom, value]}
-'''
-
- def test_check(self):
- stack_identifier = self.stack_create(
- template=self.main_template,
- files={'the.yaml': self.nested_templ}
- )
-
- self.client.actions.check(stack_id=stack_identifier)
- self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')
-
-
-class TemplateResourceErrorMessageTest(functional_base.FunctionalTestsBase):
- """Prove that nested stack errors don't suck."""
-
- template = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- victim:
- Type: fail.yaml
-'''
- nested_templ = '''
-HeatTemplateFormatVersion: '2012-12-12'
-Resources:
- oops:
- Type: OS::Heat::TestResource
- Properties:
- fail: true
- wait_secs: 2
-'''
-
- def test_fail(self):
- stack_identifier = self.stack_create(
- template=self.template,
- files={'fail.yaml': self.nested_templ},
- expected_status='CREATE_FAILED')
- stack = self.client.stacks.get(stack_identifier)
-
- exp_path = 'resources.victim.resources.oops'
- exp_msg = 'Test Resource failed oops'
- exp = 'Resource CREATE failed: ValueError: %s: %s' % (exp_path,
- exp_msg)
- self.assertEqual(exp, stack.stack_status_reason)
-
-
-class TemplateResourceSuspendResumeTest(functional_base.FunctionalTestsBase):
- """Prove that we can do template resource suspend/resume."""
-
- main_template = '''
-heat_template_version: 2014-10-16
-parameters:
-resources:
- the_nested:
- type: the.yaml
-'''
-
- nested_templ = '''
-heat_template_version: 2014-10-16
-resources:
- test_random_string:
- type: OS::Heat::RandomString
-'''
-
- def test_suspend_resume(self):
- """Basic test for template resource suspend resume."""
- stack_identifier = self.stack_create(
- template=self.main_template,
- files={'the.yaml': self.nested_templ}
- )
-
- self.stack_suspend(stack_identifier=stack_identifier)
- self.stack_resume(stack_identifier=stack_identifier)
-
-
-class ValidateFacadeTest(functional_base.FunctionalTestsBase):
- """Prove that nested stack errors don't suck."""
-
- template = '''
-heat_template_version: 2015-10-15
-resources:
- thisone:
- type: OS::Thingy
- properties:
- one: pre
- two: post
-outputs:
- one:
- value: {get_attr: [thisone, here-it-is]}
-'''
- templ_facade = '''
-heat_template_version: 2015-04-30
-parameters:
- one:
- type: string
- two:
- type: string
-outputs:
- here-it-is:
- value: noop
-'''
- env = '''
-resource_registry:
- OS::Thingy: facade.yaml
- resources:
- thisone:
- OS::Thingy: concrete.yaml
-'''
-
- def setUp(self):
- super(ValidateFacadeTest, self).setUp()
- self.client = self.orchestration_client
-
- def test_missing_param(self):
- templ_missing_parameter = '''
-heat_template_version: 2015-04-30
-parameters:
- one:
- type: string
-resources:
- str:
- type: OS::Heat::RandomString
-outputs:
- here-it-is:
- value:
- not-important
-'''
- try:
- self.stack_create(
- template=self.template,
- environment=self.env,
- files={'facade.yaml': self.templ_facade,
- 'concrete.yaml': templ_missing_parameter},
- expected_status='CREATE_FAILED')
- except heat_exceptions.HTTPBadRequest as exc:
- exp = ('ERROR: Required property two for facade '
- 'OS::Thingy missing in provider')
- self.assertEqual(exp, six.text_type(exc))
-
- def test_missing_output(self):
- templ_missing_output = '''
-heat_template_version: 2015-04-30
-parameters:
- one:
- type: string
- two:
- type: string
-resources:
- str:
- type: OS::Heat::RandomString
-'''
- try:
- self.stack_create(
- template=self.template,
- environment=self.env,
- files={'facade.yaml': self.templ_facade,
- 'concrete.yaml': templ_missing_output},
- expected_status='CREATE_FAILED')
- except heat_exceptions.HTTPBadRequest as exc:
- exp = ('ERROR: Attribute here-it-is for facade '
- 'OS::Thingy missing in provider')
- self.assertEqual(exp, six.text_type(exc))
-
-
-class TemplateResourceNewParamTest(functional_base.FunctionalTestsBase):
-
- main_template = '''
-heat_template_version: 2013-05-23
-resources:
- my_resource:
- type: resource.yaml
- properties:
- value1: foo
-'''
- nested_templ = '''
-heat_template_version: 2013-05-23
-parameters:
- value1:
- type: string
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value: {get_param: value1}
-'''
- main_template_update = '''
-heat_template_version: 2013-05-23
-resources:
- my_resource:
- type: resource.yaml
- properties:
- value1: foo
- value2: foo
-'''
- nested_templ_update_fail = '''
-heat_template_version: 2013-05-23
-parameters:
- value1:
- type: string
- value2:
- type: string
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- fail: True
- value:
- str_replace:
- template: VAL1-VAL2
- params:
- VAL1: {get_param: value1}
- VAL2: {get_param: value2}
-'''
- nested_templ_update = '''
-heat_template_version: 2013-05-23
-parameters:
- value1:
- type: string
- value2:
- type: string
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value:
- str_replace:
- template: VAL1-VAL2
- params:
- VAL1: {get_param: value1}
- VAL2: {get_param: value2}
-'''
-
- def test_update(self):
- stack_identifier = self.stack_create(
- template=self.main_template,
- files={'resource.yaml': self.nested_templ})
-
- # Make the update fails with the new parameter inserted.
- self.update_stack(
- stack_identifier,
- self.main_template_update,
- files={'resource.yaml': self.nested_templ_update_fail},
- expected_status='UPDATE_FAILED')
-
- # Fix the update, it should succeed now.
- self.update_stack(
- stack_identifier,
- self.main_template_update,
- files={'resource.yaml': self.nested_templ_update})
-
-
-class TemplateResourceRemovedParamTest(functional_base.FunctionalTestsBase):
-
- main_template = '''
-heat_template_version: 2013-05-23
-parameters:
- value1:
- type: string
- default: foo
-resources:
- my_resource:
- type: resource.yaml
- properties:
- value1: {get_param: value1}
-'''
- nested_templ = '''
-heat_template_version: 2013-05-23
-parameters:
- value1:
- type: string
- default: foo
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value: {get_param: value1}
-'''
- main_template_update = '''
-heat_template_version: 2013-05-23
-resources:
- my_resource:
- type: resource.yaml
-'''
- nested_templ_update = '''
-heat_template_version: 2013-05-23
-parameters:
- value1:
- type: string
- default: foo
- value2:
- type: string
- default: bar
-resources:
- test:
- type: OS::Heat::TestResource
- properties:
- value:
- str_replace:
- template: VAL1-VAL2
- params:
- VAL1: {get_param: value1}
- VAL2: {get_param: value2}
-'''
-
- def test_update(self):
- stack_identifier = self.stack_create(
- template=self.main_template,
- environment={'parameters': {'value1': 'spam'}},
- files={'resource.yaml': self.nested_templ})
-
- self.update_stack(
- stack_identifier,
- self.main_template_update,
- environment={'parameter_defaults': {'value2': 'egg'}},
- files={'resource.yaml': self.nested_templ_update}, existing=True)
diff --git a/heat_tempest_plugin/tests/functional/test_template_validate.py b/heat_tempest_plugin/tests/functional/test_template_validate.py
index ed85443..e41f952 100644
--- a/heat_tempest_plugin/tests/functional/test_template_validate.py
+++ b/heat_tempest_plugin/tests/functional/test_template_validate.py
@@ -14,6 +14,7 @@
import six
from heatclient import exc
+from tempest.lib import decorators
from heat_tempest_plugin.tests.functional import functional_base
@@ -86,6 +87,7 @@
length: {get_param: aparam}
'''
+ @decorators.idempotent_id('b65a80c2-a507-4deb-9e7e-43181cc05211')
def test_template_validate_basic(self):
ret = self.client.stacks.validate(template=self.random_template)
expected = {'Description': 'the stack description',
@@ -102,6 +104,7 @@
'resource_registry': {u'resources': {}}}}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('bf27371d-e202-4bae-9f13-2ef137958517')
def test_template_validate_override_default(self):
env = {'parameters': {'aparam': 5}}
ret = self.client.stacks.validate(template=self.random_template,
@@ -121,6 +124,7 @@
'resource_registry': {u'resources': {}}}}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('0278e03d-ed50-4909-b29d-9c4267d3fcd6')
def test_template_validate_override_none(self):
env = {'resource_registry': {
'OS::Heat::RandomString': 'OS::Heat::None'}}
@@ -142,6 +146,7 @@
u'resources': {}}}}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('acb1435b-f1db-4427-9121-7e3144ddb81e')
def test_template_validate_basic_required_param(self):
tmpl = self.random_template.replace('default: 10', '')
ret = self.client.stacks.validate(template=tmpl)
@@ -158,6 +163,7 @@
'resource_registry': {u'resources': {}}}}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('7aac1feb-8256-4f70-8459-5e9780d28904')
def test_template_validate_fail_version(self):
fail_template = self.random_template.replace('2014-10-16', 'invalid')
ex = self.assertRaises(exc.HTTPBadRequest,
@@ -165,6 +171,7 @@
template=fail_template)
self.assertIn('The template version is invalid', six.text_type(ex))
+ @decorators.idempotent_id('6a6472d2-71fa-4ebe-a2b6-20878838555b')
def test_template_validate_parameter_groups(self):
ret = self.client.stacks.validate(template=self.random_template_groups)
expected = {'Description': 'the stack description',
@@ -198,6 +205,7 @@
'resource_registry': {u'resources': {}}}}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('5100cf18-f52a-47a2-880c-d540edad149f')
def test_template_validate_nested_off(self):
files = {'mynested.yaml': self.random_template}
ret = self.client.stacks.validate(template=self.parent_template,
@@ -218,6 +226,7 @@
u'resources': {}}}}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('480bcf64-25ae-49c7-b147-7cbc27d09cea')
def test_template_validate_nested_on(self):
files = {'mynested.yaml': self.random_template}
ret = self.client.stacks.validate(template=self.parent_template_noprop,
@@ -244,6 +253,7 @@
u'resources': {}}}}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('a0bb07f0-2e10-4226-a205-a7eb04df415f')
def test_template_validate_nested_on_multiple(self):
# parent_template -> nested_template -> random_template
nested_template = self.random_template.replace(
diff --git a/heat_tempest_plugin/tests/functional/test_templates.py b/heat_tempest_plugin/tests/functional/test_templates.py
index bfcf8bf..5cb7b66 100644
--- a/heat_tempest_plugin/tests/functional/test_templates.py
+++ b/heat_tempest_plugin/tests/functional/test_templates.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
from heat_tempest_plugin.tests.functional import functional_base
@@ -18,9 +19,8 @@
"""This will test the following template calls:
1. Get the template content for the specific stack
- 2. List template versions
- 3. List resource types
- 4. Show resource details for OS::Heat::TestResource
+ 2. List resource types
+ 3. Show resource details for OS::Heat::TestResource
"""
template = {
@@ -39,6 +39,7 @@
}
}
+ @decorators.idempotent_id('ac6ebc41-bd6a-4df4-80e5-f4b9ae3b5506')
def test_get_stack_template(self):
stack_identifier = self.stack_create(
template=self.template
@@ -46,24 +47,13 @@
template_from_client = self.client.stacks.template(stack_identifier)
self.assertEqual(self.template, template_from_client)
- def test_template_version(self):
- template_versions = self.client.template_versions.list()
- supported_template_versions = ["2013-05-23", "2014-10-16",
- "2015-04-30", "2015-10-15",
- "2012-12-12", "2010-09-09",
- "2016-04-08", "2016-10-14", "newton",
- "2017-02-24", "ocata",
- "2017-09-01", "pike",
- "2018-03-02", "queens"]
- for template in template_versions:
- self.assertIn(template.version.split(".")[1],
- supported_template_versions)
-
+ @decorators.idempotent_id('9f9a2fc0-f029-4d1f-a2eb-f019b9f75944')
def test_resource_types(self):
resource_types = self.client.resource_types.list()
self.assertTrue(any(resource.resource_type == "OS::Heat::TestResource"
for resource in resource_types))
+ @decorators.idempotent_id('fafbdcd0-eec3-4e6f-9c88-1e4835d085cf')
def test_show_resource_template(self):
resource_details = self.client.resource_types.get(
resource_type="OS::Heat::TestResource"
diff --git a/heat_tempest_plugin/tests/functional/test_translation.py b/heat_tempest_plugin/tests/functional/test_translation.py
deleted file mode 100644
index 484663b..0000000
--- a/heat_tempest_plugin/tests/functional/test_translation.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-template_subnet_old_network = """
-heat_template_version: 2016-10-14
-parameters:
- net_cidr:
- type: string
-resources:
- net:
- type: OS::Neutron::Net
- subnet:
- type: OS::Neutron::Subnet
- properties:
- cidr: { get_param: net_cidr }
- network_id: { get_resource: net }
-"""
-
-template_with_get_attr = """
-heat_template_version: 2016-10-14
-description: Test template to create/update subnet with translation
-parameters:
- net_cidr:
- type: string
-resources:
- net:
- type: OS::Neutron::Net
- net_value:
- type: OS::Heat::Value
- properties:
- value: { get_resource: net }
- subnet:
- type: OS::Neutron::Subnet
- properties:
- network: { get_attr: [net_value, value] }
- cidr: { get_param: net_cidr }
-"""
-
-template_value_from_nested_stack_main = """
-heat_template_version: 2016-10-14
-parameters:
- flavor:
- type: string
- image:
- type: string
- public_net:
- type: string
-resources:
- network_settings:
- type: network.yaml
- properties:
- public_net: { get_param: public_net }
- server:
- type: OS::Nova::Server
- properties:
- flavor: { get_param: flavor }
- image: { get_param: image }
- networks: { get_attr: [network_settings, networks] }
-"""
-
-template_value_from_nested_stack_network = """
-heat_template_version: 2016-10-14
-parameters:
- public_net:
- type: string
-outputs:
- networks:
- value:
- - uuid: { get_param: public_net }
-"""
-
-
-class TestTranslation(functional_base.FunctionalTestsBase):
-
- def test_create_update_subnet_old_network(self):
- # Just create and update where network is translated properly.
- env = {'parameters': {'net_cidr': '11.11.11.0/24'}}
- stack_identifier = self.stack_create(
- template=template_subnet_old_network,
- environment=env)
- env = {'parameters': {'net_cidr': '11.11.12.0/24'}}
- self.update_stack(stack_identifier,
- template=template_subnet_old_network,
- environment=env)
-
- def test_create_update_translation_with_get_attr(self):
- # Check create and update successful for translation function value.
- env = {'parameters': {'net_cidr': '11.11.11.0/24'}}
- stack_identifier = self.stack_create(
- template=template_with_get_attr,
- environment=env)
- env = {'parameters': {'net_cidr': '11.11.12.0/24'}}
- self.update_stack(stack_identifier,
- template=template_with_get_attr,
- environment=env)
-
- def test_value_from_nested_stack(self):
- env = {'parameters': {
- 'flavor': self.conf.minimal_instance_type,
- 'image': self.conf.minimal_image_ref,
- 'public_net': self.conf.fixed_network_name
- }}
- self.stack_create(
- template=template_value_from_nested_stack_main,
- environment=env,
- files={'network.yaml': template_value_from_nested_stack_network})
diff --git a/heat_tempest_plugin/tests/functional/test_unicode_template.py b/heat_tempest_plugin/tests/functional/test_unicode_template.py
index 9184455..02bad65 100644
--- a/heat_tempest_plugin/tests/functional/test_unicode_template.py
+++ b/heat_tempest_plugin/tests/functional/test_unicode_template.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import decorators
+
from heat_tempest_plugin.tests.functional import functional_base
@@ -61,6 +63,7 @@
self.assertEqual('OS::Heat::RandomString',
result['resource_type'])
+ @decorators.idempotent_id('871cc5dd-7550-494d-8c6d-9d549a8c0305')
def test_template_validate_basic(self):
ret = self.client.stacks.validate(template=self.random_template)
expected = {
@@ -82,6 +85,7 @@
}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('e84c76b8-a5e5-45e7-9c4b-4c160d1c385f')
def test_template_validate_override_default(self):
env = {'parameters': {u'\u53c2\u6570': 5}}
ret = self.client.stacks.validate(template=self.random_template,
@@ -106,6 +110,7 @@
}
self.assertEqual(expected, ret)
+ @decorators.idempotent_id('66338945-a3ae-4e3a-aa17-ab802ceb00b1')
def test_stack_preview(self):
result = self.client.stacks.preview(
template=self.random_template,
@@ -113,6 +118,7 @@
disable_rollback=True).to_dict()
self._assert_preview_results(result)
+ @decorators.idempotent_id('d2c4a10c-3cb4-4efd-889d-695a0acbd04f')
def test_create_stack(self):
stack_identifier = self.stack_create(template=self.random_template)
stack = self.client.stacks.get(stack_identifier)
diff --git a/heat_tempest_plugin/tests/functional/test_update_restricted.py b/heat_tempest_plugin/tests/functional/test_update_restricted.py
deleted file mode 100644
index 83cb280..0000000
--- a/heat_tempest_plugin/tests/functional/test_update_restricted.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import time
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-test_template = {
- 'heat_template_version': '2013-05-23',
- 'description': 'Test template to create one instance.',
- 'resources': {
- 'bar': {
- 'type': 'OS::Heat::TestResource',
- 'properties': {
- 'value': '1234',
- 'update_replace': False,
- }
- }
- }
-}
-
-env_both_restrict = {u'resource_registry': {
- u'resources': {
- 'bar': {'restricted_actions': ['update', 'replace']}
- }
-}
-}
-
-env_replace_restrict = {u'resource_registry': {
- u'resources': {
- '*ar': {'restricted_actions': 'replace'}
- }
-}
-}
-
-reason_update_restrict = 'update is restricted for resource.'
-reason_replace_restrict = 'replace is restricted for resource.'
-
-
-class UpdateRestrictedStackTest(functional_base.FunctionalTestsBase):
-
- def _check_for_restriction_reason(self, events,
- reason, num_expected=1):
- matched = [e for e in events
- if e.resource_status_reason == reason]
- return len(matched) == num_expected
-
- def test_update(self):
- stack_identifier = self.stack_create(template=test_template)
-
- update_template = test_template.copy()
- props = update_template['resources']['bar']['properties']
- props['value'] = '4567'
-
- # check update fails - with 'both' restricted
- self.update_stack(stack_identifier, update_template,
- env_both_restrict,
- expected_status='UPDATE_FAILED')
-
- self.assertTrue(self.verify_resource_status(stack_identifier, 'bar',
- 'CREATE_COMPLETE'))
- resource_events = self.client.events.list(stack_identifier, 'bar')
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_update_restrict))
-
- # Ensure the timestamp changes, since this will be very quick
- time.sleep(1)
-
- # check update succeeds - with only 'replace' restricted
- self.update_stack(stack_identifier, update_template,
- env_replace_restrict,
- expected_status='UPDATE_COMPLETE')
-
- self.assertTrue(self.verify_resource_status(stack_identifier, 'bar',
- 'UPDATE_COMPLETE'))
- resource_events = self.client.events.list(stack_identifier, 'bar')
- self.assertFalse(
- self._check_for_restriction_reason(resource_events,
- reason_update_restrict, 2))
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_replace_restrict, 0))
-
- def test_replace(self):
- stack_identifier = self.stack_create(template=test_template)
-
- update_template = test_template.copy()
- props = update_template['resources']['bar']['properties']
- props['update_replace'] = True
-
- # check replace fails - with 'both' restricted
- self.update_stack(stack_identifier, update_template,
- env_both_restrict,
- expected_status='UPDATE_FAILED')
-
- self.assertTrue(self.verify_resource_status(stack_identifier, 'bar',
- 'CREATE_COMPLETE'))
- resource_events = self.client.events.list(stack_identifier, 'bar')
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_replace_restrict))
-
- # Ensure the timestamp changes, since this will be very quick
- time.sleep(1)
-
- # check replace fails - with only 'replace' restricted
- self.update_stack(stack_identifier, update_template,
- env_replace_restrict,
- expected_status='UPDATE_FAILED')
-
- self.assertTrue(self.verify_resource_status(stack_identifier, 'bar',
- 'CREATE_COMPLETE'))
- resource_events = self.client.events.list(stack_identifier, 'bar')
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_replace_restrict, 2))
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_update_restrict, 0))
-
- def test_update_type_changed(self):
- stack_identifier = self.stack_create(template=test_template)
-
- update_template = test_template.copy()
- rsrc = update_template['resources']['bar']
- rsrc['type'] = 'OS::Heat::None'
-
- # check replace fails - with 'both' restricted
- self.update_stack(stack_identifier, update_template,
- env_both_restrict,
- expected_status='UPDATE_FAILED')
-
- self.assertTrue(self.verify_resource_status(stack_identifier, 'bar',
- 'CREATE_COMPLETE'))
- resource_events = self.client.events.list(stack_identifier, 'bar')
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_replace_restrict))
-
- # Ensure the timestamp changes, since this will be very quick
- time.sleep(1)
-
- # check replace fails - with only 'replace' restricted
- self.update_stack(stack_identifier, update_template,
- env_replace_restrict,
- expected_status='UPDATE_FAILED')
-
- self.assertTrue(self.verify_resource_status(stack_identifier, 'bar',
- 'CREATE_COMPLETE'))
- resource_events = self.client.events.list(stack_identifier, 'bar')
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_replace_restrict, 2))
- self.assertTrue(
- self._check_for_restriction_reason(resource_events,
- reason_update_restrict, 0))
diff --git a/heat_tempest_plugin/tests/functional/test_validation.py b/heat_tempest_plugin/tests/functional/test_validation.py
deleted file mode 100644
index ab1762e..0000000
--- a/heat_tempest_plugin/tests/functional/test_validation.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-from heat_tempest_plugin.tests.functional import functional_base
-
-
-class StackValidationTest(functional_base.FunctionalTestsBase):
-
- def setUp(self):
- super(StackValidationTest, self).setUp()
- if not self.conf.minimal_image_ref:
- raise self.skipException("No image configured to test")
-
- if not self.conf.minimal_instance_type:
- raise self.skipException(
- "No minimal_instance_type configured to test")
-
- self.assign_keypair()
-
- def test_stack_validate_provider_references_parent_resource(self):
- template = '''
-heat_template_version: 2014-10-16
-parameters:
- keyname:
- type: string
- flavor:
- type: string
- image:
- type: string
- network:
- type: string
-resources:
- config:
- type: My::Config
- properties:
- server: {get_resource: server}
-
- server:
- type: OS::Nova::Server
- properties:
- image: {get_param: image}
- flavor: {get_param: flavor}
- key_name: {get_param: keyname}
- networks: [{network: {get_param: network} }]
- user_data_format: SOFTWARE_CONFIG
-
-'''
- config_template = '''
-heat_template_version: 2014-10-16
-parameters:
- server:
- type: string
-resources:
- config:
- type: OS::Heat::SoftwareConfig
-
- deployment:
- type: OS::Heat::SoftwareDeployment
- properties:
- config:
- get_resource: config
- server:
- get_param: server
-'''
- files = {'provider.yaml': config_template}
- env = {'resource_registry':
- {'My::Config': 'provider.yaml'}}
- parameters = {'keyname': self.keypair_name,
- 'flavor': self.conf.minimal_instance_type,
- 'image': self.conf.minimal_image_ref,
- 'network': self.conf.fixed_network_name}
- # Note we don't wait for CREATE_COMPLETE, because we're using a
- # minimal image without the tools to apply the config.
- # The point of the test is just to prove that validation won't
- # falsely prevent stack creation starting, ref bug #1407100
- # Note that we can be sure My::Config will stay IN_PROGRESS as
- # there's no signal sent to the deployment
- self.stack_create(template=template,
- files=files,
- environment=env,
- parameters=parameters,
- expected_status='CREATE_IN_PROGRESS')
diff --git a/heat_tempest_plugin/tests/functional/test_waitcondition.py b/heat_tempest_plugin/tests/functional/test_waitcondition.py
index 84d8ef1..b50c0a2 100644
--- a/heat_tempest_plugin/tests/functional/test_waitcondition.py
+++ b/heat_tempest_plugin/tests/functional/test_waitcondition.py
@@ -13,6 +13,7 @@
import json
from keystoneclient.v3 import client as keystoneclient
+from tempest.lib import decorators
from zaqarclient.queues.v2 import client as zaqarclient
from heat_tempest_plugin.tests.functional import functional_base
@@ -38,6 +39,7 @@
value: {'Fn::Select': ['data_id', {get_attr: [wait_condition, data]}]}
'''
+ @decorators.idempotent_id('90183f0d-9929-43a6-8fb6-b81003824c6d')
def test_signal_queues(self):
stack_identifier = self.stack_create(
template=self.template,
diff --git a/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py b/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py
index 3108c1e..4e25158 100644
--- a/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py
+++ b/heat_tempest_plugin/tests/scenario/test_aodh_alarm.py
@@ -11,7 +11,9 @@
# under the License.
import datetime
+
from oslo_log import log as logging
+from tempest.lib import decorators
from heat_tempest_plugin.common import test
from heat_tempest_plugin.tests.scenario import scenario_base
@@ -35,6 +37,7 @@
actual))
return actual == expected
+ @decorators.idempotent_id('fc0f18a6-f65c-4df1-b9c5-e160dea59849')
def test_alarm(self):
"""Confirm we can create an alarm and trigger it."""
# create metric
diff --git a/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py b/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py
index e576867..b8ffa1f 100644
--- a/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py
+++ b/heat_tempest_plugin/tests/scenario/test_autoscaling_lb.py
@@ -14,6 +14,7 @@
import time
import requests
+from tempest.lib import decorators
from heat_tempest_plugin.common import test
from heat_tempest_plugin.tests.scenario import scenario_base
@@ -48,6 +49,7 @@
resp.add(r.text)
self.assertEqual(expected_num, len(resp))
+ @decorators.idempotent_id('48ddbc45-cef6-4640-acd6-7efc281833b9')
def test_autoscaling_loadbalancer_neutron(self):
"""Check work of AutoScaing and Neutron LBaaS v1 resource in Heat.
diff --git a/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py b/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py
index 65fa91c..52957f5 100644
--- a/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py
+++ b/heat_tempest_plugin/tests/scenario/test_autoscaling_lbv2.py
@@ -14,6 +14,7 @@
import time
import requests
+from tempest.lib import decorators
from heat_tempest_plugin.common import test
from heat_tempest_plugin.tests.scenario import scenario_base
@@ -50,6 +51,7 @@
break
self.assertEqual(expected_num, len(resp))
+ @decorators.idempotent_id('89459930-aa61-4557-989b-3429d3b3b612')
def test_autoscaling_loadbalancer_neutron(self):
"""Check work of AutoScaing and Neutron LBaaS v2 resource in Heat.
diff --git a/heat_tempest_plugin/tests/scenario/test_base_resources.py b/heat_tempest_plugin/tests/scenario/test_base_resources.py
index e63242e..ddd8564 100644
--- a/heat_tempest_plugin/tests/scenario/test_base_resources.py
+++ b/heat_tempest_plugin/tests/scenario/test_base_resources.py
@@ -10,9 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+from heatclient.common import template_utils
+from tempest.lib import decorators
+
from heat_tempest_plugin.common import test
from heat_tempest_plugin.tests.scenario import scenario_base
-from heatclient.common import template_utils
class BasicResourcesTest(scenario_base.ScenarioTestsBase):
@@ -38,6 +40,7 @@
server_networks = self._stack_output(stack, 'server_networks')
self.assertIn(self.private_net_name, server_networks)
+ @decorators.idempotent_id('bd151ea4-2dcd-4440-9bf0-eff63a98e5d4')
def test_base_resources_integration(self):
"""Define test for base resources interation from core porjects
diff --git a/heat_tempest_plugin/tests/scenario/test_server_cfn_init.py b/heat_tempest_plugin/tests/scenario/test_server_cfn_init.py
index 5ea0fb9..963d1ad 100644
--- a/heat_tempest_plugin/tests/scenario/test_server_cfn_init.py
+++ b/heat_tempest_plugin/tests/scenario/test_server_cfn_init.py
@@ -12,6 +12,8 @@
import json
+from tempest.lib import decorators
+
from heat_tempest_plugin.common import exceptions
from heat_tempest_plugin.tests.scenario import scenario_base
@@ -92,6 +94,7 @@
self._log_console_output(servers=[server])
raise
+ @decorators.idempotent_id('3f7726fc-a41b-40ca-ab38-51e2973f146a')
def test_server_cfn_init(self):
"""Check cfn-init and cfn-signal availability on the created server.
diff --git a/heat_tempest_plugin/tests/scenario/test_server_signal.py b/heat_tempest_plugin/tests/scenario/test_server_signal.py
index e2730dd..a43c74a 100644
--- a/heat_tempest_plugin/tests/scenario/test_server_signal.py
+++ b/heat_tempest_plugin/tests/scenario/test_server_signal.py
@@ -13,6 +13,8 @@
import json
+from tempest.lib import decorators
+
from heat_tempest_plugin.common import exceptions
from heat_tempest_plugin.tests.scenario import scenario_base
@@ -21,11 +23,11 @@
"""Test a server in a created network can signal to heat."""
def _test_server_signal(self, user_data_format='RAW',
- image=None):
+ image=None, flavor=None):
"""Check a server in a created network can signal to heat."""
parameters = {
'key_name': self.keypair_name,
- 'flavor': self.conf.minimal_instance_type,
+ 'flavor': flavor,
'image': image,
'timeout': self.conf.build_timeout,
'user_data_format': user_data_format
@@ -75,11 +77,15 @@
self.fail(
"Timed out waiting for %s to become reachable" % server_ip)
+ @decorators.idempotent_id('8da0f6cc-60e6-4298-9e54-e1f905c5552a')
def test_server_signal_userdata_format_raw(self):
- self._test_server_signal(image=self.conf.minimal_image_ref)
+ self._test_server_signal(image=self.conf.minimal_image_ref,
+ flavor=self.conf.minimal_instance_type)
+ @decorators.idempotent_id('3d753d42-7c16-4a0e-8f73-875881826626')
def test_server_signal_userdata_format_software_config(self):
if not self.conf.image_ref:
raise self.skipException("No image configured to test")
self._test_server_signal(user_data_format='SOFTWARE_CONFIG',
- image=self.conf.image_ref)
+ image=self.conf.image_ref,
+ flavor=self.conf.instance_type)
diff --git a/heat_tempest_plugin/tests/scenario/test_server_software_config.py b/heat_tempest_plugin/tests/scenario/test_server_software_config.py
index 1546684..4d2f7dc 100644
--- a/heat_tempest_plugin/tests/scenario/test_server_software_config.py
+++ b/heat_tempest_plugin/tests/scenario/test_server_software_config.py
@@ -12,6 +12,7 @@
from heatclient.common import template_utils
import six
+from tempest.lib import decorators
from heat_tempest_plugin.tests.scenario import scenario_base
@@ -132,6 +133,7 @@
dep1_dep.updated_time,
dep1_dep.creation_time)
+ @decorators.idempotent_id('fb2afe23-d1a8-45fc-bf8d-b18fc8412972')
def test_server_software_config(self):
"""Check that passed files with scripts are executed on created server.
diff --git a/heat_tempest_plugin/tests/scenario/test_volumes.py b/heat_tempest_plugin/tests/scenario/test_volumes.py
index eea6e97..57d0936 100644
--- a/heat_tempest_plugin/tests/scenario/test_volumes.py
+++ b/heat_tempest_plugin/tests/scenario/test_volumes.py
@@ -14,6 +14,7 @@
from cinderclient import exceptions as cinder_exceptions
from oslo_log import log as logging
import six
+from tempest.lib import decorators
from heat_tempest_plugin.common import exceptions
from heat_tempest_plugin.tests.scenario import scenario_base
@@ -98,6 +99,7 @@
self.volume_client.volumes.get,
volume_id2)
+ @decorators.idempotent_id('c3416735-87bf-4478-85c5-b3823819eb19')
def test_cinder_volume_create_backup_restore(self):
"""Ensure the 'Snapshot' deletion policy works.
diff --git a/releasenotes/notes/support-check-uuid-aa577427e4d32e4b.yaml b/releasenotes/notes/support-check-uuid-aa577427e4d32e4b.yaml
new file mode 100644
index 0000000..053c240
--- /dev/null
+++ b/releasenotes/notes/support-check-uuid-aa577427e4d32e4b.yaml
@@ -0,0 +1,7 @@
+---
+other:
+ - |
+ We can now run check-uuid to check if we give tempest id for all tempest
+ tests (not includes tests which run with gabbits) by running
+ ``tox -e pep8``. Also now you can run ``tox -e uuidgen`` for automatically
+ add ids to tests.
diff --git a/requirements.txt b/requirements.txt
index 0ca9082..d5dbc36 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,20 +2,21 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT
-keystoneauth1>=3.2.0 # Apache-2.0
-oslo.config>=4.6.0 # Apache-2.0
-oslo.log>=3.30.0 # Apache-2.0
+keystoneauth1>=3.4.0 # Apache-2.0
+oslo.config>=5.1.0 # Apache-2.0
+oslo.log>=3.36.0 # Apache-2.0
oslo.messaging>=5.29.0 # Apache-2.0
+os-collect-config>=5.0.0 # Apache-2.0
paramiko>=2.0.0 # LGPLv2.1+
-python-cinderclient>=3.2.0 # Apache-2.0
+python-cinderclient>=3.3.0 # Apache-2.0
gnocchiclient>=3.3.1 # Apache-2.0
python-heatclient>=1.10.0 # Apache-2.0
-python-neutronclient>=6.3.0 # Apache-2.0
+python-neutronclient>=6.7.0 # Apache-2.0
python-novaclient>=9.1.0 # Apache-2.0
python-swiftclient>=3.2.0 # Apache-2.0
python-zaqarclient>=1.0.0 # Apache-2.0
-testtools>=1.4.0 # MIT
+testtools>=2.2.0 # MIT
testscenarios>=0.4 # Apache-2.0/BSD
-tempest>=16.1.0 # Apache-2.0
+tempest>=17.1.0 # Apache-2.0
gabbi>=1.35.0 # Apache-2.0
kombu!=4.0.2,>=4.0.0 # BSD
diff --git a/test-requirements.txt b/test-requirements.txt
index f3e74e0..90882be 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -4,5 +4,5 @@
# Hacking already pins down pep8, pyflakes and flake8
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
-openstackdocstheme>=1.17.0 # Apache-2.0
-sphinx>=1.6.2 # BSD
+openstackdocstheme>=1.18.1 # Apache-2.0
+sphinx!=1.6.6,>=1.6.2 # BSD
diff --git a/tox.ini b/tox.ini
index ece89c6..7b8aeec 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,7 +12,17 @@
testr run {posargs}
[testenv:pep8]
-commands = flake8
+setenv =
+ PYTHONPATH = .
+commands =
+ flake8
+ check-uuid --package heat_tempest_plugin
+
+[testenv:uuidgen]
+setenv =
+ PYTHONPATH = .
+commands =
+ check-uuid --fix --package heat_tempest_plugin
[testenv:docs]
deps = -r{toxinidir}/requirements.txt