Merge "Negative test autogeneration framework"
diff --git a/tempest/api/compute/v3/servers/test_server_metadata_negative.py b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
index 4062696..2c413db 100644
--- a/tempest/api/compute/v3/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
@@ -18,12 +18,12 @@
 from tempest import test
 
 
-class ServerMetadataV3NegativeTestJSON(base.BaseV3ComputeTest):
+class ServerMetadataV3NegativeTest(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ServerMetadataV3NegativeTestJSON, cls).setUpClass()
+        super(ServerMetadataV3NegativeTest, cls).setUpClass()
         cls.client = cls.servers_client
         cls.quotas = cls.quotas_client
         cls.admin_client = cls._get_identity_admin_client()
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
new file mode 100644
index 0000000..5fde76a
--- /dev/null
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -0,0 +1,136 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NTT Corporation
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import tarfile
+import tempfile
+
+from tempest.api.object_storage import base
+from tempest.common import custom_matchers
+from tempest import test
+
+
+class BulkTest(base.BaseObjectTest):
+
+    def setUp(self):
+        super(BulkTest, self).setUp()
+        self.containers = []
+
+    def tearDown(self):
+        self.delete_containers(self.containers)
+        super(BulkTest, self).tearDown()
+
+    def _create_archive(self):
+        # Create an archived file for bulk upload testing.
+        # Directory and files contained in the directory correspond to
+        # container and subsidiary objects.
+        tmp_dir = tempfile.mkdtemp()
+        tmp_file = tempfile.mkstemp(dir=tmp_dir)
+
+        # Extract a container name and an object name
+        container_name = tmp_dir.split("/")[-1]
+        object_name = tmp_file[1].split("/")[-1]
+
+        # Create tar file
+        tarpath = tempfile.NamedTemporaryFile(suffix=".tar")
+        tar = tarfile.open(None, 'w', tarpath)
+        tar.add(tmp_dir, arcname=container_name)
+        tar.close()
+        tarpath.flush()
+
+        return tarpath.name, container_name, object_name
+
+    @test.attr(type='gate')
+    def test_extract_archive(self):
+        # Test bulk operation of file upload with an archived file
+        filepath, container_name, object_name = self._create_archive()
+
+        params = {'extract-archive': 'tar'}
+        with open(filepath) as fh:
+            mydata = fh.read()
+            resp, body = self.account_client.create_account(data=mydata,
+                                                            params=params)
+
+        self.containers.append(container_name)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+
+        # When uploading an archived file with the bulk operation, the response
+        # does not contain 'content-length' header. This is the special case,
+        # therefore the existence of response headers is checked without
+        # custom matcher.
+        self.assertIn('transfer-encoding', resp)
+        self.assertIn('content-type', resp)
+        self.assertIn('x-trans-id', resp)
+        self.assertIn('date', resp)
+
+        # Check only the format of common headers with custom matcher
+        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+
+        param = {'format': 'json'}
+        resp, body = self.account_client.list_account_containers(param)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Account', 'GET')
+
+        self.assertIn(container_name, [b['name'] for b in body])
+
+        param = {'format': 'json'}
+        resp, contents_list = self.container_client.list_container_contents(
+            container_name, param)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+
+        self.assertIn(object_name, [c['name'] for c in contents_list])
+
+    @test.attr(type='gate')
+    def test_bulk_delete(self):
+        # Test bulk operation of deleting multiple files
+        filepath, container_name, object_name = self._create_archive()
+
+        params = {'extract-archive': 'tar'}
+        with open(filepath) as fh:
+            mydata = fh.read()
+            resp, body = self.account_client.create_account(data=mydata,
+                                                            params=params)
+
+        data = '%s/%s\n%s' % (container_name, object_name, container_name)
+        params = {'bulk-delete': ''}
+        resp, body = self.account_client.delete_account(data=data,
+                                                        params=params)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+
+        # When deleting multiple files using the bulk operation, the response
+        # does not contain 'content-length' header. This is the special case,
+        # therefore the existence of response headers is checked without
+        # custom matcher.
+        self.assertIn('transfer-encoding', resp)
+        self.assertIn('content-type', resp)
+        self.assertIn('x-trans-id', resp)
+        self.assertIn('date', resp)
+
+        # Check only the format of common headers with custom matcher
+        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+
+        # Check if a container is deleted
+        param = {'format': 'txt'}
+        resp, body = self.account_client.list_account_containers(param)
+
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Account', 'GET')
+
+        self.assertNotIn(container_name, body)
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 18199ef..84cc91e 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -20,15 +20,32 @@
 
 
 class ContainerTest(base.BaseObjectTest):
