Merge "Add testcases for security groups"
diff --git a/.testr.conf b/.testr.conf
index 05b12c4..c25ebec 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -2,7 +2,8 @@
 test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
              OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
              OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
-             ${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
+             OS_TEST_PATH=${OS_TEST_PATH:-./tempest} \
+             ${PYTHON:-python} -m subunit.run discover -t ./ $OS_TEST_PATH $LISTOPT $IDOPTION
 test_id_option=--load-list $IDFILE
 test_list_option=--list
 group_regex=([^\.]*\.)*
diff --git a/HACKING.rst b/HACKING.rst
index a74ff73..1083075 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -9,8 +9,8 @@
 ------------------------------
 
 - [T102] Cannot import OpenStack python clients in tempest/api tests
-- [T103] tempest/tests is deprecated
 - [T104] Scenario tests require a services decorator
+- [T105] Unit tests cannot use setUpClass
 
 Test Data/Configuration
 -----------------------
@@ -192,3 +192,15 @@
 The sample config file is autogenerated using a script. If any changes are made
 to the config variables in tempest then the sample config file must be
 regenerated. This can be done running the script: tools/generate_sample.sh
+
+Unit Tests
+----------
+Unit tests are a separate class of tests in tempest. They verify tempest
+itself, and thus have a different set of guidelines around them:
+
+1. They can not require anything running externally. All you should need to
+   run the unit tests is the git tree, python and the dependencies installed.
+   This includes running services, a config file, etc.
+
+2. The unit tests cannot use setUpClass, instead fixtures and testresources
+   should be used for shared state between tests.
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
index a822fae..a8c5276 100644
--- a/etc/whitelist.yaml
+++ b/etc/whitelist.yaml
@@ -218,3 +218,6 @@
     - module: ".*"
       message: ".*"
 
+s-proxy:
+    - module: "proxy-server"
+      message: "Timeout talking to memcached"
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index dd92ee9..d20068e 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 from tempest import config
-from tempest.exceptions import InvalidConfiguration
 from tempest.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
@@ -26,29 +25,3 @@
 RESIZE_AVAILABLE = CONFIG.compute_feature_enabled.resize
 CHANGE_PASSWORD_AVAILABLE = CONFIG.compute_feature_enabled.change_password
 DISK_CONFIG_ENABLED = CONFIG.compute_feature_enabled.disk_config
-MULTI_USER = True
-
-
-# All compute tests -- single setup function
-def generic_setup_package():
-    LOG.debug("Entering tempest.api.compute.setup_package")
-
-    global MULTI_USER
-
-    # Determine if there are two regular users that can be
-    # used in testing. If the test cases are allowed to create
-    # users (config.compute.allow_tenant_isolation is true,
-    # then we allow multi-user.
-    if not CONFIG.compute.allow_tenant_isolation:
-        user1 = CONFIG.identity.username
-        user2 = CONFIG.identity.alt_username
-        if not user2 or user1 == user2:
-            MULTI_USER = False
-        else:
-            user2_password = CONFIG.identity.alt_password
-            user2_tenant_name = CONFIG.identity.alt_tenant_name
-            if not user2_password or not user2_tenant_name:
-                msg = ("Alternate user specified but not alternate "
-                       "tenant or password: alt_tenant_name=%s alt_password=%s"
-                       % (user2_tenant_name, user2_password))
-                raise InvalidConfiguration(msg)
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 467a6f9..609d2c6 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -18,7 +18,6 @@
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
 from tempest.common.utils import data_utils
-from tempest import exceptions
 from tempest.test import attr
 
 
@@ -35,7 +34,6 @@
     def setUpClass(cls):
         super(AggregatesAdminTestJSON, cls).setUpClass()
         cls.client = cls.os_adm.aggregates_client
-        cls.user_client = cls.aggregates_client
         cls.aggregate_name_prefix = 'test_aggregate_'
         cls.az_name_prefix = 'test_az_'
 
@@ -141,54 +139,6 @@
                          (x['id'], x['name'], x['availability_zone']),
                           aggregates))
 
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_create_as_user(self):
-        # Regular user is not allowed to create an aggregate.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.create_aggregate,
-                          aggregate_name)
-
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_delete_as_user(self):
-        # Regular user is not allowed to delete an aggregate.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        resp, aggregate = self.client.create_aggregate(aggregate_name)
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.delete_aggregate,
-                          aggregate['id'])
-
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_list_as_user(self):
-        # Regular user is not allowed to list aggregates.
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.list_aggregates)
-
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_get_details_as_user(self):
-        # Regular user is not allowed to get aggregate details.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        resp, aggregate = self.client.create_aggregate(aggregate_name)
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.get_aggregate,
-                          aggregate['id'])
-
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_delete_with_invalid_id(self):
-        # Delete an aggregate with invalid id should raise exceptions.
-        self.assertRaises(exceptions.NotFound,
-                          self.client.delete_aggregate, -1)
-
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_get_details_with_invalid_id(self):
-        # Get aggregate details with invalid id should raise exceptions.
-        self.assertRaises(exceptions.NotFound,
-                          self.client.get_aggregate, -1)
-
     @attr(type='gate')
     def test_aggregate_add_remove_host(self):
         # Add an host to the given aggregate and remove.
