Move image_meta_to_headers from images_client

Glance v1 images_client contains image_meta_to_headers() which
converts a dict to headers. However, most service clients'
methods don't convert like that.
Then this moves image_meta_to_headers() to common place like
the compute module from the service client.

Partially implements blueprint consistent-service-method-names

Change-Id: Id8e47fd35f7667578854bc439238a4b0f36fbb8f
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 427a748..6f80730 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -16,6 +16,7 @@
 import six
 
 from tempest.api.compute import base
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
@@ -55,15 +56,18 @@
         super(ImagesMetadataTestJSON, cls).resource_setup()
         cls.image_id = None
 
-        name = data_utils.rand_name('image')
+        params = {
+            'name': data_utils.rand_name('image'),
+            'container_format': 'bare',
+            'disk_format': 'raw'
+        }
         if CONF.image_feature_enabled.api_v1:
-            kwargs = dict(is_public=False)
+            params.update({'is_public': False})
+            params = {'headers': common_image.image_meta_to_headers(**params)}
         else:
-            kwargs = dict(visibility='private')
-        body = cls.glance_client.create_image(name=name,
-                                              container_format='bare',
-                                              disk_format='raw',
-                                              **kwargs)
+            params.update({'visibility': 'private'})
+
+        body = cls.glance_client.create_image(**params)
         body = body['image'] if 'image' in body else body
         cls.image_id = body['id']
         cls.images.append(cls.image_id)
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index e74ca67..9017461 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -19,6 +19,7 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
@@ -58,15 +59,19 @@
         super(ListImageFiltersTestJSON, cls).resource_setup()
 
         def _create_image():
-            name = data_utils.rand_name('image')
+            params = {
+                'name': data_utils.rand_name('image'),
+                'container_format': 'bare',
+                'disk_format': 'raw'
+            }
             if CONF.image_feature_enabled.api_v1:
-                kwargs = dict(is_public=False)
+                params.update({'is_public': False})
+                params = {'headers':
+                          common_image.image_meta_to_headers(**params)}
             else:
-                kwargs = dict(visibility='private')
-            body = cls.glance_client.create_image(name=name,
-                                                  container_format='bare',
-                                                  disk_format='raw',
-                                                  **kwargs)
+                params.update({'visibility': 'private'})
+
+            body = cls.glance_client.create_image(**params)
             body = body['image'] if 'image' in body else body
             image_id = body['id']
             cls.images.append(image_id)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 3fefc81..6fd6ea6 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -14,6 +14,7 @@
 
 from six import moves
 
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
@@ -55,14 +56,20 @@
         super(BaseImageTest, cls).resource_cleanup()
 
     @classmethod
-    def create_image(cls, **kwargs):
+    def create_image(cls, data=None, **kwargs):
         """Wrapper that returns a test image."""
 
         if 'name' not in kwargs:
             name = data_utils.rand_name(cls.__name__ + "-instance")
             kwargs['name'] = name
 
-        image = cls.client.create_image(**kwargs)
+        params = cls._get_create_params(**kwargs)
+        if data:
+            # NOTE: On glance v1 API, the data should be passed on
+            # a header. Then here handles the data separately.
+            params['data'] = data
+
+        image = cls.client.create_image(**params)
         # Image objects returned by the v1 client have the image
         # data inside a dict that is keyed against 'image'.
         if 'image' in image:
@@ -70,6 +77,10 @@
         cls.created_images.append(image['id'])
         return image
 
+    @classmethod
+    def _get_create_params(cls, **kwargs):
+        return kwargs
+
 
 class BaseV1ImageTest(BaseImageTest):
 
@@ -85,6 +96,10 @@
         super(BaseV1ImageTest, cls).setup_clients()
         cls.client = cls.os.image_client
 
+    @classmethod
+    def _get_create_params(cls, **kwargs):
+        return {'headers': common_image.image_meta_to_headers(**kwargs)}
+
 
 class BaseV1ImageMembersTest(BaseV1ImageTest):
 
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 59ac646..def7750 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -320,8 +320,10 @@
         metadata = common_image.get_image_meta_from_headers(resp)
         self.assertEqual(metadata['properties'], {'key1': 'value1'})
         metadata['properties'].update(req_metadata)
-        metadata = self.client.update_image(
-            self.image_id, properties=metadata['properties'])['image']
+        headers = common_image.image_meta_to_headers(
+            properties=metadata['properties'])
+        metadata = self.client.update_image(self.image_id,
+                                            headers=headers)['image']
 
         resp = self.client.check_image(self.image_id)
         resp_metadata = common_image.get_image_meta_from_headers(resp)
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index babee74..9e67c25 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -27,17 +27,17 @@
     def test_register_with_invalid_container_format(self):
         # Negative tests for invalid data supplied to POST /images
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          name='test',
-                          container_format='wrong',
-                          disk_format='vhd',)
+                          headers={'x-image-meta-name': 'test',
+                                   'x-image-meta-container_format': 'wrong',
+                                   'x-image-meta-disk_format': 'vhd'})
 
     @test.attr(type=['negative'])
     @test.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67')
     def test_register_with_invalid_disk_format(self):
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          name='test',
-                          container_format='bare',
-                          disk_format='wrong',)
+                          headers={'x-image-meta-name': 'test',
+                                   'x-image-meta-container_format': 'bare',
+                                   'x-image-meta-disk_format': 'wrong'})
 
     @test.attr(type=['negative'])
     @test.idempotent_id('bb016f15-0820-4f27-a92d-09b2f67d2488')
