Merge "Remove claim that scenario tests need 2 services"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 2ecace0..ec14c7e 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -293,6 +293,8 @@
 container_sync_interval = 5
 # Set to True if the Account Quota middleware is enabled
 accounts_quotas_available = True
+# Set to True if the Container Quota middleware is enabled
+container_quotas_available = True
 
 # Set operator role for tests that require creating a container
 operator_role = Member
@@ -430,3 +432,7 @@
 log_check_interval = 60
 # The default number of threads created while stress test
 default_thread_number_per_action=4
+
+[debug]
+# Enable diagnostic commands
+enable = True
diff --git a/tempest/api/README.rst b/tempest/api/README.rst
index 9d8dc10..9eac19d 100644
--- a/tempest/api/README.rst
+++ b/tempest/api/README.rst
@@ -1,5 +1,5 @@
-Tempest Guide to API tests
-==========================
+Tempest Field Guide to API tests
+================================
 
 
 What are these tests?
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index af76ad0..19d7973 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -15,6 +15,7 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
 
@@ -71,6 +72,30 @@
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.list_hosts)
 
+    @attr(type='gate')
+    def test_show_host_detail(self):
+        resp, hosts = self.client.list_hosts()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(hosts) >= 1)
+        hostname = hosts[0]['host_name']
+
+        resp, resources = self.client.show_host_detail(hostname)
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(resources) >= 1)
+        host_resource = resources[0]['resource']
+        self.assertIsNotNone(host_resource)
+        self.assertIsNotNone(host_resource['cpu'])
+        self.assertIsNotNone(host_resource['disk_gb'])
+        self.assertIsNotNone(host_resource['memory_mb'])
+        self.assertIsNotNone(host_resource['project'])
+        self.assertEqual(hostname, host_resource['host'])
+
+    @attr(type='negative')
+    def test_show_host_detail_with_nonexist_hostname(self):
+        hostname = rand_name('rand_hostname')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.show_host_detail, hostname)
+
 
 class HostsAdminTestXML(HostsAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 156274d..69b7872 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -19,7 +19,6 @@
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
-import testtools
 
 
 class QuotasAdminTestJSON(base.BaseComputeAdminTest):
@@ -73,12 +72,12 @@
                          sorted(quota_set.keys()))
         self.assertEqual(quota_set['id'], self.demo_tenant_id)
 
-    @testtools.skip("Skipped until the Bug #1160749 is resolved")
     @attr(type='gate')
     def test_update_all_quota_resources_for_tenant(self):
         # Admin can update all the resource quota limits for a tenant
-        new_quota_set = {'force': True,
-                         'injected_file_content_bytes': 20480,
+        resp, default_quota_set = self.client.get_default_quota_set(
+            self.demo_tenant_id)
+        new_quota_set = {'injected_file_content_bytes': 20480,
                          'metadata_items': 256, 'injected_files': 10,
                          'ram': 10240, 'floating_ips': 20, 'fixed_ips': 10,
                          'key_pairs': 200, 'injected_file_path_bytes': 512,
@@ -87,9 +86,12 @@
         # Update limits for all quota resources
         resp, quota_set = self.adm_client.update_quota_set(
             self.demo_tenant_id,
+            force=True,
             **new_quota_set)
+
+        default_quota_set.pop('id')
         self.addCleanup(self.adm_client.update_quota_set,
-                        self.demo_tenant_id, **self.default_quota_set)
+                        self.demo_tenant_id, **default_quota_set)
         self.assertEqual(200, resp.status)
         self.assertEqual(new_quota_set, quota_set)
 
@@ -114,7 +116,6 @@
 
     # TODO(afazekas): Add dedicated tenant to the skiped quota tests
     # it can be moved into the setUpClass as well
-    @testtools.skip("Skipped until the Bug #1160749 is resolved")
     @attr(type='gate')
     def test_create_server_when_cpu_quota_is_full(self):
         # Disallow server creation when tenant's vcpu quota is full
@@ -130,7 +131,6 @@
                         cores=default_vcpu_quota)
         self.assertRaises(exceptions.OverLimit, self.create_server)
 
-    @testtools.skip("Skipped until the Bug #1160749 is resolved")
     @attr(type='gate')
     def test_create_server_when_memory_quota_is_full(self):
         # Disallow server creation when tenant's memory quota is full
@@ -146,9 +146,13 @@
                         ram=default_mem_quota)
         self.assertRaises(exceptions.OverLimit, self.create_server)
 
-# TODO(afazekas): Add test that tried to update the quota_set as a regular user
+    @attr(type='gate')
+    def test_update_quota_normal_user(self):
+        self.assertRaises(exceptions.Unauthorized,
+                          self.client.update_quota_set,
+                          self.demo_tenant_id,
+                          ram=0)
 
-    @testtools.skip("Skipped until the Bug #1160749 is resolved")
     @attr(type=['negative', 'gate'])
     def test_create_server_when_instances_quota_is_full(self):
         # Once instances quota limit is reached, disallow server creation