-    @classmethod
-    def setUpClass(cls):
-        super(ContainerTest, cls).setUpClass()
-        cls.containers = []
+    def setUp(self):
+        super(ContainerTest, self).setUp()
+        self.containers = []
 
-    @classmethod
-    def tearDownClass(cls):
-        cls.delete_containers(cls.containers)
-        super(ContainerTest, cls).tearDownClass()
+    def tearDown(self):
+        self.delete_containers(self.containers)
+        super(ContainerTest, self).tearDown()
+
+    def _create_container(self):
+        # setup container
+        container_name = data_utils.rand_name(name='TestContainer')
+        self.container_client.create_container(container_name)
+        self.containers.append(container_name)
+
+        return container_name
+
+    def _create_object(self, container_name, object_name=None):
+        # setup object
+        if object_name is None:
+            object_name = data_utils.rand_name(name='TestObject')
+        data = data_utils.arbitrary_string()
+        self.object_client.create_object(container_name,
+                                         object_name,
+                                         data)
+
+        return object_name
 
     @attr(type='smoke')
     def test_create_container(self):
@@ -39,12 +56,97 @@
         self.assertHeaders(resp, 'Container', 'PUT')
 
     @attr(type='smoke')
+    def test_create_container_overwrite(self):
+        # overwrite container with the same name
+        container_name = data_utils.rand_name(name='TestContainer')
+        self.container_client.create_container(container_name)
+        self.containers.append(container_name)
+
+        resp, _ = self.container_client.create_container(container_name)
+        self.assertIn(resp['status'], ('202', '201'))
+        self.assertHeaders(resp, 'Container', 'PUT')
+
+    @attr(type='smoke')
+    def test_create_container_with_metadata_key(self):
+        # create container with the blank value of metadata
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata = {'test-container-meta': ''}
+        resp, _ = self.container_client.create_container(
+            container_name,
+            metadata=metadata)
+        self.containers.append(container_name)
+        self.assertIn(resp['status'], ('201', '202'))
+        self.assertHeaders(resp, 'Container', 'PUT')
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        # if the value of metadata is blank, metadata is not registered
+        # in the server
+        self.assertNotIn('x-container-meta-test-container-meta', resp)
+
+    @attr(type='smoke')
+    def test_create_container_with_metadata_value(self):
+        # create container with metadata value
+        container_name = data_utils.rand_name(name='TestContainer')
+
+        metadata = {'test-container-meta': 'Meta1'}
+        resp, _ = self.container_client.create_container(
+            container_name,
+            metadata=metadata)
+        self.containers.append(container_name)
+        self.assertIn(resp['status'], ('201', '202'))
+        self.assertHeaders(resp, 'Container', 'PUT')
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertIn('x-container-meta-test-container-meta', resp)
+        self.assertEqual(resp['x-container-meta-test-container-meta'],
+                         metadata['test-container-meta'])
+
+    @attr(type='smoke')
+    def test_create_container_with_remove_metadata_key(self):
+        # create container with the blank value of remove metadata
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata_1 = {'test-container-meta': 'Meta1'}
+        self.container_client.create_container(
+            container_name,
+            metadata=metadata_1)
+        self.containers.append(container_name)
+
+        metadata_2 = {'test-container-meta': ''}
+        resp, _ = self.container_client.create_container(
+            container_name,
+            remove_metadata=metadata_2)
+        self.assertIn(resp['status'], ('201', '202'))
+        self.assertHeaders(resp, 'Container', 'PUT')
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertNotIn('x-container-meta-test-container-meta', resp)
+
+    @attr(type='smoke')
+    def test_create_container_with_remove_metadata_value(self):
+        # create container with remove metadata
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata = {'test-container-meta': 'Meta1'}
+        self.container_client.create_container(container_name,
+                                               metadata=metadata)
+        self.containers.append(container_name)
+
+        resp, _ = self.container_client.create_container(
+            container_name,
+            remove_metadata=metadata)
+        self.assertIn(resp['status'], ('201', '202'))
+        self.assertHeaders(resp, 'Container', 'PUT')
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertNotIn('x-container-meta-test-container-meta', resp)
+
+    @attr(type='smoke')
     def test_delete_container(self):
         # create a container
