Merge "port test_quotas into v3 part1"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 8742b67..937bbd3 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -220,14 +220,6 @@
 # admin credentials are known. (boolean value)
 #allow_tenant_isolation=false
 
-# If allow_tenant_isolation is True and a tenant that would be
-# created for a given test already exists (such as from a
-# previously-failed run), re-use that tenant instead of
-# failing because of the conflict. Note that this would result
-# in the tenant being deleted at the end of a subsequent
-# successful run. (boolean value)
-#allow_tenant_reuse=true
-
 # Valid secondary image reference to be used in tests. (string
 # value)
 #image_ref={$IMAGE_ID}
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 7ef5466..1ba9b16 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -181,17 +181,18 @@
         return resp, image
 
     @classmethod
-    def rebuild_server(cls, **kwargs):
+    def rebuild_server(cls, server_id, **kwargs):
         # Destroy an existing server and creates a new one
-        try:
-            cls.servers_client.delete_server(cls.server_id)
-            cls.servers_client.wait_for_server_termination(cls.server_id)
-        except Exception as exc:
-            LOG.exception(exc)
-            pass
+        if server_id:
+            try:
+                cls.servers_client.delete_server(server_id)
+                cls.servers_client.wait_for_server_termination(server_id)
+            except Exception as exc:
+                LOG.exception(exc)
+                pass
         resp, server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
-        cls.server_id = server['id']
         cls.password = server['adminPass']
+        return server['id']
 
 
 class BaseV2ComputeAdminTest(BaseV2ComputeTest):
@@ -237,6 +238,7 @@
         cls.availability_zone_client = cls.os.availability_zone_v3_client
         cls.interfaces_client = cls.os.interfaces_v3_client
         cls.hypervisor_client = cls.os.hypervisor_v3_client
+        cls.tenant_usages_client = cls.os.tenant_usages_v3_client
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
@@ -258,17 +260,17 @@
         return resp, image
 
     @classmethod
-    def rebuild_server(cls, **kwargs):
+    def rebuild_server(cls, server_id, **kwargs):
         # Destroy an existing server and creates a new one
         try:
-            cls.servers_client.delete_server(cls.server_id)
-            cls.servers_client.wait_for_server_termination(cls.server_id)
+            cls.servers_client.delete_server(server_id)
+            cls.servers_client.wait_for_server_termination(server_id)
         except Exception as exc:
             LOG.exception(exc)
             pass
         resp, server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
-        cls.server_id = server['id']
         cls.password = server['admin_password']
+        return server['id']
 
 
 class BaseV3ComputeAdminTest(BaseV3ComputeTest):
@@ -300,3 +302,4 @@
         cls.availability_zone_admin_client = \
             cls.os_adm.availability_zone_v3_client
         cls.hypervisor_admin_client = cls.os_adm.hypervisor_v3_client
+        cls.tenant_usages_admin_client = cls.os_adm.tenant_usages_v3_client
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index a06309a..f4ad449 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -15,10 +15,8 @@
 #    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.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
 
@@ -32,7 +30,7 @@
     def setUpClass(cls):
         super(FloatingIPsTestJSON, cls).setUpClass()
         cls.client = cls.floating_ips_client
-        cls.servers_client = cls.servers_client
+        #cls.servers_client = cls.servers_client
 
         # Server creation
         resp, server = cls.create_test_server(wait_until='ACTIVE')
@@ -41,17 +39,6 @@
         resp, body = cls.client.create_floating_ip()
         cls.floating_ip_id = body['id']
         cls.floating_ip = body['ip']
-        # Generating a nonexistent floatingIP id
-        cls.floating_ip_ids = []
-        resp, body = cls.client.list_floating_ips()
-        for i in range(len(body)):
-            cls.floating_ip_ids.append(body[i]['id'])
-        while True:
-            cls.non_exist_id = data_utils.rand_int_id(start=999)
-            if cls.config.service_available.neutron:
-                cls.non_exist_id = str(uuid.uuid4())
-            if cls.non_exist_id not in cls.floating_ip_ids:
-                break
 
     @classmethod
     def tearDownClass(cls):
@@ -76,14 +63,6 @@
             # Deleting the floating IP which is created in this method
             self.client.delete_floating_ip(floating_ip_id_allocated)
 
-    @attr(type=['negative', 'gate'])
-    def test_allocate_floating_ip_from_nonexistent_pool(self):
-        # Positive test:Allocation of a new floating IP from a nonexistent_pool
-        # to a project should fail
-        self.assertRaises(exceptions.NotFound,
-                          self.client.create_floating_ip,
-                          "non_exist_pool")
-
     @attr(type='gate')
     def test_delete_floating_ip(self):
         # Positive test:Deletion of valid floating IP from project
@@ -115,38 +94,13 @@
             self.server_id)
         self.assertEqual(202, resp.status)
 
-    @attr(type=['negative', 'gate'])
-    def test_delete_nonexistant_floating_ip(self):
-        # Negative test:Deletion of a nonexistent floating IP
-        # from project should fail
-
-        # Deleting the non existent floating IP
-        self.assertRaises(exceptions.NotFound, self.client.delete_floating_ip,
-                          self.non_exist_id)
-
-    @attr(type=['negative', 'gate'])
-    def test_associate_nonexistant_floating_ip(self):
-        # Negative test:Association of a non existent floating IP
-        # to specific server should fail
-        # Associating non existent floating IP
-        self.assertRaises(exceptions.NotFound,
-                          self.client.associate_floating_ip_to_server,
-                          "0.0.0.0", self.server_id)
-
-    @attr(type=['negative', 'gate'])
-    def test_dissociate_nonexistant_floating_ip(self):
-        # Negative test:Dissociation of a non existent floating IP should fail
-        # Dissociating non existent floating IP
-        self.assertRaises(exceptions.NotFound,
-                          self.client.disassociate_floating_ip_from_server,
-                          "0.0.0.0", self.server_id)
-
     @attr(type='gate')
     def test_associate_already_associated_floating_ip(self):
         # positive test:Association of an already associated floating IP
         # to specific server should change the association of the Floating IP
         # Create server so as to use for Multiple association