@@ -262,48 +212,6 @@
         resp, body = admin_servers_client.get_server(server['id'])
         self.assertEqual(self.host, body[self._host_key])
 
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_add_non_exist_host(self):
-        # Adding a non-exist host to an aggregate should raise exceptions.
-        resp, hosts_all = self.os_adm.hosts_client.list_hosts()
-        hosts = map(lambda x: x['host_name'], hosts_all)
-        while True:
-            non_exist_host = data_utils.rand_name('nonexist_host_')
-            if non_exist_host not in hosts:
-                break
-
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        resp, aggregate = self.client.create_aggregate(aggregate_name)
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
-        self.assertRaises(exceptions.NotFound, self.client.add_host,
-                          aggregate['id'], non_exist_host)
-
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_add_host_as_user(self):
-        # Regular user is not allowed to add a host to an aggregate.
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        resp, aggregate = self.client.create_aggregate(aggregate_name)
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.add_host,
-                          aggregate['id'], self.host)
-
-    @attr(type=['negative', 'gate'])
-    def test_aggregate_remove_host_as_user(self):
-        # Regular user is not allowed to remove a host from an aggregate.
-        self.useFixture(fixtures.LockFixture('availability_zone'))
-        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
-        resp, aggregate = self.client.create_aggregate(aggregate_name)
-        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-        self.client.add_host(aggregate['id'], self.host)
-        self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
-
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.remove_host,
-                          aggregate['id'], self.host)
-
 
 class AggregatesAdminTestXML(AggregatesAdminTestJSON):
     _host_key = (
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
new file mode 100644
index 0000000..8506206
--- /dev/null
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -0,0 +1,196 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+# 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.compute import base
+from tempest.common import tempest_fixtures as fixtures
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class AggregatesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+    """
+    Tests Aggregates API that require admin privileges
+    """
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(AggregatesAdminNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.os_adm.aggregates_client
+        cls.user_client = cls.aggregates_client
+        cls.aggregate_name_prefix = 'test_aggregate_'
+        cls.az_name_prefix = 'test_az_'
+
+        resp, hosts_all = cls.os_adm.hosts_client.list_hosts()
+        hosts = map(lambda x: x['host_name'],
+                    filter(lambda y: y['service'] == 'compute', hosts_all))
+        cls.host = hosts[0]
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_create_as_user(self):
+        # Regular user is not allowed to create an aggregate.
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.create_aggregate,
+                          aggregate_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_create_aggregate_name_length_less_than_1(self):
+        # the length of aggregate name should >= 1 and <=255
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_aggregate,
+                          '')
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_create_aggregate_name_length_exceeds_255(self):
+        # the length of aggregate name should >= 1 and <=255
+        aggregate_name = 'a' * 256
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_aggregate,
+                          aggregate_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_create_with_existent_aggregate_name(self):
+        # creating an aggregate with existent aggregate name is forbidden
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+        self.assertRaises(exceptions.Conflict,
+                          self.client.create_aggregate,
+                          aggregate_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_delete_as_user(self):
+        # Regular user is not allowed to delete an aggregate.
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.delete_aggregate,
+                          aggregate['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_list_as_user(self):
+        # Regular user is not allowed to list aggregates.
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.list_aggregates)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_get_details_as_user(self):
+        # Regular user is not allowed to get aggregate details.
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.get_aggregate,
+                          aggregate['id'])
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_delete_with_invalid_id(self):
+        # Delete an aggregate with invalid id should raise exceptions.
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_aggregate, -1)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_get_details_with_invalid_id(self):
+        # Get aggregate details with invalid id should raise exceptions.
+        self.assertRaises(exceptions.NotFound,
+                          self.client.get_aggregate, -1)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_add_non_exist_host(self):
+        # Adding a non-exist host to an aggregate should raise exceptions.
+        resp, hosts_all = self.os_adm.hosts_client.list_hosts()
+        hosts = map(lambda x: x['host_name'], hosts_all)
+        while True:
+            non_exist_host = data_utils.rand_name('nonexist_host_')
+            if non_exist_host not in hosts:
+                break
+
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+        self.assertRaises(exceptions.NotFound, self.client.add_host,
+                          aggregate['id'], non_exist_host)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_add_host_as_user(self):
+        # Regular user is not allowed to add a host to an aggregate.
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.add_host,
+                          aggregate['id'], self.host)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_add_existent_host(self):
+        self.useFixture(fixtures.LockFixture('availability_zone'))
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+        resp, body = self.client.add_host(aggregate['id'], self.host)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
+
+        self.assertRaises(exceptions.Conflict, self.client.add_host,
+                          aggregate['id'], self.host)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_remove_host_as_user(self):
+        # Regular user is not allowed to remove a host from an aggregate.
+        self.useFixture(fixtures.LockFixture('availability_zone'))
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+        resp, body = self.client.add_host(aggregate['id'], self.host)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.remove_host,
+                          aggregate['id'], self.host)
+
+    @attr(type=['negative', 'gate'])
+    def test_aggregate_remove_nonexistent_host(self):
+        non_exist_host = data_utils.rand_name('nonexist_host_')
+        aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        resp, aggregate = self.client.create_aggregate(aggregate_name)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+        self.assertRaises(exceptions.NotFound, self.client.remove_host,
+                          aggregate['id'], non_exist_host)
+
+
+class AggregatesAdminNegativeTestXML(AggregatesAdminNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index cf72e49..0fb9460 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -130,40 +130,6 @@
                 flag = True
         self.assertTrue(flag)
 
-    @test.attr(type=['negative', 'gate'])
-    def test_get_flavor_details_for_deleted_flavor(self):
-        # Delete a flavor and ensure it is not listed
-        # Create a test flavor
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-
-        resp, flavor = self.client.create_flavor(flavor_name,
-                                                 self.ram,
-                                                 self.vcpus, self.disk,
-                                                 new_flavor_id,
-                                                 ephemeral=self.ephemeral,
-                                                 swap=self.swap,
-                                                 rxtx=self.rxtx)
-        # Delete the flavor
-        new_flavor_id = flavor['id']
-        resp_delete, body = self.client.delete_flavor(new_flavor_id)
-        self.assertEqual(200, resp.status)
-        self.assertEqual(202, resp_delete.status)
-
-        # Deleted flavors can be seen via detailed GET
-        resp, flavor = self.client.get_flavor_details(new_flavor_id)
-        self.assertEqual(resp.status, 200)
-        self.assertEqual(flavor['name'], flavor_name)
-
-        # Deleted flavors should not show up in a list however
-        resp, flavors = self.client.list_flavors_with_detail()
-        self.assertEqual(resp.status, 200)
-        flag = True
-        for flavor in flavors:
-            if flavor['name'] == flavor_name:
-                flag = False
-        self.assertTrue(flag)
-
     @test.attr(type='gate')
     def test_create_list_flavor_without_extra_data(self):
         # Create a flavor and ensure it is listed
@@ -347,49 +313,6 @@
         self.assertEqual(flavor['ram'], int(ram))
         self.assertEqual(int(flavor['id']), new_flavor_id)
 
-    @test.attr(type=['negative', 'gate'])
-    def test_invalid_is_public_string(self):
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.list_flavors_with_detail,
-                          {'is_public': 'invalid'})
-
-    @test.attr(type=['negative', 'gate'])
-    def test_create_flavor_as_user(self):
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.create_flavor,
-                          flavor_name, self.ram, self.vcpus, self.disk,
-                          new_flavor_id, ephemeral=self.ephemeral,
-                          swap=self.swap, rxtx=self.rxtx)
-
-    @test.attr(type=['negative', 'gate'])
-    def test_delete_flavor_as_user(self):
-        self.assertRaises(exceptions.Unauthorized,
-                          self.user_client.delete_flavor,
-                          self.flavor_ref_alt)
-
-    @test.attr(type=['negative', 'gate'])
-    def test_create_flavor_using_invalid_ram(self):
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.create_flavor,
-                          flavor_name, -1, self.vcpus,
-                          self.disk, new_flavor_id)
-
-    @test.attr(type=['negative', 'gate'])
-    def test_create_flavor_using_invalid_vcpus(self):
-        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
-        new_flavor_id = data_utils.rand_int_id(start=1000)
-
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.create_flavor,
-                          flavor_name, self.ram, 0,
-                          self.disk, new_flavor_id)
-
 
 class FlavorsAdminTestXML(FlavorsAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_flavors_negative.py b/tempest/api/compute/admin/test_flavors_negative.py
new file mode 100644
index 0000000..1d9198a
--- /dev/null
+++ b/tempest/api/compute/admin/test_flavors_negative.py
@@ -0,0 +1,344 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 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.
+
+import uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class FlavorsAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+    """
+    Tests Flavors API Create and Delete that require admin privileges
+    """
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(FlavorsAdminNegativeTestJSON, cls).setUpClass()
+        if not test.is_extension_enabled('FlavorExtraData', 'compute'):
+            msg = "FlavorExtraData extension not enabled."
+            raise cls.skipException(msg)
+
+        cls.client = cls.os_adm.flavors_client
+        cls.user_client = cls.os.flavors_client
+        cls.flavor_name_prefix = 'test_flavor_'
+        cls.ram = 512
+        cls.vcpus = 1
+        cls.disk = 10
+        cls.ephemeral = 10
+        cls.swap = 1024
+        cls.rxtx = 2
+
+    def flavor_clean_up(self, flavor_id):
+        resp, body = self.client.delete_flavor(flavor_id)
+        self.assertEqual(resp.status, 202)
+        self.client.wait_for_resource_deletion(flavor_id)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_get_flavor_details_for_deleted_flavor(self):
+        # Delete a flavor and ensure it is not listed
+        # Create a test flavor
+        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+
+        # no need to specify flavor_id, we can get the flavor_id from a
+        # response of create_flavor() call.
+        resp, flavor = self.client.create_flavor(flavor_name,
+                                                 self.ram,
+                                                 self.vcpus, self.disk,
+                                                 '',
+                                                 ephemeral=self.ephemeral,
+                                                 swap=self.swap,
+                                                 rxtx=self.rxtx)
+        # Delete the flavor
+        new_flavor_id = flavor['id']
+        resp_delete, body = self.client.delete_flavor(new_flavor_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(202, resp_delete.status)
+
+        # Deleted flavors can be seen via detailed GET
+        resp, flavor = self.client.get_flavor_details(new_flavor_id)
+        self.assertEqual(resp.status, 200)
+        self.assertEqual(flavor['name'], flavor_name)
+
+        # Deleted flavors should not show up in a list however
+        resp, flavors = self.client.list_flavors_with_detail()
+        self.assertEqual(resp.status, 200)
+        flag = True
+        for flavor in flavors:
+            if flavor['name'] == flavor_name:
+                flag = False
+        self.assertTrue(flag)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_invalid_is_public_string(self):
+        # the 'is_public' parameter can be 'none/true/false' if it exists
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.list_flavors_with_detail,
+                          {'is_public': 'invalid'})
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_as_user(self):
+        # only admin user can create a flavor
+        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.create_flavor,
+                          flavor_name, self.ram, self.vcpus, self.disk,
+                          new_flavor_id, ephemeral=self.ephemeral,
+                          swap=self.swap, rxtx=self.rxtx)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_delete_flavor_as_user(self):
+        # only admin user can delete a flavor
+        self.assertRaises(exceptions.Unauthorized,
+                          self.user_client.delete_flavor,
+                          self.flavor_ref_alt)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_using_invalid_ram(self):
+        # the 'ram' attribute must be positive integer
+        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          flavor_name, -1, self.vcpus,
+                          self.disk, new_flavor_id)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_using_invalid_vcpus(self):
+        # the 'vcpu' attribute must be positive integer
+        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          flavor_name, self.ram, -1,
+                          self.disk, new_flavor_id)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_name_length_less_than_1(self):
+        # ensure name length >= 1
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          '',
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_name_length_exceeds_255(self):
+        # ensure name do not exceed 255 characters
+        new_flavor_name = 'a' * 256
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_invalid_name(self):
+        # the regex of flavor_name is '^[\w\.\- ]*$'
+        invalid_flavor_name = data_utils.rand_name('invalid-!@#$%-')
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          invalid_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_invalid_flavor_id(self):
+        # the regex of flavor_id is '^[\w\.\- ]*$', and it cannot contain
+        # leading and/or trailing whitespace
+        new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        invalid_flavor_id = '!@#$%'
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          invalid_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_id_length_exceeds_255(self):
+        # the length of flavor_id should not exceed 255 characters
+        new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        invalid_flavor_id = 'a' * 256
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          invalid_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_invalid_root_gb(self):
+        # root_gb attribute should be non-negative ( >= 0) integer
+        new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          -1,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_invalid_ephemeral_gb(self):
+        # ephemeral_gb attribute should be non-negative ( >= 0) integer
+        new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=-1,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_invalid_swap(self):
+        # swap attribute should be non-negative ( >= 0) integer
+        new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=-1,
+                          rxtx=self.rxtx,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_invalid_rxtx_factor(self):
+        # rxtx_factor attribute should be a positive float
+        new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=-1.5,
+                          is_public='False')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_with_invalid_is_public(self):
+        # is_public attribute should be boolean
+        new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_flavor,
+                          new_flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx,
+                          is_public='Invalid')
+
+    @test.attr(type=['negative', 'gate'])
+    def test_create_flavor_already_exists(self):
+        flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+        new_flavor_id = str(uuid.uuid4())
+
+        resp, flavor = self.client.create_flavor(flavor_name,
+                                                 self.ram, self.vcpus,
+                                                 self.disk,
+                                                 new_flavor_id,
+                                                 ephemeral=self.ephemeral,
+                                                 swap=self.swap,
+                                                 rxtx=self.rxtx)
+        self.assertEqual(200, resp.status)
+        self.addCleanup(self.flavor_clean_up, flavor['id'])
+
+        self.assertRaises(exceptions.Conflict,
+                          self.client.create_flavor,
+                          flavor_name,
+                          self.ram, self.vcpus,
+                          self.disk,
+                          new_flavor_id,
+                          ephemeral=self.ephemeral,
+                          swap=self.swap,
+                          rxtx=self.rxtx)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_delete_nonexistent_flavor(self):
+        nonexistent_flavor_id = str(uuid.uuid4())
+
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_flavor,
+                          nonexistent_flavor_id)
+
+
+class FlavorsAdminNegativeTestXML(FlavorsAdminNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 5028cad..6fe3186 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -33,11 +33,8 @@
     def setUpClass(cls):
         super(ServersAdminTestJSON, cls).setUpClass()
         cls.client = cls.os_adm.servers_client
+        cls.non_admin_client = cls.servers_client
         cls.flavors_client = cls.os_adm.flavors_client
-        cls.identity_client = cls._get_identity_admin_client()
-        tenant = cls.identity_client.get_tenant_by_name(
-            cls.client.tenant_name)
-        cls.tenant_id = tenant['id']
 
         cls.s1_name = data_utils.rand_name('server')
         resp, server = cls.create_test_server(name=cls.s1_name,
@@ -116,6 +113,34 @@
         for key in basic_attrs:
             self.assertIn(key, str(diagnostic.keys()))
 
+    @attr(type='gate')
+    def test_rebuild_server_in_error_state(self):
+        # The server in error state should be rebuilt using the provided
+        # image and changed to ACTIVE state
+
+        # resetting vm state require admin priviledge
+        resp, server = self.client.reset_state(self.s1_id, state='error')
+        self.assertEqual(202, resp.status)
+        resp, rebuilt_server = self.non_admin_client.rebuild(
+            self.s1_id, self.image_ref_alt)
+        self.addCleanup(self.non_admin_client.wait_for_server_status,
+                        self.s1_id, 'ACTIVE')
+        self.addCleanup(self.non_admin_client.rebuild, self.s1_id,
+                        self.image_ref)
+
+        # Verify the properties in the initial response are correct
+        self.assertEqual(self.s1_id, rebuilt_server['id'])
+        rebuilt_image_id = rebuilt_server['image']['id']
+        self.assertEqual(self.image_ref_alt, rebuilt_image_id)
+        self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+        self.non_admin_client.wait_for_server_status(rebuilt_server['id'],
+                                                     'ACTIVE',
+                                                     raise_on_error=False)
+        # Verify the server properties after rebuilding
+        resp, server = self.non_admin_client.get_server(rebuilt_server['id'])
+        rebuilt_image_id = server['image']['id']
+        self.assertEqual(self.image_ref_alt, rebuilt_image_id)
+
 
 class ServersAdminTestXML(ServersAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 3c00851..d18b749 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -17,7 +17,6 @@
 
 import time
 
-from tempest.api import compute
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import exceptions
@@ -31,7 +30,6 @@
 class BaseComputeTest(tempest.test.BaseTestCase):
     """Base test case class for all Compute API tests."""
 
-    conclusion = compute.generic_setup_package()
     force_tenant_isolation = False
 
     @classmethod
@@ -55,6 +53,30 @@
         cls.image_ssh_password = cls.config.compute.image_ssh_password
         cls.servers = []
         cls.images = []
+        cls.multi_user = cls.get_multi_user()
+
+    @classmethod
+    def get_multi_user(cls):
+        multi_user = True
+        # Determine if there are two regular users that can be
+        # used in testing. If the test cases are allowed to create
+        # users (config.compute.allow_tenant_isolation is true,
+        # then we allow multi-user.
+        if not cls.config.compute.allow_tenant_isolation:
+            user1 = cls.config.identity.username
+            user2 = cls.config.identity.alt_username
+            if not user2 or user1 == user2:
+                multi_user = False
+            else:
+                user2_password = cls.config.identity.alt_password
+                user2_tenant_name = cls.config.identity.alt_tenant_name
+                if not user2_password or not user2_tenant_name:
+                    msg = ("Alternate user specified but not alternate "
+                           "tenant or password: alt_tenant_name=%s "
+                           "alt_password=%s"
+                           % (user2_tenant_name, user2_password))
+                    raise exceptions.InvalidConfiguration(msg)
+        return multi_user
 
     @classmethod
     def clear_servers(cls):
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index fa99422..092fd65 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest import exceptions
 from tempest.test import attr
 
 
@@ -50,12 +49,6 @@
         resp, flavor = self.client.get_flavor_details(self.flavor_ref)
         self.assertEqual(self.flavor_ref, flavor['id'])
 
-    @attr(type=['negative', 'gate'])
-    def test_get_non_existant_flavor(self):
-        # flavor details are not returned for non-existent flavors
-        self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
-                          999)
-
     @attr(type='gate')
     def test_list_flavors_limit_results(self):
         # Only the expected number of flavors should be returned
@@ -136,24 +129,6 @@
         resp, flavors = self.client.list_flavors(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @attr(type=['negative', 'gate'])
-    def test_invalid_minRam_filter(self):
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.list_flavors_with_detail,
-                          {'minRam': 'invalid'})
-
-    @attr(type=['negative', 'gate'])
-    def test_invalid_minDisk_filter(self):
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.list_flavors_with_detail,
-                          {'minDisk': 'invalid'})
-
-    @attr(type=['negative', 'gate'])
-    def test_get_flavor_details_for_invalid_flavor_id(self):
-        # Ensure 404 returned for non-existent flavor ID
-        self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
-                          9999)
-
 
 class FlavorsTestXML(FlavorsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
new file mode 100644
index 0000000..81e4f87
--- /dev/null
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -0,0 +1,69 @@
+# 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.
+
+import uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class FlavorsNegativeTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(FlavorsNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.flavors_client
+
+        # Generating a nonexistent flavor id
+        resp, flavors = cls.client.list_flavors()
+        flavor_ids = [flavor['id'] for flavor in flavors]
+        while True:
+            cls.nonexistent_flavor_id = data_utils.rand_int_id(start=999)
+            if cls.nonexistent_flavor_id not in flavor_ids:
+                break
+
+    @attr(type=['negative', 'gate'])
+    def test_invalid_minRam_filter(self):
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.list_flavors_with_detail,
+                          {'minRam': 'invalid'})
+
+    @attr(type=['negative', 'gate'])
+    def test_invalid_minDisk_filter(self):
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.list_flavors_with_detail,
+                          {'minDisk': 'invalid'})
+
+    @attr(type=['negative', 'gate'])
+    def test_get_flavor_details_for_invalid_flavor_id(self):
+        # Ensure 404 returned for invalid flavor ID
+        invalid_flavor_id = str(uuid.uuid4())
+        self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
+                          invalid_flavor_id)
+
+    @attr(type=['negative', 'gate'])
+    def test_non_existent_flavor_id(self):
+        # flavor details are not returned for non-existent flavors
+        self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
+                          self.nonexistent_flavor_id)
+
+
+class FlavorsNegativeTestXML(FlavorsNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 55260bf..f7db89b 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -14,7 +14,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest import clients
 from tempest.common.utils import data_utils
@@ -36,7 +35,7 @@
 
         cls.image_ids = []
 
-        if compute.MULTI_USER:
+        if cls.multi_user:
             if cls.config.compute.allow_tenant_isolation:
                 creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 18e32d8..6e4c8cb 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -49,7 +49,7 @@
             LOG.exception(exc)
             # Rebuild server if cannot reach the ACTIVE state
             # Usually it means the server had a serius accident
-            self.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
@@ -68,7 +68,7 @@
 
         cls.image_ids = []
 
-        if compute.MULTI_USER:
+        if cls.multi_user:
             if cls.config.compute.allow_tenant_isolation:
                 creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
@@ -117,6 +117,20 @@
         self.assertEqual('204', resp['status'])
         self.client.wait_for_resource_deletion(image_id)
 
+    @attr(type=['gate'])
+    def test_create_image_specify_multibyte_character_image_name(self):
+        if self.__class__._interface == "xml":
+            # NOTE(sdague): not entirely accurage, but we'd need a ton of work
+            # in our XML client to make this good
+            raise self.skipException("Not testable in XML")
+        # prefix character is:
+        # http://www.fileformat.info/info/unicode/char/1F4A9/index.htm
+        utf8_name = data_utils.rand_name(u'\xF0\x9F\x92\xA9')
+        resp, body = self.client.create_image(self.server_id, utf8_name)
+        image_id = data_utils.parse_image_id(resp['location'])
+        self.addCleanup(self.client.delete_image, image_id)
+        self.assertEqual('202', resp['status'])
+
 
 class ImagesOneServerTestXML(ImagesOneServerTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 4cd41ee..b8a4304 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -16,7 +16,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest import clients
 from tempest.common.utils import data_utils
@@ -50,7 +49,10 @@
             LOG.exception(exc)
             # Rebuild server if cannot reach the ACTIVE state
             # Usually it means the server had a serius accident
-            self.server_id = self.rebuild_server(self.server_id)
+            self._reset_server()
+
+    def _reset_server(self):
+        self.__class__.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
@@ -69,7 +71,7 @@
 
         cls.image_ids = []
 
-        if compute.MULTI_USER:
+        if cls.multi_user:
             if cls.config.compute.allow_tenant_isolation:
                 creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
@@ -84,11 +86,15 @@
     @skip_because(bug="1006725")
     @attr(type=['negative', 'gate'])
     def test_create_image_specify_multibyte_character_image_name(self):
-        # Return an error if the image name has multi-byte characters
-        snapshot_name = data_utils.rand_name('\xef\xbb\xbf')
+        if self.__class__._interface == "xml":
+            raise self.skipException("Not testable in XML")
+        # invalid multibyte sequence from:
+        # http://stackoverflow.com/questions/1301402/
+        #     example-invalid-utf8-string
+        invalid_name = data_utils.rand_name(u'\xc3\x28')
         self.assertRaises(exceptions.BadRequest,
                           self.client.create_image, self.server_id,
-                          snapshot_name)
+                          invalid_name)
 
     @attr(type=['negative', 'gate'])
     def test_create_image_specify_invalid_metadata(self):
@@ -117,12 +123,12 @@
         self.assertEqual(202, resp.status)
         image_id = data_utils.parse_image_id(resp['location'])
         self.image_ids.append(image_id)
+        self.addCleanup(self._reset_server)
 
         # Create second snapshot
         alt_snapshot_name = data_utils.rand_name('test-snap-')
         self.assertRaises(exceptions.Conflict, self.client.create_image,
                           self.server_id, alt_snapshot_name)
-        self.client.wait_for_image_status(image_id, 'ACTIVE')
 
     @attr(type=['negative', 'gate'])
     def test_create_image_specify_name_over_256_chars(self):
@@ -141,6 +147,7 @@
         self.assertEqual(202, resp.status)
         image_id = data_utils.parse_image_id(resp['location'])
         self.image_ids.append(image_id)
+        self.addCleanup(self._reset_server)
 
         # Do not wait, attempt to delete the image, ensure it's successful
         resp, body = self.client.delete_image(image_id)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5fa4c35..5552d0b 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -45,7 +45,7 @@
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
         except Exception:
             # Rebuild server if something happened to it during a test
-            self.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
@@ -123,6 +123,7 @@
                                                    metadata=meta,
                                                    personality=personality,
                                                    adminPass=password)