-        container_name = data_utils.rand_name(name='TestContainer')
-        resp, _ = self.container_client.create_container(container_name)
-        self.assertHeaders(resp, 'Container', 'PUT')
-        self.containers.append(container_name)
+        container_name = self._create_container()
         # delete container
         resp, _ = self.container_client.delete_container(container_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
@@ -53,82 +155,280 @@
         self.containers.remove(container_name)
 
     @attr(type='smoke')
-    def test_list_container_contents_json(self):
-        # add metadata to an object
-
-        # create a container
-        container_name = data_utils.rand_name(name='TestContainer')
-        resp, _ = self.container_client.create_container(container_name)
-        self.assertHeaders(resp, 'Container', 'PUT')
-        self.containers.append(container_name)
-        # create object
-        object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
-        resp, _ = self.object_client.create_object(container_name,
-                                                   object_name, data)
-        self.assertHeaders(resp, 'Object', 'PUT')
-        # set object metadata
-        meta_key = data_utils.rand_name(name='Meta-Test-')
-        meta_value = data_utils.rand_name(name='MetaValue-')
-        orig_metadata = {meta_key: meta_value}
-        resp, _ = self.object_client.update_object_metadata(container_name,
-                                                            object_name,
-                                                            orig_metadata)
-        self.assertHeaders(resp, 'Object', 'POST')
+    def test_list_container_contents(self):
         # get container contents list
+        container_name = self._create_container()
+        object_name = self._create_object(container_name)
+
+        resp, object_list = self.container_client.list_container_contents(
+            container_name)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual(object_name, object_list.strip('\n'))
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_no_object(self):
+        # get empty container contents list
+        container_name = self._create_container()
+
+        resp, object_list = self.container_client.list_container_contents(
+            container_name)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual('', object_list.strip('\n'))
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_delimiter(self):
+        # get container contents list using delimiter param
+        container_name = self._create_container()
+        object_name = data_utils.rand_name(name='TestObject/')
+        self._create_object(container_name, object_name)
+
+        params = {'delimiter': '/'}
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual(object_name.split('/')[0], object_list.strip('/\n'))
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_end_marker(self):
+        # get container contents list using end_marker param
+        container_name = self._create_container()
+        object_name = self._create_object(container_name)
+
+        params = {'end_marker': 'ZzzzObject1234567890'}
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual(object_name, object_list.strip('\n'))
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_format_json(self):
+        # get container contents list using format_json param
+        container_name = self._create_container()
+        self._create_object(container_name)
+
         params = {'format': 'json'}
-        resp, object_list = \
-            self.container_client.\
-            list_container_contents(container_name, params=params)
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertHeaders(resp, 'Container', 'GET')
 
         self.assertIsNotNone(object_list)
-
-        object_names = [obj['name'] for obj in object_list]
-        self.assertIn(object_name, object_names)
+        self.assertTrue([c['name'] for c in object_list])
+        self.assertTrue([c['hash'] for c in object_list])
+        self.assertTrue([c['bytes'] for c in object_list])
+        self.assertTrue([c['content_type'] for c in object_list])
+        self.assertTrue([c['last_modified'] for c in object_list])
 
     @attr(type='smoke')
-    def test_container_metadata(self):
-        # update/retrieve/delete container metadata
+    def test_list_container_contents_with_format_xml(self):
+        # get container contents list using format_xml param
+        container_name = self._create_container()
+        self._create_object(container_name)
 
-        # create a container
-        container_name = data_utils.rand_name(name='TestContainer')
-        resp, _ = self.container_client.create_container(container_name)
-        self.assertHeaders(resp, 'Container', 'PUT')
-        self.containers.append(container_name)
-        # update container metadata
-        metadata = {'name': 'Pictures',
-                    'description': 'Travel'
-                    }
-        resp, _ = \
-            self.container_client.update_container_metadata(container_name,
-                                                            metadata=metadata)
+        params = {'format': 'xml'}
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
-        self.assertHeaders(resp, 'Container', 'POST')
+        self.assertHeaders(resp, 'Container', 'GET')
 
-        # list container metadata
+        self.assertIsNotNone(object_list)
+        self.assertEqual(object_list.tag, 'container')
+        self.assertTrue('name' in object_list.keys())
+        self.assertEqual(object_list.find(".//object").tag, 'object')
+        self.assertEqual(object_list.find(".//name").tag, 'name')
+        self.assertEqual(object_list.find(".//hash").tag, 'hash')
+        self.assertEqual(object_list.find(".//bytes").tag, 'bytes')
+        self.assertEqual(object_list.find(".//content_type").tag,
+                         'content_type')
+        self.assertEqual(object_list.find(".//last_modified").tag,
+                         'last_modified')
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_limit(self):
+        # get container contents list using limit param
+        container_name = self._create_container()
+        object_name = self._create_object(container_name)
+
+        params = {'limit': data_utils.rand_int_id(1, 10000)}
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual(object_name, object_list.strip('\n'))
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_marker(self):
+        # get container contents list using marker param
+        container_name = self._create_container()
+        object_name = self._create_object(container_name)
+
+        params = {'marker': 'AaaaObject1234567890'}
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual(object_name, object_list.strip('\n'))
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_path(self):
+        # get container contents list using path param
+        container_name = self._create_container()
+        object_name = data_utils.rand_name(name='Swift/TestObject')
+        self._create_object(container_name, object_name)
+
+        params = {'path': 'Swift'}
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual(object_name, object_list.strip('\n'))
+
+    @attr(type='smoke')
+    def test_list_container_contents_with_prefix(self):
+        # get container contents list using prefix param
+        container_name = self._create_container()
+        object_name = self._create_object(container_name)
+
+        prefix_key = object_name[0:8]
+        params = {'prefix': prefix_key}
+        resp, object_list = self.container_client.list_container_contents(
+            container_name,
+            params=params)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+        self.assertEqual(object_name, object_list.strip('\n'))
+
+    @attr(type='smoke')
+    def test_list_container_metadata(self):
+        # List container metadata
+        container_name = self._create_container()
+
+        metadata = {'name': 'Pictures'}
+        self.container_client.update_container_metadata(
+            container_name,
+            metadata=metadata)
+
         resp, _ = self.container_client.list_container_metadata(
             container_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertHeaders(resp, 'Container', 'HEAD')
-
         self.assertIn('x-container-meta-name', resp)
-        self.assertIn('x-container-meta-description', resp)
-        self.assertEqual(resp['x-container-meta-name'], 'Pictures')
-        self.assertEqual(resp['x-container-meta-description'], 'Travel')
+        self.assertEqual(resp['x-container-meta-name'], metadata['name'])
 
-        # delete container metadata
-        resp, _ = self.container_client.delete_container_metadata(
+    @attr(type='smoke')
+    def test_list_no_container_metadata(self):
+        # HEAD container without metadata
+        container_name = self._create_container()
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'HEAD')
+        self.assertNotIn('x-container-meta-', str(resp))
+
+    @attr(type='smoke')
+    def test_update_container_metadata_with_create_and_delete_matadata(self):
+        # Send one request of adding and deleting metadata
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata_1 = {'test-container-meta1': 'Meta1'}
+        self.container_client.create_container(container_name,
+                                               metadata=metadata_1)
+        self.containers.append(container_name)
+
+        metadata_2 = {'test-container-meta2': 'Meta2'}
+        resp, _ = self.container_client.update_container_metadata(
             container_name,
-            metadata=metadata.keys())
+            metadata=metadata_2,
+            remove_metadata=metadata_1)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
         self.assertHeaders(resp, 'Container', 'POST')
 
-        # check if the metadata are no longer there
-        resp, _ = self.container_client.list_container_metadata(container_name)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
-        self.assertHeaders(resp, 'Container', 'HEAD')
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertNotIn('x-container-meta-test-container-meta1', resp)
+        self.assertIn('x-container-meta-test-container-meta2', resp)
+        self.assertEqual(resp['x-container-meta-test-container-meta2'],
+                         metadata_2['test-container-meta2'])
 
-        self.assertNotIn('x-container-meta-name', resp)
-        self.assertNotIn('x-container-meta-description', resp)
+    @attr(type='smoke')
+    def test_update_container_metadata_with_create_metadata(self):
+        # update container metadata using add metadata
+        container_name = self._create_container()
+
+        metadata = {'test-container-meta1': 'Meta1'}
+        resp, _ = self.container_client.update_container_metadata(
+            container_name,
+            metadata=metadata)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'POST')
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertIn('x-container-meta-test-container-meta1', resp)
+        self.assertEqual(resp['x-container-meta-test-container-meta1'],
+                         metadata['test-container-meta1'])
+
+    @attr(type='smoke')
+    def test_update_container_metadata_with_delete_metadata(self):
+        # update container metadata using delete metadata
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata = {'test-container-meta1': 'Meta1'}
+        self.container_client.create_container(container_name,
+                                               metadata=metadata)
+        self.containers.append(container_name)
+
+        resp, _ = self.container_client.delete_container_metadata(
+            container_name,
+            metadata=metadata)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'POST')
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertNotIn('x-container-meta-test-container-meta1', resp)
+
+    @attr(type='smoke')
+    def test_update_container_metadata_with_create_matadata_key(self):
+        # update container metadata with a blenk value of metadata
+        container_name = self._create_container()
+
+        metadata = {'test-container-meta1': ''}
+        resp, _ = self.container_client.update_container_metadata(
+            container_name,
+            metadata=metadata)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'POST')
+
+        resp, _ = self.container_client.list_container_metadata(
+            container_name)
+        self.assertNotIn('x-container-meta-test-container-meta1', resp)
+
+    @attr(type='smoke')
+    def test_update_container_metadata_with_delete_metadata_key(self):
+        # update container metadata with a blank value of matadata
+        container_name = data_utils.rand_name(name='TestContainer')
+        metadata = {'test-container-meta1': 'Meta1'}
+        self.container_client.create_container(container_name,
+                                               metadata=metadata)
+        self.containers.append(container_name)
+
+        metadata = {'test-container-meta1': ''}
+        resp, _ = self.container_client.delete_container_metadata(
+            container_name,
+            metadata=metadata)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'POST')
+
+        resp, _ = self.container_client.list_container_metadata(container_name)
+        self.assertNotIn('x-container-meta-test-container-meta1', resp)
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 3211ef8..12fda92 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -64,6 +64,19 @@
                                                           status)
         super(SnapshotsActionsTest, self).tearDown()
 
