Merge "Sharing codes for cinder v1 and v2 tests"
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 67d0203..2a9b407 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -16,6 +16,7 @@
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest import exceptions
 from tempest.openstack.common import log as logging
 import tempest.test
 
@@ -25,9 +26,11 @@
 
 
 class BaseVolumeTest(tempest.test.BaseTestCase):
-
     """Base test case class for all Cinder API tests."""
 
+    _api_version = 2
+    _interface = 'json'
+
     @classmethod
     def setUpClass(cls):
         cls.set_network_resources()
@@ -47,6 +50,28 @@
         cls.snapshots = []
         cls.volumes = []
 
+        if cls._api_version == 1:
+            if not CONF.volume_feature_enabled.api_v1:
+                msg = "Volume API v1 is disabled"
+                raise cls.skipException(msg)
+            cls.snapshots_client = cls.os.snapshots_client
+            cls.volumes_client = cls.os.volumes_client
+            cls.backups_client = cls.os.backups_client
+            cls.volume_services_client = cls.os.volume_services_client
+            cls.volumes_extension_client = cls.os.volumes_extension_client
+            cls.availability_zone_client = (
+                cls.os.volume_availability_zone_client)
+
+        elif cls._api_version == 2:
+            if not CONF.volume_feature_enabled.api_v2:
+                msg = "Volume API v2 is disabled"
+                raise cls.skipException(msg)
+            cls.volumes_client = cls.os.volumes_v2_client
+
+        else:
+            msg = ("Invalid Cinder API version (%s)" % cls._api_version)
+            raise exceptions.InvalidConfiguration(message=msg)
+
     @classmethod
     def tearDownClass(cls):
         cls.clear_snapshots()
@@ -55,6 +80,22 @@
         super(BaseVolumeTest, cls).tearDownClass()
 
     @classmethod