+        self.addCleanup(self.client.rebuild, self.server_id, self.image_ref)
 
         # Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
@@ -133,15 +134,44 @@
         # Verify the server properties after the rebuild completes
         self.client.wait_for_server_status(rebuilt_server['id'], 'ACTIVE')
         resp, server = self.client.get_server(rebuilt_server['id'])
-        rebuilt_image_id = rebuilt_server['image']['id']
+        rebuilt_image_id = server['image']['id']
         self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
-        self.assertEqual(new_name, rebuilt_server['name'])
+        self.assertEqual(new_name, server['name'])
 
         if self.run_ssh:
             # Verify that the user can authenticate with the provided password
             linux_client = RemoteClient(server, self.ssh_user, password)
             self.assertTrue(linux_client.can_authenticate())
 
+    @attr(type='gate')
+    def test_rebuild_server_in_stop_state(self):
+        # The server in stop state  should be rebuilt using the provided
+        # image and remain in SHUTOFF state
+        resp, server = self.client.get_server(self.server_id)
+        old_image = server['image']['id']
+        new_image = self.image_ref_alt \
+            if old_image == self.image_ref else self.image_ref
+        resp, server = self.client.stop(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.client.wait_for_server_status(self.server_id, 'SHUTOFF')
+        self.addCleanup(self.client.start, self.server_id)
+        resp, rebuilt_server = self.client.rebuild(self.server_id, new_image)
+        self.addCleanup(self.client.wait_for_server_status, self.server_id,
+                        'SHUTOFF')
+        self.addCleanup(self.client.rebuild, self.server_id, old_image)
+
+        # Verify the properties in the initial response are correct
+        self.assertEqual(self.server_id, rebuilt_server['id'])
+        rebuilt_image_id = rebuilt_server['image']['id']
+        self.assertEqual(new_image, rebuilt_image_id)
+        self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+
+        # Verify the server properties after the rebuild completes
+        self.client.wait_for_server_status(rebuilt_server['id'], 'SHUTOFF')
+        resp, server = self.client.get_server(rebuilt_server['id'])
+        rebuilt_image_id = server['image']['id']
+        self.assertEqual(new_image, rebuilt_image_id)
+
     def _detect_server_image_flavor(self, server_id):
         # Detects the current server image flavor ref.
         resp, server = self.client.get_server(self.server_id)
@@ -217,6 +247,7 @@
         image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
         self.assertEqual(202, resp.status)
+        self.os.image_client.wait_for_image_status(image1_id, 'active')
 
         backup2 = data_utils.rand_name('backup')
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
@@ -227,6 +258,7 @@
         image2_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(self.os.image_client.delete_image, image2_id)
         self.assertEqual(202, resp.status)
+        self.os.image_client.wait_for_image_status(image2_id, 'active')
 
         # verify they have been created
         properties = {
@@ -266,6 +298,14 @@
         self.assertEqual((backup2, backup3),
                          (image_list[0]['name'], image_list[1]['name']))
 
+    def _get_output(self):
+        resp, output = self.servers_client.get_console_output(
+            self.server_id, 10)
+        self.assertEqual(200, resp.status)
+        self.assertTrue(output, "Console output was empty.")
+        lines = len(output.split('\n'))
+        self.assertEqual(lines, 10)
+
     @attr(type='gate')
     def test_get_console_output(self):
         # Positive test:Should be able to GET the console output
@@ -280,29 +320,24 @@
         self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
-        def get_output():
-            resp, output = self.servers_client.get_console_output(
-                self.server_id, 10)
-            self.assertEqual(200, resp.status)
-            self.assertTrue(output, "Console output was empty.")
-            lines = len(output.split('\n'))
-            self.assertEqual(lines, 10)
-        self.wait_for(get_output)
+        self.wait_for(self._get_output)
 
-    @skip_because(bug="1014683")
     @attr(type='gate')
-    def test_get_console_output_server_id_in_reboot_status(self):
+    def test_get_console_output_server_id_in_shutoff_status(self):
         # Positive test:Should be able to GET the console output
-        # for a given server_id in reboot status
-        resp, output = self.servers_client.reboot(self.server_id, 'SOFT')
-        self.servers_client.wait_for_server_status(self.server_id,
-                                                   'REBOOT')
-        resp, output = self.servers_client.get_console_output(self.server_id,
-                                                              10)
-        self.assertEqual(200, resp.status)
-        self.assertIsNotNone(output)
-        lines = len(output.split('\n'))
-        self.assertEqual(lines, 10)
+        # for a given server_id in SHUTOFF status
+
+        # NOTE: SHUTOFF is irregular status. To avoid test instability,
+        #       one server is created only for this test without using
+        #       the server that was created in setupClass.
+        resp, server = self.create_test_server(wait_until='ACTIVE')
+        temp_server_id = server['id']
+
+        resp, server = self.servers_client.stop(temp_server_id)
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(temp_server_id, 'SHUTOFF')
+
+        self.wait_for(self._get_output)
 
     @attr(type='gate')
     def test_pause_unpause_server(self):
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 7b86d2d..8142250 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -34,7 +34,7 @@
         try:
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
         except Exception:
-            self.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index 49c4f32..327c7d1 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -15,7 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest import clients
 from tempest.common.utils import data_utils
@@ -31,12 +30,10 @@
 
     @classmethod
     def setUpClass(cls):
-        if not compute.MULTI_USER:
+        super(AuthorizationTestJSON, cls).setUpClass()
+        if not cls.multi_user:
             msg = "Need >1 user"
             raise cls.skipException(msg)
-
-        super(AuthorizationTestJSON, cls).setUpClass()
-
         cls.client = cls.os.servers_client
         cls.images_client = cls.os.images_client
         cls.keypairs_client = cls.os.keypairs_client
@@ -85,7 +82,7 @@
 
     @classmethod
     def tearDownClass(cls):
-        if compute.MULTI_USER:
+        if cls.multi_user:
             cls.images_client.delete_image(cls.image['id'])
             cls.keypairs_client.delete_keypair(cls.keypairname)
             cls.security_client.delete_security_group(cls.security_group['id'])
diff --git a/tempest/api/compute/v3/images/test_images.py b/tempest/api/compute/v3/images/test_images.py
index 3aacafb..a179d65 100644
--- a/tempest/api/compute/v3/images/test_images.py
+++ b/tempest/api/compute/v3/images/test_images.py
@@ -14,7 +14,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest import clients
 from tempest.common.utils import data_utils
@@ -34,7 +33,7 @@
         cls.client = cls.images_client
         cls.servers_client = cls.servers_client
 
-        if compute.MULTI_USER:
+        if cls.multi_user:
             if cls.config.compute.allow_tenant_isolation:
                 creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
diff --git a/tempest/api/compute/v3/servers/test_list_servers_negative.py b/tempest/api/compute/v3/servers/test_list_servers_negative.py
index 6225345..3f7f885 100644
--- a/tempest/api/compute/v3/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_list_servers_negative.py
@@ -17,7 +17,6 @@
 
 import datetime
 
-from tempest.api import compute
 from tempest.api.compute import base
 from tempest import clients
 from tempest import exceptions
@@ -53,7 +52,7 @@
         cls.client = cls.servers_client
         cls.servers = []
 
-        if compute.MULTI_USER:
+        if cls.multi_user:
             if cls.config.compute.allow_tenant_isolation:
                 creds = cls.isolated_creds.get_alt_creds()
                 username, tenant_name, password = creds
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index dc78a47..090f4dd 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -45,7 +45,7 @@
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
         except Exception:
             # Rebuild server if something happened to it during a test
-            self.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index 7b86d2d..8142250 100644
--- a/tempest/api/compute/v3/servers/test_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -34,7 +34,7 @@
         try:
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
         except Exception:
-            self.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index f214641..b57dcfe 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -43,9 +43,8 @@
         cls.volume_list = []
         cls.volume_id_list = []
         for i in range(3):
-            v_name = data_utils.rand_name('volume-%s')
+            v_name = data_utils.rand_name('volume-%s' % cls._interface)
             metadata = {'Type': 'work'}
-            v_name += cls._interface
             try:
                 resp, volume = cls.client.create_volume(size=1,
                                                         display_name=v_name,
@@ -110,6 +109,65 @@
                          ', '.join(m_vol['displayName']
                                    for m_vol in missing_volumes))
 
+    @attr(type='gate')
+    def test_volume_list_param_limit(self):
+        # Return the list of volumes based on limit set
+        params = {'limit': 2}
+        resp, fetched_vol_list = self.client.list_volumes(params=params)
+        self.assertEqual(200, resp.status)
+
+        self.assertEqual(len(fetched_vol_list), params['limit'],
+                         "Failed to list volumes by limit set")
+
+    @attr(type='gate')
+    def test_volume_list_with_detail_param_limit(self):
+        # Return the list of volumes with details based on limit set.
+        params = {'limit': 2}
+        resp, fetched_vol_list = \
+            self.client.list_volumes_with_detail(params=params)
+        self.assertEqual(200, resp.status)
+
+        self.assertEqual(len(fetched_vol_list), params['limit'],
+                         "Failed to list volume details by limit set")
+
+    @attr(type='gate')
+    def test_volume_list_param_offset_and_limit(self):
+        # Return the list of volumes based on offset and limit set.
+        # get all volumes list
+        response, all_vol_list = self.client.list_volumes()
+        params = {'offset': 1, 'limit': 1}
+        resp, fetched_vol_list = self.client.list_volumes(params=params)
+        self.assertEqual(200, resp.status)
+
+        # Validating length of the fetched volumes
+        self.assertEqual(len(fetched_vol_list), params['limit'],
+                         "Failed to list volumes by offset and limit")
+        # Validating offset of fetched volume
+        for index, volume in enumerate(fetched_vol_list):
+            self.assertEqual(volume['id'],
+                             all_vol_list[index + params['offset']]['id'],
+                             "Failed to list volumes by offset and limit")
+
+    @attr(type='gate')
+    def test_volume_list_with_detail_param_offset_and_limit(self):
+        # Return the list of volumes details based on offset and limit set.
+        # get all volumes list
+        response, all_vol_list = self.client.list_volumes_with_detail()
+        params = {'offset': 1, 'limit': 1}
+        resp, fetched_vol_list = \
+            self.client.list_volumes_with_detail(params=params)
+        self.assertEqual(200, resp.status)
+
+        # Validating length of the fetched volumes
+        self.assertEqual(len(fetched_vol_list), params['limit'],
+                         "Failed to list volume details by offset and limit")
+        # Validating offset of fetched volume
+        for index, volume in enumerate(fetched_vol_list):
+            self.assertEqual(volume['id'],
+                             all_vol_list[index + params['offset']]['id'],
+                             "Failed to list volume details by "
+                             "offset and limit")
+
 
 class VolumesTestXML(VolumesTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
new file mode 100644
index 0000000..5e13a5a
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -0,0 +1,222 @@
+# 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.api.identity import base
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+
+
+class BaseTrustsV3Test(base.BaseIdentityAdminTest):
+
+    def setUp(self):
+        super(BaseTrustsV3Test, self).setUp()
+        # Use alt_username as the trustee
+        self.trustee_username = self.config.identity.alt_username
+
+        self.trust_id = None
+        self.create_trustor_and_roles()
+        self.addCleanup(self.cleanup_trust_user_and_roles)
+
+    def create_trustor_and_roles(self):
+        # Get trustor project ID, use the admin project
+        self.trustor_project_name = self.v3_client.tenant_name
+        self.trustor_project_id = self.get_tenant_by_name(
+            self.trustor_project_name)['id']
+        self.assertIsNotNone(self.trustor_project_id)
+
+        # Create a trustor User
+        self.trustor_username = rand_name('user-')
+        u_desc = self.trustor_username + 'description'
+        u_email = self.trustor_username + '@testmail.tm'
+        self.trustor_password = rand_name('pass-')
+        resp, user = self.v3_client.create_user(
+            self.trustor_username,
+            description=u_desc,
+            password=self.trustor_password,
+            email=u_email,
+            project_id=self.trustor_project_id)
+        self.assertEqual(resp['status'], '201')
+        self.trustor_user_id = user['id']
+
+        # And two roles, one we'll delegate and one we won't
+        self.delegated_role = rand_name('DelegatedRole-')
+        self.not_delegated_role = rand_name('NotDelegatedRole-')
+
+        resp, role = self.v3_client.create_role(self.delegated_role)
+        self.assertEqual(resp['status'], '201')
+        self.delegated_role_id = role['id']
+
+        resp, role = self.v3_client.create_role(self.not_delegated_role)
+        self.assertEqual(resp['status'], '201')
+        self.not_delegated_role_id = role['id']
+
+        # Assign roles to trustor
+        self.v3_client.assign_user_role(self.trustor_project_id,
+                                        self.trustor_user_id,
+                                        self.delegated_role_id)
+        self.v3_client.assign_user_role(self.trustor_project_id,
+                                        self.trustor_user_id,
+                                        self.not_delegated_role_id)
+
+        # Get trustee user ID, use the demo user
+        trustee_username = self.v3_non_admin_client.user
+        self.trustee_user_id = self.get_user_by_name(trustee_username)['id']
+        self.assertIsNotNone(self.trustee_user_id)
+
+        # Initialize a new client with the trustor credentials
+        os = clients.Manager(username=self.trustor_username,
+                             password=self.trustor_password,
+                             tenant_name=self.trustor_project_name,
+                             interface=self._interface)
+        self.trustor_v3_client = os.identity_v3_client
+
+    def cleanup_trust_user_and_roles(self):
+        if self.trust_id:
+            try:
+                self.trustor_v3_client.delete_trust(self.trust_id)
+            except exceptions.NotFound:
+                pass
+            self.trust_id = None
+
+        if self.trustor_user_id:
+            self.v3_client.delete_user(self.trustor_user_id)
+        if self.delegated_role_id:
+            self.v3_client.delete_role(self.delegated_role_id)
+        if self.not_delegated_role_id:
+            self.v3_client.delete_role(self.not_delegated_role_id)
+
+    def create_trust(self, impersonate=True, expires=None):
+
+        resp, trust_create = self.trustor_v3_client.create_trust(
+            trustor_user_id=self.trustor_user_id,
+            trustee_user_id=self.trustee_user_id,
+            project_id=self.trustor_project_id,
+            role_names=[self.delegated_role],
+            impersonation=impersonate,
+            expires_at=expires)
+        self.assertEqual('201', resp['status'])
+        self.trust_id = trust_create['id']
+        return trust_create
+
+    def validate_trust(self, trust, impersonate=True, expires=None,
+                       summary=False):
+        self.assertIsNotNone(trust['id'])
+        self.assertEqual(impersonate, trust['impersonation'])
+        self.assertEqual(expires, trust['expires_at'])
+        self.assertEqual(self.trustor_user_id, trust['trustor_user_id'])
+        self.assertEqual(self.trustee_user_id, trust['trustee_user_id'])
+        self.assertIn('v3/OS-TRUST/trusts', trust['links']['self'])
+        self.assertEqual(self.trustor_project_id, trust['project_id'])
+        if not summary:
+            self.assertEqual(self.delegated_role, trust['roles'][0]['name'])
+            self.assertEqual(1, len(trust['roles']))
+
+    def get_trust(self):
+        resp, trust_get = self.trustor_v3_client.get_trust(self.trust_id)
+        self.assertEqual('200', resp['status'])
+        return trust_get
+
+    def validate_role(self, role):
+        self.assertEqual(self.delegated_role_id, role['id'])
+        self.assertEqual(self.delegated_role, role['name'])
+        self.assertIn('v3/roles/%s' % self.delegated_role_id,
+                      role['links']['self'])
+        self.assertNotEqual(self.not_delegated_role_id, role['id'])
+        self.assertNotEqual(self.not_delegated_role, role['name'])
+        self.assertNotIn('v3/roles/%s' % self.not_delegated_role_id,
+                         role['links']['self'])
+
+    def check_trust_roles(self):
+        # Check we find the delegated role
+        resp, roles_get = self.trustor_v3_client.get_trust_roles(
+            self.trust_id)
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(1, len(roles_get))
+        self.validate_role(roles_get[0])
+
+        resp, role_get = self.trustor_v3_client.get_trust_role(
+            self.trust_id, self.delegated_role_id)
+        self.assertEqual('200', resp['status'])
+        self.validate_role(role_get)
+
+        resp, role_get = self.trustor_v3_client.check_trust_role(
+            self.trust_id, self.delegated_role_id)
+        self.assertEqual('204', resp['status'])
+
+        # And that we don't find not_delegated_role
+        self.assertRaises(exceptions.NotFound,
+                          self.trustor_v3_client.get_trust_role,
+                          self.trust_id,
+                          self.not_delegated_role_id)
+
+        self.assertRaises(exceptions.NotFound,
+                          self.trustor_v3_client.check_trust_role,
+                          self.trust_id,
+                          self.not_delegated_role_id)
+
+    def delete_trust(self):
+        resp, trust_delete = self.trustor_v3_client.delete_trust(self.trust_id)
+        self.assertEqual('204', resp['status'])
+        self.assertRaises(exceptions.NotFound,
+                          self.trustor_v3_client.get_trust,
+                          self.trust_id)
+        self.trust_id = None
+
+
+class TrustsV3TestJSON(BaseTrustsV3Test):
+    _interface = 'json'
+
+    def setUp(self):
+        super(TrustsV3TestJSON, self).setUp()
+        self.create_trustor_and_roles()
+
+    @attr(type='smoke')
+    def test_trust_impersonate(self):
+        # Test case to check we can create, get and delete a trust
+        # updates are not supported for trusts
+        trust = self.create_trust()
+        self.validate_trust(trust)
+
+        trust_get = self.get_trust()
+        self.validate_trust(trust_get)
+
+        self.check_trust_roles()
+
+        self.delete_trust()
+
+    @attr(type='smoke')
+    def test_trust_noimpersonate(self):
+        # Test case to check we can create, get and delete a trust
+        # with impersonation=False
+        trust = self.create_trust(impersonate=False)
+        self.validate_trust(trust, impersonate=False)
+
+        trust_get = self.get_trust()
+        self.validate_trust(trust_get, impersonate=False)
+
+        self.check_trust_roles()
+
+        self.delete_trust()
+
+    @attr(type='smoke')
+    def test_trust_expire_invalid(self):
+        # Test case to check we can check an invlaid expiry time
+        # is rejected with the correct error
+        # with an expiry specified
+        expires_str = 'bad.123Z'
+        self.assertRaises(exceptions.BadRequest,
+                          self.create_trust,
+                          expires=expires_str)
diff --git a/tempest/api/network/admin/test_agent_management.py b/tempest/api/network/admin/test_agent_management.py
new file mode 100644
index 0000000..94659b2
--- /dev/null
+++ b/tempest/api/network/admin/test_agent_management.py
@@ -0,0 +1,82 @@
+# 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.
+
+from tempest.api.network import base
+from tempest.common import tempest_fixtures as fixtures
+from tempest.test import attr
+
+
+class AgentManagementTestJSON(base.BaseAdminNetworkTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(AgentManagementTestJSON, cls).setUpClass()
+        resp, body = cls.admin_client.list_agents()
+        agents = body['agents']
+        cls.agent = agents[0]
+
+    @attr(type='smoke')
+    def test_list_agent(self):
+        resp, body = self.admin_client.list_agents()
+        self.assertEqual('200', resp['status'])
+        agents = body['agents']
+        self.assertIn(self.agent, agents)
+
+    @attr(type='smoke')
+    def test_show_agent(self):
+        resp, body = self.admin_client.show_agent(self.agent['id'])
+        agent = body['agent']
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(agent['id'], self.agent['id'])
+
+    @attr(type='smoke')
+    def test_update_agent_status(self):
+        origin_status = self.agent['admin_state_up']
+        # Try to update the 'admin_state_up' to the original
+        # one to avoid the negative effect.
+        agent_status = {'admin_state_up': origin_status}
+        resp, body = self.admin_client.update_agent(agent_id=self.agent['id'],
+                                                    agent_info=agent_status)
+        updated_status = body['agent']['admin_state_up']
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(origin_status, updated_status)
+
+    @attr(type='smoke')
+    def test_update_agent_description(self):
+        self.useFixture(fixtures.LockFixture('agent_description'))
+        description = 'description for update agent.'
+        agent_description = {'description': description}
+        resp, body = self.admin_client.update_agent(
+            agent_id=self.agent['id'],
+            agent_info=agent_description)
+        self.assertEqual('200', resp['status'])
+        self.addCleanup(self._restore_agent)
+        updated_description = body['agent']['description']
+        self.assertEqual(updated_description, description)
+
+    def _restore_agent(self):
+        """
+        Restore the agent description after update test.
+        """
+        description = self.agent['description'] or ''
+        origin_agent = {'description': description}
+        self.admin_client.update_agent(agent_id=self.agent['id'],
+                                       agent_info=origin_agent)
+
+
+class AgentManagementTestXML(AgentManagementTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/network/common.py b/tempest/api/network/common.py
index ab19fa8..528a204 100644
--- a/tempest/api/network/common.py
+++ b/tempest/api/network/common.py
@@ -47,6 +47,9 @@
     def delete(self):
         raise NotImplemented()
 
+    def __hash__(self):
+        return id(self)
+
 
 class DeletableNetwork(DeletableResource):
 
@@ -86,6 +89,23 @@
 
 class DeletableFloatingIp(DeletableResource):
 
+    def update(self, *args, **kwargs):
+        result = self.client.update_floatingip(floatingip=self.id,
+                                               body=dict(
+                                                   floatingip=dict(*args,
+                                                                   **kwargs)
+                                               ))
+        super(DeletableFloatingIp, self).update(**result['floatingip'])
+
+    def __repr__(self):
+        return '<%s addr="%s">' % (self.__class__.__name__,
+                                   self.floating_ip_address)
+
+    def __str__(self):
+        return '<"FloatingIP" addr="%s" id="%s">' % (self.__class__.__name__,
+                                                     self.floating_ip_address,
+                                                     self.id)
+
     def delete(self):
         self.client.delete_floatingip(self.id)
 
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 3cbe23f..06e07bb 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -15,6 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import netaddr
+
 from tempest.api.network import base
 from tempest.common.utils import data_utils
 from tempest.test import attr
@@ -229,3 +231,22 @@
             {'network_id': self.network_cfg.public_network_id,
              'enable_snat': False})
         self._verify_gateway_port(router['id'])
+
+    @attr(type='smoke')
+    def test_update_extra_route(self):
+        self.network = self.create_network()
+        self.name = self.network['name']
+        self.subnet = self.create_subnet(self.network)
+        # Add router interface with subnet id
+        self.router = self.create_router(data_utils.rand_name('router-'), True)
+        self.create_router_interface(self.router['id'], self.subnet['id'])
+        self.addCleanup(
+            self._delete_extra_routes,
+            self.router['id'])
+        # Update router extra route
+        cidr = netaddr.IPNetwork(self.subnet['cidr'])
+        resp, extra_route = self.client.update_extra_routes(
+            self.router['id'], str(cidr[0]), str(self.subnet['cidr']))
+
+    def _delete_extra_routes(self, router_id):
+        resp, _ = self.client.delete_extra_routes(router_id)
diff --git a/tempest/api/network/test_vpnaas_extensions.py b/tempest/api/network/test_vpnaas_extensions.py
index 7905b97..63a8e24 100644
--- a/tempest/api/network/test_vpnaas_extensions.py
+++ b/tempest/api/network/test_vpnaas_extensions.py
@@ -40,7 +40,9 @@
         super(VPNaaSJSON, cls).setUpClass()
         cls.network = cls.create_network()
         cls.subnet = cls.create_subnet(cls.network)
-        cls.router = cls.create_router(data_utils.rand_name("router-"))
+        cls.router = cls.create_router(
+            data_utils.rand_name("router-"),
+            external_network_id=cls.network_cfg.public_network_id)
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         cls.vpnservice = cls.create_vpnservice(cls.subnet['id'],
                                                cls.router['id'])
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 4dbc88a..def330e 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -57,13 +57,13 @@
         # Create 3 test volumes
         cls.volume_list = []
         cls.volume_id_list = []
+        cls.metadata = {'Type': 'work'}
         for i in range(3):
             v_name = data_utils.rand_name('volume')
-            metadata = {'Type': 'work'}
             try:
                 resp, volume = cls.client.create_volume(size=1,
                                                         display_name=v_name,
-                                                        metadata=metadata)
+                                                        metadata=cls.metadata)
                 cls.client.wait_for_volume_status(volume['id'], 'available')
                 resp, volume = cls.client.get_volume(volume['id'])
                 cls.volume_list.append(volume)
@@ -88,6 +88,25 @@
             cls.client.wait_for_resource_deletion(volid)
         super(VolumesListTest, cls).tearDownClass()
 
+    def _list_by_param_value_and_assert(self, params, with_detail=False):
+        """
+        Perform list or list_details action with given params
+        and validates result.
+        """
+        if with_detail:
+            resp, fetched_vol_list = \
+                self.client.list_volumes_with_detail(params=params)
+        else:
+            resp, fetched_vol_list = self.client.list_volumes(params=params)
+
+        self.assertEqual(200, resp.status)
+        # Validating params of fetched volumes
+        for volume in fetched_vol_list:
+            for key in params:
+                msg = "Failed to list volumes %s by %s" % \
+                      ('details' if with_detail else '', key)
+                self.assertEqual(params[key], volume[key], msg)
+
     @attr(type='smoke')
     def test_volume_list(self):
         # Get a list of Volumes
@@ -164,6 +183,34 @@
             self.assertEqual(zone, volume['availability_zone'])
         self.assertVolumesIn(fetched_list, self.volume_list)
 
+    @attr(type='gate')
+    def test_volume_list_with_param_metadata(self):
+        # Test to list volumes when metadata param is given
+        params = {'metadata': self.metadata}
+        self._list_by_param_value_and_assert(params)
+
+    @attr(type='gate')
+    def test_volume_list_with_detail_param_metadata(self):
+        # Test to list volumes details when metadata param is given
+        params = {'metadata': self.metadata}
+        self._list_by_param_value_and_assert(params, with_detail=True)
+
+    @attr(type='gate')
+    def test_volume_list_param_display_name_and_status(self):
+        # Test to list volume when display name and status param is given
+        volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+        params = {'display_name': volume['display_name'],
+                  'status': 'available'}
+        self._list_by_param_value_and_assert(params)
+
+    @attr(type='gate')
+    def test_volume_list_with_detail_param_display_name_and_status(self):
+        # Test to list volume when name and status param is given
+        volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+        params = {'display_name': volume['display_name'],
+                  'status': 'available'}
+        self._list_by_param_value_and_assert(params, with_detail=True)
+
 
 class VolumeListTestXML(VolumesListTest):
     _interface = 'xml'
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 99e8de7..6c45c3d 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -37,6 +37,27 @@
     def tearDownClass(cls):
         super(VolumesSnapshotTest, cls).tearDownClass()
 
+    def _list_by_param_values_and_assert(self, params, with_detail=False):
+        """
+        Perform list or list_details action with given params
+        and validates result.
+        """
+        if with_detail:
+            resp, fetched_snap_list = \
+                self.snapshots_client.\
+                list_snapshots_with_detail(params=params)
+        else:
+            resp, fetched_snap_list = \
+                self.snapshots_client.list_snapshots(params=params)
+
+        self.assertEqual(200, resp.status)
+        # Validating params of fetched snapshots
+        for snap in fetched_snap_list:
+            for key in params:
+                msg = "Failed to list snapshots %s by %s" % \
+                      ('details' if with_detail else '', key)
+                self.assertEqual(params[key], snap[key], msg)
+
     @attr(type='gate')
     def test_snapshot_create_get_list_update_delete(self):
         # Create a snapshot
@@ -83,6 +104,46 @@
         self.snapshots.remove(snapshot)
 
     @attr(type='gate')
+    def test_snapshots_list_with_params(self):
+        """list snapshots with params."""
+        # Create a snapshot
+        display_name = data_utils.rand_name('snap')
+        snapshot = self.create_snapshot(self.volume_origin['id'],
+                                        display_name=display_name)
+
+        # Verify list snapshots by display_name filter
+        params = {'display_name': snapshot['display_name']}
+        self._list_by_param_values_and_assert(params)
+
+        # Verify list snapshots by status filter
+        params = {'status': 'available'}
+        self._list_by_param_values_and_assert(params)
+
+        # Verify list snapshots by status and display name filter
+        params = {'status': 'available',
+                  'display_name': snapshot['display_name']}
+        self._list_by_param_values_and_assert(params)
+
+    @attr(type='gate')
+    def test_snapshots_list_details_with_params(self):
+        """list snapshot details with params."""
+        # Create a snapshot
+        display_name = data_utils.rand_name('snap')
+        snapshot = self.create_snapshot(self.volume_origin['id'],
+                                        display_name=display_name)
+
+        # Verify list snapshot details by display_name filter
+        params = {'display_name': snapshot['display_name']}
+        self._list_by_param_values_and_assert(params, with_detail=True)
+        # Verify list snapshot details by status filter
+        params = {'status': 'available'}
+        self._list_by_param_values_and_assert(params, with_detail=True)
+        # Verify list snapshot details by status and display name filter
+        params = {'status': 'available',
+                  'display_name': snapshot['display_name']}
+        self._list_by_param_values_and_assert(params, with_detail=True)
+
+    @attr(type='gate')
     def test_volume_from_snapshot(self):
         # Create a temporary snap using wrapper method from base, then
         # create a snap based volume, check resp code and deletes it
diff --git a/tempest/cli/simple_read_only/test_neutron.py b/tempest/cli/simple_read_only/test_neutron.py
index 047b17d..80376ab 100644
--- a/tempest/cli/simple_read_only/test_neutron.py
+++ b/tempest/cli/simple_read_only/test_neutron.py
@@ -44,35 +44,43 @@
             raise cls.skipException(msg)
         super(SimpleReadOnlyNeutronClientTest, cls).setUpClass()
 
+    @test.attr(type='smoke')
     def test_neutron_fake_action(self):
         self.assertRaises(subprocess.CalledProcessError,
                           self.neutron,
                           'this-does-not-exist')
 
+    @test.attr(type='smoke')
     def test_neutron_net_list(self):
         self.neutron('net-list')
 
+    @test.attr(type='smoke')
     def test_neutron_ext_list(self):
         ext = self.parser.listing(self.neutron('ext-list'))
         self.assertTableStruct(ext, ['alias', 'name'])
 
+    @test.attr(type='smoke')
     def test_neutron_dhcp_agent_list_hosting_net(self):
         self.neutron('dhcp-agent-list-hosting-net',
                      params=CONF.compute.fixed_network_name)
 
+    @test.attr(type='smoke')
     def test_neutron_agent_list(self):
         agents = self.parser.listing(self.neutron('agent-list'))
         field_names = ['id', 'agent_type', 'host', 'alive', 'admin_state_up']
         self.assertTableStruct(agents, field_names)
 
+    @test.attr(type='smoke')
     def test_neutron_floatingip_list(self):
         self.neutron('floatingip-list')
 
     @test.skip_because(bug="1240694")
+    @test.attr(type='smoke')
     def test_neutron_meter_label_list(self):
         self.neutron('meter-label-list')
 
     @test.skip_because(bug="1240694")
+    @test.attr(type='smoke')
     def test_neutron_meter_label_rule_list(self):
         self.neutron('meter-label-rule-list')
 
@@ -83,40 +91,52 @@
             if '404 Not Found' not in e.stderr:
                 self.fail('%s: Unexpected failure.' % command)
 
+    @test.attr(type='smoke')
     def test_neutron_lb_healthmonitor_list(self):
         self._test_neutron_lbaas_command('lb-healthmonitor-list')
 
+    @test.attr(type='smoke')
     def test_neutron_lb_member_list(self):
         self._test_neutron_lbaas_command('lb-member-list')
 
+    @test.attr(type='smoke')
     def test_neutron_lb_pool_list(self):
         self._test_neutron_lbaas_command('lb-pool-list')
 
+    @test.attr(type='smoke')
     def test_neutron_lb_vip_list(self):
         self._test_neutron_lbaas_command('lb-vip-list')
 
+    @test.attr(type='smoke')
     def test_neutron_net_external_list(self):
         self.neutron('net-external-list')
 
+    @test.attr(type='smoke')
     def test_neutron_port_list(self):
         self.neutron('port-list')
 
+    @test.attr(type='smoke')
     def test_neutron_quota_list(self):
         self.neutron('quota-list')
 
+    @test.attr(type='smoke')
     def test_neutron_router_list(self):
         self.neutron('router-list')
 
+    @test.attr(type='smoke')
     def test_neutron_security_group_list(self):
         security_grp = self.parser.listing(self.neutron('security-group-list'))
         self.assertTableStruct(security_grp, ['id', 'name', 'description'])
 
+    @test.attr(type='smoke')
     def test_neutron_security_group_rule_list(self):
         self.neutron('security-group-rule-list')
 
+    @test.attr(type='smoke')
     def test_neutron_subnet_list(self):
         self.neutron('subnet-list')
 
+    @test.attr(type='smoke')
     def test_neutron_help(self):
         help_text = self.neutron('help')
         lines = help_text.split('\n')
@@ -136,11 +156,14 @@
 
      # Optional arguments:
 
+    @test.attr(type='smoke')
     def test_neutron_version(self):
         self.neutron('', flags='--version')
 
+    @test.attr(type='smoke')
     def test_neutron_debug_net_list(self):
         self.neutron('net-list', flags='--debug')
 
+    @test.attr(type='smoke')
     def test_neutron_quiet_net_list(self):
         self.neutron('net-list', flags='--quiet')
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 0d0e794..e64a257 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -89,3 +89,7 @@
         # usually to /dev/ttyS0
         cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
         return self.ssh_client.exec_command(cmd)
+
+    def ping_host(self, host):
+        cmd = 'ping -c1 -w1 %s' % host
+        return self.ssh_client.exec_command(cmd)
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index bea2cdc..44198f0 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -25,7 +25,7 @@
 
 # NOTE(afazekas): This function needs to know a token and a subject.
 def wait_for_server_status(client, server_id, status, ready_wait=True,
-                           extra_timeout=0):
+                           extra_timeout=0, raise_on_error=True):
     """Waits for a server to reach a given status."""
 
     def _get_task_state(body):
@@ -69,16 +69,22 @@
                      '/'.join((old_status, str(old_task_state))),
                      '/'.join((server_status, str(task_state))),
                      time.time() - start_time)
-        if server_status == 'ERROR':
+        if (server_status == 'ERROR') and raise_on_error:
             raise exceptions.BuildErrorException(server_id=server_id)
 
         timed_out = int(time.time()) - start_time >= timeout
 
         if timed_out:
-            message = ('Server %s failed to reach %s status within the '
-                       'required time (%s s).' %
-                       (server_id, status, timeout))
+            expected_task_state = 'None' if ready_wait else 'n/a'
+            message = ('Server %(server_id)s failed to reach %(status)s '
+                       'status and task state "%(expected_task_state)s" '
+                       'within the required time (%(timeout)s s).' %
+                       {'server_id': server_id,
+                        'status': status,
+                        'expected_task_state': expected_task_state,
+                        'timeout': timeout})
             message += ' Current status: %s.' % server_status
+            message += ' Current task state: %s.' % task_state
             raise exceptions.TimeoutException(message)
         old_status = server_status
         old_task_state = task_state
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 4c1c107..2adc98e 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -21,6 +21,7 @@
 
 PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
 TEST_DEFINITION = re.compile(r'^\s*def test.*')
+SETUPCLASS_DEFINITION = re.compile(r'^\s*def setUpClass')
 SCENARIO_DECORATOR = re.compile(r'\s*@.*services\(')
 
 
@@ -52,6 +53,14 @@
                         "T104: Scenario tests require a service decorator")
 
 
