Merge "skip test_list_servers_filtered_by_ip_regex on neutron gate"
diff --git a/tempest/api/README.rst b/tempest/api/README.rst
index 617fda4..9d8dc10 100644
--- a/tempest/api/README.rst
+++ b/tempest/api/README.rst
@@ -9,15 +9,15 @@
 works with the OpenStack API as documented. The current largest
 portion of Tempest code is devoted to test cases that do exactly this.
 
-It's also important to test not only the expected possitive path on
+It's also important to test not only the expected positive path on
 APIs, but also to provide them with invalid data to ensure they fail
 in expected and documented ways. Over the course of the OpenStack
 project Tempest has discovered many fundamental bugs by doing just
 this.
 
-In order for some APIs to return meaniful results, there must be
+In order for some APIs to return meaningful results, there must be
 enough data in the system. This means these tests might start by
-spinning up a server, image, etc, then opperating on it.
+spinning up a server, image, etc, then operating on it.
 
 
 Why are these tests in tempest?
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 15e28fd..acf0275 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -36,6 +36,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(BaseComputeTest, cls).setUpClass()
         if not cls.config.service_available.nova:
             skip_msg = ("%s skipped as nova is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 9f7b24b..d98fb71 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -53,6 +53,7 @@
             cls.client.delete_endpoint(e['id'])
         for s in cls.service_ids:
             cls.identity_client.delete_service(s)
+        super(EndPointsTestJSON, cls).tearDownClass()
 
     @attr(type='gate')
     def test_list_endpoints(self):
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 1237ce4..bfb5372 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -25,6 +25,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(BaseIdentityAdminTest, cls).setUpClass()
         os = clients.AdminManager(interface=cls._interface)
         cls.client = os.identity_client
         cls.token_client = os.token_client
@@ -45,6 +46,7 @@
     @classmethod
     def tearDownClass(cls):
         cls.data.teardown_all()
+        super(BaseIdentityAdminTest, cls).tearDownClass()
 
     def disable_user(self, user_name):
         user = self.get_user_by_name(user_name)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 4e61495..4f54a15 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -29,6 +29,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(BaseImageTest, cls).setUpClass()
         cls.created_images = []
         cls._interface = 'json'
         cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index d3fa763..2a3b3f7 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -47,6 +47,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(BaseNetworkTest, cls).setUpClass()
         os = clients.Manager()
         cls.network_cfg = os.config.network
         if not cls.config.service_available.neutron:
@@ -64,6 +65,7 @@
             cls.client.delete_subnet(subnet['id'])
         for network in cls.networks:
             cls.client.delete_network(network['id'])
+        super(BaseNetworkTest, cls).tearDownClass()
 
     @classmethod
     def create_network(cls, network_name=None):
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 5a1fb5a..820328c 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -26,6 +26,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(BaseObjectTest, cls).setUpClass()
         if not cls.config.service_available.swift:
             skip_msg = ("%s skipped as swift is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 8b9fc8c..eaaed39 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -31,6 +31,7 @@
     @classmethod
     def tearDownClass(cls):
         cls.delete_containers(cls.containers)
+        super(ContainerTest, cls).tearDownClass()
 
     @attr(type='smoke')
     def test_create_container(self):
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 5de4df0..d18c2ad 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -50,6 +50,7 @@
     def tearDownClass(cls):
         for client in cls.clients.values():
             cls.delete_containers(cls.containers, client[0], client[1])
+        super(ContainerSyncTest, cls).tearDownClass()
 
     @testtools.skip('Until Bug #1093743 is resolved.')
     @attr(type='gate')
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index b546cec..8703480 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -41,6 +41,7 @@
         NotFound exception and also non empty container cannot be deleted.
         """
         cls.delete_containers([cls.container_name])
+        super(ObjectExpiryTest, cls).tearDownClass()
 
     @testtools.skip('Until Bug #1069849 is resolved.')
     @attr(type='gate')
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index c8d9965..c599562 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -47,6 +47,7 @@
         cls.delete_containers(cls.containers)
         # delete the user setup created
         cls.data.teardown_all()
+        super(ObjectTest, cls).tearDownClass()
 
     @attr(type='smoke')
     def test_create_object(self):
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index cda3e4f..2b93c32 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -29,6 +29,7 @@
     @classmethod
     def tearDownClass(cls):
         cls.delete_containers(cls.containers)
+        super(ContainerTest, cls).tearDownClass()
 
     def assertContainer(self, container, count, byte, versioned):
         resp, _ = self.container_client.list_container_metadata(container)
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index d06d942..745dd87 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -28,7 +28,7 @@
 
     @classmethod
     def setUpClass(cls):
-
+        super(BaseOrchestrationTest, cls).setUpClass()
         os = clients.OrchestrationManager()
         cls.orchestration_cfg = os.config.orchestration
         if not os.config.service_available.heat:
@@ -107,6 +107,7 @@
     def tearDownClass(cls):
         cls.clear_stacks()
         cls.clear_keypairs()
+        super(BaseOrchestrationTest, cls).tearDownClass()
 
     def wait_for(self, condition):
         """Repeatedly calls condition() until a timeout."""
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 52ab5b7..7781647 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -31,6 +31,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(BaseVolumeTest, cls).setUpClass()
         cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
 
         if not cls.config.service_available.cinder:
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 4447da0..d0f0127 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -150,6 +150,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(OfficialClientTest, cls).setUpClass()
         cls.isolated_creds = isolated_creds.IsolatedCreds(
             __name__, tempest_client=False)
         if cls.config.compute.allow_tenant_isolation:
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index 6ba31ea..3a8986c 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -30,6 +30,8 @@
     "http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1"
 XMLNS_OS_FLV_ACCESS = \
     "http://docs.openstack.org/compute/ext/flavor_access/api/v1.1"
+XMLNS_OS_FLV_WITH_EXT_SPECS = \
+    "http://docs.openstack.org/compute/ext/flavor_with_extra_specs/api/v2.0"
 
 
 class FlavorsClientXML(RestClientXML):
@@ -49,6 +51,11 @@
             if k == '{%s}ephemeral' % XMLNS_OS_FLV_EXT_DATA:
                 k = 'OS-FLV-EXT-DATA:ephemeral'
 
+            if k == '{%s}extra_specs' % XMLNS_OS_FLV_WITH_EXT_SPECS:
+                k = 'OS-FLV-WITH-EXT-SPECS:extra_specs'
+                flavor[k] = dict(v)
+                continue
+
             try:
                 v = int(v)
             except ValueError:
diff --git a/tempest/test.py b/tempest/test.py
index 0cd0b08..7787790 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -15,6 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import atexit
 import os
 import time
 
@@ -91,6 +92,17 @@
         LOG.info("Overriding skipException to nose SkipTest")
         testtools.TestCase.skipException = nose.plugins.skip.SkipTest
 
+at_exit_set = set()
+
+
+def validate_tearDownClass():
+    if at_exit_set:
+        raise RuntimeError("tearDownClass does not calls the super's"
+                           "tearDownClass in these classes: "
+                           + str(at_exit_set))
+
+atexit.register(validate_tearDownClass)
+
 
 class BaseTestCase(testtools.TestCase,
                    testtools.testcase.WithAttributes,
@@ -98,29 +110,43 @@
 
     config = config.TempestConfig()
 
+    setUpClassCalled = False
+
     @classmethod
     def setUpClass(cls):
         if hasattr(super(BaseTestCase, cls), 'setUpClass'):
             super(BaseTestCase, cls).setUpClass()
+        cls.setUpClassCalled = True
 
-    def setUp(cls):
-        super(BaseTestCase, cls).setUp()
+    @classmethod
+    def tearDownClass(cls):
+        at_exit_set.remove(cls)
+        if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
+            super(BaseTestCase, cls).tearDownClass()
+
+    def setUp(self):
+        super(BaseTestCase, self).setUp()
+        if not self.setUpClassCalled:
+            raise RuntimeError("setUpClass does not calls the super's"
+                               "setUpClass in the "
+                               + self.__class__.__name__)
+        at_exit_set.add(self.__class__)
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
             test_timeout = int(test_timeout)
         except ValueError:
             test_timeout = 0
         if test_timeout > 0:
-            cls.useFixture(fixtures.Timeout(test_timeout, gentle=True))
+            self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
 
         if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
                 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
-            stdout = cls.useFixture(fixtures.StringStream('stdout')).stream
-            cls.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
+            stdout = self.useFixture(fixtures.StringStream('stdout')).stream
+            self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
         if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
                 os.environ.get('OS_STDERR_CAPTURE') == '1'):
-            stderr = cls.useFixture(fixtures.StringStream('stderr')).stream
-            cls.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
+            stderr = self.useFixture(fixtures.StringStream('stderr')).stream
+            self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
 
     @classmethod
     def _get_identity_admin_client(cls):
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 8812a10..e0c9f06 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -197,6 +197,7 @@
 
     @classmethod
     def setUpClass(cls):
+        super(BotoTestCase, cls).setUpClass()
         # The trash contains cleanup functions and paramaters in tuples
         # (function, *args, **kwargs)
         cls._resource_trash_bin = {}
@@ -261,6 +262,10 @@
                 LOG.exception(exc)
             finally:
                 del cls._resource_trash_bin[key]
+        super(BotoTestCase, cls).tearDownClass()
+        # NOTE(afazekas): let the super called even on exceptions
+        # The real exceptions already logged, if the super throws another,
+        # does not causes hidden issues
         if fail_count:
             raise exceptions.TearDownException(num=fail_count)