@@ -163,7 +167,6 @@
                         instances=default_instances_quota)
         self.assertRaises(exceptions.OverLimit, self.create_server)
 
-    @testtools.skip("Skipped until the Bug #1160749 is resolved")
     @attr(type=['negative', 'gate'])
     def test_security_groups_exceed_limit(self):
         # Negative test: Creation Security Groups over limit should FAIL
@@ -174,6 +177,7 @@
 
         resp, quota_set =\
             self.adm_client.update_quota_set(self.demo_tenant_id,
+                                             force=True,
                                              security_groups=sg_quota)
 
         self.addCleanup(self.adm_client.update_quota_set,
@@ -185,7 +189,6 @@
                           self.sg_client.create_security_group,
                           "sg-overlimit", "sg-desc")
 
-    @testtools.skip("Skipped until the Bug #1160749 is resolved")
     @attr(type=['negative', 'gate'])
     def test_security_groups_rules_exceed_limit(self):
         # Negative test: Creation of Security Group Rules should FAIL
@@ -198,6 +201,7 @@
         resp, quota_set =\
             self.adm_client.update_quota_set(
                 self.demo_tenant_id,
+                force=True,
                 security_group_rules=sg_rules_quota)
 
         self.addCleanup(self.adm_client.update_quota_set,
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index a769744..9a7feac 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -42,7 +42,6 @@
         resp, _ = cls.client.create_image(cls.server_id, name, {})
         cls.image_id = resp['location'].rsplit('/', 1)[1]
 
-        cls.client.wait_for_image_resp_code(cls.image_id, 200)
         cls.client.wait_for_image_status(cls.image_id, 'ACTIVE')
 
     @classmethod
@@ -89,7 +88,7 @@
         # The value for a specific metadata key should be returned
         resp, meta = self.client.get_image_metadata_item(self.image_id,
                                                          'key2')
-        self.assertTrue('value2', meta['key2'])
+        self.assertEqual('value2', meta['key2'])
 
     @attr(type='gate')
     def test_set_image_metadata_item(self):
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 2f0ed6b..820aeaf 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -59,7 +59,6 @@
     def __create_image__(self, server_id, name, meta=None):
         resp, body = self.client.create_image(server_id, name, meta)
         image_id = parse_image_id(resp['location'])
-        self.client.wait_for_image_resp_code(image_id, 200)
         self.client.wait_for_image_status(image_id, 'ACTIVE')
         self.image_ids.append(image_id)
         return resp, body
@@ -166,7 +165,7 @@
     def test_delete_image_id_is_over_35_character_limit(self):
         # Return an error while trying to delete image with id over limit
         self.assertRaises(exceptions.NotFound, self.client.delete_image,
-                          '11a22b9-120q-5555-cc11-00ab112223gj-3fac')
+                          '11a22b9-12a9-5555-cc11-00ab112223fa-3fac')
 
 
 class ImagesTestXML(ImagesTestJSON):
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 06e9ab2..ede54c8 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -104,7 +104,6 @@
         resp, body = self.client.create_image(self.server['id'], name, meta)
         self.assertEqual(202, resp.status)
         image_id = parse_image_id(resp['location'])
-        self.client.wait_for_image_resp_code(image_id, 200)
         self.client.wait_for_image_status(image_id, 'ACTIVE')
 
         # Verify the image was created correctly
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index e700278..dff8cc4 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -47,7 +47,6 @@
             # Create images to be used in the filter tests
             resp, body = cls.create_image_from_server(cls.server1['id'])
             cls.image1_id = parse_image_id(resp['location'])
-            cls.client.wait_for_image_resp_code(cls.image1_id, 200)
             cls.client.wait_for_image_status(cls.image1_id, 'ACTIVE')
             resp, cls.image1 = cls.client.get_image(cls.image1_id)
 
@@ -56,13 +55,11 @@
             # server will sometimes cause failures
             resp, body = cls.create_image_from_server(cls.server2['id'])
             cls.image3_id = parse_image_id(resp['location'])
-            cls.client.wait_for_image_resp_code(cls.image3_id, 200)
             cls.client.wait_for_image_status(cls.image3_id, 'ACTIVE')
             resp, cls.image3 = cls.client.get_image(cls.image3_id)
 
             resp, body = cls.create_image_from_server(cls.server1['id'])
             cls.image2_id = parse_image_id(resp['location'])
-            cls.client.wait_for_image_resp_code(cls.image2_id, 200)
 
             cls.client.wait_for_image_status(cls.image2_id, 'ACTIVE')
             resp, cls.image2 = cls.client.get_image(cls.image2_id)
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index edfafec..7e4a70b 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -87,7 +87,7 @@
                                                    min_count=1,
                                                    max_count=2,
                                                    return_reservation_id=True)
