enable volume list tests for cinder v2 - part2

this ports test_volumes_list into cinder v2 api,
and also ports corresponding client.

1. changes of the response data of list and list details in v2:
(their items are same in v1)
- list view includes items:
id, name, link
- list details view includes items:
id, status, size, availability_zone,created_at, attachments, name,
description, volume_type, snapshot_id, source_volid, metadata,
links, user_id, bootable

2. use 'name' instead of 'display_name'

v2 commit id:
0c507aa6d08a9471bf896961cc99d40f337f1e4d
484fb9e1c748e718349e451e80a0e025ed007b1d

Change-Id: I19cf00270f0ca55770188c6c71c02e13c5bd82f6
Implements: blueprint cinder-v2-api-tests
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 990cb37..790ff92 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -826,4 +826,7 @@
 # Is the v1 volume API enabled (boolean value)
 #api_v1=true
 
+# Is the v2 volume API enabled (boolean value)
+#api_v2=true
+
 
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index de2b240..6b6f638 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -69,18 +69,6 @@
     # only in a single location in the source, and could be more general.
 
     @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
-
-    @classmethod
     def clear_volumes(cls):
         for volume in cls.volumes:
             try:
@@ -120,6 +108,18 @@
         cls.volumes_client = cls.os.volumes_client
         cls.volumes_extension_client = cls.os.volumes_extension_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,
+                                                        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 BaseVolumeV1AdminTest(BaseVolumeV1Test):
     """Base test case class for all Volume Admin API tests."""
@@ -144,3 +144,25 @@
             cls.os_adm = clients.AdminManager(interface=cls._interface)
         cls.client = cls.os_adm.volume_types_client
         cls.hosts_client = cls.os_adm.volume_hosts_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/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 049544d..d0e8b99 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -23,10 +23,10 @@
 
 LOG = logging.getLogger(__name__)
 
-VOLUME_FIELDS = ('id', 'display_name')
+VOLUME_FIELDS = ('id', 'name')
 
 
-class VolumesListTest(base.BaseVolumeV1Test):
+class VolumesV2ListTestJSON(base.BaseVolumeV2Test):
 
     """
     This test creates a number of 1G volumes. To run successfully,
@@ -48,7 +48,7 @@
             return
 
         def str_vol(vol):
-            return "%s:%s" % (vol['id'], vol['display_name'])
+            return "%s:%s" % (vol['id'], vol['name'])
 
         raw_msg = "Could not find volumes %s in expected list %s; fetched %s"
         self.fail(raw_msg % ([str_vol(v) for v in missing_vols],
@@ -57,7 +57,7 @@
 
     @classmethod
     def setUpClass(cls):
-        super(VolumesListTest, cls).setUpClass()
+        super(VolumesV2ListTestJSON, cls).setUpClass()
         cls.client = cls.volumes_client
 
         # Create 3 test volumes
@@ -67,7 +67,6 @@
         for i in range(3):
             try:
                 volume = cls.create_volume(metadata=cls.metadata)
-
                 resp, volume = cls.client.get_volume(volume['id'])
                 cls.volume_list.append(volume)
                 cls.volume_id_list.append(volume['id'])
@@ -90,9 +89,10 @@
         for volid in cls.volume_id_list:
             resp, _ = cls.client.delete_volume(volid)
             cls.client.wait_for_resource_deletion(volid)
-        super(VolumesListTest, cls).tearDownClass()
+        super(VolumesV2ListTestJSON, cls).tearDownClass()
 
-    def _list_by_param_value_and_assert(self, params, with_detail=False):
+    def _list_by_param_value_and_assert(self, params, expected_list=None,
+                                        with_detail=False):
         """
         Perform list or list_details action with given params
         and validates result.
@@ -104,17 +104,22 @@
             resp, fetched_vol_list = self.client.list_volumes(params=params)
 
         self.assertEqual(200, resp.status)
+        if expected_list is None:
+            expected_list = self.volume_list
+        self.assertVolumesIn(fetched_vol_list, expected_list,
+                             fields=VOLUME_FIELDS)
         # Validating params of fetched volumes
-        for volume in fetched_vol_list:
-            for key in params:
-                msg = "Failed to list volumes %s by %s" % \
-                      ('details' if with_detail else '', key)
-                if key == 'metadata':
-                    self.assertThat(volume[key].items(),
-                                    ContainsAll(params[key].items()),
-                                    msg)
-                else:
-                    self.assertEqual(params[key], volume[key], msg)
+        if with_detail:
+            for volume in fetched_vol_list:
+                for key in params:
+                    msg = "Failed to list volumes %s by %s" % \
+                          ('details' if with_detail else '', key)
+                    if key == 'metadata':
+                        self.assertThat(volume[key].items(),
+                                        ContainsAll(params[key].items()),
+                                        msg)
+                    else:
+                        self.assertEqual(params[key], volume[key], msg)
 
     @attr(type='smoke')
     def test_volume_list(self):
