Image metadata for volume

Cinder allows user to modify an image property or add new properties

Added:
- Api for sets volume image metadata
- Api for deletes volume image metadata

Test:
- updating and deleting image metadata of volume
Note: Cinder V1 does not support image metadata modfication

Change-Id: Ieb0932407f1ae14cea0025bb57ab92f9687b228e
diff --git a/tempest/api/volume/v2/test_image_metadata.py b/tempest/api/volume/v2/test_image_metadata.py
new file mode 100644
index 0000000..1e7bb30
--- /dev/null
+++ b/tempest/api/volume/v2/test_image_metadata.py
@@ -0,0 +1,64 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from testtools import matchers
+
+from tempest.api.volume import base
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesV2ImageMetadata(base.BaseVolumeTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesV2ImageMetadata, cls).resource_setup()
+        # Create a volume from image ID
+        cls.volume = cls.create_volume(imageRef=CONF.compute.image_ref)
+
+    @test.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
+    @test.services('image')
+    def test_update_image_metadata(self):
+        # Update image metadata
+        image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
+                          'image_name': 'image',
+                          'kernel_id': '6ff710d2-942b-4d6b-9168-8c9cc2404ab1',
+                          'ramdisk_id': 'somedisk'}
+        self.volumes_client.update_volume_image_metadata(self.volume['id'],
+                                                         **image_metadata)
+
+        # Fetch image metadata from the volume
+        volume_image_metadata = self.volumes_client.show_volume(
+            self.volume['id'])['volume']['volume_image_metadata']
+
+        # Verify image metadata was updated
+        self.assertThat(volume_image_metadata.items(),
+                        matchers.ContainsAll(image_metadata.items()))
+
+        # Delete one item from image metadata of the volume
+        self.volumes_client.delete_volume_image_metadata(self.volume['id'],
+                                                         'ramdisk_id')
+        del image_metadata['ramdisk_id']
+
+        # Fetch the new image metadata from the volume
+        volume_image_metadata = self.volumes_client.show_volume(
+            self.volume['id'])['volume']['volume_image_metadata']
+
+        # Verify image metadata was updated after item deletion
+        self.assertThat(volume_image_metadata.items(),
+                        matchers.ContainsAll(image_metadata.items()))
+        self.assertNotIn('ramdisk_id', volume_image_metadata)
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index 6237745..a3a4eb6 100644
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -299,6 +299,23 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def update_volume_image_metadata(self, volume_id, **kwargs):
+        """Update image metadata for the volume."""
+        post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
+        url = "volumes/%s/action" % (volume_id)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_image_metadata(self, volume_id, key_name):
+        """Delete image metadata item for the volume."""
+        post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
+        url = "volumes/%s/action" % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def retype_volume(self, volume_id, **kwargs):
         """Updates volume with new volume type."""
         post_body = json.dumps({'os-retype': kwargs})