Merge "Skip secgroup invalid name/desc tests until bug 1161411 is fixed"
diff --git a/doc/source/index.rst b/doc/source/index.rst
index f012097..00c4e9a 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -33,6 +33,13 @@
    field_guide/thirdparty
    field_guide/whitebox
 
+------------------
+API and test cases
+------------------
+.. toctree::
+   :maxdepth: 1
+
+   api/modules
 
 ==================
 Indices and tables
diff --git a/etc/logging.conf.sample b/etc/logging.conf.sample
index 685dd36..3b468f1 100644
--- a/etc/logging.conf.sample
+++ b/etc/logging.conf.sample
@@ -1,5 +1,5 @@
 [loggers]
-keys=root,tempest
+keys=root,tempest,tempest_stress
 
 [handlers]
 keys=file,syslog,devel
@@ -16,6 +16,11 @@
 handlers=file
 qualname=tempest
 
+[logger_tempest_stress]
+level=INFO
+handlers=file,devel
+qualname=tempest.stress
+
 [handler_file]
 class=FileHandler
 level=DEBUG
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 4f433a6..f1aaa07 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -10,6 +10,8 @@
 # lock/semaphore base directory
 lock_path=/tmp
 
+default_log_levels=tempest.stress=INFO,amqplib=WARN,sqlalchemy=WARN,boto=WARN,suds=INFO,keystone=INFO,eventlet.wsgi.server=WARN
+
 [identity]
 # This section contains configuration options that a variety of Tempest
 # test clients use when authenticating with different user/tenant
@@ -267,6 +269,8 @@
 # Number of seconds to wait while looping to check the status of a
 # container to container synchronization
 container_sync_interval = 5
+# Set to True if the Account Quota middleware is enabled
+accounts_quotas_available = True
 
 [boto]
 # This section contains configuration options used when executing tests
diff --git a/run_tests.sh b/run_tests.sh
index a645b22..f8636c1 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -11,23 +11,22 @@
   echo "  -u, --update             Update the virtual environment with any newer package versions"
   echo "  -s, --smoke              Only run smoke tests"
   echo "  -w, --whitebox           Only run whitebox tests"
-  echo "  -t, --with-testr         Run using testr instead of nose"
+  echo "  -t, --parallel           Run testr parallel"
   echo "  -c, --nova-coverage      Enable Nova coverage collection"
   echo "  -C, --config             Config file location"
   echo "  -p, --pep8               Just run pep8"
   echo "  -h, --help               Print this usage message"
   echo "  -d, --debug              Debug this script -- set -o xtrace"
-  echo "  -S, --stdout             Don't capture stdout"
   echo "  -l, --logging            Enable logging"
   echo "  -L, --logging-config     Logging config file location.  Default is etc/logging.conf"
-  echo "  -- [NOSEOPTIONS]         After the first '--' you can pass arbitrary arguments to nosetests "
+  echo "  -- [TESTROPTIONS]        After the first '--' you can pass arbitrary arguments to testr "
 }
 
-noseargs=""
+testrargs=""
 just_pep8=0
 venv=.venv
 with_venv=tools/with_venv.sh
-with_testr=0
+parallel=0
 always_venv=0
 never_venv=0
 no_site_packages=0
@@ -39,7 +38,7 @@
 logging=0
 logging_config=etc/logging.conf
 
-if ! options=$(getopt -o VNnfuswtcphdSC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,whitebox,with-testr,nova-coverage,pep8,help,debug,stdout,config:,logging,logging-config: -- "$@")
+if ! options=$(getopt -o VNnfuswtcphdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,whitebox,parallel,nova-coverage,pep8,help,debug,config:,logging,logging-config: -- "$@")
 then
     # parse error
     usage
@@ -60,14 +59,13 @@
     -c|--nova-coverage) let nova_coverage=1;;
     -C|--config) config_file=$2; shift;;
     -p|--pep8) let just_pep8=1;;
-    -s|--smoke) noseargs="$noseargs --attr=type=smoke";;
-    -w|--whitebox) noseargs="$noseargs --attr=type=whitebox";;
-    -t|--with-testr) with_testr=1;;
-    -S|--stdout) noseargs="$noseargs -s";;
+    -s|--smoke) testrargs="$testrargs smoke";;
+    -w|--whitebox) testrargs="$testrargs whitebox";;
+    -t|--parallel) parallel=1;;
     -l|--logging) logging=1;;
     -L|--logging-config) logging_config=$2; shift;;
-    --) [ "yes" == "$first_uu" ] || noseargs="$noseargs $1"; first_uu=no  ;;
-    *) noseargs="$noseargs $1"
+    --) [ "yes" == "$first_uu" ] || testrargs="$testrargs $1"; first_uu=no  ;;
+    *) testrargs="$testrargs $1"
   esac
   shift
 done
@@ -90,24 +88,10 @@
 
 cd `dirname "$0"`
 
-export NOSE_WITH_OPENSTACK=1
-export NOSE_OPENSTACK_COLOR=1
-export NOSE_OPENSTACK_RED=15.00
-export NOSE_OPENSTACK_YELLOW=3.00
-export NOSE_OPENSTACK_SHOW_ELAPSED=1
-export NOSE_OPENSTACK_STDOUT=1
-
 if [ $no_site_packages -eq 1 ]; then
   installvenvopts="--no-site-packages"
 fi
 
-# only add tempest default if we don't specify a test
-if [[ "x$noseargs" =~ "tempest" ]]; then
-  noseargs="$noseargs"
-else
-  noseargs="$noseargs tempest"
-fi
-
 function testr_init {
   if [ ! -d .testrepository ]; then
       ${wrapper} testr init
@@ -115,12 +99,12 @@
 }
 
 function run_tests {
-  if [ $with_testr -eq 1 ]; then
-      testr_init
-      ${wrapper} find . -type f -name "*.pyc" -delete
-      ${wrapper} testr run --parallel --subunit $noseargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
+  testr_init
+  ${wrapper} find . -type f -name "*.pyc" -delete
+  if [ $parallel -eq 1 ]; then
+      ${wrapper} testr run --parallel --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
   else
-      ${wrapper} $NOSETESTS
+      ${wrapper} testr run --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
   fi
 }
 
