Merge "Update config generator from oslo-incubator"
diff --git a/README.rst b/README.rst
index 4393ae9..ea36619 100644
--- a/README.rst
+++ b/README.rst
@@ -130,3 +130,57 @@
 unittest2.TestSuite instead. See:
 
 https://code.google.com/p/unittest-ext/issues/detail?id=79
+
+Branchless Tempest Considerations
+---------------------------------
+
+Starting with the OpenStack Icehouse release Tempest no longer has any stable
+branches. This is to better ensure API consistency between releases because
+the API behavior should not change between releases. This means that the stable
+branches are also gated by the Tempest master branch, which also means that
+proposed commits to Tempest must work against both the master and all the
+currently supported stable branches of the projects. As such there are a few
+special considerations that have to be accounted for when pushing new changes
+to tempest.
+
+1. New Tests for new features
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When adding tests for new features that were not in previous releases of the
+projects the new test has to be properly skipped with a feature flag. Whether
+this is just as simple as using the @test.requires_ext() decorator to check
+if the required extension (or discoverable optional API) is enabled or adding
+a new config option to the appropriate section. If there isn't a method of
+selecting the new **feature** from the config file then there won't be a
+mechanism to disable the test with older stable releases and the new test won't
+be able to merge.
+
+2. Bug fix on core project needing Tempest changes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When trying to land a bug fix which changes a tested API you'll have to use the
+following procedure::
+
+    - Propose change to the project, get a +2 on the change even with failing
+    - Propose skip on Tempest which will only be approved after the
+      corresponding change in the project has a +2 on change
+    - Land project change in master and all open stable branches (if required)
+    - Land changed test in Tempest
+
+Otherwise the bug fix won't be able to land in the project.
+
+3. New Tests for existing features
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If a test is being added for a feature that exists in all the current releases
+of the projects then the only concern is that the API behavior is the same
+across all the versions of the project being tested. If the behavior is not
+consistent the test will not be able to merge.
+
+API Stability
+-------------
+
+For new tests being added to Tempest the assumption is that the API being
+tested is considered stable and adheres to the OpenStack API stability
+guidelines. If an API is still considered experimental or in development then
+it should not be tested by Tempest until it is considered stable.
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 9051310..bf0b308 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -119,13 +119,13 @@
 
 # Timeout for association of Nova instance and Ironic node
 # (integer value)
-#association_timeout=10
+#association_timeout=30
 
 # Timeout for Ironic power transitions. (integer value)
-#power_timeout=20
+#power_timeout=60
 
 # Timeout for unprovisioning an Ironic node. (integer value)
-#unprovision_timeout=20
+#unprovision_timeout=60
 
 
 [boto]
@@ -424,6 +424,10 @@
 # as [nova.rdp]->enabled in nova.conf (boolean value)
 #rdp_console=false
 
+# Does the test environment support instance rescue mode?
+# (boolean value)
+#rescue=true
+
 
 [dashboard]
 
diff --git a/requirements.txt b/requirements.txt
index f907e7d..ab2903a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,7 +8,7 @@
 paramiko>=1.13.0
 netaddr>=0.7.6
 python-glanceclient>=0.9.0
-python-keystoneclient>=0.8.0
+python-keystoneclient>=0.9.0
 python-novaclient>=2.17.0
 python-neutronclient>=2.3.4,<3
 python-cinderclient>=1.0.6
@@ -18,8 +18,8 @@
 python-swiftclient>=2.0.2
 testresources>=0.2.4
 testrepository>=0.0.18
-oslo.config>=1.2.0
-six>=1.6.0
+oslo.config>=1.2.1
+six>=1.7.0
 iso8601>=0.1.9
 fixtures>=0.3.14
 testscenarios>=0.4
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index 8f875d2..208b032 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -74,7 +74,7 @@
         self.assertEqual(self.ip_range, body['ip_range'])
         resp, ips_list = self.client.list_floating_ips_bulk()
         self.assertEqual(200, resp.status)
-        self.assertNotEqual(0, len(body))
+        self.assertNotEqual(0, len(ips_list))
         for ip in netaddr.IPNetwork(self.ip_range).iter_hosts():
             self.assertIn(str(ip), map(lambda x: x['address'], ips_list))
         resp, body = self.client.delete_floating_ips_bulk(self.ip_range)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index e135eca..279dc51 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -102,6 +102,28 @@
                                                   self.password)
         self.assertTrue(linux_client.hostname_equals_servername(self.name))
 
+    @test.skip_because(bug="1306367", interface="xml")
+    @test.attr(type='gate')
+    def test_create_server_with_scheduler_hint_group(self):
+        # Create a server with the scheduler hint "group".
+        name = data_utils.rand_name('server_group')
+        policies = ['affinity']
+        resp, body = self.client.create_server_group(name=name,
+                                                     policies=policies)
+        self.assertEqual(200, resp.status)
+        group_id = body['id']
+        self.addCleanup(self.client.delete_server_group, group_id)
+
+        hints = {'group': group_id}
+        resp, server = self.create_test_server(sched_hints=hints,
+                                               wait_until='ACTIVE')
+        self.assertEqual(202, resp.status)
+
+        # Check a server is in the group
+        resp, server_group = self.client.get_server_group(group_id)
+        self.assertEqual(200, resp.status)
+        self.assertIn(server['id'], server_group['members'])
+
 
 class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
     disk_config = 'AUTO'
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index d0fd876..71fcbff 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import base64
+import logging
 
 import testtools
 import urlparse
@@ -27,6 +28,8 @@
 
 CONF = config.CONF
 
+LOG = logging.getLogger(__name__)
+
 
 class ServerActionsTestJSON(base.BaseV2ComputeTest):
     run_ssh = CONF.compute.run_ssh
@@ -267,7 +270,14 @@
         # the oldest one should be deleted automatically in this test
         def _clean_oldest_backup(oldest_backup):
             if oldest_backup_exist:
-                self.os.image_client.delete_image(oldest_backup)
+                try:
+                    self.os.image_client.delete_image(oldest_backup)
+                except exceptions.NotFound:
+                    pass
+                else:
+                    LOG.warning("Deletion of oldest backup %s should not have "
+                                "been successful as it should have been "
+                                "deleted during rotation." % oldest_backup)
 
         image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 0cd23fd..f1ef5d5 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -75,6 +75,7 @@
         policy = ['anti-affinity']
         self._create_delete_server_group(policy)
 
+    @test.skip_because(bug="1324348")
     @test.attr(type='gate')
     def test_create_delete_server_group_with_multiple_policies(self):
         # Create and Delete the server-group with multiple policies
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 093e9e2..ab98d88 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -15,14 +15,21 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest import config
 from tempest import test
 
+CONF = config.CONF
+
 
 class ServerRescueTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
     @test.safe_setup
     def setUpClass(cls):
+        if not CONF.compute_feature_enabled.rescue:
+            msg = "Server rescue not available."
+            raise cls.skipException(msg)
+
         cls.set_network_resources(network=True, subnet=True, router=True)
         super(ServerRescueTestJSON, cls).setUpClass()
 
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index dae4709..b35e55c 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -28,6 +28,10 @@
     @classmethod
     @test.safe_setup
     def setUpClass(cls):
+        if not CONF.compute_feature_enabled.rescue:
+            msg = "Server rescue not available."
+            raise cls.skipException(msg)
+
         cls.set_network_resources(network=True, subnet=True, router=True)
         super(ServerRescueNegativeTestJSON, cls).setUpClass()
         cls.device = 'vdf'
diff --git a/tempest/api/compute/v3/admin/test_servers_negative.py b/tempest/api/compute/v3/admin/test_servers_negative.py
index a971463..5eb6395 100644
--- a/tempest/api/compute/v3/admin/test_servers_negative.py
+++ b/tempest/api/compute/v3/admin/test_servers_negative.py
@@ -54,6 +54,7 @@
             flavor_id = data_utils.rand_int_id(start=1000)
         return flavor_id
 
+    @test.skip_because(bug="1298131")
     @test.attr(type=['negative', 'gate'])
     def test_resize_server_using_overlimit_ram(self):
         flavor_name = data_utils.rand_name("flavor-")
@@ -67,11 +68,12 @@
                                                              ram, vcpus, disk,
                                                              flavor_id)
         self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
-        self.assertRaises(exceptions.OverLimit,
+        self.assertRaises(exceptions.Unauthorized,
                           self.client.resize,
                           self.servers[0]['id'],
                           flavor_ref['id'])
 
+    @test.skip_because(bug="1298131")
     @test.attr(type=['negative', 'gate'])
     def test_resize_server_using_overlimit_vcpus(self):
         flavor_name = data_utils.rand_name("flavor-")
@@ -85,7 +87,7 @@
                                                              ram, vcpus, disk,
                                                              flavor_id)
         self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