+    def _create_reset_and_force_delete_temp_snapshot(self, status=None):
+        # Create snapshot, reset snapshot status,
+        # and force delete temp snapshot
+        temp_snapshot = self.create_snapshot(self.volume['id'])
+        if status:
+            resp, body = self.admin_snapshots_client.\
+                reset_snapshot_status(temp_snapshot['id'], status)
+            self.assertEqual(202, resp.status)
+        resp_delete, volume_delete = self.admin_snapshots_client.\
+            force_delete_snapshot(temp_snapshot['id'])
+        self.assertEqual(202, resp_delete.status)
+        self.client.wait_for_resource_deletion(temp_snapshot['id'])
+
     def _get_progress_alias(self):
         return 'os-extended-snapshot-attributes:progress'
 
@@ -99,6 +112,26 @@
         self.assertEqual(status, snapshot_get['status'])
         self.assertEqual(progress, snapshot_get[progress_alias])
 
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_creating(self):
+        # test force delete when status of snapshot is creating
+        self._create_reset_and_force_delete_temp_snapshot('creating')
+
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_deleting(self):
+        # test force delete when status of snapshot is deleting
+        self._create_reset_and_force_delete_temp_snapshot('deleting')
+
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_error(self):
+        # test force delete when status of snapshot is error
+        self._create_reset_and_force_delete_temp_snapshot('error')
+
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_error_deleting(self):
+        # test force delete when status of snapshot is error_deleting
+        self._create_reset_and_force_delete_temp_snapshot('error_deleting')
+
 
 class SnapshotsActionsTestXML(SnapshotsActionsTest):
     _interface = "xml"
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index a02c967..4a7921f 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -131,6 +131,8 @@
                 return InvalidFormat(key, value)
             elif key == 'etag' and not value.isalnum():
                 return InvalidFormat(key, value)