@@ -139,8 +123,6 @@
   ${wrapper} python tools/tempest_coverage.py -c report
 }
 
-NOSETESTS="nosetests $noseargs"
-
 if [ $never_venv -eq 0 ]
 then
   # Remove the virtual environment if --force used
@@ -187,7 +169,7 @@
     run_coverage_report
 fi
 
-if [ -z "$noseargs" ]; then
+if [ -z "$testrargs" ]; then
     run_pep8
 fi
 
diff --git a/setup.cfg b/setup.cfg
index 3b13b60..7cfc4ce 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -35,3 +35,6 @@
 #                coverage http://pypi.python.org/pypi/coverage
 #                openstack-nose https://github.com/openstack-dev/openstack-nose
 verbosity=2
+
+[pbr]
+autodoc_tree_index_modules=true
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 64f1854..14eced2 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -89,23 +89,6 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_image,
                           self.server['id'], snapshot_name, meta)
 
-    @testtools.skipUnless(compute.MULTI_USER,
-                          'Need multiple users for this test.')
-    @attr(type=['negative', 'gate'])
-    def test_delete_image_of_another_tenant(self):
-        # Return an error while trying to delete another tenant's image
-        self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
-        snapshot_name = rand_name('test-snap-')
-        resp, body = self.client.create_image(self.server['id'], snapshot_name)
-        image_id = parse_image_id(resp['location'])
-        self.image_ids.append(image_id)
-        self.client.wait_for_image_resp_code(image_id, 200)
-        self.client.wait_for_image_status(image_id, 'ACTIVE')
-
-        # Delete image
-        self.assertRaises(exceptions.NotFound,
-                          self.alt_client.delete_image, image_id)
-
     def _get_default_flavor_disk_size(self, flavor_id):
         resp, flavor = self.flavors_client.get_flavor_details(flavor_id)
         return flavor['disk']
@@ -144,16 +127,6 @@
         self.assertEqual('204', resp['status'])
         self.client.wait_for_resource_deletion(image_id)
 
-    @testtools.skipUnless(compute.MULTI_USER,
-                          'Need multiple users for this test.')
-    @attr(type=['negative', 'gate'])
-    def test_create_image_for_server_in_another_tenant(self):
-        # Creating image of another tenant's server should be return error
-
-        snapshot_name = rand_name('test-snap-')
-        self.assertRaises(exceptions.NotFound, self.alt_client.create_image,
-                          self.server['id'], snapshot_name)
-
     @attr(type=['negative', 'gate'])
     def test_create_second_image_when_first_image_is_being_saved(self):
         # Disallow creating another image when first image is being saved
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 5bd1e7a..30db206 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -107,6 +107,8 @@
                          "The fetched Security Group is different "
                          "from the created Group")
 
+    @testtools.skipIf(config.TempestConfig().service_available.neutron,
+                      "Skipped until the Bug #1182384 is resolved")
     @attr(type=['negative', 'gate'])
     def test_security_group_get_nonexistant_group(self):
         # Negative test:Should not be able to GET the details
@@ -195,6 +197,8 @@
                           self.client.delete_security_group,
                           default_security_group_id)
 
+    @testtools.skipIf(config.TempestConfig().service_available.neutron,
+                      "Skipped until the Bug #1182384 is resolved")
     @attr(type=['negative', 'gate'])
     def test_delete_nonexistant_security_group(self):
         # Negative test:Deletion of a nonexistant Security Group should Fail
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 703f143..e09a23f 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -255,6 +255,20 @@
         self.assertRaises(exceptions.NotFound, self.client.get_server,
                           '999erra43')
 