-        resp, body = self.servers_client.create_server('floating-server2',
+        new_name = rand_name('floating_server')
+        resp, body = self.servers_client.create_server(new_name,
                                                        self.image_ref,
                                                        self.flavor_ref)
         self.servers_client.wait_for_server_status(body['id'], 'ACTIVE')
@@ -173,14 +127,6 @@
                           self.client.disassociate_floating_ip_from_server,
                           self.floating_ip, self.server_id)
 
-    @attr(type=['negative', 'gate'])
-    def test_associate_ip_to_server_without_passing_floating_ip(self):
-        # Negative test:Association of empty floating IP to specific server
-        # should raise NotFound exception
-        self.assertRaises(exceptions.NotFound,
-                          self.client.associate_floating_ip_to_server,
-                          '', self.server_id)
-
 
 class FloatingIPsTestXML(FloatingIPsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
new file mode 100644
index 0000000..89315bb
--- /dev/null
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -0,0 +1,94 @@
+# 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.test import attr
+
+
+class FloatingIPsNegativeTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+    server_id = None
+
+    @classmethod
+    def setUpClass(cls):
+        super(FloatingIPsNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.floating_ips_client
+
+        # Server creation
+        resp, server = cls.create_test_server(wait_until='ACTIVE')
+        cls.server_id = server['id']
+        # Generating a nonexistent floatingIP id
+        cls.floating_ip_ids = []
+        resp, body = cls.client.list_floating_ips()
+        for i in range(len(body)):
+            cls.floating_ip_ids.append(body[i]['id'])
+        while True:
+            cls.non_exist_id = data_utils.rand_int_id(start=999)
+            if cls.config.service_available.neutron:
+                cls.non_exist_id = str(uuid.uuid4())
+            if cls.non_exist_id not in cls.floating_ip_ids:
+                break
+
+    @attr(type=['negative', 'gate'])
+    def test_allocate_floating_ip_from_nonexistent_pool(self):
+        # Negative test:Allocation of a new floating IP from a nonexistent_pool
+        # to a project should fail
+        self.assertRaises(exceptions.NotFound,
+                          self.client.create_floating_ip,
+                          "non_exist_pool")
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_nonexistent_floating_ip(self):
+        # Negative test:Deletion of a nonexistent floating IP
+        # from project should fail
+
+        # Deleting the non existent floating IP
+        self.assertRaises(exceptions.NotFound, self.client.delete_floating_ip,
+                          self.non_exist_id)
+
+    @attr(type=['negative', 'gate'])
+    def test_associate_nonexistent_floating_ip(self):
+        # Negative test:Association of a non existent floating IP
+        # to specific server should fail
+        # Associating non existent floating IP
+        self.assertRaises(exceptions.NotFound,
+                          self.client.associate_floating_ip_to_server,
+                          "0.0.0.0", self.server_id)
+
+    @attr(type=['negative', 'gate'])
+    def test_dissociate_nonexistent_floating_ip(self):
+        # Negative test:Dissociation of a non existent floating IP should fail
+        # Dissociating non existent floating IP
+        self.assertRaises(exceptions.NotFound,
+                          self.client.disassociate_floating_ip_from_server,
+                          "0.0.0.0", self.server_id)
+
+    @attr(type=['negative', 'gate'])
+    def test_associate_ip_to_server_without_passing_floating_ip(self):
+        # Negative test:Association of empty floating IP to specific server
+        # should raise NotFound exception
+        self.assertRaises(exceptions.NotFound,
+                          self.client.associate_floating_ip_to_server,
+                          '', self.server_id)
+
+
+class FloatingIPsNegativeTestXML(FloatingIPsNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 612c110..18e32d8 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.rebuild_server()
+            self.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index b4e778c..4cd41ee 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -50,7 +50,7 @@
             LOG.exception(exc)
             # Rebuild server if cannot reach the ACTIVE state
             # Usually it means the server had a serius accident
-            self.rebuild_server()
+            self.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 5e9ee5c..0121c42 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -80,7 +80,7 @@
     def _get_alternative_flavor(self):
         resp, server = self.client.get_server(self.server_id)
 
-        if int(server['flavor']['id']) == self.flavor_ref:
+        if server['flavor']['id'] == self.flavor_ref:
             return self.flavor_ref_alt
         else:
             return self.flavor_ref
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index e3441bd..5fa4c35 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -45,13 +45,13 @@
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
         except Exception:
             # Rebuild server if something happened to it during a test
-            self.rebuild_server()
+            self.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
         super(ServerActionsTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
-        cls.rebuild_server()
+        cls.server_id = cls.rebuild_server(None)
 
     @testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
                           'Change password not available.')
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 5ec0cbe..7b86d2d 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.rebuild_server()
+            self.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/v3/admin/test_simple_tenant_usage.py b/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
index a599f06..3fc58eb 100644
--- a/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
@@ -23,15 +23,15 @@
 import time
 
 
-class TenantUsagesTestJSON(base.BaseV2ComputeAdminTest):
+class TenantUsagesV3TestJSON(base.BaseV3ComputeAdminTest):
 
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(TenantUsagesTestJSON, cls).setUpClass()
-        cls.adm_client = cls.os_adm.tenant_usages_client
-        cls.client = cls.os.tenant_usages_client
+        super(TenantUsagesV3TestJSON, cls).setUpClass()
+        cls.adm_client = cls.tenant_usages_admin_client
+        cls.client = cls.tenant_usages_client
         cls.identity_client = cls._get_identity_admin_client()
 
         resp, tenants = cls.identity_client.list_tenants()
@@ -111,5 +111,5 @@
                           self.client.list_tenant_usages, params)
 
 
-class TenantUsagesTestXML(TenantUsagesTestJSON):
+class TenantUsagesV3TestXML(TenantUsagesV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 20cdf30..dc78a47 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -45,13 +45,13 @@
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
         except Exception:
             # Rebuild server if something happened to it during a test
-            self.rebuild_server()
+            self.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
         super(ServerActionsV3TestJSON, cls).setUpClass()
         cls.client = cls.servers_client
-        cls.rebuild_server()
+        cls.server_id = cls.rebuild_server(None)
 
     @testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
                           'Change password not available.')
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index 5ec0cbe..7b86d2d 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.rebuild_server()
+            self.server_id = self.rebuild_server(self.server_id)
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
new file mode 100644
index 0000000..ac10e68
--- /dev/null
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -0,0 +1,78 @@
+# 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.test import attr
+
+
+class DHCPAgentSchedulersTestJSON(base.BaseAdminNetworkTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(DHCPAgentSchedulersTestJSON, cls).setUpClass()
+        # Create a network and make sure it will be hosted by a
+        # dhcp agent.
+        cls.network = cls.create_network()
+        cls.subnet = cls.create_subnet(cls.network)
+        cls.cidr = cls.subnet['cidr']
+        cls.port = cls.create_port(cls.network)
+
+    @attr(type='smoke')
+    def test_list_dhcp_agent_hosting_network(self):
+        resp, body = self.admin_client.list_dhcp_agent_hosting_network(
+            self.network['id'])
+        self.assertEqual(resp['status'], '200')
+
+    @attr(type='smoke')
+    def test_list_networks_hosted_by_one_dhcp(self):
+        resp, body = self.admin_client.list_dhcp_agent_hosting_network(
+            self.network['id'])
+        agents = body['agents']
+        self.assertIsNotNone(agents)
+        agent = agents[0]
+        self.assertTrue(self._check_network_in_dhcp_agent(
+            self.network['id'], agent))
+
+    def _check_network_in_dhcp_agent(self, network_id, agent):
+        network_ids = []
+        resp, body = self.admin_client.list_networks_hosted_by_one_dhcp_agent(
+            agent['id'])
+        self.assertEqual(resp['status'], '200')
+        networks = body['networks']
+        for network in networks:
+            network_ids.append(network['id'])
+        return network_id in network_ids
+
+    @attr(type='smoke')
+    def test_remove_network_from_dhcp_agent(self):
+        resp, body = self.admin_client.list_dhcp_agent_hosting_network(
+            self.network['id'])
+        agents = body['agents']
+        self.assertIsNotNone(agents)
+        # Get an agent.
+        agent = agents[0]
+        network_id = self.network['id']
+        resp, body = self.admin_client.remove_network_from_dhcp_agent(
+            agent_id=agent['id'],
+            network_id=network_id)
+        self.assertEqual(resp['status'], '204')
+        self.assertFalse(self._check_network_in_dhcp_agent(
+            network_id, agent))
+
+
+class DHCPAgentSchedulersTestXML(DHCPAgentSchedulersTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index cb0c247..32f4c95 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -15,10 +15,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import uuid
+
 from tempest.api.network import base_security_groups as base
 from tempest import exceptions
 from tempest.test import attr
-import uuid
 
 
 class NegativeSecGroupTest(base.BaseSecGroupTest):
@@ -74,6 +75,22 @@
                                    port_range_max=pmax)
             self.assertIn(msg, str(ex))
 
+    @attr(type=['negative', 'smoke'])
+    def test_create_additional_default_security_group_fails(self):
+        # Create security group named 'default', it should be failed.
+        name = 'default'
+        self.assertRaises(exceptions.Conflict,
+                          self.client.create_security_group,
+                          name)
+
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_with_non_existent_security_group(self):
+        # Create security group rules with not existing security group.
+        non_existent_sg = str(uuid.uuid4())
+        self.assertRaises(exceptions.NotFound,
+                          self.client.create_security_group_rule,
+                          non_existent_sg)
+
 
 class NegativeSecGroupTestXML(NegativeSecGroupTest):
     _interface = 'xml'
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 94048f7..1eea30a 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -16,7 +16,7 @@
 
 from tempest.api.object_storage import base
 from tempest.common.utils import data_utils
-from tempest.test import attr, HTTP_SUCCESS
+from tempest import test
 
 
 class StaticWebTest(base.BaseObjectTest):
@@ -48,7 +48,7 @@
         cls.data.teardown_all()
         super(StaticWebTest, cls).tearDownClass()
 
-    @attr('gate')
+    @test.attr('gate')
     def test_web_index(self):
         headers = {'web-index': self.object_name}
 
@@ -59,7 +59,7 @@
         # we should retrieve the self.object_name file
         resp, body = self.custom_account_client.request("GET",
                                                         self.container_name)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertEqual(body, self.object_data)
 
         # clean up before exiting
@@ -70,7 +70,7 @@
             self.container_name)
         self.assertNotIn('x-container-meta-web-index', body)
 
-    @attr('gate')
+    @test.attr('gate')
     def test_web_listing(self):
         headers = {'web-listings': 'true'}
 
@@ -81,7 +81,7 @@
         # we should retrieve a listing of objects
         resp, body = self.custom_account_client.request("GET",
                                                         self.container_name)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertIn(self.object_name, body)
 
         # clean up before exiting
@@ -92,7 +92,7 @@
             self.container_name)
         self.assertNotIn('x-container-meta-web-listings', body)
 
-    @attr('gate')
+    @test.attr('gate')
     def test_web_listing_css(self):
         headers = {'web-listings': 'true',
                    'web-listings-css': 'listings.css'}
@@ -104,12 +104,12 @@
         # we should retrieve a listing of objects
         resp, body = self.custom_account_client.request("GET",
                                                         self.container_name)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertIn(self.object_name, body)
         css = '<link rel="stylesheet" type="text/css" href="listings.css" />'
         self.assertIn(css, body)
 
-    @attr('gate')
+    @test.attr('gate')
     def test_web_error(self):
         headers = {'web-listings': 'true',
                    'web-error': self.object_name}
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
new file mode 100644
index 0000000..997120a
--- /dev/null
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -0,0 +1,193 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NTT Corporation
+#
+#    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 hashlib
+import json
+import re
+
+from tempest.api.object_storage import base
+from tempest.common import custom_matchers
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+# Each segment, except for the final one, must be at least 1 megabyte
+MIN_SEGMENT_SIZE = 1024 * 1024
+
+
+class ObjectSloTest(base.BaseObjectTest):
+
+    def setUp(self):
+        super(ObjectSloTest, self).setUp()
+        self.container_name = data_utils.rand_name(name='TestContainer')
+        self.container_client.create_container(self.container_name)
+        self.objects = []
+
+    def tearDown(self):
+        for obj in self.objects:
+            try:
+                self.object_client.delete_object(
+                    self.container_name,
+                    obj)
+            except exceptions.NotFound:
+                pass
+        self.container_client.delete_container(self.container_name)
+        super(ObjectSloTest, self).tearDown()
+
+    def _create_object(self, container_name, object_name, data, params=None):
+        resp, _ = self.object_client.create_object(container_name,
+                                                   object_name,
+                                                   data,
+                                                   params)
+        self.objects.append(object_name)
+
+        return resp
+
+    def _create_manifest(self):
+        # Create a manifest file for SLO uploading
+        object_name = data_utils.rand_name(name='TestObject')
+        object_name_base_1 = object_name + '_01'
+        object_name_base_2 = object_name + '_02'
+        data_size = MIN_SEGMENT_SIZE
+        self.data = data_utils.arbitrary_string(data_size)
+        self._create_object(self.container_name,
+                            object_name_base_1,
+                            self.data)
+        self._create_object(self.container_name,
+                            object_name_base_2,
+                            self.data)
+
+        path_object_1 = '/%s/%s' % (self.container_name,
+                                    object_name_base_1)
+        path_object_2 = '/%s/%s' % (self.container_name,
+                                    object_name_base_2)
+        data_manifest = [{'path': path_object_1,
+                          'etag': hashlib.md5(self.data).hexdigest(),
+                          'size_bytes': data_size},
+                         {'path': path_object_2,
+                          'etag': hashlib.md5(self.data).hexdigest(),
+                          'size_bytes': data_size}]
+
+        return json.dumps(data_manifest)
+
+    def _create_large_object(self):
+        # Create a large object for preparation of testing various SLO
+        # features
+        manifest = self._create_manifest()
+
+        params = {'multipart-manifest': 'put'}
+        object_name = data_utils.rand_name(name='TestObject')
+        self._create_object(self.container_name,
+                            object_name,
+                            manifest,
+                            params)
+        return object_name
+
+    def _assertHeadersSLO(self, resp, method):
+        # Check the existence of common headers with custom matcher
+        self.assertThat(resp, custom_matchers.ExistsAllResponseHeaders(
+                        'Object', method))
+        # When sending GET or HEAD requests to SLO the response contains
+        # 'X-Static-Large-Object' header
+        if method in ('GET', 'HEAD'):
+            self.assertIn('x-static-large-object', resp)
+
+        # Check common headers for all HTTP methods
+        self.assertTrue(re.match("^tx[0-9a-f]*-[0-9a-f]*$",
+                                 resp['x-trans-id']))
+        self.assertTrue(resp['content-length'].isdigit())
+        self.assertNotEqual(len(resp['date']), 0)
+        # Etag value of a large object is enclosed in double-quotations.
+        self.assertTrue(resp['etag'].startswith('\"'))
+        self.assertTrue(resp['etag'].endswith('\"'))
+        self.assertTrue(resp['etag'].strip('\"').isalnum())
+        # Check header formats for a specific method
+        if method in ('GET', 'HEAD'):
+            self.assertTrue(re.match("^\d+\.?\d*\Z", resp['x-timestamp']))
+            self.assertNotEqual(len(resp['content-type']), 0)
+            self.assertEqual(resp['accept-ranges'], 'bytes')
+            self.assertEqual(resp['x-static-large-object'], 'True')
+
+    @test.attr(type='gate')
+    def test_upload_manifest(self):
+        # create static large object from multipart manifest
+        manifest = self._create_manifest()
+
+        params = {'multipart-manifest': 'put'}
+        object_name = data_utils.rand_name(name='TestObject')
+        resp = self._create_object(self.container_name,
+                                   object_name,
+                                   manifest,
+                                   params)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+        self._assertHeadersSLO(resp, 'PUT')
+
+    @test.attr(type='gate')
+    def test_list_large_object_metadata(self):
+        # list static large object metadata using multipart manifest
+        object_name = self._create_large_object()
+
+        resp, body = self.object_client.list_object_metadata(
+            self.container_name,
+            object_name)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+        self._assertHeadersSLO(resp, 'HEAD')
+
+    @test.attr(type='gate')
+    def test_retrieve_large_object(self):
+        # list static large object using multipart manifest
+        object_name = self._create_large_object()
+
+        resp, body = self.object_client.get_object(
+            self.container_name,
+            object_name)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+        self._assertHeadersSLO(resp, 'GET')
+
+        sum_data = self.data + self.data
+        self.assertEqual(body, sum_data)
+
+    @test.attr(type='gate')
+    def test_delete_large_object(self):
+        # delete static large object using multipart manifest
+        object_name = self._create_large_object()
+
+        params_del = {'multipart-manifest': 'delete'}
+        resp, body = self.object_client.delete_object(
+            self.container_name,
+            object_name,
+            params=params_del)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+
+        # When deleting SLO using multipart manifest, the response contains
+        # not 'content-length' but 'transfer-encoding' header. This is the
+        # special case, therefore the existence of response headers is checked
+        # outside of custom matcher.
+        self.assertIn('transfer-encoding', resp)
+        self.assertIn('content-type', resp)
+        self.assertIn('x-trans-id', resp)
+        self.assertIn('date', resp)
+
+        # Check only the format of common headers with custom matcher
+        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+
+        resp, body = self.container_client.list_container_contents(
+            self.container_name)
+        self.assertEqual(int(resp['x-container-object-count']), 0)
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
new file mode 100644
index 0000000..e7d8c02
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -0,0 +1,34 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.volume import base
+from tempest.test import attr
+
+
+class VolumeHostsAdminTestsJSON(base.BaseVolumeAdminTest):
+    _interface = "json"
+
+    @attr(type='gate')
+    def test_list_hosts(self):
+        resp, hosts = self.hosts_client.list_hosts()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(hosts) >= 2, "No. of hosts are < 2,"
+                        "response of list hosts is: % s" % hosts)
+
+
+class VolumeHostsAdminTestsXML(VolumeHostsAdminTestsJSON):
+    _interface = 'xml'
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 4063eef..cb9ff11 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -47,7 +47,7 @@
         super(VolumesActionsTest, cls).tearDownClass()
 
     def _reset_volume_status(self, volume_id, status):