-        self.assertTrue(resp['status'], 202)
+        self.assertEqual(resp['status'], '202')
         self.assertIn('reservation_id', body)
 
 
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index 9997b97..d396129 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -118,7 +118,7 @@
         # The value for a specific metadata key should be returned
         resp, meta = self.client.get_server_metadata_item(self.server_id,
                                                           'key2')
-        self.assertTrue('value2', meta['key2'])
+        self.assertEqual('value2', meta['key2'])
 
     @attr(type='gate')
     def test_set_server_metadata_item(self):
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index efdadb0..5997f9f 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -65,7 +65,6 @@
         name = rand_name('image')
         resp, body = cls.client.create_image(server['id'], name)
         image_id = parse_image_id(resp['location'])
-        cls.images_client.wait_for_image_resp_code(image_id, 200)
         cls.images_client.wait_for_image_status(image_id, 'ACTIVE')
         resp, cls.image = cls.images_client.get_image(image_id)
 
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index 057e633..dc17e90 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -102,11 +102,39 @@
         self.client.clear_auth()
 
     @attr(type='smoke')
+    def test_update_user(self):
+        # Test case to check if updating of user attributes is successful.
+        test_user = rand_name('test_user_')
+        self.data.setup_test_tenant()
+        resp, user = self.client.create_user(test_user, self.alt_password,
+                                             self.data.tenant['id'],
+                                             self.alt_email)
+        # Delete the User at the end of this method
+        self.addCleanup(self.client.delete_user, user['id'])
+        # Updating user details with new values
+        u_name2 = rand_name('user2-')
+        u_email2 = u_name2 + '@testmail.tm'
+        resp, update_user = self.client.update_user(user['id'], name=u_name2,
+                                                    email=u_email2,
+                                                    enabled=False)
+        # Assert response body of update user.
+        self.assertEqual(200, resp.status)
+        self.assertEqual(u_name2, update_user['name'])
+        self.assertEqual(u_email2, update_user['email'])
+        self.assertEqual('false', str(update_user['enabled']).lower())
+        # GET by id after updating
+        resp, updated_user = self.client.get_user(user['id'])
+        # Assert response body of GET after updating
+        self.assertEqual(u_name2, updated_user['name'])
+        self.assertEqual(u_email2, updated_user['email'])
+        self.assertEqual('false', str(updated_user['enabled']).lower())
+
+    @attr(type='smoke')
     def test_delete_user(self):
         # Delete a user
-        alt_user2 = rand_name('alt_user_')
+        test_user = rand_name('test_user_')
         self.data.setup_test_tenant()