+def no_setupclass_for_unit_tests(physical_line, filename):
+    if 'tempest/tests' in filename:
+        if SETUPCLASS_DEFINITION.match(physical_line):
+            return (physical_line.find('def'),
+                    "T105: setUpClass can not be used with unit tests")
+
+
 def factory(register):
     register(import_no_clients_in_api)
     register(scenario_tests_need_service_tags)
+    register(no_setupclass_for_unit_tests)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 06841e1..b24d2b9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -343,7 +343,11 @@
                     raise
 
             new_status = thing.status
-            if new_status == error_status:
+
+            # Some components are reporting error status in lower case
+            # so case sensitive comparisons can really mess things
+            # up.
+            if new_status.lower() == error_status.lower():
                 message = "%s failed to get to expected status. \
                           In %s state." % (thing, new_status)
                 raise exceptions.BuildErrorException(message)
@@ -537,6 +541,27 @@
         routers = self.network_client.list_routers()
         return routers['routers']
 
+    def _list_ports(self):
+        ports = self.network_client.list_ports()
+        return ports['ports']
+
+    def _get_tenant_own_network_num(self, tenant_id):
+        nets = self._list_networks()
+        ownnets = [value for value in nets if tenant_id == value['tenant_id']]
+        return len(ownnets)
+
+    def _get_tenant_own_subnet_num(self, tenant_id):
+        subnets = self._list_subnets()
+        ownsubnets = ([value for value in subnets
+                      if tenant_id == value['tenant_id']])
+        return len(ownsubnets)
+
+    def _get_tenant_own_port_num(self, tenant_id):
+        ports = self._list_ports()
+        ownports = ([value for value in ports
+                    if tenant_id == value['tenant_id']])
+        return len(ownports)
+
     def _create_subnet(self, network, namestart='subnet-smoke-'):
         """
         Create a subnet for the given network within the cidr block
@@ -603,7 +628,15 @@
         self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
         return floating_ip
 
-    def _ping_ip_address(self, ip_address):
+    def _disassociate_floating_ip(self, floating_ip):
+        """
+        :param floating_ip: type DeletableFloatingIp
+        """
+        floating_ip.update(port_id=None)
+        self.assertEqual(None, floating_ip.port_id)
+        return floating_ip
+
+    def _ping_ip_address(self, ip_address, should_succeed=True):
         cmd = ['ping', '-c1', '-w1', ip_address]
 
         def ping():
@@ -611,8 +644,7 @@
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
             proc.wait()
-            if proc.returncode == 0:
-                return True
+            return (proc.returncode == 0) == should_succeed
 
         return tempest.test.call_until_true(
             ping, self.config.compute.ping_timeout, 1)
@@ -624,17 +656,37 @@
                                 timeout=timeout)
         return ssh_client.test_connection_auth()
 
-    def _check_vm_connectivity(self, ip_address, username, private_key):
-        self.assertTrue(self._ping_ip_address(ip_address),
-                        "Timed out waiting for %s to become "
-                        "reachable" % ip_address)
-        self.assertTrue(self._is_reachable_via_ssh(
-            ip_address,
-            username,
-            private_key,
-            timeout=self.config.compute.ssh_timeout),
-            'Auth failure in connecting to %s@%s via ssh' %
-            (username, ip_address))
+    def _check_vm_connectivity(self, ip_address,
+                               username=None,
+                               private_key=None,
+                               should_connect=True):
+        """
+        :param ip_address: server to test against
+        :param username: server's ssh username
+        :param private_key: server's ssh private key to be used
+        :param should_connect: True/False indicates positive/negative test
+            positive - attempt ping and ssh
+            negative - attempt ping and fail if succeed
+
+        :raises: AssertError if the result of the connectivity check does
+            not match the value of the should_connect param
+        """
+        if should_connect:
+            msg = "Timed out waiting for %s to become reachable" % ip_address
+        else:
+            msg = "ip address %s is reachable" % ip_address
+        self.assertTrue(self._ping_ip_address(ip_address,
+                                              should_succeed=should_connect),
+                        msg=msg)
+        if should_connect:
+            # no need to check ssh for negative connectivity
+            self.assertTrue(self._is_reachable_via_ssh(
+                ip_address,
+                username,
+                private_key,
+                timeout=self.config.compute.ssh_timeout),
+                'Auth failure in connecting to %s@%s via ssh' %
+                (username, ip_address))
 
     def _create_security_group_nova(self, client=None,
                                     namestart='secgroup-smoke-',
@@ -801,6 +853,24 @@
 
         return rules
 
+    def _ssh_to_server(self, server, private_key):
+        ssh_login = self.config.compute.image_ssh_user
+        return self.get_remote_client(server,
+                                      username=ssh_login,
+                                      private_key=private_key)
+
+    def _show_quota_network(self, tenant_id):
+        quota = self.network_client.show_quota(tenant_id)
+        return quota['quota']['network']
+
+    def _show_quota_subnet(self, tenant_id):
+        quota = self.network_client.show_quota(tenant_id)
+        return quota['quota']['subnet']
+
+    def _show_quota_port(self, tenant_id):
+        quota = self.network_client.show_quota(tenant_id)
+        return quota['quota']['port']
+
 
 class OrchestrationScenarioTest(OfficialClientTest):
     """