@@ -136,64 +141,44 @@
     @attr(type='gate')
     def test_volume_list_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {'display_name': volume['display_name']}
+        params = {'name': volume['name']}
         resp, fetched_vol = self.client.list_volumes(params)
         self.assertEqual(200, resp.status)
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
-        self.assertEqual(fetched_vol[0]['display_name'],
-                         volume['display_name'])
+        self.assertEqual(fetched_vol[0]['name'], volume['name'])
 
     @attr(type='gate')
     def test_volume_list_details_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {'display_name': volume['display_name']}
+        params = {'name': volume['name']}
         resp, fetched_vol = self.client.list_volumes_with_detail(params)
         self.assertEqual(200, resp.status)
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
-        self.assertEqual(fetched_vol[0]['display_name'],
-                         volume['display_name'])
+        self.assertEqual(fetched_vol[0]['name'], volume['name'])
 
     @attr(type='gate')
     def test_volumes_list_by_status(self):
         params = {'status': 'available'}
-        resp, fetched_list = self.client.list_volumes(params)
-        self.assertEqual(200, resp.status)
-        for volume in fetched_list:
-            self.assertEqual('available', volume['status'])
-        self.assertVolumesIn(fetched_list, self.volume_list,
-                             fields=VOLUME_FIELDS)
+        self._list_by_param_value_and_assert(params)
 
     @attr(type='gate')
     def test_volumes_list_details_by_status(self):
         params = {'status': 'available'}
-        resp, fetched_list = self.client.list_volumes_with_detail(params)
-        self.assertEqual(200, resp.status)
-        for volume in fetched_list:
-            self.assertEqual('available', volume['status'])
-        self.assertVolumesIn(fetched_list, self.volume_list)
+        self._list_by_param_value_and_assert(params, with_detail=True)
 
     @attr(type='gate')
     def test_volumes_list_by_availability_zone(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         zone = volume['availability_zone']
         params = {'availability_zone': zone}
-        resp, fetched_list = self.client.list_volumes(params)
-        self.assertEqual(200, resp.status)
-        for volume in fetched_list:
-            self.assertEqual(zone, volume['availability_zone'])
-        self.assertVolumesIn(fetched_list, self.volume_list,
-                             fields=VOLUME_FIELDS)
+        self._list_by_param_value_and_assert(params)
 
     @attr(type='gate')
     def test_volumes_list_details_by_availability_zone(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         zone = volume['availability_zone']
         params = {'availability_zone': zone}
-        resp, fetched_list = self.client.list_volumes_with_detail(params)
-        self.assertEqual(200, resp.status)
-        for volume in fetched_list:
-            self.assertEqual(zone, volume['availability_zone'])
-        self.assertVolumesIn(fetched_list, self.volume_list)
+        self._list_by_param_value_and_assert(params, with_detail=True)
 
     @attr(type='gate')
     def test_volume_list_with_param_metadata(self):
@@ -211,18 +196,19 @@
     def test_volume_list_param_display_name_and_status(self):
         # Test to list volume when display name and status param is given
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {'display_name': volume['display_name'],
+        params = {'name': volume['name'],
                   'status': 'available'}
-        self._list_by_param_value_and_assert(params)
+        self._list_by_param_value_and_assert(params, expected_list=[volume])
 
     @attr(type='gate')
     def test_volume_list_with_detail_param_display_name_and_status(self):
         # Test to list volume when name and status param is given
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
-        params = {'display_name': volume['display_name'],
+        params = {'name': volume['name'],
                   'status': 'available'}
-        self._list_by_param_value_and_assert(params, with_detail=True)
+        self._list_by_param_value_and_assert(params, expected_list=[volume],
+                                             with_detail=True)
 
 
-class VolumeListTestXML(VolumesListTest):
+class VolumesV2ListTestXML(VolumesV2ListTestJSON):
     _interface = 'xml'
diff --git a/tempest/clients.py b/tempest/clients.py
index fd46656..778defb 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -156,6 +156,8 @@
     ExtensionsClientJSON as VolumeExtensionClientJSON
 from tempest.services.volume.json.snapshots_client import SnapshotsClientJSON
 from tempest.services.volume.json.volumes_client import VolumesClientJSON
+from tempest.services.volume.v2.json.volumes_client import VolumesV2ClientJSON
+from tempest.services.volume.v2.xml.volumes_client import VolumesV2ClientXML
 from tempest.services.volume.xml.admin.volume_hosts_client import \
     VolumeHostsClientXML
 from tempest.services.volume.xml.admin.volume_types_client import \
@@ -216,6 +218,7 @@
                 auth_provider)
             self.snapshots_client = SnapshotsClientXML(auth_provider)
             self.volumes_client = VolumesClientXML(auth_provider)
+            self.volumes_v2_client = VolumesV2ClientXML(auth_provider)
             self.volume_types_client = VolumeTypesClientXML(
                 auth_provider)
             self.identity_client = IdentityClientXML(auth_provider)
@@ -280,6 +283,7 @@
                 auth_provider)
             self.snapshots_client = SnapshotsClientJSON(auth_provider)
             self.volumes_client = VolumesClientJSON(auth_provider)
+            self.volumes_v2_client = VolumesV2ClientJSON(auth_provider)
             self.volume_types_client = VolumeTypesClientJSON(
                 auth_provider)
             self.identity_client = IdentityClientJSON(auth_provider)
diff --git a/tempest/config.py b/tempest/config.py
index 068193b..a632f84 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -393,6 +393,9 @@
     cfg.BoolOpt('api_v1',
                 default=True,
                 help="Is the v1 volume API enabled"),
+    cfg.BoolOpt('api_v2',
+                default=True,
+                help="Is the v2 volume API enabled"),
 ]
 
 
diff --git a/tempest/services/volume/v2/json/volumes_client.py b/tempest/services/volume/v2/json/volumes_client.py
index 0524212..7b34e0e 100644
--- a/tempest/services/volume/v2/json/volumes_client.py
+++ b/tempest/services/volume/v2/json/volumes_client.py
@@ -24,14 +24,15 @@
 CONF = config.CONF
 
 
-class VolumesClientJSON(RestClient):
+class VolumesV2ClientJSON(RestClient):
     """
-    Client class to send CRUD Volume API requests to a Cinder endpoint
+    Client class to send CRUD Volume V2 API requests to a Cinder endpoint
     """
 
     def __init__(self, auth_provider):
-        super(VolumesClientJSON, self).__init__(auth_provider)
+        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
@@ -72,7 +73,7 @@
         Creates a new Volume.
         size(Required): Size of volume in GB.
         Following optional keyword arguments are accepted:
-        display_name: Optional Volume Name.
+        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
@@ -147,7 +148,7 @@
     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_name = body['name']
         volume_status = body['status']
         start = int(time.time())
 
@@ -202,13 +203,13 @@
                                self.headers)
         return resp, body
 