-        #Reset the volume status
+        # Reset the volume status
         resp, body = self.admin_volume_client.reset_volume_status(volume_id,
                                                                   status)
         return resp, body
@@ -57,6 +57,26 @@
         self._reset_volume_status(self.volume['id'], 'available')
         super(VolumesActionsTest, self).tearDown()
 
+    def _create_temp_volume(self):
+        # Create a temp volume for force delete tests
+        vol_name = utils.rand_name('Volume')
+        resp, temp_volume = self.client.create_volume(size=1,
+                                                      display_name=vol_name)
+        self.client.wait_for_volume_status(temp_volume['id'], 'available')
+
+        return temp_volume
+
+    def _create_reset_and_force_delete_temp_volume(self, status=None):
+        # Create volume, reset volume status, and force delete temp volume
+        temp_volume = self._create_temp_volume()
+        if status:
+            resp, body = self._reset_volume_status(temp_volume['id'], status)
+            self.assertEqual(202, resp.status)
+        resp_delete, volume_delete = self.admin_volume_client.\
+            force_delete_volume(temp_volume['id'])
+        self.assertEqual(202, resp_delete.status)
+        self.client.wait_for_resource_deletion(temp_volume['id'])
+
     @attr(type='gate')
     def test_volume_reset_status(self):
         # test volume reset status : available->error->available