diff --git a/tempest/scenario/orchestration/test_autoscaling.py b/tempest/scenario/orchestration/test_autoscaling.py
index e843793..90ef3a0 100644
--- a/tempest/scenario/orchestration/test_autoscaling.py
+++ b/tempest/scenario/orchestration/test_autoscaling.py
@@ -19,6 +19,7 @@
 from tempest.test import attr
 from tempest.test import call_until_true
 from tempest.test import services
+from tempest.test import skip_because
 
 
 class AutoScalingTest(manager.OrchestrationScenarioTest):
@@ -62,6 +63,7 @@
         if not self.config.orchestration.keypair_name:
             self.set_resource('stack', self.stack)
 
+    @skip_because(bug="1257575")
     @attr(type='slow')
     @services('orchestration', 'compute')
     def test_scale_up_then_down(self):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index bfded53..d605dff 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -20,14 +20,56 @@
 from tempest.common import debug
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest.openstack.common import jsonutils
 from tempest.openstack.common import log as logging
 from tempest.scenario import manager
+
+import tempest.test
 from tempest.test import attr
 from tempest.test import services
 
 LOG = logging.getLogger(__name__)
 
 
+class FloatingIPCheckTracker(object):
+    """
+    Checking VM connectivity through floating IP addresses is bound to fail
+    if the floating IP has not actually been associated with the VM yet.
+    This helper class facilitates checking for floating IP assignments on
+    VMs. It only checks for a given IP address once.
+    """
+
+    def __init__(self, compute_client, floating_ip_map):
+        self.compute_client = compute_client
+        self.unchecked = floating_ip_map.copy()
+
+    def run_checks(self):
+        """Check for any remaining unverified floating IPs
+
+        Gets VM details from nova and checks for floating IPs
+        within the returned information. Returns true when all
+        checks are complete and is suitable for use with
+        tempest.test.call_until_true()
+        """
+        to_delete = []
+        loggable_map = {}
+        for check_addr, server in self.unchecked.iteritems():
+            serverdata = self.compute_client.servers.get(server.id)
+            ip_addr = [addr for sublist in serverdata.networks.values() for
+                       addr in sublist]
+            if check_addr.floating_ip_address in ip_addr:
+                to_delete.append(check_addr)
+            else:
+                loggable_map[server.id] = check_addr
+
+        for to_del in to_delete:
+            del self.unchecked[to_del]
+
+        LOG.debug('Unchecked floating IPs: %s',
+                  jsonutils.dumps(loggable_map))
+        return len(self.unchecked) == 0
+
+
 class TestNetworkBasicOps(manager.NetworkScenarioTest):
 
     """
@@ -46,6 +88,9 @@
          ssh server hosted at the IP address.  This check guarantees
          that the IP address is associated with the target VM.
 
+       - detach the floating-ip from the VM and verify that it becomes
+       unreachable
+
        # TODO(mnewby) - Need to implement the following:
        - the Tempest host can ssh into the VM via the IP address and
          successfully execute the following:
@@ -228,36 +273,56 @@
         # key-based authentication by cloud-init.
         ssh_login = self.config.compute.image_ssh_user
         private_key = self.keypairs[self.tenant_id].private_key
-        for server in self.servers:
-            for net_name, ip_addresses in server.networks.iteritems():
-                for ip_address in ip_addresses:
-                    self._check_vm_connectivity(ip_address, ssh_login,
-                                                private_key)
+        try:
+            for server in self.servers:
+                for net_name, ip_addresses in server.networks.iteritems():
+                    for ip_address in ip_addresses:
+                        self._check_vm_connectivity(ip_address, ssh_login,
+                                                    private_key)
+        except Exception as exc:
+            LOG.exception(exc)
+            debug.log_ip_ns()
+            raise exc
 
-    def _assign_floating_ips(self):
+    def _wait_for_floating_ip_association(self):
+        ip_tracker = FloatingIPCheckTracker(self.compute_client,
+                                            self.floating_ips)
+
+        self.assertTrue(
+            tempest.test.call_until_true(
+                ip_tracker.run_checks, self.config.compute.build_timeout,
+                self.config.compute.build_interval),
+            "Timed out while waiting for the floating IP assignments "
+            "to propagate")
+
+    def _create_and_associate_floating_ips(self):
         public_network_id = self.config.network.public_network_id
         for server in self.servers:
             floating_ip = self._create_floating_ip(server, public_network_id)
-            self.floating_ips.setdefault(server, [])
-            self.floating_ips[server].append(floating_ip)
+            self.floating_ips[floating_ip] = server
 
-    def _check_public_network_connectivity(self):
+    def _check_public_network_connectivity(self, should_connect=True):
         # The target login is assumed to have been configured for
         # key-based authentication by cloud-init.
         ssh_login = self.config.compute.image_ssh_user
         private_key = self.keypairs[self.tenant_id].private_key
         try:
-            for server, floating_ips in self.floating_ips.iteritems():
-                for floating_ip in floating_ips:
-                    ip_address = floating_ip.floating_ip_address
-                    self._check_vm_connectivity(ip_address,
-                                                ssh_login,
-                                                private_key)
+            for floating_ip, server in self.floating_ips.iteritems():
+                ip_address = floating_ip.floating_ip_address
+                self._check_vm_connectivity(ip_address,
+                                            ssh_login,
+                                            private_key,
+                                            should_connect=should_connect)
         except Exception as exc:
             LOG.exception(exc)
             debug.log_ip_ns()
             raise exc
 
+    def _disassociate_floating_ips(self):
+        for floating_ip, server in self.floating_ips.iteritems():
+            self._disassociate_floating_ip(floating_ip)
+            self.floating_ips[floating_ip] = None
+
     @attr(type='smoke')
     @services('compute', 'network')
     def test_network_basic_ops(self):
@@ -266,6 +331,9 @@
         self._create_networks()
         self._check_networks()
         self._create_servers()
-        self._assign_floating_ips()
-        self._check_public_network_connectivity()
+        self._create_and_associate_floating_ips()
+        self._wait_for_floating_ip_association()
         self._check_tenant_network_connectivity()
+        self._check_public_network_connectivity(should_connect=True)
+        self._disassociate_floating_ips()
+        self._check_public_network_connectivity(should_connect=False)
diff --git a/tempest/scenario/test_network_quotas.py b/tempest/scenario/test_network_quotas.py
index 3268066..cb7aa0b 100644
--- a/tempest/scenario/test_network_quotas.py
+++ b/tempest/scenario/test_network_quotas.py
@@ -20,8 +20,6 @@
 from tempest.scenario.manager import NetworkScenarioTest
 from tempest.test import services
 
-MAX_REASONABLE_ITERATIONS = 51  # more than enough. Default for port is 50.
-
 
 class TestNetworkQuotaBasic(NetworkScenarioTest):
     """