-        resp, user = self.client.create_user(alt_user2, self.alt_password,
+        resp, user = self.client.create_user(test_user, self.alt_password,
                                              self.data.tenant['id'],
                                              self.alt_email)
         self.assertEqual('200', resp['status'])
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index 2a20493..4c995d3 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -19,11 +19,13 @@
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
+import testtools
 
 
 class UsersTestJSON(base.BaseIdentityAdminTest):
     _interface = 'json'
 
+    @testtools.skip("Skipped until the Bug #1221889 is resolved")
     @attr(type='smoke')
     def test_tokens(self):
         # Valid user's token is authenticated
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
new file mode 100644
index 0000000..31fe711
--- /dev/null
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -0,0 +1,122 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Cloudwatt
+# 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 testtools
+
+from tempest.api.object_storage import base
+from tempest.common.utils.data_utils import arbitrary_string
+from tempest.common.utils.data_utils import rand_name
+from tempest import config
+from tempest import exceptions
+from tempest.test import attr
+from tempest.test import HTTP_SUCCESS
+
+QUOTA_BYTES = 10
+QUOTA_COUNT = 3
+SKIP_MSG = "Container quotas middleware not available."
+
+
+class ContainerQuotasTest(base.BaseObjectTest):
+    """Attemps to test the perfect behavior of quotas in a container."""
+    container_quotas_available = \
+        config.TempestConfig().object_storage.container_quotas_available
+
+    def setUp(self):
+        """Creates and sets a container with quotas.
+
+        Quotas are set by adding meta values to the container,
+        and are validated when set:
+          - X-Container-Meta-Quota-Bytes:
+                     Maximum size of the container, in bytes.
+          - X-Container-Meta-Quota-Count:
+                     Maximum object count of the container.
+        """
+        super(ContainerQuotasTest, self).setUp()
+        self.container_name = rand_name(name="TestContainer")
+        self.container_client.create_container(self.container_name)
+        metadata = {"quota-bytes": str(QUOTA_BYTES),
+                    "quota-count": str(QUOTA_COUNT), }
+        self.container_client.update_container_metadata(
+            self.container_name, metadata)
+
+    def tearDown(self):
+        """Cleans the container of any object after each test."""
+        self.delete_containers([self.container_name])
+        super(ContainerQuotasTest, self).tearDown()
+
+    @testtools.skipIf(not container_quotas_available, SKIP_MSG)
+    @attr(type="smoke")
+    def test_upload_valid_object(self):
+        """Attempts to uploads an object smaller than the bytes quota."""
+        object_name = rand_name(name="TestObject")
+        data = arbitrary_string(QUOTA_BYTES)
+
+        nbefore = self._get_bytes_used()
+
+        resp, _ = self.object_client.create_object(
+            self.container_name, object_name, data)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+        nafter = self._get_bytes_used()
+        self.assertEqual(nbefore + len(data), nafter)
+
+    @testtools.skipIf(not container_quotas_available, SKIP_MSG)
+    @attr(type="smoke")
+    def test_upload_large_object(self):
+        """Attempts to upload an object lagger than the bytes quota."""
+        object_name = rand_name(name="TestObject")
+        data = arbitrary_string(QUOTA_BYTES + 1)
+
+        nbefore = self._get_bytes_used()
+
+        self.assertRaises(exceptions.OverLimit,
+                          self.object_client.create_object,
+                          self.container_name, object_name, data)
+
+        nafter = self._get_bytes_used()
+        self.assertEqual(nbefore, nafter)
+
+    @testtools.skipIf(not container_quotas_available, SKIP_MSG)
+    @attr(type="smoke")
+    def test_upload_too_many_objects(self):
+        """Attempts to upload many objects that exceeds the count limit."""
+        for _ in range(QUOTA_COUNT):
+            name = rand_name(name="TestObject")
+            self.object_client.create_object(self.container_name, name, "")
+
+        nbefore = self._get_object_count()
+        self.assertEqual(nbefore, QUOTA_COUNT)
+
+        self.assertRaises(exceptions.OverLimit,
+                          self.object_client.create_object,
+                          self.container_name, "OverQuotaObject", "")
+
+        nafter = self._get_object_count()
+        self.assertEqual(nbefore, nafter)
+
+    def _get_container_metadata(self):
+        resp, _ = self.container_client.list_container_metadata(
+            self.container_name)
+        return resp
+
+    def _get_object_count(self):
+        resp = self._get_container_metadata()
+        return int(resp["x-container-object-count"])
+
+    def _get_bytes_used(self):
+        resp = self._get_container_metadata()
+        return int(resp["x-container-bytes-used"])
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index b15f8dd..66b6969 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -52,7 +52,7 @@
         # List Volume types.
         resp, body = self.client.list_volume_types()
         self.assertEqual(200, resp.status)
-        self.assertTrue(type(body), list)
+        self.assertIsInstance(body, list)
 
     @attr(type='smoke')
     def test_create_get_delete_volume_with_volume_type_and_extra_specs(self):
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 7d94f58..12ecaac 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -46,7 +46,7 @@
         resp, body = self.client.list_volume_types_extra_specs(
             self.volume_type['id'])
         self.assertEqual(200, resp.status)
-        self.assertTrue(type(body), dict)
+        self.assertIsInstance(body, dict)
         self.assertTrue('spec1' in body, "Incorrect volume type extra"
                         " spec returned")
 
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 12b03b5..70fe1a3 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -34,7 +34,7 @@
         self.assertEqual(202, resp.status)
         self.client.wait_for_resource_deletion(volume_id)
 
-    def _volume_create_get_delete(self, **kwargs):
+    def _volume_create_get_update_delete(self, **kwargs):
         # Create a volume, Get it's details and Delete the volume
         volume = {}
         v_name = rand_name('Volume')
@@ -74,6 +74,29 @@
         if 'imageRef' not in kwargs:
             self.assertEqual(fetched_volume['bootable'], False)
 
+        # Update Volume
+        new_v_name = rand_name('new-Volume')
+        new_desc = 'This is the new description of volume'
+        resp, update_volume = \
+            self.client.update_volume(volume['id'],
+                                      display_name=new_v_name,
+                                      display_description=new_desc)
+        # Assert response body for update_volume method
+        self.assertEqual(200, resp.status)
+        self.assertEqual(new_v_name, update_volume['display_name'])
+        self.assertEqual(new_desc, update_volume['display_description'])
+        # Assert response body for get_volume method
+        resp, updated_volume = self.client.get_volume(volume['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(volume['id'], updated_volume['id'])
+        self.assertEqual(new_v_name, updated_volume['display_name'])
+        self.assertEqual(new_desc, updated_volume['display_description'])
+        self.assertEqual(metadata, updated_volume['metadata'])
+        if 'imageRef' in kwargs:
+            self.assertEqual(updated_volume['bootable'], True)
+        if 'imageRef' not in kwargs:
+            self.assertEqual(updated_volume['bootable'], False)
+
     @attr(type='gate')
     def test_volume_get_metadata_none(self):
         # Create a volume without passing metadata, get details, and delete
@@ -94,19 +117,20 @@
         self.assertEqual(fetched_volume['metadata'], {})
 
     @attr(type='smoke')
-    def test_volume_create_get_delete(self):
-        self._volume_create_get_delete()
+    def test_volume_create_get_update_delete(self):
+        self._volume_create_get_update_delete()
 
     @attr(type='smoke')
     @services('image')
-    def test_volume_create_get_delete_from_image(self):
-        self._volume_create_get_delete(imageRef=self.config.compute.image_ref)
+    def test_volume_create_get_update_delete_from_image(self):
+        self._volume_create_get_update_delete(imageRef=self.
+                                              config.compute.image_ref)
 
     @attr(type='gate')
-    def test_volume_create_get_delete_as_clone(self):
+    def test_volume_create_get_update_delete_as_clone(self):
         origin = self.create_volume(size=1,
                                     display_name="Volume Origin")
-        self._volume_create_get_delete(source_volid=origin['id'])
+        self._volume_create_get_update_delete(source_volid=origin['id'])
 
 
 class VolumesGetTestXML(VolumesGetTest):
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 0328b44..6b186e5 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -38,7 +38,7 @@
         super(VolumesSnapshotTest, cls).tearDownClass()
 
     @attr(type='gate')
-    def test_snapshot_create_get_list_delete(self):
+    def test_snapshot_create_get_list_update_delete(self):
         # Create a snapshot
         s_name = rand_name('snap')
         snapshot = self.create_snapshot(self.volume_origin['id'],
@@ -58,6 +58,24 @@
         snaps_data = [(f['id'], f['display_name']) for f in snaps_list]
         self.assertIn(tracking_data, snaps_data)
 
+        # Updates snapshot with new values
+        new_s_name = rand_name('new-snap')
+        new_desc = 'This is the new description of snapshot.'
+        resp, update_snapshot = \
+            self.snapshots_client.update_snapshot(snapshot['id'],
+                                                  display_name=new_s_name,
+                                                  display_description=new_desc)
+        # Assert response body for update_snapshot method
+        self.assertEqual(200, resp.status)
+        self.assertEqual(new_s_name, update_snapshot['display_name'])
+        self.assertEqual(new_desc, update_snapshot['display_description'])
+        # Assert response body for get_snapshot method
+        resp, updated_snapshot = \
+            self.snapshots_client.get_snapshot(snapshot['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(new_s_name, updated_snapshot['display_name'])
+        self.assertEqual(new_desc, updated_snapshot['display_description'])
+
         # Delete the snapshot
         self.snapshots_client.delete_snapshot(snapshot['id'])
         self.assertEqual(200, resp.status)
diff --git a/tempest/cli/README.rst b/tempest/cli/README.rst
index f86adf3..dcd940b 100644
--- a/tempest/cli/README.rst
+++ b/tempest/cli/README.rst
@@ -1,5 +1,5 @@
-Tempest Guide to CLI tests
-==========================
+Tempest Field Guide to CLI tests
+================================
 
 
 What are these tests?
diff --git a/tempest/common/commands.py b/tempest/common/commands.py
new file mode 100644
index 0000000..4fc85b6
--- /dev/null
+++ b/tempest/common/commands.py
@@ -0,0 +1,76 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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
+
+from tempest.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+# NOTE(afazekas):
+# These commands assumes the tempest node is the same as
+# the only one service node. all-in-one installation.
+
+
+def sudo_cmd_call(cmd):
+    args = shlex.split(cmd)
+    subprocess_args = {'stdout': subprocess.PIPE,
+                       'stderr': subprocess.STDOUT}
+    try:
+        proc = subprocess.Popen(['/usr/bin/sudo'] + args, **subprocess_args)
+        return proc.communicate()[0]
+        if proc.returncode != 0:
+            LOG.error(cmd + "returned with: " +
+                      proc.returncode + "exit status")
+    except subprocess.CalledProcessError as e:
+        LOG.error("command output:\n%s" % e.output)
+
+
+def ip_addr_raw():
+    return sudo_cmd_call("ip a")
+
+
+def ip_route_raw():
+    return sudo_cmd_call("ip r")
+
+
+def ip_ns_raw():
+    return sudo_cmd_call("ip netns list")
+
+
+def iptables_raw(table):
+    return sudo_cmd_call("iptables -v -S -t " + table)
+
+
+def ip_ns_list():
+    return ip_ns_raw().split()
+
+
+def ip_ns_exec(ns, cmd):
+    return sudo_cmd_call(" ".join(("ip netns exec", ns, cmd)))
+
+
+def ip_ns_addr(ns):
+    return ip_ns_exec(ns, "ip a")
+
+
+def ip_ns_route(ns):
+    return ip_ns_exec(ns, "ip r")
+
+
+def iptables_ns(ns, table):
+    return ip_ns_exec(ns, "iptables -v -S -t " + table)
diff --git a/tempest/common/debug.py b/tempest/common/debug.py
new file mode 100644
index 0000000..69c933c
--- /dev/null
+++ b/tempest/common/debug.py
@@ -0,0 +1,41 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.common import commands
+from tempest import config
+
+from tempest.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+tables = ['filter', 'nat', 'mangle']
+
+
+def log_ip_ns():
+    if not config.TempestConfig().debug.enable:
+        return
+    LOG.info("Host Addr:\n" + commands.ip_addr_raw())
+    LOG.info("Host Route:\n" + commands.ip_route_raw())
+    for table in ['filter', 'nat', 'mangle']:
+        LOG.info('Host %s table:\n%s', table, commands.iptables_raw(table))
+    ns_list = commands.ip_ns_list()
+    LOG.info("Host ns list" + str(ns_list))
+    for ns in ns_list:
+        LOG.info("ns(%s) Addr:\n%s", ns, commands.ip_ns_addr(ns))
+        LOG.info("ns(%s) Route:\n%s", ns, commands.ip_ns_route(ns))
+        for table in ['filter', 'nat', 'mangle']:
+            LOG.info('ns(%s) table(%s):\n%s', ns, table,
+                     commands.iptables_ns(ns, table))
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 81393a9..d0d5e84 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -32,6 +32,9 @@
 MAX_RECURSION_DEPTH = 2
 TOKEN_CHARS_RE = re.compile('^[-A-Za-z0-9+/=]*$')
 
+# All the successful HTTP status codes from RFC 2616
+HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206)
+
 
 class RestClient(object):
     TYPE = "json"
@@ -266,6 +269,20 @@
             raise exceptions.AuthenticationFailure(user=user,
                                                    password=password)
 
+    def expected_success(self, expected_code, read_code):
+        assert_msg = ("This function only allowed to use for HTTP status"
+                      "codes which explicitly defined in the RFC 2616. {0}"
+                      " is not a defined Success Code!").format(expected_code)
+        assert expected_code in HTTP_SUCCESS, assert_msg
+
+        # NOTE(afazekas): the http status code above 400 is processed by
+        # the _error_checker method
+        if read_code < 400 and read_code != expected_code:
+                pattern = """Unexpected http success status code {0},
+                             The expected status code is {1}"""
+                details = pattern.format(read_code, expected_code)
+                raise exceptions.InvalidHttpSuccessCode(details)
+
     def post(self, url, body, headers):
         return self.request('POST', url, headers, body)
 
diff --git a/tempest/config.py b/tempest/config.py
index 1b52f5e..1005fbb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -393,6 +393,10 @@
     cfg.BoolOpt('accounts_quotas_available',
                 default=True,
                 help="Set to True if the Account Quota middleware is enabled"),
+    cfg.BoolOpt('container_quotas_available',
+                default=True,
+                help="Set to True if the container quota middleware "
+                     "is enabled"),
     cfg.StrOpt('operator_role',
                default='Member',
                help="Role to add to users created for swift tests to "
@@ -628,6 +632,21 @@
     for opt in ServiceAvailableGroup:
         conf.register_opt(opt, group='service_available')
 
+debug_group = cfg.OptGroup(name="debug",
+                           title="Debug System")
+
+DebugGroup = [
+    cfg.BoolOpt('enable',
+                default=True,
+                help="Enable diagnostic commands"),
+]
+
+
+def register_debug_opts(conf):
+    conf.register_group(debug_group)
+    for opt in DebugGroup:
+        conf.register_opt(opt, group='debug')
+
 
 @singleton
 class TempestConfig:
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 924ebc9..8c97312 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -57,6 +57,10 @@
     pass
 
 
+class InvalidHttpSuccessCode(RestClientException):
+    message = "The success code is different than the expected one"
+
+
 class NotFound(RestClientException):
     message = "Object not found"
 
diff --git a/tempest/scenario/README.rst b/tempest/scenario/README.rst
index 6703dda..ce12a3b 100644
--- a/tempest/scenario/README.rst
+++ b/tempest/scenario/README.rst
@@ -1,5 +1,5 @@
-Tempest Guide to Scenario tests
-===============================
+Tempest Field Guide to Scenario tests
+=====================================
 
 
 What are these tests?
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 9d7086c..daa7d13 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -17,6 +17,7 @@
 #    under the License.
 
 from tempest.api.network import common as net_common
+from tempest.common import debug
 from tempest.common.utils.data_utils import rand_name
 from tempest import config
 from tempest.openstack.common import log as logging
@@ -202,21 +203,25 @@
             self.assertIn(myrouter.name, seen_router_names)
             self.assertIn(myrouter.id, seen_router_ids)
 
+    def _create_server(self, name, network):
+        tenant_id = network.tenant_id
+        keypair_name = self.keypairs[tenant_id].name
+        security_groups = [self.security_groups[tenant_id].name]
+        create_kwargs = {
+            'nics': [
+                {'net-id': network.id},
+            ],
+            'key_name': keypair_name,
+            'security_groups': security_groups,
+        }
+        server = self.create_server(self.compute_client, name=name,
+                                    create_kwargs=create_kwargs)
+        return server
+
     def _create_servers(self):
         for i, network in enumerate(self.networks):
-            tenant_id = network.tenant_id
             name = rand_name('server-smoke-%d-' % i)
-            keypair_name = self.keypairs[tenant_id].name
-            security_groups = [self.security_groups[tenant_id].name]
-            create_kwargs = {
-                'nics': [
-                    {'net-id': network.id},
-                ],
-                'key_name': keypair_name,
-                'security_groups': security_groups,
-            }
-            server = self.create_server(self.compute_client, name=name,
-                                        create_kwargs=create_kwargs)
+            server = self._create_server(name, network)
             self.servers.append(server)
 
     def _check_tenant_network_connectivity(self):
@@ -246,10 +251,17 @@
         # 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, 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)
+        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)
+        except Exception as exc:
+            LOG.exception(exc)
+            debug.log_ip_ns()
+            raise exc
 
     @attr(type='smoke')
     @services('compute', 'network')
@@ -259,6 +271,6 @@
         self._create_networks()
         self._check_networks()
         self._create_servers()
-        self._check_tenant_network_connectivity()
         self._assign_floating_ips()
         self._check_public_network_connectivity()
+        self._check_tenant_network_connectivity()
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 8093d19..30a3f7b 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -37,3 +37,10 @@
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['hosts']
+
+    def show_host_detail(self, hostname):
+        """Show detail information for the host."""
+
+        resp, body = self.get("os-hosts/%s" % str(hostname))
+        body = json.loads(body)
+        return resp, body['host']
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index b13d0f1..0850158 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -72,6 +72,7 @@
     def get_image(self, image_id):
         """Returns the details of a single image."""
         resp, body = self.get("images/%s" % str(image_id))
+        self.expected_success(200, resp)
         body = json.loads(body)
         return resp, body['image']
 
@@ -79,21 +80,6 @@
         """Deletes the provided image."""
         return self.delete("images/%s" % str(image_id))
 
-    def wait_for_image_resp_code(self, image_id, code):
-        """
-        Waits until the HTTP response code for the request matches the
-        expected value
-        """
-        resp, body = self.get("images/%s" % str(image_id))
-        start = int(time.time())
-
-        while resp.status != code:
-            time.sleep(self.build_interval)
-            resp, body = self.get("images/%s" % str(image_id))
-
-            if int(time.time()) - start >= self.build_timeout:
-                raise exceptions.TimeoutException
-
     def wait_for_image_status(self, image_id, status):
         """Waits for an image to reach a given status."""
         resp, image = self.get_image(image_id)
diff --git a/tempest/services/compute/xml/hosts_client.py b/tempest/services/compute/xml/hosts_client.py
index 70aeb48..9743143 100644
--- a/tempest/services/compute/xml/hosts_client.py
+++ b/tempest/services/compute/xml/hosts_client.py
@@ -39,3 +39,11 @@
         node = etree.fromstring(body)
         body = [xml_to_json(x) for x in node.getchildren()]
         return resp, body
+
+    def show_host_detail(self, hostname):
+        """Show detail information for the host."""
+
+        resp, body = self.get("os-hosts/%s" % str(hostname), self.headers)
+        node = etree.fromstring(body)
+        body = [xml_to_json(x) for x in node.getchildren()]
+        return resp, body
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index cc13aa1..b17ae78 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -130,6 +130,7 @@
     def get_image(self, image_id):
         """Returns the details of a single image."""
         resp, body = self.get("images/%s" % str(image_id), self.headers)
+        self.expected_success(200, resp)
         body = self._parse_image(etree.fromstring(body))
         return resp, body
 
@@ -137,21 +138,6 @@
         """Deletes the provided image."""
         return self.delete("images/%s" % str(image_id), self.headers)
 
-    def wait_for_image_resp_code(self, image_id, code):
-        """
-        Waits until the HTTP response code for the request matches the
-        expected value
-        """
-        resp, body = self.get("images/%s" % str(image_id), self.headers)
-        start = int(time.time())
-
-        while resp.status != code:
-            time.sleep(self.build_interval)
-            resp, body = self.get("images/%s" % str(image_id), self.headers)
-
-            if int(time.time()) - start >= self.build_timeout:
-                raise exceptions.TimeoutException
-
     def wait_for_image_status(self, image_id, status):
         """Waits for an image to reach a given status."""
         resp, image = self.get_image(image_id)
diff --git a/tempest/services/identity/json/identity_client.py b/tempest/services/identity/json/identity_client.py
index 47977df..18132ed 100644
--- a/tempest/services/identity/json/identity_client.py
+++ b/tempest/services/identity/json/identity_client.py
@@ -152,6 +152,14 @@
         body = json.loads(body)
         return resp, body['user']
 
+    def update_user(self, user_id, **kwargs):
+        """Updates a user."""
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s' % user_id, put_body,
+                              self.headers)
+        body = json.loads(body)
+        return resp, body['user']
+
     def get_user(self, user_id):
         """GET a user."""
         resp, body = self.get("users/%s" % user_id)
diff --git a/tempest/services/identity/xml/identity_client.py b/tempest/services/identity/xml/identity_client.py
index 7a00b84..9d44826 100644
--- a/tempest/services/identity/xml/identity_client.py
+++ b/tempest/services/identity/xml/identity_client.py
@@ -172,6 +172,18 @@
         body = self._parse_body(etree.fromstring(body))
         return resp, body
 
+    def update_user(self, user_id, **kwargs):
+        """Updates a user."""
+        if 'enabled' in kwargs:
+            kwargs['enabled'] = str(kwargs['enabled']).lower()
+        update_user = Element("user", xmlns=XMLNS, **kwargs)
+
+        resp, body = self.put('users/%s' % user_id,
+                              str(Document(update_user)),
+                              self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
     def get_user(self, user_id):
         """GET a user."""
         resp, body = self.get("users/%s" % user_id, self.headers)
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index ce2da90..10ba3fd 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -76,6 +76,14 @@
         body = json.loads(body)
         return resp, body['snapshot']
 
+    def update_snapshot(self, snapshot_id, **kwargs):
+        """Updates a snapshot."""
+        put_body = json.dumps({'snapshot': kwargs})
+        resp, body = self.put('snapshots/%s' % snapshot_id, put_body,
+                              self.headers)
+        body = json.loads(body)
+        return resp, body['snapshot']
+
     # NOTE(afazekas): just for the wait function
     def _get_snapshot_status(self, snapshot_id):
         resp, body = self.get_snapshot(snapshot_id)
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index c35452e..32b6270 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -85,6 +85,14 @@
         body = json.loads(body)
         return resp, body['volume']
 
+    def update_volume(self, volume_id, **kwargs):
+        """Updates the Specified Volume."""
+        put_body = json.dumps({'volume': kwargs})
+        resp, body = self.put('volumes/%s' % volume_id, put_body,
+                              self.headers)
+        body = json.loads(body)
+        return resp, body['volume']
+
     def delete_volume(self, volume_id):
         """Deletes the Specified Volume."""
         return self.delete("volumes/%s" % str(volume_id))
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 3596017..b7ba56b 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -90,6 +90,16 @@
         body = xml_to_json(etree.fromstring(body))
         return resp, body
 
+    def update_snapshot(self, snapshot_id, **kwargs):
+        """Updates a snapshot."""
+        put_body = Element("snapshot", xmlns=XMLNS_11, **kwargs)
+
+        resp, body = self.put('snapshots/%s' % snapshot_id,
+                              str(Document(put_body)),
+                              self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
     # NOTE(afazekas): just for the wait function
     def _get_snapshot_status(self, snapshot_id):
         resp, body = self.get_snapshot(snapshot_id)
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 9fa7a1e..7915637 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -151,6 +151,16 @@
         body = xml_to_json(etree.fromstring(body))
         return resp, body
 
+    def update_volume(self, volume_id, **kwargs):
+        """Updates the Specified Volume."""
+        put_body = Element("volume", xmlns=XMLNS_11, **kwargs)
+
+        resp, body = self.put('volumes/%s' % volume_id,
+                              str(Document(put_body)),
+                              self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
     def delete_volume(self, volume_id):
         """Deletes the Specified Volume."""
         return self.delete("volumes/%s" % str(volume_id))
diff --git a/tempest/tests/README.rst b/tempest/tests/README.rst
index 4098686..33d321f 100644
--- a/tempest/tests/README.rst
+++ b/tempest/tests/README.rst
@@ -1,5 +1,5 @@
-Tempest Guide to Unit tests
-===========================
+Tempest Field Guide to Unit tests
+=================================
 
 What are these tests?
 ---------------------
diff --git a/tempest/thirdparty/README.rst b/tempest/thirdparty/README.rst
index b775817..53cb54b 100644
--- a/tempest/thirdparty/README.rst
+++ b/tempest/thirdparty/README.rst
@@ -1,5 +1,5 @@
-Tempest Guide to Third Party API tests
-======================================
+Tempest Field Guide to Third Party API tests
+============================================
 
 
 What are these tests?
diff --git a/test-requirements.txt b/test-requirements.txt
index 6c313ca..1ede25e 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,3 +2,4 @@
 # needed for doc build
 docutils==0.9.1
 sphinx>=1.1.2
+python-subunit
diff --git a/tools/install_venv.py b/tools/install_venv.py
index 1664b35..f37e0cd 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -56,7 +56,7 @@
 
     Also, make test will automatically use the virtualenv.
     """
-    print help
+    print(help)
 
 
 def main(argv):
diff --git a/tox.ini b/tox.ini
index 0b57eb2..1b8a0fd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -30,6 +30,7 @@
 [testenv:heat-slow]
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}
+         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}'