+    def create_volume(cls, size=1, **kwargs):
+        """Wrapper utility that returns a test volume."""
+        vol_name = data_utils.rand_name('Volume')
+        if cls._api_version == 1:
+            resp, volume = cls.volumes_client.create_volume(
+                size, display_name=vol_name, **kwargs)
+            assert 200 == resp.status
+        elif cls._api_version == 2:
+            resp, volume = cls.volumes_client.create_volume(
+                size, name=vol_name, **kwargs)
+            assert 202 == resp.status
+        cls.volumes.append(volume)
+        cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        return volume
+
+    @classmethod
     def create_snapshot(cls, volume_id=1, **kwargs):
         """Wrapper utility that returns a test snapshot."""
         resp, snapshot = cls.snapshots_client.create_snapshot(volume_id,
@@ -98,30 +139,11 @@
 
 
 class BaseVolumeV1Test(BaseVolumeTest):
-    @classmethod
-    def setUpClass(cls):
-        if not CONF.volume_feature_enabled.api_v1:
-            msg = "Volume API v1 not supported"
-            raise cls.skipException(msg)
-        super(BaseVolumeV1Test, cls).setUpClass()
-        cls.snapshots_client = cls.os.snapshots_client
-        cls.volumes_client = cls.os.volumes_client
-        cls.backups_client = cls.os.backups_client
-        cls.volume_services_client = cls.os.volume_services_client
-        cls.volumes_extension_client = cls.os.volumes_extension_client
-        cls.availability_zone_client = cls.os.volume_availability_zone_client
+    _api_version = 1
 
-    @classmethod
-    def create_volume(cls, size=1, **kwargs):
-        """Wrapper utility that returns a test volume."""
-        vol_name = data_utils.rand_name('Volume')
-        resp, volume = cls.volumes_client.create_volume(size,
-                                                        display_name=vol_name,
-                                                        **kwargs)
-        assert 200 == resp.status
-        cls.volumes.append(volume)
-        cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
-        return volume
+
+class BaseVolumeV2Test(BaseVolumeTest):
+    _api_version = 2
 
 
 class BaseVolumeV1AdminTest(BaseVolumeV1Test):
@@ -144,25 +166,3 @@
         cls.client = cls.os_adm.volume_types_client
         cls.hosts_client = cls.os_adm.volume_hosts_client
         cls.quotas_client = cls.os_adm.volume_quotas_client
-
-
-class BaseVolumeV2Test(BaseVolumeTest):
-    @classmethod
-    def setUpClass(cls):
-        if not CONF.volume_feature_enabled.api_v2:
-            msg = "Volume API v2 not supported"
-            raise cls.skipException(msg)
-        super(BaseVolumeV2Test, cls).setUpClass()
-        cls.volumes_client = cls.os.volumes_v2_client
-
-    @classmethod
-    def create_volume(cls, size=1, **kwargs):
-        """Wrapper utility that returns a test volume."""
-        vol_name = data_utils.rand_name('Volume')
-        resp, volume = cls.volumes_client.create_volume(size,
-                                                        name=vol_name,
-                                                        **kwargs)
-        assert 202 == resp.status
-        cls.volumes.append(volume)
-        cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
-        return volume
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index b55a037..6c97497 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -24,13 +24,13 @@
 CONF = config.CONF
 
 
-class VolumesClientJSON(rest_client.RestClient):
+class BaseVolumesClientJSON(rest_client.RestClient):
     """
-    Client class to send CRUD Volume API requests to a Cinder endpoint
+    Base client class to send CRUD Volume API requests to a Cinder endpoint
     """
 
     def __init__(self, auth_provider):
-        super(VolumesClientJSON, self).__init__(auth_provider)
+        super(BaseVolumesClientJSON, self).__init__(auth_provider)
 
         self.service = CONF.volume.catalog_type
         self.build_interval = CONF.volume.build_interval
@@ -72,7 +72,8 @@
         Creates a new Volume.
         size: Size of volume in GB.
         Following optional keyword arguments are accepted:
-        display_name: Optional Volume Name.
+        display_name: Optional Volume Name(only for V1).
+        name: Optional Volume Name(only for V2).
         metadata: A dictionary of values to be used as metadata.
         volume_type: Optional Name of volume_type for the volume
         snapshot_id: When specified the volume is created from this snapshot
@@ -150,7 +151,6 @@
     def wait_for_volume_status(self, volume_id, status):
         """Waits for a Volume to reach a given status."""
         resp, body = self.get_volume(volume_id)
-        volume_name = body['display_name']
         volume_status = body['status']
         start = int(time.time())
 
@@ -162,9 +162,10 @@
                 raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
 
             if int(time.time()) - start >= self.build_timeout:
-                message = ('Volume %s failed to reach %s status within '
-                           'the required time (%s s).' %
-                           (volume_name, status, self.build_timeout))
+                message = 'Volume %s failed to reach %s status within '\
+                          'the required time (%s s).' % (volume_id,
+                                                         status,
+                                                         self.build_timeout)
                 raise exceptions.TimeoutException(message)
 
     def is_resource_deleted(self, id):
@@ -297,3 +298,9 @@
         url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
         resp, body = self.delete(url)
         return resp, body
+
+
+class VolumesClientJSON(BaseVolumesClientJSON):
+    """
+    Client class to send CRUD Volume V1 API requests to a Cinder endpoint
+    """
diff --git a/tempest/services/volume/v2/json/volumes_client.py b/tempest/services/volume/v2/json/volumes_client.py
index df20a2a..1f16ead 100644
--- a/tempest/services/volume/v2/json/volumes_client.py
+++ b/tempest/services/volume/v2/json/volumes_client.py
@@ -13,18 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-import time
-import urllib
-
-from tempest.common import rest_client
-from tempest import config
-from tempest import exceptions
-
-CONF = config.CONF
+from tempest.services.volume.json import volumes_client
 
 
-class VolumesV2ClientJSON(rest_client.RestClient):
+class VolumesV2ClientJSON(volumes_client.BaseVolumesClientJSON):
     """
     Client class to send CRUD Volume V2 API requests to a Cinder endpoint
     """
@@ -33,268 +25,3 @@
         super(VolumesV2ClientJSON, self).__init__(auth_provider)
 
         self.api_version = "v2"
-        self.service = CONF.volume.catalog_type
-        self.build_interval = CONF.volume.build_interval
-        self.build_timeout = CONF.volume.build_timeout
-
-    def get_attachment_from_volume(self, volume):
-        """Return the element 'attachment' from input volumes."""
-        return volume['attachments'][0]
-
-    def list_volumes(self, params=None):
-        """List all the volumes created."""
-        url = 'volumes'
-        if params:
-                url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        return resp, body['volumes']
-
-    def list_volumes_with_detail(self, params=None):
-        """List the details of all volumes."""
-        url = 'volumes/detail'
-        if params:
-                url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        return resp, body['volumes']
-
-    def get_volume(self, volume_id):
-        """Returns the details of a single volume."""
-        url = "volumes/%s" % str(volume_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        return resp, body['volume']
-
-    def create_volume(self, size=None, **kwargs):
-        """
-        Creates a new Volume.
-        size: Size of volume in GB.
-        Following optional keyword arguments are accepted:
-        name: Optional Volume Name.
-        metadata: A dictionary of values to be used as metadata.
-        volume_type: Optional Name of volume_type for the volume
-        snapshot_id: When specified the volume is created from this snapshot
-        imageRef: When specified the volume is created from this image
-        """
-        # for bug #1293885:
-        # If no size specified, read volume size from CONF
-        if size is None:
-            size = CONF.volume.volume_size
-        post_body = {'size': size}
-        post_body.update(kwargs)
-        post_body = json.dumps({'volume': post_body})
-        resp, body = self.post('volumes', post_body)
-        body = json.loads(body)
-        return resp, body['volume']
-
-    def update_volume(self, volume_id, **kwargs):
-        """Updates the Specified Volume."""
-        put_body = json.dumps({'volume': kwargs})
-        resp, body = self.put('volumes/%s' % volume_id, put_body)
-        body = json.loads(body)
-        return resp, body['volume']
-
-    def delete_volume(self, volume_id):
-        """Deletes the Specified Volume."""
-        return self.delete("volumes/%s" % str(volume_id))
-
-    def upload_volume(self, volume_id, image_name, disk_format):
-        """Uploads a volume in Glance."""
-        post_body = {
-            'image_name': image_name,
-            'disk_format': disk_format
-        }
-        post_body = json.dumps({'os-volume_upload_image': post_body})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        return resp, body['os-volume_upload_image']
-
-    def attach_volume(self, volume_id, instance_uuid, mountpoint):
-        """Attaches a volume to a given instance on a given mountpoint."""
-        post_body = {
-            'instance_uuid': instance_uuid,
-            'mountpoint': mountpoint,
-        }
-        post_body = json.dumps({'os-attach': post_body})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        return resp, body
-
-    def detach_volume(self, volume_id):
-        """Detaches a volume from an instance."""
-        post_body = {}
-        post_body = json.dumps({'os-detach': post_body})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        return resp, body
-
-    def reserve_volume(self, volume_id):
-        """Reserves a volume."""
-        post_body = {}
-        post_body = json.dumps({'os-reserve': post_body})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        return resp, body
-
-    def unreserve_volume(self, volume_id):
-        """Restore a reserved volume ."""
-        post_body = {}
-        post_body = json.dumps({'os-unreserve': post_body})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        return resp, body
-
-    def wait_for_volume_status(self, volume_id, status):
-        """Waits for a Volume to reach a given status."""
-        resp, body = self.get_volume(volume_id)
-        volume_name = body['name']
-        volume_status = body['status']
-        start = int(time.time())
-
-        while volume_status != status:
-            time.sleep(self.build_interval)
-            resp, body = self.get_volume(volume_id)
-            volume_status = body['status']
-            if volume_status == 'error':
-                raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
-
-            if int(time.time()) - start >= self.build_timeout:
-                message = ('Volume %s failed to reach %s status within '
-                           'the required time (%s s).' %
-                           (volume_name, status, self.build_timeout))
-                raise exceptions.TimeoutException(message)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.get_volume(id)
-        except exceptions.NotFound:
-            return True
-        return False
-
-    def extend_volume(self, volume_id, extend_size):
-        """Extend a volume."""
-        post_body = {
-            'new_size': extend_size
-        }
-        post_body = json.dumps({'os-extend': post_body})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        return resp, body
-
-    def reset_volume_status(self, volume_id, status):
-        """Reset the Specified Volume's Status."""
-        post_body = json.dumps({'os-reset_status': {"status": status}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        return resp, body
-
-    def volume_begin_detaching(self, volume_id):
-        """Volume Begin Detaching."""
-        post_body = json.dumps({'os-begin_detaching': {}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        return resp, body
-
-    def volume_roll_detaching(self, volume_id):
-        """Volume Roll Detaching."""
-        post_body = json.dumps({'os-roll_detaching': {}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        return resp, body
-
-    def create_volume_transfer(self, vol_id, name=None):
-        """Create a volume transfer."""
-        post_body = {
-            'volume_id': vol_id
-        }
-        if name:
-            post_body['name'] = name
-        post_body = json.dumps({'transfer': post_body})
-        resp, body = self.post('os-volume-transfer', post_body)
-        body = json.loads(body)
-        return resp, body['transfer']
-
-    def get_volume_transfer(self, transfer_id):
-        """Returns the details of a volume transfer."""
-        url = "os-volume-transfer/%s" % str(transfer_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        return resp, body['transfer']
-
-    def list_volume_transfers(self, params=None):
-        """List all the volume transfers created."""
-        url = 'os-volume-transfer'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        return resp, body['transfers']
-
-    def delete_volume_transfer(self, transfer_id):
-        """Delete a volume transfer."""
-        return self.delete("os-volume-transfer/%s" % str(transfer_id))
-
-    def accept_volume_transfer(self, transfer_id, transfer_auth_key):
-        """Accept a volume transfer."""
-        post_body = {
-            'auth_key': transfer_auth_key,
-        }
-        url = 'os-volume-transfer/%s/accept' % transfer_id
-        post_body = json.dumps({'accept': post_body})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        return resp, body['transfer']
-
-    def update_volume_readonly(self, volume_id, readonly):
-        """Update the Specified Volume readonly."""
-        post_body = {
-            'readonly': readonly
-        }
-        post_body = json.dumps({'os-update_readonly_flag': post_body})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        return resp, body
-
-    def force_delete_volume(self, volume_id):
-        """Force Delete Volume."""
-        post_body = json.dumps({'os-force_delete': {}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        return resp, body
-
-    def create_volume_metadata(self, volume_id, metadata):
-        """Create metadata for the volume."""
-        put_body = json.dumps({'metadata': metadata})
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.post(url, put_body)
-        body = json.loads(body)
-        return resp, body['metadata']
-
-    def get_volume_metadata(self, volume_id):
-        """Get metadata of the volume."""
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        return resp, body['metadata']
-
-    def update_volume_metadata(self, volume_id, metadata):
-        """Update metadata for the volume."""
-        put_body = json.dumps({'metadata': metadata})
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        return resp, body['metadata']
-
-    def update_volume_metadata_item(self, volume_id, id, meta_item):
-        """Update metadata item for the volume."""
-        put_body = json.dumps({'meta': meta_item})
-        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        return resp, body['meta']
-
-    def delete_volume_metadata_item(self, volume_id, id):
-        """Delete metadata item for the volume."""
-        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
-        resp, body = self.delete(url)
-        return resp, body
diff --git a/tempest/services/volume/v2/xml/volumes_client.py b/tempest/services/volume/v2/xml/volumes_client.py
index 1fdaf19..c1bcf6e 100644
--- a/tempest/services/volume/v2/xml/volumes_client.py
+++ b/tempest/services/volume/v2/xml/volumes_client.py
@@ -13,32 +13,23 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import time
 import urllib
 
 from lxml import etree
 
-from tempest.common import rest_client
 from tempest.common import xml_utils as common
-from tempest import config
-from tempest import exceptions
-
-CONF = config.CONF
+from tempest.services.volume.xml import volumes_client
 
 
-class VolumesV2ClientXML(rest_client.RestClient):
+class VolumesV2ClientXML(volumes_client.BaseVolumesClientXML):
     """
-    Client class to send CRUD Volume API requests to a Cinder endpoint
+    Client class to send CRUD Volume API V2 requests to a Cinder endpoint
     """
-    TYPE = "xml"
 
     def __init__(self, auth_provider):
         super(VolumesV2ClientXML, self).__init__(auth_provider)
 
         self.api_version = "v2"
-        self.service = CONF.volume.catalog_type
-        self.build_interval = CONF.compute.build_interval
-        self.build_timeout = CONF.compute.build_timeout
 
     def _parse_volume(self, body):
         vol = dict((attr, body.get(attr)) for attr in body.keys())
@@ -53,46 +44,9 @@
                                        child.getchildren())
             else:
                 vol[tag] = common.xml_to_json(child)
+        self._translate_attributes_to_json(vol)
         return vol
 
-    def get_attachment_from_volume(self, volume):
-        """Return the element 'attachment' from input volumes."""
-        return volume['attachments']['attachment']
-
-    def _check_if_bootable(self, volume):
-        """
-        Check if the volume is bootable, also change the value
-        of 'bootable' from string to boolean.
-        """
-
-        # NOTE(jdg): Version 1 of Cinder API uses lc strings
-        # We should consider being explicit in this check to
-        # avoid introducing bugs like: LP #1227837
-
-        if volume['bootable'].lower() == 'true':
-            volume['bootable'] = True
-        elif volume['bootable'].lower() == 'false':
-            volume['bootable'] = False
-        else:
-            raise ValueError(
-                'bootable flag is supposed to be either True or False,'
-                'it is %s' % volume['bootable'])
-        return volume
-
-    def list_volumes(self, params=None):
-        """List all the volumes created."""
-        url = 'volumes'
-
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = etree.fromstring(body)
-        volumes = []
-        if body is not None:
-            volumes += [self._parse_volume(vol) for vol in list(body)]
-        return resp, volumes
-
     def list_volumes_with_detail(self, params=None):
         """List all the details of volumes."""
         url = 'volumes/detail'
@@ -116,293 +70,3 @@
         body = self._parse_volume(etree.fromstring(body))
         body = self._check_if_bootable(body)
         return resp, body
-
-    def create_volume(self, size=None, **kwargs):
-        """Creates a new Volume.
-
-        :param size: Size of volume in GB.
-        :param name: Optional Volume Name.
-        :param metadata: An optional dictionary of values for metadata.
-        :param volume_type: Optional Name of volume_type for the volume
-        :param snapshot_id: When specified the volume is created from
-                            this snapshot
-        :param imageRef: When specified the volume is created from this
-                         image
-        """
-        # for bug #1293885:
-        # If no size specified, read volume size from CONF
-        if size is None:
-            size = CONF.volume.volume_size
-        # NOTE(afazekas): it should use a volume namespace
-        volume = common.Element("volume", xmlns=common.XMLNS_11, size=size)
-
-        if 'metadata' in kwargs:
-            _metadata = common.Element('metadata')
-            volume.append(_metadata)
-            for key, value in kwargs['metadata'].items():
-                meta = common.Element('meta')
-                meta.add_attr('key', key)
-                meta.append(common.Text(value))
-                _metadata.append(meta)
-            attr_to_add = kwargs.copy()
-            del attr_to_add['metadata']
-        else:
-            attr_to_add = kwargs
-
-        for key, value in attr_to_add.items():
-            volume.add_attr(key, value)
-
-        resp, body = self.post('volumes', str(common.Document(volume)))
-        body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def update_volume(self, volume_id, **kwargs):
-        """Updates the Specified Volume."""
-        put_body = common.Element("volume", xmlns=common.XMLNS_11, **kwargs)
-
-        resp, body = self.put('volumes/%s' % volume_id,
-                              str(common.Document(put_body)))
-        body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def delete_volume(self, volume_id):
-        """Deletes the Specified Volume."""
-        return self.delete("volumes/%s" % str(volume_id))
-
-    def wait_for_volume_status(self, volume_id, status):
-        """Waits for a Volume to reach a given status."""
-        resp, body = self.get_volume(volume_id)
-        volume_status = body['status']
-        start = int(time.time())
-
-        while volume_status != status:
-            time.sleep(self.build_interval)
-            resp, body = self.get_volume(volume_id)
-            volume_status = body['status']
-            if volume_status == 'error':
-                raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
-
-            if int(time.time()) - start >= self.build_timeout:
-                message = 'Volume %s failed to reach %s status within '\
-                          'the required time (%s s).' % (volume_id,
-                                                         status,
-                                                         self.build_timeout)
-                raise exceptions.TimeoutException(message)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.get_volume(id)
-        except exceptions.NotFound:
-            return True
-        return False
-
-    def attach_volume(self, volume_id, instance_uuid, mountpoint):
-        """Attaches a volume to a given instance on a given mountpoint."""
-        post_body = common.Element("os-attach",
-                                   instance_uuid=instance_uuid,
-                                   mountpoint=mountpoint
-                                   )
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def detach_volume(self, volume_id):
-        """Detaches a volume from an instance."""
-        post_body = common.Element("os-detach")
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def upload_volume(self, volume_id, image_name, disk_format):
-        """Uploads a volume in Glance."""
-        post_body = common.Element("os-volume_upload_image",
-                                   image_name=image_name,
-                                   disk_format=disk_format)
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        volume = common.xml_to_json(etree.fromstring(body))
-        return resp, volume
-
-    def extend_volume(self, volume_id, extend_size):
-        """Extend a volume."""
-        post_body = common.Element("os-extend",
-                                   new_size=extend_size)
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def reset_volume_status(self, volume_id, status):
-        """Reset the Specified Volume's Status."""
-        post_body = common.Element("os-reset_status",
-                                   status=status
-                                   )
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def volume_begin_detaching(self, volume_id):
-        """Volume Begin Detaching."""
-        post_body = common.Element("os-begin_detaching")
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def volume_roll_detaching(self, volume_id):
-        """Volume Roll Detaching."""
-        post_body = common.Element("os-roll_detaching")
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def reserve_volume(self, volume_id):
-        """Reserves a volume."""
-        post_body = common.Element("os-reserve")
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def unreserve_volume(self, volume_id):
-        """Restore a reserved volume ."""
-        post_body = common.Element("os-unreserve")
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def create_volume_transfer(self, vol_id, name=None):
-        """Create a volume transfer."""
-        post_body = common.Element("transfer", volume_id=vol_id)
-        if name:
-            post_body.add_attr('name', name)
-        resp, body = self.post('os-volume-transfer',
-                               str(common.Document(post_body)))
-        volume = common.xml_to_json(etree.fromstring(body))
-        return resp, volume
-
-    def get_volume_transfer(self, transfer_id):
-        """Returns the details of a volume transfer."""
-        url = "os-volume-transfer/%s" % str(transfer_id)
-        resp, body = self.get(url)
-        volume = common.xml_to_json(etree.fromstring(body))
-        return resp, volume
-
-    def list_volume_transfers(self, params=None):
-        """List all the volume transfers created."""
-        url = 'os-volume-transfer'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = etree.fromstring(body)
-        volumes = []
-        if body is not None:
-            volumes += [self._parse_volume_transfer(vol) for vol in list(body)]
-        return resp, volumes
-
-    def _parse_volume_transfer(self, body):
-        vol = dict((attr, body.get(attr)) for attr in body.keys())
-        for child in body.getchildren():
-            tag = child.tag
-            if tag.startswith("{"):
-                tag = tag.split("}", 1)
-            vol[tag] = common.xml_to_json(child)
-        return vol
-
-    def delete_volume_transfer(self, transfer_id):
-        """Delete a volume transfer."""
-        return self.delete("os-volume-transfer/%s" % str(transfer_id))
-
-    def accept_volume_transfer(self, transfer_id, transfer_auth_key):
-        """Accept a volume transfer."""
-        post_body = common.Element("accept", auth_key=transfer_auth_key)
-        url = 'os-volume-transfer/%s/accept' % transfer_id
-        resp, body = self.post(url, str(common.Document(post_body)))
-        volume = common.xml_to_json(etree.fromstring(body))
-        return resp, volume
-
-    def update_volume_readonly(self, volume_id, readonly):
-        """Update the Specified Volume readonly."""
-        post_body = common.Element("os-update_readonly_flag",
-                                   readonly=readonly)
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def force_delete_volume(self, volume_id):
-        """Force Delete Volume."""
-        post_body = common.Element("os-force_delete")
-        url = 'volumes/%s/action' % str(volume_id)
-        resp, body = self.post(url, str(common.Document(post_body)))
-        if body:
-            body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def _metadata_body(self, meta):
-        post_body = common.Element('metadata')
-        for k, v in meta.items():
-            data = common.Element('meta', key=k)
-            data.append(common.Text(v))
-            post_body.append(data)
-        return post_body
-
-    def _parse_key_value(self, node):
-        """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
-        data = {}
-        for node in node.getchildren():
-            data[node.get('key')] = node.text
-        return data
-
-    def create_volume_metadata(self, volume_id, metadata):
-        """Create metadata for the volume."""
-        post_body = self._metadata_body(metadata)
-        resp, body = self.post('volumes/%s/metadata' % volume_id,
-                               str(common.Document(post_body)))
-        body = self._parse_key_value(etree.fromstring(body))
-        return resp, body
-
-    def get_volume_metadata(self, volume_id):
-        """Get metadata of the volume."""
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.get(url)
-        body = self._parse_key_value(etree.fromstring(body))
-        return resp, body
-
-    def update_volume_metadata(self, volume_id, metadata):
-        """Update metadata for the volume."""
-        put_body = self._metadata_body(metadata)
-        url = "volumes/%s/metadata" % str(volume_id)
-        resp, body = self.put(url, str(common.Document(put_body)))
-        body = self._parse_key_value(etree.fromstring(body))
-        return resp, body
-
-    def update_volume_metadata_item(self, volume_id, id, meta_item):
-        """Update metadata item for the volume."""
-        for k, v in meta_item.items():
-            put_body = common.Element('meta', key=k)
-            put_body.append(common.Text(v))
-        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
-        resp, body = self.put(url, str(common.Document(put_body)))
-        body = common.xml_to_json(etree.fromstring(body))
-        return resp, body
-
-    def delete_volume_metadata_item(self, volume_id, id):
-        """Delete metadata item for the volume."""
-        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
-        return self.delete(url)
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 9799e55..2d4a9e9 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -32,14 +32,14 @@
 VOLUMES_TENANT_NS = VOLUME_NS_BASE + 'volume_tenant_attribute/api/v1'
 
 
-class VolumesClientXML(rest_client.RestClient):
+class BaseVolumesClientXML(rest_client.RestClient):
     """
-    Client class to send CRUD Volume API requests to a Cinder endpoint
+    Base client class to send CRUD Volume API requests to a Cinder endpoint
     """
     TYPE = "xml"
 
     def __init__(self, auth_provider):
-        super(VolumesClientXML, self).__init__(auth_provider)
+        super(BaseVolumesClientXML, self).__init__(auth_provider)
         self.service = CONF.volume.catalog_type
         self.build_interval = CONF.compute.build_interval
         self.build_timeout = CONF.compute.build_timeout
@@ -141,6 +141,8 @@
         """Creates a new Volume.
 
         :param size: Size of volume in GB.
+        :param display_name: Optional Volume Name(only for V1).
+        :param name: Optional Volume Name(only for V2).
         :param display_name: Optional Volume Name.
         :param metadata: An optional dictionary of values for metadata.
         :param volume_type: Optional Name of volume_type for the volume
@@ -428,3 +430,9 @@
         """Delete metadata item for the volume."""
         url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
         return self.delete(url)
+
+
+class VolumesClientXML(BaseVolumesClientXML):
+    """
+    Client class to send CRUD Volume API V1 requests to a Cinder endpoint
+    """