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)