@@ -84,6 +104,19 @@
         resp_get, volume_get = self.client.get_volume(self.volume['id'])
         self.assertEqual('in-use', volume_get['status'])
 
+    def test_volume_force_delete_when_volume_is_creating(self):
+        # test force delete when status of volume is creating
+        self._create_reset_and_force_delete_temp_volume('creating')
+
+    def test_volume_force_delete_when_volume_is_attaching(self):
+        # test force delete when status of volume is attaching
+        self._create_reset_and_force_delete_temp_volume('attaching')
+
+    @attr(type='gate')
+    def test_volume_force_delete_when_volume_is_error(self):
+        # test force delete when status of volume is error
+        self._create_reset_and_force_delete_temp_volume('error')
+
 
 class VolumesActionsTestXML(VolumesActionsTest):
     _interface = "xml"
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index cdf8638..465f570 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -151,3 +151,4 @@
         else:
             cls.os_adm = clients.AdminManager(interface=cls._interface)
         cls.client = cls.os_adm.volume_types_client
+        cls.hosts_client = cls.os_adm.volume_hosts_client
diff --git a/tempest/clients.py b/tempest/clients.py
index 291b946..ac79ce0 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -62,6 +62,8 @@
     ServersV3ClientJSON
 from tempest.services.compute.v3.json.services_client import \
     ServicesV3ClientJSON
