diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py index 09131e2bf9..f12d4bbf10 100644 --- a/tempest/api/volume/test_volumes_actions.py +++ b/tempest/api/volume/test_volumes_actions.py @@ -16,7 +16,7 @@ # under the License. from tempest.api.volume.base import BaseVolumeTest -from tempest.common.utils.data_utils import rand_name +from tempest.common.utils import data_utils from tempest.test import attr from tempest.test import services from tempest.test import stresstest @@ -32,8 +32,8 @@ class VolumesActionsTest(BaseVolumeTest): cls.image_client = cls.os.image_client # Create a test shared instance and volume for attach/detach tests - srv_name = rand_name(cls.__name__ + '-Instance-') - vol_name = rand_name(cls.__name__ + '-Volume-') + srv_name = data_utils.rand_name(cls.__name__ + '-Instance-') + vol_name = data_utils.rand_name(cls.__name__ + '-Volume-') resp, cls.server = cls.servers_client.create_server(srv_name, cls.image_ref, cls.flavor_ref) @@ -102,7 +102,7 @@ class VolumesActionsTest(BaseVolumeTest): # it is shared with the other tests. After it is uploaded in Glance, # there is no way to delete it from Cinder, so we delete it from Glance # using the Glance image_client and from Cinder via tearDownClass. - image_name = rand_name('Image-') + image_name = data_utils.rand_name('Image-') resp, body = self.client.upload_volume(self.volume['id'], image_name, self.config.volume.disk_format) @@ -112,6 +112,17 @@ class VolumesActionsTest(BaseVolumeTest): self.image_client.wait_for_image_status(image_id, 'active') self.client.wait_for_volume_status(self.volume['id'], 'available') + @attr(type='gate') + def test_volume_extend(self): + # Extend Volume Test. + extend_size = int(self.volume['size']) + 1 + resp, body = self.client.extend_volume(self.volume['id'], extend_size) + self.assertEqual(202, resp.status) + self.client.wait_for_volume_status(self.volume['id'], 'available') + resp, volume = self.client.get_volume(self.volume['id']) + self.assertEqual(200, resp.status) + self.assertEqual(int(volume['size']), extend_size) + class VolumesActionsTestXML(VolumesActionsTest): _interface = "xml" diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py index 02adc5dc9e..9bab9a0a9f 100644 --- a/tempest/api/volume/test_volumes_negative.py +++ b/tempest/api/volume/test_volumes_negative.py @@ -177,6 +177,41 @@ class VolumesNegativeTest(base.BaseVolumeTest): self.client.detach_volume, 'xxx') + @attr(type=['negative', 'gate']) + def test_volume_extend_with_size_smaller_than_original_size(self): + # Extend volume with smaller size than original size. + extend_size = 0 + self.assertRaises(exceptions.BadRequest, self.client.extend_volume, + self.volume['id'], extend_size) + + @attr(type=['negative', 'gate']) + def test_volume_extend_with_non_number_size(self): + # Extend volume when size is non number. + extend_size = 'abc' + self.assertRaises(exceptions.BadRequest, self.client.extend_volume, + self.volume['id'], extend_size) + + @attr(type=['negative', 'gate']) + def test_volume_extend_with_None_size(self): + # Extend volume with None size. + extend_size = None + self.assertRaises(exceptions.BadRequest, self.client.extend_volume, + self.volume['id'], extend_size) + + @attr(type=['negative', 'gate']) + def test_volume_extend_with_nonexistent_volume_id(self): + # Extend volume size when volume is nonexistent. + extend_size = int(self.volume['size']) + 1 + self.assertRaises(exceptions.NotFound, self.client.extend_volume, + str(uuid.uuid4()), extend_size) + + @attr(type=['negative', 'gate']) + def test_volume_extend_without_passing_volume_id(self): + # Extend volume size when passing volume id is None. + extend_size = int(self.volume['size']) + 1 + self.assertRaises(exceptions.NotFound, self.client.extend_volume, + None, extend_size) + class VolumesNegativeTestXML(VolumesNegativeTest): _interface = 'xml' diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py index 62a6e24dc8..f054a2b455 100644 --- a/tempest/services/volume/json/volumes_client.py +++ b/tempest/services/volume/json/volumes_client.py @@ -154,3 +154,13 @@ class VolumesClientJSON(RestClient): 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, self.headers) + return resp, body diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py index b59ec039dc..f5d8beb2d0 100644 --- a/tempest/services/volume/xml/volumes_client.py +++ b/tempest/services/volume/xml/volumes_client.py @@ -227,3 +227,13 @@ class VolumesClientXML(RestClientXML): resp, body = self.post(url, str(Document(post_body)), self.headers) volume = xml_to_json(etree.fromstring(body)) return resp, volume + + def extend_volume(self, volume_id, extend_size): + """Extend a volume.""" + post_body = Element("os-extend", + new_size=extend_size) + url = 'volumes/%s/action' % str(volume_id) + resp, body = self.post(url, str(Document(post_body)), self.headers) + if body: + body = xml_to_json(etree.fromstring(body)) + return resp, body