-    def create_volume_transfer(self, vol_id, display_name=None):
+    def create_volume_transfer(self, vol_id, name=None):
         """Create a volume transfer."""
         post_body = {
             'volume_id': vol_id
         }
-        if display_name:
-            post_body['name'] = display_name
+        if name:
+            post_body['name'] = name
         post_body = json.dumps({'transfer': post_body})
         resp, body = self.post('os-volume-transfer',
                                post_body,
diff --git a/tempest/services/volume/v2/xml/volumes_client.py b/tempest/services/volume/v2/xml/volumes_client.py
index deb56fd..8ecf982 100644
--- a/tempest/services/volume/v2/xml/volumes_client.py
+++ b/tempest/services/volume/v2/xml/volumes_client.py
@@ -30,13 +30,15 @@
 CONF = config.CONF
 
 
-class VolumesClientXML(RestClientXML):
+class VolumesV2ClientXML(RestClientXML):
     """
     Client class to send CRUD Volume API requests to a Cinder endpoint
     """
 
     def __init__(self, auth_provider):
-        super(VolumesClientXML, self).__init__(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
@@ -92,8 +94,6 @@
         volumes = []
         if body is not None:
             volumes += [self._parse_volume(vol) for vol in list(body)]
-        for v in volumes:
-            v = self._check_if_bootable(v)
         return resp, volumes
 
     def list_volumes_with_detail(self, params=None):
@@ -124,7 +124,7 @@
         """Creates a new Volume.
 
         :param size: Size of volume in GB. (Required)
-        :param display_name: Optional Volume Name.
+        :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
@@ -285,12 +285,12 @@
             body = xml_to_json(etree.fromstring(body))
         return resp, body
 
-    def create_volume_transfer(self, vol_id, display_name=None):
+    def create_volume_transfer(self, vol_id, name=None):
         """Create a volume transfer."""
         post_body = Element("transfer",
                             volume_id=vol_id)
-        if display_name:
-            post_body.add_attr('name', display_name)
+        if name:
+            post_body.add_attr('name', name)
         resp, body = self.post('os-volume-transfer',
                                str(Document(post_body)),
                                self.headers)