+from tempest.services.compute.v3.json.tenant_usages_client import \
+    TenantUsagesV3ClientJSON
 from tempest.services.compute.v3.xml.availability_zone_client import \
     AvailabilityZoneV3ClientXML
 from tempest.services.compute.v3.xml.extensions_client import \
@@ -73,6 +75,8 @@
 from tempest.services.compute.v3.xml.servers_client import ServersV3ClientXML
 from tempest.services.compute.v3.xml.services_client import \
     ServicesV3ClientXML
+from tempest.services.compute.v3.xml.tenant_usages_client import \
+    TenantUsagesV3ClientXML
 from tempest.services.compute.xml.aggregates_client import AggregatesClientXML
 from tempest.services.compute.xml.availability_zone_client import \
     AvailabilityZoneClientXML
@@ -136,10 +140,14 @@
     ObjectClientCustomizedHeader
 from tempest.services.orchestration.json.orchestration_client import \
     OrchestrationClient
+from tempest.services.volume.json.admin.volume_hosts_client import \
+    VolumeHostsClientJSON
 from tempest.services.volume.json.admin.volume_types_client import \
     VolumeTypesClientJSON
 from tempest.services.volume.json.snapshots_client import SnapshotsClientJSON
 from tempest.services.volume.json.volumes_client import VolumesClientJSON
