Framework for resource safe class level fixtures

Modify BaseTestCase, add resource_setup and resource_cleanup
methods, invoked by setUpClass and tearDownClass respectively.

Partially-implements bp:resource-cleanup

Change-Id: I5f025b4f76d6d85fbefbada9475daf784425037a
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 66043d3..6e35a31 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -94,13 +94,6 @@
         cls.network_client = cls.manager.network_client
 
     @classmethod
-    def tearDownClass(cls):
-        # Isolated creds also manages network resources, which should
-        # be cleaned up at the end of the test case
-        cls.isolated_creds.clear_isolated_creds()
-        super(ScenarioTest, cls).tearDownClass()
-
-    @classmethod
     def _get_credentials(cls, get_creds, ctype):
         if CONF.compute.allow_tenant_isolation:
             creds = get_creds()
@@ -1015,11 +1008,6 @@
         cls.ceilometer_client = cls.manager.ceilometer_client
 
     @classmethod
-    def tearDownClass(cls):
-        cls.isolated_creds.clear_isolated_creds()
-        super(OfficialClientTest, cls).tearDownClass()
-
-    @classmethod
     def _get_credentials(cls, get_creds, ctype):
         if CONF.compute.allow_tenant_isolation:
             creds = get_creds()
diff --git a/tempest/test.py b/tempest/test.py
index d2b32d4..4a22b1b 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -69,6 +69,9 @@
 def safe_setup(f):
     """A decorator used to wrap the setUpClass for cleaning up resources
        when setUpClass failed.
+
+    Deprecated, see:
+    http://specs.openstack.org/openstack/qa-specs/specs/resource-cleanup.html
     """
     @functools.wraps(f)
     def decorator(cls):
@@ -279,15 +282,51 @@
 
     @classmethod
     def setUpClass(cls):
+        # It should never be overridden by descendants
         if hasattr(super(BaseTestCase, cls), 'setUpClass'):
             super(BaseTestCase, cls).setUpClass()
         cls.setUpClassCalled = True
+        # No test resource is allocated until here
+        try:
+            # TODO(andreaf) Split-up resource_setup in stages:
+            # skip checks, pre-hook, credentials, clients, resources, post-hook
+            cls.resource_setup()
+        except Exception:
+            etype, value, trace = sys.exc_info()
+            LOG.info("%s in resource setup. Invoking tearDownClass." % etype)
+            # Catch any exception in tearDown so we can re-raise the original
+            # exception at the end
+            try:
+                cls.tearDownClass()
+            except Exception as te:
+                LOG.exception("tearDownClass failed: %s" % te)
+            try:
+                raise etype(value), None, trace
+            finally:
+                del trace  # for avoiding circular refs
 
     @classmethod
     def tearDownClass(cls):
         at_exit_set.discard(cls)
+        # It should never be overridden by descendants
         if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
             super(BaseTestCase, cls).tearDownClass()
+        try:
+            cls.resource_cleanup()
+        finally:
+            cls.clear_isolated_creds()
+
+    @classmethod
+    def resource_setup(cls):
+        """Class level setup steps for test cases.
+        Recommended order: skip checks, credentials, clients, resources.
+        """
+        pass
+
+    @classmethod
+    def resource_cleanup(cls):
+        """Class level resource cleanup for test cases. """
+        pass
 
     def setUp(self):
         super(BaseTestCase, self).setUp()
@@ -355,7 +394,7 @@
         """
         Clears isolated creds if set
         """
-        if getattr(cls, 'isolated_creds'):
+        if hasattr(cls, 'isolated_creds'):
             cls.isolated_creds.clear_isolated_creds()
 
     @classmethod