+    @attr(type=['negative', 'gate'])
+    def test_stop_non_existent_server(self):
+        # Stop a non existent server
+        non_exist_id = rand_name('non-existent-server')
+        self.assertRaises(exceptions.NotFound, self.servers_client.stop,
+                          non_exist_id)
+
+    @attr(type=['negative', 'gate'])
+    def test_pause_non_existent_server(self):
+        # pause a non existent server
+        non_exist_id = rand_name('non-existent-server')
+        self.assertRaises(exceptions.NotFound, self.client.pause_server,
+                          non_exist_id)
+
 
 class ServersNegativeTestXML(ServersNegativeTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
new file mode 100644
index 0000000..bc050dc
--- /dev/null
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -0,0 +1,115 @@
+# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+#
+# Author: Joe H. Rahme <joe.hakim.rahme@enovance.com>
+#
+# 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 testtools
+
+from tempest.api.object_storage import base
+from tempest import clients
+from tempest.common.utils.data_utils import arbitrary_string
+from tempest.common.utils.data_utils import rand_name
+import tempest.config
+from tempest import exceptions
+from tempest.test import attr
+
+
+class AccountQuotasTest(base.BaseObjectTest):
+    accounts_quotas_available = \
+        tempest.config.TempestConfig().object_storage.accounts_quotas_available
+
+    @classmethod
+    def setUpClass(cls):
+        super(AccountQuotasTest, cls).setUpClass()
+        cls.container_name = rand_name(name="TestContainer")
+        cls.container_client.create_container(cls.container_name)
+
+        cls.data.setup_test_user()
+
+        cls.os_reselleradmin = clients.Manager(
+            cls.data.test_user,
+            cls.data.test_password,
+            cls.data.test_tenant)
+
+        # Retrieve the ResellerAdmin role id
+        reseller_role_id = None
+        try:
+            _, roles = cls.os_admin.identity_client.list_roles()
+            reseller_role_id = next(r['id'] for r in roles if r['name']
+                                    == 'ResellerAdmin')
+        except StopIteration:
+            msg = "No ResellerAdmin role found"
+            raise exceptions.NotFound(msg)
+
+        # Retrieve the ResellerAdmin tenant id
+        _, users = cls.os_admin.identity_client.get_users()
+        reseller_user_id = next(usr['id'] for usr in users if usr['name']
+                                == cls.data.test_user)
+
+        # Retrieve the ResellerAdmin tenant id
+        _, tenants = cls.os_admin.identity_client.list_tenants()
+        reseller_tenant_id = next(tnt['id'] for tnt in tenants if tnt['name']
+                                  == cls.data.test_tenant)
+
+        # Assign the newly created user the appropriate ResellerAdmin role
+        cls.os_admin.identity_client.assign_user_role(
+            reseller_tenant_id,
+            reseller_user_id,
+            reseller_role_id)
+
+        # Retrieve a ResellerAdmin auth token and use it to set a quota
+        # on the client's account
+        cls.reselleradmin_token = cls.token_client.get_token(
+            cls.data.test_user,
+            cls.data.test_password,
+            cls.data.test_tenant)
+
+        headers = {"X-Auth-Token": cls.reselleradmin_token,
+                   "X-Account-Meta-Quota-Bytes": "20"}
+
+        cls.os.custom_account_client.request("POST", "", headers, "")
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.delete_containers([cls.container_name])
+        cls.data.teardown_all()
+
+        # remove the quota from the container
+        headers = {"X-Auth-Token": cls.reselleradmin_token,
+                   "X-Remove-Account-Meta-Quota-Bytes": "x"}
+
+        cls.os.custom_account_client.request("POST", "", headers, "")
+
+        super(AccountQuotasTest, cls).tearDownClass()
+
+    @testtools.skipIf(not accounts_quotas_available,
+                      "Account Quotas middleware not available")
+    @attr(type="smoke")
+    def test_upload_valid_object(self):
+        object_name = rand_name(name="TestObject")
+        data = arbitrary_string()
+        resp, _ = self.object_client.create_object(self.container_name,
+                                                   object_name, data)
+
+        self.assertEqual(resp["status"], "201")
+
+    @testtools.skipIf(not accounts_quotas_available,
+                      "Account Quotas middleware not available")
+    @attr(type=["negative", "smoke"])
+    def test_upload_large_object(self):
+        object_name = rand_name(name="TestObject")
+        data = arbitrary_string(30)
+        self.assertRaises(exceptions.OverLimit,
+                          self.object_client.create_object,
+                          self.container_name, object_name, data)
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
new file mode 100644
index 0000000..1a31b91
--- /dev/null
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -0,0 +1,225 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack, LLC
+# All Rights Reserved.
+#
+#    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 tempest.api.object_storage import base
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+from tempest.test import HTTP_SUCCESS
+
+
+class ObjectTestACLs(base.BaseObjectTest):
+    @classmethod
+    def setUpClass(cls):
+        super(ObjectTestACLs, cls).setUpClass()
+        cls.data.setup_test_user()
+        cls.new_token = cls.token_client.get_token(cls.data.test_user,
+                                                   cls.data.test_password,
+                                                   cls.data.test_tenant)
+        cls.custom_headers = {'X-Auth-Token': cls.new_token}
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.data.teardown_all()
+        super(ObjectTestACLs, cls).tearDownClass()
+
+    def setUp(self):
+        super(ObjectTestACLs, self).setUp()
+        self.container_name = rand_name(name='TestContainer')
+        self.container_client.create_container(self.container_name)
+
+    def tearDown(self):
+        self.delete_containers([self.container_name])
+        super(ObjectTestACLs, self).tearDown()
+
+    @attr(type=['negative', 'gate'])
+    def test_write_object_without_using_creds(self):
+        # trying to create object with empty headers
+        # X-Auth-Token is not provided
+        object_name = rand_name(name='Object')
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.create_object,
+                          self.container_name, object_name, 'data')
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_object_without_using_creds(self):
+        # create object
+        object_name = rand_name(name='Object')
+        resp, _ = self.object_client.create_object(self.container_name,
+                                                   object_name, 'data')
+        # trying to delete object with empty headers
+        # X-Auth-Token is not provided
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.delete_object,
+                          self.container_name, object_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_write_object_with_non_authorized_user(self):
+        # attempt to upload another file using non-authorized user
+        # User provided token is forbidden. ACL are not set
+        object_name = rand_name(name='Object')
+        # trying to create object with non-authorized user
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.create_object,
+                          self.container_name, object_name, 'data',
+                          metadata=self.custom_headers)
+
+    @attr(type=['negative', 'gate'])
+    def test_read_object_with_non_authorized_user(self):
+        # attempt to read object using non-authorized user
+        # User provided token is forbidden. ACL are not set
+        object_name = rand_name(name='Object')
+        resp, _ = self.object_client.create_object(
+            self.container_name, object_name, 'data')
+        self.assertEqual(resp['status'], '201')
+        # trying to get object with non authorized user token
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.get_object,
+                          self.container_name, object_name,
+                          metadata=self.custom_headers)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_object_with_non_authorized_user(self):
+        # attempt to delete object using non-authorized user
+        # User provided token is forbidden. ACL are not set
+        object_name = rand_name(name='Object')
+        resp, _ = self.object_client.create_object(
+            self.container_name, object_name, 'data')
+        self.assertEqual(resp['status'], '201')
+        # trying to delete object with non-authorized user token
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.delete_object,
+                          self.container_name, object_name,
+                          metadata=self.custom_headers)
+
+    @attr(type=['negative', 'smoke'])
+    def test_read_object_without_rights(self):
+        # attempt to read object using non-authorized user
+        # update X-Container-Read metadata ACL
+        cont_headers = {'X-Container-Read': 'badtenant:baduser'}
+        resp_meta, body = self.container_client.update_container_metadata(
+            self.container_name, metadata=cont_headers,
+            metadata_prefix='')
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        # create object
+        object_name = rand_name(name='Object')
+        resp, _ = self.object_client.create_object(self.container_name,
+                                                   object_name, 'data')
+        self.assertEqual(resp['status'], '201')
+        # Trying to read the object without rights
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.get_object,
+                          self.container_name, object_name,
+                          metadata=self.custom_headers)
+
+    @attr(type=['negative', 'smoke'])
+    def test_write_object_without_rights(self):
+        # attempt to write object using non-authorized user
+        # update X-Container-Write metadata ACL
+        cont_headers = {'X-Container-Write': 'badtenant:baduser'}
+        resp_meta, body = self.container_client.update_container_metadata(
+            self.container_name, metadata=cont_headers,
+            metadata_prefix='')
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        # Trying to write the object without rights
+        object_name = rand_name(name='Object')
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.create_object,
+                          self.container_name,
+                          object_name, 'data',
+                          metadata=self.custom_headers)
+
+    @attr(type='smoke')
+    def test_read_object_with_rights(self):
+        # attempt to read object using authorized user
+        # update X-Container-Read metadata ACL
+        cont_headers = {'X-Container-Read':
+                        self.data.test_tenant + ':' + self.data.test_user}
+        resp_meta, body = self.container_client.update_container_metadata(
+            self.container_name, metadata=cont_headers,
+            metadata_prefix='')
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        # create object
+        object_name = rand_name(name='Object')
+        resp, _ = self.object_client.create_object(self.container_name,
+                                                   object_name, 'data')
+        self.assertEqual(resp['status'], '201')
+        # Trying to read the object with rights
+        resp, _ = self.custom_object_client.get_object(
+            self.container_name, object_name,
+            metadata=self.custom_headers)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+    @attr(type='smoke')
+    def test_write_object_with_rights(self):
+        # attempt to write object using authorized user
+        # update X-Container-Write metadata ACL
+        cont_headers = {'X-Container-Write':
+                        self.data.test_tenant + ':' + self.data.test_user}
+        resp_meta, body = self.container_client.update_container_metadata(
+            self.container_name, metadata=cont_headers,
+            metadata_prefix='')
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        # Trying to write the object with rights
+        object_name = rand_name(name='Object')
+        resp, _ = self.custom_object_client.create_object(
+            self.container_name,
+            object_name, 'data',
+            metadata=self.custom_headers)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+    @attr(type=['negative', 'smoke'])
+    def test_write_object_without_write_rights(self):
+        # attempt to write object using non-authorized user
+        # update X-Container-Read and X-Container-Write metadata ACL
+        cont_headers = {'X-Container-Read':
+                        self.data.test_tenant + ':' + self.data.test_user,
+                        'X-Container-Write': ''}
+        resp_meta, body = self.container_client.update_container_metadata(
+            self.container_name, metadata=cont_headers,
+            metadata_prefix='')
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        # Trying to write the object without write rights
+        object_name = rand_name(name='Object')
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.create_object,
+                          self.container_name,
+                          object_name, 'data',
+                          metadata=self.custom_headers)
+
+    @attr(type=['negative', 'smoke'])
+    def test_delete_object_without_write_rights(self):
+        # attempt to delete object using non-authorized user
+        # update X-Container-Read and X-Container-Write metadata ACL
+        cont_headers = {'X-Container-Read':
+                        self.data.test_tenant + ':' + self.data.test_user,
+                        'X-Container-Write': ''}
+        resp_meta, body = self.container_client.update_container_metadata(
+            self.container_name, metadata=cont_headers,
+            metadata_prefix='')
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        # create object
+        object_name = rand_name(name='Object')
+        resp, _ = self.object_client.create_object(self.container_name,
+                                                   object_name, 'data')
+        self.assertEqual(resp['status'], '201')
+        # Trying to delete the object without write rights
+        self.assertRaises(exceptions.Unauthorized,
+                          self.custom_object_client.delete_object,
+                          self.container_name,
+                          object_name,
+                          metadata=self.custom_headers)
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 6136216..c8d9965 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -21,7 +21,6 @@
 from tempest.api.object_storage import base
 from tempest.common.utils.data_utils import arbitrary_string
 from tempest.common.utils.data_utils import rand_name