+            elif key == 'transfer-encoding' and not value == 'chunked':
+                return InvalidFormat(key, value)
 
         return None
 
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index dd6e730..d80d4c0 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -291,8 +291,8 @@
     def get(self, url, headers=None):
         return self.request('GET', url, headers)
 
-    def delete(self, url, headers=None):
-        return self.request('DELETE', url, headers)
+    def delete(self, url, headers=None, body=None):
+        return self.request('DELETE', url, headers, body)
 
     def patch(self, url, body, headers):
         return self.request('PATCH', url, headers, body)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index be0e045..020a256 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -196,7 +196,8 @@
             floating_ip = self._create_floating_ip(server, public_network_id)
             self.floating_ips[floating_ip] = server
 
-    def _check_public_network_connectivity(self, should_connect=True):
+    def _check_public_network_connectivity(self, should_connect=True,
+                                           msg=None):
         # The target login is assumed to have been configured for
         # key-based authentication by cloud-init.
         ssh_login = CONF.compute.image_ssh_user
@@ -212,7 +213,10 @@
                                             private_key,
                                             should_connect=should_connect)
         except Exception:
-            LOG.exception('Public network connectivity check failed')
+            ex_msg = 'Public network connectivity check failed'
+            if msg:
+                ex_msg += ": " + msg
+            LOG.exception(ex_msg)
             self._log_console_output(servers=self.servers.keys())
             debug.log_ip_ns()
             raise
