Merge "Add unit tests for api_version detection"
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index a65d7b7..e1dbc76 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -34,6 +34,7 @@
@classmethod
def setUpClass(cls):
+ cls.set_network_resources()
super(BaseComputeTest, cls).setUpClass()
os = cls.get_client_manager()
@@ -218,7 +219,6 @@
@classmethod
def setUpClass(cls):
# By default compute tests do not create network resources
- cls.set_network_resources()
super(BaseV2ComputeTest, cls).setUpClass()
cls.servers_client = cls.os.servers_client
cls.flavors_client = cls.os.flavors_client
@@ -324,7 +324,6 @@
cls.__name__)
raise cls.skipException(skip_msg)
- cls.set_network_resources()
super(BaseV3ComputeTest, cls).setUpClass()
cls.servers_client = cls.os.servers_v3_client
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index d919245..c1f468b 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -27,6 +27,7 @@
class AccountQuotasTest(base.BaseObjectTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(AccountQuotasTest, cls).setUpClass()
cls.container_name = data_utils.rand_name(name="TestContainer")
@@ -101,7 +102,8 @@
@classmethod
def tearDownClass(cls):
- cls.delete_containers([cls.container_name])
+ if hasattr(cls, "container_name"):
+ cls.delete_containers([cls.container_name])
cls.data.teardown_all()
super(AccountQuotasTest, cls).tearDownClass()
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 5a79529..4677f97 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -27,6 +27,7 @@
class AccountQuotasNegativeTest(base.BaseObjectTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(AccountQuotasNegativeTest, cls).setUpClass()
cls.container_name = data_utils.rand_name(name="TestContainer")
@@ -100,7 +101,8 @@
@classmethod
def tearDownClass(cls):
- cls.delete_containers([cls.container_name])
+ if hasattr(cls, "container_name"):
+ cls.delete_containers([cls.container_name])
cls.data.teardown_all()
super(AccountQuotasNegativeTest, cls).tearDownClass()
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 4b895d8..7fb0604 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -29,10 +29,13 @@
class AccountTest(base.BaseObjectTest):
+
+ containers = []
+
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(AccountTest, cls).setUpClass()
- cls.containers = []
for i in moves.xrange(ord('a'), ord('f') + 1):
name = data_utils.rand_name(name='%s-' % chr(i))
cls.container_client.create_container(name)
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 6c71340..581c6d9 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -23,6 +23,7 @@
class StaticWebTest(base.BaseObjectTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(StaticWebTest, cls).setUpClass()
cls.container_name = data_utils.rand_name(name="TestContainer")
@@ -45,7 +46,8 @@
@classmethod
def tearDownClass(cls):
- cls.delete_containers([cls.container_name])
+ if hasattr(cls, "container_name"):
+ cls.delete_containers([cls.container_name])
cls.data.teardown_all()
super(StaticWebTest, cls).tearDownClass()
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 9bd986f..6bda83b 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -31,8 +31,10 @@
class ContainerSyncTest(base.BaseObjectTest):
+ clients = {}
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ContainerSyncTest, cls).setUpClass()
cls.containers = []
@@ -50,7 +52,6 @@
int(container_sync_timeout / cls.container_sync_interval)
# define container and object clients
- cls.clients = {}
cls.clients[data_utils.rand_name(name='TestContainerSync')] = \
(cls.container_client, cls.object_client)
cls.clients[data_utils.rand_name(name='TestContainerSync')] = \
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index 81db252..dc5585e 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -26,7 +26,11 @@
class ObjectFormPostTest(base.BaseObjectTest):
+ metadata = {}
+ containers = []
+
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ObjectFormPostTest, cls).setUpClass()
cls.container_name = data_utils.rand_name(name='TestContainer')
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index fe0c994..878bf6d 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -26,7 +26,11 @@
class ObjectFormPostNegativeTest(base.BaseObjectTest):
+ metadata = {}
+ containers = []
+
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ObjectFormPostNegativeTest, cls).setUpClass()
cls.container_name = data_utils.rand_name(name='TestContainer')
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index cf24f66..7d26433 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -27,7 +27,11 @@
class ObjectTempUrlNegativeTest(base.BaseObjectTest):
+ metadata = {}
+ containers = []
+
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ObjectTempUrlNegativeTest, cls).setUpClass()
diff --git a/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml b/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml
new file mode 100644
index 0000000..08e3da4
--- /dev/null
+++ b/tempest/api/orchestration/stacks/templates/cinder_basic_delete_retain.yaml
@@ -0,0 +1,25 @@
+heat_template_version: 2013-05-23
+
+resources:
+ volume:
+ deletion_policy: 'Retain'
+ type: OS::Cinder::Volume
+ properties:
+ size: 1
+ description: a descriptive description
+
+outputs:
+ status:
+ description: status
+ value: { get_attr: ['volume', 'status'] }
+
+ size:
+ description: size
+ value: { get_attr: ['volume', 'size'] }
+
+ display_description:
+ description: display_description
+ value: { get_attr: ['volume', 'display_description'] }
+
+ volume_id:
+ value: { get_resource: volume }
diff --git a/tempest/api/orchestration/stacks/test_volumes.py b/tempest/api/orchestration/stacks/test_volumes.py
index a60cd37..2544c41 100644
--- a/tempest/api/orchestration/stacks/test_volumes.py
+++ b/tempest/api/orchestration/stacks/test_volumes.py
@@ -15,6 +15,7 @@
from tempest.api.orchestration import base
from tempest.common.utils import data_utils
from tempest import config
+from tempest import exceptions
from tempest import test
@@ -30,6 +31,24 @@
if not CONF.service_available.cinder:
raise cls.skipException('Cinder support is required')
+ def _cinder_verify(self, volume_id):
+ self.assertIsNotNone(volume_id)
+ resp, volume = self.volumes_client.get_volume(volume_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual('available', volume.get('status'))
+ self.assertEqual(1, volume.get('size'))
+ self.assertEqual('a descriptive description',
+ volume.get('display_description'))
+
+ def _outputs_verify(self, stack_identifier):
+ self.assertEqual('available',
+ self.get_stack_output(stack_identifier, 'status'))
+ self.assertEqual('1',
+ self.get_stack_output(stack_identifier, 'size'))
+ self.assertEqual('a descriptive description',
+ self.get_stack_output(stack_identifier,
+ 'display_description'))
+
@test.attr(type='gate')
def test_cinder_volume_create_delete(self):
"""Create and delete a volume via OS::Cinder::Volume."""
@@ -40,19 +59,43 @@
# Verify with cinder that the volume exists, with matching details
volume_id = self.get_stack_output(stack_identifier, 'volume_id')
- self.assertIsNotNone(volume_id)
- resp, volume = self.volumes_client.get_volume(volume_id)
- self.assertEqual(200, resp.status)
- self.assertEqual('available', volume.get('status'))
- self.assertEqual(1, volume.get('size'))
- self.assertEqual('a descriptive description',
- volume.get('display_description'))
+ self._cinder_verify(volume_id)
# Verify the stack outputs are as expected
- self.assertEqual('available',
- self.get_stack_output(stack_identifier, 'status'))
- self.assertEqual('1',
- self.get_stack_output(stack_identifier, 'size'))
- self.assertEqual('a descriptive description',
- self.get_stack_output(stack_identifier,
- 'display_description'))
+ self._outputs_verify(stack_identifier)
+
+ # Delete the stack and ensure the volume is gone
+ self.client.delete_stack(stack_identifier)
+ self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
+ self.assertRaises(exceptions.NotFound,
+ self.volumes_client.get_volume,
+ volume_id)
+
+ def _cleanup_volume(self, volume_id):
+ """Cleanup the volume direct with cinder."""
+ resp = self.volumes_client.delete_volume(volume_id)
+ self.assertEqual(202, resp[0].status)
+ self.volumes_client.wait_for_resource_deletion(volume_id)
+
+ @test.attr(type='gate')
+ def test_cinder_volume_create_delete_retain(self):
+ """Ensure the 'Retain' deletion policy is respected."""
+ stack_name = data_utils.rand_name('heat')
+ template = self.load_template('cinder_basic_delete_retain')
+ stack_identifier = self.create_stack(stack_name, template)
+ self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
+
+ # Verify with cinder that the volume exists, with matching details
+ volume_id = self.get_stack_output(stack_identifier, 'volume_id')
+ self.addCleanup(self._cleanup_volume, volume_id)
+ self._cinder_verify(volume_id)
+
+ # Verify the stack outputs are as expected
+ self._outputs_verify(stack_identifier)
+
+ # Delete the stack and ensure the volume is *not* gone
+ self.client.delete_stack(stack_identifier)
+ self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
+ self._cinder_verify(volume_id)
+
+ # Volume cleanup happens via addCleanup calling _cleanup_volume
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 8c07d4f..9745cb4 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -282,7 +282,7 @@
return ""
def _log_request(self, method, req_url, resp,
- secs="", req_headers=None,
+ secs="", req_headers={},
req_body=None, resp_body=None):
# if we have the request id, put it in the right part of the log
extra = dict(request_id=self._get_request_id(resp))
@@ -306,6 +306,8 @@
# world this is important to match
trace_regex = CONF.debug.trace_requests
if trace_regex and re.search(trace_regex, caller_name):
+ if 'X-Auth-Token' in req_headers:
+ req_headers['X-Auth-Token'] = '<omitted>'
log_fmt = """Request (%s): %s %s %s%s
Request - Headers: %s
Body: %s