+from tempest.services.volume.xml.admin.volume_hosts_client import \
+    VolumeHostsClientXML
 from tempest.services.volume.xml.admin.volume_types_client import \
     VolumeTypesClientXML
 from tempest.services.volume.xml.snapshots_client import SnapshotsClientXML
@@ -230,6 +238,8 @@
             self.service_client = ServiceClientXML(*client_args)
             self.aggregates_client = AggregatesClientXML(*client_args)
             self.services_client = ServicesClientXML(*client_args)
+            self.tenant_usages_v3_client = TenantUsagesV3ClientXML(
+                *client_args)
             self.tenant_usages_client = TenantUsagesClientXML(*client_args)
             self.policy_client = PolicyClientXML(*client_args)
             self.hypervisor_v3_client = HypervisorV3ClientXML(*client_args)
@@ -239,6 +249,7 @@
             self.credentials_client = CredentialsClientXML(*client_args)
             self.instance_usages_audit_log_client = \
                 InstanceUsagesAuditLogClientXML(*client_args)
+            self.volume_hosts_client = VolumeHostsClientXML(*client_args)
 
             if client_args_v3_auth:
                 self.servers_client_v3_auth = ServersClientXML(
@@ -278,6 +289,8 @@
             self.service_client = ServiceClientJSON(*client_args)
             self.aggregates_client = AggregatesClientJSON(*client_args)
             self.services_client = ServicesClientJSON(*client_args)
+            self.tenant_usages_v3_client = TenantUsagesV3ClientJSON(
+                *client_args)
             self.tenant_usages_client = TenantUsagesClientJSON(*client_args)
             self.policy_client = PolicyClientJSON(*client_args)
             self.hypervisor_v3_client = HypervisorV3ClientJSON(*client_args)
@@ -287,6 +300,7 @@
             self.credentials_client = CredentialsClientJSON(*client_args)
             self.instance_usages_audit_log_client = \
                 InstanceUsagesAuditLogClientJSON(*client_args)
+            self.volume_hosts_client = VolumeHostsClientJSON(*client_args)
 
             if client_args_v3_auth:
                 self.servers_client_v3_auth = ServersClientJSON(
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 742a354..c397b7c 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -62,7 +62,7 @@
                             password=self.password,
                             look_for_keys=self.look_for_keys,
                             key_filename=self.key_filename,
-                            timeout=self.timeout, pkey=self.pkey)
+                            timeout=self.channel_timeout, pkey=self.pkey)
                 _timeout = False
                 break
             except (socket.error,
diff --git a/tempest/config.py b/tempest/config.py
index d97c7b2..220fd04 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -103,14 +103,6 @@
                      "users. This option enables isolated test cases and "
                      "better parallel execution, but also requires that "
                      "OpenStack Identity API admin credentials are known."),
-    cfg.BoolOpt('allow_tenant_reuse',
-                default=True,
-                help="If allow_tenant_isolation is True and a tenant that "
-                     "would be created for a given test already exists (such "
-                     "as from a previously-failed run), re-use that tenant "
-                     "instead of failing because of the conflict. Note that "
-                     "this would result in the tenant being deleted at the "
-                     "end of a subsequent successful run."),
     cfg.StrOpt('image_ref',
                default="{$IMAGE_ID}",
                help="Valid secondary image reference to be used in tests."),
diff --git a/tempest/services/compute/v3/json/tenant_usages_client.py b/tempest/services/compute/v3/json/tenant_usages_client.py
index 4dd6964..298f363 100644
--- a/tempest/services/compute/v3/json/tenant_usages_client.py
+++ b/tempest/services/compute/v3/json/tenant_usages_client.py
@@ -21,12 +21,12 @@
 from tempest.common.rest_client import RestClient
 
 