diff --git a/tempest/common/image.py b/tempest/common/image.py
index 42ce5ac..72e3a72 100644
--- a/tempest/common/image.py
+++ b/tempest/common/image.py
@@ -13,6 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import copy
+
+import six
+
 
 def get_image_meta_from_headers(resp):
     meta = {'properties': {}}
@@ -36,3 +40,23 @@
             except ValueError:
                 pass
     return meta
+
+
+def image_meta_to_headers(**metadata):
+    headers = {}
+    fields_copy = copy.deepcopy(metadata)
+
+    copy_from = fields_copy.pop('copy_from', None)
+    if copy_from is not None:
+        headers['x-glance-api-copy-from'] = copy_from
+
+    for key, value in six.iteritems(fields_copy.pop('properties', {})):
+        headers['x-image-meta-property-%s' % key] = str(value)
+
+    for key, value in six.iteritems(fields_copy.pop('api', {})):
+        headers['x-glance-api-property-%s' % key] = str(value)
+
+    for key, value in six.iteritems(fields_copy):
+        headers['x-image-meta-%s' % key] = str(value)
+
+    return headers
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index dd6e0e5..11c96e0 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -388,6 +388,7 @@
         if CONF.image_feature_enabled.api_v1:
             params['is_public'] = 'False'
             params['properties'] = properties
+            params = {'headers': common_image.image_meta_to_headers(**params)}
         else:
             params['visibility'] = 'private'
             # Additional properties are flattened out in the v2 API.
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index ed0a676..0db98f8 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -13,11 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import copy
 import functools
 
 from oslo_serialization import jsonutils as json
-import six
 from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
@@ -29,20 +27,6 @@
 class ImagesClient(rest_client.RestClient):
     api_version = "v1"
 
-    def _image_meta_to_headers(self, fields):
-        headers = {}
-        fields_copy = copy.deepcopy(fields)
-        copy_from = fields_copy.pop('copy_from', None)
-        if copy_from is not None:
-            headers['x-glance-api-copy-from'] = copy_from
-        for key, value in six.iteritems(fields_copy.pop('properties', {})):
-            headers['x-image-meta-property-%s' % key] = str(value)
-        for key, value in six.iteritems(fields_copy.pop('api', {})):
-            headers['x-glance-api-property-%s' % key] = str(value)
-        for key, value in six.iteritems(fields_copy):
-            headers['x-image-meta-%s' % key] = str(value)
-        return headers
-
     def _create_with_data(self, headers, data):
         # We are going to do chunked transfert, so split the input data
         # info fixed-sized chunks.
@@ -74,14 +58,14 @@
             self._http = self._get_http()
         return self._http
 
-    def create_image(self, data=None, **kwargs):
+    def create_image(self, data=None, headers=None):
         """Create an image.
 
         Available params: http://developer.openstack.org/
                           api-ref-image-v1.html#createImage-v1
         """
-        headers = {}
-        headers.update(self._image_meta_to_headers(kwargs))
+        if headers is None:
+            headers = {}
 
         if data is not None:
             return self._create_with_data(headers, data)
@@ -91,14 +75,14 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
-    def update_image(self, image_id, data=None, **kwargs):
+    def update_image(self, image_id, data=None, headers=None):
         """Update an image.
 
         Available params: http://developer.openstack.org/
                           api-ref-image-v1.html#updateImage-v1
         """
-        headers = {}
-        headers.update(self._image_meta_to_headers(kwargs))
+        if headers is None:
+            headers = {}
 
         if data is not None:
             return self._update_with_data(image_id, headers, data)
diff --git a/tempest/tests/common/test_image.py b/tempest/tests/common/test_image.py
index fdd0ae8..34772a2 100644
--- a/tempest/tests/common/test_image.py
+++ b/tempest/tests/common/test_image.py
@@ -38,3 +38,22 @@
             'name': 'New Http Image'
         }
         self.assertEqual(expected, observed)
+
+    def test_image_meta_to_headers(self):
+        observed = image.image_meta_to_headers(
+            name='test',
+            container_format='wrong',
+            disk_format='vhd',
+            copy_from='http://localhost/images/10',
+            properties={'foo': 'bar'},
+            api={'abc': 'def'})
+
+        expected = {
+            'x-image-meta-name': 'test',
+            'x-image-meta-container_format': 'wrong',
+            'x-image-meta-disk_format': 'vhd',
+            'x-glance-api-copy-from': 'http://localhost/images/10',
+            'x-image-meta-property-foo': 'bar',
+            'x-glance-api-property-abc': 'def'
+        }
+        self.assertEqual(expected, observed)