@@ -242,6 +246,10 @@
         self._check_tenant_network_connectivity()
         self._check_public_network_connectivity(should_connect=True)
         self._disassociate_floating_ips()
-        self._check_public_network_connectivity(should_connect=False)
+        self._check_public_network_connectivity(should_connect=False,
+                                                msg="after disassociate "
+                                                    "floating ip")
         self._reassociate_floating_ips()
-        self._check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True,
+                                                msg="after re-associate "
+                                                    "floating ip")
diff --git a/tempest/scenario/test_swift_basic_ops.py b/tempest/scenario/test_swift_basic_ops.py
index b367f7f..60df606 100644
--- a/tempest/scenario/test_swift_basic_ops.py
+++ b/tempest/scenario/test_swift_basic_ops.py
@@ -93,7 +93,7 @@
             for obj in not_present_obj:
                 self.assertNotIn(obj, object_list)
 
-    @services('object')
+    @services('object_storage')
     def test_swift_basic_ops(self):
         self._get_swift_stat()
         container_name = self._create_container()
diff --git a/tempest/services/compute/v3/json/interfaces_client.py b/tempest/services/compute/v3/json/interfaces_client.py
index 8f0760c..dc06395 100644
--- a/tempest/services/compute/v3/json/interfaces_client.py
+++ b/tempest/services/compute/v3/json/interfaces_client.py
@@ -38,14 +38,14 @@
 
     def create_interface(self, server, port_id=None, network_id=None,
                          fixed_ip=None):
