Merge "Validate TemplateResource schema when based on other templates"
diff --git a/common/config.py b/common/config.py
index 5a33590..b45a8d8 100644
--- a/common/config.py
+++ b/common/config.py
@@ -94,21 +94,26 @@
cfg.IntOpt('tenant_network_mask_bits',
default=28,
help="The mask bits for tenant ipv4 subnets"),
- cfg.BoolOpt('skip_software_config_tests',
- default=True,
- help="Skip software config deployment tests"),
+ cfg.BoolOpt('skip_scenario_tests',
+ default=False,
+ help="Skip all scenario tests"),
+ cfg.BoolOpt('skip_functional_tests',
+ default=False,
+ help="Skip all functional tests"),
+ cfg.ListOpt('skip_functional_test_list',
+ help="List of functional test class or class.method "
+ "names to skip ex. AutoscalingGroupTest,"
+ "InstanceGroupBasicTest.test_size_updates_work"),
+ cfg.ListOpt('skip_scenario_test_list',
+ help="List of scenario test class or class.method "
+ "names to skip ex. NeutronLoadBalancerTest, "
+ "CeilometerAlarmTest.test_alarm"),
+ cfg.ListOpt('skip_test_stack_action_list',
+ help="List of stack actions in tests to skip "
+ "ex. ABANDON, ADOPT, SUSPEND, RESUME"),
cfg.IntOpt('volume_size',
default=1,
help='Default size in GB for volumes created by volumes tests'),
- cfg.BoolOpt('skip_stack_adopt_tests',
- default=False,
- help="Skip Stack Adopt Integration tests"),
- cfg.BoolOpt('skip_stack_abandon_tests',
- default=False,
- help="Skip Stack Abandon Integration tests"),
- cfg.BoolOpt('skip_notification_tests',
- default=False,
- help="Skip Notification Integration tests"),
cfg.IntOpt('connectivity_timeout',
default=120,
help="Timeout in seconds to wait for connectivity to "
diff --git a/common/test.py b/common/test.py
index 584af6d..46911f0 100644
--- a/common/test.py
+++ b/common/test.py
@@ -250,9 +250,9 @@
def _verify_status(self, stack, stack_identifier, status, fail_regexp):
if stack.stack_status == status:
- # Handle UPDATE_COMPLETE case: Make sure we don't
- # wait for a stale UPDATE_COMPLETE status.
- if status == 'UPDATE_COMPLETE':
+ # Handle UPDATE_COMPLETE/FAILED case: Make sure we don't
+ # wait for a stale UPDATE_COMPLETE/FAILED status.
+ if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
if self.updated_time.get(
stack_identifier) != stack.updated_time:
self.updated_time[stack_identifier] = stack.updated_time
@@ -263,8 +263,8 @@
wait_for_action = status.split('_')[0]
if (stack.action == wait_for_action and
fail_regexp.search(stack.stack_status)):
- # Handle UPDATE_FAILED case.
- if status == 'UPDATE_FAILED':
+ # Handle UPDATE_COMPLETE/UPDATE_FAILED case.
+ if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
if self.updated_time.get(
stack_identifier) != stack.updated_time:
self.updated_time[stack_identifier] = stack.updated_time
@@ -279,7 +279,7 @@
stack_status_reason=stack.stack_status_reason)
def _wait_for_stack_status(self, stack_identifier, status,
- failure_pattern='^.*_FAILED$',
+ failure_pattern=None,
success_on_not_found=False):
"""
Waits for a Stack to reach a given status.
@@ -288,7 +288,13 @@
CREATE_COMPLETE, not just COMPLETE which is exposed
via the status property of Stack in heatclient
"""
- fail_regexp = re.compile(failure_pattern)
+ if failure_pattern:
+ fail_regexp = re.compile(failure_pattern)
+ elif 'FAILED' in status:
+ # If we're looking for e.g CREATE_FAILED, COMPLETE is unexpected.
+ fail_regexp = re.compile('^.*_COMPLETE$')
+ else:
+ fail_regexp = re.compile('^.*_FAILED$')
build_timeout = self.conf.build_timeout
build_interval = self.conf.build_interval
@@ -335,6 +341,8 @@
build_timeout = self.conf.build_timeout
build_interval = self.conf.build_interval
start = timeutils.utcnow()
+ self.updated_time[stack_identifier] = self.client.stacks.get(
+ stack_identifier).updated_time
while timeutils.delta_seconds(start,
timeutils.utcnow()) < build_timeout:
try:
@@ -426,7 +434,7 @@
tags=tags
)
if expected_status not in ['ROLLBACK_COMPLETE'] and enable_cleanup:
- self.addCleanup(self.client.stacks.delete, name)
+ self.addCleanup(self._stack_delete, name)
stack = self.client.stacks.get(name)
stack_identifier = '%s/%s' % (name, stack.id)
@@ -443,7 +451,8 @@
def stack_adopt(self, stack_name=None, files=None,
parameters=None, environment=None, adopt_data=None,
wait_for_status='ADOPT_COMPLETE'):
- if self.conf.skip_stack_adopt_tests:
+ if (self.conf.skip_test_stack_action_list and
+ 'ADOPT' in self.conf.skip_test_stack_action_list):
self.skipTest('Testing Stack adopt disabled in conf, skipping')
name = stack_name or self._stack_rand_name()
templ_files = files or {}
@@ -457,7 +466,7 @@
environment=env,
adopt_stack_data=adopt_data,
)
- self.addCleanup(self.client.stacks.delete, name)
+ self.addCleanup(self._stack_delete, name)
stack = self.client.stacks.get(name)
stack_identifier = '%s/%s' % (name, stack.id)
@@ -465,25 +474,32 @@
return stack_identifier
def stack_abandon(self, stack_id):
- if self.conf.skip_stack_abandon_tests:
- self.addCleanup(self.client.stacks.delete, stack_id)
+ if (self.conf.skip_test_stack_action_list and
+ 'ABANDON' in self.conf.skip_test_stack_action_list):
+ self.addCleanup(self._stack_delete, stack_id)
self.skipTest('Testing Stack abandon disabled in conf, skipping')
info = self.client.stacks.abandon(stack_id=stack_id)
return info
def stack_suspend(self, stack_identifier):
+ if (self.conf.skip_test_stack_action_list and
+ 'SUSPEND' in self.conf.skip_test_stack_action_list):
+ self.addCleanup(self._stack_delete, stack_identifier)
+ self.skipTest('Testing Stack suspend disabled in conf, skipping')
stack_name = stack_identifier.split('/')[0]
self.client.actions.suspend(stack_name)
-
# improve debugging by first checking the resource's state.
self._wait_for_all_resource_status(stack_identifier,
'SUSPEND_COMPLETE')
self._wait_for_stack_status(stack_identifier, 'SUSPEND_COMPLETE')
def stack_resume(self, stack_identifier):
+ if (self.conf.skip_test_stack_action_list and
+ 'RESUME' in self.conf.skip_test_stack_action_list):
+ self.addCleanup(self._stack_delete, stack_identifier)
+ self.skipTest('Testing Stack resume disabled in conf, skipping')
stack_name = stack_identifier.split('/')[0]
self.client.actions.resume(stack_name)
-
# improve debugging by first checking the resource's state.
self._wait_for_all_resource_status(stack_identifier,
'RESUME_COMPLETE')
diff --git a/common/test_resources/test_resource.py b/common/test_resources/test_resource.py
deleted file mode 100644
index 55255a6..0000000
--- a/common/test_resources/test_resource.py
+++ /dev/null
@@ -1,134 +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 eventlet
-
-from heat.common.i18n import _
-from heat.engine import attributes
-from heat.engine import properties
-from heat.engine import resource
-from heat.engine import support
-from oslo_log import log as logging
-
-LOG = logging.getLogger(__name__)
-
-
-class TestResource(resource.Resource):
- '''
- A resource which stores the string value that was provided.
-
- This resource is to be used only for testing.
- It has control knobs such as 'update_replace', 'fail', 'wait_secs'
-
- '''
-
- support_status = support.SupportStatus(version='2014.1')
-
- PROPERTIES = (
- VALUE, UPDATE_REPLACE, FAIL, WAIT_SECS
- ) = (
- 'value', 'update_replace', 'fail', 'wait_secs'
- )
-
- ATTRIBUTES = (
- OUTPUT,
- ) = (
- 'output',
- )
-
- properties_schema = {
- VALUE: properties.Schema(
- properties.Schema.STRING,
- _('The input string to be stored.'),
- default='test_string',
- update_allowed=True
- ),
- FAIL: properties.Schema(
- properties.Schema.BOOLEAN,
- _('Value which can be set to fail the resource operation '
- 'to test failure scenarios.'),
- update_allowed=True,
- default=False
- ),
- UPDATE_REPLACE: properties.Schema(
- properties.Schema.BOOLEAN,
- _('Value which can be set to trigger update replace for '
- 'the particular resource'),
- update_allowed=True,
- default=False
- ),
- WAIT_SECS: properties.Schema(
- properties.Schema.NUMBER,
- _('Value which can be set for resource to wait after an action '
- 'is performed.'),
- update_allowed=True,
- default=0,
- ),
- }
-
- attributes_schema = {
- OUTPUT: attributes.Schema(
- _('The string that was stored. This value is '
- 'also available by referencing the resource.'),
- cache_mode=attributes.Schema.CACHE_NONE
- ),
- }
-
- def handle_create(self):
- value = self.properties.get(self.VALUE)
- fail_prop = self.properties.get(self.FAIL)
- sleep_secs = self.properties.get(self.WAIT_SECS)
-
- self.data_set('value', value, redact=False)
- self.resource_id_set(self.physical_resource_name())
-
- # sleep for specified time
- if sleep_secs:
- LOG.debug("Resource %s sleeping for %s seconds",
- self.name, sleep_secs)
- eventlet.sleep(sleep_secs)
-
- # emulate failure
- if fail_prop:
- raise ValueError("Test Resource failed %s" % self.name)
-
- def handle_update(self, json_snippet=None, tmpl_diff=None, prop_diff=None):
- value = prop_diff.get(self.VALUE)
- new_prop = json_snippet._properties
- if value:
- update_replace = new_prop.get(self.UPDATE_REPLACE, False)
- if update_replace:
- raise resource.UpdateReplace(self.name)
- else:
- fail_prop = new_prop.get(self.FAIL, False)
- sleep_secs = new_prop.get(self.WAIT_SECS, 0)
- # emulate failure
- if fail_prop:
- raise Exception("Test Resource failed %s", self.name)
- # update in place
- self.data_set('value', value, redact=False)
-
- if sleep_secs:
- LOG.debug("Update of Resource %s sleeping for %s seconds",
- self.name, sleep_secs)
- eventlet.sleep(sleep_secs)
-
- def _resolve_attribute(self, name):
- if name == self.OUTPUT:
- return self.data().get('value')
-
-
-def resource_mapping():
- return {
- 'OS::Heat::TestResource': TestResource,
- }
diff --git a/functional/functional_base.py b/functional/functional_base.py
new file mode 100644
index 0000000..3dff3e4
--- /dev/null
+++ b/functional/functional_base.py
@@ -0,0 +1,31 @@
+# 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_integrationtests.common import test
+
+
+class FunctionalTestsBase(test.HeatIntegrationTest):
+
+ def setUp(self):
+ super(FunctionalTestsBase, self).setUp()
+ self.check_skip_test()
+ self.client = self.orchestration_client
+
+ def check_skip_test(self):
+ test_cls_name = self.__class__.__name__
+ test_method_name = '.'.join([test_cls_name, self._testMethodName])
+ test_skipped = (self.conf.skip_functional_test_list and (
+ test_cls_name in self.conf.skip_functional_test_list or
+ test_method_name in self.conf.skip_functional_test_list))
+
+ if self.conf.skip_functional_tests or test_skipped:
+ self.skipTest('Test disabled in conf, skipping')
diff --git a/functional/test_autoscaling.py b/functional/test_autoscaling.py
index 301981f..9041405 100644
--- a/functional/test_autoscaling.py
+++ b/functional/test_autoscaling.py
@@ -13,16 +13,19 @@
import copy
import json
+from heatclient import exc
from oslo_log import log as logging
+import six
from testtools import matchers
from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
LOG = logging.getLogger(__name__)
-class AutoscalingGroupTest(test.HeatIntegrationTest):
+class AutoscalingGroupTest(functional_base.FunctionalTestsBase):
template = '''
{
@@ -104,7 +107,7 @@
waiter:
type: AWS::CloudFormation::WaitCondition
properties:
- Handle: {Ref: ready_poster}
+ Handle: {get_resource: ready_poster}
Timeout: 1
outputs:
PublicIp:
@@ -113,7 +116,6 @@
def setUp(self):
super(AutoscalingGroupTest, self).setUp()
- self.client = self.orchestration_client
if not self.conf.image_ref:
raise self.skipException("No image configured to test")
if not self.conf.minimal_image_ref:
@@ -235,7 +237,7 @@
parameters={},
environment=env
)
- self.addCleanup(self.client.stacks.delete, stack_name)
+ 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')
@@ -728,8 +730,13 @@
self._wait_for_resource_status(
stack_identifier, 'JobServerGroup', 'SUSPEND_COMPLETE')
- # Send a signal and confirm nothing happened.
- self.client.resources.signal(stack_identifier, 'ScaleUpPolicy')
+ # Send a signal and a 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',
diff --git a/functional/test_aws_stack.py b/functional/test_aws_stack.py
index 172bc24..de8c6b8 100644
--- a/functional/test_aws_stack.py
+++ b/functional/test_aws_stack.py
@@ -20,12 +20,12 @@
import yaml
from heat_integrationtests.common import test
-
+from heat_integrationtests.functional import functional_base
LOG = logging.getLogger(__name__)
-class AwsStackTest(test.HeatIntegrationTest):
+class AwsStackTest(functional_base.FunctionalTestsBase):
test_template = '''
HeatTemplateFormatVersion: '2012-12-12'
Resources:
@@ -75,7 +75,6 @@
def setUp(self):
super(AwsStackTest, self).setUp()
- self.client = self.orchestration_client
self.object_container_name = AwsStackTest.__name__
self.project_id = self.identity_client.auth_ref.project_id
self.object_client.put_container(self.object_container_name)
diff --git a/functional/test_conditional_exposure.py b/functional/test_conditional_exposure.py
index c74b297..d037712 100644
--- a/functional/test_conditional_exposure.py
+++ b/functional/test_conditional_exposure.py
@@ -13,23 +13,10 @@
from heatclient import exc
import keystoneclient
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class ConditionalExposureTestBase(test.HeatIntegrationTest):
- def setUp(self):
- super(ConditionalExposureTestBase, self).setUp()
- self.client = self.orchestration_client
-
- def _delete(self, stack_name):
- stacks = self.client.stacks.list()
- for s in stacks:
- if s.stack_name == stack_name:
- self._stack_delete(s.identifier)
- break
-
-
-class ServiceBasedExposureTest(ConditionalExposureTestBase):
+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
@@ -73,10 +60,42 @@
def test_unavailable_resources_not_created(self):
stack_name = self._stack_rand_name()
- self.addCleanup(self._delete, stack_name)
ex = self.assertRaises(exc.HTTPBadRequest,
self.client.stacks.create,
stack_name=stack_name,
template=self.unavailable_template)
self.assertIn('ResourceTypeUnavailable', ex.message)
self.assertIn('OS::Sahara::NodeGroupTemplate', ex.message)
+
+
+class RoleBasedExposureTest(functional_base.FunctionalTestsBase):
+ forbidden_resource_type = "OS::Nova::Flavor"
+ fl_tmpl = """
+heat_template_version: 2015-10-15
+
+resources:
+ not4everyone:
+ type: OS::Nova::Flavor
+ properties:
+ ram: 20000
+ vcpus: 10
+"""
+
+ def test_non_admin_forbidden_create_flavors(self):
+ """Fail to create Flavor resource w/o admin role
+
+ Integration tests job runs as normal OpenStack user,
+ and OS::Nova:Flavor is configured to require
+ admin role in default policy file of Heat.
+ """
+ stack_name = self._stack_rand_name()
+ ex = self.assertRaises(exc.Forbidden,
+ self.client.stacks.create,
+ stack_name=stack_name,
+ template=self.fl_tmpl)
+ self.assertIn(self.forbidden_resource_type, ex.message)
+
+ def test_forbidden_resource_not_listed(self):
+ resources = self.client.resource_types.list()
+ self.assertNotIn(self.forbidden_resource_type,
+ (r.resource_type for r in resources))
diff --git a/functional/test_create_update.py b/functional/test_create_update.py
index 6053303..1b273dd 100644
--- a/functional/test_create_update.py
+++ b/functional/test_create_update.py
@@ -12,9 +12,10 @@
import copy
-from heat_integrationtests.common import test
import json
+from heat_integrationtests.functional import functional_base
+
test_template_one_resource = {
'heat_template_version': '2013-05-23',
'description': 'Test template to create one instance.',
@@ -25,7 +26,10 @@
'value': 'Test1',
'fail': False,
'update_replace': False,
- 'wait_secs': 0
+ 'wait_secs': 0,
+ 'action_wait_secs': {'create': 1},
+ 'client_name': 'nova',
+ 'entity_name': 'servers',
}
}
}
@@ -41,7 +45,8 @@
'value': 'Test1',
'fail': False,
'update_replace': False,
- 'wait_secs': 0
+ 'wait_secs': 0,
+ 'action_wait_secs': {'update': 1}
}
},
'test2': {
@@ -68,10 +73,9 @@
return modified_template
-class CreateStackTest(test.HeatIntegrationTest):
+class CreateStackTest(functional_base.FunctionalTestsBase):
def setUp(self):
super(CreateStackTest, self).setUp()
- self.client = self.orchestration_client
def test_create_rollback(self):
values = {'fail': True, 'value': 'test_create_rollback'}
@@ -84,7 +88,7 @@
disable_rollback=False)
-class UpdateStackTest(test.HeatIntegrationTest):
+class UpdateStackTest(functional_base.FunctionalTestsBase):
provider_template = {
'heat_template_version': '2013-05-23',
@@ -132,7 +136,6 @@
def setUp(self):
super(UpdateStackTest, self).setUp()
- self.client = self.orchestration_client
def test_stack_update_nochange(self):
template = _change_rsrc_properties(test_template_one_resource,
@@ -240,6 +243,29 @@
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'],
diff --git a/functional/test_create_update_neutron_port.py b/functional/test_create_update_neutron_port.py
index 3d82bc4..4b2df59 100644
--- a/functional/test_create_update_neutron_port.py
+++ b/functional/test_create_update_neutron_port.py
@@ -12,7 +12,7 @@
from testtools import testcase
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
test_template = '''
@@ -44,11 +44,10 @@
'''
-class UpdatePortTest(test.HeatIntegrationTest):
+class UpdatePortTest(functional_base.FunctionalTestsBase):
def setUp(self):
super(UpdatePortTest, self).setUp()
- self.client = self.orchestration_client
def get_port_id_and_ip(self, stack_identifier):
resources = self.client.resources.list(stack_identifier)
diff --git a/functional/test_default_parameters.py b/functional/test_default_parameters.py
index 00000fd..3e00c35 100644
--- a/functional/test_default_parameters.py
+++ b/functional/test_default_parameters.py
@@ -12,10 +12,10 @@
import yaml
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class DefaultParametersTest(test.HeatIntegrationTest):
+class DefaultParametersTest(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: 2013-05-23
@@ -65,7 +65,6 @@
def setUp(self):
super(DefaultParametersTest, self).setUp()
- self.client = self.orchestration_client
def test_defaults(self):
env = {'parameters': {}, 'parameter_defaults': {}}
diff --git a/functional/test_encrypted_parameter.py b/functional/test_encrypted_parameter.py
index c862f17..21dc925 100644
--- a/functional/test_encrypted_parameter.py
+++ b/functional/test_encrypted_parameter.py
@@ -10,35 +10,55 @@
# License for the specific language governing permissions and limitations
# under the License.
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class EncryptedParametersTest(test.HeatIntegrationTest):
+class EncryptedParametersTest(functional_base.FunctionalTestsBase):
template = '''
-heat_template_version: 2013-05-23
+heat_template_version: 2014-10-16
parameters:
+ image:
+ type: string
+ flavor:
+ type: string
+ network:
+ type: string
foo:
type: string
- description: Parameter with encryption turned on
+ description: 'parameter with encryption turned on'
hidden: true
default: secret
+resources:
+ server_with_encrypted_property:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: foo }
+ image: { get_param: image }
+ flavor: { get_param: flavor }
+ networks: [{network: {get_param: network} }]
outputs:
encrypted_foo_param:
- description: ''
- value: {get_param: foo}
+ description: 'encrypted param'
+ value: { get_param: foo }
'''
def setUp(self):
super(EncryptedParametersTest, self).setUp()
- self.client = self.orchestration_client
def test_db_encryption(self):
- # Create a stack with a non-default value for 'foo' to be encrypted
+ # Create a stack with the value of 'foo' to be encrypted
foo_param = 'my_encrypted_foo'
+ parameters = {
+ "image": self.conf.minimal_image_ref,
+ "flavor": self.conf.minimal_instance_type,
+ 'network': self.conf.fixed_network_name,
+ "foo": foo_param
+ }
+
stack_identifier = self.stack_create(
template=self.template,
- parameters={'foo': foo_param}
+ parameters=parameters
)
stack = self.client.stacks.get(stack_identifier)
diff --git a/functional/test_encryption_vol_type.py b/functional/test_encryption_vol_type.py
new file mode 100644
index 0000000..e1a3e76
--- /dev/null
+++ b/functional/test_encryption_vol_type.py
@@ -0,0 +1,91 @@
+# 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_integrationtests.common import clients
+from heat_integrationtests.common import config
+from heat_integrationtests.functional import functional_base
+
+test_encryption_vol_type = {
+ 'heat_template_version': '2015-04-30',
+ 'description': 'Test template to create encryption volume type.',
+ 'resources': {
+ 'my_volume_type': {
+ 'type': 'OS::Cinder::VolumeType',
+ 'properties': {
+ 'name': 'LUKS'
+ }
+ },
+ 'my_encrypted_vol_type': {
+ 'type': 'OS::Cinder::EncryptedVolumeType',
+ 'properties': {
+ 'provider': 'nova.volume.encryptors.luks.LuksEncryptor',
+ 'control_location': 'front-end',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 512,
+ 'volume_type': {'get_resource': 'my_volume_type'}
+ }
+ }
+ }
+}
+
+
+class EncryptionVolTypeTest(functional_base.FunctionalTestsBase):
+ def setUp(self):
+ super(EncryptionVolTypeTest, self).setUp()
+ self.conf = config.init_conf()
+ # cinder security policy usage of volume type is limited
+ # to being used by administrators only.
+ # Temporarily set username as admin for this test case.
+ self.conf.username = 'admin'
+ self.manager = clients.ClientManager(self.conf)
+ self.client = self.manager.orchestration_client
+ self.volume_client = self.manager.volume_client
+
+ def check_stack(self, sid):
+ vt = 'my_volume_type'
+ e_vt = 'my_encrypted_vol_type'
+
+ # check if only two resources are present.
+ expected_resources = {vt: 'OS::Cinder::VolumeType',
+ e_vt: 'OS::Cinder::EncryptedVolumeType'}
+ self.assertEqual(expected_resources,
+ self.list_resources(sid))
+
+ e_vt_obj = self.client.resources.get(sid, e_vt)
+ my_encrypted_vol_type_tmpl_prop = test_encryption_vol_type[
+ 'resources']['my_encrypted_vol_type']['properties']
+
+ # check if the phy rsrc specs was created in accordance with template.
+ phy_rsrc_specs = self.volume_client.volume_encryption_types.get(
+ e_vt_obj.physical_resource_id)
+ self.assertEqual(my_encrypted_vol_type_tmpl_prop['key_size'],
+ phy_rsrc_specs.key_size)
+ self.assertEqual(my_encrypted_vol_type_tmpl_prop['provider'],
+ phy_rsrc_specs.provider)
+ self.assertEqual(my_encrypted_vol_type_tmpl_prop['cipher'],
+ phy_rsrc_specs.cipher)
+ self.assertEqual(my_encrypted_vol_type_tmpl_prop['control_location'],
+ phy_rsrc_specs.control_location)
+
+ def test_create_update(self):
+ stack_identifier = self.stack_create(
+ template=test_encryption_vol_type)
+ self.check_stack(stack_identifier)
+
+ # Change some properties and trigger update.
+ my_encrypted_vol_type_tmpl_prop = test_encryption_vol_type[
+ 'resources']['my_encrypted_vol_type']['properties']
+ my_encrypted_vol_type_tmpl_prop['key_size'] = 256
+ my_encrypted_vol_type_tmpl_prop['cipher'] = 'aes-cbc-essiv'
+ self.update_stack(stack_identifier, test_encryption_vol_type)
+ self.check_stack(stack_identifier)
diff --git a/functional/test_heat_autoscaling.py b/functional/test_heat_autoscaling.py
index dbe3e8f..e87badb 100644
--- a/functional/test_heat_autoscaling.py
+++ b/functional/test_heat_autoscaling.py
@@ -10,10 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class HeatAutoscalingTest(test.HeatIntegrationTest):
+class HeatAutoscalingTest(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: 2014-10-16
@@ -74,7 +74,6 @@
def setUp(self):
super(HeatAutoscalingTest, self).setUp()
- self.client = self.orchestration_client
def _assert_output_values(self, stack_id):
stack = self.client.stacks.get(stack_id)
@@ -99,7 +98,7 @@
self._assert_output_values(stack_id)
-class AutoScalingGroupUpdateWithNoChanges(test.HeatIntegrationTest):
+class AutoScalingGroupUpdateWithNoChanges(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: 2013-05-23
@@ -123,7 +122,6 @@
def setUp(self):
super(AutoScalingGroupUpdateWithNoChanges, self).setUp()
- self.client = self.orchestration_client
def test_as_group_update_without_resource_changes(self):
stack_identifier = self.stack_create(template=self.template)
diff --git a/functional/test_hooks.py b/functional/test_hooks.py
index 5c6f6bd..ce1e7aa 100644
--- a/functional/test_hooks.py
+++ b/functional/test_hooks.py
@@ -14,17 +14,16 @@
import yaml
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
LOG = logging.getLogger(__name__)
-class HooksTest(test.HeatIntegrationTest):
+class HooksTest(functional_base.FunctionalTestsBase):
def setUp(self):
super(HooksTest, self).setUp()
- self.client = self.orchestration_client
self.template = {'heat_template_version': '2014-10-16',
'resources': {
'foo_step1': {'type': 'OS::Heat::RandomString'},
diff --git a/functional/test_instance_group.py b/functional/test_instance_group.py
index 1455d0b..b8bcc3d 100644
--- a/functional/test_instance_group.py
+++ b/functional/test_instance_group.py
@@ -15,10 +15,10 @@
from testtools import matchers
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class InstanceGroupTest(test.HeatIntegrationTest):
+class InstanceGroupTest(functional_base.FunctionalTestsBase):
template = '''
{
@@ -105,7 +105,6 @@
def setUp(self):
super(InstanceGroupTest, self).setUp()
- self.client = self.orchestration_client
if not self.conf.image_ref:
raise self.skipException("No image configured to test")
if not self.conf.minimal_image_ref:
@@ -226,7 +225,7 @@
parameters={},
environment=env
)
- self.addCleanup(self.client.stacks.delete, stack_name)
+ 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')
diff --git a/functional/test_notifications.py b/functional/test_notifications.py
index c729a67..3b8e003 100644
--- a/functional/test_notifications.py
+++ b/functional/test_notifications.py
@@ -17,7 +17,7 @@
import requests
from heat_integrationtests.common import test
-
+from heat_integrationtests.functional import functional_base
BASIC_NOTIFICATIONS = [
'orchestration.stack.create.start',
@@ -70,7 +70,7 @@
return self._notifications
-class NotificationTest(test.HeatIntegrationTest):
+class NotificationTest(functional_base.FunctionalTestsBase):
basic_template = '''
heat_template_version: 2013-05-23
@@ -124,10 +124,6 @@
def setUp(self):
super(NotificationTest, self).setUp()
- if self.conf.skip_notification_tests:
- self.skipTest('Testing Notifications disabled in conf, skipping')
-
- self.client = self.orchestration_client
self.exchange = kombu.Exchange('heat', 'topic', durable=False)
queue = kombu.Queue(exchange=self.exchange,
routing_key='notifications.info',
diff --git a/functional/test_preview.py b/functional/test_preview.py
new file mode 100644
index 0000000..19da01b
--- /dev/null
+++ b/functional/test_preview.py
@@ -0,0 +1,188 @@
+# 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_integrationtests.common import test
+from heatclient import exc
+import six
+
+
+class StackPreviewTest(test.HeatIntegrationTest):
+ template = '''
+heat_template_version: 2015-04-30
+parameters:
+ incomming:
+ type: string
+resources:
+ one:
+ type: OS::Heat::TestResource
+ properties:
+ value: fred
+ two:
+ type: OS::Heat::TestResource
+ properties:
+ value: {get_param: incomming}
+ depends_on: one
+outputs:
+ main_out:
+ value: {get_attr: [two, output]}
+ '''
+ env = '''
+parameters:
+ incomming: abc
+ '''
+
+ def setUp(self):
+ super(StackPreviewTest, self).setUp()
+ self.client = self.orchestration_client
+ self.project_id = self.identity_client.auth_ref.project_id
+
+ def _assert_resource(self, res, stack_name):
+ self.assertEqual(stack_name, res['stack_name'])
+ self.assertEqual('INIT', res['resource_action'])
+ self.assertEqual('COMPLETE', res['resource_status'])
+ for field in ('resource_status_reason', 'physical_resource_id',
+ 'description'):
+ self.assertIn(field, res)
+ self.assertEqual('', res[field])
+ # 'creation_time' and 'updated_time' are None when preview
+ for field in ('creation_time', 'updated_time'):
+ self.assertIn(field, res)
+ self.assertIsNone(res[field])
+ self.assertIn('output', res['attributes'])
+
+ # resource_identity
+ self.assertEqual(stack_name,
+ res['resource_identity']['stack_name'])
+ self.assertEqual('None', res['resource_identity']['stack_id'])
+ self.assertEqual(self.project_id,
+ res['resource_identity']['tenant'])
+ self.assertEqual('/resources/%s' % res['resource_name'],
+ res['resource_identity']['path'])
+ # stack_identity
+ self.assertEqual(stack_name,
+ res['stack_identity']['stack_name'])
+ self.assertEqual('None', res['stack_identity']['stack_id'])
+ self.assertEqual(self.project_id,
+ res['stack_identity']['tenant'])
+ self.assertEqual('', res['stack_identity']['path'])
+
+ def _assert_results(self, result, stack_name):
+ # global stuff.
+ self.assertEqual(stack_name, result['stack_name'])
+ self.assertTrue(result['disable_rollback'])
+ self.assertEqual('None', result['id'])
+ self.assertIsNone(result['parent'])
+ self.assertEqual('No description', result['template_description'])
+
+ # parameters
+ self.assertEqual('None', result['parameters']['OS::stack_id'])
+ self.assertEqual(stack_name, result['parameters']['OS::stack_name'])
+ self.assertEqual('abc', result['parameters']['incomming'])
+
+ def test_basic_pass(self):
+ stack_name = self._stack_rand_name()
+ result = self.client.stacks.preview(
+ template=self.template,
+ stack_name=stack_name,
+ disable_rollback=True,
+ environment=self.env).to_dict()
+
+ self._assert_results(result, stack_name)
+ for res in result['resources']:
+ self._assert_resource(res, stack_name)
+ self.assertEqual('OS::Heat::TestResource',
+ res['resource_type'])
+
+ # common properties
+ self.assertEqual(False, res['properties']['fail'])
+ self.assertEqual(0, res['properties']['wait_secs'])
+ self.assertEqual(False, res['properties']['update_replace'])
+
+ if res['resource_name'] == 'one':
+ self.assertEqual('fred', res['properties']['value'])
+ self.assertEqual(['two'], res['required_by'])
+ if res['resource_name'] == 'two':
+ self.assertEqual('abc', res['properties']['value'])
+ self.assertEqual([], res['required_by'])
+
+ def test_basic_fail(self):
+ stack_name = self._stack_rand_name()
+
+ # break the template so it fails validation.
+ wont_work = self.template.replace('get_param: incomming',
+ 'get_param: missing')
+ excp = self.assertRaises(exc.HTTPBadRequest,
+ self.client.stacks.preview,
+ template=wont_work,
+ stack_name=stack_name,
+ disable_rollback=True,
+ environment=self.env)
+
+ self.assertIn('Property error: : resources.two.properties.value: '
+ ': The Parameter (missing) was not provided.',
+ six.text_type(excp))
+
+ def test_nested_pass(self):
+ """Nested stacks need to recurse down the stacks."""
+ main_template = '''
+heat_template_version: 2015-04-30
+parameters:
+ incomming:
+ type: string
+resources:
+ main:
+ type: nested.yaml
+ properties:
+ value: {get_param: incomming}
+outputs:
+ main_out:
+ value: {get_attr: [main, output]}
+ '''
+ nested_template = '''
+heat_template_version: 2015-04-30
+parameters:
+ value:
+ type: string
+resources:
+ nested:
+ type: OS::Heat::TestResource
+ properties:
+ value: {get_param: value}
+outputs:
+ output:
+ value: {get_attr: [nested, output]}
+'''
+ stack_name = self._stack_rand_name()
+ result = self.client.stacks.preview(
+ disable_rollback=True,
+ stack_name=stack_name,
+ template=main_template,
+ files={'nested.yaml': nested_template},
+ environment=self.env).to_dict()
+
+ self._assert_results(result, stack_name)
+
+ # nested resources return a list of their resources.
+ res = result['resources'][0][0]
+ nested_stack_name = '%s-%s' % (stack_name,
+ res['parent_resource'])
+
+ self._assert_resource(res, nested_stack_name)
+ self.assertEqual('OS::Heat::TestResource',
+ res['resource_type'])
+
+ self.assertEqual(False, res['properties']['fail'])
+ self.assertEqual(0, res['properties']['wait_secs'])
+ self.assertEqual(False, res['properties']['update_replace'])
+
+ self.assertEqual('abc', res['properties']['value'])
+ self.assertEqual([], res['required_by'])
diff --git a/functional/test_reload_on_sighup.py b/functional/test_reload_on_sighup.py
index f149a58..cba5386 100644
--- a/functional/test_reload_on_sighup.py
+++ b/functional/test_reload_on_sighup.py
@@ -17,10 +17,10 @@
from oslo_concurrency import processutils
from six.moves import configparser
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class ReloadOnSighupTest(test.HeatIntegrationTest):
+class ReloadOnSighupTest(functional_base.FunctionalTestsBase):
def setUp(self):
self.config_file = "/etc/heat/heat.conf"
diff --git a/functional/test_remote_stack.py b/functional/test_remote_stack.py
index 868f24e..91a165c 100644
--- a/functional/test_remote_stack.py
+++ b/functional/test_remote_stack.py
@@ -14,10 +14,10 @@
from heatclient import exc
import six
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class RemoteStackTest(test.HeatIntegrationTest):
+class RemoteStackTest(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: 2013-05-23
resources:
@@ -45,7 +45,6 @@
def setUp(self):
super(RemoteStackTest, self).setUp()
- self.client = self.orchestration_client
def test_remote_stack_alone(self):
stack_id = self.stack_create(template=self.remote_template)
diff --git a/functional/test_resource_group.py b/functional/test_resource_group.py
index 8bc9950..5335245 100644
--- a/functional/test_resource_group.py
+++ b/functional/test_resource_group.py
@@ -17,10 +17,10 @@
import six
import yaml
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class ResourceGroupTest(test.HeatIntegrationTest):
+class ResourceGroupTest(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: 2013-05-23
resources:
@@ -44,7 +44,6 @@
def setUp(self):
super(ResourceGroupTest, self).setUp()
- self.client = self.orchestration_client
def _group_nested_identifier(self, stack_identifier,
group_name='random_group'):
@@ -328,7 +327,7 @@
self.assertNotEqual(initial_rand, updated_rand)
-class ResourceGroupTestNullParams(test.HeatIntegrationTest):
+class ResourceGroupTestNullParams(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: 2013-05-23
parameters:
@@ -383,7 +382,6 @@
def setUp(self):
super(ResourceGroupTestNullParams, self).setUp()
- self.client = self.orchestration_client
def test_create_pass_zero_parameter(self):
templ = self.template.replace('type: empty',
@@ -403,7 +401,7 @@
self.assertEqual(self.param, self._stack_output(stack, 'val')[0])
-class ResourceGroupAdoptTest(test.HeatIntegrationTest):
+class ResourceGroupAdoptTest(functional_base.FunctionalTestsBase):
"""Prove that we can do resource group adopt."""
main_template = '''
@@ -424,7 +422,6 @@
def setUp(self):
super(ResourceGroupAdoptTest, self).setUp()
- self.client = self.orchestration_client
def _yaml_to_json(self, yaml_templ):
return yaml.load(yaml_templ)
@@ -474,7 +471,7 @@
self.assertEqual('different', self._stack_output(stack, 'test1'))
-class ResourceGroupErrorResourceTest(test.HeatIntegrationTest):
+class ResourceGroupErrorResourceTest(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: "2013-05-23"
resources:
@@ -497,7 +494,6 @@
def setUp(self):
super(ResourceGroupErrorResourceTest, self).setUp()
- self.client = self.orchestration_client
def test_fail(self):
stack_identifier = self.stack_create(
diff --git a/functional/test_software_config.py b/functional/test_software_config.py
index 2e0ed76..af7d671 100644
--- a/functional/test_software_config.py
+++ b/functional/test_software_config.py
@@ -10,11 +10,17 @@
# License for the specific language governing permissions and limitations
# under the License.
-from heat_integrationtests.common import test
+from oslo_utils import timeutils
+import requests
+import time
+import yaml
+
+from heat_integrationtests.common import exceptions
+from heat_integrationtests.functional import functional_base
-class ParallelDeploymentsTest(test.HeatIntegrationTest):
- template = '''
+class ParallelDeploymentsTest(functional_base.FunctionalTestsBase):
+ server_template = '''
heat_template_version: "2013-05-23"
parameters:
flavor:
@@ -30,48 +36,112 @@
image: {get_param: image}
flavor: {get_param: flavor}
user_data_format: SOFTWARE_CONFIG
- networks: [{network: {get_param: network} }]
+ networks: [{network: {get_param: network}}]
+outputs:
+ server:
+ value: {get_resource: server}
+'''
+
+ config_template = '''
+heat_template_version: "2013-05-23"
+parameters:
+ server:
+ type: string
+resources:
config:
type: OS::Heat::SoftwareConfig
properties:
- config: hi!
- dep1:
- type: OS::Heat::SoftwareDeployment
- properties:
- config: {get_resource: config}
- server: {get_resource: server}
- signal_transport: NO_SIGNAL
- dep2:
- type: OS::Heat::SoftwareDeployment
- properties:
- config: {get_resource: config}
- server: {get_resource: server}
- signal_transport: NO_SIGNAL
- dep3:
- type: OS::Heat::SoftwareDeployment
- properties:
- config: {get_resource: config}
- server: {get_resource: server}
- signal_transport: NO_SIGNAL
- dep4:
- type: OS::Heat::SoftwareDeployment
- properties:
- config: {get_resource: config}
- server: {get_resource: server}
- signal_transport: NO_SIGNAL
'''
- def setUp(self):
- super(ParallelDeploymentsTest, self).setUp()
- self.client = self.orchestration_client
+ deployment_snippet = '''
+type: OS::Heat::SoftwareDeployments
+properties:
+ config: {get_resource: config}
+ servers: {'0': {get_param: server}}
+'''
- def test_fail(self):
+ enable_cleanup = True
+
+ def test_deployments_metadata(self):
parms = {'flavor': self.conf.minimal_instance_type,
'network': self.conf.fixed_network_name,
'image': self.conf.minimal_image_ref}
stack_identifier = self.stack_create(
parameters=parms,
- template=self.template)
- stack = self.client.stacks.get(stack_identifier)
- server_metadata = self.client.resources.metadata(stack.id, 'server')
- self.assertEqual(4, len(server_metadata['deployments']))
+ template=self.server_template,
+ enable_cleanup=self.enable_cleanup)
+ server_stack = self.client.stacks.get(stack_identifier)
+ server = server_stack.outputs[0]['output_value']
+
+ config_stacks = []
+ # add up to 3 stacks each with up to 3 deployments
+ deploy_count = 0
+ deploy_count = self.deploy_many_configs(
+ stack_identifier,
+ server,
+ config_stacks,
+ 2,
+ 5,
+ deploy_count)
+ self.deploy_many_configs(
+ stack_identifier,
+ server,
+ config_stacks,
+ 3,
+ 3,
+ deploy_count)
+
+ self.signal_deployments(stack_identifier)
+ for config_stack in config_stacks:
+ self._wait_for_stack_status(config_stack, 'CREATE_COMPLETE')
+
+ def deploy_many_configs(self, stack, server, config_stacks,
+ stack_count, deploys_per_stack,
+ deploy_count_start):
+ for a in range(stack_count):
+ config_stacks.append(
+ self.deploy_config(server, deploys_per_stack))
+
+ new_count = deploy_count_start + stack_count * deploys_per_stack
+ self.wait_for_deploy_metadata_set(stack, new_count)
+ return new_count
+
+ def deploy_config(self, server, deploy_count):
+ parms = {'server': server}
+ template = yaml.safe_load(self.config_template)
+ resources = template['resources']
+ resources['config']['properties'] = {'config': 'x' * 10000}
+ for a in range(deploy_count):
+ resources['dep_%s' % a] = yaml.safe_load(self.deployment_snippet)
+ return self.stack_create(
+ parameters=parms,
+ template=template,
+ enable_cleanup=self.enable_cleanup,
+ expected_status=None)
+
+ def wait_for_deploy_metadata_set(self, stack, deploy_count):
+ build_timeout = self.conf.build_timeout
+ build_interval = self.conf.build_interval
+
+ start = timeutils.utcnow()
+ while timeutils.delta_seconds(start,
+ timeutils.utcnow()) < build_timeout:
+ server_metadata = self.client.resources.metadata(
+ stack, 'server')
+ if len(server_metadata['deployments']) == deploy_count:
+ return
+ time.sleep(build_interval)
+
+ message = ('Deployment resources failed to be created within '
+ 'the required time (%s s).' %
+ (build_timeout))
+ raise exceptions.TimeoutException(message)
+
+ def signal_deployments(self, stack_identifier):
+ server_metadata = self.client.resources.metadata(
+ stack_identifier, 'server')
+ for dep in server_metadata['deployments']:
+ iv = dict((i['name'], i['value']) for i in dep['inputs'])
+ sigurl = iv.get('deploy_signal_id')
+ requests.post(sigurl, data='{}',
+ headers={'content-type': None})
diff --git a/functional/test_stack_tags.py b/functional/test_stack_tags.py
index cdcdcd3..4a97798 100644
--- a/functional/test_stack_tags.py
+++ b/functional/test_stack_tags.py
@@ -10,21 +10,27 @@
# License for the specific language governing permissions and limitations
# under the License.
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class StackTagTest(test.HeatIntegrationTest):
+class StackTagTest(functional_base.FunctionalTestsBase):
template = '''
heat_template_version: 2014-10-16
description:
foo
+parameters:
+ input:
+ type: string
+ default: test
+resources:
+ not-used:
+ type: OS::Heat::TestResource
+ properties:
+ wait_secs: 1
+ value: {get_param: input}
'''
- def setUp(self):
- super(StackTagTest, self).setUp()
- self.client = self.orchestration_client
-
def test_stack_tag(self):
# Stack create with stack tags
tags = 'foo,bar'
@@ -42,7 +48,8 @@
self.update_stack(
stack_identifier,
template=self.template,
- tags=updated_tags)
+ tags=updated_tags,
+ parameters={'input': 'next'})
# Ensure property tag is populated and matches updated tags
updated_stack = self.client.stacks.get(stack_identifier)
@@ -51,7 +58,8 @@
# Delete tags
self.update_stack(
stack_identifier,
- template=self.template
+ template=self.template,
+ parameters={'input': 'none'}
)
# Ensure property tag is not populated
diff --git a/functional/test_swiftsignal_update.py b/functional/test_swiftsignal_update.py
index 0f1c43c..5321014 100644
--- a/functional/test_swiftsignal_update.py
+++ b/functional/test_swiftsignal_update.py
@@ -11,7 +11,7 @@
# under the License.
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
test_template = '''
heat_template_version: 2014-10-16
@@ -31,11 +31,10 @@
'''
-class SwiftSignalHandleUpdateTest(test.HeatIntegrationTest):
+class SwiftSignalHandleUpdateTest(functional_base.FunctionalTestsBase):
def setUp(self):
super(SwiftSignalHandleUpdateTest, self).setUp()
- self.client = self.orchestration_client
def test_stack_update_same_template_replace_no_url(self):
stack_identifier = self.stack_create(template=test_template)
diff --git a/functional/test_template_resource.py b/functional/test_template_resource.py
index e047da1..1ec05d9 100644
--- a/functional/test_template_resource.py
+++ b/functional/test_template_resource.py
@@ -17,9 +17,10 @@
import yaml
from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class TemplateResourceTest(test.HeatIntegrationTest):
+class TemplateResourceTest(functional_base.FunctionalTestsBase):
"""Prove that we can use the registry in a nested provider."""
template = '''
@@ -48,7 +49,6 @@
def setUp(self):
super(TemplateResourceTest, self).setUp()
- self.client = self.orchestration_client
def test_nested_env(self):
main_templ = '''
@@ -148,7 +148,7 @@
self.assertEqual('freddy', secret_out2)
-class NestedAttributesTest(test.HeatIntegrationTest):
+class NestedAttributesTest(functional_base.FunctionalTestsBase):
"""Prove that we can use the template resource references."""
main_templ = '''
@@ -174,7 +174,6 @@
def setUp(self):
super(NestedAttributesTest, self).setUp()
- self.client = self.orchestration_client
def test_stack_ref(self):
nested_templ = '''
@@ -246,7 +245,7 @@
self.assertEqual(old_way, test_attr2)
-class TemplateResourceUpdateTest(test.HeatIntegrationTest):
+class TemplateResourceUpdateTest(functional_base.FunctionalTestsBase):
"""Prove that we can do template resource updates."""
main_template = '''
@@ -460,7 +459,6 @@
def setUp(self):
super(TemplateResourceUpdateTest, self).setUp()
- self.client = self.orchestration_client
def test_template_resource_update_template_schema(self):
stack_identifier = self.stack_create(
@@ -484,7 +482,7 @@
self._stack_output(stack, 'value'))
-class TemplateResourceUpdateFailedTest(test.HeatIntegrationTest):
+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'
@@ -507,7 +505,6 @@
def setUp(self):
super(TemplateResourceUpdateFailedTest, self).setUp()
- self.client = self.orchestration_client
self.assign_keypair()
def test_update_on_failed_create(self):
@@ -528,7 +525,7 @@
files={'server_fail.yaml': self.nested_templ})
-class TemplateResourceAdoptTest(test.HeatIntegrationTest):
+class TemplateResourceAdoptTest(functional_base.FunctionalTestsBase):
"""Prove that we can do template resource adopt/abandon."""
main_template = '''
@@ -563,7 +560,6 @@
def setUp(self):
super(TemplateResourceAdoptTest, self).setUp()
- self.client = self.orchestration_client
def _yaml_to_json(self, yaml_templ):
return yaml.load(yaml_templ)
@@ -608,7 +604,7 @@
self.assertEqual('goopie', self._stack_output(stack, 'value'))
-class TemplateResourceCheckTest(test.HeatIntegrationTest):
+class TemplateResourceCheckTest(functional_base.FunctionalTestsBase):
"""Prove that we can do template resource check."""
main_template = '''
@@ -643,7 +639,6 @@
def setUp(self):
super(TemplateResourceCheckTest, self).setUp()
- self.client = self.orchestration_client
def test_check(self):
stack_identifier = self.stack_create(
@@ -655,7 +650,7 @@
self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')
-class TemplateResourceErrorMessageTest(test.HeatIntegrationTest):
+class TemplateResourceErrorMessageTest(functional_base.FunctionalTestsBase):
"""Prove that nested stack errors don't suck."""
template = '''
HeatTemplateFormatVersion: '2012-12-12'
@@ -675,7 +670,6 @@
def setUp(self):
super(TemplateResourceErrorMessageTest, self).setUp()
- self.client = self.orchestration_client
def test_fail(self):
stack_identifier = self.stack_create(
@@ -691,7 +685,7 @@
self.assertEqual(exp, stack.stack_status_reason)
-class TemplateResourceSuspendResumeTest(test.HeatIntegrationTest):
+class TemplateResourceSuspendResumeTest(functional_base.FunctionalTestsBase):
"""Prove that we can do template resource suspend/resume."""
main_template = '''
@@ -711,7 +705,6 @@
def setUp(self):
super(TemplateResourceSuspendResumeTest, self).setUp()
- self.client = self.orchestration_client
def test_suspend_resume(self):
"""Basic test for template resource suspend resume"""
diff --git a/functional/test_validation.py b/functional/test_validation.py
index 1111ea8..da5cd68 100644
--- a/functional/test_validation.py
+++ b/functional/test_validation.py
@@ -11,14 +11,13 @@
# under the License.
-from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
-class StackValidationTest(test.HeatIntegrationTest):
+class StackValidationTest(functional_base.FunctionalTestsBase):
def setUp(self):
super(StackValidationTest, self).setUp()
- self.client = self.orchestration_client
if not self.conf.minimal_image_ref:
raise self.skipException("No image configured to test")
diff --git a/scenario/scenario_base.py b/scenario/scenario_base.py
index dd25b89..84e14a9 100644
--- a/scenario/scenario_base.py
+++ b/scenario/scenario_base.py
@@ -18,6 +18,8 @@
def setUp(self):
super(ScenarioTestsBase, self).setUp()
+ self.check_skip_test()
+
self.client = self.orchestration_client
self.sub_dir = 'templates'
self.assign_keypair()
@@ -55,3 +57,12 @@
)
return stack_id
+
+ def check_skip_test(self):
+ test_cls_name = self.__class__.__name__
+ test_method_name = '.'.join([test_cls_name, self._testMethodName])
+ test_skipped = (self.conf.skip_scenario_test_list and (
+ test_cls_name in self.conf.skip_scenario_test_list or
+ test_method_name in self.conf.skip_scenario_test_list))
+ if self.conf.skip_scenario_tests or test_skipped:
+ self.skipTest('Test disabled in conf, skipping')
diff --git a/scenario/templates/app_server_neutron.yaml b/scenario/templates/app_server_neutron.yaml
new file mode 100644
index 0000000..9cbf82a
--- /dev/null
+++ b/scenario/templates/app_server_neutron.yaml
@@ -0,0 +1,65 @@
+heat_template_version: 2015-10-15
+
+description: |
+ App server that is a member of Neutron Pool.
+
+parameters:
+
+ image:
+ type: string
+
+ flavor:
+ type: string
+
+ net:
+ type: string
+
+ sec_group:
+ type: string
+
+ pool_id:
+ type: string
+
+ app_port:
+ type: number
+
+ timeout:
+ type: number
+
+resources:
+
+ config:
+ type: OS::Test::WebAppConfig
+ properties:
+ app_port: { get_param: app_port }
+ wc_curl_cli: { get_attr: [ handle, curl_cli ] }
+
+ server:
+ type: OS::Nova::Server
+ properties:
+ image: { get_param: image }
+ flavor: { get_param: flavor }
+ networks:
+ - network: { get_param: net }
+ security_groups:
+ - { get_param: sec_group }
+ user_data_format: RAW
+ user_data: { get_resource: config }
+
+ handle:
+ type: OS::Heat::WaitConditionHandle
+
+ waiter:
+ type: OS::Heat::WaitCondition
+ depends_on: server
+ properties:
+ timeout: { get_param: timeout }
+ handle: { get_resource: handle }
+
+ pool_member:
+ type: OS::Neutron::PoolMember
+ depends_on: waiter
+ properties:
+ address: { get_attr: [ server, networks, { get_param: net }, 0 ] }
+ pool_id: { get_param: pool_id }
+ protocol_port: { get_param: app_port }
diff --git a/scenario/templates/netcat-webapp.yaml b/scenario/templates/netcat-webapp.yaml
new file mode 100644
index 0000000..fdb0335
--- /dev/null
+++ b/scenario/templates/netcat-webapp.yaml
@@ -0,0 +1,35 @@
+heat_template_version: 2015-10-15
+
+description: |
+ Simplest web-app using netcat reporting only hostname.
+ Specifically tailored for minimal Cirros image.
+
+parameters:
+
+ app_port:
+ type: number
+
+ wc_curl_cli:
+ type: string
+
+resources:
+
+ webapp_nc:
+ type: OS::Heat::SoftwareConfig
+ properties:
+ group: ungrouped
+ config:
+ str_replace:
+ template: |
+ #! /bin/sh -v
+ Body=$(hostname)
+ Response="HTTP/1.1 200 OK\r\nContent-Length: ${#Body}\r\n\r\n$Body"
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ while true ; do echo -e $Response | nc -llp PORT; done
+ params:
+ PORT: { get_param: app_port }
+ wc_notify: { get_param: wc_curl_cli }
+
+outputs:
+ OS::stack_id:
+ value: { get_resource: webapp_nc }
diff --git a/scenario/templates/test_autoscaling_lb_neutron.yaml b/scenario/templates/test_autoscaling_lb_neutron.yaml
new file mode 100644
index 0000000..d47e787
--- /dev/null
+++ b/scenario/templates/test_autoscaling_lb_neutron.yaml
@@ -0,0 +1,113 @@
+heat_template_version: 2015-04-30
+
+description: |
+ Template which tests Neutron load balancing requests to members of
+ Heat AutoScalingGroup.
+ Instances must be running some webserver on a given app_port
+ producing HTTP response that is different between servers
+ but stable over time for given server.
+
+parameters:
+ flavor:
+ type: string
+ image:
+ type: string
+ net:
+ type: string
+ subnet:
+ type: string
+ public_net:
+ type: string
+ app_port:
+ type: number
+ default: 8080
+ lb_port:
+ type: number
+ default: 80
+ timeout:
+ type: number
+ default: 600
+
+resources:
+
+ sec_group:
+ type: OS::Neutron::SecurityGroup
+ properties:
+ rules:
+ - remote_ip_prefix: 0.0.0.0/0
+ protocol: tcp
+ port_range_min: { get_param: app_port }
+ port_range_max: { get_param: app_port }
+
+ asg:
+ type: OS::Heat::AutoScalingGroup
+ properties:
+ desired_capacity: 1
+ max_size: 2
+ min_size: 1
+ resource:
+ type: OS::Test::NeutronAppServer
+ properties:
+ image: { get_param: image }
+ flavor: { get_param: flavor }
+ net: { get_param: net}
+ sec_group: { get_resource: sec_group }
+ app_port: { get_param: app_port }
+ pool_id: { get_resource: pool }
+ timeout: { get_param: timeout }
+
+ scale_up:
+ type: OS::Heat::ScalingPolicy
+ properties:
+ adjustment_type: change_in_capacity
+ auto_scaling_group_id: { get_resource: asg }
+ scaling_adjustment: 1
+
+ scale_down:
+ type: OS::Heat::ScalingPolicy
+ properties:
+ adjustment_type: change_in_capacity
+ auto_scaling_group_id: { get_resource: asg }
+ scaling_adjustment: -1
+
+ health_monitor:
+ type: OS::Neutron::HealthMonitor
+ properties:
+ delay: 3
+ type: HTTP
+ timeout: 3
+ max_retries: 3
+
+ pool:
+ type: OS::Neutron::Pool
+ properties:
+ lb_method: ROUND_ROBIN
+ protocol: HTTP
+ subnet: { get_param: subnet }
+ monitors:
+ - { get_resource: health_monitor }
+ vip:
+ protocol_port: { get_param: lb_port }
+
+ floating_ip:
+ type: OS::Neutron::FloatingIP
+ properties:
+ floating_network: { get_param: public_net }
+ port_id:
+ { get_attr: [pool, vip, 'port_id'] }
+
+ loadbalancer:
+ type: OS::Neutron::LoadBalancer
+ properties:
+ pool_id: { get_resource: pool }
+ protocol_port: { get_param: app_port }
+
+outputs:
+ lburl:
+ description: URL of the loadbalanced app
+ value:
+ str_replace:
+ template: http://IP_ADDRESS:PORT
+ params:
+ IP_ADDRESS: { get_attr: [ floating_ip, floating_ip_address ] }
+ PORT: { get_param: lb_port }
diff --git a/scenario/templates/test_neutron_autoscaling.yaml b/scenario/templates/test_neutron_autoscaling.yaml
deleted file mode 100644
index a34ec43..0000000
--- a/scenario/templates/test_neutron_autoscaling.yaml
+++ /dev/null
@@ -1,56 +0,0 @@
-heat_template_version: 2014-10-16
-
-description: Auto-scaling Test
-
-parameters:
- image_id:
- type: string
- label: Image ID
- description: Image ID from configurations
- capacity:
- type: string
- label: Capacity
- description: Auto-scaling group desired capacity
- fixed_subnet:
- type: string
- label: fixed subnetwork ID
- description: subnetwork ID used for autoscaling
- instance_type:
- type: string
- label: instance_type
- description: type of instance to launch
-
-resources:
- test_pool:
- type: OS::Neutron::Pool
- properties:
- description: Test Pool
- lb_method: ROUND_ROBIN
- name: test_pool
- protocol: HTTP
- subnet: { get_param: fixed_subnet }
- vip: {
- "description": "Test VIP",
- "protocol_port": 80,
- "name": "test_vip"
- }
- load_balancer:
- type: OS::Neutron::LoadBalancer
- properties:
- protocol_port: 80
- pool_id: { get_resource: test_pool }
- launch_config:
- type: AWS::AutoScaling::LaunchConfiguration
- properties:
- ImageId: { get_param: image_id }
- InstanceType: { get_param: instance_type }
- server_group:
- type: AWS::AutoScaling::AutoScalingGroup
- properties:
- AvailabilityZones : ["nova"]
- LaunchConfigurationName : { get_resource : launch_config }
- VPCZoneIdentifier: [{ get_param: fixed_subnet }]
- MinSize : 1
- MaxSize : 5
- DesiredCapacity: { get_param: capacity }
- LoadBalancerNames : [ { get_resource : load_balancer } ]
diff --git a/scenario/templates/test_neutron_loadbalancer.yaml b/scenario/templates/test_neutron_loadbalancer.yaml
deleted file mode 100644
index dd659d0..0000000
--- a/scenario/templates/test_neutron_loadbalancer.yaml
+++ /dev/null
@@ -1,133 +0,0 @@
-heat_template_version: 2014-10-16
-
-description: |
- Template which tests neutron load balancing resources
-
-parameters:
- key_name:
- type: string
- flavor:
- type: string
- image:
- type: string
- network:
- type: string
- private_subnet_id:
- type: string
- external_network_id:
- type: string
- port:
- type: string
- default: '80'
- timeout:
- type: number
-
-resources:
- sec_group:
- type: OS::Neutron::SecurityGroup
- properties:
- description: Add security group rules for servers
- name: security-group
- rules:
- - remote_ip_prefix: 0.0.0.0/0
- protocol: tcp
- port_range_min: { get_param: port }
- port_range_max: { get_param: port }
- - remote_ip_prefix: 0.0.0.0/0
- protocol: icmp
-
- wait_condition:
- type: OS::Heat::WaitCondition
- properties:
- handle: { get_resource: wait_condition_handle }
- count: 2
- timeout: { get_param: timeout }
-
- wait_condition_handle:
- type: OS::Heat::WaitConditionHandle
-
- config:
- type: OS::Heat::SoftwareConfig
- properties:
- group: ungrouped
- config:
- str_replace:
- template: |
- #!/bin/bash -v
- echo $(hostname) > index.html
- python -m SimpleHTTPServer port &
- wc_notify --data-binary '{"status": "SUCCESS"}'
- params:
- wc_notify: { get_attr: ['wait_condition_handle', 'curl_cli'] }
- port: { get_param: port }
-
- server1:
- type: OS::Nova::Server
- properties:
- name: Server1
- image: { get_param: image }
- flavor: { get_param: flavor }
- key_name: { get_param: key_name }
- networks: [{network: {get_param: network} }]
- security_groups: [{ get_resource: sec_group }]
- user_data_format: SOFTWARE_CONFIG
- user_data: { get_resource: config }
-
- server2:
- type: OS::Nova::Server
- properties:
- name: Server2
- image: { get_param: image }
- flavor: { get_param: flavor }
- key_name: { get_param: key_name }
- networks: [{network: {get_param: network} }]
- security_groups: [{ get_resource: sec_group }]
- user_data_format: SOFTWARE_CONFIG
- user_data: { get_resource: config }
-
- health_monitor:
- type: OS::Neutron::HealthMonitor
- properties:
- delay: 3
- type: HTTP
- timeout: 3
- max_retries: 3
-
- test_pool:
- type: OS::Neutron::Pool
- properties:
- lb_method: ROUND_ROBIN
- protocol: HTTP
- subnet: { get_param: private_subnet_id }
- monitors:
- - { get_resource: health_monitor }
- vip:
- protocol_port: { get_param: port }
-
- floating_ip:
- type: OS::Neutron::FloatingIP
- properties:
- floating_network: { get_param: external_network_id }
- port_id:
- { get_attr: [test_pool, vip, 'port_id'] }
- fixed_ip_address:
- { get_attr: [test_pool, vip, 'address'] }
-
- LBaaS:
- type: OS::Neutron::LoadBalancer
- depends_on: wait_condition
- properties:
- pool_id: { get_resource: test_pool }
- protocol_port: { get_param: port }
- members:
- - { get_resource: server1 }
-
-outputs:
- serv1_ip:
- value: {get_attr: [server1, networks, { get_param: network }, 0]}
- serv2_ip:
- value: {get_attr: [server2, networks, { get_param: network }, 0]}
- vip:
- value: {get_attr: [test_pool, vip, address]}
- fip:
- value: {get_attr: [floating_ip, floating_ip_address]}
diff --git a/scenario/test_autoscaling_lb.py b/scenario/test_autoscaling_lb.py
new file mode 100644
index 0000000..21b27dd
--- /dev/null
+++ b/scenario/test_autoscaling_lb.py
@@ -0,0 +1,114 @@
+#
+# 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
+
+import requests
+
+from heat_integrationtests.common import test
+from heat_integrationtests.scenario import scenario_base
+
+
+class AutoscalingLoadBalancerTest(scenario_base.ScenarioTestsBase):
+ """
+ The class is responsible for testing ASG + LB scenario.
+
+ The very common use case tested is an autoscaling group
+ of some web application servers behind a loadbalancer.
+ """
+
+ def setUp(self):
+ super(AutoscalingLoadBalancerTest, self).setUp()
+ self.template_name = 'test_autoscaling_lb_neutron.yaml'
+ self.app_server_template_name = 'app_server_neutron.yaml'
+ self.webapp_template_name = 'netcat-webapp.yaml'
+
+ def check_num_responses(self, url, expected_num, retries=10):
+ resp = set()
+ for count in range(retries):
+ time.sleep(1)
+ r = requests.get(url)
+ # skip unsuccessfull requests
+ if r.status_code == 200:
+ resp.add(r.text)
+ self.assertEqual(expected_num, len(resp))
+
+ def autoscale_complete(self, stack_id, expected_num):
+ res_list = self.client.resources.list(stack_id)
+ all_res_complete = all(res.resource_status in ('UPDATE_COMPLETE',
+ 'CREATE_COMPLETE')
+ for res in res_list)
+ all_res = len(res_list) == expected_num
+ return all_res and all_res_complete
+
+ def test_autoscaling_loadbalancer_neutron(self):
+ """
+ Check work of AutoScaing and Neutron LBaaS resource in Heat.
+
+ The scenario is the following:
+ 1. Launch a stack with a load balancer and autoscaling group
+ of one server, wait until stack create is complete.
+ 2. Check that there is only one distinctive response from
+ loadbalanced IP.
+ 3. Signal the scale_up policy, wait until all resources in
+ autoscaling group are complete.
+ 4. Check that now there are two distinctive responses from
+ loadbalanced IP.
+ """
+
+ parameters = {
+ 'flavor': self.conf.minimal_instance_type,
+ 'image': self.conf.minimal_image_ref,
+ 'net': self.conf.fixed_network_name,
+ 'subnet': self.conf.fixed_subnet_name,
+ 'public_net': self.conf.floating_network_name,
+ 'app_port': 8080,
+ 'lb_port': 80,
+ 'timeout': 600
+ }
+
+ app_server_template = self._load_template(
+ __file__, self.app_server_template_name, self.sub_dir
+ )
+ webapp_template = self._load_template(
+ __file__, self.webapp_template_name, self.sub_dir
+ )
+ files = {'appserver.yaml': app_server_template,
+ 'webapp.yaml': webapp_template}
+ env = {'resource_registry':
+ {'OS::Test::NeutronAppServer': 'appserver.yaml',
+ 'OS::Test::WebAppConfig': 'webapp.yaml'}}
+ # Launch stack
+ sid = self.launch_stack(
+ template_name=self.template_name,
+ parameters=parameters,
+ files=files,
+ environment=env
+ )
+ stack = self.client.stacks.get(sid)
+ lb_url = self._stack_output(stack, 'lburl')
+ # Check number of distinctive responces, must be 1
+ self.check_num_responses(lb_url, 1)
+
+ # Signal the scaling hook
+ self.client.resources.signal(sid, 'scale_up')
+
+ # Wait for AutoScalingGroup update to finish
+ asg = self.client.resources.get(sid, 'asg')
+ test.call_until_true(self.conf.build_timeout,
+ self.conf.build_interval,
+ self.autoscale_complete,
+ asg.physical_resource_id, 2)
+
+ # Check number of distinctive responses, must now be 2
+ self.check_num_responses(lb_url, 2)
diff --git a/scenario/test_ceilometer_alarm.py b/scenario/test_ceilometer_alarm.py
index d8f21fd..e0523aa 100644
--- a/scenario/test_ceilometer_alarm.py
+++ b/scenario/test_ceilometer_alarm.py
@@ -13,15 +13,15 @@
from oslo_log import log as logging
from heat_integrationtests.common import test
+from heat_integrationtests.scenario import scenario_base
LOG = logging.getLogger(__name__)
-class CeilometerAlarmTest(test.HeatIntegrationTest):
+class CeilometerAlarmTest(scenario_base.ScenarioTestsBase):
"""Class is responsible for testing of ceilometer usage."""
def setUp(self):
super(CeilometerAlarmTest, self).setUp()
- self.client = self.orchestration_client
self.template = self._load_template(__file__,
'test_ceilometer_alarm.yaml',
'templates')
diff --git a/scenario/test_neutron_autoscaling.py b/scenario/test_neutron_autoscaling.py
deleted file mode 100644
index c81e22d..0000000
--- a/scenario/test_neutron_autoscaling.py
+++ /dev/null
@@ -1,73 +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_integrationtests.scenario import scenario_base
-
-
-class NeutronAutoscalingTest(scenario_base.ScenarioTestsBase):
- """
- The class is responsible for testing of neutron resources autoscaling.
- """
-
- def setUp(self):
- super(NeutronAutoscalingTest, self).setUp()
- raise self.skipException("Skipping until bug #1479869 is fixed.")
- if not self.conf.fixed_subnet_name:
- raise self.skipException("No sub-network configured to test")
- self.template_name = 'test_neutron_autoscaling.yaml'
-
- def test_neutron_autoscaling(self):
- """
- Check autoscaling of load balancer members in Heat.
-
- The alternative scenario is the following:
- 1. Launch a stack with a load balancer.
- 2. Check that the load balancer created
- one load balancer member for stack.
- 3. Update stack definition: increase desired capacity of stack.
- 4. Check that number of members in load balancer was increased.
- """
-
- parameters = {
- "image_id": self.conf.minimal_image_ref,
- "capacity": "1",
- "instance_type": self.conf.minimal_instance_type,
- "fixed_subnet": self.net['subnets'][0],
- }
-
- # Launch stack
- stack_id = self.launch_stack(
- template_name=self.template_name,
- parameters=parameters
- )
-
- # Check number of members
- pool_resource = self.client.resources.get(stack_id, 'test_pool')
- pool_members = self.network_client.list_members(
- pool_id=pool_resource.physical_resource_id)['members']
- self.assertEqual(1, len(pool_members))
-
- # Increase desired capacity and update the stack
- template = self._load_template(
- __file__, self.template_name, self.sub_dir
- )
- parameters["capacity"] = "2"
- self.update_stack(
- stack_id,
- template=template,
- parameters=parameters
- )
-
- # Check number of members
- pool_members = self.network_client.list_members(
- pool_id=pool_resource.physical_resource_id)['members']
- self.assertEqual(2, len(pool_members))
diff --git a/scenario/test_neutron_loadbalancer.py b/scenario/test_neutron_loadbalancer.py
deleted file mode 100644
index a890257..0000000
--- a/scenario/test_neutron_loadbalancer.py
+++ /dev/null
@@ -1,109 +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 six.moves import urllib
-
-from heat_integrationtests.scenario import scenario_base
-
-
-class NeutronLoadBalancerTest(scenario_base.ScenarioTestsBase):
- """
- The class is responsible for testing of neutron resources balancer.
- """
-
- def setUp(self):
- super(NeutronLoadBalancerTest, self).setUp()
- raise self.skipException("Skipping until bug #1479869 is fixed.")
- self.public_net = self._get_network(self.conf.floating_network_name)
- self.template_name = 'test_neutron_loadbalancer.yaml'
-
- def collect_responses(self, ip, expected_resp):
- resp = set()
- for count in range(10):
- time.sleep(1)
- resp.add(urllib.request.urlopen('http://%s/' % ip).read())
-
- self.assertEqual(expected_resp, resp)
-
- def test_neutron_loadbalancer(self):
- """
- Check work of Neutron LBaaS resource in Heat.
-
- The alternative scenario is the following:
- 1. Launch a stack with a load balancer, two servers,
- but use only one as a LB member.
- 2. Check connection to the servers and LB.
- 3. Collect info about responces, which were received by LB from
- its members (responces have to be received only from 'server1').
- 4. Update stack definition: include 'server2' into LBaaS.
- 5. Check that number of members in LB was increased and
- responces were received from 'server1' and 'server2'.
- """
-
- parameters = {
- 'key_name': self.keypair_name,
- 'flavor': self.conf.minimal_instance_type,
- 'image': self.conf.image_ref,
- 'network': self.net['name'],
- 'private_subnet_id': self.net['subnets'][0],
- 'external_network_id': self.public_net['id'],
- 'timeout': self.conf.build_timeout
- }
-
- # Launch stack
- sid = self.launch_stack(
- template_name=self.template_name,
- parameters=parameters
- )
-
- stack = self.client.stacks.get(sid)
- floating_ip = self._stack_output(stack, 'fip')
- vip = self._stack_output(stack, 'vip')
- server1_ip = self._stack_output(stack, 'serv1_ip')
- server2_ip = self._stack_output(stack, 'serv2_ip')
- # Check connection and info about received responses
- self.check_connectivity(server1_ip)
- self.collect_responses(server1_ip, {'server1\n'})
-
- self.check_connectivity(server2_ip)
- self.collect_responses(server2_ip, {'server2\n'})
-
- self.check_connectivity(vip)
- self.collect_responses(vip, {'server1\n'})
-
- self.check_connectivity(floating_ip)
- self.collect_responses(floating_ip, {'server1\n'})
-
- # Include 'server2' to LB and update the stack
- template = self._load_template(
- __file__, self.template_name, self.sub_dir
- )
-
- template = template.replace(
- '- { get_resource: server1 }',
- '- { get_resource: server1 }\n - { get_resource: server2 }\n'
- )
-
- self.update_stack(
- sid,
- template=template,
- parameters=parameters
- )
-
- self.check_connectivity(vip)
- self.collect_responses(vip, {'server1\n', 'server2\n'})
-
- self.check_connectivity(floating_ip)
- self.collect_responses(floating_ip, {'server1\n', 'server2\n'})
diff --git a/scenario/test_server_cfn_init.py b/scenario/test_server_cfn_init.py
index 197e0c0..267b44b 100644
--- a/scenario/test_server_cfn_init.py
+++ b/scenario/test_server_cfn_init.py
@@ -23,7 +23,6 @@
def setUp(self):
super(CfnInitIntegrationTest, self).setUp()
- raise self.skipException("Skipping until bug #1479869 is fixed.")
def check_stack(self, sid):
# Check status of all resources
diff --git a/scenario/test_server_software_config.py b/scenario/test_server_software_config.py
index 19fd1a8..8739a99 100644
--- a/scenario/test_server_software_config.py
+++ b/scenario/test_server_software_config.py
@@ -44,9 +44,6 @@
def setUp(self):
super(SoftwareConfigIntegrationTest, self).setUp()
- if self.conf.skip_software_config_tests:
- self.skipTest('Testing software config disabled in conf, '
- 'skipping')
self.stack_name = self._stack_rand_name()
def check_stack(self):
diff --git a/scenario/test_volumes.py b/scenario/test_volumes.py
index 7562304..d4b1aa8 100644
--- a/scenario/test_volumes.py
+++ b/scenario/test_volumes.py
@@ -14,7 +14,6 @@
from cinderclient import exceptions as cinder_exceptions
from oslo_log import log as logging
import six
-from testtools import testcase
from heat_integrationtests.common import exceptions
from heat_integrationtests.scenario import scenario_base
@@ -101,8 +100,6 @@
self.volume_client.volumes.get,
volume_id2)
- @testcase.skip('Skipped until failure rate '
- 'can be reduced ref bug #1382300')
def test_cinder_volume_create_backup_restore(self):
"""
Ensure the 'Snapshot' deletion policy works.