-from tempest import exceptions
 from tempest.test import attr
 from tempest.test import HTTP_SUCCESS
 
@@ -230,74 +229,6 @@
             self.fail("Got exception :%s ; while copying"
                       " object across containers" % e)
 
-    @attr(type=['negative', 'gate'])
-    def test_write_object_without_using_creds(self):
-        # trying to create object with empty headers
-        object_name = rand_name(name='Object')
-        data = arbitrary_string(size=len(object_name),
-                                base_text=object_name)
-        obj_headers = {'Content-Type': 'application/json',
-                       'Accept': 'application/json'}
-        self.assertRaises(exceptions.Unauthorized,
-                          self.custom_object_client.create_object,
-                          self.container_name, object_name, data,
-                          metadata=obj_headers)
-
-    @attr(type=['negative', 'gate'])
-    def test_delete_object_without_using_creds(self):
-        # create object
-        object_name = rand_name(name='Object')
-        data = arbitrary_string(size=len(object_name),
-                                base_text=object_name)
-        resp, _ = self.object_client.create_object(self.container_name,
-                                                   object_name, data)
-        # trying to delete object with empty headers
-        self.assertRaises(exceptions.Unauthorized,
-                          self.custom_object_client.delete_object,
-                          self.container_name, object_name)
-
-    @attr(type=['negative', 'gate'])
-    def test_write_object_with_non_authorized_user(self):
-        # attempt to upload another file using non-authorized user
-        object_name = rand_name(name='Object')
-        data = arbitrary_string(size=len(object_name) * 5,
-                                base_text=object_name)
-
-        # trying to create object with non-authorized user
-        self.assertRaises(exceptions.Unauthorized,
-                          self.custom_object_client.create_object,
-                          self.container_name, object_name, data,
-                          metadata=self.custom_headers)
-
-    @attr(type=['negative', 'gate'])
-    def test_read_object_with_non_authorized_user(self):
-        object_name = rand_name(name='Object')
-        data = arbitrary_string(size=len(object_name) * 5,
-                                base_text=object_name)
-        resp, body = self.object_client.create_object(
-            self.container_name, object_name, data)
-        self.assertEqual(resp['status'], '201')
-
-        # trying to get object with non authorized user token
-        self.assertRaises(exceptions.Unauthorized,
-                          self.custom_object_client.get_object,
-                          self.container_name, object_name,
-                          metadata=self.custom_headers)
-
-    @attr(type=['negative', 'gate'])
-    def test_delete_object_with_non_authorized_user(self):
-        object_name = rand_name(name='Object')
-        data = arbitrary_string(size=len(object_name) * 5,
-                                base_text=object_name)
-        resp, body = self.object_client.create_object(
-            self.container_name, object_name, data)
-        self.assertEqual(resp['status'], '201')
-        # trying to delete object with non-authorized user token
-        self.assertRaises(exceptions.Unauthorized,
-                          self.custom_object_client.delete_object,
-                          self.container_name, object_name,
-                          metadata=self.custom_headers)
-
     @attr(type='gate')
     def test_get_object_using_temp_url(self):
         # access object using temporary URL within expiration time