-        self.assertRaises(exceptions.OverLimit,
+        self.assertRaises(exceptions.Unauthorized,
                           self.client.resize,
                           self.servers[0]['id'],
                           flavor_ref['id'])
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index e098311..3ee8050 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import logging
+
 import testtools
 import urlparse
 
@@ -25,6 +27,8 @@
 
 CONF = config.CONF
 
+LOG = logging.getLogger(__name__)
+
 
 class ServerActionsV3Test(base.BaseV3ComputeTest):
     run_ssh = CONF.compute.run_ssh
@@ -260,7 +264,14 @@
         # the oldest one should be deleted automatically in this test
         def _clean_oldest_backup(oldest_backup):
             if oldest_backup_exist:
-                self.images_client.delete_image(oldest_backup)
+                try:
+                    self.images_client.delete_image(oldest_backup)
+                except exceptions.NotFound:
+                    pass
+                else:
+                    LOG.warning("Deletion of oldest backup %s should not have "
+                                "been successful as it should have been "
+                                "deleted during rotation." % oldest_backup)
 
         image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
diff --git a/tempest/api/compute/v3/servers/test_server_rescue.py b/tempest/api/compute/v3/servers/test_server_rescue.py
index b3dcb51..da58f26 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue.py
@@ -14,13 +14,19 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest import config
 from tempest import test
 
+CONF = config.CONF
+
 
 class ServerRescueV3Test(base.BaseV3ComputeTest):
 
     @classmethod
     def setUpClass(cls):
+        if not CONF.compute_feature_enabled.rescue:
+            msg = "Server rescue not available."
+            raise cls.skipException(msg)
         super(ServerRescueV3Test, cls).setUpClass()
 
         # Server for positive tests
diff --git a/tempest/api/compute/v3/servers/test_server_rescue_negative.py b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
index eb6bcdd..5eb6c9a 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
@@ -28,6 +28,10 @@
     @classmethod
     @test.safe_setup
     def setUpClass(cls):
+        if not CONF.compute_feature_enabled.rescue:
+            msg = "Server rescue not available."
+            raise cls.skipException(msg)
+
         super(ServerRescueNegativeV3Test, cls).setUpClass()
         cls.device = 'vdf'
 
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 9981292..02d391b 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -142,9 +142,9 @@
 
     def _create_image(self):
         name = data_utils.rand_name('image')
-        resp, image = self.os_img_client.create_image(name,
-                                                      container_format='bare',
-                                                      disk_format='raw')
+        _, image = self.os_img_client.create_image(name,
+                                                   container_format='bare',
+                                                   disk_format='raw')
         image_id = image['id']
         self.addCleanup(self.os_img_client.delete_image, image_id)
         return image_id
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index 4cbb62f..f91cb69 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -22,27 +22,21 @@
     @test.attr(type='gate')
     def test_add_image_member(self):
         image = self._create_image()
-        resp = self.client.add_member(self.alt_tenant_id, image)
-        self.assertEqual(204, resp.status)
-        resp, body = self.client.get_image_membership(image)
-        self.assertEqual(200, resp.status)
+        self.client.add_member(self.alt_tenant_id, image)
+        _, body = self.client.get_image_membership(image)
         members = body['members']
         members = map(lambda x: x['member_id'], members)
         self.assertIn(self.alt_tenant_id, members)
         # get image as alt user
-        resp, body = self.alt_img_cli.get_image(image)
-        self.assertEqual(200, resp.status)
+        self.alt_img_cli.get_image(image)
 
     @test.attr(type='gate')
     def test_get_shared_images(self):
         image = self._create_image()
-        resp = self.client.add_member(self.alt_tenant_id, image)
-        self.assertEqual(204, resp.status)
+        self.client.add_member(self.alt_tenant_id, image)
         share_image = self._create_image()
-        resp = self.client.add_member(self.alt_tenant_id, share_image)
-        self.assertEqual(204, resp.status)
-        resp, body = self.client.get_shared_images(self.alt_tenant_id)
-        self.assertEqual(200, resp.status)
+        self.client.add_member(self.alt_tenant_id, share_image)
+        _, body = self.client.get_shared_images(self.alt_tenant_id)
         images = body['shared_images']
         images = map(lambda x: x['image_id'], images)
         self.assertIn(share_image, images)
@@ -51,11 +45,8 @@
     @test.attr(type='gate')
     def test_remove_member(self):
         image_id = self._create_image()
-        resp = self.client.add_member(self.alt_tenant_id, image_id)
-        self.assertEqual(204, resp.status)
-        resp = self.client.delete_member(self.alt_tenant_id, image_id)
-        self.assertEqual(204, resp.status)
-        resp, body = self.client.get_image_membership(image_id)
-        self.assertEqual(200, resp.status)
+        self.client.add_member(self.alt_tenant_id, image_id)
+        self.client.delete_member(self.alt_tenant_id, image_id)
+        _, body = self.client.get_image_membership(image_id)
         members = body['members']
         self.assertEqual(0, len(members), str(members))
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 2df3f7f..8528e42 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -30,11 +30,11 @@
     def test_register_then_upload(self):
         # Register, then upload an image
         properties = {'prop1': 'val1'}
-        resp, body = self.create_image(name='New Name',
-                                       container_format='bare',
-                                       disk_format='raw',
-                                       is_public=False,
-                                       properties=properties)
+        _, body = self.create_image(name='New Name',
+                                    container_format='bare',
+                                    disk_format='raw',
+                                    is_public=False,
+                                    properties=properties)
         self.assertIn('id', body)
         image_id = body.get('id')
         self.assertEqual('New Name', body.get('name'))
@@ -45,20 +45,19 @@
 
         # Now try uploading an image file
         image_file = StringIO.StringIO(('*' * 1024))
-        resp, body = self.client.update_image(image_id, data=image_file)
+        _, body = self.client.update_image(image_id, data=image_file)
         self.assertIn('size', body)
         self.assertEqual(1024, body.get('size'))
 
     @test.attr(type='gate')
     def test_register_remote_image(self):
         # Register a new remote image
-        resp, body = self.create_image(name='New Remote Image',
-                                       container_format='bare',
-                                       disk_format='raw', is_public=False,
-                                       location='http://example.com'
-                                                '/someimage.iso',
-                                       properties={'key1': 'value1',
-                                                   'key2': 'value2'})
+        _, body = self.create_image(name='New Remote Image',
+                                    container_format='bare',
+                                    disk_format='raw', is_public=False,
+                                    location=CONF.image.http_image,
+                                    properties={'key1': 'value1',
+                                                'key2': 'value2'})
         self.assertIn('id', body)
         self.assertEqual('New Remote Image', body.get('name'))
         self.assertFalse(body.get('is_public'))
@@ -69,28 +68,27 @@
 
     @test.attr(type='gate')
     def test_register_http_image(self):
-        resp, body = self.create_image(name='New Http Image',
-                                       container_format='bare',
-                                       disk_format='raw', is_public=False,
-                                       copy_from=CONF.image.http_image)
+        _, body = self.create_image(name='New Http Image',
+                                    container_format='bare',
+                                    disk_format='raw', is_public=False,
+                                    copy_from=CONF.image.http_image)
         self.assertIn('id', body)
         image_id = body.get('id')
         self.assertEqual('New Http Image', body.get('name'))
         self.assertFalse(body.get('is_public'))
         self.client.wait_for_image_status(image_id, 'active')
-        resp, body = self.client.get_image(image_id)
-        self.assertEqual(resp['status'], '200')
+        self.client.get_image(image_id)
 
     @test.attr(type='gate')
     def test_register_image_with_min_ram(self):
         # Register an image with min ram
         properties = {'prop1': 'val1'}
-        resp, body = self.create_image(name='New_image_with_min_ram',
-                                       container_format='bare',
-                                       disk_format='raw',
-                                       is_public=False,
-                                       min_ram=40,
-                                       properties=properties)
+        _, body = self.create_image(name='New_image_with_min_ram',
+                                    container_format='bare',
+                                    disk_format='raw',
+                                    is_public=False,
+                                    min_ram=40,
+                                    properties=properties)
         self.assertIn('id', body)
         self.assertEqual('New_image_with_min_ram', body.get('name'))
         self.assertFalse(body.get('is_public'))
@@ -98,8 +96,7 @@
         self.assertEqual(40, body.get('min_ram'))
         for key, val in properties.items():
             self.assertEqual(val, body.get('properties')[key])
-        resp, body = self.client.delete_image(body['id'])
-        self.assertEqual('200', resp['status'])
+        self.client.delete_image(body['id'])
 
 
 class ListImagesTest(base.BaseV1ImageTest):
@@ -143,12 +140,12 @@
         image
         """
         name = 'New Remote Image %s' % name
-        location = 'http://example.com/someimage_%s.iso' % name
-        resp, image = cls.create_image(name=name,
-                                       container_format=container_format,
-                                       disk_format=disk_format,
-                                       is_public=False,
-                                       location=location)
+        location = CONF.image.http_image
+        _, image = cls.create_image(name=name,
+                                    container_format=container_format,
+                                    disk_format=disk_format,
+                                    is_public=False,
+                                    location=location)
         image_id = image['id']
         return image_id
 
@@ -162,26 +159,24 @@
         """
         image_file = StringIO.StringIO('*' * size)
         name = 'New Standard Image %s' % name
-        resp, image = cls.create_image(name=name,
-                                       container_format=container_format,
-                                       disk_format=disk_format,
-                                       is_public=False, data=image_file)
+        _, image = cls.create_image(name=name,
+                                    container_format=container_format,
+                                    disk_format=disk_format,
+                                    is_public=False, data=image_file)
         image_id = image['id']
         return image_id
 
     @test.attr(type='gate')
     def test_index_no_params(self):
         # Simple test to see all fixture images returned
-        resp, images_list = self.client.image_list()
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list()
         image_list = map(lambda x: x['id'], images_list)
         for image_id in self.created_images:
             self.assertIn(image_id, image_list)
 
     @test.attr(type='gate')
     def test_index_disk_format(self):
-        resp, images_list = self.client.image_list(disk_format='ami')
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list(disk_format='ami')
         for image in images_list:
             self.assertEqual(image['disk_format'], 'ami')
         result_set = set(map(lambda x: x['id'], images_list))
@@ -190,8 +185,7 @@
 
     @test.attr(type='gate')
     def test_index_container_format(self):
-        resp, images_list = self.client.image_list(container_format='bare')
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list(container_format='bare')
         for image in images_list:
             self.assertEqual(image['container_format'], 'bare')
         result_set = set(map(lambda x: x['id'], images_list))
@@ -200,8 +194,7 @@
 
     @test.attr(type='gate')
     def test_index_max_size(self):
-        resp, images_list = self.client.image_list(size_max=42)
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list(size_max=42)
         for image in images_list:
             self.assertTrue(image['size'] <= 42)
         result_set = set(map(lambda x: x['id'], images_list))
@@ -210,8 +203,7 @@
 
     @test.attr(type='gate')
     def test_index_min_size(self):
-        resp, images_list = self.client.image_list(size_min=142)
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list(size_min=142)
         for image in images_list:
             self.assertTrue(image['size'] >= 142)
         result_set = set(map(lambda x: x['id'], images_list))
@@ -220,10 +212,9 @@
 
     @test.attr(type='gate')
     def test_index_status_active_detail(self):
-        resp, images_list = self.client.image_list_detail(status='active',
-                                                          sort_key='size',
-                                                          sort_dir='desc')
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list_detail(status='active',
+                                                       sort_key='size',
+                                                       sort_dir='desc')
         top_size = images_list[0]['size']  # We have non-zero sized images
         for image in images_list:
             size = image['size']
@@ -233,9 +224,8 @@
 
     @test.attr(type='gate')
     def test_index_name(self):
-        resp, images_list = self.client.image_list_detail(
+        _, images_list = self.client.image_list_detail(
             name='New Remote Image dup')
-        self.assertEqual(resp['status'], '200')
         result_set = set(map(lambda x: x['id'], images_list))
         for image in images_list:
             self.assertEqual(image['name'], 'New Remote Image dup')
@@ -267,10 +257,10 @@
         cls.snapshot_set = set((cls.snapshot,))
 
         image_file = StringIO.StringIO('*' * 42)
-        resp, image = cls.create_image(name="Standard Image",
-                                       container_format='ami',
-                                       disk_format='ami',
-                                       is_public=False, data=image_file)
+        _, image = cls.create_image(name="Standard Image",
+                                    container_format='ami',
+                                    disk_format='ami',
+                                    is_public=False, data=image_file)
         cls.image_id = image['id']
         cls.client.wait_for_image_status(image['id'], 'active')
 
@@ -282,7 +272,7 @@
 
     @classmethod
     def _create_snapshot(cls, name, image_id, flavor, **kwargs):
-        resp, server = cls.servers_client.create_server(
+        _, server = cls.servers_client.create_server(
             name, image_id, flavor, **kwargs)
         cls.servers.append(server)
         cls.servers_client.wait_for_server_status(
@@ -298,9 +288,8 @@
     @test.services('compute')
     def test_index_server_id(self):
         # The images should contain images filtered by server id
-        resp, images = self.client.image_list_detail(
+        _, images = self.client.image_list_detail(
             {'instance_uuid': self.servers[0]['id']})
-        self.assertEqual(200, resp.status)
         result_set = set(map(lambda x: x['id'], images))
         self.assertEqual(self.snapshot_set, result_set)
 
@@ -309,9 +298,8 @@
     def test_index_type(self):
         # The list of servers should be filtered by image type
         params = {'image_type': 'snapshot'}
-        resp, images = self.client.image_list_detail(params)
+        _, images = self.client.image_list_detail(params)
 
-        self.assertEqual(200, resp.status)
         result_set = set(map(lambda x: x['id'], images))
         self.assertIn(self.snapshot, result_set)
 
@@ -319,9 +307,8 @@
     @test.services('compute')
     def test_index_limit(self):
         # Verify only the expected number of results are returned
-        resp, images = self.client.image_list_detail(limit=1)
+        _, images = self.client.image_list_detail(limit=1)
 
-        self.assertEqual(200, resp.status)
         self.assertEqual(1, len(images))
 
     @test.attr(type='gate')
@@ -330,13 +317,11 @@
         # Verify an update image is returned
         # Becoming ACTIVE will modify the updated time
         # Filter by the image's created time
-        resp, image = self.client.get_image_meta(self.snapshot)
-        self.assertEqual(200, resp.status)
+        _, image = self.client.get_image_meta(self.snapshot)
         self.assertEqual(self.snapshot, image['id'])
-        resp, images = self.client.image_list_detail(
+        _, images = self.client.image_list_detail(
             changes_since=image['updated_at'])
 
-        self.assertEqual(200, resp.status)
         result_set = set(map(lambda x: x['id'], images))
         self.assertIn(self.image_id, result_set)
         self.assertNotIn(self.snapshot, result_set)
@@ -358,18 +343,18 @@
         """
         image_file = StringIO.StringIO('*' * size)
         name = 'New Standard Image %s' % name
-        resp, image = cls.create_image(name=name,
-                                       container_format=container_format,
-                                       disk_format=disk_format,
-                                       is_public=False, data=image_file,
-                                       properties={'key1': 'value1'})
+        _, image = cls.create_image(name=name,
+                                    container_format=container_format,
+                                    disk_format=disk_format,
+                                    is_public=False, data=image_file,
+                                    properties={'key1': 'value1'})
         image_id = image['id']
         return image_id
 
     @test.attr(type='gate')
     def test_list_image_metadata(self):
         # All metadata key/value pairs for an image should be returned
-        resp, resp_metadata = self.client.get_image_meta(self.image_id)
+        _, resp_metadata = self.client.get_image_meta(self.image_id)
         expected = {'key1': 'value1'}
         self.assertEqual(expected, resp_metadata['properties'])
 
@@ -377,13 +362,12 @@
     def test_update_image_metadata(self):
         # The metadata for the image should match the updated values
         req_metadata = {'key1': 'alt1', 'key2': 'value2'}
-        resp, metadata = self.client.get_image_meta(self.image_id)
-        self.assertEqual(200, resp.status)
+        _, metadata = self.client.get_image_meta(self.image_id)
         self.assertEqual(metadata['properties'], {'key1': 'value1'})
         metadata['properties'].update(req_metadata)
-        resp, metadata = self.client.update_image(
+        _, metadata = self.client.update_image(
             self.image_id, properties=metadata['properties'])
 
-        resp, resp_metadata = self.client.get_image_meta(self.image_id)
+        _, resp_metadata = self.client.get_image_meta(self.image_id)
         expected = {'key1': 'alt1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata['properties'])
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 37dc163..ae777eb 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -37,11 +37,11 @@
 
         uuid = '00000000-1111-2222-3333-444455556666'
         image_name = data_utils.rand_name('image')
-        resp, body = self.create_image(name=image_name,
-                                       container_format='bare',
-                                       disk_format='raw',
-                                       visibility='private',
-                                       ramdisk_id=uuid)
+        _, body = self.create_image(name=image_name,
+                                    container_format='bare',
+                                    disk_format='raw',
+                                    visibility='private',
+                                    ramdisk_id=uuid)
         self.assertIn('id', body)
         image_id = body.get('id')
         self.assertIn('name', body)
@@ -54,12 +54,10 @@
         # Now try uploading an image file
         file_content = '*' * 1024
         image_file = StringIO.StringIO(file_content)
-        resp, body = self.client.store_image(image_id, image_file)
-        self.assertEqual(resp.status, 204)
+        self.client.store_image(image_id, image_file)
 
         # Now try to get image details
-        resp, body = self.client.get_image(image_id)
-        self.assertEqual(200, resp.status)
+        _, body = self.client.get_image(image_id)
         self.assertEqual(image_id, body['id'])
         self.assertEqual(image_name, body['name'])
         self.assertEqual(uuid, body['ramdisk_id'])
@@ -67,8 +65,7 @@
         self.assertEqual(1024, body.get('size'))
 
         # Now try get image file
-        resp, body = self.client.get_image_file(image_id)
-        self.assertEqual(200, resp.status)
+        _, body = self.client.get_image_file(image_id)
         self.assertEqual(file_content, body)
 
     @test.attr(type='gate')
@@ -77,11 +74,10 @@
 
         # Create image
         image_name = data_utils.rand_name('image')
-        resp, body = self.client.create_image(name=image_name,
-                                              container_format='bare',
-                                              disk_format='raw',
-                                              visibility='private')
-        self.assertEqual(201, resp.status)
+        _, body = self.client.create_image(name=image_name,
+                                           container_format='bare',
+                                           disk_format='raw',
+                                           visibility='private')
         image_id = body['id']
 
         # Delete Image
@@ -89,8 +85,7 @@
         self.client.wait_for_resource_deletion(image_id)
 
         # Verifying deletion
-        resp, images = self.client.image_list()
-        self.assertEqual(resp.status, 200)
+        _, images = self.client.image_list()
         self.assertNotIn(image_id, images)
 
     @test.attr(type='gate')
@@ -99,11 +94,10 @@
 
         # Create image
         image_name = data_utils.rand_name('image')
-        resp, body = self.client.create_image(name=image_name,
-                                              container_format='bare',
-                                              disk_format='iso',
-                                              visibility='private')
-        self.assertEqual(201, resp.status)
+        _, body = self.client.create_image(name=image_name,
+                                           container_format='bare',
+                                           disk_format='iso',
+                                           visibility='private')
         self.addCleanup(self.client.delete_image, body['id'])
         self.assertEqual('queued', body['status'])
         image_id = body['id']
@@ -111,20 +105,16 @@
         # Now try uploading an image file
         file_content = '*' * 1024
         image_file = StringIO.StringIO(file_content)
-        resp, body = self.client.store_image(image_id, image_file)
-        self.assertEqual(204, resp.status)
+        self.client.store_image(image_id, image_file)
 
         # Update Image
         new_image_name = data_utils.rand_name('new-image')
-        resp, body = self.client.update_image(image_id, [
+        _, body = self.client.update_image(image_id, [
             dict(replace='/name', value=new_image_name)])
 
-        self.assertEqual(200, resp.status)
-
         # Verifying updating
 
-        resp, body = self.client.get_image(image_id)
-        self.assertEqual(200, resp.status)
+        _, body = self.client.get_image(image_id)
         self.assertEqual(image_id, body['id'])
         self.assertEqual(new_image_name, body['name'])
 
@@ -157,12 +147,12 @@
         """
         image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
         name = data_utils.rand_name('image-')
-        resp, body = cls.create_image(name=name,
-                                      container_format=container_format,
-                                      disk_format=disk_format,
-                                      visibility='private')
+        _, body = cls.create_image(name=name,
+                                   container_format=container_format,
+                                   disk_format=disk_format,
+                                   visibility='private')
         image_id = body['id']
-        resp, body = cls.client.store_image(image_id, data=image_file)
+        cls.client.store_image(image_id, data=image_file)
 
         return image_id
 
@@ -170,8 +160,7 @@
         """
         Perform list action with given params and validates result.
         """
-        resp, images_list = self.client.image_list(params=params)
-        self.assertEqual(200, resp.status)
+        _, images_list = self.client.image_list(params=params)
         # Validating params of fetched images
         for image in images_list:
             for key in params:
@@ -181,8 +170,7 @@
     @test.attr(type='gate')
     def test_index_no_params(self):
         # Simple test to see all fixture images returned
-        resp, images_list = self.client.image_list()
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list()
         image_list = map(lambda x: x['id'], images_list)
 
         for image in self.created_images:
@@ -211,8 +199,7 @@
         # Test to get all images by size
         image_id = self.created_images[1]
         # Get image metadata
-        resp, image = self.client.get_image(image_id)
-        self.assertEqual(resp['status'], '200')
+        _, image = self.client.get_image(image_id)
 
         params = {"size": image['size']}
         self._list_by_param_value_and_assert(params)
@@ -222,13 +209,11 @@
         # Test to get all images with size between 2000 to 3000
         image_id = self.created_images[1]
         # Get image metadata
-        resp, image = self.client.get_image(image_id)
-        self.assertEqual(resp['status'], '200')
+        _, image = self.client.get_image(image_id)
 
         size = image['size']
         params = {"size_min": size - 500, "size_max": size + 500}
-        resp, images_list = self.client.image_list(params=params)
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list(params=params)
         image_size_list = map(lambda x: x['size'], images_list)
 
         for image_size in image_size_list:
@@ -246,8 +231,7 @@
     def test_list_images_param_limit(self):
         # Test to get images by limit
         params = {"limit": 2}
-        resp, images_list = self.client.image_list(params=params)
-        self.assertEqual(resp['status'], '200')
+        _, images_list = self.client.image_list(params=params)
 
         self.assertEqual(len(images_list), params['limit'],
                          "Failed to get images by limit")
@@ -256,14 +240,12 @@
     def test_get_image_schema(self):
         # Test to get image schema
         schema = "image"
-        resp, body = self.client.get_schema(schema)
-        self.assertEqual(200, resp.status)
+        _, body = self.client.get_schema(schema)
         self.assertEqual("image", body['name'])
 
     @test.attr(type='gate')
     def test_get_images_schema(self):
         # Test to get images schema
         schema = "images"
-        resp, body = self.client.get_schema(schema)
-        self.assertEqual(200, resp.status)
+        _, body = self.client.get_schema(schema)
         self.assertEqual("images", body['name'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index f80c818..5aaf578 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -20,9 +20,8 @@
     @test.attr(type='gate')
     def test_image_share_accept(self):
         image_id = self._create_image()
-        resp, member = self.os_img_client.add_member(image_id,
-                                                     self.alt_tenant_id)
-        self.assertEqual(200, resp.status)
+        _, member = self.os_img_client.add_member(image_id,
+                                                  self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
@@ -31,8 +30,7 @@
                                                  self.alt_tenant_id,
                                                  'accepted')
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        resp, body = self.os_img_client.get_image_membership(image_id)
-        self.assertEqual(200, resp.status)
+        _, body = self.os_img_client.get_image_membership(image_id)
         members = body['members']
         member = members[0]
         self.assertEqual(len(members), 1, str(members))
@@ -43,17 +41,15 @@
     @test.attr(type='gate')
     def test_image_share_reject(self):
         image_id = self._create_image()
-        resp, member = self.os_img_client.add_member(image_id,
-                                                     self.alt_tenant_id)
-        self.assertEqual(200, resp.status)
+        _, member = self.os_img_client.add_member(image_id,
+                                                  self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
-        resp, _ = self.alt_img_client.update_member_status(image_id,
-                                                           self.alt_tenant_id,
-                                                           'rejected')
-        self.assertEqual(200, resp.status)
+        self.alt_img_client.update_member_status(image_id,
+                                                 self.alt_tenant_id,
+                                                 'rejected')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
 
     @test.attr(type='gate')
@@ -66,9 +62,8 @@
                                                  'accepted')
 
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        resp, member = self.os_img_client.get_member(image_id,
-                                                     self.alt_tenant_id)
-        self.assertEqual(200, resp.status)
+        _, member = self.os_img_client.get_member(image_id,
+                                                  self.alt_tenant_id)
         self.assertEqual(self.alt_tenant_id, member['member_id'])
         self.assertEqual(image_id, member['image_id'])
         self.assertEqual('accepted', member['status'])
@@ -83,18 +78,15 @@
                                                  'accepted')
 
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        resp = self.os_img_client.remove_member(image_id, self.alt_tenant_id)
-        self.assertEqual(204, resp.status)
+        self.os_img_client.remove_member(image_id, self.alt_tenant_id)
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
 
     @test.attr(type='gate')
     def test_get_image_member_schema(self):
-        resp, body = self.os_img_client.get_schema("member")
-        self.assertEqual(200, resp.status)
+        _, body = self.os_img_client.get_schema("member")
         self.assertEqual("member", body['name'])
 
     @test.attr(type='gate')
     def test_get_image_members_schema(self):
-        resp, body = self.os_img_client.get_schema("members")
-        self.assertEqual(200, resp.status)
+        _, body = self.os_img_client.get_schema("members")
         self.assertEqual("members", body['name'])
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
index 98ef649..7da6e65 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -21,8 +21,8 @@
     @test.attr(type=['negative', 'gate'])
     def test_image_share_invalid_status(self):
         image_id = self._create_image()
-        resp, member = self.os_img_client.add_member(image_id,
-                                                     self.alt_tenant_id)
+        _, member = self.os_img_client.add_member(image_id,
+                                                  self.alt_tenant_id)
         self.assertEqual(member['status'], 'pending')
         self.assertRaises(exceptions.BadRequest,
                           self.alt_img_client.update_member_status,
@@ -31,8 +31,8 @@
     @test.attr(type=['negative', 'gate'])
     def test_image_share_owner_cannot_accept(self):
         image_id = self._create_image()
-        resp, member = self.os_img_client.add_member(image_id,
-                                                     self.alt_tenant_id)
+        _, member = self.os_img_client.add_member(image_id,
+                                                  self.alt_tenant_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
         self.assertRaises(exceptions.Unauthorized,
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 27ba39c..722929e 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -52,11 +52,10 @@
     def test_get_delete_deleted_image(self):
         # get and delete the deleted image
         # create and delete image
-        resp, body = self.client.create_image(name='test',
-                                              container_format='bare',
-                                              disk_format='raw')
+        _, body = self.client.create_image(name='test',
+                                           container_format='bare',
+                                           disk_format='raw')
         image_id = body['id']
-        self.assertEqual(201, resp.status)
         self.client.delete_image(image_id)
         self.client.wait_for_resource_deletion(image_id)
 
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
index dec3353..a9db24b 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -21,23 +21,19 @@
 
     @test.attr(type='gate')
     def test_update_delete_tags_for_image(self):
-        resp, body = self.create_image(container_format='bare',
-                                       disk_format='raw',
-                                       visibility='private')
+        _, body = self.create_image(container_format='bare',
+                                    disk_format='raw',
+                                    visibility='private')
         image_id = body['id']
         tag = data_utils.rand_name('tag-')
         self.addCleanup(self.client.delete_image, image_id)
 
         # Creating image tag and verify it.
-        resp, body = self.client.add_image_tag(image_id, tag)
-        self.assertEqual(resp.status, 204)
-        resp, body = self.client.get_image(image_id)
-        self.assertEqual(resp.status, 200)
+        self.client.add_image_tag(image_id, tag)
+        _, body = self.client.get_image(image_id)
         self.assertIn(tag, body['tags'])
 
         # Deleting image tag and verify it.
-        resp = self.client.delete_image_tag(image_id, tag)
-        self.assertEqual(resp.status, 204)
-        resp, body = self.client.get_image(image_id)
-        self.assertEqual(resp.status, 200)
+        self.client.delete_image_tag(image_id, tag)
+        _, body = self.client.get_image(image_id)
         self.assertNotIn(tag, body['tags'])
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 13cfa0a..8e42b7c 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -33,10 +33,10 @@
     @test.attr(type=['negative', 'gate'])
     def test_delete_non_existing_tag(self):
         # Delete non existing tag.
-        resp, body = self.create_image(container_format='bare',
-                                       disk_format='raw',
-                                       visibility='private'
-                                       )
+        _, body = self.create_image(container_format='bare',
+                                    disk_format='raw',
+                                    visibility='private'
+                                    )
         image_id = body['id']
         tag = data_utils.rand_name('non-exist-tag-')
         self.addCleanup(self.client.delete_image, image_id)
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 53ca20d..73b4f3b 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -54,14 +54,18 @@
         self.assertEqual(resp['status'], '200')
         self.assertHeaders(resp, 'Object', 'HEAD')
         self.assertIn('x-delete-at', resp)
+        # we want to ensure that we will sleep long enough for things to
+        # actually expire, so figure out how many secs in the future that is.
+        sleepy_time = int(resp['x-delete-at']) - int(time.time())
+
         resp, body = self.object_client.get_object(self.container_name,
                                                    self.object_name)
         self.assertEqual(resp['status'], '200')
         self.assertHeaders(resp, 'Object', 'GET')
         self.assertIn('x-delete-at', resp)
 
-        # sleep for over 5 seconds, so that object expires
-        time.sleep(5)
+        # add a couple of seconds for safety.
+        time.sleep(sleepy_time + 3)
 
         # object should not be there anymore
         self.assertRaises(exceptions.NotFound, self.object_client.get_object,
@@ -69,10 +73,12 @@
 
     @test.attr(type='gate')
     def test_get_object_after_expiry_time(self):
-        metadata = {'X-Delete-After': '3'}
+        # the 10s is important, because the get calls can take 3s each
+        # some times
+        metadata = {'X-Delete-After': '10'}
         self._test_object_expiry(metadata)
 
     @test.attr(type='gate')
     def test_get_object_at_expiry_time(self):
-        metadata = {'X-Delete-At': str(int(time.time()) + 3)}
+        metadata = {'X-Delete-At': str(int(time.time()) + 10)}
         self._test_object_expiry(metadata)
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 06e63a4..1ef9aa1 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -13,11 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import cStringIO as StringIO
 import hashlib
 import random
 import re
 from six import moves
 import time
+import zlib
 
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
@@ -61,7 +63,7 @@
 
         return object_name, data_segments
 
-    @test.attr(type='smoke')
+    @test.attr(type='gate')
     def test_create_object(self):
         # create object
         object_name = data_utils.rand_name(name='TestObject')
@@ -76,7 +78,242 @@
         self.assertEqual(resp['status'], '201')
         self.assertHeaders(resp, 'Object', 'PUT')
 
-    @test.attr(type='smoke')
+        # check uploaded content
+        _, body = self.object_client.get_object(self.container_name,
+                                                object_name)
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_content_disposition(self):
+        # create object with content_disposition
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        metadata = {}
+        metadata['content-disposition'] = 'inline'
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        resp, body = self.object_client.get_object(
+            self.container_name,
+            object_name,
+            metadata=None)
+        self.assertIn('content-disposition', resp)
+        self.assertEqual(resp['content-disposition'], 'inline')
+        self.assertEqual(body, data)
+
+    @test.attr(type='gate')
+    def test_create_object_with_content_encoding(self):
+        # create object with content_encoding
+        object_name = data_utils.rand_name(name='TestObject')
+
+        # put compressed string
+        data_before = 'x' * 2000
+        data = zlib.compress(data_before)
+        metadata = {}
+        metadata['content-encoding'] = 'deflate'
+
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        # download compressed object
+        metadata = {}
+        metadata['accept-encoding'] = 'deflate'
+        resp, body = self.object_client.get_object(
+            self.container_name,
+            object_name,
+            metadata=metadata)
+        self.assertEqual(body, data_before)
+
+    @test.attr(type='gate')
+    def test_create_object_with_etag(self):
+        # create object with etag
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        md5 = hashlib.md5(data).hexdigest()
+        metadata = {'Etag': md5}
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        # check uploaded content
+        _, body = self.object_client.get_object(self.container_name,
+                                                object_name)
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_expect_continue(self):
+        # create object with expect_continue
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        metadata = {'Expect': '100-continue'}
+        resp = self.custom_object_client.create_object_continue(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata)
+
+        self.assertIn('status', resp)
+        self.assertEqual(resp['status'], '100')
+
+        self.custom_object_client.create_object_continue(
+            self.container_name,
+            object_name,
+            data,
+            metadata=None)
+
+        # check uploaded content
+        _, body = self.object_client.get_object(self.container_name,
+                                                object_name)
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_transfer_encoding(self):
+        # create object with transfer_encoding
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string(1024)
+        status, _, resp_headers = self.object_client.put_object_with_chunk(
+            container=self.container_name,
+            name=object_name,
+            contents=StringIO.StringIO(data),
+            chunk_size=512)
+        self.assertEqual(status, 201)
+        self.assertHeaders(resp_headers, 'Object', 'PUT')
+
+        # check uploaded content
+        _, body = self.object_client.get_object(self.container_name,
+                                                object_name)
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_x_fresh_metadata(self):
+        # create object with x_fresh_metadata
+        object_name_base = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        metadata_1 = {'X-Object-Meta-test-meta': 'Meta'}
+        self.object_client.create_object(self.container_name,
+                                         object_name_base,
+                                         data,
+                                         metadata=metadata_1)
+        object_name = data_utils.rand_name(name='TestObject')
+        metadata_2 = {'X-Copy-From': '%s/%s' % (self.container_name,
+                                                object_name_base),
+                      'X-Fresh-Metadata': 'true'}
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            '',
+            metadata=metadata_2)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        resp, body = self.object_client.get_object(self.container_name,
+                                                   object_name)
+        self.assertNotIn('x-object-meta-test-meta', resp)
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_x_object_meta(self):
+        # create object with object_meta
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        metadata = {'X-Object-Meta-test-meta': 'Meta'}
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        resp, body = self.object_client.get_object(self.container_name,
+                                                   object_name)
+        self.assertIn('x-object-meta-test-meta', resp)
+        self.assertEqual(resp['x-object-meta-test-meta'], 'Meta')
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_x_object_metakey(self):
+        # create object with the blank value of metadata
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        metadata = {'X-Object-Meta-test-meta': ''}
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        resp, body = self.object_client.get_object(self.container_name,
+                                                   object_name)
+        self.assertIn('x-object-meta-test-meta', resp)
+        self.assertEqual(resp['x-object-meta-test-meta'], '')
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_x_remove_object_meta(self):
+        # create object with x_remove_object_meta
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        metadata_add = {'X-Object-Meta-test-meta': 'Meta'}
+        self.object_client.create_object(self.container_name,
+                                         object_name,
+                                         data,
+                                         metadata=metadata_add)
+        metadata_remove = {'X-Remove-Object-Meta-test-meta': 'Meta'}
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata_remove)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        resp, body = self.object_client.get_object(self.container_name,
+                                                   object_name)
+        self.assertNotIn('x-object-meta-test-meta', resp)
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
+    def test_create_object_with_x_remove_object_metakey(self):
+        # create object with the blank value of remove metadata
+        object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        metadata_add = {'X-Object-Meta-test-meta': 'Meta'}
+        self.object_client.create_object(self.container_name,
+                                         object_name,
+                                         data,
+                                         metadata=metadata_add)
+        metadata_remove = {'X-Remove-Object-Meta-test-meta': ''}
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data,
+            metadata=metadata_remove)
+        self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
+        resp, body = self.object_client.get_object(self.container_name,
+                                                   object_name)
+        self.assertNotIn('x-object-meta-test-meta', resp)
+        self.assertEqual(data, body)
+
+    @test.attr(type='gate')
     def test_delete_object(self):
         # create object
         object_name = data_utils.rand_name(name='TestObject')
diff --git a/tempest/api_schema/compute/agents.py b/tempest/api_schema/compute/agents.py
index b04cf64..b9ad240 100644
--- a/tempest/api_schema/compute/agents.py
+++ b/tempest/api_schema/compute/agents.py
@@ -38,3 +38,24 @@
         'required': ['agents']
     }
 }
+
+common_create_agent = {
+    'type': 'object',
+    'properties': {
+        'agent': {
+            'type': 'object',
+            'properties': {
+                'agent_id': {'type': ['integer', 'string']},
+                'hypervisor': {'type': 'string'},
+                'os': {'type': 'string'},
+                'architecture': {'type': 'string'},
+                'version': {'type': 'string'},
+                'url': {'type': 'string', 'format': 'uri'},
+                'md5hash': {'type': 'string'}
+            },
+            'required': ['agent_id', 'hypervisor', 'os', 'architecture',
+                         'version', 'url', 'md5hash']
+        }
+    },
+    'required': ['agent']
+}
diff --git a/tempest/api_schema/compute/v2/agents.py b/tempest/api_schema/compute/v2/agents.py
index 837731f..30f999f 100644
--- a/tempest/api_schema/compute/v2/agents.py
+++ b/tempest/api_schema/compute/v2/agents.py
@@ -12,6 +12,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.api_schema.compute import agents
+
+create_agent = {
+    'status_code': [200],
+    'response_body': agents.common_create_agent
+}
+
 delete_agent = {
     'status_code': [200]
 }
diff --git a/tempest/api_schema/compute/v2/floating_ips.py b/tempest/api_schema/compute/v2/floating_ips.py
index 3ea6320..03e6aef 100644
--- a/tempest/api_schema/compute/v2/floating_ips.py
+++ b/tempest/api_schema/compute/v2/floating_ips.py
@@ -98,3 +98,22 @@
 add_remove_floating_ip = {
     'status_code': [202]
 }
+
+create_floating_ips_bulk = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'floating_ips_bulk_create': {
+                'type': 'object',
+                'properties': {
+                    'interface': {'type': ['string', 'null']},
+                    'ip_range': {'type': 'string'},
+                    'pool': {'type': ['string', 'null']},
+                },
+                'required': ['interface', 'ip_range', 'pool']
+            }
+        },
+        'required': ['floating_ips_bulk_create']
+    }
+}
diff --git a/tempest/api_schema/compute/v3/agents.py b/tempest/api_schema/compute/v3/agents.py
index 63d1c46..597a089 100644
--- a/tempest/api_schema/compute/v3/agents.py
+++ b/tempest/api_schema/compute/v3/agents.py
@@ -12,6 +12,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.api_schema.compute import agents
+
+create_agent = {
+    'status_code': [201],
+    'response_body': agents.common_create_agent
+}
+
 delete_agent = {
     'status_code': [204]
 }
diff --git a/tempest/auth.py b/tempest/auth.py
index 9c51edb..830dca9 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -213,7 +213,7 @@
         # build authenticated request
         # returns new request, it does not touch the original values
         _headers = copy.deepcopy(headers) if headers is not None else {}
-        _headers['X-Auth-Token'] = token
+        _headers['X-Auth-Token'] = str(token)
         if url is None or url == "":
             _url = base_url
         else:
@@ -223,7 +223,7 @@
             parts[2] = re.sub("/{2,}", "/", parts[2])
             _url = urlparse.urlunparse(parts)
         # no change to method or body
-        return _url, _headers, body
+        return str(_url), _headers, body
 
     def _auth_client(self):
         raise NotImplementedError
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 6aa98c4..0571f4f 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -83,6 +83,12 @@
         return self.cmd_with_auth(
             'cinder', action, flags, params, admin, fail_ok)
 
+    def swift(self, action, flags='', params='', admin=True, fail_ok=False):
+        """Executes swift command for the given action."""
+        flags += ' --os-endpoint-type %s' % CONF.object_storage.endpoint_type
+        return self.cmd_with_auth(
+            'swift', action, flags, params, admin, fail_ok)
+
     def neutron(self, action, flags='', params='', admin=True, fail_ok=False):
         """Executes neutron command for the given action."""
         flags += ' --endpoint-type %s' % CONF.network.endpoint_type
diff --git a/tempest/cli/simple_read_only/test_swift.py b/tempest/cli/simple_read_only/test_swift.py
new file mode 100644
index 0000000..6d6caa7
--- /dev/null
+++ b/tempest/cli/simple_read_only/test_swift.py
@@ -0,0 +1,95 @@
+# Copyright 2014 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 re
+import subprocess
+
+import tempest.cli
+from tempest import config
+
+CONF = config.CONF
+
+
+class SimpleReadOnlySwiftClientTest(tempest.cli.ClientTestBase):
+    """Basic, read-only tests for Swift CLI client.
+
+    Checks return values and output of read-only commands.
+    These tests do not presume any content, nor do they create
+    their own. They only verify the structure of output if present.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        if not CONF.service_available.swift:
+            msg = ("%s skipped as Swift is not available" % cls.__name__)
+            raise cls.skipException(msg)
+        super(SimpleReadOnlySwiftClientTest, cls).setUpClass()
+
+    def test_swift_fake_action(self):
+        self.assertRaises(subprocess.CalledProcessError,
+                          self.swift,
+                          'this-does-not-exist')
+
+    def test_swift_list(self):
+        self.swift('list')
+
+    def test_swift_stat(self):
+        output = self.swift('stat')
+        entries = ['Account', 'Containers', 'Objects', 'Bytes', 'Content-Type',
+                   'X-Timestamp', 'X-Trans-Id']
+        for entry in entries:
+            self.assertTrue(entry in output)
+
+    def test_swift_capabilities(self):
+        output = self.swift('capabilities')
+        entries = ['account_listing_limit', 'container_listing_limit',
+                   'max_file_size', 'Additional middleware']
+        for entry in entries:
+            self.assertTrue(entry in output)
+
+    def test_swift_help(self):
+        help_text = self.swift('', flags='--help')
+        lines = help_text.split('\n')
+        self.assertFirstLineStartsWith(lines, 'Usage: swift')
+
+        commands = []
+        cmds_start = lines.index('Positional arguments:')
+        cmds_end = lines.index('Examples:')
+        command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
+        for line in lines[cmds_start:cmds_end]:
+            match = command_pattern.match(line)
+            if match:
+                commands.append(match.group(1))
+        commands = set(commands)
+        wanted_commands = set(('stat', 'list', 'delete',
+                               'download', 'post', 'upload'))
+        self.assertFalse(wanted_commands - commands)
+
+    # Optional arguments:
+
+    def test_swift_version(self):
+        self.swift('', flags='--version')
+
+    def test_swift_debug_list(self):
+        self.swift('list', flags='--debug')
+
+    def test_swift_retries_list(self):
+        self.swift('list', flags='--retries 3')
+
+    def test_swift_region_list(self):
+        region = CONF.object_storage.region
+        if not region:
+            region = CONF.identity.region
+        self.swift('list', flags='--os-region-name ' + region)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 33128a9..9e0f4d3 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -18,6 +18,7 @@
 import json
 from lxml import etree
 import re
+import string
 import time
 
 import jsonschema
@@ -295,9 +296,11 @@
                     req_url,
                     secs,
                     str(req_headers),
-                    str(req_body)[:2048],
+                    filter(lambda x: x in string.printable,
+                           str(req_body)[:2048]),
                     str(resp),
-                    str(resp_body)[:2048]),
+                    filter(lambda x: x in string.printable,
+                           str(resp_body)[:2048])),
                 extra=extra)
 
     def _parse_resp(self, body):
@@ -369,7 +372,7 @@
         # The warning is normal for SHOULD/SHOULD NOT case
 
         # Likely it will cause an error
-        if not resp_body and resp.status >= 400:
+        if method != 'HEAD' and not resp_body and resp.status >= 400:
             self.LOG.warning("status >= 400 response with empty body")
 
     def _request(self, method, url, headers=None, body=None):
diff --git a/tempest/config.py b/tempest/config.py
index e3f0f2a..7d871cb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -310,7 +310,11 @@
     cfg.BoolOpt('rdp_console',
                 default=False,
                 help='Enable RDP console. This configuration value should '
-                     'be same as [nova.rdp]->enabled in nova.conf')
+                     'be same as [nova.rdp]->enabled in nova.conf'),
+    cfg.BoolOpt('rescue',
+                default=True,
+                help='Does the test environment support instance rescue '
+                     'mode?')
 ]
 
 
@@ -920,14 +924,14 @@
                default=300,
                help="Timeout for Ironic node to completely provision"),
     cfg.IntOpt('association_timeout',
-               default=10,
+               default=30,
                help="Timeout for association of Nova instance and Ironic "
                     "node"),
     cfg.IntOpt('power_timeout',
-               default=20,
+               default=60,
                help="Timeout for Ironic power transitions."),
     cfg.IntOpt('unprovision_timeout',
-               default=20,
+               default=60,
                help="Timeout for unprovisioning an Ironic node.")
 ]
 
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 183d422..4e35aaa 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -50,7 +50,7 @@
     T104: Scenario tests require a services decorator
     """
 
-    if 'tempest/scenario/test_' in filename:
+    if 'tempest/scenario/' in filename and '/test_' in filename:
         if TEST_DEFINITION.match(physical_line):
             if not SCENARIO_DECORATOR.match(previous_logical):
                 return (physical_line.find('def'),
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index c836227..07d8828 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -407,7 +407,16 @@
             username = CONF.scenario.ssh_user
         if private_key is None:
             private_key = self.keypair.private_key
-        return remote_client.RemoteClient(ip, username, pkey=private_key)
+        linux_client = remote_client.RemoteClient(ip, username,
+                                                  pkey=private_key)
+        try:
+            linux_client.validate_authentication()
+        except exceptions.SSHTimeout:
+            LOG.exception('ssh connection to %s failed' % ip)
+            debug.log_net_debug()
+            raise
+
+        return linux_client
 
     def _log_console_output(self, servers=None):
         if not servers:
@@ -869,9 +878,7 @@
                         msg=msg)
         if should_connect:
             # no need to check ssh for negative connectivity
-            linux_client = self.get_remote_client(ip_address, username,
-                                                  private_key)
-            linux_client.validate_authentication()
+            self.get_remote_client(ip_address, username, private_key)
 
     def _check_public_network_connectivity(self, ip_address, username,
                                            private_key, should_connect=True,
@@ -885,13 +892,15 @@
                                         username,
                                         private_key,
                                         should_connect=should_connect)
-        except Exception:
+        except Exception as e:
             ex_msg = 'Public network connectivity check failed'
             if msg:
                 ex_msg += ": " + msg
             LOG.exception(ex_msg)
             self._log_console_output(servers)
-            debug.log_net_debug()
+            # network debug is called as part of ssh init
+            if not isinstance(e, exceptions.SSHTimeout):
+                debug.log_net_debug()
             raise
 
     def _check_tenant_network_connectivity(self, server,
@@ -912,10 +921,12 @@
                                                 username,
                                                 private_key,
                                                 should_connect=should_connect)
-        except Exception:
+        except Exception as e:
             LOG.exception('Tenant network connectivity check failed')
             self._log_console_output(servers_for_debug)
-            debug.log_net_debug()
+            # network debug is called as part of ssh init
+            if not isinstance(e, exceptions.SSHTimeout):
+                debug.log_net_debug()
             raise
 
     def _check_remote_connectivity(self, source, dest, should_succeed=True):
diff --git a/tempest/scenario/test_load_balancer_basic.py b/tempest/scenario/test_load_balancer_basic.py
index 1c24b5c..03cfef5 100644
--- a/tempest/scenario/test_load_balancer_basic.py
+++ b/tempest/scenario/test_load_balancer_basic.py
@@ -148,7 +148,6 @@
             ssh_client = self.get_remote_client(
                 server_or_ip=ip,
                 private_key=private_key)
-            ssh_client.validate_authentication()
 
             # Write a backend's responce into a file
             resp = """HTTP/1.0 200 OK\r\nContent-Length: 8\r\n\r\n%s"""
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 58f0dbf..0406217 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -93,11 +93,12 @@
     def ssh_to_server(self):
         try:
             self.linux_client = self.get_remote_client(self.floating_ip.ip)
-            self.linux_client.validate_authentication()
-        except Exception:
+        except Exception as e:
             LOG.exception('ssh to server failed')
             self._log_console_output()
-            debug.log_net_debug()
+            # network debug is called as part of ssh init
+            if not isinstance(e, test.exceptions.SSHTimeout):
+                debug.log_net_debug()
             raise
 
     def check_partitions(self):
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index b4e509a..dd89dc0 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -336,6 +336,8 @@
             self.assertTrue(self._check_remote_connectivity(access_point, ip,
                                                             should_succeed),
                             msg)
+        except test.exceptions.SSHTimeout:
+            raise
         except Exception:
             debug.log_net_debug()
             raise
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index dc7a092..54f1d9e 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -93,11 +93,10 @@
             instance.add_floating_ip(floating_ip)
             # Check ssh
             try:
-                linux_client = self.get_remote_client(
+                self.get_remote_client(
                     server_or_ip=floating_ip.ip,
                     username=self.image_utils.ssh_user(self.image_ref),
                     private_key=self.keypair.private_key)
-                linux_client.validate_authentication()
             except Exception:
                 LOG.exception('ssh to server failed')
                 self._log_console_output()
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index ab335e2..d41490a 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -49,8 +49,9 @@
         try:
             return self.get_remote_client(server_or_ip)
         except Exception:
-            LOG.exception()
+            LOG.exception('Initializing SSH connection failed')
             self._log_console_output()
+            raise
 
     def _write_timestamp(self, server_or_ip):
         ssh_client = self._ssh_to_server(server_or_ip)
diff --git a/tempest/services/compute/json/agents_client.py b/tempest/services/compute/json/agents_client.py
index 98d8896..4f6602f 100644
--- a/tempest/services/compute/json/agents_client.py
+++ b/tempest/services/compute/json/agents_client.py
@@ -46,7 +46,9 @@
         """Create an agent build."""
         post_body = json.dumps({'agent': kwargs})
         resp, body = self.post('os-agents', post_body)
-        return resp, self._parse_resp(body)
+        body = json.loads(body)
+        self.validate_response(schema.create_agent, resp, body)
+        return resp, body['agent']
 
     def delete_agent(self, agent_id):
         """Delete an existing agent build."""
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 6fc2bc2..92b4ddf 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -123,6 +123,7 @@
         post_body = json.dumps({'floating_ips_bulk_create': post_body})
         resp, body = self.post('os-floating-ips-bulk', post_body)
         body = json.loads(body)
+        self.validate_response(schema.create_floating_ips_bulk, resp, body)
         return resp, body['floating_ips_bulk_create']
 
     def list_floating_ips_bulk(self):
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 23c1e64..e1661c0 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -432,8 +432,9 @@
         return self.action(server_id, 'shelveOffload', None, **kwargs)
 
     def get_console_output(self, server_id, length):
+        kwargs = {'length': length} if length else {}
         return self.action(server_id, 'os-getConsoleOutput', 'output',
-                           common_schema.get_console_output, length=length)
+                           common_schema.get_console_output, **kwargs)
 
     def list_virtual_interfaces(self, server_id):
         """
diff --git a/tempest/services/compute/v3/json/agents_client.py b/tempest/services/compute/v3/json/agents_client.py
index 48be54c..31314b7 100644
--- a/tempest/services/compute/v3/json/agents_client.py
+++ b/tempest/services/compute/v3/json/agents_client.py
@@ -43,7 +43,9 @@
         """Create an agent build."""
         post_body = json.dumps({'agent': kwargs})
         resp, body = self.post('os-agents', post_body)
-        return resp, self._parse_resp(body)
+        body = json.loads(body)
+        self.validate_response(schema.create_agent, resp, body)
+        return resp, body['agent']
 
     def delete_agent(self, agent_id):
         """Delete an existing agent build."""
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 626e655..156d889 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -594,8 +594,9 @@
         return resp, body
 
     def get_console_output(self, server_id, length):
+        kwargs = {'length': length} if length else {}
         return self.action(server_id, 'os-getConsoleOutput', 'output',
-                           length=length)
+                           **kwargs)
 
     def list_virtual_interfaces(self, server_id):
         """
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index e22cd9c..2353908 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -153,6 +153,7 @@
             return self._create_with_data(headers, kwargs.get('data'))
 
         resp, body = self.post('v1/images', None, headers)
+        self.expected_success(201, resp.status)
         body = json.loads(body)
         return resp, body['image']
 
@@ -176,12 +177,15 @@
 
         url = 'v1/images/%s' % image_id
         resp, body = self.put(url, data, headers)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         return resp, body['image']
 
     def delete_image(self, image_id):
         url = 'v1/images/%s' % image_id
-        return self.delete(url)
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        return resp, body
 
     def image_list(self, **kwargs):
         url = 'v1/images'
@@ -190,6 +194,7 @@
             url += '?%s' % urllib.urlencode(kwargs)
 
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         return resp, body['images']
 
@@ -210,18 +215,21 @@
             url += '?%s' % urllib.urlencode(kwargs)
 
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         return resp, body['images']
 
     def get_image_meta(self, image_id):
         url = 'v1/images/%s' % image_id
         resp, __ = self.head(url)
+        self.expected_success(200, resp.status)
         body = self._image_meta_from_headers(resp)
         return resp, body
 
     def get_image(self, image_id):
         url = 'v1/images/%s' % image_id
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         return resp, body
 
     def is_resource_deleted(self, id):
@@ -234,12 +242,14 @@
     def get_image_membership(self, image_id):
         url = 'v1/images/%s/members' % image_id
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         return resp, body
 
     def get_shared_images(self, member_id):
         url = 'v1/shared-images/%s' % member_id
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         return resp, body
 
@@ -249,20 +259,15 @@
         if can_share:
             body = json.dumps({'member': {'can_share': True}})
         resp, __ = self.put(url, body)
+        self.expected_success(204, resp.status)
         return resp
 
     def delete_member(self, member_id, image_id):
         url = 'v1/images/%s/members/%s' % (image_id, member_id)
         resp, __ = self.delete(url)
+        self.expected_success(204, resp.status)
         return resp
 
-    def replace_membership_list(self, image_id, member_list):
-        url = 'v1/images/%s/members' % image_id
-        body = json.dumps({'membership': member_list})
-        resp, data = self.put(url, body)
-        data = json.loads(data)
-        return resp, data
-
     # NOTE(afazekas): just for the wait function
     def _get_image_status(self, image_id):
         resp, meta = self.get_image_meta(image_id)
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 201869e..c420df9 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -61,6 +61,7 @@
         headers = {"Content-Type": "application/openstack-images-v2.0"
                                    "-json-patch"}
         resp, body = self.patch('v2/images/%s' % image_id, data, headers)
+        self.expected_success(200, resp.status)
         return resp, self._parse_resp(body)
 
     def create_image(self, name, container_format, disk_format, **kwargs):
@@ -81,12 +82,14 @@
         self._validate_schema(data)
 
         resp, body = self.post('v2/images', data)
+        self.expected_success(201, resp.status)
         body = json.loads(body)
         return resp, body
 
     def delete_image(self, image_id):
         url = 'v2/images/%s' % image_id
-        self.delete(url)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
 
     def image_list(self, params=None):
         url = 'v2/images'
@@ -95,6 +98,7 @@
             url += '?%s' % urllib.urlencode(params)
 
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         self._validate_schema(body, type='images')
         return resp, body['images']
@@ -102,6 +106,7 @@
     def get_image(self, image_id):
         url = 'v2/images/%s' % image_id
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         return resp, body
 
@@ -117,36 +122,40 @@
         headers = {'Content-Type': 'application/octet-stream'}
         resp, body = self.http.raw_request('PUT', url, headers=headers,
                                            body=data)
+        self.expected_success(204, resp.status)
         return resp, body
 
     def get_image_file(self, image_id):
         url = 'v2/images/%s/file' % image_id
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         return resp, body
 
     def add_image_tag(self, image_id, tag):
         url = 'v2/images/%s/tags/%s' % (image_id, tag)
         resp, body = self.put(url, body=None)
+        self.expected_success(204, resp.status)
         return resp, body
 
     def delete_image_tag(self, image_id, tag):
         url = 'v2/images/%s/tags/%s' % (image_id, tag)
         resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
         return resp
 
     def get_image_membership(self, image_id):
         url = 'v2/images/%s/members' % image_id
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
-        self.expected_success(200, resp)
         return resp, body
 
     def add_member(self, image_id, member_id):
         url = 'v2/images/%s/members' % image_id
         data = json.dumps({'member': member_id})
         resp, body = self.post(url, data)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
-        self.expected_success(200, resp)
         return resp, body
 
     def update_member_status(self, image_id, member_id, status):
@@ -154,24 +163,25 @@
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
         data = json.dumps({'status': status})
         resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
-        self.expected_success(200, resp)
         return resp, body
 
     def get_member(self, image_id, member_id):
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
         resp, body = self.get(url)
-        self.expected_success(200, resp)
+        self.expected_success(200, resp.status)
         return resp, json.loads(body)
 
     def remove_member(self, image_id, member_id):
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
         resp, _ = self.delete(url)
-        self.expected_success(204, resp)
+        self.expected_success(204, resp.status)
         return resp
 
     def get_schema(self, schema):
         url = 'v2/schemas/%s' % schema
         resp, body = self.get(url)
+        self.expected_success(200, resp.status)
         body = json.loads(body)
         return resp, body
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index f3f4eb6..b2f8205 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -13,7 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import httplib
 import urllib
+import urlparse
 
 from tempest.common import http
 from tempest.common import rest_client
@@ -143,6 +145,31 @@
         resp, body = self.put(url, data)
         return resp, body
 
+    def put_object_with_chunk(self, container, name, contents, chunk_size):
+        """
+        Put an object with Transfer-Encoding header
+        """
+        if self.base_url is None:
+            self._set_auth()
+
+        headers = {'Transfer-Encoding': 'chunked'}
+        if self.token:
+            headers['X-Auth-Token'] = self.token
+
+        conn = put_object_connection(self.base_url, container, name, contents,
+                                     chunk_size, headers)
+
+        resp = conn.getresponse()
+        body = resp.read()
+
+        resp_headers = {}
+        for header, value in resp.getheaders():
+            resp_headers[header.lower()] = value
+
+        self._error_checker('PUT', None, headers, contents, resp, body)
+
+        return resp.status, resp.reason, resp_headers
+
 
 class ObjectClientCustomizedHeader(rest_client.RestClient):
 
@@ -220,3 +247,89 @@
         url = "%s/%s" % (str(container), str(object_name))
         resp, body = self.delete(url, headers=headers)
         return resp, body
+
+    def create_object_continue(self, container, object_name,
+                               data, metadata=None):
+        """Create storage object."""
+        headers = {}
+        if metadata:
+            for key in metadata:
+                headers[str(key)] = metadata[key]
+
+        if not data:
+            headers['content-length'] = '0'
+
+        if self.base_url is None:
+            self._set_auth()
+        headers['X-Auth-Token'] = self.token
+
+        conn = put_object_connection(self.base_url, str(container),
+                                     str(object_name), data, None, headers)
+
+        response = conn.response_class(conn.sock,
+                                       strict=conn.strict,
+                                       method=conn._method)
+        version, status, reason = response._read_status()
+        resp = {'version': version,
+                'status': str(status),
+                'reason': reason}
+
+        return resp
+
+
+def put_object_connection(base_url, container, name, contents=None,
+                          chunk_size=65536, headers=None, query_string=None):
+    """
+    Helper function to make connection to put object with httplib
+    :param base_url: base_url of an object client
+    :param container: container name that the object is in
+    :param name: object name to put
+    :param contents: a string or a file like object to read object data
+                     from; if None, a zero-byte put will be done
+    :param chunk_size: chunk size of data to write; it defaults to 65536;
+                       used only if the the contents object has a 'read'
+                       method, eg. file-like objects, ignored otherwise
+    :param headers: additional headers to include in the request, if any
+    :param query_string: if set will be appended with '?' to generated path
+    """
+    parsed = urlparse.urlparse(base_url)
+    if parsed.scheme == 'https':
+        conn = httplib.HTTPSConnection(parsed.netloc)
+    else:
+        conn = httplib.HTTPConnection(parsed.netloc)
+    path = str(parsed.path) + "/"
+    path += "%s/%s" % (str(container), str(name))
+
+    if query_string:
+        path += '?' + query_string
+    if headers:
+        headers = dict(headers)
+    else:
+        headers = {}
+    if hasattr(contents, 'read'):
+        conn.putrequest('PUT', path)
+        for header, value in headers.iteritems():
+            conn.putheader(header, value)
+        if 'Content-Length' not in headers:
+            if 'Transfer-Encoding' not in headers:
+                conn.putheader('Transfer-Encoding', 'chunked')
+            conn.endheaders()
+            chunk = contents.read(chunk_size)
+            while chunk:
+                conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
+                chunk = contents.read(chunk_size)
+            conn.send('0\r\n\r\n')
+        else:
+            conn.endheaders()
+            left = headers['Content-Length']
+            while left > 0:
+                size = chunk_size
+                if size > left:
+                    size = left
+                chunk = contents.read(size)
+                conn.send(chunk)
+                left -= len(chunk)
+    else:
+        conn.request('PUT', path, contents, headers)
+
+    return conn
diff --git a/tempest/test.py b/tempest/test.py
index 73dc870..650fad7 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -257,6 +257,12 @@
 
     network_resources = {}
 
+    # NOTE(sdague): log_format is defined inline here instead of using the oslo
+    # default because going through the config path recouples config to the
+    # stress tests too early, and depending on testr order will fail unit tests
+    log_format = ('%(asctime)s %(process)d %(levelname)-8s '
+                  '[%(name)s] %(message)s')
+
     @classmethod
     def setUpClass(cls):
         if hasattr(super(BaseTestCase, cls), 'setUpClass'):
@@ -294,9 +300,8 @@
             self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
         if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
             os.environ.get('OS_LOG_CAPTURE') != '0'):
-            log_format = '%(asctime)-15s %(message)s'
             self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
-                                                   format=log_format,
+                                                   format=self.log_format,
                                                    level=None))
 
     @classmethod
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index ab81836..91c3274 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -70,9 +70,15 @@
         self.assertFalse(checks.scenario_tests_need_service_tags(
             'def test_fake_test:', './tempest/api/compute/test_fake.py',
             "@test.services('image')"))
