Merge "Add more tests for Swift Account Quota"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index f1aaa07..703d92a 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -1,5 +1,5 @@
 [DEFAULT]
-# log_config = /opt/stack/tempest/etc/logging.conf.sample
+#log_config = /opt/stack/tempest/etc/logging.conf.sample
 
 # disable logging to the stderr
 use_stderr = False
@@ -272,6 +272,9 @@
 # Set to True if the Account Quota middleware is enabled
 accounts_quotas_available = True
 
+# Set operator role for tests that require creating a container
+operator_role = Member
+
 [boto]
 # This section contains configuration options used when executing tests
 # with boto.
@@ -285,7 +288,7 @@
 aws_access =
 aws_secret =
 
-#Image materials for S3 upload
+# Image materials for S3 upload
 # ALL content of the specified directory will be uploaded to S3
 s3_materials_path = /opt/stack/devstack/files/images/s3-materials/cirros-0.3.1
 
@@ -293,22 +296,22 @@
 # Subdirectories not allowed!
 # The filenames will be used as a Keys in the S3 Buckets
 
-#ARI Ramdisk manifest. Must be in the above s3_materials_path
+# ARI Ramdisk manifest. Must be in the above s3_materials_path
 ari_manifest = cirros-0.3.1-x86_64-initrd.manifest.xml
 
-#AMI Machine Image manifest. Must be in the above s3_materials_path
+# AMI Machine Image manifest. Must be in the above s3_materials_path
 ami_manifest = cirros-0.3.1-x86_64-blank.img.manifest.xml
 
-#AKI Kernel Image manifest, Must be in the above s3_materials_path
+# AKI Kernel Image manifest, Must be in the above s3_materials_path
 aki_manifest = cirros-0.3.1-x86_64-vmlinuz.manifest.xml
 
-#Instance type
+# Instance type
 instance_type = m1.tiny
 
-#TCP/IP connection timeout
+# TCP/IP connection timeout
 http_socket_timeout = 5
 
-#Number of retries actions on connection or 5xx error
+# Number of retries actions on connection or 5xx error
 num_retries = 1
 
 # Status change wait timout
diff --git a/requirements.txt b/requirements.txt
index cc61b01..06db0e6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,6 +13,7 @@
 python-novaclient>=2.10.0
 python-neutronclient>=2.2.3,<3.0.0
 python-cinderclient>=1.0.4
+python-heatclient>=0.2.3
 testresources
 keyring
 testrepository
diff --git a/run_tests.sh b/run_tests.sh
index a645b22..f995cde 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,15 +99,30 @@
 }
 
 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
 }
 
+function run_tests_nose {
+    NOSE_WITH_OPENSTACK=1
+    NOSE_OPENSTACK_COLOR=1
+    NOSE_OPENSTACK_RED=15.00
+    NOSE_OPENSTACK_YELLOW=3.00
+    NOSE_OPENSTACK_SHOW_ELAPSED=1
+    NOSE_OPENSTACK_STDOUT=1
+    if [[ "x$noseargs" =~ "tempest" ]]; then
+        noseargs="$testrargs"
+    else
+        noseargs="$noseargs tempest"
+    fi
+    ${wrapper} nosetests $noseargs
+}
+
 function run_pep8 {
   echo "Running pep8 ..."
   ${wrapper} flake8
@@ -139,8 +138,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
@@ -180,14 +177,20 @@
     run_coverage_start
 fi
 
-run_tests
+
+py_version=`${wrapper} python --version 2>&1`
+if [[ $py_version =~ "2.6" ]] ; then
+    run_tests_nose
+else
+    run_tests
+fi
 retval=$?
 
 if [ $nova_coverage -eq 1 ]; then
     run_coverage_report
 fi
 
-if [ -z "$noseargs" ]; then
+if [ -z "$testrargs" ]; then
     run_pep8
 fi
 
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/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 8b96370..895f773 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -15,8 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.api.compute import base
 from tempest import config
 from tempest import exceptions
@@ -30,6 +28,9 @@
     @classmethod
     def setUpClass(cls):
         super(FixedIPsBase, cls).setUpClass()
+        if cls.config.service_available.neutron:
+            msg = ("%s skipped as neutron is available" % cls.__name__)
+            raise cls.skipException(msg)
         # NOTE(maurosr): The idea here is: the server creation is just an
         # auxiliary element to the ip details or reservation, there was no way
         # (at least none in my mind) to get an valid and existing ip except
@@ -56,8 +57,6 @@
 
     CONF = config.TempestConfig()
 
-    @testtools.skipIf(CONF.service_available.neutron, "This feature is not" +
-                      "implemented by Neutron. See bug: #1194569")
     @attr(type='gate')
     def test_list_fixed_ip_details(self):
         resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 6d0a5b5..69e15f7 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -15,6 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api import compute
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_int_id
@@ -193,9 +195,10 @@
                 flag = True
         self.assertTrue(flag)
 
+    @testtools.skip("Skipped until the Bug #1209101 is resolved")
     @attr(type='gate')
-    def test_flavor_not_public_verify_entry_not_in_list_details(self):
-        # Create a flavor with os-flavor-access:is_public false should not
+    def test_list_non_public_flavor(self):
+        # Create a flavor with os-flavor-access:is_public false should
         # be present in list_details.
         # This operation requires the user to have 'admin' role
         flavor_name = rand_name(self.flavor_name_prefix)
@@ -208,13 +211,22 @@
                                                  new_flavor_id,
                                                  is_public="False")
         self.addCleanup(self.flavor_clean_up, flavor['id'])
-        flag = False
         # Verify flavor is retrieved
+        flag = False
         resp, flavors = self.client.list_flavors_with_detail()
         self.assertEqual(resp.status, 200)
         for flavor in flavors:
             if flavor['name'] == flavor_name:
                 flag = True
+        self.assertTrue(flag)
+
+        # Verify flavor is not retrieved with other user
+        flag = False
+        resp, flavors = self.user_client.list_flavors_with_detail()
+        self.assertEqual(resp.status, 200)
+        for flavor in flavors:
+            if flavor['name'] == flavor_name:
+                flag = True
         self.assertFalse(flag)
 
     @attr(type='gate')
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 849cebb..af76ad0 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -33,13 +33,13 @@
         cls.client = cls.os_adm.hosts_client
         cls.non_admin_client = cls.os.hosts_client
 
-    @attr(type=['positive', 'gate'])
+    @attr(type='gate')
     def test_list_hosts(self):
         resp, hosts = self.client.list_hosts()
         self.assertEqual(200, resp.status)
         self.assertTrue(len(hosts) >= 2)
 
-    @attr(type='positive')
+    @attr(type='gate')
     def test_list_hosts_with_zone(self):
         resp, hosts = self.client.list_hosts()
         host = hosts[0]
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 00a5955..5ca16f4 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -40,20 +40,20 @@
         self.assertEqual(200, resp.status)
         return hypers
 
-    @attr(type=['positive', 'gate'])
+    @attr(type='gate')
     def test_get_hypervisor_list(self):
         # List of hypervisor and available hypervisors hostname
         hypers = self._list_hypervisors()
         self.assertTrue(len(hypers) > 0)
 
-    @attr(type=['positive', 'gate'])
+    @attr(type='gate')
     def test_get_hypervisor_list_details(self):
         # Display the details of the all hypervisor
         resp, hypers = self.client.get_hypervisor_list_details()
         self.assertEqual(200, resp.status)
         self.assertTrue(len(hypers) > 0)
 
-    @attr(type=['positive', 'gate'])
+    @attr(type='gate')
     def test_get_hypervisor_show_details(self):
         # Display the details of the specified hypervisor
         hypers = self._list_hypervisors()
@@ -66,7 +66,7 @@
         self.assertEqual(details['hypervisor_hostname'],
                          hypers[0]['hypervisor_hostname'])
 
-    @attr(type=['positive', 'gate'])
+    @attr(type='gate')
     def test_get_hypervisor_show_servers(self):
         # Show instances about the specific hypervisors
         hypers = self._list_hypervisors()
@@ -77,14 +77,14 @@
         self.assertEqual(200, resp.status)
         self.assertTrue(len(hypervisors) > 0)
 
-    @attr(type=['positive', 'gate'])
+    @attr(type='gate')
     def test_get_hypervisor_stats(self):
         # Verify the stats of the all hypervisor
         resp, stats = self.client.get_hypervisor_stats()
         self.assertEqual(200, resp.status)
         self.assertTrue(len(stats) > 0)
 
-    @attr(type=['positive', 'gate'])
+    @attr(type='gate')
     def test_get_hypervisor_uptime(self):
         # Verify that GET shows the specified hypervisor uptime
         hypers = self._list_hypervisors()
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index d8d162e..156274d 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -39,7 +39,8 @@
         # NOTE(afazekas): these test cases should always create and use a new
         # tenant most of them should be skipped if we can't do that
         if cls.config.compute.allow_tenant_isolation:
-            cls.demo_tenant_id = cls.isolated_creds[0][0]['tenantId']
+            cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
+                'tenantId')
         else:
             cls.demo_tenant_id = [tnt['id'] for tnt in tenants if tnt['name']
                                   == cls.config.identity.tenant_name][0]
@@ -83,24 +84,14 @@
                          'key_pairs': 200, 'injected_file_path_bytes': 512,
                          'instances': 20, 'security_group_rules': 20,
                          'cores': 2, 'security_groups': 20}
-        try:
-            # Update limits for all quota resources
-            resp, quota_set = self.adm_client.update_quota_set(
-                self.demo_tenant_id,
-                **new_quota_set)
-            self.addCleanup(self.adm_client.update_quota_set,
-                            self.demo_tenant_id, **self.default_quota_set)
-            self.assertEqual(200, resp.status)
-            self.assertEqual(new_quota_set, quota_set)
-        except Exception:
-            self.fail("Admin could not update quota set for the tenant")
-        finally:
-            # Reset quota resource limits to default values
-            resp, quota_set = self.adm_client.update_quota_set(
-                self.demo_tenant_id,
-                **self.default_quota_set)
-            self.assertEqual(200, resp.status, "Failed to reset quota "
-                             "defaults")
+        # Update limits for all quota resources
+        resp, quota_set = self.adm_client.update_quota_set(
+            self.demo_tenant_id,
+            **new_quota_set)
+        self.addCleanup(self.adm_client.update_quota_set,
+                        self.demo_tenant_id, **self.default_quota_set)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(new_quota_set, quota_set)
 
     # TODO(afazekas): merge these test cases
     @attr(type='gate')
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index d40b0e0..acf0275 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -19,6 +19,7 @@
 
 from tempest.api import compute
 from tempest import clients
+from tempest.common import isolated_creds
 from tempest.common.utils.data_utils import parse_image_id
 from tempest.common.utils.data_utils import rand_name
 from tempest.openstack.common import log as logging
@@ -35,13 +36,14 @@
 
     @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)
-        cls.isolated_creds = []
+        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
 
         if cls.config.compute.allow_tenant_isolation:
-            creds = cls._get_isolated_creds()
+            creds = cls.isolated_creds.get_primary_creds()
             username, tenant_name, password = creds
             os = clients.Manager(username=username,
                                  password=password,
@@ -108,7 +110,8 @@
     def tearDownClass(cls):
         cls.clear_images()
         cls.clear_servers()
-        cls._clear_isolated_creds()
+        cls.isolated_creds.clear_isolated_creds()
+        super(BaseComputeTest, cls).tearDownClass()
 
     @classmethod
     def create_server(cls, **kwargs):
@@ -187,7 +190,7 @@
                    "in configuration.")
             raise cls.skipException(msg)
         if cls.config.compute.allow_tenant_isolation:
-            creds = cls._get_isolated_creds(admin=True)
+            creds = cls.isolated_creds.get_admin_creds()
             admin_username, admin_tenant_name, admin_password = creds
             cls.os_adm = clients.Manager(username=admin_username,
                                          password=admin_password,
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 4f9364b..2f0ed6b 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -39,7 +39,7 @@
 
         if compute.MULTI_USER:
             if cls.config.compute.allow_tenant_isolation:
-                creds = cls._get_isolated_creds()
+                creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
                 cls.alt_manager = clients.Manager(username=username,
                                                   password=password,
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 14eced2..0052a30 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -54,7 +54,7 @@
 
         if compute.MULTI_USER:
             if cls.config.compute.allow_tenant_isolation:
-                creds = cls._get_isolated_creds()
+                creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
                 cls.alt_manager = clients.Manager(username=username,
                                                   password=password,
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 5f8606e..30db206 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -125,6 +125,8 @@
         self.assertRaises(exceptions.NotFound, self.client.get_security_group,
                           non_exist_id)
 
+    @testtools.skipIf(config.TempestConfig().service_available.neutron,
+                      "Skipped until the Bug #1161411 is resolved")
     @attr(type=['negative', 'gate'])
     def test_security_group_create_with_invalid_group_name(self):
         # Negative test: Security Group should not be created with group name
@@ -143,6 +145,8 @@
                           self.client.create_security_group, s_name,
                           s_description)
 
+    @testtools.skipIf(config.TempestConfig().service_available.neutron,
+                      "Skipped until the Bug #1161411 is resolved")
     @attr(type=['negative', 'gate'])
     def test_security_group_create_with_invalid_group_description(self):
         # Negative test:Security Group should not be created with description
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index b8f965c..ade7604 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -20,6 +20,7 @@
 from tempest.api.compute import base
 from tempest.api import utils
 from tempest.common.utils.data_utils import rand_name
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
@@ -218,6 +219,8 @@
         self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
         self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
+    @testtools.skipIf(config.TempestConfig().service_available.neutron,
+                      "Skipped until the Bug #1182883 is resolved")
     @attr(type='gate')
     def test_list_servers_filtered_by_ip_regex(self):
         # Filter servers by regex ip
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index bad4a11..14ea174 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -35,7 +35,7 @@
 
         if compute.MULTI_USER:
             if cls.config.compute.allow_tenant_isolation:
-                creds = cls._get_isolated_creds()
+                creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
                 cls.alt_manager = clients.Manager(username=username,
                                                   password=password,
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index 55dba97..60297a9 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -41,7 +41,7 @@
         cls.security_client = cls.os.security_groups_client
 
         if cls.config.compute.allow_tenant_isolation:
-            creds = cls._get_isolated_creds()
+            creds = cls.isolated_creds.get_alt_creds()
             username, tenant_name, password = creds
             cls.alt_manager = clients.Manager(username=username,
                                               password=password,
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index e756870..b67a5e0 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -1,6 +1,6 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
-# Copyright 2012 IBM Corp.
+# Copyright 2013 IBM Corp.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -42,10 +42,11 @@
             raise cls.skipException(skip_msg)
 
     def _detach(self, server_id, volume_id):
-        self.servers_client.detach_volume(server_id, volume_id)
-        self.volumes_client.wait_for_volume_status(volume_id, 'available')
+        if self.attached:
+            self.servers_client.detach_volume(server_id, volume_id)
+            self.volumes_client.wait_for_volume_status(volume_id, 'available')
 
-    def _delete(self, volume):
+    def _delete_volume(self):
         if self.volume:
             self.volumes_client.delete_volume(self.volume['id'])
             self.volume = None
@@ -54,6 +55,7 @@
         # Start a server and wait for it to become ready
         resp, server = self.create_server(wait_until='ACTIVE',
                                           adminPass='password')
+        self.server = server
 
         # Record addresses so that we can ssh later
         resp, server['addresses'] = \
@@ -63,6 +65,7 @@
         resp, volume = self.volumes_client.create_volume(1,
                                                          display_name='test')
         self.volume = volume
+        self.addCleanup(self._delete_volume)
         self.volumes_client.wait_for_volume_status(volume['id'], 'available')
 
         # Attach the volume to the server
@@ -71,49 +74,41 @@
         self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
 
         self.attached = True
+        self.addCleanup(self._detach, server['id'], volume['id'])
 
     @testtools.skipIf(not run_ssh, 'SSH required for this test')
     @attr(type='gate')
     def test_attach_detach_volume(self):
         # Stop and Start a server with an attached volume, ensuring that
         # the volume remains attached.
-        try:
-            self._create_and_attach()
-            server = self.server
-            volume = self.volume
+        self._create_and_attach()
+        server = self.server
+        volume = self.volume
 
-            self.servers_client.stop(server['id'])
-            self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
+        self.servers_client.stop(server['id'])
+        self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
 
-            self.servers_client.start(server['id'])
-            self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        self.servers_client.start(server['id'])
+        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
 
-            linux_client = RemoteClient(server,
-                                        self.ssh_user, server['adminPass'])
-            partitions = linux_client.get_partitions()
-            self.assertIn(self.device, partitions)
+        linux_client = RemoteClient(server,
+                                    self.ssh_user, server['adminPass'])
+        partitions = linux_client.get_partitions()
+        self.assertIn(self.device, partitions)
 
-            self._detach(server['id'], volume['id'])
-            self.attached = False
+        self._detach(server['id'], volume['id'])
+        self.attached = False
 
-            self.servers_client.stop(server['id'])
-            self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
+        self.servers_client.stop(server['id'])
+        self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
 
-            self.servers_client.start(server['id'])
-            self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        self.servers_client.start(server['id'])
+        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
 
-            linux_client = RemoteClient(server,
-                                        self.ssh_user, server['adminPass'])
-            partitions = linux_client.get_partitions()
-            self.assertNotIn(self.device, partitions)
-        except Exception:
-            self.fail("The test_attach_detach_volume is faild!")
-        finally:
-            if self.attached:
-                self._detach(server['id'], volume['id'])
-            # NOTE(maurosr): here we do the cleanup for volume, servers are
-            # dealt on BaseComputeTest.tearDownClass
-            self._delete(self.volume)
+        linux_client = RemoteClient(server,
+                                    self.ssh_user, server['adminPass'])
+        partitions = linux_client.get_partitions()
+        self.assertNotIn(self.device, partitions)
 
 
 class AttachVolumeTestXML(AttachVolumeTestJSON):
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index 6f90b04..8cdcee1 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -26,11 +26,14 @@
 class UsersTestJSON(base.BaseIdentityAdminTest):
     _interface = 'json'
 
-    alt_user = rand_name('test_user_')
-    alt_password = rand_name('pass_')
-    alt_email = alt_user + '@testmail.tm'
-    alt_tenant = rand_name('test_tenant_')
-    alt_description = rand_name('desc_')
+    @classmethod
+    def setUpClass(cls):
+        super(UsersTestJSON, cls).setUpClass()
+        cls.alt_user = rand_name('test_user_')
+        cls.alt_password = rand_name('pass_')
+        cls.alt_email = cls.alt_user + '@testmail.tm'
+        cls.alt_tenant = rand_name('test_tenant_')
+        cls.alt_description = rand_name('desc_')
 
     @attr(type='smoke')
     def test_create_user(self):
@@ -101,8 +104,9 @@
     @attr(type='smoke')
     def test_delete_user(self):
         # Delete a user
+        alt_user2 = rand_name('alt_user_')
         self.data.setup_test_tenant()
-        resp, user = self.client.create_user('user_1234', self.alt_password,
+        resp, user = self.client.create_user(alt_user2, self.alt_password,
                                              self.data.tenant['id'],
                                              self.alt_email)
         self.assertEquals('200', resp['status'])
@@ -228,13 +232,16 @@
         self.data.setup_test_tenant()
         user_ids = list()
         fetched_user_ids = list()
-        resp, user1 = self.client.create_user('tenant_user1', 'password1',
+        alt_tenant_user1 = rand_name('tenant_user1_')
+        resp, user1 = self.client.create_user(alt_tenant_user1, 'password1',
                                               self.data.tenant['id'],
                                               'user1@123')
         self.assertEquals('200', resp['status'])
         user_ids.append(user1['id'])
         self.data.users.append(user1)
-        resp, user2 = self.client.create_user('tenant_user2', 'password2',
+
+        alt_tenant_user2 = rand_name('tenant_user2_')
+        resp, user2 = self.client.create_user(alt_tenant_user2, 'password2',
                                               self.data.tenant['id'],
                                               'user2@123')
         self.assertEquals('200', resp['status'])
@@ -267,9 +274,11 @@
         resp, role = self.client.assign_user_role(tenant['id'], user['id'],
                                                   role['id'])
         self.assertEquals('200', resp['status'])
-        resp, second_user = self.client.create_user('second_user', 'password1',
+
+        alt_user2 = rand_name('second_user_')
+        resp, second_user = self.client.create_user(alt_user2, 'password1',
                                                     self.data.tenant['id'],
-                                                    'user1@123')
+                                                    'user2@123')
         self.assertEquals('200', resp['status'])
         user_ids.append(second_user['id'])
         self.data.users.append(second_user)
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/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
new file mode 100644
index 0000000..980323a
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -0,0 +1,170 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# 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.identity import base
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class RolesV3TestJSON(base.BaseIdentityAdminTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(RolesV3TestJSON, cls).setUpClass()
+        cls.fetched_role_ids = list()
+        u_name = rand_name('user-')
+        u_desc = '%s description' % u_name
+        u_email = '%s@testmail.tm' % u_name
+        u_password = rand_name('pass-')
+        resp = [None] * 5
+        resp[0], cls.project = cls.v3_client.create_project(
+            rand_name('project-'), description=rand_name('project-desc-'))
+        resp[1], cls.domain = cls.v3_client.create_domain(
+            rand_name('domain-'), description=rand_name('domain-desc-'))
+        resp[2], cls.group_body = cls.v3_client.create_group(
+            rand_name('Group-'), project_id=cls.project['id'],
+            domain_id=cls.domain['id'])
+        resp[3], cls.user_body = cls.v3_client.create_user(
+            u_name, description=u_desc, password=u_password,
+            email=u_email, project_id=cls.project['id'],
+            domain_id=cls.domain['id'])
+        resp[4], cls.role = cls.v3_client.create_role(rand_name('Role-'))
+        for r in resp:
+            assert r['status'] == '201', "Expected: %s" % r['status']
+
+    @classmethod
+    def tearDownClass(cls):
+        resp = [None] * 5
+        resp[0], _ = cls.v3_client.delete_role(cls.role['id'])
+        resp[1], _ = cls.v3_client.delete_group(cls.group_body['id'])
+        resp[2], _ = cls.v3_client.delete_user(cls.user_body['id'])
+        resp[3], _ = cls.v3_client.delete_project(cls.project['id'])
+        # NOTE(harika-vakadi): It is necessary to disable the domian
+        # before deleting,or else it would result in unauthorized error
+        cls.v3_client.update_domain(cls.domain['id'], enabled=False)
+        resp[4], _ = cls.v3_client.delete_domain(cls.domain['id'])
+        for r in resp:
+            assert r['status'] == '204', "Expected: %s" % r['status']
+        super(RolesV3TestJSON, cls).tearDownClass()
+
+    def _list_assertions(self, resp, body, fetched_role_ids, role_id):
+        self.assertEqual(resp['status'], '200')
+        self.assertEqual(len(body), 1)
+        self.assertIn(role_id, fetched_role_ids)
+
+    @attr(type='smoke')
+    def test_role_create_update_get(self):
+        r_name = rand_name('Role-')
+        resp, role = self.v3_client.create_role(r_name)
+        self.addCleanup(self.v3_client.delete_role, role['id'])
+        self.assertEqual(resp['status'], '201')
+        self.assertIn('name', role)
+        self.assertEqual(role['name'], r_name)
+
+        new_name = rand_name('NewRole-')
+        resp, updated_role = self.v3_client.update_role(new_name, role['id'])
+        self.assertEqual(resp['status'], '200')
+        self.assertIn('name', updated_role)
+        self.assertIn('id', updated_role)
+        self.assertIn('links', updated_role)
+        self.assertNotEqual(r_name, updated_role['name'])
+
+        resp, new_role = self.v3_client.get_role(role['id'])
+        self.assertEqual(resp['status'], '200')
+        self.assertEqual(new_name, new_role['name'])
+        self.assertEqual(updated_role['id'], new_role['id'])
+
+    @attr(type='smoke')
+    def test_grant_list_revoke_role_to_user_on_project(self):
+        resp, _ = self.v3_client.assign_user_role_on_project(
+            self.project['id'], self.user_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+        resp, roles = self.v3_client.list_user_roles_on_project(
+            self.project['id'], self.user_body['id'])
+
+        for i in roles:
+            self.fetched_role_ids.append(i['id'])
+
+        self._list_assertions(resp, roles, self.fetched_role_ids,
+                              self.role['id'])
+
+        resp, _ = self.v3_client.revoke_role_from_user_on_project(
+            self.project['id'], self.user_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+    @attr(type='smoke')
+    def test_grant_list_revoke_role_to_user_on_domain(self):
+        resp, _ = self.v3_client.assign_user_role_on_domain(
+            self.domain['id'], self.user_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+        resp, roles = self.v3_client.list_user_roles_on_domain(
+            self.domain['id'], self.user_body['id'])
+
+        for i in roles:
+            self.fetched_role_ids.append(i['id'])
+
+        self._list_assertions(resp, roles, self.fetched_role_ids,
+                              self.role['id'])
+
+        resp, _ = self.v3_client.revoke_role_from_user_on_domain(
+            self.domain['id'], self.user_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+    @attr(type='smoke')
+    def test_grant_list_revoke_role_to_group_on_project(self):
+        resp, _ = self.v3_client.assign_group_role_on_project(
+            self.project['id'], self.group_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+        resp, roles = self.v3_client.list_group_roles_on_project(
+            self.project['id'], self.group_body['id'])
+
+        for i in roles:
+            self.fetched_role_ids.append(i['id'])
+
+        self._list_assertions(resp, roles, self.fetched_role_ids,
+                              self.role['id'])
+
+        resp, _ = self.v3_client.revoke_role_from_group_on_project(
+            self.project['id'], self.group_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+    @attr(type='smoke')
+    def test_grant_list_revoke_role_to_group_on_domain(self):
+        resp, _ = self.v3_client.assign_group_role_on_domain(
+            self.domain['id'], self.group_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+        resp, roles = self.v3_client.list_group_roles_on_domain(
+            self.domain['id'], self.group_body['id'])
+
+        for i in roles:
+            self.fetched_role_ids.append(i['id'])
+
+        self._list_assertions(resp, roles, self.fetched_role_ids,
+                              self.role['id'])
+
+        resp, _ = self.v3_client.revoke_role_from_group_on_domain(
+            self.domain['id'], self.group_body['id'], self.role['id'])
+        self.assertEqual(resp['status'], '204')
+
+
+class RolesV3TestXML(RolesV3TestJSON):
+    _interface = 'xml'
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 086c50e..4f54a15 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -15,6 +15,7 @@
 #    under the License.
 
 from tempest import clients
+from tempest.common import isolated_creds
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.openstack.common import log as logging
@@ -28,14 +29,15 @@
 
     @classmethod
     def setUpClass(cls):
-        cls.isolated_creds = []
+        super(BaseImageTest, cls).setUpClass()
         cls.created_images = []
         cls._interface = 'json'
+        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
         if not cls.config.service_available.glance:
             skip_msg = ("%s skipped as glance is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
         if cls.config.compute.allow_tenant_isolation:
-            creds = cls._get_isolated_creds()
+            creds = cls.isolated_creds.get_primary_creds()
             username, tenant_name, password = creds
             cls.os = clients.Manager(username=username,
                                      password=password,
@@ -53,7 +55,8 @@
 
         for image_id in cls.created_images:
                 cls.client.wait_for_resource_deletion(image_id)
-        cls._clear_isolated_creds()
+        cls.isolated_creds.clear_isolated_creds()
+        super(BaseImageTest, cls).tearDownClass()
 
     @classmethod
     def create_image(cls, **kwargs):
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index d3fa763..19c5f84 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -47,7 +47,8 @@
 
     @classmethod
     def setUpClass(cls):
-        os = clients.Manager()
+        super(BaseNetworkTest, cls).setUpClass()
+        os = clients.Manager(interface=cls._interface)
         cls.network_cfg = os.config.network
         if not cls.config.service_available.neutron:
             raise cls.skipException("Neutron support is required")
@@ -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/network/test_networks.py b/tempest/api/network/test_networks.py
index 00a8ef7..7f49452 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -23,7 +23,8 @@
 from tempest.test import attr
 
 
-class NetworksTest(base.BaseNetworkTest):
+class NetworksTestJSON(base.BaseNetworkTest):
+    _interface = 'json'
 
     """
     Tests the following operations in the Neutron API using the REST client for
@@ -55,13 +56,25 @@
 
     @classmethod
     def setUpClass(cls):
-        super(NetworksTest, cls).setUpClass()
+        super(NetworksTestJSON, cls).setUpClass()
         cls.network = cls.create_network()
         cls.name = cls.network['name']
         cls.subnet = cls.create_subnet(cls.network)
         cls.cidr = cls.subnet['cidr']
         cls.port = cls.create_port(cls.network)
 
+    def _delete_networks(self, created_networks):
+        for n in created_networks:
+            resp, body = self.client.delete_network(n['id'])
+            self.assertEqual(204, resp.status)
+        # Asserting that the networks are not found in the list after deletion
+        resp, body = self.client.list_networks()
+        networks_list = list()
+        for network in body['networks']:
+            networks_list.append(network['id'])
+        for n in created_networks:
+            self.assertNotIn(n['id'], networks_list)
+
     @attr(type='gate')
     def test_create_update_delete_network_subnet(self):
         # Creates a network
@@ -97,7 +110,7 @@
         self.assertEqual('200', resp['status'])
         updated_subnet = body['subnet']
         self.assertEqual(updated_subnet['name'], new_subnet)
-        # Deletes subnet and network
+        # Delete subnet and network
         resp, body = self.client.delete_subnet(subnet_id)
         self.assertEqual('204', resp['status'])
         resp, body = self.client.delete_network(net_id)
@@ -116,6 +129,7 @@
     def test_list_networks(self):
         # Verify the network exists in the list of all networks
         resp, body = self.client.list_networks()
+        self.assertEqual('200', resp['status'])
         networks = body['networks']
         found = None
         for n in networks:
@@ -137,6 +151,7 @@
     def test_list_subnets(self):
         # Verify the subnet exists in the list of all subnets
         resp, body = self.client.list_subnets()
+        self.assertEqual('200', resp['status'])
         subnets = body['subnets']
         found = None
         for n in subnets:
@@ -147,7 +162,7 @@
 
     @attr(type='gate')
     def test_create_update_delete_port(self):
-        # Verify that successful port creation & deletion
+        # Verify that successful port creation, update & deletion
         resp, body = self.client.create_port(self.network['id'])
         self.assertEqual('201', resp['status'])
         port = body['port']
@@ -162,7 +177,7 @@
         self.assertEqual('204', resp['status'])
 
     @attr(type='gate')
-    def test_show_ports(self):
+    def test_show_port(self):
         # Verify the details of port
         resp, body = self.client.show_port(self.port['id'])
         self.assertEqual('200', resp['status'])
@@ -192,3 +207,24 @@
         non_exist_id = rand_name('subnet')
         self.assertRaises(exceptions.NotFound, self.client.show_subnet,
                           non_exist_id)
+
+    @attr(type='gate')
+    def test_bulk_create_delete_network(self):
+        # Creates 2 networks in one request
+        network_names = [rand_name('network-'), rand_name('network-')]
+        resp, body = self.client.create_bulk_network(2, network_names)
+        created_networks = body['networks']
+        self.assertEqual('201', resp['status'])
+        self.addCleanup(self._delete_networks, created_networks)
+        # Asserting that the networks are found in the list after creation
+        resp, body = self.client.list_networks()
+        networks_list = list()
+        for network in body['networks']:
+            networks_list.append(network['id'])
+        for n in created_networks:
+            self.assertIsNotNone(n['id'])
+            self.assertIn(n['id'], networks_list)
+
+
+class NetworksTestXML(NetworksTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/network/test_quotas.py b/tempest/api/network/test_quotas.py
index ba70f34..b49cbe8 100644
--- a/tempest/api/network/test_quotas.py
+++ b/tempest/api/network/test_quotas.py
@@ -23,6 +23,7 @@
 
 
 class QuotasTest(base.BaseNetworkTest):
+    _interface = 'json'
 
     """
     Tests the following operations in the Neutron API using the REST client for
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
new file mode 100644
index 0000000..4f687b0
--- /dev/null
+++ b/tempest/api/network/test_routers.py
@@ -0,0 +1,134 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# 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.network import base
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class RoutersTest(base.BaseNetworkTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(RoutersTest, cls).setUpClass()
+
+    def _delete_router(self, router_id):
+        resp, _ = self.client.delete_router(router_id)
+        self.assertEqual(204, resp.status)
+        # Asserting that the router is not found in the list
+        # after deletion
+        resp, list_body = self.client.list_routers()
+        self.assertEqual('200', resp['status'])
+        routers_list = list()
+        for router in list_body['routers']:
+            routers_list.append(router['id'])
+        self.assertNotIn(router_id, routers_list)
+
+    def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
+        resp, _ = self.client.remove_router_interface_with_subnet_id(
+            router_id, subnet_id)
+        self.assertEqual('200', resp['status'])
+
+    def _remove_router_interface_with_port_id(self, router_id, port_id):
+        resp, _ = self.client.remove_router_interface_with_port_id(
+            router_id, port_id)
+        self.assertEqual('200', resp['status'])
+
+    @attr(type='gate')
+    def test_create_show_list_update_delete_router(self):
+        # Create a router
+        name = rand_name('router-')
+        resp, create_body = self.client.create_router(
+            name, external_gateway_info={
+                "network_id": self.network_cfg.public_network_id},
+            admin_state_up=False)
+        self.assertEqual('201', resp['status'])
+        self.addCleanup(self._delete_router, create_body['router']['id'])
+        self.assertEqual(create_body['router']['name'], name)
+        self.assertEqual(
+            create_body['router']['external_gateway_info']['network_id'],
+            self.network_cfg.public_network_id)
+        self.assertEqual(create_body['router']['admin_state_up'], False)
+        # Show details of the created router
+        resp, show_body = self.client.show_router(
+            create_body['router']['id'])
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(show_body['router']['name'], name)
+        self.assertEqual(
+            show_body['router']['external_gateway_info']['network_id'],
+            self.network_cfg.public_network_id)
+        self.assertEqual(show_body['router']['admin_state_up'], False)
+        # List routers and verify if created router is there in response
+        resp, list_body = self.client.list_routers()
+        self.assertEqual('200', resp['status'])
+        routers_list = list()
+        for router in list_body['routers']:
+            routers_list.append(router['id'])
+        self.assertIn(create_body['router']['id'], routers_list)
+        # Update the name of router and verify if it is updated
+        updated_name = 'updated ' + name
+        resp, update_body = self.client.update_router(
+            create_body['router']['id'], name=updated_name)
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(update_body['router']['name'], updated_name)
+        resp, show_body = self.client.show_router(
+            create_body['router']['id'])
+        self.assertEqual(show_body['router']['name'], updated_name)
+
+    @attr(type='gate')
+    def test_add_remove_router_interface_with_subnet_id(self):
+        network = self.create_network()
+        subnet = self.create_subnet(network)
+        name = rand_name('router-')
+        resp, create_body = self.client.create_router(name)
+        self.addCleanup(self.client.delete_router, create_body['router']['id'])
+        # Add router interafce with subnet id
+        resp, interface = self.client.add_router_interface_with_subnet_id(
+            create_body['router']['id'], subnet['id'])
+        self.assertEqual('200', resp['status'])
+        self.addCleanup(self._remove_router_interface_with_subnet_id,
+                        create_body['router']['id'], subnet['id'])
+        self.assertTrue('subnet_id' in interface.keys())
+        self.assertTrue('port_id' in interface.keys())
+        # Verify router id is equal to device id in port details
+        resp, show_port_body = self.client.show_port(
+            interface['port_id'])
+        self.assertEqual(show_port_body['port']['device_id'],
+                         create_body['router']['id'])
+
+    @attr(type='gate')
+    def test_add_remove_router_interface_with_port_id(self):
+        network = self.create_network()
+        self.create_subnet(network)
+        name = rand_name('router-')
+        resp, create_body = self.client.create_router(name)
+        self.addCleanup(self.client.delete_router, create_body['router']['id'])
+        resp, port_body = self.client.create_port(network['id'])
+        # add router interface to port created above
+        resp, interface = self.client.add_router_interface_with_port_id(
+            create_body['router']['id'], port_body['port']['id'])
+        self.assertEqual('200', resp['status'])
+        self.addCleanup(self._remove_router_interface_with_port_id,
+                        create_body['router']['id'], port_body['port']['id'])
+        self.assertTrue('subnet_id' in interface.keys())
+        self.assertTrue('port_id' in interface.keys())
+        # Verify router id is equal to device id in port details
+        resp, show_port_body = self.client.show_port(
+            interface['port_id'])
+        self.assertEqual(show_port_body['port']['device_id'],
+                         create_body['router']['id'])
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 5a1fb5a..e6e8d17 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -18,6 +18,7 @@
 
 from tempest.api.identity.base import DataGenerator
 from tempest import clients
+from tempest.common import isolated_creds
 from tempest import exceptions
 import tempest.test
 
@@ -26,19 +27,45 @@
 
     @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)
-        cls.os = clients.Manager()
+        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
+        if cls.config.compute.allow_tenant_isolation:
+            # Get isolated creds for normal user
+            creds = cls.isolated_creds.get_primary_creds()
+            username, tenant_name, password = creds
+            cls.os = clients.Manager(username=username,
+                                     password=password,
+                                     tenant_name=tenant_name)
+            # Get isolated creds for admin user
+            admin_creds = cls.isolated_creds.get_admin_creds()
+            admin_username, admin_tenant_name, admin_password = admin_creds
+            cls.os_admin = clients.Manager(username=admin_username,
+                                           password=admin_password,
+                                           tenant_name=admin_tenant_name)
+            # Get isolated creds for alt user
+            alt_creds = cls.isolated_creds.get_alt_creds()
+            alt_username, alt_tenant, alt_password = alt_creds
+            cls.os_alt = clients.Manager(username=alt_username,
+                                         password=alt_password,
+                                         tenant_name=alt_tenant)
+            # Add isolated users to operator role so that they can create a
+            # container in swift.
+            cls._assign_member_role()
+        else:
+            cls.os = clients.Manager()
+            cls.os_admin = clients.AdminManager()
+            cls.os_alt = clients.AltManager()
+
         cls.object_client = cls.os.object_client
         cls.container_client = cls.os.container_client
         cls.account_client = cls.os.account_client
         cls.custom_object_client = cls.os.custom_object_client
-        cls.os_admin = clients.AdminManager()
         cls.token_client = cls.os_admin.token_client
         cls.identity_admin_client = cls.os_admin.identity_client
         cls.custom_account_client = cls.os.custom_account_client
-        cls.os_alt = clients.AltManager()
         cls.object_client_alt = cls.os_alt.object_client
         cls.container_client_alt = cls.os_alt.container_client
         cls.identity_client_alt = cls.os_alt.identity_client
@@ -46,6 +73,22 @@
         cls.data = DataGenerator(cls.identity_admin_client)
 
     @classmethod
+    def _assign_member_role(cls):
+        primary_user = cls.isolated_creds.get_primary_user()
+        alt_user = cls.isolated_creds.get_alt_user()
+        swift_role = cls.config.object_storage.operator_role
+        try:
+            resp, roles = cls.os_admin.identity_client.list_roles()
+            role = next(r for r in roles if r['name'] == swift_role)
+        except StopIteration:
+            msg = "No role named %s found" % swift_role
+            raise exceptions.NotFound(msg)
+        for user in [primary_user, alt_user]:
+            cls.os_admin.identity_client.assign_user_role(user['tenantId'],
+                                                          user['id'],
+                                                          role['id'])
+
+    @classmethod
     def delete_containers(cls, containers, container_client=None,
                           object_client=None):
         """Remove given containers and all objects in them.
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 52b37c1..b443933 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -15,6 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import random
+
 from tempest.api.object_storage import base
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
@@ -26,12 +28,17 @@
     @classmethod
     def setUpClass(cls):
         super(AccountTest, cls).setUpClass()
-        cls.container_name = rand_name(name='TestContainer')
-        cls.container_client.create_container(cls.container_name)
+        cls.containers = []
+        for i in xrange(ord('a'), ord('f') + 1):
+            name = rand_name(name='%s-' % chr(i))
+            cls.container_client.create_container(name)
+            cls.containers.append(name)
+        cls.containers_count = len(cls.containers)
 
     @classmethod
     def tearDownClass(cls):
-        cls.container_client.delete_container(cls.container_name)
+        cls.delete_containers(cls.containers)
+        super(AccountTest, cls).tearDownClass()
 
     @attr(type='smoke')
     def test_list_containers(self):
@@ -42,7 +49,59 @@
 
         self.assertIsNotNone(container_list)
         container_names = [c['name'] for c in container_list]
-        self.assertIn(self.container_name, container_names)
+        for container_name in self.containers:
+            self.assertIn(container_name, container_names)
+
+    @attr(type='smoke')
+    def test_list_containers_with_limit(self):
+        # list containers one of them, half of them then all of them
+        for limit in (1, self.containers_count / 2, self.containers_count):
+            params = {'limit': limit}
+            resp, container_list = \
+                self.account_client.list_account_containers(params=params)
+            self.assertEquals(len(container_list), limit)
+
+    @attr(type='smoke')
+    def test_list_containers_with_marker(self):
+        # list containers using marker param
+        # first expect to get 0 container as we specified last
+        # the container as marker
+        # second expect to get the bottom half of the containers
+        params = {'marker': self.containers[-1]}
+        resp, container_list = \
+            self.account_client.list_account_containers(params=params)
+        self.assertEquals(len(container_list), 0)
+        params = {'marker': self.containers[self.containers_count / 2]}
+        resp, container_list = \
+            self.account_client.list_account_containers(params=params)
+        self.assertEquals(len(container_list), self.containers_count / 2 - 1)
+
+    @attr(type='smoke')
+    def test_list_containers_with_end_marker(self):
+        # list containers using end_marker param
+        # first expect to get 0 container as we specified first container as
+        # end_marker
+        # second expect to get the top half of the containers
+        params = {'end_marker': self.containers[0]}
+        resp, container_list = \
+            self.account_client.list_account_containers(params=params)
+        self.assertEquals(len(container_list), 0)
+        params = {'end_marker': self.containers[self.containers_count / 2]}
+        resp, container_list = \
+            self.account_client.list_account_containers(params=params)
+        self.assertEquals(len(container_list), self.containers_count / 2)
+
+    @attr(type='smoke')
+    def test_list_containers_with_limit_and_marker(self):
+        # list containers combining marker and limit param
+        # result are always limitated by the limit whatever the marker
+        for marker in random.choice(self.containers):
+            limit = random.randint(0, self.containers_count - 1)
+            params = {'marker': marker,
+                      'limit': limit}
+            resp, container_list = \
+                self.account_client.list_account_containers(params=params)
+            self.assertLessEqual(len(container_list), limit)
 
     @attr(type='smoke')
     def test_list_account_metadata(self):
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/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index b64a324..797aa71 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -81,8 +81,8 @@
                 cls.volume_id_list.append(cls.volume2['id'])
                 cls.volume_client.wait_for_volume_status(cls.volume2['id'],
                                                          'available')
-        except Exception:
-            LOG.exception("setup failed")
+        except Exception as e:
+            LOG.exception("setup failed: %s" % e)
             cls.tearDownClass()
             raise
 
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 27caaad..822f691 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -38,126 +38,111 @@
                                                                auth_url,
                                                                adm_tenant)
 
+    def _delete_volume(self, volume_id):
+        resp, _ = self.volumes_client.delete_volume(volume_id)
+        self.assertEqual(202, resp.status)
+
+    def _delete_volume_type(self, volume_type_id):
+        resp, _ = self.client.delete_volume_type(volume_type_id)
+        self.assertEqual(202, resp.status)
+
     @attr(type='smoke')
     def test_volume_type_list(self):
         # List Volume types.
-        try:
-            resp, body = self.client.list_volume_types()
-            self.assertEqual(200, resp.status)
-            self.assertTrue(type(body), list)
-        except Exception:
-            self.fail("Could not list volume types")
+        resp, body = self.client.list_volume_types()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(type(body), list)
 
     @attr(type='smoke')
     def test_create_get_delete_volume_with_volume_type_and_extra_specs(self):
         # Create/get/delete volume with volume_type and extra spec.
-        try:
-            volume = {}
-            vol_name = rand_name("volume-")
-            vol_type_name = rand_name("volume-type-")
-            proto = self.config.volume.storage_protocol
-            vendor = self.config.volume.vendor_name
-            extra_specs = {"storage_protocol": proto,
-                           "vendor_name": vendor}
-            body = {}
-            resp, body = self.client.create_volume_type(
-                vol_type_name,
-                extra_specs=extra_specs)
-            self.assertEqual(200, resp.status)
-            self.assertIn('id', body)
-            self.assertIn('name', body)
-            resp, volume = self.volumes_client.create_volume(
-                size=1, display_name=vol_name,
-                volume_type=vol_type_name)
-            self.assertEqual(200, resp.status)
-            self.assertIn('id', volume)
-            self.assertIn('display_name', volume)
-            self.assertEqual(volume['display_name'], vol_name,
-                             "The created volume name is not equal "
-                             "to the requested name")
-            self.assertTrue(volume['id'] is not None,
-                            "Field volume id is empty or not found.")
-            self.volumes_client.wait_for_volume_status(volume['id'],
-                                                       'available')
-            resp, fetched_volume = self.volumes_client.get_volume(volume['id'])
-            self.assertEqual(200, resp.status)
-            self.assertEqual(vol_name, fetched_volume['display_name'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-            self.assertEqual(volume['id'], fetched_volume['id'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-            self.assertEqual(vol_type_name, fetched_volume['volume_type'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-        except Exception:
-            self.fail("Could not create correct volume with volume_type")
-        finally:
-            if volume:
-                # Delete the Volume if it was created
-                resp, _ = self.volumes_client.delete_volume(volume['id'])
-                self.assertEqual(202, resp.status)
-
-            if body:
-                resp, _ = self.client.delete_volume_type(body['id'])
-                self.assertEqual(202, resp.status)
+        volume = {}
+        vol_name = rand_name("volume-")
+        vol_type_name = rand_name("volume-type-")
+        proto = self.config.volume.storage_protocol
+        vendor = self.config.volume.vendor_name
+        extra_specs = {"storage_protocol": proto,
+                       "vendor_name": vendor}
+        body = {}
+        resp, body = self.client.create_volume_type(
+            vol_type_name,
+            extra_specs=extra_specs)
+        self.assertEqual(200, resp.status)
+        self.assertIn('id', body)
+        self.addCleanup(self._delete_volume_type, body['id'])
+        self.assertIn('name', body)
+        resp, volume = self.volumes_client.create_volume(
+            size=1, display_name=vol_name,
+            volume_type=vol_type_name)
+        self.assertEqual(200, resp.status)
+        self.assertIn('id', volume)
+        self.addCleanup(self._delete_volume, volume['id'])
+        self.assertIn('display_name', volume)
+        self.assertEqual(volume['display_name'], vol_name,
+                         "The created volume name is not equal "
+                         "to the requested name")
+        self.assertTrue(volume['id'] is not None,
+                        "Field volume id is empty or not found.")
+        self.volumes_client.wait_for_volume_status(volume['id'],
+                                                   'available')
+        resp, fetched_volume = self.volumes_client.get_volume(volume['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(vol_name, fetched_volume['display_name'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
+        self.assertEqual(volume['id'], fetched_volume['id'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
+        self.assertEqual(vol_type_name, fetched_volume['volume_type'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
 
     @attr(type='smoke')
     def test_volume_type_create_delete(self):
         # Create/Delete volume type.
-        try:
-            name = rand_name("volume-type-")
-            extra_specs = {"storage_protocol": "iSCSI",
-                           "vendor_name": "Open Source"}
-            resp, body = self.client.create_volume_type(
-                name,
-                extra_specs=extra_specs)
-            self.assertEqual(200, resp.status)
-            self.assertIn('id', body)
-            self.assertIn('name', body)
-            self.assertEqual(body['name'], name,
-                             "The created volume_type name is not equal "
-                             "to the requested name")
-            self.assertTrue(body['id'] is not None,
-                            "Field volume_type id is empty or not found.")
-            resp, _ = self.client.delete_volume_type(body['id'])
-            self.assertEqual(202, resp.status)
-        except Exception:
-            self.fail("Could not create a volume_type")
+        name = rand_name("volume-type-")
+        extra_specs = {"storage_protocol": "iSCSI",
+                       "vendor_name": "Open Source"}
+        resp, body = self.client.create_volume_type(
+            name,
+            extra_specs=extra_specs)
+        self.assertEqual(200, resp.status)
+        self.assertIn('id', body)
+        self.addCleanup(self._delete_volume_type, body['id'])
+        self.assertIn('name', body)
+        self.assertEqual(body['name'], name,
+                         "The created volume_type name is not equal "
+                         "to the requested name")
+        self.assertTrue(body['id'] is not None,
+                        "Field volume_type id is empty or not found.")
 
     @attr(type='smoke')
     def test_volume_type_create_get(self):
         # Create/get volume type.
-        try:
-            body = {}
-            name = rand_name("volume-type-")
-            extra_specs = {"storage_protocol": "iSCSI",
-                           "vendor_name": "Open Source"}
-            resp, body = self.client.create_volume_type(
-                name,
-                extra_specs=extra_specs)
-            self.assertEqual(200, resp.status)
-            self.assertIn('id', body)
-            self.assertIn('name', body)
-            self.assertEqual(body['name'], name,
-                             "The created volume_type name is not equal "
-                             "to the requested name")
-            self.assertTrue(body['id'] is not None,
-                            "Field volume_type id is empty or not found.")
-            resp, fetched_volume_type = self.client.get_volume_type(body['id'])
-            self.assertEqual(200, resp.status)
-            self.assertEqual(name, fetched_volume_type['name'],
-                             'The fetched Volume_type is different '
-                             'from the created Volume_type')
-            self.assertEqual(str(body['id']), fetched_volume_type['id'],
-                             'The fetched Volume_type is different '
-                             'from the created Volume_type')
-            self.assertEqual(extra_specs, fetched_volume_type['extra_specs'],
-                             'The fetched Volume_type is different '
-                             'from the created Volume_type')
-        except Exception:
-            self.fail("Could not create a volume_type")
-        finally:
-            if body:
-                resp, _ = self.client.delete_volume_type(body['id'])
-                self.assertEqual(202, resp.status)
+        body = {}
+        name = rand_name("volume-type-")
+        extra_specs = {"storage_protocol": "iSCSI",
+                       "vendor_name": "Open Source"}
+        resp, body = self.client.create_volume_type(
+            name,
+            extra_specs=extra_specs)
+        self.assertEqual(200, resp.status)
+        self.assertIn('id', body)
+        self.addCleanup(self._delete_volume_type, body['id'])
+        self.assertIn('name', body)
+        self.assertEqual(body['name'], name,
+                         "The created volume_type name is not equal "
+                         "to the requested name")
+        self.assertTrue(body['id'] is not None,
+                        "Field volume_type id is empty or not found.")
+        resp, fetched_volume_type = self.client.get_volume_type(body['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(name, fetched_volume_type['name'],
+                         'The fetched Volume_type is different '
+                         'from the created Volume_type')
+        self.assertEqual(str(body['id']), fetched_volume_type['id'],
+                         'The fetched Volume_type is different '
+                         'from the created Volume_type')
+        self.assertEqual(extra_specs, fetched_volume_type['extra_specs'],
+                         'The fetched Volume_type is different '
+                         'from the created Volume_type')
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 417f296..7d94f58 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -37,68 +37,59 @@
     @attr(type='smoke')
     def test_volume_type_extra_specs_list(self):
         # List Volume types extra specs.
-        try:
-            extra_specs = {"spec1": "val1"}
-            resp, body = self.client.create_volume_type_extra_specs(
-                self.volume_type['id'], extra_specs)
-            self.assertEqual(200, resp.status)
-            self.assertEqual(extra_specs, body,
-                             "Volume type extra spec incorrectly created")
-            resp, body = self.client.list_volume_types_extra_specs(
-                self.volume_type['id'])
-            self.assertEqual(200, resp.status)
-            self.assertTrue(type(body), dict)
-            self.assertTrue('spec1' in body, "Incorrect volume type extra"
-                            " spec returned")
-        except Exception:
-            self.fail("Could not list volume types extra specs")
+        extra_specs = {"spec1": "val1"}
+        resp, body = self.client.create_volume_type_extra_specs(
+            self.volume_type['id'], extra_specs)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(extra_specs, body,
+                         "Volume type extra spec incorrectly created")
+        resp, body = self.client.list_volume_types_extra_specs(
+            self.volume_type['id'])
+        self.assertEqual(200, resp.status)
+        self.assertTrue(type(body), dict)
+        self.assertTrue('spec1' in body, "Incorrect volume type extra"
+                        " spec returned")
 
     @attr(type='gate')
     def test_volume_type_extra_specs_update(self):
         # Update volume type extra specs
-        try:
-            extra_specs = {"spec2": "val1"}
-            resp, body = self.client.create_volume_type_extra_specs(
-                self.volume_type['id'], extra_specs)
-            self.assertEqual(200, resp.status)
-            self.assertEqual(extra_specs, body,
-                             "Volume type extra spec incorrectly created")
+        extra_specs = {"spec2": "val1"}
+        resp, body = self.client.create_volume_type_extra_specs(
+            self.volume_type['id'], extra_specs)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(extra_specs, body,
+                         "Volume type extra spec incorrectly created")
 
-            extra_spec = {"spec2": "val2"}
-            resp, body = self.client.update_volume_type_extra_specs(
-                self.volume_type['id'],
-                extra_spec.keys()[0],
-                extra_spec)
-            self.assertEqual(200, resp.status)
-            self.assertTrue('spec2' in body,
-                            "Volume type extra spec incorrectly updated")
-            self.assertEqual(extra_spec['spec2'], body['spec2'],
-                             "Volume type extra spec incorrectly updated")
-        except Exception:
-            self.fail("Couldnt update volume type extra spec")
+        extra_spec = {"spec2": "val2"}
+        resp, body = self.client.update_volume_type_extra_specs(
+            self.volume_type['id'],
+            extra_spec.keys()[0],
+            extra_spec)
+        self.assertEqual(200, resp.status)
+        self.assertTrue('spec2' in body,
+                        "Volume type extra spec incorrectly updated")
+        self.assertEqual(extra_spec['spec2'], body['spec2'],
+                         "Volume type extra spec incorrectly updated")
 
     @attr(type='smoke')
     def test_volume_type_extra_spec_create_get_delete(self):
         # Create/Get/Delete volume type extra spec.
-        try:
-            extra_specs = {"spec3": "val1"}
-            resp, body = self.client.create_volume_type_extra_specs(
-                self.volume_type['id'],
-                extra_specs)
-            self.assertEqual(200, resp.status)
-            self.assertEqual(extra_specs, body,
-                             "Volume type extra spec incorrectly created")
+        extra_specs = {"spec3": "val1"}
+        resp, body = self.client.create_volume_type_extra_specs(
+            self.volume_type['id'],
+            extra_specs)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(extra_specs, body,
+                         "Volume type extra spec incorrectly created")
 
-            resp, _ = self.client.get_volume_type_extra_specs(
-                self.volume_type['id'],
-                extra_specs.keys()[0])
-            self.assertEqual(200, resp.status)
-            self.assertEqual(extra_specs, body,
-                             "Volume type extra spec incorrectly fetched")
+        resp, _ = self.client.get_volume_type_extra_specs(
+            self.volume_type['id'],
+            extra_specs.keys()[0])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(extra_specs, body,
+                         "Volume type extra spec incorrectly fetched")
 
-            resp, _ = self.client.delete_volume_type_extra_specs(
-                self.volume_type['id'],
-                extra_specs.keys()[0])
-            self.assertEqual(202, resp.status)
-        except Exception:
-            self.fail("Could not create a volume_type extra spec")
+        resp, _ = self.client.delete_volume_type_extra_specs(
+            self.volume_type['id'],
+            extra_specs.keys()[0])
+        self.assertEqual(202, resp.status)
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 379baa2..7781647 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -18,6 +18,7 @@
 import time
 
 from tempest import clients
+from tempest.common import isolated_creds
 from tempest.openstack.common import log as logging
 import tempest.test
 
@@ -30,14 +31,15 @@
 
     @classmethod
     def setUpClass(cls):
-        cls.isolated_creds = []
+        super(BaseVolumeTest, cls).setUpClass()
+        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
 
         if not cls.config.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
         if cls.config.compute.allow_tenant_isolation:
-            creds = cls._get_isolated_creds()
+            creds = cls.isolated_creds.get_primary_creds()
             username, tenant_name, password = creds
             os = clients.Manager(username=username,
                                  password=password,
@@ -67,7 +69,8 @@
     def tearDownClass(cls):
         cls.clear_snapshots()
         cls.clear_volumes()
-        cls._clear_isolated_creds()
+        cls.isolated_creds.clear_isolated_creds()
+        super(BaseVolumeTest, cls).tearDownClass()
 
     @classmethod
     def create_snapshot(cls, volume_id=1, **kwargs):
@@ -149,7 +152,7 @@
                    "in configuration.")
             raise cls.skipException(msg)
         if cls.config.compute.allow_tenant_isolation:
-            creds = cls._get_isolated_creds(admin=True)
+            creds = cls.isolated_creds.get_admin_creds()
             admin_username, admin_tenant_name, admin_password = creds
             cls.os_adm = clients.Manager(username=admin_username,
                                          password=admin_password,
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 39f61f3..2e90f16 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -28,76 +28,65 @@
         super(VolumesGetTest, cls).setUpClass()
         cls.client = cls.volumes_client
 
+    def _delete_volume(self, volume_id):
+        resp, _ = self.client.delete_volume(volume_id)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_resource_deletion(volume_id)
+
     def _volume_create_get_delete(self, **kwargs):
         # Create a volume, Get it's details and Delete the volume
-        try:
-            volume = {}
-            v_name = rand_name('Volume')
-            metadata = {'Type': 'Test'}
-            # Create a volume
-            resp, volume = self.client.create_volume(size=1,
-                                                     display_name=v_name,
-                                                     metadata=metadata,
-                                                     **kwargs)
-            self.assertEqual(200, resp.status)
-            self.assertIn('id', volume)
-            self.assertIn('display_name', volume)
-            self.assertEqual(volume['display_name'], v_name,
-                             "The created volume name is not equal "
-                             "to the requested name")
-            self.assertTrue(volume['id'] is not None,
-                            "Field volume id is empty or not found.")
-            self.client.wait_for_volume_status(volume['id'], 'available')
-            # Get Volume information
-            resp, fetched_volume = self.client.get_volume(volume['id'])
-            self.assertEqual(200, resp.status)
-            self.assertEqual(v_name,
-                             fetched_volume['display_name'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-            self.assertEqual(volume['id'],
-                             fetched_volume['id'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-            self.assertEqual(metadata,
-                             fetched_volume['metadata'],
-                             'The fetched Volume is different '
-                             'from the created Volume')
-        except Exception:
-            self.fail("Could not create a volume")
-        finally:
-            if volume:
-                # Delete the Volume if it was created
-                resp, _ = self.client.delete_volume(volume['id'])
-                self.assertEqual(202, resp.status)
-                self.client.wait_for_resource_deletion(volume['id'])
+        volume = {}
+        v_name = rand_name('Volume')
+        metadata = {'Type': 'Test'}
+        # Create a volume
+        resp, volume = self.client.create_volume(size=1,
+                                                 display_name=v_name,
+                                                 metadata=metadata,
+                                                 **kwargs)
+        self.assertEqual(200, resp.status)
+        self.assertIn('id', volume)
+        self.addCleanup(self._delete_volume, volume['id'])
+        self.assertIn('display_name', volume)
+        self.assertEqual(volume['display_name'], v_name,
+                         "The created volume name is not equal "
+                         "to the requested name")
+        self.assertTrue(volume['id'] is not None,
+                        "Field volume id is empty or not found.")
+        self.client.wait_for_volume_status(volume['id'], 'available')
+        # Get Volume information
+        resp, fetched_volume = self.client.get_volume(volume['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(v_name,
+                         fetched_volume['display_name'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
+        self.assertEqual(volume['id'],
+                         fetched_volume['id'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
+        self.assertEqual(metadata,
+                         fetched_volume['metadata'],
+                         'The fetched Volume is different '
+                         'from the created Volume')
 
     @attr(type='gate')
     def test_volume_get_metadata_none(self):
         # Create a volume without passing metadata, get details, and delete
-        try:
-            volume = {}
-            v_name = rand_name('Volume-')
-            # Create a volume without metadata
-            resp, volume = self.client.create_volume(size=1,
-                                                     display_name=v_name,
-                                                     metadata={})
-            self.assertEqual(200, resp.status)
-            self.assertIn('id', volume)
-            self.assertIn('display_name', volume)
-            self.client.wait_for_volume_status(volume['id'], 'available')
-            # GET Volume
-            resp, fetched_volume = self.client.get_volume(volume['id'])
-            self.assertEqual(200, resp.status)
-            self.assertEqual(fetched_volume['metadata'], {})
-        except Exception:
-            self.fail("Could not get volume metadata")
-        finally:
-            if volume:
-                # Delete the Volume if it was created
-                resp, _ = self.client.delete_volume(volume['id'])
-                self.assertEqual(202, resp.status)
-                self.client.wait_for_resource_deletion(volume['id'])
+        volume = {}
+        v_name = rand_name('Volume-')
+        # Create a volume without metadata
+        resp, volume = self.client.create_volume(size=1,
+                                                 display_name=v_name,
+                                                 metadata={})
+        self.assertEqual(200, resp.status)
+        self.assertIn('id', volume)
+        self.addCleanup(self._delete_volume, volume['id'])
+        self.assertIn('display_name', volume)
+        self.client.wait_for_volume_status(volume['id'], 'available')
+        # GET Volume
+        resp, fetched_volume = self.client.get_volume(volume['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(fetched_volume['metadata'], {})
 
     @attr(type='smoke')
     def test_volume_create_get_delete(self):
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index f04d23f..08f585a 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -93,7 +93,7 @@
     def cmd_with_auth(self, cmd, action, flags='', params='',
                       admin=True, fail_ok=False):
         """Executes given command with auth attributes appended."""
-        #TODO(jogo) make admin=False work
+        # TODO(jogo) make admin=False work
         creds = ('--os-username %s --os-tenant-name %s --os-password %s '
                  '--os-auth-url %s ' % (self.identity.admin_username,
                  self.identity.admin_tenant_name, self.identity.admin_password,
@@ -134,6 +134,11 @@
             for field in field_names:
                 self.assertIn(field, item)
 
+    def assertFirstLineStartsWith(self, lines, beginning):
+        self.assertTrue(lines[0].startswith(beginning),
+                        msg=('Beginning of first line has invalid content: %s'
+                             % lines[:3]))
+
 
 class CommandFailed(subprocess.CalledProcessError):
     # adds output attribute for python2.6
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index e9ce87b..21acae8 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -87,7 +87,7 @@
     def test_admin_help(self):
         help_text = self.cinder('help')
         lines = help_text.split('\n')
-        self.assertTrue(lines[0].startswith('usage: cinder'))
+        self.assertFirstLineStartsWith(lines, 'usage: cinder')
 
         commands = []
         cmds_start = lines.index('Positional arguments:')
diff --git a/tempest/cli/simple_read_only/test_compute.py b/tempest/cli/simple_read_only/test_compute.py
index 4c7f604..9b358e6 100644
--- a/tempest/cli/simple_read_only/test_compute.py
+++ b/tempest/cli/simple_read_only/test_compute.py
@@ -48,7 +48,7 @@
                           self.nova,
                           'this-does-nova-exist')
 
-    #NOTE(jogo): Commands in order listed in 'nova help'
+    # NOTE(jogo): Commands in order listed in 'nova help'
 
     # Positional arguments:
 
diff --git a/tempest/cli/simple_read_only/test_compute_manage.py b/tempest/cli/simple_read_only/test_compute_manage.py
index 1848827..523c65f 100644
--- a/tempest/cli/simple_read_only/test_compute_manage.py
+++ b/tempest/cli/simple_read_only/test_compute_manage.py
@@ -41,7 +41,7 @@
                           self.nova_manage,
                           'this-does-nova-exist')
 
-    #NOTE(jogo): Commands in order listed in 'nova-manage -h'
+    # NOTE(jogo): Commands in order listed in 'nova-manage -h'
 
     # test flags
     def test_help_flag(self):
diff --git a/tempest/cli/simple_read_only/test_glance.py b/tempest/cli/simple_read_only/test_glance.py
index 3d58451..d02c60b 100644
--- a/tempest/cli/simple_read_only/test_glance.py
+++ b/tempest/cli/simple_read_only/test_glance.py
@@ -48,7 +48,7 @@
     def test_glance_help(self):
         help_text = self.glance('help')
         lines = help_text.split('\n')
-        self.assertTrue(lines[0].startswith('usage: glance'))
+        self.assertFirstLineStartsWith(lines, 'usage: glance')
 
         commands = []
         cmds_start = lines.index('Positional arguments:')
diff --git a/tempest/cli/simple_read_only/test_keystone.py b/tempest/cli/simple_read_only/test_keystone.py
index 4c7982b..1e8009f 100644
--- a/tempest/cli/simple_read_only/test_keystone.py
+++ b/tempest/cli/simple_read_only/test_keystone.py
@@ -46,7 +46,9 @@
         out = self.keystone('catalog')
         catalog = self.parser.details_multiple(out, with_label=True)
         for svc in catalog:
-            self.assertTrue(svc['__label'].startswith('Service:'))
+            self.assertTrue(svc['__label'].startswith('Service:'),
+                            msg=('Invalid beginning of service block: %s' %
+                                 svc['__label']))
 
     def test_admin_endpoint_list(self):
         out = self.keystone('endpoint-list')
@@ -94,7 +96,7 @@
     def test_admin_help(self):
         help_text = self.keystone('help')
         lines = help_text.split('\n')
-        self.assertTrue(lines[0].startswith('usage: keystone'))
+        self.assertFirstLineStartsWith(lines, 'usage: keystone')
 
         commands = []
         cmds_start = lines.index('Positional arguments:')
diff --git a/tempest/cli/simple_read_only/test_neutron.py b/tempest/cli/simple_read_only/test_neutron.py
index 7b8340d..ae3a1a7 100644
--- a/tempest/cli/simple_read_only/test_neutron.py
+++ b/tempest/cli/simple_read_only/test_neutron.py
@@ -92,7 +92,7 @@
     def test_neutron_help(self):
         help_text = self.neutron('help')
         lines = help_text.split('\n')
-        self.assertTrue(lines[0].startswith('usage: neutron'))
+        self.assertFirstLineStartsWith(lines, 'usage: neutron')
 
         commands = []
         cmds_start = lines.index('Commands for API v2.0:')
diff --git a/tempest/clients.py b/tempest/clients.py
index 195cb89..48e4939 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -90,7 +90,8 @@
 from tempest.services.identity.xml.identity_client import TokenClientXML
 from tempest.services.image.v1.json.image_client import ImageClientJSON
 from tempest.services.image.v2.json.image_client import ImageClientV2JSON
-from tempest.services.network.json.network_client import NetworkClient
+from tempest.services.network.json.network_client import NetworkClientJSON
+from tempest.services.network.xml.network_client import NetworkClientXML
 from tempest.services.object_storage.account_client import AccountClient
 from tempest.services.object_storage.account_client import \
     AccountClientCustomizedHeader
@@ -116,6 +117,11 @@
     "xml": ImagesClientXML,
 }
 
+NETWORKS_CLIENTS = {
+    "json": NetworkClientJSON,
+    "xml": NetworkClientXML,
+}
+
 KEYPAIRS_CLIENTS = {
     "json": KeyPairsClientJSON,
     "xml": KeyPairsClientXML,
@@ -295,6 +301,7 @@
 
         try:
             self.servers_client = SERVERS_CLIENTS[interface](*client_args)
+            self.network_client = NETWORKS_CLIENTS[interface](*client_args)
             self.limits_client = LIMITS_CLIENTS[interface](*client_args)
             if self.config.service_available.glance:
                 self.images_client = IMAGES_CLIENTS[interface](*client_args)
@@ -339,7 +346,6 @@
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
-        self.network_client = NetworkClient(*client_args)
         self.hosts_client = HostsClientJSON(*client_args)
         self.account_client = AccountClient(*client_args)
         if self.config.service_available.glance:
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
new file mode 100644
index 0000000..22e1bd2
--- /dev/null
+++ b/tempest/common/isolated_creds.py
@@ -0,0 +1,248 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+#    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 keystoneclient.v2_0.client
+
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest import config
+from tempest import exceptions
+from tempest.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class IsolatedCreds(object):
+
+    def __init__(self, name, tempest_client=True, interface='json',
+                 password='pass'):
+        self.isolated_creds = {}
+        self.name = name
+        self.config = config.TempestConfig()
+        self.tempest_client = tempest_client
+        self.interface = interface
+        self.password = password
+        self.admin_client = self._get_identity_admin_client()
+
+    def _get_keystone_client(self):
+        username = self.config.identity.admin_username
+        password = self.config.identity.admin_password
+        tenant_name = self.config.identity.admin_tenant_name
+        auth_url = self.config.identity.uri
+        dscv = self.config.identity.disable_ssl_certificate_validation
+        return keystoneclient.v2_0.client.Client(username=username,
+                                                 password=password,
+                                                 tenant_name=tenant_name,
+                                                 auth_url=auth_url,
+                                                 insecure=dscv)
+
+    def _get_identity_admin_client(self):
+        """
+        Returns an instance of the Identity Admin API client
+        """
+        if self.tempest_client:
+            os = clients.AdminManager(interface=self.interface)
+            admin_client = os.identity_client
+        else:
+            admin_client = self._get_keystone_client()
+        return admin_client
+
+    def _create_tenant(self, name, description):
+        if self.tempest_client:
+            resp, tenant = self.admin_client.create_tenant(
+                name=name, description=description)
+        else:
+            tenant = self.admin_client.tenants.create(name,
+                                                      description=description)
+        return tenant
+
+    def _get_tenant_by_name(self, name):
+        if self.tempest_client:
+            resp, tenant = self.admin_client.get_tenant_by_name(name)
+        else:
+            tenants = self.admin_client.tenants.list()
+            for ten in tenants:
+                if ten['name'] == name:
+                    tenant = ten
+            raise exceptions.NotFound('No such tenant')
+        return tenant
+
+    def _create_user(self, username, password, tenant, email):
+        if self.tempest_client:
+            resp, user = self.admin_client.create_user(username, password,
+                                                       tenant['id'], email)
+        else:
+            user = self.admin_client.users.create(username, password, email,
+                                                  tenant_id=tenant.id)
+        return user
+
+    def _get_user(self, tenant, username):
+        if self.tempest_client:
+            resp, user = self.admin_client.get_user_by_username(tenant['id'],
+                                                                username)
+        else:
+            user = self.admin_client.users.get(username)
+        return user
+
+    def _list_roles(self):
+        if self.tempest_client:
+            resp, roles = self.admin_client.list_roles()
+        else:
+            roles = self.admin_client.roles.list()
+        return roles
+
+    def _assign_user_role(self, tenant, user, role):
+        if self.tempest_client:
+            self.admin_client.assign_user_role(tenant, user, role)
+        else:
+            self.admin_client.roles.add_user_role(user, role, tenant=tenant)
+
+    def _delete_user(self, user):
+        if self.tempest_client:
+            self.admin_client.delete_user(user)
+        else:
+            self.admin_client.users.delete(user)
+
+    def _delete_tenant(self, tenant):
+        if self.tempest_client:
+            self.admin_client.delete_tenant(tenant)
+        else:
+            self.admin_client.tenants.delete(tenant)
+
+    def _create_creds(self, suffix=None, admin=False):
+        rand_name_root = rand_name(self.name)
+        if suffix:
+            rand_name_root += suffix
+        tenant_name = rand_name_root + "-tenant"
+        tenant_desc = tenant_name + "-desc"
+        rand_name_root = rand_name(self.name)
+        tenant = self._create_tenant(name=tenant_name,
+                                     description=tenant_desc)
+        if suffix:
+            rand_name_root += suffix
+        username = rand_name_root + "-user"
+        email = rand_name_root + "@example.com"
+        user = self._create_user(username, self.password,
+                                 tenant, email)
+        if admin:
+            role = None
+            try:
+                roles = self._list_roles()
+                if self.tempest_client:
+                    role = next(r for r in roles if r['name'] == 'admin')
+                else:
+                    role = next(r for r in roles if r.name == 'admin')
+            except StopIteration:
+                msg = "No admin role found"
+                raise exceptions.NotFound(msg)
+            if self.tempest_client:
+                self._assign_user_role(tenant['id'], user['id'], role['id'])
+            else:
+                self._assign_user_role(tenant.id, user.id, role.id)
+        return user, tenant
+
+    def _get_cred_names(self, user, tenant):
+        if self.tempest_client:
+            username = user.get('name')
+            tenant_name = tenant.get('name')
+        else:
+            username = user.name
+            tenant_name = tenant.name
+        return username, tenant_name
+
+    def get_primary_tenant(self):
+        return self.isolated_creds.get('primary')[1]
+
+    def get_primary_user(self):
+        return self.isolated_creds.get('primary')[0]
+
+    def get_alt_tenant(self):
+        return self.isolated_creds.get('alt')[1]
+
+    def get_alt_user(self):
+        return self.isolated_creds.get('alt')[0]
+
+    def get_admin_tenant(self):
+        return self.isolated_creds.get('admin')[1]
+
+    def get_admin_user(self):
+        return self.isolated_creds.get('admin')[0]
+
+    def get_primary_creds(self):
+        if self.isolated_creds.get('primary'):
+            user, tenant = self.isolated_creds['primary']
+            username, tenant_name = self._get_cred_names(user, tenant)
+        else:
+            user, tenant = self._create_creds()
+            username, tenant_name = self._get_cred_names(user, tenant)
+            self.isolated_creds['primary'] = (user, tenant)
+            LOG.info("Aquired isolated creds:\n user: %s, tenant: %s"
+                     % (username, tenant_name))
+        return username, tenant_name, self.password
+
+    def get_admin_creds(self):
+        if self.isolated_creds.get('admin'):
+            user, tenant = self.isolated_creds['admin']
+            username, tenant_name = self._get_cred_names(user, tenant)
+        else:
+            user, tenant = self._create_creds(admin=True)
+            username, tenant_name = self._get_cred_names(user, tenant)
+            self.isolated_creds['admin'] = (user, tenant)
+            LOG.info("Aquired admin isolated creds:\n user: %s, tenant: %s"
+                     % (username, tenant_name))
+            return username, tenant_name, self.password
+
+    def get_alt_creds(self):
+        if self.isolated_creds.get('alt'):
+            user, tenant = self.isolated_creds['alt']
+            username, tenant_name = self._get_cred_names(user, tenant)
+        else:
+            user, tenant = self._create_creds()
+            username, tenant_name = self._get_cred_names(user, tenant)
+            self.isolated_creds['alt'] = (user, tenant)
+            LOG.info("Aquired alt isolated creds:\n user: %s, tenant: %s"
+                     % (username, tenant_name))
+        return username, tenant_name, self.password
+
+    def clear_isolated_creds(self):
+        if not self.isolated_creds:
+            return
+        for cred in self.isolated_creds:
+            user, tenant = self.isolated_creds.get(cred)
+            try:
+                if self.tempest_client:
+                    self._delete_user(user['id'])
+                else:
+                    self._delete_user(user.id)
+            except exceptions.NotFound:
+                if self.tempest_client:
+                    name = user['name']
+                else:
+                    name = user.name
+                LOG.warn("user with name: %s not found for delete" % name)
+                pass
+            try:
+                if self.tempest_client:
+                    self._delete_tenant(tenant['id'])
+                else:
+                    self._delete_tenant(tenant.id)
+            except exceptions.NotFound:
+                if self.tempest_client:
+                    name = tenant['name']
+                else:
+                    name = tenant.name
+                LOG.warn("tenant with name: %s not found for delete" % name)
+                pass
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 09b87b2..ea5b4f4 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -166,7 +166,8 @@
 
         elif resp.status == 401:
             raise exceptions.AuthenticationFailure(user=user,
-                                                   password=password)
+                                                   password=password,
+                                                   tenant=tenant_name)
         raise exceptions.IdentityError('Unexpected status code {0}'.format(
             resp.status))
 
@@ -322,7 +323,7 @@
         if (resp.status in set((204, 205, 304)) or resp.status < 200 or
                 method.upper() == 'HEAD') and resp_body:
             raise exceptions.ResponseWithNonEmptyBody(status=resp.status)
-        #NOTE(afazekas):
+        # NOTE(afazekas):
         # If the HTTP Status Code is 205
         #   'The response MUST NOT include an entity.'
         # A HTTP entity has an entity-body and an 'entity-header'.
@@ -335,7 +336,7 @@
             0 != len(set(resp.keys()) - set(('status',)) -
                      self.response_header_lc - self.general_header_lc)):
                         raise exceptions.ResponseWithEntity()
-        #NOTE(afazekas)
+        # NOTE(afazekas)
         # Now the swift sometimes (delete not empty container)
         # returns with non json error response, we can create new rest class
         # for swift.
@@ -457,8 +458,8 @@
             message = resp_body
             if parse_resp:
                 resp_body = self._parse_resp(resp_body)
-                #I'm seeing both computeFault and cloudServersFault come back.
-                #Will file a bug to fix, but leave as is for now.
+                # I'm seeing both computeFault and cloudServersFault come back.
+                # Will file a bug to fix, but leave as is for now.
                 if 'cloudServersFault' in resp_body:
                     message = resp_body['cloudServersFault']['message']
                 elif 'computeFault' in resp_body:
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 04cc851..be350c8 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -112,10 +112,10 @@
         channel.shutdown_write()
         out_data = []
         err_data = []
-
-        select_params = [channel], [], [], self.channel_timeout
+        poll = select.poll()
+        poll.register(channel, select.POLLIN)
         while True:
-            ready = select.select(*select_params)
+            ready = poll.poll(self.channel_timeout)
             if not any(ready):
                 raise exceptions.TimeoutException(
                     "Command: '{0}' executed on host '{1}'.".format(
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index de2bf43..2cbb74d 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -24,7 +24,7 @@
 
 class RemoteClient():
 
-    #Note(afazekas): It should always get an address instead of server
+    # NOTE(afazekas): It should always get an address instead of server
     def __init__(self, server, username, password=None, pkey=None):
         ssh_timeout = TempestConfig().compute.ssh_timeout
         network = TempestConfig().compute.network_for_ssh
diff --git a/tempest/config.py b/tempest/config.py
index 9b1a91e..e0ac843 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -369,6 +369,10 @@
     cfg.BoolOpt('accounts_quotas_available',
                 default=True,
                 help="Set to True if the Account Quota middleware is enabled"),
+    cfg.StrOpt('operator_role',
+               default='Member',
+               help="Role to add to users created for swift tests to "
+                    "enable creating containers"),
 ]
 
 
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 448fbdf..62bd8cf 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -105,7 +105,7 @@
 
 class AuthenticationFailure(RestClientException):
     message = ("Authentication with user %(user)s and password "
-               "%(password)s failed")
+               "%(password)s failed auth using tenant %(tenant)s.")
 
 
 class EndpointNotFound(TempestException):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index e785299..dad7eca 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -16,11 +16,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
 import subprocess
 
 # Default client libs
 import cinderclient.client
 import glanceclient
+import heatclient.client
 import keystoneclient.v2_0.client
 import netaddr
 from neutronclient.common import exceptions as exc
@@ -29,6 +31,7 @@
 
 
 from tempest.api.network import common as net_common
+from tempest.common import isolated_creds
 from tempest.common import ssh
 from tempest.common.utils.data_utils import rand_name
 import tempest.manager
@@ -47,34 +50,30 @@
 
     NOVACLIENT_VERSION = '2'
     CINDERCLIENT_VERSION = '1'
+    HEATCLIENT_VERSION = '1'
 
-    def __init__(self):
+    def __init__(self, username, password, tenant_name):
         super(OfficialClientManager, self).__init__()
-        self.compute_client = self._get_compute_client()
+        self.compute_client = self._get_compute_client(username,
+                                                       password,
+                                                       tenant_name)
+        self.identity_client = self._get_identity_client(username,
+                                                         password,
+                                                         tenant_name)
         self.image_client = self._get_image_client()
-        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'
-        ]
+        self.volume_client = self._get_volume_client(username,
+                                                     password,
+                                                     tenant_name)
+        self.orchestration_client = self._get_orchestration_client(
+            username,
+            password,
+            tenant_name)
 
-    def _get_compute_client(self, username=None, password=None,
-                            tenant_name=None):
+    def _get_compute_client(self, username, password, tenant_name):
         # Novaclient will not execute operations for anyone but the
         # identified user, so a new client needs to be created for
         # each user that operations need to be performed for.
-        if not username:
-            username = self.config.identity.username
-        if not password:
-            password = self.config.identity.password
-        if not tenant_name:
-            tenant_name = self.config.identity.tenant_name
-
         self._validate_credentials(username, password, tenant_name)
 
         auth_url = self.config.identity.uri
@@ -91,23 +90,14 @@
                                         insecure=dscv)
 
     def _get_image_client(self):
-        keystone = self._get_identity_client()
-        token = keystone.auth_token
-        endpoint = keystone.service_catalog.url_for(service_type='image',
-                                                    endpoint_type='publicURL')
+        token = self.identity_client.auth_token
+        endpoint = self.identity_client.service_catalog.url_for(
+            service_type='image', endpoint_type='publicURL')
         dscv = self.config.identity.disable_ssl_certificate_validation
         return glanceclient.Client('1', endpoint=endpoint, token=token,
                                    insecure=dscv)
 
-    def _get_volume_client(self, username=None, password=None,
-                           tenant_name=None):
-        if not username:
-            username = self.config.identity.username
-        if not password:
-            password = self.config.identity.password
-        if not tenant_name:
-            tenant_name = self.config.identity.tenant_name
-
+    def _get_volume_client(self, username, password, tenant_name):
         auth_url = self.config.identity.uri
         return cinderclient.client.Client(self.CINDERCLIENT_VERSION,
                                           username,
@@ -115,19 +105,37 @@
                                           tenant_name,
                                           auth_url)
 
-    def _get_identity_client(self, username=None, password=None,
-                             tenant_name=None):
-        # This identity client is not intended to check the security
-        # of the identity service, so use admin credentials by default.
+    def _get_orchestration_client(self, username=None, password=None,
+                                  tenant_name=None):
         if not username:
             username = self.config.identity.admin_username
         if not password:
             password = self.config.identity.admin_password
         if not tenant_name:
-            tenant_name = self.config.identity.admin_tenant_name
+            tenant_name = self.config.identity.tenant_name
 
         self._validate_credentials(username, password, tenant_name)
 
+        keystone = self._get_identity_client(username, password, tenant_name)
+        token = keystone.auth_token
+        try:
+            endpoint = keystone.service_catalog.url_for(
+                service_type='orchestration',
+                endpoint_type='publicURL')
+        except keystoneclient.exceptions.EndpointNotFound:
+            return None
+        else:
+            return heatclient.client.Client(self.HEATCLIENT_VERSION,
+                                            endpoint,
+                                            token=token,
+                                            username=username,
+                                            password=password)
+
+    def _get_identity_client(self, username, password, tenant_name):
+        # This identity client is not intended to check the security
+        # of the identity service, so use admin credentials by default.
+        self._validate_credentials(username, password, tenant_name)
+
         auth_url = self.config.identity.uri
         dscv = self.config.identity.disable_ssl_certificate_validation
 
@@ -160,7 +168,7 @@
                                                 insecure=dscv)
 
 
-class OfficialClientTest(tempest.test.TestCase):
+class OfficialClientTest(tempest.test.BaseTestCase):
     """
     Official Client test base class for scenario testing.
 
@@ -173,7 +181,33 @@
      * Use only the default client tool for calling an API
     """
 
-    manager_class = OfficialClientManager
+    @classmethod
+    def setUpClass(cls):
+        super(OfficialClientTest, cls).setUpClass()
+        cls.isolated_creds = isolated_creds.IsolatedCreds(
+            __name__, tempest_client=False)
+
+        username, tenant_name, password = cls.credentials()
+
+        cls.manager = OfficialClientManager(username, password, tenant_name)
+        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.orchestration_client = cls.manager.orchestration_client
+        cls.resource_keys = {}
+        cls.os_resources = []
+
+    @classmethod
+    def credentials(cls):
+        if cls.config.compute.allow_tenant_isolation:
+            return cls.isolated_creds.get_primary_creds()
+
+        username = cls.config.identity.username
+        password = cls.config.identity.password
+        tenant_name = cls.config.identity.tenant_name
+        return username, tenant_name, password
 
     @classmethod
     def tearDownClass(cls):
@@ -214,59 +248,63 @@
 
             # Block until resource deletion has completed or timed-out
             tempest.test.call_until_true(is_deletion_complete, 10, 1)
-
-
-class NetworkScenarioTest(OfficialClientTest):
-    """
-    Base class for network scenario tests
-    """
+        cls.isolated_creds.clear_isolated_creds()
+        super(OfficialClientTest, cls).tearDownClass()
 
     @classmethod
-    def check_preconditions(cls):
-        if (cls.config.service_available.neutron):
-            cls.enabled = True
-            #verify that neutron_available is telling the truth
-            try:
-                cls.network_client.list_networks()
-            except exc.EndpointNotFound:
-                cls.enabled = False
-                raise
-        else:
-            cls.enabled = False
-            msg = 'Neutron not available'
-            raise cls.skipException(msg)
+    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 setUpClass(cls):
-        super(NetworkScenarioTest, cls).setUpClass()
-        cls.tenant_id = cls.manager._get_identity_client(
-            cls.config.identity.username,
-            cls.config.identity.password,
-            cls.config.identity.tenant_name).tenant_id
+    def get_resource(cls, key):
+        return cls.resource_keys[key]
 
-    def _create_keypair(self, client, namestart='keypair-smoke-'):
-        kp_name = rand_name(namestart)
-        keypair = client.keypairs.create(kp_name)
-        try:
-            self.assertEqual(keypair.id, kp_name)
-            self.set_resource(kp_name, keypair)
-        except AttributeError:
-            self.fail("Keypair object not successfully created.")
-        return keypair
+    @classmethod
+    def remove_resource(cls, key):
+        thing = cls.resource_keys[key]
+        cls.os_resources.remove(thing)
+        del cls.resource_keys[key]
 
-    def _create_security_group(self, client, namestart='secgroup-smoke-'):
-        # Create security group
-        sg_name = rand_name(namestart)
-        sg_desc = sg_name + " description"
-        secgroup = client.security_groups.create(sg_name, sg_desc)
-        try:
-            self.assertEqual(secgroup.name, sg_name)
-            self.assertEqual(secgroup.description, sg_desc)
-            self.set_resource(sg_name, secgroup)
-        except AttributeError:
-            self.fail("SecurityGroup object not successfully created.")
+    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))
 
-        # Add rules to the security group
+    def create_loginable_secgroup_rule(self, client=None, secgroup_id=None):
+        if client is None:
+            client = self.compute_client
+        if secgroup_id is None:
+            sgs = client.security_groups.list()
+            for sg in sgs:
+                if sg.name == 'default':
+                    secgroup_id = sg.id
 
         # These rules are intended to permit inbound ssh and icmp
         # traffic from all sources, so no group_id is provided.
@@ -289,10 +327,104 @@
             }
         ]
         for ruleset in rulesets:
+            sg_rule = client.security_group_rules.create(secgroup_id,
+                                                         **ruleset)
+            self.set_resource(sg_rule.id, sg_rule)
+
+    def create_server(self, client, name=None, image=None, flavor=None,
+                      create_kwargs={}):
+        if name is None:
+            name = rand_name('scenario-server-')
+        if image is None:
+            image = self.config.compute.image_ref
+        if flavor is None:
+            flavor = self.config.compute.flavor_ref
+        LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
+                  name, image, flavor)
+        server = client.servers.create(name, image, flavor, **create_kwargs)
+        try:
+            self.assertEqual(server.name, name)
+            self.set_resource(name, server)
+        except AttributeError:
+            self.fail("Server not successfully created.")
+        self.status_timeout(client.servers, server.id, 'ACTIVE')
+        # The instance retrieved on creation is missing network
+        # details, necessitating retrieval after it becomes active to
+        # ensure correct details.
+        server = client.servers.get(server.id)
+        self.set_resource(name, server)
+        LOG.debug("Created server: %s", server)
+        return server
+
+    def create_volume(self, client=None, size=1, name=None,
+                      snapshot_id=None, imageRef=None):
+        if client is None:
+            client = self.volume_client
+        if name is None:
+            name = rand_name('scenario-volume-')
+        LOG.debug("Creating a volume (size :%s, name: %s)", size, name)
+        volume = client.volumes.create(size=size, display_name=name,
+                                       snapshot_id=snapshot_id,
+                                       imageRef=imageRef)
+        self.set_resource(name, volume)
+        self.assertEqual(name, volume.display_name)
+        self.status_timeout(client.volumes, volume.id, 'available')
+        LOG.debug("Created volume: %s", volume)
+        return volume
+
+    def create_keypair(self, client=None, name=None):
+        if client is None:
+            client = self.compute_client
+        if name is None:
+            name = rand_name('scenario-keypair-')
+        keypair = client.keypairs.create(name)
+        self.assertEqual(keypair.name, name)
+        self.set_resource(name, keypair)
+        return keypair
+
+
+class NetworkScenarioTest(OfficialClientTest):
+    """
+    Base class for network scenario tests
+    """
+
+    @classmethod
+    def check_preconditions(cls):
+        if (cls.config.service_available.neutron):
+            cls.enabled = True
+            # verify that neutron_available is telling the truth
             try:
-                client.security_group_rules.create(secgroup.id, **ruleset)
-            except Exception:
-                self.fail("Failed to create rule in security group.")
+                cls.network_client.list_networks()
+            except exc.EndpointNotFound:
+                cls.enabled = False
+                raise
+        else:
+            cls.enabled = False
+            msg = 'Neutron not available'
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setUpClass(cls):
+        super(NetworkScenarioTest, cls).setUpClass()
+        cls.tenant_id = cls.manager._get_identity_client(
+            cls.config.identity.username,
+            cls.config.identity.password,
+            cls.config.identity.tenant_name).tenant_id
+
+    def _create_security_group(self, client, namestart='secgroup-smoke-'):
+        # Create security group
+        sg_name = rand_name(namestart)
+        sg_desc = sg_name + " description"
+        secgroup = client.security_groups.create(sg_name, sg_desc)
+        try:
+            self.assertEqual(secgroup.name, sg_name)
+            self.assertEqual(secgroup.description, sg_desc)
+            self.set_resource(sg_name, secgroup)
+        except AttributeError:
+            self.fail("SecurityGroup object not successfully created.")
+
+        # Add rules to the security group
+        self.create_loginable_secgroup_rule(client, secgroup.id)
 
         return secgroup
 
@@ -369,31 +501,6 @@
         self.set_resource(name, port)
         return port
 
-    def _create_server(self, client, network, name, key_name, security_groups):
-        flavor_id = self.config.compute.flavor_ref
-        base_image_id = self.config.compute.image_ref
-        create_kwargs = {
-            'nics': [
-                {'net-id': network.id},
-            ],
-            'key_name': key_name,
-            'security_groups': security_groups,
-        }
-        server = client.servers.create(name, base_image_id, flavor_id,
-                                       **create_kwargs)
-        try:
-            self.assertEqual(server.name, name)
-            self.set_resource(name, server)
-        except AttributeError:
-            self.fail("Server not successfully created.")
-        self.status_timeout(client.servers, server.id, 'ACTIVE')
-        # The instance retrieved on creation is missing network
-        # details, necessitating retrieval after it becomes active to
-        # ensure correct details.
-        server = client.servers.get(server.id)
-        self.set_resource(name, server)
-        return server
-
     def _create_floating_ip(self, server, external_network_id):
         result = self.network_client.list_ports(device_id=server.id)
         ports = result.get('ports', [])
@@ -446,3 +553,30 @@
             timeout=self.config.compute.ssh_timeout),
             'Auth failure in connecting to %s@%s via ssh' %
             (username, ip_address))
+
+
+class OrchestrationScenarioTest(OfficialClientTest):
+    """
+    Base class for orchestration scenario tests
+    """
+
+    @classmethod
+    def credentials(cls):
+        username = cls.config.identity.admin_username
+        password = cls.config.identity.admin_password
+        tenant_name = cls.config.identity.tenant_name
+        return username, tenant_name, password
+
+    def _load_template(self, base_file, file_name):
+        filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
+                                file_name)
+        with open(filepath) as f:
+            return f.read()
+
+    @classmethod
+    def _stack_rand_name(cls):
+        return rand_name(cls.__name__ + '-')
+
+    def _create_keypair(self):
+        kp_name = rand_name('keypair-smoke')
+        return self.compute_client.keypairs.create(kp_name)
diff --git a/tempest/scenario/orchestration/__init__.py b/tempest/scenario/orchestration/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/scenario/orchestration/__init__.py
diff --git a/tempest/scenario/orchestration/test_autoscaling.py b/tempest/scenario/orchestration/test_autoscaling.py
new file mode 100644
index 0000000..cd959a8
--- /dev/null
+++ b/tempest/scenario/orchestration/test_autoscaling.py
@@ -0,0 +1,108 @@
+# 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.scenario import manager
+from tempest.test import attr
+from tempest.test import call_until_true
+import time
+
+
+LOG = logging.getLogger(__name__)
+
+
+class AutoScalingTest(manager.OrchestrationScenarioTest):
+
+    def setUp(self):
+        super(AutoScalingTest, self).setUp()
+        if not self.config.orchestration.image_ref:
+            raise self.skipException("No image available to test")
+        self.client = self.orchestration_client
+
+    def assign_keypair(self):
+        self.stack_name = self._stack_rand_name()
+        if self.config.orchestration.keypair_name:
+            self.keypair_name = self.config.orchestration.keypair_name
+        else:
+            self.keypair = self._create_keypair()
+            self.keypair_name = self.keypair.id
+            self.set_resource('keypair', self.keypair)
+
+    def launch_stack(self):
+        self.parameters = {
+            'KeyName': self.keypair_name,
+            'InstanceType': self.config.orchestration.instance_type,
+            'ImageId': self.config.orchestration.image_ref,
+            'StackStart': str(time.time())
+        }
+
+        # create the stack
+        self.template = self._load_template(__file__, 'test_autoscaling.yaml')
+        self.client.stacks.create(
+            stack_name=self.stack_name,
+            template=self.template,
+            parameters=self.parameters)
+
+        self.stack = self.client.stacks.get(self.stack_name)
+        self.stack_identifier = '%s/%s' % (self.stack_name, self.stack.id)
+
+        # if a keypair was set, do not delete the stack on exit to allow
+        # for manual post-mortums
+        if not self.config.orchestration.keypair_name:
+            self.set_resource('stack', self.stack)
+
+    @attr(type='slow')
+    def test_scale_up_then_down(self):
+
+        self.assign_keypair()
+        self.launch_stack()
+
+        sid = self.stack_identifier
+        timeout = self.config.orchestration.build_timeout
+        interval = 10
+
+        self.assertEqual('CREATE', self.stack.action)
+        # wait for create to complete.
+        self.status_timeout(self.client.stacks, sid, 'COMPLETE')
+
+        self.stack.get()
+        self.assertEqual('CREATE_COMPLETE', self.stack.stack_status)
+
+        # the resource SmokeServerGroup is implemented as a nested
+        # stack, so servers can be counted by counting the resources
+        # inside that nested stack
+        resource = self.client.resources.get(sid, 'SmokeServerGroup')
+        nested_stack_id = resource.physical_resource_id
+
+        def server_count():
+            # the number of servers is the number of resources
+            # in the nexted stack
+            self.server_count = len(
+                self.client.resources.list(nested_stack_id))
+            return self.server_count
+
+        def assertScale(from_servers, to_servers):
+            call_until_true(lambda: server_count() == to_servers,
+                            timeout, interval)
+            self.assertEqual(to_servers, self.server_count,
+                             'Failed scaling from %d to %d servers' % (
+                                 from_servers, to_servers))
+
+        # he marched them up to the top of the hill
+        assertScale(1, 2)
+        assertScale(2, 3)
+
+        # and he marched them down again
+        assertScale(3, 2)
+        assertScale(2, 1)
diff --git a/tempest/scenario/orchestration/test_autoscaling.yaml b/tempest/scenario/orchestration/test_autoscaling.yaml
new file mode 100644
index 0000000..045b3bc
--- /dev/null
+++ b/tempest/scenario/orchestration/test_autoscaling.yaml
@@ -0,0 +1,182 @@
+HeatTemplateFormatVersion: '2012-12-12'
+Description: |
+  Template which tests autoscaling and load balancing
+Parameters:
+  KeyName:
+    Type: String
+  InstanceType:
+    Type: String
+  ImageId:
+    Type: String
+  StackStart:
+    Description: Epoch seconds when the stack was launched
+    Type: Number
+  ConsumeStartSeconds:
+    Description: Seconds after invocation when memory should be consumed
+    Type: Number
+    Default: '60'
+  ConsumeStopSeconds:
+    Description: Seconds after StackStart when memory should be released
+    Type: Number
+    Default: '420'
+  ScaleUpThreshold:
+    Description: Memory percentage threshold to scale up on
+    Type: Number
+    Default: '70'
+  ScaleDownThreshold:
+    Description: Memory percentage threshold to scale down on
+    Type: Number
+    Default: '60'
+  ConsumeMemoryLimit:
+    Description: Memory percentage threshold to consume
+    Type: Number
+    Default: '71'
+Resources:
+  SmokeServerGroup:
+    Type: AWS::AutoScaling::AutoScalingGroup
+    Properties:
+      AvailabilityZones: {'Fn::GetAZs': ''}
+      LaunchConfigurationName: {Ref: LaunchConfig}
+      MinSize: '1'
+      MaxSize: '3'
+  SmokeServerScaleUpPolicy:
+    Type: AWS::AutoScaling::ScalingPolicy
+    Properties:
+      AdjustmentType: ChangeInCapacity
+      AutoScalingGroupName: {Ref: SmokeServerGroup}
+      Cooldown: '60'
+      ScalingAdjustment: '1'
+  SmokeServerScaleDownPolicy:
+    Type: AWS::AutoScaling::ScalingPolicy
+    Properties:
+      AdjustmentType: ChangeInCapacity
+      AutoScalingGroupName: {Ref: SmokeServerGroup}
+      Cooldown: '60'
+      ScalingAdjustment: '-1'
+  MEMAlarmHigh:
+    Type: AWS::CloudWatch::Alarm
+    Properties:
+      AlarmDescription: Scale-up if MEM > ScaleUpThreshold% for 10 seconds
+      MetricName: MemoryUtilization
+      Namespace: system/linux
+      Statistic: Average
+      Period: '10'
+      EvaluationPeriods: '1'
+      Threshold: {Ref: ScaleUpThreshold}
+      AlarmActions: [{Ref: SmokeServerScaleUpPolicy}]
+      Dimensions:
+      - Name: AutoScalingGroupName
+        Value: {Ref: SmokeServerGroup}
+      ComparisonOperator: GreaterThanThreshold
+  MEMAlarmLow:
+    Type: AWS::CloudWatch::Alarm
+    Properties:
+      AlarmDescription: Scale-down if MEM < ScaleDownThreshold% for 10 seconds
+      MetricName: MemoryUtilization
+      Namespace: system/linux
+      Statistic: Average
+      Period: '10'
+      EvaluationPeriods: '1'
+      Threshold: {Ref: ScaleDownThreshold}
+      AlarmActions: [{Ref: SmokeServerScaleDownPolicy}]
+      Dimensions:
+      - Name: AutoScalingGroupName
+        Value: {Ref: SmokeServerGroup}
+      ComparisonOperator: LessThanThreshold
+  CfnUser:
+    Type: AWS::IAM::User
+  SmokeKeys:
+    Type: AWS::IAM::AccessKey
+    Properties:
+      UserName: {Ref: CfnUser}
+  SmokeSecurityGroup:
+    Type: AWS::EC2::SecurityGroup
+    Properties:
+      GroupDescription: Standard firewall rules
+      SecurityGroupIngress:
+      - {IpProtocol: tcp, FromPort: '22', ToPort: '22', CidrIp: 0.0.0.0/0}
+      - {IpProtocol: tcp, FromPort: '80', ToPort: '80', CidrIp: 0.0.0.0/0}
+  LaunchConfig:
+    Type: AWS::AutoScaling::LaunchConfiguration
+    Metadata:
+      AWS::CloudFormation::Init:
+        config:
+          files:
+            /etc/cfn/cfn-credentials:
+              content:
+                Fn::Replace:
+                - $AWSAccessKeyId: {Ref: SmokeKeys}
+                  $AWSSecretKey: {'Fn::GetAtt': [SmokeKeys, SecretAccessKey]}
+                - |
+                  AWSAccessKeyId=$AWSAccessKeyId
+                  AWSSecretKey=$AWSSecretKey
+              mode: '000400'
+              owner: root
+              group: root
+            /root/watch_loop:
+              content:
+                Fn::Replace:
+                - _hi_: {Ref: MEMAlarmHigh}
+                  _lo_: {Ref: MEMAlarmLow}
+                - |
+                  #!/bin/bash
+                  while :
+                  do
+                    /opt/aws/bin/cfn-push-stats --watch _hi_ --mem-util
+                    /opt/aws/bin/cfn-push-stats --watch _lo_ --mem-util
+                    sleep 4
+                  done
+              mode: '000700'
+              owner: root
+              group: root
+            /root/consume_memory:
+              content:
+                Fn::Replace:
+                - StackStart: {Ref: StackStart}
+                  ConsumeStopSeconds: {Ref: ConsumeStopSeconds}
+                  ConsumeStartSeconds: {Ref: ConsumeStartSeconds}
+                  ConsumeMemoryLimit: {Ref: ConsumeMemoryLimit}
+                - |
+                  #!/usr/bin/env python
+                  import psutil
+                  import time
+                  import datetime
+                  import sys
+                  a = []
+                  sleep_until_consume = ConsumeStartSeconds
+                  stack_start = StackStart
+                  consume_stop_time = stack_start + ConsumeStopSeconds
+                  memory_limit = ConsumeMemoryLimit
+                  if sleep_until_consume > 0:
+                      sys.stdout.flush()
+                      time.sleep(sleep_until_consume)
+                  while psutil.virtual_memory().percent < memory_limit:
+                      sys.stdout.flush()
+                      a.append(' ' * 10**5)
+                      time.sleep(0.1)
+                  sleep_until_exit = consume_stop_time - time.time()
+                  if sleep_until_exit > 0:
+                      time.sleep(sleep_until_exit)
+              mode: '000700'
+              owner: root
+              group: root
+    Properties:
+      ImageId: {Ref: ImageId}
+      InstanceType: {Ref: InstanceType}
+      KeyName: {Ref: KeyName}
+      SecurityGroups: [{Ref: SmokeSecurityGroup}]
+      UserData:
+        Fn::Base64:
+          Fn::Replace:
+          - ConsumeStopSeconds: {Ref: ConsumeStopSeconds}
+            ConsumeStartSeconds: {Ref: ConsumeStartSeconds}
+            ConsumeMemoryLimit: {Ref: ConsumeMemoryLimit}
+          - |
+            #!/bin/bash -v
+            /opt/aws/bin/cfn-init
+            # report on memory consumption every 4 seconds
+            /root/watch_loop &
+            # wait ConsumeStartSeconds then ramp up memory consumption
+            # until it is over ConsumeMemoryLimit%
+            # then exits ConsumeStopSeconds seconds after stack launch
+            /root/consume_memory > /root/consume_memory.log &
\ No newline at end of file
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 13b31ec..25735e9 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -83,22 +83,13 @@
                                         properties=properties)
 
     def nova_keypair_add(self):
-        name = rand_name('scenario-keypair-')
-
-        self.keypair = self.compute_client.keypairs.create(name=name)
-        self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
-        self.assertEqual(name, self.keypair.name)
+        self.keypair = self.create_keypair()
 
     def nova_boot(self):
-        name = rand_name('scenario-server-')
-        client = self.compute_client
-        flavor_id = self.config.compute.flavor_ref
-        self.server = client.servers.create(name=name, image=self.image,
-                                            flavor=flavor_id,
-                                            key_name=self.keypair.name)
-        self.addCleanup(self.compute_client.servers.delete, self.server)
-        self.assertEqual(name, self.server.name)
-        self._wait_for_server_status('ACTIVE')
+        create_kwargs = {'key_name': self.keypair.name}
+        self.server = self.create_server(self.compute_client,
+                                         image=self.image,
+                                         create_kwargs=create_kwargs)
 
     def nova_list(self):
         servers = self.compute_client.servers.list()
@@ -111,15 +102,7 @@
         self.assertEqual(self.server, got_server)
 
     def cinder_create(self):
-        name = rand_name('scenario-volume-')
-        LOG.debug("volume display-name:%s" % name)
-        self.volume = self.volume_client.volumes.create(size=1,
-                                                        display_name=name)
-        LOG.debug("volume created:%s" % self.volume.display_name)
-        self._wait_for_volume_status('available')
-
-        self.addCleanup(self.volume_client.volumes.delete, self.volume)
-        self.assertEqual(name, self.volume.display_name)
+        self.volume = self.create_volume()
 
     def cinder_list(self):
         volumes = self.volume_client.volumes.list()
@@ -148,25 +131,6 @@
     def nova_floating_ip_add(self):
         self.server.add_floating_ip(self.floating_ip)
 
-    def nova_security_group_rule_create(self):
-        sgs = self.compute_client.security_groups.list()
-        for sg in sgs:
-            if sg.name == 'default':
-                secgroup = sg
-
-        ruleset = {
-            # ssh
-            'ip_protocol': 'tcp',
-            'from_port': 22,
-            'to_port': 22,
-            'cidr': '0.0.0.0/0',
-            'group_id': None
-        }
-        sg_rule = self.compute_client.security_group_rules.create(secgroup.id,
-                                                                  **ruleset)
-        self.addCleanup(self.compute_client.security_group_rules.delete,
-                        sg_rule.id)
-
     def ssh_to_server(self):
         username = self.config.scenario.ssh_user
         self.linux_client = RemoteClient(self.floating_ip.ip,
@@ -200,7 +164,7 @@
 
         self.nova_floating_ip_create()
         self.nova_floating_ip_add()
-        self.nova_security_group_rule_create()
+        self.create_loginable_secgroup_rule()
         self.ssh_to_server()
         self.check_partitions()
 
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 390e004..70939f6 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -18,6 +18,7 @@
 
 from tempest.api.network import common as net_common
 from tempest.common.utils.data_utils import rand_name
+from tempest import config
 from tempest.scenario import manager
 from tempest.test import attr
 
@@ -40,7 +41,7 @@
          ssh server hosted at the IP address.  This check guarantees
          that the IP address is associated with the target VM.
 
-       #TODO(mnewby) - Need to implement the following:
+       # TODO(mnewby) - Need to implement the following:
        - the Tempest host can ssh into the VM via the IP address and
          successfully execute the following:
 
@@ -88,6 +89,8 @@
 
     """
 
+    CONF = config.TempestConfig()
+
     @classmethod
     def check_preconditions(cls):
         super(TestNetworkBasicOps, cls).check_preconditions()
@@ -157,8 +160,8 @@
 
     @attr(type='smoke')
     def test_001_create_keypairs(self):
-        self.keypairs[self.tenant_id] = self._create_keypair(
-            self.compute_client)
+        self.keypairs[self.tenant_id] = self.create_keypair(
+            name=rand_name('keypair-smoke-'))
 
     @attr(type='smoke')
     def test_002_create_security_groups(self):
@@ -177,8 +180,8 @@
 
     @attr(type='smoke')
     def test_004_check_networks(self):
-        #Checks that we see the newly created network/subnet/router via
-        #checking the result of list_[networks,routers,subnets]
+        # Checks that we see the newly created network/subnet/router via
+        # checking the result of list_[networks,routers,subnets]
         seen_nets = self._list_networks()
         seen_names = [n['name'] for n in seen_nets]
         seen_ids = [n['id'] for n in seen_nets]
@@ -208,8 +211,15 @@
             name = rand_name('server-smoke-%d-' % i)
             keypair_name = self.keypairs[tenant_id].name
             security_groups = [self.security_groups[tenant_id].name]
-            server = self._create_server(self.compute_client, network,
-                                         name, keypair_name, security_groups)
+            create_kwargs = {
+                'nics': [
+                    {'net-id': network.id},
+                ],
+                'key_name': keypair_name,
+                'security_groups': security_groups,
+            }
+            server = self.create_server(self.compute_client, name=name,
+                                        create_kwargs=create_kwargs)
             self.servers.append(server)
 
     @attr(type='smoke')
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 0ec3a1d..2903687 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -36,14 +36,8 @@
      * Terminate the instance
     """
 
-    def create_keypair(self):
-        kp_name = rand_name('keypair-smoke')
-        self.keypair = self.compute_client.keypairs.create(kp_name)
-        try:
-            self.assertEqual(self.keypair.id, kp_name)
-            self.set_resource('keypair', self.keypair)
-        except AttributeError:
-            self.fail("Keypair object not successfully created.")
+    def add_keypair(self):
+        self.keypair = self.create_keypair()
 
     def create_security_group(self):
         sg_name = rand_name('secgroup-smoke')
@@ -58,50 +52,15 @@
             self.fail("SecurityGroup object not successfully created.")
 
         # Add rules to the security group
-        rulesets = [
-            {
-                'ip_protocol': 'tcp',
-                'from_port': 1,
-                'to_port': 65535,
-                'cidr': '0.0.0.0/0',
-                'group_id': self.secgroup.id
-            },
-            {
-                'ip_protocol': 'icmp',
-                'from_port': -1,
-                'to_port': -1,
-                'cidr': '0.0.0.0/0',
-                'group_id': self.secgroup.id
-            }
-        ]
-        for ruleset in rulesets:
-            try:
-                self.compute_client.security_group_rules.create(
-                    self.secgroup.id, **ruleset)
-            except Exception:
-                self.fail("Failed to create rule in security group.")
+        self.create_loginable_secgroup_rule(secgroup_id=self.secgroup.id)
 
     def boot_instance(self):
-        i_name = rand_name('instance')
-        flavor_id = self.config.compute.flavor_ref
-        base_image_id = self.config.compute.image_ref
         create_kwargs = {
-            'key_name': self.get_resource('keypair').id
+            'key_name': self.keypair.id
         }
-        self.instance = self.compute_client.servers.create(
-            i_name, base_image_id, flavor_id, **create_kwargs)
-        try:
-            self.assertEqual(self.instance.name, i_name)
-            self.set_resource('instance', self.instance)
-        except AttributeError:
-            self.fail("Instance not successfully created.")
-
-        self.assertEqual(self.instance.status, 'BUILD')
-
-    def wait_on_active(self):
-        instance_id = self.get_resource('instance').id
-        self.status_timeout(
-            self.compute_client.servers, instance_id, 'ACTIVE')
+        instance = self.create_server(self.compute_client,
+                                      create_kwargs=create_kwargs)
+        self.set_resource('instance', instance)
 
     def pause_server(self):
         instance = self.get_resource('instance')
@@ -145,10 +104,9 @@
         self.remove_resource('instance')
 
     def test_server_basicops(self):
-        self.create_keypair()
+        self.add_keypair()
         self.create_security_group()
         self.boot_instance()
-        self.wait_on_active()
         self.pause_server()
         self.unpause_server()
         self.suspend_server()
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 6e305c1..1e090af 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -44,45 +44,14 @@
         self.status_timeout(self.image_client.images, image_id, status)
 
     def _boot_image(self, image_id):
-        name = rand_name('scenario-server-')
-        client = self.compute_client
-        flavor_id = self.config.compute.flavor_ref
-        LOG.debug("name:%s, image:%s" % (name, image_id))
-        server = client.servers.create(name=name,
-                                       image=image_id,
-                                       flavor=flavor_id,
-                                       key_name=self.keypair.name)
-        self.addCleanup(self.compute_client.servers.delete, server)
-        self.assertEqual(name, server.name)
-        self._wait_for_server_status(server, 'ACTIVE')
-        server = client.servers.get(server)  # getting network information
-        LOG.debug("server:%s" % server)
-        return server
+        create_kwargs = {
+            'key_name': self.keypair.name
+        }
+        return self.create_server(self.compute_client, image=image_id,
+                                  create_kwargs=create_kwargs)
 
     def _add_keypair(self):
-        name = rand_name('scenario-keypair-')
-        self.keypair = self.compute_client.keypairs.create(name=name)
-        self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
-        self.assertEqual(name, self.keypair.name)
-
-    def _create_security_group_rule(self):
-        sgs = self.compute_client.security_groups.list()
-        for sg in sgs:
-            if sg.name == 'default':
-                secgroup = sg
-
-        ruleset = {
-            # ssh
-            'ip_protocol': 'tcp',
-            'from_port': 22,
-            'to_port': 22,
-            'cidr': '0.0.0.0/0',
-            'group_id': None
-        }
-        sg_rule = self.compute_client.security_group_rules.create(secgroup.id,
-                                                                  **ruleset)
-        self.addCleanup(self.compute_client.security_group_rules.delete,
-                        sg_rule.id)
+        self.keypair = self.create_keypair()
 
     def _ssh_to_server(self, server_or_ip):
         if isinstance(server_or_ip, basestring):
@@ -129,7 +98,7 @@
     def test_snapshot_pattern(self):
         # prepare for booting a instance
         self._add_keypair()
-        self._create_security_group_rule()
+        self.create_loginable_secgroup_rule()
 
         # boot a instance and create a timestamp file in it
         server = self._boot_image(self.config.compute.image_ref)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 4434604..8864b2f 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -64,26 +64,14 @@
                             volume_snapshot.id, status)
 
     def _boot_image(self, image_id):
-        name = rand_name('scenario-server-')
-        client = self.compute_client
-        flavor_id = self.config.compute.flavor_ref
-        LOG.debug("name:%s, image:%s" % (name, image_id))
-        server = client.servers.create(name=name,
-                                       image=image_id,
-                                       flavor=flavor_id,
-                                       key_name=self.keypair.name)
-        self.addCleanup(self.compute_client.servers.delete, server)
-        self.assertEqual(name, server.name)
-        self._wait_for_server_status(server, 'ACTIVE')
-        server = client.servers.get(server)  # getting network information
-        LOG.debug("server:%s" % server)
-        return server
+        create_kwargs = {
+            'key_name': self.keypair.name
+        }
+        return self.create_server(self.compute_client, image=image_id,
+                                  create_kwargs=create_kwargs)
 
     def _add_keypair(self):
-        name = rand_name('scenario-keypair-')
-        self.keypair = self.compute_client.keypairs.create(name=name)
-        self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
-        self.assertEqual(name, self.keypair.name)
+        self.keypair = self.create_keypair()
 
     def _create_floating_ip(self):
         floating_ip = self.compute_client.floating_ips.create()
@@ -93,25 +81,6 @@
     def _add_floating_ip(self, server, floating_ip):
         server.add_floating_ip(floating_ip)
 
-    def _create_security_group_rule(self):
-        sgs = self.compute_client.security_groups.list()
-        for sg in sgs:
-            if sg.name == 'default':
-                secgroup = sg
-
-        ruleset = {
-            # ssh
-            'ip_protocol': 'tcp',
-            'from_port': 22,
-            'to_port': 22,
-            'cidr': '0.0.0.0/0',
-            'group_id': None
-        }
-        sg_rule = self.compute_client.security_group_rules.create(secgroup.id,
-                                                                  **ruleset)
-        self.addCleanup(self.compute_client.security_group_rules.delete,
-                        sg_rule.id)
-
     def _remote_client_to_server(self, server_or_ip):
         if isinstance(server_or_ip, basestring):
             ip = server_or_ip
@@ -163,20 +132,7 @@
             self.volume_client.volumes, volume.id, status)
 
     def _create_volume(self, snapshot_id=None):
-        name = rand_name('scenario-volume-')
-        LOG.debug("volume display-name:%s" % name)
-        volume = self.volume_client.volumes.create(size=1,
-                                                   display_name=name,
-                                                   snapshot_id=snapshot_id)
-        LOG.debug("volume created:%s" % volume.display_name)
-
-        def cleaner():
-            self._wait_for_volume_status(volume, 'available')
-            self.volume_client.volumes.delete(volume)
-        self.addCleanup(cleaner)
-        self._wait_for_volume_status(volume, 'available')
-        self.assertEqual(name, volume.display_name)
-        return volume
+        return self.create_volume(snapshot_id=snapshot_id)
 
     def _attach_volume(self, server, volume):
         attach_volume_client = self.compute_client.volumes.create_server_volume
@@ -223,7 +179,7 @@
     def test_stamp_pattern(self):
         # prepare for booting a instance
         self._add_keypair()
-        self._create_security_group_rule()
+        self.create_loginable_secgroup_rule()
 
         # boot an instance and create a timestamp file in it
         volume = self._create_volume()
diff --git a/tempest/scenario/test_volume_snapshot_pattern.py b/tempest/scenario/test_volume_snapshot_pattern.py
new file mode 100644
index 0000000..8fa177e
--- /dev/null
+++ b/tempest/scenario/test_volume_snapshot_pattern.py
@@ -0,0 +1,96 @@
+# 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')
+        return self.create_volume(name=vol_name, imageRef=img_uuid)
+
+    def _boot_instance_from_volume(self, vol_id):
+        # 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
+        }
+        return self.create_server(self.compute_client,
+                                  create_kwargs=create_kwargs)
+
+    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')
+        return self.create_volume(name=vol_name, snapshot_id=snap_id)
+
+    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/services/botoclients.py b/tempest/services/botoclients.py
index 32ec109..66fb7af 100644
--- a/tempest/services/botoclients.py
+++ b/tempest/services/botoclients.py
@@ -180,7 +180,7 @@
         :return: Returns with the first available zone name
         """
         for zone in self.get_all_zones():
-            #NOTE(afazekas): zone.region_name was None
+            # NOTE(afazekas): zone.region_name was None
             if (zone.state == "available" and
                 zone.region.name == self.connection_data["region"].name):
                 return zone.name
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 1b965f3..5f58c43 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -79,9 +79,9 @@
         return self.delete("flavors/%s" % str(flavor_id))
 
     def is_resource_deleted(self, id):
-        #Did not use get_flavor_details(id) for verification as it gives
-        #200 ok even for deleted id. LP #981263
-        #we can remove the loop here and use get by ID when bug gets sortedout
+        # Did not use get_flavor_details(id) for verification as it gives
+        # 200 ok even for deleted id. LP #981263
+        # we can remove the loop here and use get by ID when bug gets sortedout
         resp, flavors = self.list_flavors_with_detail()
         for flavor in flavors:
             if flavor['id'] == id:
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
index 90b2096..a464816 100644
--- a/tempest/services/compute/json/keypairs_client.py
+++ b/tempest/services/compute/json/keypairs_client.py
@@ -30,11 +30,11 @@
     def list_keypairs(self):
         resp, body = self.get("os-keypairs")
         body = json.loads(body)
-        #Each returned keypair is embedded within an unnecessary 'keypair'
-        #element which is a deviation from other resources like floating-ips,
-        #servers, etc. A bug?
-        #For now we shall adhere to the spec, but the spec for keypairs
-        #is yet to be found
+        # Each returned keypair is embedded within an unnecessary 'keypair'
+        # element which is a deviation from other resources like floating-ips,
+        # servers, etc. A bug?
+        # For now we shall adhere to the spec, but the spec for keypairs
+        # is yet to be found
         return resp, body['keypairs']
 
     def get_keypair(self, key_name):
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index a6451df..6fbb9e3 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -49,6 +49,11 @@
             if k == '{%s}ephemeral' % XMLNS_OS_FLV_EXT_DATA:
                 k = 'OS-FLV-EXT-DATA:ephemeral'
 
+            if k == 'extra_specs':
+                k = 'OS-FLV-WITH-EXT-SPECS:extra_specs'
+                flavor[k] = dict(v)
+                continue
+
             try:
                 v = int(v)
             except ValueError:
@@ -117,9 +122,9 @@
         return self.delete("flavors/%s" % str(flavor_id), self.headers)
 
     def is_resource_deleted(self, id):
-        #Did not use get_flavor_details(id) for verification as it gives
-        #200 ok even for deleted id. LP #981263
-        #we can remove the loop here and use get by ID when bug gets sortedout
+        # Did not use get_flavor_details(id) for verification as it gives
+        # 200 ok even for deleted id. LP #981263
+        # we can remove the loop here and use get by ID when bug gets sortedout
         resp, flavors = self.list_flavors_with_detail()
         for flavor in flavors:
             if flavor['id'] == id:
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 12e7034..5c7a629 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -350,7 +350,7 @@
         addrs = []
         for child in node.getchildren():
             addrs.append({'version': int(child.get('version')),
-                         'addr': child.get('version')})
+                         'addr': child.get('addr')})
         return {node.get('id'): addrs}
 
     def list_addresses(self, server_id):
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index 56a1a72..0a56e84 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -63,11 +63,12 @@
 
     def update_user(self, user_id, name, **kwargs):
         """Updates a user."""
-        email = kwargs.get('email', None)
-        en = kwargs.get('enabled', True)
-        project_id = kwargs.get('project_id', None)
-        description = kwargs.get('description', None)
-        domain_id = kwargs.get('domain_id', 'default')
+        resp, body = self.get_user(user_id)
+        email = kwargs.get('email', body['email'])
+        en = kwargs.get('enabled', body['enabled'])
+        project_id = kwargs.get('project_id', body['project_id'])
+        description = kwargs.get('description', body['description'])
+        domain_id = kwargs.get('domain_id', body['domain_id'])
         post_body = {
             'name': name,
             'email': email,
@@ -149,6 +150,17 @@
         body = json.loads(body)
         return resp, body['role']
 
+    def update_role(self, name, role_id):
+        """Create a Role."""
+        post_body = {
+            'name': name
+        }
+        post_body = json.dumps({'role': post_body})
+        resp, body = self.patch('roles/%s' % str(role_id), post_body,
+                                self.headers)
+        body = json.loads(body)
+        return resp, body['role']
+
     def delete_role(self, role_id):
         """Delete a role."""
         resp, body = self.delete('roles/%s' % str(role_id))
@@ -222,6 +234,107 @@
         resp, body = self.delete("auth/tokens", headers=headers)
         return resp, body
 
+    def create_group(self, name, **kwargs):
+        """Creates a group."""
+        description = kwargs.get('description', None)
+        domain_id = kwargs.get('domain_id', 'default')
+        project_id = kwargs.get('project_id', None)
+        post_body = {
+            'description': description,
+            'domain_id': domain_id,
+            'project_id': project_id,
+            'name': name
+        }
+        post_body = json.dumps({'group': post_body})
+        resp, body = self.post('groups', post_body, self.headers)
+        body = json.loads(body)
+        return resp, body['group']
+
+    def delete_group(self, group_id):
+        """Delete a group."""
+        resp, body = self.delete('groups/%s' % str(group_id))
+        return resp, body
+
+    def assign_user_role_on_project(self, project_id, user_id, role_id):
+        """Add roles to a user on a project."""
+        resp, body = self.put('projects/%s/users/%s/roles/%s' %
+                              (project_id, user_id, role_id), None,
+                              self.headers)
+        return resp, body
+
+    def assign_user_role_on_domain(self, domain_id, user_id, role_id):
+        """Add roles to a user on a domain."""
+        resp, body = self.put('domains/%s/users/%s/roles/%s' %
+                              (domain_id, user_id, role_id), None,
+                              self.headers)
+        return resp, body
+
+    def list_user_roles_on_project(self, project_id, user_id):
+        """list roles of a user on a project."""
+        resp, body = self.get('projects/%s/users/%s/roles' %
+                              (project_id, user_id))
+        body = json.loads(body)
+        return resp, body['roles']
+
+    def list_user_roles_on_domain(self, domain_id, user_id):
+        """list roles of a user on a domain."""
+        resp, body = self.get('domains/%s/users/%s/roles' %
+                              (domain_id, user_id))
+        body = json.loads(body)
+        return resp, body['roles']
+
+    def revoke_role_from_user_on_project(self, project_id, user_id, role_id):
+        """Delete role of a user on a project."""
+        resp, body = self.delete('projects/%s/users/%s/roles/%s' %
+                                 (project_id, user_id, role_id))
+        return resp, body
+
+    def revoke_role_from_user_on_domain(self, domain_id, user_id, role_id):
+        """Delete role of a user on a domain."""
+        resp, body = self.delete('domains/%s/users/%s/roles/%s' %
+                                 (domain_id, user_id, role_id))
+        return resp, body
+
+    def assign_group_role_on_project(self, project_id, group_id, role_id):
+        """Add roles to a user on a project."""
+        resp, body = self.put('projects/%s/groups/%s/roles/%s' %
+                              (project_id, group_id, role_id), None,
+                              self.headers)
+        return resp, body
+
+    def assign_group_role_on_domain(self, domain_id, group_id, role_id):
+        """Add roles to a user on a domain."""
+        resp, body = self.put('domains/%s/groups/%s/roles/%s' %
+                              (domain_id, group_id, role_id), None,
+                              self.headers)
+        return resp, body
+
+    def list_group_roles_on_project(self, project_id, group_id):
+        """list roles of a user on a project."""
+        resp, body = self.get('projects/%s/groups/%s/roles' %
+                              (project_id, group_id))
+        body = json.loads(body)
+        return resp, body['roles']
+
+    def list_group_roles_on_domain(self, domain_id, group_id):
+        """list roles of a user on a domain."""
+        resp, body = self.get('domains/%s/groups/%s/roles' %
+                              (domain_id, group_id))
+        body = json.loads(body)
+        return resp, body['roles']
+
+    def revoke_role_from_group_on_project(self, project_id, group_id, role_id):
+        """Delete role of a user on a project."""
+        resp, body = self.delete('projects/%s/groups/%s/roles/%s' %
+                                 (project_id, group_id, role_id))
+        return resp, body
+
+    def revoke_role_from_group_on_domain(self, domain_id, group_id, role_id):
+        """Delete role of a user on a domain."""
+        resp, body = self.delete('domains/%s/groups/%s/roles/%s' %
+                                 (domain_id, group_id, role_id))
+        return resp, body
+
 
 class V3TokenClientJSON(RestClient):
 
diff --git a/tempest/services/identity/v3/xml/identity_client.py b/tempest/services/identity/v3/xml/identity_client.py
index 571b491..03e06dc 100644
--- a/tempest/services/identity/v3/xml/identity_client.py
+++ b/tempest/services/identity/v3/xml/identity_client.py
@@ -52,6 +52,14 @@
                 array.append(xml_to_json(child))
         return array
 
+    def _parse_roles(self, node):
+        array = []
+        for child in node.getchildren():
+            tag_list = child.tag.split('}', 1)
+            if tag_list[1] == "role":
+                array.append(xml_to_json(child))
+        return array
+
     def _parse_array(self, node):
         array = []
         for child in node.getchildren():
@@ -95,11 +103,12 @@
 
     def update_user(self, user_id, name, **kwargs):
         """Updates a user."""
-        email = kwargs.get('email', None)
-        en = kwargs.get('enabled', True)
-        project_id = kwargs.get('project_id', None)
-        domain_id = kwargs.get('domain_id', 'default')
-        description = kwargs.get('description', None)
+        resp, body = self.get_user(user_id)
+        email = kwargs.get('email', body['email'])
+        en = kwargs.get('enabled', body['enabled'])
+        project_id = kwargs.get('project_id', body['project_id'])
+        description = kwargs.get('description', body['description'])
+        domain_id = kwargs.get('domain_id', body['domain_id'])
         update_user = Element("user",
                               xmlns=XMLNS,
                               name=name,
@@ -182,6 +191,17 @@
         body = self._parse_body(etree.fromstring(body))
         return resp, body
 
+    def update_role(self, name, role_id):
+        """Updates a Role."""
+        post_body = Element("role",
+                            xmlns=XMLNS,
+                            name=name)
+        resp, body = self.patch('roles/%s' % str(role_id),
+                                str(Document(post_body)),
+                                self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
     def delete_role(self, role_id):
         """Delete a role."""
         resp, body = self.delete('roles/%s' % str(role_id),
@@ -257,6 +277,107 @@
         resp, body = self.delete("auth/tokens", headers=headers)
         return resp, body
 
+    def create_group(self, name, **kwargs):
+        """Creates a group."""
+        description = kwargs.get('description', None)
+        domain_id = kwargs.get('domain_id', 'default')
+        project_id = kwargs.get('project_id', None)
+        post_body = Element("group",
+                            xmlns=XMLNS,
+                            name=name,
+                            description=description,
+                            domain_id=domain_id,
+                            project_id=project_id)
+        resp, body = self.post('groups', str(Document(post_body)),
+                               self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def delete_group(self, group_id):
+        """Delete a group."""
+        resp, body = self.delete('groups/%s' % group_id, self.headers)
+        return resp, body
+
+    def assign_user_role_on_project(self, project_id, user_id, role_id):
+        """Add roles to a user on a project."""
+        resp, body = self.put('projects/%s/users/%s/roles/%s' %
+                              (project_id, user_id, role_id), '',
+                              self.headers)
+        return resp, body
+
+    def assign_user_role_on_domain(self, domain_id, user_id, role_id):
+        """Add roles to a user on a domain."""
+        resp, body = self.put('domains/%s/users/%s/roles/%s' %
+                              (domain_id, user_id, role_id), '',
+                              self.headers)
+        return resp, body
+
+    def list_user_roles_on_project(self, project_id, user_id):
+        """list roles of a user on a project."""
+        resp, body = self.get('projects/%s/users/%s/roles' %
+                              (project_id, user_id), self.headers)
+        body = self._parse_roles(etree.fromstring(body))
+        return resp, body
+
+    def list_user_roles_on_domain(self, domain_id, user_id):
+        """list roles of a user on a domain."""
+        resp, body = self.get('domains/%s/users/%s/roles' %
+                              (domain_id, user_id), self.headers)
+        body = self._parse_roles(etree.fromstring(body))
+        return resp, body
+
+    def revoke_role_from_user_on_project(self, project_id, user_id, role_id):
+        """Delete role of a user on a project."""
+        resp, body = self.delete('projects/%s/users/%s/roles/%s' %
+                                 (project_id, user_id, role_id), self.headers)
+        return resp, body
+
+    def revoke_role_from_user_on_domain(self, domain_id, user_id, role_id):
+        """Delete role of a user on a domain."""
+        resp, body = self.delete('domains/%s/users/%s/roles/%s' %
+                                 (domain_id, user_id, role_id), self.headers)
+        return resp, body
+
+    def assign_group_role_on_project(self, project_id, group_id, role_id):
+        """Add roles to a user on a project."""
+        resp, body = self.put('projects/%s/groups/%s/roles/%s' %
+                              (project_id, group_id, role_id), '',
+                              self.headers)
+        return resp, body
+
+    def assign_group_role_on_domain(self, domain_id, group_id, role_id):
+        """Add roles to a user on a domain."""
+        resp, body = self.put('domains/%s/groups/%s/roles/%s' %
+                              (domain_id, group_id, role_id), '',
+                              self.headers)
+        return resp, body
+
+    def list_group_roles_on_project(self, project_id, group_id):
+        """list roles of a user on a project."""
+        resp, body = self.get('projects/%s/groups/%s/roles' %
+                              (project_id, group_id), self.headers)
+        body = self._parse_roles(etree.fromstring(body))
+        return resp, body
+
+    def list_group_roles_on_domain(self, domain_id, group_id):
+        """list roles of a user on a domain."""
+        resp, body = self.get('domains/%s/groups/%s/roles' %
+                              (domain_id, group_id), self.headers)
+        body = self._parse_roles(etree.fromstring(body))
+        return resp, body
+
+    def revoke_role_from_group_on_project(self, project_id, group_id, role_id):
+        """Delete role of a user on a project."""
+        resp, body = self.delete('projects/%s/groups/%s/roles/%s' %
+                                 (project_id, group_id, role_id), self.headers)
+        return resp, body
+
+    def revoke_role_from_group_on_domain(self, domain_id, group_id, role_id):
+        """Delete role of a user on a domain."""
+        resp, body = self.delete('domains/%s/groups/%s/roles/%s' %
+                                 (domain_id, group_id, role_id), self.headers)
+        return resp, body
+
 
 class V3TokenClientXML(RestClientXML):
 
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index bd48068..1921d78 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -247,13 +247,13 @@
         data = json.loads(data)
         return resp, data
 
-    #NOTE(afazekas): just for the wait function
+    # NOTE(afazekas): just for the wait function
     def _get_image_status(self, image_id):
         resp, meta = self.get_image_meta(image_id)
         status = meta['status']
         return status
 
-    #NOTE(afazkas): Wait reinvented again. It is not in the correct layer
+    # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
     def wait_for_image_status(self, image_id, status):
         """Waits for a Image to reach a given status."""
         start_time = time.time()
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 2c808a9..588dc8f 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -17,22 +17,24 @@
 from tempest.common.rest_client import RestClient
 
 
-class NetworkClient(RestClient):
+class NetworkClientJSON(RestClient):
 
     """
     Tempest REST client for Neutron. Uses v2 of the Neutron API, since the
     V1 API has been removed from the code base.
 
-    Implements create, delete, list and show for the basic Neutron
-    abstractions (networks, sub-networks and ports):
+    Implements create, delete, update, list and show for the basic Neutron
+    abstractions (networks, sub-networks, routers and ports):
+
+    Implements add/remove interface to router using subnet ID / port ID
 
     It also implements list, show, update and reset for OpenStack Networking
     quotas
     """
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(NetworkClient, self).__init__(config, username, password,
-                                            auth_url, tenant_name)
+        super(NetworkClientJSON, self).__init__(config, username, password,
+                                                auth_url, tenant_name)
         self.service = self.config.network.catalog_type
         self.version = '2.0'
         self.uri_prefix = "v%s" % (self.version)
@@ -55,6 +57,17 @@
         body = json.loads(body)
         return resp, body
 
+    def create_bulk_network(self, count, names):
+        network_list = list()
+        for i in range(count):
+            network_list.append({'name': names[i]})
+        post_body = {'networks': network_list}
+        body = json.dumps(post_body)
+        uri = '%s/networks' % (self.uri_prefix)
+        resp, body = self.post(uri, headers=self.headers, body=body)
+        body = json.loads(body)
+        return resp, body
+
     def show_network(self, uuid):
         uri = '%s/networks/%s' % (self.uri_prefix, uuid)
         resp, body = self.get(uri, self.headers)
@@ -95,15 +108,14 @@
         body = json.loads(body)
         return resp, body
 
-    def create_port(self, network_id, state=None):
-        if not state:
-            state = True
+    def create_port(self, network_id, **kwargs):
         post_body = {
             'port': {
                 'network_id': network_id,
-                'admin_state_up': state,
             }
         }
+        for key, val in kwargs.items():
+            post_body['port'][key] = val
         body = json.dumps(post_body)
         uri = '%s/ports' % (self.uri_prefix)
         resp, body = self.post(uri, headers=self.headers, body=body)
@@ -187,3 +199,89 @@
         resp, body = self.put(uri, body=body, headers=self.headers)
         body = json.loads(body)
         return resp, body
+
+    def list_routers(self):
+        uri = '%s/routers' % (self.uri_prefix)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def create_router(self, name, **kwargs):
+        post_body = {
+            'router': {
+                'name': name,
+            }
+        }
+        post_body['router']['admin_state_up'] = kwargs.get(
+            'admin_state_up', True)
+        post_body['router']['external_gateway_info'] = kwargs.get(
+            'external_gateway_info', None)
+        body = json.dumps(post_body)
+        uri = '%s/routers' % (self.uri_prefix)
+        resp, body = self.post(uri, headers=self.headers, body=body)
+        body = json.loads(body)
+        return resp, body
+
+    def delete_router(self, router_id):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        resp, body = self.delete(uri, self.headers)
+        return resp, body
+
+    def show_router(self, router_id):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def update_router(self, router_id, **kwargs):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        update_body = {}
+        update_body['name'] = kwargs.get('name', body['router']['name'])
+        update_body['admin_state_up'] = kwargs.get(
+            'admin_state_up', body['router']['admin_state_up'])
+        # Must uncomment/modify these lines once LP question#233187 is solved
+        #update_body['external_gateway_info'] = kwargs.get(
+        # 'external_gateway_info', body['router']['external_gateway_info'])
+        update_body = dict(router=update_body)
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def add_router_interface_with_subnet_id(self, router_id, subnet_id):
+        uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
+              router_id)
+        update_body = {"subnet_id": subnet_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def add_router_interface_with_port_id(self, router_id, port_id):
+        uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
+              router_id)
+        update_body = {"port_id": port_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
+        uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
+              router_id)
+        update_body = {"subnet_id": subnet_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def remove_router_interface_with_port_id(self, router_id, port_id):
+        uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
+              router_id)
+        update_body = {"port_id": port_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body, self.headers)
+        body = json.loads(body)
+        return resp, body
diff --git a/tempest/services/network/xml/__init__.py b/tempest/services/network/xml/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/network/xml/__init__.py
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
new file mode 100755
index 0000000..d4fb656
--- /dev/null
+++ b/tempest/services/network/xml/network_client.py
@@ -0,0 +1,172 @@
+# 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 lxml import etree
+import xml.etree.ElementTree as ET
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class NetworkClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(NetworkClientXML, self).__init__(config, username, password,
+                                               auth_url, tenant_name)
+        self.service = self.config.network.catalog_type
+        self.version = '2.0'
+        self.uri_prefix = "v%s" % (self.version)
+
+    def list_networks(self):
+        uri = '%s/networks' % (self.uri_prefix)
+        resp, body = self.get(uri, self.headers)
+        networks = self._parse_array(etree.fromstring(body))
+        networks = {"networks": networks}
+        return resp, networks
+
+    def create_network(self, name):
+        uri = '%s/networks' % (self.uri_prefix)
+        post_body = Element("network")
+        p2 = Element("name", name)
+        post_body.append(p2)
+        resp, body = self.post(uri, str(Document(post_body)), self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def create_bulk_network(self, count, names):
+        uri = '%s/networks' % (self.uri_prefix)
+        post_body = Element("networks")
+        for i in range(count):
+                p1 = Element("network")
+                p2 = Element("name", names[i])
+                p1.append(p2)
+                post_body.append(p1)
+        resp, body = self.post(uri, str(Document(post_body)), self.headers)
+        networks = self._parse_array(etree.fromstring(body))
+        networks = {"networks": networks}
+        return resp, networks
+
+    def delete_network(self, uuid):
+        uri = '%s/networks/%s' % (self.uri_prefix, str(uuid))
+        return self.delete(uri, self.headers)
+
+    def show_network(self, uuid):
+        uri = '%s/networks/%s' % (self.uri_prefix, str(uuid))
+        resp, body = self.get(uri, self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def create_subnet(self, net_uuid, cidr):
+        uri = '%s/subnets' % (self.uri_prefix)
+        subnet = Element("subnet")
+        p2 = Element("network_id", net_uuid)
+        p3 = Element("cidr", cidr)
+        p4 = Element("ip_version", 4)
+        subnet.append(p2)
+        subnet.append(p3)
+        subnet.append(p4)
+        resp, body = self.post(uri, str(Document(subnet)), self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def delete_subnet(self, subnet_id):
+        uri = '%s/subnets/%s' % (self.uri_prefix, str(subnet_id))
+        return self.delete(uri, self.headers)
+
+    def list_subnets(self):
+        uri = '%s/subnets' % (self.uri_prefix)
+        resp, body = self.get(uri, self.headers)
+        subnets = self._parse_array(etree.fromstring(body))
+        subnets = {"subnets": subnets}
+        return resp, subnets
+
+    def show_subnet(self, uuid):
+        uri = '%s/subnets/%s' % (self.uri_prefix, str(uuid))
+        resp, body = self.get(uri, self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def create_port(self, net_uuid, **kwargs):
+        uri = '%s/ports' % (self.uri_prefix)
+        port = Element("port")
+        p1 = Element('network_id', net_uuid)
+        port.append(p1)
+        for key, val in kwargs.items():
+            key = Element(key, val)
+            port.append(key)
+        resp, body = self.post(uri, str(Document(port)), self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def delete_port(self, port_id):
+        uri = '%s/ports/%s' % (self.uri_prefix, str(port_id))
+        return self.delete(uri, self.headers)
+
+    def _parse_array(self, node):
+        array = []
+        for child in node.getchildren():
+            array.append(xml_to_json(child))
+        return array
+
+    def list_ports(self):
+        url = '%s/ports' % (self.uri_prefix)
+        resp, body = self.get(url, self.headers)
+        ports = self._parse_array(etree.fromstring(body))
+        ports = {"ports": ports}
+        return resp, ports
+
+    def show_port(self, port_id):
+        uri = '%s/ports/%s' % (self.uri_prefix, str(port_id))
+        resp, body = self.get(uri, self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def update_port(self, port_id, name):
+        uri = '%s/ports/%s' % (self.uri_prefix, str(port_id))
+        port = Element("port")
+        p2 = Element("name", name)
+        port.append(p2)
+        resp, body = self.put(uri, str(Document(port)), self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def update_subnet(self, subnet_id, name):
+        uri = '%s/subnets/%s' % (self.uri_prefix, str(subnet_id))
+        subnet = Element("subnet")
+        p2 = Element("name", name)
+        subnet.append(p2)
+        resp, body = self.put(uri, str(Document(subnet)), self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def update_network(self, net_id, name):
+        uri = '%s/networks/%s' % (self.uri_prefix, str(net_id))
+        network = Element("network")
+        p2 = Element("name", name)
+        network.append(p2)
+        resp, body = self.put(uri, str(Document(network)), self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+
+def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
+    body = ET.fromstring(xml_returned_body)
+    root_tag = body.tag
+    if root_tag.startswith("{"):
+        ns, root_tag = root_tag.split("}", 1)
+    body = xml_to_json(etree.fromstring(xml_returned_body))
+    body = {root_tag: body}
+    return body
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index a71a287..8defbbb 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -84,10 +84,13 @@
             DEFAULT:  Python-List returned in response body
         """
 
-        url = '?format=%s' % self.format
         if params:
-            url += '&%s' + urllib.urlencode(params)
+            if 'format' not in params:
+                params['format'] = self.format
+        else:
+            params = {'format': self.format}
 
+        url = '?' + urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body
@@ -99,7 +102,7 @@
         super(AccountClientCustomizedHeader, self).__init__(config, username,
                                                             password, auth_url,
                                                             tenant_name)
-        #Overwrites json-specific header encoding in RestClient
+        # Overwrites json-specific header encoding in RestClient
         self.service = self.config.object_storage.catalog_type
         self.format = 'json'
 
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 93477fa..dd5f3ec 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -26,7 +26,7 @@
         super(ContainerClient, self).__init__(config, username, password,
                                               auth_url, tenant_name)
 
-        #Overwrites json-specific header encoding in RestClient
+        # Overwrites json-specific header encoding in RestClient
         self.headers = {}
         self.service = self.config.object_storage.catalog_type
         self.format = 'json'
@@ -94,8 +94,8 @@
             item count is beyond 10,000 item listing limit.
             Does not require any paramaters aside from container name.
         """
-        #TODO(dwalleck):  Rewite using json format to avoid newlines at end of
-        #obj names. Set limit to API limit - 1 (max returned items = 9999)
+        # TODO(dwalleck):  Rewite using json format to avoid newlines at end of
+        # obj names. Set limit to API limit - 1 (max returned items = 9999)
         limit = 9999
         if params is not None:
             if 'limit' in params:
@@ -114,16 +114,16 @@
 
         if len(objlist) >= limit:
 
-            #Increment marker
+            # Increment marker
             marker = objlist[len(objlist) - 1]
 
-            #Get the next chunk of the list
+            # Get the next chunk of the list
             objlist.extend(_list_all_container_objects(container,
                                                       params={'marker': marker,
                                                               'limit': limit}))
             return objlist
         else:
-            #Return final, complete list
+            # Return final, complete list
             return objlist"""
 
     def list_container_contents(self, container, params=None):
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index c894612..181838e 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -155,7 +155,7 @@
         super(ObjectClientCustomizedHeader, self).__init__(config, username,
                                                            password, auth_url,
                                                            tenant_name)
-        #Overwrites json-specific header encoding in RestClient
+        # Overwrites json-specific header encoding in RestClient
         self.service = self.config.object_storage.catalog_type
         self.format = 'json'
 
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index 034b452..ce2da90 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -76,11 +76,11 @@
         body = json.loads(body)
         return resp, body['snapshot']
 
-    #NOTE(afazekas): just for the wait function
+    # NOTE(afazekas): just for the wait function
     def _get_snapshot_status(self, snapshot_id):
         resp, body = self.get_snapshot(snapshot_id)
         status = body['status']
-        #NOTE(afazekas): snapshot can reach an "error"
+        # NOTE(afazekas): snapshot can reach an "error"
         # state in a "normal" lifecycle
         if (status == 'error'):
             raise exceptions.SnapshotBuildErrorException(
@@ -88,7 +88,7 @@
 
         return status
 
-    #NOTE(afazkas): Wait reinvented again. It is not in the correct layer
+    # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
     def wait_for_snapshot_status(self, snapshot_id, status):
         """Waits for a Snapshot to reach a given status."""
         start_time = time.time()
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 017ca95..51c46da 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -81,7 +81,7 @@
         display_name: Optional snapshot Name.
         display_description: User friendly snapshot description.
         """
-        #NOTE(afazekas): it should use the volume namaspace
+        # NOTE(afazekas): it should use the volume namaspace
         snapshot = Element("snapshot", xmlns=XMLNS_11, volume_id=volume_id)
         for key, value in kwargs.items():
             snapshot.add_attr(key, value)
@@ -90,11 +90,11 @@
         body = xml_to_json(etree.fromstring(body))
         return resp, body
 
-    #NOTE(afazekas): just for the wait function
+    # NOTE(afazekas): just for the wait function
     def _get_snapshot_status(self, snapshot_id):
         resp, body = self.get_snapshot(snapshot_id)
         status = body['status']
-        #NOTE(afazekas): snapshot can reach an "error"
+        # NOTE(afazekas): snapshot can reach an "error"
         # state in a "normal" lifecycle
         if (status == 'error'):
             raise exceptions.SnapshotBuildErrorException(
@@ -102,7 +102,7 @@
 
         return status
 
-    #NOTE(afazkas): Wait reinvented again. It is not in the correct layer
+    # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
     def wait_for_snapshot_status(self, snapshot_id, status):
         """Waits for a Snapshot to reach a given status."""
         start_time = time.time()
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 8eda26b..eaa3ae0 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -103,7 +103,7 @@
         :param imageRef: When specified the volume is created from this
                          image
         """
-        #NOTE(afazekas): it should use a volume namespace
+        # NOTE(afazekas): it should use a volume namespace
         volume = Element("volume", xmlns=XMLNS_11, size=size)
 
         if 'metadata' in kwargs:
diff --git a/tempest/stress/actions/unit_test.py b/tempest/stress/actions/unit_test.py
new file mode 100644
index 0000000..95cc1bc
--- /dev/null
+++ b/tempest/stress/actions/unit_test.py
@@ -0,0 +1,79 @@
+# 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 importutils
+import tempest.stress.stressaction as stressaction
+
+
+class SetUpClassRunTime(object):
+
+    process = 'process'
+    action = 'action'
+    application = 'application'
+
+    allowed = set((process, action, application))
+
+    @classmethod
+    def validate(cls, name):
+        if name not in cls.allowed:
+            raise KeyError("\'%s\' not a valid option" % name)
+
+
+class UnitTest(stressaction.StressAction):
+    """This is a special action for running existing unittests as stress test.
+       You need to pass ``test_method`` and ``class_setup_per``
+       using ``kwargs`` in the JSON descriptor;
+       ``test_method`` should be the fully qualified name of a unittest,
+       ``class_setup_per`` should be one from:
+           ``application``: once in the stress job lifetime
+           ``process``: once in the worker process lifetime
+           ``action``: on each action
+       Not all combination working in every case.
+    """
+
+    def setUp(self, **kwargs):
+        method = kwargs['test_method'].split('.')
+        self.test_method = method.pop()
+        self.klass = importutils.import_class('.'.join(method))
+        # valid options are 'process', 'application' , 'action'
+        self.class_setup_per = kwargs.get('class_setup_per',
+                                          SetUpClassRunTime.process)
+        SetUpClassRunTime.validate(self.class_setup_per)
+
+        if self.class_setup_per == SetUpClassRunTime.application:
+            self.klass.setUpClass()
+        self.setupclass_called = False
+
+    def run_core(self):
+        res = self.klass(self.test_method).run()
+        if res.errors:
+            raise RuntimeError(res.errors)
+
+    def run(self):
+        if self.class_setup_per != SetUpClassRunTime.application:
+            if (self.class_setup_per == SetUpClassRunTime.action
+                or self.setupclass_called is False):
+                self.klass.setUpClass()
+                self.setupclass_called = True
+
+            self.run_core()
+
+            if (self.class_setup_per == SetUpClassRunTime.action):
+                self.klass.tearDownClass()
+        else:
+            self.run_core()
+
+    def tearDown(self):
+        if self.class_setup_per != SetUpClassRunTime.action:
+            self.klass.tearDownClass()
diff --git a/tempest/stress/etc/sample-unit-test.json b/tempest/stress/etc/sample-unit-test.json
new file mode 100644
index 0000000..b388bfe
--- /dev/null
+++ b/tempest/stress/etc/sample-unit-test.json
@@ -0,0 +1,8 @@
+[{"action": "tempest.stress.actions.unit_test.UnitTest",
+  "threads": 8,
+  "use_admin": false,
+  "use_isolated_tenants": false,
+  "kwargs": {"test_method": "tempest.cli.simple_read_only.test_glance.SimpleReadOnlyGlanceClientTest.test_glance_fake_action",
+             "class_setup_per": "process"}
+  }
+]
diff --git a/tempest/test.py b/tempest/test.py
index 6c304c3..68cedf0 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
 
@@ -24,9 +25,7 @@
 import testtools
 
 from tempest import clients
-from tempest.common.utils.data_utils import rand_name
 from tempest import config
-from tempest import exceptions
 from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
@@ -93,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,
@@ -100,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):
@@ -143,85 +167,6 @@
             cls.config.identity.uri
         )
 
-    @classmethod
-    def _get_isolated_creds(cls, admin=False):
-        """
-        Creates a new set of user/tenant/password credentials for a
-        **regular** user of the Compute API so that a test case can
-        operate in an isolated tenant container.
-        """
-        admin_client = cls._get_identity_admin_client()
-        password = "pass"
-
-        while True:
-            try:
-                rand_name_root = rand_name(cls.__name__)
-                if cls.isolated_creds:
-                # Main user already created. Create the alt or admin one...
-                    if admin:
-                        rand_name_root += '-admin'
-                    else:
-                        rand_name_root += '-alt'
-                tenant_name = rand_name_root + "-tenant"
-                tenant_desc = tenant_name + "-desc"
-
-                resp, tenant = admin_client.create_tenant(
-                    name=tenant_name, description=tenant_desc)
-                break
-            except exceptions.Duplicate:
-                if cls.config.compute.allow_tenant_reuse:
-                    tenant = admin_client.get_tenant_by_name(tenant_name)
-                    LOG.info('Re-using existing tenant %s', tenant)
-                    break
-
-        while True:
-            try:
-                rand_name_root = rand_name(cls.__name__)
-                if cls.isolated_creds:
-                # Main user already created. Create the alt one...
-                    rand_name_root += '-alt'
-                username = rand_name_root + "-user"
-                email = rand_name_root + "@example.com"
-                resp, user = admin_client.create_user(username,
-                                                      password,
-                                                      tenant['id'],
-                                                      email)
-                break
-            except exceptions.Duplicate:
-                if cls.config.compute.allow_tenant_reuse:
-                    user = admin_client.get_user_by_username(tenant['id'],
-                                                             username)
-                    LOG.info('Re-using existing user %s', user)
-                    break
-        # Store the complete creds (including UUID ids...) for later
-        # but return just the username, tenant_name, password tuple
-        # that the various clients will use.
-        cls.isolated_creds.append((user, tenant))
-
-        # Assign admin role if this is for admin creds
-        if admin:
-            _, roles = admin_client.list_roles()
-            role = None
-            try:
-                _, roles = admin_client.list_roles()
-                role = next(r for r in roles if r['name'] == 'admin')
-            except StopIteration:
-                msg = "No admin role found"
-                raise exceptions.NotFound(msg)
-            admin_client.assign_user_role(tenant['id'], user['id'], role['id'])
-
-        return username, tenant_name, password
-
-    @classmethod
-    def _clear_isolated_creds(cls):
-        if not cls.isolated_creds:
-            return
-        admin_client = cls._get_identity_admin_client()
-
-        for user, tenant in cls.isolated_creds:
-            admin_client.delete_user(user['id'])
-            admin_client.delete_tenant(tenant['id'])
-
 
 def call_until_true(func, duration, sleep_for):
     """
@@ -244,70 +189,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/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)
 
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index b2632f1..3b1b107 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -72,7 +72,7 @@
         cls.flavor_ref = cls.config.compute.flavor_ref
         cls.flavor_ref_alt = cls.config.compute.flavor_ref_alt
 
-    #NOTE(afazekas): Mimics the helper method used in the api tests
+    # NOTE(afazekas): Mimics the helper method used in the api tests
     @classmethod
     def create_server(cls, **kwargs):
         flavor_ref = cls.config.compute.flavor_ref
@@ -127,7 +127,7 @@
             cmd = shlex.split(cmd)
             result = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 
-        #Todo(rohitk): Need to define host connection parameters in config
+        # TODO(rohitk): Need to define host connection parameters in config
         else:
             client = self.get_ssh_connection(self.config.whitebox.api_host,
                                              self.config.whitebox.api_user,
diff --git a/tempest/whitebox/test_servers_whitebox.py b/tempest/whitebox/test_servers_whitebox.py
index 1c1cdeb..abe903c 100644
--- a/tempest/whitebox/test_servers_whitebox.py
+++ b/tempest/whitebox/test_servers_whitebox.py
@@ -26,7 +26,7 @@
     @classmethod
     def setUpClass(cls):
         super(ServersWhiteboxTest, cls).setUpClass()
-        #NOTE(afazekas): Strange relationship
+        # NOTE(afazekas): Strange relationship
         BaseIdentityAdminTest.setUpClass()
         cls.client = cls.servers_client
         cls.img_client = cls.images_client
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
index 490d263..45f05bd 100755
--- a/tools/pretty_tox_serial.sh
+++ b/tools/pretty_tox_serial.sh
@@ -5,5 +5,7 @@
 if [ ! -d .testrepository ]; then
     testr init
 fi
-testr run --subunit $TESTRARGS | subunit-2to1 | tools/colorizer.py
+testr run --subunit $TESTRARGS | subunit2pyunit
+retval=$?
 testr slowest
+exit $retval
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index 1ed6961..c244808 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -61,7 +61,7 @@
     """
     Return the skip tuples in a test file
     """
-    BUG_RE = re.compile(r'.*skip\(.*bug:*\s*\#*(\d+)', re.IGNORECASE)
+    BUG_RE = re.compile(r'.*skip.*bug:*\s*\#*(\d+)', re.IGNORECASE)
     DEF_RE = re.compile(r'.*def (\w+)\(')
     bug_found = False
     results = []
diff --git a/tox.ini b/tox.ini
index c3562e6..ea27b92 100644
--- a/tox.ini
+++ b/tox.ini
@@ -11,24 +11,13 @@
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}
 commands =
-  python setup.py testr --slowest
-
+  python setup.py testr --slowest --testr-args='{posargs}'
 
 [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}'
 
@@ -38,7 +27,14 @@
 commands =
   sh tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
 
-[testenv:smoke]
+[testenv:heat-slow]
+sitepackages = True
+setenv = VIRTUAL_ENV={envdir}
+# The regex below is used to select heat api/scenario tests tagged as slow.
+commands =
+  sh tools/pretty_tox_serial.sh '(?=.*\[.*\bslow\b.*\])(^tempest\.(api|scenario)\.orchestration) {posargs}'
+
+[testenv:py26-full]
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}
          NOSE_WITH_OPENSTACK=1
@@ -48,20 +44,31 @@
          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
+  nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit -sv --xunit-file=nosetests-full.xml tempest/api tempest/scenario tempest/thirdparty tempest/cli {posargs}
+
+[testenv:py26-smoke]
+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 {posargs}
+
+[testenv:smoke]
+sitepackages = True
+setenv = VIRTUAL_ENV={envdir}
+commands =
+   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))'
    python -m tools/tempest_coverage -c report --html {posargs}
 
 [testenv:stress]