-        post_body = dict(interface_attachment=dict())
+        post_body = dict()
         if port_id:
             post_body['port_id'] = port_id
         if network_id:
             post_body['net_id'] = network_id
         if fixed_ip:
             post_body['fixed_ips'] = [dict(ip_address=fixed_ip)]
-        post_body = json.dumps(post_body)
+        post_body = json.dumps({'interface_attachment': post_body})
         resp, body = self.post('servers/%s/os-attach-interfaces' % server,
                                headers=self.headers,
                                body=post_body)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 4c5b832..28d10aa 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -31,6 +31,37 @@
         self.service = CONF.object_storage.catalog_type
         self.format = 'json'
 
+    def create_account(self, data=None,
+                       params=None,
+                       metadata={},
+                       remove_metadata={},
+                       metadata_prefix='X-Account-Meta-',
+                       remove_metadata_prefix='X-Remove-Account-Meta-'):
+        """Create an account."""
+        url = ''
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        headers = {}
+        for key in metadata:
+            headers[metadata_prefix + key] = metadata[key]
+        for key in remove_metadata:
+            headers[remove_metadata_prefix + key] = remove_metadata[key]
+
+        resp, body = self.put(url, data, headers)
+        return resp, body
+
+    def delete_account(self, data=None, params=None):
+        """Delete an account."""
+        url = ''
+        if params:
+            if 'bulk-delete' in params:
+                url += 'bulk-delete&'
+            url = '?%s%s' % (url, urllib.urlencode(params))
+
+        resp, body = self.delete(url, headers=None, body=data)
+        return resp, body
+
     def list_account_metadata(self):
         """
         HEAD on the storage URL
@@ -91,7 +122,9 @@
 
         url = '?' + urllib.urlencode(params)
         resp, body = self.get(url)
-        body = json.loads(body)
+
+        if params and params.get('format') == 'json':
+            body = json.loads(body)
         return resp, body
 
     def list_extensions(self):
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 4308589..bc85c73 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -18,6 +18,7 @@
 
 from tempest.common.rest_client import RestClient
 from tempest import config
+from xml.etree import ElementTree as etree
 
 CONF = config.CONF
 
@@ -32,8 +33,12 @@
         self.service = CONF.object_storage.catalog_type
         self.format = 'json'
 
-    def create_container(self, container_name, metadata=None,
-                         metadata_prefix='X-Container-Meta-'):
+    def create_container(
+            self, container_name,
+            metadata=None,
+            remove_metadata=None,
+            metadata_prefix='X-Container-Meta-',
+            remove_metadata_prefix='X-Remove-Container-Meta-'):
         """
            Creates a container, with optional metadata passed in as a
            dictionary
@@ -44,6 +49,9 @@
         if metadata is not None:
             for key in metadata:
                 headers[metadata_prefix + key] = metadata[key]
+        if remove_metadata is not None:
+            for key in remove_metadata:
+                headers[remove_metadata_prefix + key] = remove_metadata[key]
 
         resp, body = self.put(url, body=None, headers=headers)
         return resp, body