@@ -46,7 +44,9 @@
     @services('network')
     def test_create_network_until_quota_hit(self):
         hit_limit = False
-        for n in xrange(MAX_REASONABLE_ITERATIONS):
+        networknum = self._get_tenant_own_network_num(self.tenant_id)
+        max = self._show_quota_network(self.tenant_id) - networknum
+        for n in xrange(max):
             try:
                 self.networks.append(
                     self._create_network(self.tenant_id,
@@ -56,6 +56,16 @@
                     raise
                 hit_limit = True
                 break
+        self.assertFalse(hit_limit, "Failed: Hit quota limit !")
+
+        try:
+            self.networks.append(
+                self._create_network(self.tenant_id,
+                                     namestart='network-quotatest-'))
+        except exc.NeutronClientException as e:
+            if (e.status_code != 409):
+                raise
+            hit_limit = True
         self.assertTrue(hit_limit, "Failed: Did not hit quota limit !")
 
     @services('network')
@@ -65,7 +75,9 @@
                 self._create_network(self.tenant_id,
                                      namestart='network-quotatest-'))
         hit_limit = False
-        for n in xrange(MAX_REASONABLE_ITERATIONS):
+        subnetnum = self._get_tenant_own_subnet_num(self.tenant_id)
+        max = self._show_quota_subnet(self.tenant_id) - subnetnum
+        for n in xrange(max):
             try:
                 self.subnets.append(
                     self._create_subnet(self.networks[0],
@@ -75,6 +87,16 @@
                     raise
                 hit_limit = True
                 break
+        self.assertFalse(hit_limit, "Failed: Hit quota limit !")
+
+        try:
+            self.subnets.append(
+                self._create_subnet(self.networks[0],
+                                    namestart='subnet-quotatest-'))
+        except exc.NeutronClientException as e:
+            if (e.status_code != 409):
+                raise
+            hit_limit = True
         self.assertTrue(hit_limit, "Failed: Did not hit quota limit !")
 
     @services('network')
@@ -84,7 +106,9 @@
                 self._create_network(self.tenant_id,
                                      namestart='network-quotatest-'))
         hit_limit = False
-        for n in xrange(MAX_REASONABLE_ITERATIONS):
+        portnum = self._get_tenant_own_port_num(self.tenant_id)
+        max = self._show_quota_port(self.tenant_id) - portnum
+        for n in xrange(max):
             try:
                 self.ports.append(
                     self._create_port(self.networks[0],
@@ -94,4 +118,14 @@
                     raise
                 hit_limit = True
                 break
+        self.assertFalse(hit_limit, "Failed: Hit quota limit !")
+
+        try:
+            self.ports.append(
+                self._create_port(self.networks[0],
+                                  namestart='port-quotatest-'))
+        except exc.NeutronClientException as e:
+            if (e.status_code != 409):
+                raise
+            hit_limit = True
         self.assertTrue(hit_limit, "Failed: Did not hit quota limit !")
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 3c6a40f..eb1a0c3 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -154,10 +154,12 @@
         body = json.loads(body)
         return resp, body
 
-    def wait_for_server_status(self, server_id, status, extra_timeout=0):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0,
+                               raise_on_error=True):
         """Waits for a server to reach a given status."""
         return waiters.wait_for_server_status(self, server_id, status,
-                                              extra_timeout=extra_timeout)
+                                              extra_timeout=extra_timeout,
+                                              raise_on_error=raise_on_error)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 7d40d0e..68f6cf0 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -363,10 +363,12 @@
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
-    def wait_for_server_status(self, server_id, status, extra_timeout=0):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0,
+                               raise_on_error=True):
         """Waits for a server to reach a given status."""
         return waiters.wait_for_server_status(self, server_id, status,
-                                              extra_timeout=extra_timeout)
+                                              extra_timeout=extra_timeout,
+                                              raise_on_error=raise_on_error)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index ec99d37..e457c1f 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -359,6 +359,66 @@
                                  (domain_id, group_id, role_id))
         return resp, body
 