diff --git a/tempest/api/orchestration/stacks/test_instance_cfn_init.py b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
index 7897b70..fe55ecf 100644
--- a/tempest/api/orchestration/stacks/test_instance_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
@@ -145,7 +145,7 @@
                 'ImageId': cls.orchestration_cfg.image_ref
             })
 
-    @attr(type='gate')
+    @attr(type='slow')
     @testtools.skipIf(existing_keypair, 'Server ssh tests are disabled.')
     def test_can_log_into_created_server(self):
 
@@ -168,7 +168,7 @@
             server, 'ec2-user', pkey=self.keypair['private_key'])
         self.assertTrue(linux_client.can_authenticate())
 
-    @attr(type='gate')
+    @attr(type='slow')
     def test_stack_wait_condition_data(self):
 
         sid = self.stack_identifier
diff --git a/tempest/config.py b/tempest/config.py
index 19170ae..9b1a91e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -366,6 +366,9 @@
                default=5,
                help="Number of seconds to wait while looping to check the"
                     "status of a container to container synchronization"),
+    cfg.BoolOpt('accounts_quotas_available',
+                default=True,
+                help="Set to True if the Account Quota middleware is enabled"),
 ]
 
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index e785299..c95e867 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -55,13 +55,6 @@
         self.identity_client = self._get_identity_client()
         self.network_client = self._get_network_client()
         self.volume_client = self._get_volume_client()
-        self.client_attr_names = [
-            'compute_client',
-            'image_client',
-            'identity_client',
-            'network_client',
-            'volume_client'
-        ]
 
     def _get_compute_client(self, username=None, password=None,
                             tenant_name=None):
@@ -160,7 +153,7 @@
                                                 insecure=dscv)
 
 
-class OfficialClientTest(tempest.test.TestCase):
+class OfficialClientTest(tempest.test.BaseTestCase):
     """
     Official Client test base class for scenario testing.
 
@@ -173,7 +166,16 @@
      * Use only the default client tool for calling an API
     """
 
-    manager_class = OfficialClientManager
+    @classmethod
+    def setUpClass(cls):
+        cls.manager = OfficialClientManager()
+        cls.compute_client = cls.manager.compute_client
+        cls.image_client = cls.manager.image_client
+        cls.identity_client = cls.manager.identity_client
+        cls.network_client = cls.manager.network_client
+        cls.volume_client = cls.manager.volume_client
+        cls.resource_keys = {}
+        cls.os_resources = []
 
     @classmethod
     def tearDownClass(cls):
@@ -215,6 +217,52 @@
             # Block until resource deletion has completed or timed-out
             tempest.test.call_until_true(is_deletion_complete, 10, 1)
 
