blob: fcbc982056e85e6507e916ff79192140ba5a796e [file] [log] [blame]
Attila Fazekas9fa29472014-08-18 09:48:00 +02001# Copyright 2012 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040016import time
17
Jordan Pittier1189dd12015-07-09 16:03:56 +020018import testtools
19
Attila Fazekas9fa29472014-08-18 09:48:00 +020020from tempest.api.volume import base
lkuchlanb5992422017-11-26 16:49:18 +020021from tempest.common import utils
Yaroslav Lobankoved3a35b2016-03-24 22:41:30 -050022from tempest.common import waiters
Jordan Pittier1189dd12015-07-09 16:03:56 +020023from tempest import config
Ken'ichi Ohmichi6b279c72017-01-27 18:26:59 -080024from tempest.lib import decorators
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040025from tempest.lib import exceptions as lib_exc
Attila Fazekas9fa29472014-08-18 09:48:00 +020026
Jordan Pittier1189dd12015-07-09 16:03:56 +020027CONF = config.CONF
28
Attila Fazekas9fa29472014-08-18 09:48:00 +020029
Ken'ichi Ohmichie8afb8c2017-03-27 11:25:37 -070030class VolumesExtendTest(base.BaseVolumeTest):
zhufl3fdd35a2020-04-16 16:29:47 +080031 """Test volume extend"""
Attila Fazekas9fa29472014-08-18 09:48:00 +020032
Ken'ichi Ohmichi6b279c72017-01-27 18:26:59 -080033 @decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
Attila Fazekas9fa29472014-08-18 09:48:00 +020034 def test_volume_extend(self):
zhufl3fdd35a2020-04-16 16:29:47 +080035 """Test extend a volume"""
Attila Fazekas9fa29472014-08-18 09:48:00 +020036 # Extend Volume Test.
zhufl8802c832019-06-03 09:31:36 +080037 volume = self.create_volume(imageRef=self.image_ref)
TommyLikefcda77b2018-01-18 15:25:12 +080038 extend_size = volume['size'] * 2
zhufl7a8f29d2017-02-17 10:16:45 +080039 self.volumes_client.extend_volume(volume['id'],
lkuchlanb21fc572016-11-28 12:25:22 +020040 new_size=extend_size)
lkuchlan52d7b0d2016-11-07 20:53:19 +020041 waiters.wait_for_volume_resource_status(self.volumes_client,
42 volume['id'], 'available')
zhufl7a8f29d2017-02-17 10:16:45 +080043 volume = self.volumes_client.show_volume(volume['id'])['volume']
Avi Avrahamd77d3d12017-02-15 16:45:25 +020044 self.assertEqual(volume['size'], extend_size)
Jordan Pittier1189dd12015-07-09 16:03:56 +020045
46 @decorators.idempotent_id('86be1cba-2640-11e5-9c82-635fb964c912')
47 @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
48 "Cinder volume snapshots are disabled")
49 def test_volume_extend_when_volume_has_snapshot(self):
zhufl3fdd35a2020-04-16 16:29:47 +080050 """Test extending a volume which has a snapshot"""
Jordan Pittier1189dd12015-07-09 16:03:56 +020051 volume = self.create_volume()
52 self.create_snapshot(volume['id'])
53
TommyLikefcda77b2018-01-18 15:25:12 +080054 extend_size = volume['size'] * 2
Jordan Pittier1189dd12015-07-09 16:03:56 +020055 self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
56
57 waiters.wait_for_volume_resource_status(self.volumes_client,
58 volume['id'], 'available')
59 resized_volume = self.volumes_client.show_volume(
60 volume['id'])['volume']
61 self.assertEqual(extend_size, resized_volume['size'])
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040062
63
Lee Yarwood4bd9edb2020-01-31 17:26:25 +000064class BaseVolumesExtendAttachedTest(base.BaseVolumeTest):
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040065 """Tests extending the size of an attached volume."""
Ghanshyam Mann7d91b692020-03-03 10:21:50 -060066 create_default_network = True
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040067
68 # We need admin credentials for getting instance action event details. By
69 # default a non-admin can list and show instance actions if they own the
70 # server instance, but since the event details can contain error messages
71 # and tracebacks, like an instance fault, those are not viewable by
72 # non-admins. This is obviously not a great user experience since the user
73 # may not know when the operation is actually complete. A microversion in
74 # the compute API will be added so that non-admins can see instance action
75 # events but will continue to hide the traceback field.
76 # TODO(mriedem): Change this to not rely on the admin user to get the event
77 # details once that microversion is available in Nova.
78 credentials = ['primary', 'admin']
79
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040080 # NOTE(mriedem): The minimum required volume API version is 3.42 and the
81 # minimum required compute API microversion is 2.51, but the compute call
82 # is implicit - Cinder calls Nova at that microversion, Tempest does not.
Sophie Huangd458bf32021-10-12 17:08:41 +000083 volume_min_microversion = '3.42'
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040084
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040085 def _find_extend_volume_instance_action(self, server_id):
86 actions = self.servers_client.list_instance_actions(
87 server_id)['instanceActions']
88 for action in actions:
89 if action['action'] == 'extend_volume':
90 return action
91
92 def _find_extend_volume_instance_action_finish_event(self, action):
93 # This has to be called by an admin client otherwise
94 # the events don't show up.
rchouhan25bf8ce2018-01-29 17:39:36 +053095 action = self.os_admin.servers_client.show_instance_action(
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040096 action['instance_uuid'], action['request_id'])['instanceAction']
97 for event in action['events']:
98 if (event['event'] == 'compute_extend_volume' and
99 event['finish_time']):
100 return event
101
Lee Yarwood4bd9edb2020-01-31 17:26:25 +0000102 def _test_extend_attached_volume(self, volume):
Matt Riedemann0cc76bf2017-07-05 17:29:31 -0400103 """This is a happy path test which does the following:
104
Matt Riedemann0cc76bf2017-07-05 17:29:31 -0400105 * Create a server instance.
106 * Attach the volume to the server.
107 * Wait for the volume status to be "in-use".
108 * Extend the size of the volume and wait for the volume status to go
109 back to "in-use".
110 * Assert the volume size change is reflected in the volume API.
111 * Wait for the "compute_extend_volume" instance action event to show
112 up in the compute API with the success or failure status. We fail
113 if we timeout waiting for the instance action event to show up, or
114 if the action on the server fails.
115 """
Matt Riedemann0cc76bf2017-07-05 17:29:31 -0400116 # Create a test server. Will be automatically cleaned up on teardown.
117 server = self.create_server()
118 # Attach the volume to the server and wait for the volume status to be
119 # "in-use".
120 self.attach_volume(server['id'], volume['id'])
121 # Extend the size of the volume. If this is successful, the volume API
122 # will change the status on the volume to "extending" before doing an
123 # RPC cast to the volume manager on the backend. Note that we multiply
124 # the size of the volume since certain Cinder backends, e.g. ScaleIO,
125 # require multiples of 8GB.
126 extend_size = volume['size'] * 2
127 self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
128 # The volume status should go back to in-use since it is still attached
129 # to the server instance.
130 waiters.wait_for_volume_resource_status(self.volumes_client,
131 volume['id'], 'in-use')
132 # Assert that the volume size has changed in the volume API.
133 volume = self.volumes_client.show_volume(volume['id'])['volume']
134 self.assertEqual(extend_size, volume['size'])
135 # Now we wait for the "compute_extend_volume" instance action event
136 # to show up for the server instance. This is our indication that the
137 # asynchronous operation is complete on the compute side.
138 start_time = int(time.time())
139 timeout = self.servers_client.build_timeout
140 action = self._find_extend_volume_instance_action(server['id'])
141 while action is None and int(time.time()) - start_time < timeout:
142 time.sleep(self.servers_client.build_interval)
143 action = self._find_extend_volume_instance_action(server['id'])
144
145 if action is None:
146 msg = ("Timed out waiting to get 'extend_volume' instance action "
147 "record for server %(server)s after %(timeout)s seconds." %
148 {'server': server['id'], 'timeout': timeout})
149 raise lib_exc.TimeoutException(msg)
150
151 # Now that we found the extend_volume instance action, we can wait for
152 # the compute_extend_volume instance action event to show up to
153 # indicate the operation is complete.
154 start_time = int(time.time())
155 event = self._find_extend_volume_instance_action_finish_event(action)
156 while event is None and int(time.time()) - start_time < timeout:
157 time.sleep(self.servers_client.build_interval)
158 event = self._find_extend_volume_instance_action_finish_event(
159 action)
160
161 if event is None:
162 msg = ("Timed out waiting to get 'compute_extend_volume' instance "
163 "action event record for server %(server)s and request "
164 "%(request_id)s after %(timeout)s seconds." %
165 {'server': server['id'],
166 'request_id': action['request_id'],
167 'timeout': timeout})
168 raise lib_exc.TimeoutException(msg)
169
170 # Finally, assert that the action completed successfully.
171 self.assertTrue(
172 event['result'].lower() == 'success',
173 "Unexpected compute_extend_volume result '%(result)s' for request "
174 "%(request_id)s." %
175 {'result': event['result'],
176 'request_id': action['request_id']})
Lee Yarwood4bd9edb2020-01-31 17:26:25 +0000177
178
179class VolumesExtendAttachedTest(BaseVolumesExtendAttachedTest):
180
181 @decorators.idempotent_id('301f5a30-1c6f-4ea0-be1a-91fd28d44354')
182 @testtools.skipUnless(CONF.volume_feature_enabled.extend_attached_volume,
183 "Attached volume extend is disabled.")
184 @utils.services('compute')
185 def test_extend_attached_volume(self):
186 volume = self.create_volume()
187 self._test_extend_attached_volume(volume)