+    def create_trust(self, trustor_user_id, trustee_user_id, project_id,
+                     role_names, impersonation, expires_at):
+        """Creates a trust."""
+        roles = [{'name': n} for n in role_names]
+        post_body = {
+            'trustor_user_id': trustor_user_id,
+            'trustee_user_id': trustee_user_id,
+            'project_id': project_id,
+            'impersonation': impersonation,
+            'roles': roles,
+            'expires_at': expires_at
+        }
+        post_body = json.dumps({'trust': post_body})
+        resp, body = self.post('OS-TRUST/trusts', post_body, self.headers)
+        body = json.loads(body)
+        return resp, body['trust']
+
+    def delete_trust(self, trust_id):
+        """Deletes a trust."""
+        resp, body = self.delete("OS-TRUST/trusts/%s" % trust_id)
+        return resp, body
+
+    def get_trusts(self, trustor_user_id=None, trustee_user_id=None):
+        """GET trusts."""
+        if trustor_user_id:
+            resp, body = self.get("OS-TRUST/trusts?trustor_user_id=%s"
+                                  % trustor_user_id)
+        elif trustee_user_id:
+            resp, body = self.get("OS-TRUST/trusts?trustee_user_id=%s"
+                                  % trustee_user_id)
+        else:
+            resp, body = self.get("OS-TRUST/trusts")
+        body = json.loads(body)
+        return resp, body['trusts']
+
+    def get_trust(self, trust_id):
+        """GET trust."""
+        resp, body = self.get("OS-TRUST/trusts/%s" % trust_id)
+        body = json.loads(body)
+        return resp, body['trust']
+
+    def get_trust_roles(self, trust_id):
+        """GET roles delegated by a trust."""
+        resp, body = self.get("OS-TRUST/trusts/%s/roles" % trust_id)
+        body = json.loads(body)
+        return resp, body['roles']
+
+    def get_trust_role(self, trust_id, role_id):
+        """GET role delegated by a trust."""
+        resp, body = self.get("OS-TRUST/trusts/%s/roles/%s"
+                              % (trust_id, role_id))
+        body = json.loads(body)
+        return resp, body['role']
+
+    def check_trust_role(self, trust_id, role_id):
+        """HEAD Check if role is delegated by a trust."""
+        resp, body = self.head("OS-TRUST/trusts/%s/roles/%s"
+                               % (trust_id, role_id))
+        return resp, body
+
 
 class V3TokenClientJSON(RestClient):
 
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index c6bd423..3199537 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -680,6 +680,24 @@
         body = json.loads(body)
         return resp, body
 