+    @classmethod
+    def set_resource(cls, key, thing):
+        LOG.debug("Adding %r to shared resources of %s" %
+                  (thing, cls.__name__))
+        cls.resource_keys[key] = thing
+        cls.os_resources.append(thing)
+
+    @classmethod
+    def get_resource(cls, key):
+        return cls.resource_keys[key]
+
+    @classmethod
+    def remove_resource(cls, key):
+        thing = cls.resource_keys[key]
+        cls.os_resources.remove(thing)
+        del cls.resource_keys[key]
+
+    def status_timeout(self, things, thing_id, expected_status):
+        """
+        Given a thing and an expected status, do a loop, sleeping
+        for a configurable amount of time, checking for the
+        expected status to show. At any time, if the returned
+        status of the thing is ERROR, fail out.
+        """
+        def check_status():
+            # python-novaclient has resources available to its client
+            # that all implement a get() method taking an identifier
+            # for the singular resource to retrieve.
+            thing = things.get(thing_id)
+            new_status = thing.status
+            if new_status == 'ERROR':
+                self.fail("%s failed to get to expected status. "
+                          "In ERROR state."
+                          % thing)
+            elif new_status == expected_status:
+                return True  # All good.
+            LOG.debug("Waiting for %s to get to %s status. "
+                      "Currently in %s status",
+                      thing, expected_status, new_status)
+        if not tempest.test.call_until_true(
+            check_status,
+            self.config.compute.build_timeout,
+            self.config.compute.build_interval):
+            self.fail("Timed out waiting for thing %s to become %s"
+                      % (thing_id, expected_status))
+
 
 class NetworkScenarioTest(OfficialClientTest):
     """
diff --git a/tempest/scenario/test_volume_snapshot_pattern.py b/tempest/scenario/test_volume_snapshot_pattern.py
new file mode 100644
index 0000000..4d8a400
--- /dev/null
+++ b/tempest/scenario/test_volume_snapshot_pattern.py
@@ -0,0 +1,122 @@
+# 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.
+
+from tempest.openstack.common import log as logging
+
+from tempest.common.utils.data_utils import rand_name
+from tempest.scenario import manager
+
+LOG = logging.getLogger(__name__)
+
+
+class TestVolumeSnapshotPattern(manager.OfficialClientTest):
+
+    """
+    This test case attempts to reproduce the following steps:
+
+     * Create in Cinder some bootable volume importing a Glance image
+     * Boot an instance from the bootable volume
+     * Create a volume snapshot while the instance is running
+     * Boot an additional instance from the new snapshot based volume
+    """
+
+    def _create_volume_from_image(self):
+        img_uuid = self.config.compute.image_ref
+        vol_name = rand_name('volume-origin')
+        vol = self.volume_client.volumes.create(size=1,
+                                                display_name=vol_name,
+                                                imageRef=img_uuid)
+        self.set_resource(vol.id, vol)
+        self.status_timeout(self.volume_client.volumes,
+                            vol.id,
+                            'available')
+        return vol
+
+    def _boot_instance_from_volume(self, vol_id):
+        # NOTE(gfidente): the img_uuid here is only needed because
+        # the novaclient requires it to be passed as arg
+        img_uuid = self.config.compute.image_ref
+        i_name = rand_name('instance')
+        flavor_id = self.config.compute.flavor_ref
+        # NOTE(gfidente): the syntax for block_device_mapping is
+        # dev_name=id:type:size:delete_on_terminate
+        # where type needs to be "snap" if the server is booted
+        # from a snapshot, size instead can be safely left empty
+        bd_map = {
+            'vda': vol_id + ':::0'
+        }
+        create_kwargs = {
+            'block_device_mapping': bd_map
+        }
+        i = self.compute_client.servers.create(name=i_name,
+                                               image=img_uuid,
+                                               flavor=flavor_id,
+                                               **create_kwargs)
+        self.set_resource(i.id, i)
+        self.status_timeout(self.compute_client.servers,
+                            i.id,
+                            'ACTIVE')
+        return i
+
+    def _create_snapshot_from_volume(self, vol_id):
+        volume_snapshots = self.volume_client.volume_snapshots
+        snap_name = rand_name('snapshot')
+        snap = volume_snapshots.create(volume_id=vol_id,
+                                       force=True,
+                                       display_name=snap_name)
+        self.set_resource(snap.id, snap)
+        self.status_timeout(volume_snapshots,
+                            snap.id,
+                            'available')
+        return snap
+
+    def _create_volume_from_snapshot(self, snap_id):
+        vol_name = rand_name('volume')
+        vol = self.volume_client.volumes.create(size=1,
+                                                display_name=vol_name,
+                                                snapshot_id=snap_id)
+        self.set_resource(vol.id, vol)
+        self.status_timeout(self.volume_client.volumes,
+                            vol.id,
+                            'available')
+        return vol
+
+    def _stop_instances(self, instances):
+        # NOTE(gfidente): two loops so we do not wait for the status twice
+        for i in instances:
+            self.compute_client.servers.stop(i)
+        for i in instances:
+            self.status_timeout(self.compute_client.servers,
+                                i.id,
+                                'SHUTOFF')
+
+    def _detach_volumes(self, volumes):
+        # NOTE(gfidente): two loops so we do not wait for the status twice
+        for v in volumes:
+            self.volume_client.volumes.detach(v)
+        for v in volumes:
+            self.status_timeout(self.volume_client.volumes,
+                                v.id,
+                                'available')
+
+    def test_volume_snapshot_pattern(self):
+        volume_origin = self._create_volume_from_image()
+        i_origin = self._boot_instance_from_volume(volume_origin.id)
+        snapshot = self._create_snapshot_from_volume(volume_origin.id)
+        volume = self._create_volume_from_snapshot(snapshot.id)
+        i = self._boot_instance_from_volume(volume.id)
+        # NOTE(gfidente): ensure resources are in clean state for
+        # deletion operations to succeed
+        self._stop_instances([i_origin, i])
+        self._detach_volumes([volume_origin, volume])
diff --git a/tempest/stress/README.rst b/tempest/stress/README.rst
index 31642b0..7c180f6 100644
--- a/tempest/stress/README.rst
+++ b/tempest/stress/README.rst
@@ -23,7 +23,8 @@
 	target_controller = "hostname or ip of controller node (for nova-manage)
 	log_check_interval = "time between checking logs for errors (default 60s)"
 
-
+To activate logging on your console please make sure that you activate `use_stderr`
+in tempest.conf or use the default `logging.conf.sample` file.
 
 Running the sample test
 -----------------------
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
index bfcf34f..1bd9485 100644
--- a/tempest/stress/cleanup.py
+++ b/tempest/stress/cleanup.py
@@ -17,13 +17,16 @@
 #    limitations under the License.
 
 from tempest import clients
+from tempest.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
 
 
-def cleanup(logger):
+def cleanup():
     admin_manager = clients.AdminManager()
 
     _, body = admin_manager.servers_client.list_servers({"all_tenants": True})
-    logger.debug("Cleanup::remove %s servers" % len(body['servers']))
+    LOG.info("Cleanup::remove %s servers" % len(body['servers']))
     for s in body['servers']:
         try:
             admin_manager.servers_client.delete_server(s['id'])
@@ -37,7 +40,7 @@
             pass
 
     _, keypairs = admin_manager.keypairs_client.list_keypairs()
-    logger.debug("Cleanup::remove %s keypairs" % len(keypairs))
+    LOG.info("Cleanup::remove %s keypairs" % len(keypairs))
     for k in keypairs:
         try:
             admin_manager.keypairs_client.delete_keypair(k['name'])
@@ -45,7 +48,7 @@
             pass
 
     _, floating_ips = admin_manager.floating_ips_client.list_floating_ips()
-    logger.debug("Cleanup::remove %s floating ips" % len(floating_ips))
+    LOG.info("Cleanup::remove %s floating ips" % len(floating_ips))
     for f in floating_ips:
         try:
             admin_manager.floating_ips_client.delete_floating_ip(f['id'])
@@ -53,13 +56,13 @@
             pass
 
     _, users = admin_manager.identity_client.get_users()
-    logger.debug("Cleanup::remove %s users" % len(users))
+    LOG.info("Cleanup::remove %s users" % len(users))
     for user in users:
         if user['name'].startswith("stress_user"):
             admin_manager.identity_client.delete_user(user['id'])
 
     _, tenants = admin_manager.identity_client.list_tenants()
-    logger.debug("Cleanup::remove %s tenants" % len(tenants))
+    LOG.info("Cleanup::remove %s tenants" % len(tenants))
     for tenant in tenants:
         if tenant['name'].startswith("stress_tenant"):
             admin_manager.identity_client.delete_tenant(tenant['id'])
@@ -69,7 +72,7 @@
 
     _, snaps = admin_manager.snapshots_client.\
         list_snapshots({"all_tenants": True})
-    logger.debug("Cleanup::remove %s snapshots" % len(snaps))
+    LOG.info("Cleanup::remove %s snapshots" % len(snaps))
     for v in snaps:
         try:
             admin_manager.snapshots_client.\
@@ -85,7 +88,7 @@
             pass
 
     _, vols = admin_manager.volumes_client.list_volumes({"all_tenants": True})
-    logger.debug("Cleanup::remove %s volumes" % len(vols))
+    LOG.info("Cleanup::remove %s volumes" % len(vols))
     for v in vols:
         try:
             admin_manager.volumes_client.\
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index d9b95e0..efc57a9 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -12,7 +12,6 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-import logging
 import multiprocessing
 import signal
 import time
@@ -22,30 +21,12 @@
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.openstack.common import importutils
+from tempest.openstack.common import log as logging
 from tempest.stress import cleanup
 
 admin_manager = clients.AdminManager()
 
-# setup logging to file
-logging.basicConfig(
-    format='%(asctime)s %(process)d %(name)-20s %(levelname)-8s %(message)s',
-    datefmt='%m-%d %H:%M:%S',
-    filename="stress.debug.log",
-    filemode="w",
-    level=logging.DEBUG,
-)
-
-# define a Handler which writes INFO messages or higher to the sys.stdout
-_console = logging.StreamHandler()
-_console.setLevel(logging.INFO)
-# set a format which is simpler for console use
-format_str = '%(asctime)s %(process)d %(name)-20s: %(levelname)-8s %(message)s'
-_formatter = logging.Formatter(format_str)
-# tell the handler to use this format
-_console.setFormatter(_formatter)
-# add the handler to the root logger
-logger = logging.getLogger('tempest.stress')
-logger.addHandler(_console)
+LOG = logging.getLogger(__name__)
 processes = []
 
 
@@ -90,7 +71,7 @@
         if not errors:
             return None
         if len(errors) > 0:
-            logger.error('%s: %s' % (node, errors))
+            LOG.error('%s: %s' % (node, errors))
             return errors
     return None
 
@@ -147,13 +128,13 @@
                                           tenant_name=tenant_name)
 
             test_obj = importutils.import_class(test['action'])
-            test_run = test_obj(manager, logger, max_runs, stop_on_error)
+            test_run = test_obj(manager, max_runs, stop_on_error)
 
             kwargs = test.get('kwargs', {})
             test_run.setUp(**dict(kwargs.iteritems()))
 
-            logger.debug("calling Target Object %s" %
-                         test_run.__class__.__name__)
+            LOG.debug("calling Target Object %s" %
+                      test_run.__class__.__name__)
 
             mp_manager = multiprocessing.Manager()
             shared_statistic = mp_manager.dict()
@@ -208,24 +189,24 @@
     sum_fails = 0
     sum_runs = 0
 
-    logger.info("Statistics (per process):")
+    LOG.info("Statistics (per process):")
     for process in processes:
         if process['statistic']['fails'] > 0:
             had_errors = True
         sum_runs += process['statistic']['runs']
         sum_fails += process['statistic']['fails']
-        logger.info(" Process %d (%s): Run %d actions (%d failed)" %
-                    (process['p_number'],
-                     process['action'],
-                     process['statistic']['runs'],
+        LOG.info(" Process %d (%s): Run %d actions (%d failed)" %
+                 (process['p_number'],
+                  process['action'],
+                  process['statistic']['runs'],
                      process['statistic']['fails']))
-    logger.info("Summary:")
-    logger.info("Run %d actions (%d failed)" %
-                (sum_runs, sum_fails))
+    LOG.info("Summary:")
+    LOG.info("Run %d actions (%d failed)" %
+             (sum_runs, sum_fails))
 
     if not had_errors:
-        logger.info("cleaning up")
-        cleanup.cleanup(logger)
+        LOG.info("cleaning up")
+        cleanup.cleanup()
     if had_errors:
         return 1
     else:
diff --git a/tempest/stress/stressaction.py b/tempest/stress/stressaction.py
index ab09adc..3719841 100644
--- a/tempest/stress/stressaction.py
+++ b/tempest/stress/stressaction.py
@@ -17,12 +17,15 @@
 import signal
 import sys
 
+from tempest.openstack.common import log as logging
+
 
 class StressAction(object):
 
-    def __init__(self, manager, logger, max_runs=None, stop_on_error=False):
+    def __init__(self, manager, max_runs=None, stop_on_error=False):
+        full_cname = self.__module__ + "." + self.__class__.__name__
+        self.logger = logging.getLogger(full_cname)
         self.manager = manager
-        self.logger = logger
         self.max_runs = max_runs
         self.stop_on_error = stop_on_error
 
diff --git a/tempest/stress/tools/cleanup.py b/tempest/stress/tools/cleanup.py
index b6a26cd..3885ba0 100755
--- a/tempest/stress/tools/cleanup.py
+++ b/tempest/stress/tools/cleanup.py
@@ -14,15 +14,6 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-import logging
-
 from tempest.stress import cleanup
 
-_console = logging.StreamHandler()
-_console.setLevel(logging.DEBUG)
-# add the handler to the root logger
-logger = logging.getLogger('tempest.stress.cleanup')
-logger.addHandler(logging.StreamHandler())
-logger.setLevel(logging.DEBUG)
-
-cleanup.cleanup(logger)
+cleanup.cleanup()
diff --git a/tempest/test.py b/tempest/test.py
index 6c304c3..96360ff 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -244,70 +244,3 @@
         time.sleep(sleep_for)
         now = time.time()
     return False
-
-
-class TestCase(BaseTestCase):
-    """Base test case class for all Tempest tests
-
-    Contains basic setup and convenience methods
-    """
-
-    manager_class = None
-
-    @classmethod
-    def setUpClass(cls):
-        cls.manager = cls.manager_class()
-        for attr_name in cls.manager.client_attr_names:
-            # Ensure that pre-existing class attributes won't be
-            # accidentally overriden.
-            assert not hasattr(cls, attr_name)
-            client = getattr(cls.manager, attr_name)
-            setattr(cls, attr_name, client)
-        cls.resource_keys = {}
-        cls.os_resources = []
-
-    @classmethod
-    def set_resource(cls, key, thing):
-        LOG.debug("Adding %r to shared resources of %s" %
-                  (thing, cls.__name__))
-        cls.resource_keys[key] = thing
-        cls.os_resources.append(thing)
-
-    @classmethod
-    def get_resource(cls, key):
-        return cls.resource_keys[key]
-
-    @classmethod
-    def remove_resource(cls, key):
-        thing = cls.resource_keys[key]
-        cls.os_resources.remove(thing)
-        del cls.resource_keys[key]
-
-    def status_timeout(self, things, thing_id, expected_status):
-        """
-        Given a thing and an expected status, do a loop, sleeping
-        for a configurable amount of time, checking for the
-        expected status to show. At any time, if the returned
-        status of the thing is ERROR, fail out.
-        """
-        def check_status():
-            # python-novaclient has resources available to its client
-            # that all implement a get() method taking an identifier
-            # for the singular resource to retrieve.
-            thing = things.get(thing_id)
-            new_status = thing.status
-            if new_status == 'ERROR':
-                self.fail("%s failed to get to expected status. "
-                          "In ERROR state."
-                          % thing)
-            elif new_status == expected_status:
-                return True  # All good.
-            LOG.debug("Waiting for %s to get to %s status. "
-                      "Currently in %s status",
-                      thing, expected_status, new_status)
-        conf = config.TempestConfig()
-        if not call_until_true(check_status,
-                               conf.compute.build_timeout,
-                               conf.compute.build_interval):
-            self.fail("Timed out waiting for thing %s to become %s"
-                      % (thing_id, expected_status))
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
index 490d263..dd7b682 100755
--- a/tools/pretty_tox_serial.sh
+++ b/tools/pretty_tox_serial.sh
@@ -5,5 +5,5 @@
 if [ ! -d .testrepository ]; then
     testr init
 fi
-testr run --subunit $TESTRARGS | subunit-2to1 | tools/colorizer.py
+testr run --subunit $TESTRARGS | subunit2pyunit
 testr slowest
diff --git a/tox.ini b/tox.ini
index c3562e6..53f85c1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,22 +13,11 @@
 commands =
   python setup.py testr --slowest
 
-
 [testenv:full]
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}
-         NOSE_WITH_OPENSTACK=1
-         NOSE_OPENSTACK_COLOR=1
-         NOSE_OPENSTACK_RED=15
-         NOSE_OPENSTACK_YELLOW=3
-         NOSE_OPENSTACK_SHOW_ELAPSED=1
-         NOSE_OPENSTACK_STDOUT=1
-commands =
-  nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit --xunit-file=nosetests-full.xml -sv tempest/api tempest/scenario tempest/thirdparty tempest/cli
-
-[testenv:testr-serial]
-sitepackages = True
-setenv = VIRTUAL_ENV={envdir}
+# The regex below is used to select which tests to run and exclude the slow tag:
+# See the testrepostiory bug: https://bugs.launchpad.net/testrepository/+bug/1208610
 commands =
   sh tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
 
@@ -41,27 +30,15 @@
 [testenv:smoke]
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}
-         NOSE_WITH_OPENSTACK=1
-         NOSE_OPENSTACK_COLOR=1
-         NOSE_OPENSTACK_RED=15
-         NOSE_OPENSTACK_YELLOW=3
-         NOSE_OPENSTACK_SHOW_ELAPSED=1
-         NOSE_OPENSTACK_STDOUT=1
 commands =
-   nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit -sv --attr=type=smoke --xunit-file=nosetests-smoke.xml tempest
+   sh tools/pretty_tox_serial.sh 'smoke {posargs}'
 
 [testenv:coverage]
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}
-         NOSE_WITH_OPENSTACK=1
-         NOSE_OPENSTACK_COLOR=1
-         NOSE_OPENSTACK_RED=15
-         NOSE_OPENSTACK_YELLOW=3
-         NOSE_OPENSTACK_SHOW_ELAPSED=1
-         NOSE_OPENSTACK_STDOUT=1
 commands =
    python -m tools/tempest_coverage -c start --combine
-   nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit --xunit-file=nosetests-full.xml -sv tempest/api tempest/scenario tempest/thirdparty tempest/cli
+   sh tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
    python -m tools/tempest_coverage -c report --html {posargs}
 
 [testenv:stress]