-class TenantUsagesClientJSON(RestClient):
+class TenantUsagesV3ClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(TenantUsagesClientJSON, self).__init__(
+        super(TenantUsagesV3ClientJSON, self).__init__(
             config, username, password, auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        self.service = self.config.compute.catalog_v3_type
 
     def list_tenant_usages(self, params=None):
         url = 'os-simple-tenant-usage'
diff --git a/tempest/services/compute/v3/xml/tenant_usages_client.py b/tempest/services/compute/v3/xml/tenant_usages_client.py
index cb92324..790bd5c 100644
--- a/tempest/services/compute/v3/xml/tenant_usages_client.py
+++ b/tempest/services/compute/v3/xml/tenant_usages_client.py
@@ -23,13 +23,13 @@
 from tempest.services.compute.xml.common import xml_to_json
 
 
-class TenantUsagesClientXML(RestClientXML):
+class TenantUsagesV3ClientXML(RestClientXML):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(TenantUsagesClientXML, self).__init__(config, username,
-                                                    password, auth_url,
-                                                    tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(TenantUsagesV3ClientXML, self).__init__(config, username,
+                                                      password, auth_url,
+                                                      tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def _parse_array(self, node):
         json = xml_to_json(node)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index aa7f2f2..aab2b9b 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -697,3 +697,21 @@
         resp, body = self.get(uri, self.headers)
         body = json.loads(body)
         return resp, body
+
+    def list_dhcp_agent_hosting_network(self, network_id):
+        uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
+        uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def remove_network_from_dhcp_agent(self, agent_id, network_id):
+        uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
+                                                 network_id)
+        resp, body = self.delete(uri, self.headers)
+        return resp, body
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index 5af2dfb..e11d4c1 100755
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -580,6 +580,26 @@
         body = {'service_providers': providers}
         return resp, body
 
+    def list_dhcp_agent_hosting_network(self, network_id):
+        uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
+        resp, body = self.get(uri, self.headers)
+        agents = self._parse_array(etree.fromstring(body))
+        body = {'agents': agents}
+        return resp, body
+
+    def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
+        uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri, self.headers)
+        networks = self._parse_array(etree.fromstring(body))
+        body = {'networks': networks}
+        return resp, body
+
+    def remove_network_from_dhcp_agent(self, agent_id, network_id):
+        uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
+                                                 network_id)
+        resp, body = self.delete(uri, self.headers)
+        return resp, body
+
 
 def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
     body = ET.fromstring(xml_returned_body)
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 2fee042..9e0adff 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -15,6 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import urllib
+
 from tempest.common import http
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
@@ -27,13 +29,16 @@
 
         self.service = self.config.object_storage.catalog_type
 
-    def create_object(self, container, object_name, data):
+    def create_object(self, container, object_name, data, params=None):
         """Create storage object."""
 
         headers = dict(self.headers)
         if not data:
             headers['content-length'] = '0'
         url = "%s/%s" % (str(container), str(object_name))
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
         resp, body = self.put(url, data, headers)
         return resp, body
 
@@ -41,9 +46,11 @@
         """Upload data to replace current storage object."""
         return self.create_object(container, object_name, data)
 
-    def delete_object(self, container, object_name):
+    def delete_object(self, container, object_name, params=None):
         """Delete storage object."""
         url = "%s/%s" % (str(container), str(object_name))
+        if params:
+            url += '?%s' % urllib.urlencode(params)
         resp, body = self.delete(url)
         return resp, body
 
diff --git a/tempest/services/volume/json/admin/volume_hosts_client.py b/tempest/services/volume/json/admin/volume_hosts_client.py
new file mode 100644
index 0000000..fc28ada
--- /dev/null
+++ b/tempest/services/volume/json/admin/volume_hosts_client.py
@@ -0,0 +1,46 @@
+# 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 json
+import urllib
+
+from tempest.common.rest_client import RestClient
+
+
+class VolumeHostsClientJSON(RestClient):
+    """
+    Client class to send CRUD Volume Hosts API requests to a Cinder endpoint
+    """
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(VolumeHostsClientJSON, self).__init__(config, username, password,
+                                                    auth_url, tenant_name)
+
+        self.service = self.config.volume.catalog_type
+        self.build_interval = self.config.volume.build_interval
+        self.build_timeout = self.config.volume.build_timeout
+
+    def list_hosts(self, params=None):
+        """Lists all hosts."""
+
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['hosts']
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index b4a1a68..967dc09 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -256,3 +256,10 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body, self.headers)
         return resp, body
+
+    def force_delete_volume(self, volume_id):
+        """Force Delete Volume."""
+        post_body = json.dumps({'os-force_delete': {}})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body,
+                               self.headers)
+        return resp, body
diff --git a/tempest/services/volume/xml/admin/volume_hosts_client.py b/tempest/services/volume/xml/admin/volume_hosts_client.py
new file mode 100644
index 0000000..59ce933
--- /dev/null
+++ b/tempest/services/volume/xml/admin/volume_hosts_client.py
@@ -0,0 +1,72 @@
+# 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 urllib
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class VolumeHostsClientXML(RestClientXML):
+    """
+    Client class to send CRUD Volume Hosts API requests to a Cinder endpoint
+    """
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(VolumeHostsClientXML, self).__init__(config, username, password,
+                                                   auth_url, tenant_name)
+        self.service = self.config.volume.catalog_type
+        self.build_interval = self.config.compute.build_interval
+        self.build_timeout = self.config.compute.build_timeout
+
+    def _parse_array(self, node):
+        """
+        This method is to parse the "list" response body
+        Eg:
+
+        <?xml version='1.0' encoding='UTF-8'?>
+        <hosts>
+        <host service-status="available" service="cinder-scheduler"/>
+        <host service-status="available" service="cinder-volume"/>
+        </hosts>
+
+        This method will append the details of specified tag,
+        here it is "host"
+        Return value would be list of hosts as below
+
+        [{'service-status': 'available', 'service': 'cinder-scheduler'},
+         {'service-status': 'available', 'service': 'cinder-volume'}]
+        """
+        array = []
+        for child in node.getchildren():
+            tag_list = child.tag.split('}', 1)
+            if tag_list[0] == "host":
+                array.append(xml_to_json(child))
+        return array
+
+    def list_hosts(self, params=None):
+        """List all the hosts."""
+        url = 'os-hosts'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 21254aa..1fc63e9 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -347,3 +347,12 @@
         if body:
             body = xml_to_json(etree.fromstring(body))
         return resp, body