@@ -54,8 +62,12 @@
         resp, body = self.delete(url)
         return resp, body
 
-    def update_container_metadata(self, container_name, metadata,
-                                  metadata_prefix='X-Container-Meta-'):
+    def update_container_metadata(
+            self, container_name,
+            metadata=None,
+            remove_metadata=None,
+            metadata_prefix='X-Container-Meta-',
+            remove_metadata_prefix='X-Remove-Container-Meta-'):
         """Updates arbitrary metadata on container."""
         url = str(container_name)
         headers = {}
@@ -63,6 +75,9 @@
         if metadata is not None:
             for key in metadata:
                 headers[metadata_prefix + key] = metadata[key]
+        if remove_metadata is not None:
+            for key in remove_metadata:
+                headers[remove_metadata_prefix + key] = remove_metadata[key]
 
         resp, body = self.post(url, body=None, headers=headers)
         return resp, body
@@ -75,7 +90,7 @@
 
         if metadata is not None:
             for item in metadata:
-                headers[metadata_prefix + item] = 'x'
+                headers[metadata_prefix + item] = metadata[item]
 
         resp, body = self.post(url, body=None, headers=headers)
         return resp, body
@@ -104,8 +119,9 @@
             if 'marker' in params:
                 limit = params['marker']
 
-        resp, objlist = self.list_container_contents(container,
-                                                     params={'limit': limit})
+        resp, objlist = self.list_container_contents(
+            container,
+            params={'limit': limit, 'format': 'json'})
         return objlist
         """tmp = []
         for obj in objlist:
@@ -162,10 +178,13 @@
         """
 
         url = str(container)
-        url += '?format=%s' % self.format
         if params:
+            url += '?'
             url += '&%s' % urllib.urlencode(params)
 
         resp, body = self.get(url)
-        body = json.loads(body)
+        if params and params.get('format') == 'json':
+            body = json.loads(body)
+        elif params and params.get('format') == 'xml':
+            body = etree.fromstring(body)
         return resp, body
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index a36083b..e8926bd 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -187,3 +187,10 @@
         url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
         resp, body = self.delete(url, self.headers)
         return resp, body
+
+    def force_delete_snapshot(self, snapshot_id):
+        """Force Delete Snapshot."""
+        post_body = json.dumps({'os-force_delete': {}})
+        resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body,
+                               self.headers)
+        return resp, body
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 3e85041..5e62b6d 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -226,3 +226,12 @@
         """Delete metadata item for the snapshot."""
         url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
         return self.delete(url)
+
+    def force_delete_snapshot(self, snapshot_id):
+        """Force Delete Snapshot."""
+        post_body = Element("os-force_delete")
+        url = 'snapshots/%s/action' % str(snapshot_id)
+        resp, body = self.post(url, str(Document(post_body)), self.headers)
+        if body:
+            body = xml_to_json(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 5af9ebf..5464c03 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -71,7 +71,7 @@
     exercised by a test case.
     """
     valid_service_list = ['compute', 'image', 'volume', 'orchestration',
-                          'network', 'identity', 'object', 'dashboard']
+                          'network', 'identity', 'object_storage', 'dashboard']
 
     def decorator(f):
         for service in args:
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index b393402..913c90b 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -127,11 +127,22 @@
                           "enabled extensions" % (service, extension))
 
 
+def check_service_availability(service):
+    if service == 'nova_v3':
+        service = 'nova'
+    return getattr(CONF.service_available, service)
+
+
 def main(argv):
     print('Running config verification...')
     os = clients.ComputeAdminManager(interface='json')
     results = {}
     for service in ['nova', 'nova_v3', 'cinder', 'neutron']:
+        # TODO(mtreinish) make this a keystone endpoint check for available
+        # services
+        if not check_service_availability(service):
+            print("%s is not available" % service)
+            continue
         results = verify_extensions(os, service, results)
     verify_glance_api_versions(os)
     verify_nova_api_versions(os)