+        self.assertFalse(checks.scenario_tests_need_service_tags(
+            'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
+            "@test.services('compute')"))
         self.assertTrue(checks.scenario_tests_need_service_tags(
             'def test_fake_test:', './tempest/scenario/test_fake.py',
             '\n'))
+        self.assertTrue(checks.scenario_tests_need_service_tags(
+            'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
+            "\n"))
 
     def test_no_vi_headers(self):
         # NOTE(mtreinish)  The lines parameter is used only for finding the
diff --git a/tempest/tests/test_ssh.py b/tempest/tests/test_ssh.py
index 0da52dc..bf1f553 100644
--- a/tempest/tests/test_ssh.py
+++ b/tempest/tests/test_ssh.py
@@ -90,8 +90,8 @@
         start_time = int(time.time())
         client._get_ssh_connection(sleep=1)
         end_time = int(time.time())
-        self.assertTrue((end_time - start_time) < 3)
-        self.assertTrue((end_time - start_time) > 1)
+        self.assertLess((end_time - start_time), 4)
+        self.assertGreater((end_time - start_time), 1)
 
     def test_get_ssh_connection_timeout(self):
         c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks()
@@ -108,8 +108,8 @@
         with testtools.ExpectedException(exceptions.SSHTimeout):
             client._get_ssh_connection()
         end_time = int(time.time())
-        self.assertTrue((end_time - start_time) < 4)
-        self.assertTrue((end_time - start_time) >= 2)
+        self.assertLess((end_time - start_time), 5)
+        self.assertGreaterEqual((end_time - start_time), 2)
 
     def test_exec_command(self):
         gsc_mock = self.patch('tempest.common.ssh.Client._get_ssh_connection')
diff --git a/test-requirements.txt b/test-requirements.txt
index f63d34e..215f28b 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 hacking>=0.9.2,<0.10
 # needed for doc build
 docutils==0.9.1
-sphinx>=1.2.1,<1.3
+sphinx>=1.1.2,!=1.2.0,<1.3
 python-subunit>=0.0.18
 oslosphinx
 mox>=0.5.3