Initial heat orchestration tests.
This does a creat/list/show/delete of a stack with no resources. It should run fast and is suitable for gating, since it doesn't create any nova instances. I'd consider it to be a smoke test. It confirms that heat-api can communicate with heat-engine, and that heat-engine has a properly configured database. Blueprint: add-basic-heat-tests Change-Id: Id249a34c3a28e1e69d9eb000c6c5c2e82d619178
This commit is contained in:
0
tempest/api/orchestration/__init__.py
Normal file
0
tempest/api/orchestration/__init__.py
Normal file
110
tempest/api/orchestration/base.py
Normal file
110
tempest/api/orchestration/base.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# 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 logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
from tempest import clients
|
||||||
|
from tempest.common.utils.data_utils import rand_name
|
||||||
|
import tempest.test
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseOrchestrationTest(tempest.test.BaseTestCase):
|
||||||
|
"""Base test case class for all Orchestration API tests."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
|
||||||
|
os = clients.OrchestrationManager()
|
||||||
|
cls.orchestration_cfg = os.config.orchestration
|
||||||
|
if not cls.orchestration_cfg.heat_available:
|
||||||
|
raise cls.skipException("Heat support is required")
|
||||||
|
|
||||||
|
cls.os = os
|
||||||
|
cls.orchestration_client = os.orchestration_client
|
||||||
|
cls.keypairs_client = os.keypairs_client
|
||||||
|
cls.stacks = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_identity_admin_client(cls):
|
||||||
|
"""
|
||||||
|
Returns an instance of the Identity Admin API client
|
||||||
|
"""
|
||||||
|
os = clients.AdminManager(interface=cls._interface)
|
||||||
|
admin_client = os.identity_client
|
||||||
|
return admin_client
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_client_args(cls):
|
||||||
|
|
||||||
|
return (
|
||||||
|
cls.config,
|
||||||
|
cls.config.identity.admin_username,
|
||||||
|
cls.config.identity.admin_password,
|
||||||
|
cls.config.identity.uri
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_stack(self, stack_name, template_data, parameters={}):
|
||||||
|
resp, body = self.client.create_stack(
|
||||||
|
stack_name,
|
||||||
|
template=template_data,
|
||||||
|
parameters=parameters)
|
||||||
|
self.assertEqual('201', resp['status'])
|
||||||
|
stack_id = resp['location'].split('/')[-1]
|
||||||
|
stack_identifier = '%s/%s' % (stack_name, stack_id)
|
||||||
|
self.stacks.append(stack_identifier)
|
||||||
|
return stack_identifier
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_stacks(cls):
|
||||||
|
for stack_identifier in cls.stacks:
|
||||||
|
try:
|
||||||
|
cls.orchestration_client.delete_stack(stack_identifier)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for stack_identifier in cls.stacks:
|
||||||
|
try:
|
||||||
|
cls.orchestration_client.wait_for_stack_status(
|
||||||
|
stack_identifier, 'DELETE_COMPLETE')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _create_keypair(self, namestart='keypair-heat-'):
|
||||||
|
kp_name = rand_name(namestart)
|
||||||
|
resp, body = self.keypairs_client.create_keypair(kp_name)
|
||||||
|
self.assertEqual(body['name'], kp_name)
|
||||||
|
return body
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.clear_stacks()
|
||||||
|
|
||||||
|
def wait_for(self, condition):
|
||||||
|
"""Repeatedly calls condition() until a timeout."""
|
||||||
|
start_time = int(time.time())
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
condition()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
if int(time.time()) - start_time >= self.build_timeout:
|
||||||
|
condition()
|
||||||
|
return
|
||||||
|
time.sleep(self.build_interval)
|
||||||
0
tempest/api/orchestration/stacks/__init__.py
Normal file
0
tempest/api/orchestration/stacks/__init__.py
Normal file
77
tempest/api/orchestration/stacks/test_stacks.py
Normal file
77
tempest/api/orchestration/stacks/test_stacks.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# 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 logging
|
||||||
|
|
||||||
|
from tempest.api.orchestration import base
|
||||||
|
from tempest.common.utils.data_utils import rand_name
|
||||||
|
from tempest.test import attr
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class StacksTestJSON(base.BaseOrchestrationTest):
|
||||||
|
_interface = 'json'
|
||||||
|
|
||||||
|
empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(StacksTestJSON, cls).setUpClass()
|
||||||
|
cls.client = cls.orchestration_client
|
||||||
|
|
||||||
|
@attr(type='smoke')
|
||||||
|
def test_stack_list_responds(self):
|
||||||
|
resp, body = self.client.list_stacks()
|
||||||
|
stacks = body['stacks']
|
||||||
|
self.assertEqual('200', resp['status'])
|
||||||
|
self.assertIsInstance(stacks, list)
|
||||||
|
|
||||||
|
@attr(type='smoke')
|
||||||
|
def test_stack_crud_no_resources(self):
|
||||||
|
stack_name = rand_name('heat')
|
||||||
|
|
||||||
|
# count how many stacks to start with
|
||||||
|
resp, body = self.client.list_stacks()
|
||||||
|
stack_count = len(body['stacks'])
|
||||||
|
|
||||||
|
# create the stack
|
||||||
|
stack_identifier = self.create_stack(
|
||||||
|
stack_name, self.empty_template)
|
||||||
|
|
||||||
|
# wait for create complete (with no resources it should be instant)
|
||||||
|
self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||||
|
|
||||||
|
# stack count will increment by 1
|
||||||
|
resp, body = self.client.list_stacks()
|
||||||
|
self.assertEqual(stack_count + 1, len(body['stacks']),
|
||||||
|
'Expected stack count to increment by 1')
|
||||||
|
|
||||||
|
# fetch the stack
|
||||||
|
resp, body = self.client.get_stack(stack_identifier)
|
||||||
|
self.assertEqual('CREATE_COMPLETE', body['stack_status'])
|
||||||
|
|
||||||
|
# fetch the stack by name
|
||||||
|
resp, body = self.client.get_stack(stack_name)
|
||||||
|
self.assertEqual('CREATE_COMPLETE', body['stack_status'])
|
||||||
|
|
||||||
|
# fetch the stack by id
|
||||||
|
stack_id = stack_identifier.split('/')[1]
|
||||||
|
resp, body = self.client.get_stack(stack_id)
|
||||||
|
self.assertEqual('CREATE_COMPLETE', body['stack_status'])
|
||||||
|
|
||||||
|
# delete the stack
|
||||||
|
resp = self.client.delete_stack(stack_identifier)
|
||||||
|
self.assertEqual('204', resp[0]['status'])
|
||||||
Reference in New Issue
Block a user