Add a test for deleting multiple objects by POST method

From swift Icehouse version, bulk deletion supports POST method as well as
DELETE.

Extra fixes:
 - Bulk is an optional function with WSGI middleware, therefore each test
   is annotated with @test.requires_ext
 - Refactor test_account_bulk.py by separating common codes to one method
 - Simplify delete_account method of the Swift's account service client

Partially implements blueprint add-icehouse-swift-tests

Change-Id: I3b63bc730f993cdb626c5a77c18ae917d98e911b
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index a94c883..743f1aa 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -50,16 +50,27 @@
 
         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()
-
+    def _upload_archive(self, filepath):
+        # upload an archived file
         params = {'extract-archive': 'tar'}
         with open(filepath) as fh:
             mydata = fh.read()
             resp, body = self.account_client.create_account(data=mydata,
                                                             params=params)
+        return resp, body
+
+    def _check_contents_deleted(self, container_name):
+        param = {'format': 'txt'}
+        resp, body = self.account_client.list_account_containers(param)
+        self.assertHeaders(resp, 'Account', 'GET')
+        self.assertNotIn(container_name, body)
+
+    @test.attr(type='gate')
+    @test.requires_ext(extension='bulk', service='object')
+    def test_extract_archive(self):
+        # Test bulk operation of file upload with an archived file
+        filepath, container_name, object_name = self._create_archive()
+        resp, _ = self._upload_archive(filepath)
 
         self.containers.append(container_name)
 
@@ -95,23 +106,17 @@
         self.assertIn(object_name, [c['name'] for c in contents_list])
 
     @test.attr(type='gate')
+    @test.requires_ext(extension='bulk', service='object')
     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)
+        self._upload_archive(filepath)
 
         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
@@ -124,11 +129,33 @@
         # 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)
+        # Check if uploaded contents are completely deleted
+        self._check_contents_deleted(container_name)
 
-        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
-        self.assertHeaders(resp, 'Account', 'GET')
+    @test.attr(type='gate')
+    @test.requires_ext(extension='bulk', service='object')
+    def test_bulk_delete_by_POST(self):
+        # Test bulk operation of deleting multiple files
+        filepath, container_name, object_name = self._create_archive()
+        self._upload_archive(filepath)
 
-        self.assertNotIn(container_name, body)
+        data = '%s/%s\n%s' % (container_name, object_name, container_name)
+        params = {'bulk-delete': ''}
+
+        resp, body = self.account_client.create_account_metadata(
+            {}, data=data, params=params)
+
+        # 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 uploaded contents are completely deleted
+        self._check_contents_deleted(container_name)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index eca57c0..4dc588f 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -58,8 +58,6 @@
         """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={}, body=data)
@@ -74,13 +72,19 @@
         return resp, body
 
     def create_account_metadata(self, metadata,
-                                metadata_prefix='X-Account-Meta-'):
+                                metadata_prefix='X-Account-Meta-',
+                                data=None, params=None):
         """Creates an account metadata entry."""
         headers = {}
-        for key in metadata:
-            headers[metadata_prefix + key] = metadata[key]
+        if metadata:
+            for key in metadata:
+                headers[metadata_prefix + key] = metadata[key]
 
-        resp, body = self.post('', headers=headers, body=None)
+        url = ''
+        if params:
+            url = '?%s%s' % (url, urllib.urlencode(params))
+
+        resp, body = self.post(url, headers=headers, body=data)
         return resp, body
 
     def delete_account_metadata(self, metadata,