+    def show_agent(self, agent_id):
+        uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def update_agent(self, agent_id, agent_info):
+        """
+        :param agent_info: Agent update information.
+        E.g {"admin_state_up": True}
+        """
+        uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
+        agent = {"agent": agent_info}
+        body = json.dumps(agent)
+        resp, body = self.put(uri, body=body, headers=self.headers)
+        body = json.loads(body)
+        return resp, body
+
     def list_routers_on_l3_agent(self, agent_id):
         uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
         resp, body = self.get(uri, self.headers)
@@ -754,3 +772,29 @@
         resp, body = self.put(uri, body=body, headers=self.headers)
         body = json.loads(body)
         return resp, body
+
+    def update_extra_routes(self, router_id, nexthop, destination):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        put_body = {
+            'router': {
+                'routes': [{'nexthop': nexthop,
+                            "destination": destination}]
+            }
+        }
+        body = json.dumps(put_body)
+        resp, body = self.put(uri, body=body, headers=self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def delete_extra_routes(self, router_id):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        null_routes = None
+        put_body = {
+            'router': {
+                'routes': null_routes
+            }
+        }
+        body = json.dumps(put_body)
+        resp, body = self.put(uri, body=body, headers=self.headers)
+        body = json.loads(body)
+        return resp, body
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index e11d4c1..c46a523 100755
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -561,6 +561,23 @@
         agents = {'agents': agents}
         return resp, agents
 
+    def show_agent(self, agent_id):
+        uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri, self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
+    def update_agent(self, agent_id, agent_info):
+        uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
+        agent = Element('agent')
+        for (key, value) in agent_info.items():
+            p = Element(key, value)
+            agent.append(p)
+        resp, body = self.put(uri, body=str(Document(agent)),
+                              headers=self.headers)
+        body = _root_tag_fetcher_and_xml_to_json_parse(body)
+        return resp, body
+
     def list_routers_on_l3_agent(self, agent_id):
         uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
         resp, body = self.get(uri, self.headers)
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index 5d980eb..9435122 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -44,7 +44,7 @@
         body = json.loads(body)
         return resp, body['snapshots']
 
-    def list_snapshot_with_detail(self, params=None):
+    def list_snapshots_with_detail(self, params=None):
         """List the details of all snapshots."""
         url = 'snapshots/detail'
         if params:
diff --git a/tempest/tests/test_wrappers.py b/tempest/tests/test_wrappers.py
index dbf1809..88bef9b 100644
--- a/tempest/tests/test_wrappers.py
+++ b/tempest/tests/test_wrappers.py
@@ -56,7 +56,7 @@
         # version or an sdist to work. so make the test directory a git repo
         # too.
         subprocess.call(['git', 'init'])
-        exit_code = subprocess.call('sh pretty_tox.sh tests.passing',
+        exit_code = subprocess.call('bash pretty_tox.sh tests.passing',
                                     shell=True, stdout=DEVNULL, stderr=DEVNULL)
         self.assertEqual(exit_code, 0)
 
@@ -71,7 +71,7 @@
         # version or an sdist to work. so make the test directory a git repo
         # too.
         subprocess.call(['git', 'init'])
-        exit_code = subprocess.call('sh pretty_tox.sh', shell=True,
+        exit_code = subprocess.call('bash pretty_tox.sh', shell=True,
                                     stdout=DEVNULL, stderr=DEVNULL)
         self.assertEqual(exit_code, 1)
 
@@ -82,7 +82,7 @@
         # Change directory, run wrapper and check result
         self.addCleanup(os.chdir, os.path.abspath(os.curdir))
         os.chdir(self.directory)
-        exit_code = subprocess.call('sh pretty_tox_serial.sh tests.passing',
+        exit_code = subprocess.call('bash pretty_tox_serial.sh tests.passing',
                                     shell=True, stdout=DEVNULL, stderr=DEVNULL)
         self.assertEqual(exit_code, 0)
 
@@ -93,6 +93,6 @@
         # Change directory, run wrapper and check result
         self.addCleanup(os.chdir, os.path.abspath(os.curdir))
         os.chdir(self.directory)
-        exit_code = subprocess.call('sh pretty_tox_serial.sh', shell=True,
+        exit_code = subprocess.call('bash pretty_tox_serial.sh', shell=True,
                                     stdout=DEVNULL, stderr=DEVNULL)
         self.assertEqual(exit_code, 1)
diff --git a/tools/check_logs.py b/tools/check_logs.py
index 6d4436e..963709b 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -27,11 +27,13 @@
 
 
 is_neutron = os.environ.get('DEVSTACK_GATE_NEUTRON', "0") == "1"
+is_grenade = (os.environ.get('DEVSTACK_GATE_GRENADE', "0") == "1" or
+              os.environ.get('DEVSTACK_GATE_GRENADE_FORWARD', "0") == "1")
 dump_all_errors = is_neutron
 
 
 def process_files(file_specs, url_specs, whitelists):
-    regexp = re.compile(r"^.*(ERROR|CRITICAL).*\[.*\-.*\]")
+    regexp = re.compile(r"^.* (ERROR|CRITICAL) .*\[.*\-.*\]")
     had_errors = False
     for (name, filename) in file_specs:
         whitelist = whitelists.get(name, [])
@@ -125,6 +127,9 @@
         if is_neutron:
             print("Currently not failing neutron builds with errors")
             return 0
+        if is_grenade:
+            print("Currently not failing grenade runs with errors")
+            return 0
         print("FAILED")
         return 1
     else:
diff --git a/tools/pretty_tox.sh b/tools/pretty_tox.sh
index a5a6076..07c35a0 100755
--- a/tools/pretty_tox.sh
+++ b/tools/pretty_tox.sh
@@ -1,4 +1,6 @@
-#!/bin/sh
+#!/usr/bin/env bash
+
+set -o pipefail
 
 TESTRARGS=$1
 python setup.py testr --slowest --testr-args="--subunit $TESTRARGS" | subunit2pyunit
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
index 45f05bd..42ce760 100755
--- a/tools/pretty_tox_serial.sh
+++ b/tools/pretty_tox_serial.sh
@@ -1,4 +1,6 @@
-#!/bin/sh
+#!/usr/bin/env bash
+
+set -o pipefail
 
 TESTRARGS=$@
 
diff --git a/tox.ini b/tox.ini
index e5698d2..c7f92ae 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,14 +9,18 @@
          LANGUAGE=en_US:en
          LC_ALL=C
 usedevelop = True
+install_command = pip install -U {opts} {packages}
 
 [testenv:py26]
+setenv = OS_TEST_PATH=./tempest/tests
 commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
 
 [testenv:py33]
+setenv = OS_TEST_PATH=./tempest/tests
 commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
 
 [testenv:py27]
+setenv = OS_TEST_PATH=./tempest/tests
 commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
 
 [testenv:all]
@@ -30,19 +34,19 @@
 # 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.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
+  bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
 
 [testenv:testr-full]
 sitepackages = True
 commands =
-  sh tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
+  bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
 
 [testenv:heat-slow]
 sitepackages = True
 setenv = OS_TEST_TIMEOUT=1200
 # 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}'
+  bash tools/pretty_tox_serial.sh '(?=.*\[.*\bslow\b.*\])(^tempest\.(api|scenario)\.orchestration) {posargs}'
 
 [testenv:large-ops]
 sitepackages = True
@@ -78,7 +82,7 @@
 [testenv:smoke]
 sitepackages = True
 commands =
-   sh tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
+   bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
 
 [testenv:smoke-serial]
 sitepackages = True
@@ -86,7 +90,7 @@
 # https://bugs.launchpad.net/tempest/+bug/1216076 so the neutron smoke
 # job would fail if we moved it to parallel.
 commands =
-   sh tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
+   bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
 
 [testenv:stress]
 sitepackages = True