+
+    def force_delete_volume(self, volume_id):
+        """Force Delete Volume."""
+        post_body = Element("os-force_delete")
+        url = 'volumes/%s/action' % str(volume_id)
+        resp, body = self.post(url, str(Document(post_body)), self.headers)
+        if body:
+            body = xml_to_json(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/tests/stress/__init__.py b/tempest/tests/stress/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/stress/__init__.py
diff --git a/tempest/tests/stress/test_stress.py b/tempest/tests/stress/test_stress.py
new file mode 100644
index 0000000..4d7de9d
--- /dev/null
+++ b/tempest/tests/stress/test_stress.py
@@ -0,0 +1,57 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Deutsche Telekom AG
+# 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 shlex
+import subprocess
+
+import tempest.cli as cli
+from tempest.openstack.common import log as logging
+import tempest.test
+
+LOG = logging.getLogger(__name__)
+
+
+class StressFrameworkTest(tempest.test.BaseTestCase):
+    """Basic test for the stress test framework.
+    """
+
+    def _cmd(self, cmd, param):
+        """Executes specified command."""
+        cmd = ' '.join([cmd, param])
+        LOG.info("running: '%s'" % cmd)
+        cmd_str = cmd
+        cmd = shlex.split(cmd)
+        result = ''
+        result_err = ''
+        try:
+            stdout = subprocess.PIPE
+            stderr = subprocess.PIPE
+            proc = subprocess.Popen(
+                cmd, stdout=stdout, stderr=stderr)
+            result, result_err = proc.communicate()
+            if proc.returncode != 0:
+                LOG.debug('error of %s:\n%s' % (cmd_str, result_err))
+                raise cli.CommandFailed(proc.returncode,
+                                        cmd,
+                                        result)
+        finally:
+            LOG.debug('output of %s:\n%s' % (cmd_str, result))
+        return proc.returncode
+
+    def test_help_function(self):
+        result = self._cmd("python", "-m tempest.stress.run_stress -h")
+        self.assertEqual(0, result)
diff --git a/tempest/tests/stress/test_stressaction.py b/tempest/tests/stress/test_stressaction.py
new file mode 100644
index 0000000..3d2901e
--- /dev/null
+++ b/tempest/tests/stress/test_stressaction.py
@@ -0,0 +1,65 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Deutsche Telekom AG
+# 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 tempest.stress.stressaction as stressaction
+import tempest.test
+
+
+class FakeStressAction(stressaction.StressAction):
+    def __init__(self, manager, max_runs=None, stop_on_error=False):
+        super(self.__class__, self).__init__(manager, max_runs, stop_on_error)
+        self._run_called = False
+
+    def run(self):
+        self._run_called = True
+
+    @property
+    def run_called(self):
+        return self._run_called
+
+
+class FakeStressActionFailing(stressaction.StressAction):
+    def run(self):
+        raise Exception('FakeStressActionFailing raise exception')
+
+
+class TestStressAction(tempest.test.BaseTestCase):
+    def _bulid_stats_dict(self, runs=0, fails=0):
+        return {'runs': runs, 'fails': fails}
+
+    def testStressTestRun(self):
+        stressAction = FakeStressAction(manager=None, max_runs=1)
+        stats = self._bulid_stats_dict()
+        stressAction.execute(stats)
+        self.assertTrue(stressAction.run_called)
+        self.assertEqual(stats['runs'], 1)
+        self.assertEqual(stats['fails'], 0)
+
+    def testStressMaxTestRuns(self):
+        stressAction = FakeStressAction(manager=None, max_runs=500)
+        stats = self._bulid_stats_dict(runs=499)
+        stressAction.execute(stats)
+        self.assertTrue(stressAction.run_called)
+        self.assertEqual(stats['runs'], 500)
+        self.assertEqual(stats['fails'], 0)
+
+    def testStressTestRunWithException(self):
+        stressAction = FakeStressActionFailing(manager=None, max_runs=1)
+        stats = self._bulid_stats_dict()
+        stressAction.execute(stats)
+        self.assertEqual(stats['runs'], 1)
+        self.assertEqual(stats['fails'], 1)
diff --git a/test-requirements.txt b/test-requirements.txt
index fbe7e43..41a784e 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,4 +1,4 @@
-hacking>=0.5.6,<0.8
+hacking>=0.8.0,<0.9
 # needed for doc build
 docutils==0.9.1
 sphinx>=1.1.2
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index 1b5fe68..347659d 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -36,11 +36,11 @@
     __, versions = os.image_client.get_versions()
     if CONF.image_feature_enabled.api_v1 != ('v1.1' in versions or 'v1.0' in
                                              versions):
-        print 'Config option image api_v1 should be change to: %s' % (
-            not CONF.image_feature_enabled.api_v1)
+        print('Config option image api_v1 should be change to: %s' % (
+            not CONF.image_feature_enabled.api_v1))
     if CONF.image_feature_enabled.api_v2 != ('v2.0' in versions):
-        print 'Config option image api_v2 should be change to: %s' % (
-            not CONF.image_feature_enabled.api_v2)
+        print('Config option image api_v2 should be change to: %s' % (
+            not CONF.image_feature_enabled.api_v2))
 
 
 def verify_extensions(os):
@@ -62,8 +62,8 @@
     for option in NOVA_EXTENSIONS.keys():
         config_value = getattr(CONF.compute_feature_enabled, option)
         if config_value != results['nova_features'][option]:
-            print "Config option: %s should be changed to: %s" % (
-                option, not config_value)
+            print("Config option: %s should be changed to: %s" % (
+                option, not config_value))
 
 
 def main(argv):