blob: c3f44e2d87f15e4dedf325982037d11be23f6e17 [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):
Attila Fazekas9fa29472014-08-18 09:48:00 +020031
Ken'ichi Ohmichi6b279c72017-01-27 18:26:59 -080032 @decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
Attila Fazekas9fa29472014-08-18 09:48:00 +020033 def test_volume_extend(self):
34 # Extend Volume Test.
zhufl8802c832019-06-03 09:31:36 +080035 volume = self.create_volume(imageRef=self.image_ref)
TommyLikefcda77b2018-01-18 15:25:12 +080036 extend_size = volume['size'] * 2
zhufl7a8f29d2017-02-17 10:16:45 +080037 self.volumes_client.extend_volume(volume['id'],
lkuchlanb21fc572016-11-28 12:25:22 +020038 new_size=extend_size)
lkuchlan52d7b0d2016-11-07 20:53:19 +020039 waiters.wait_for_volume_resource_status(self.volumes_client,
40 volume['id'], 'available')
zhufl7a8f29d2017-02-17 10:16:45 +080041 volume = self.volumes_client.show_volume(volume['id'])['volume']
Avi Avrahamd77d3d12017-02-15 16:45:25 +020042 self.assertEqual(volume['size'], extend_size)
Jordan Pittier1189dd12015-07-09 16:03:56 +020043
44 @decorators.idempotent_id('86be1cba-2640-11e5-9c82-635fb964c912')
45 @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
46 "Cinder volume snapshots are disabled")
47 def test_volume_extend_when_volume_has_snapshot(self):
48 volume = self.create_volume()
49 self.create_snapshot(volume['id'])
50
TommyLikefcda77b2018-01-18 15:25:12 +080051 extend_size = volume['size'] * 2
Jordan Pittier1189dd12015-07-09 16:03:56 +020052 self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
53
54 waiters.wait_for_volume_resource_status(self.volumes_client,
55 volume['id'], 'available')
56 resized_volume = self.volumes_client.show_volume(
57 volume['id'])['volume']
58 self.assertEqual(extend_size, resized_volume['size'])
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040059
60
61class VolumesExtendAttachedTest(base.BaseVolumeTest):
62 """Tests extending the size of an attached volume."""
63
64 # We need admin credentials for getting instance action event details. By
65 # default a non-admin can list and show instance actions if they own the
66 # server instance, but since the event details can contain error messages
67 # and tracebacks, like an instance fault, those are not viewable by
68 # non-admins. This is obviously not a great user experience since the user
69 # may not know when the operation is actually complete. A microversion in
70 # the compute API will be added so that non-admins can see instance action
71 # events but will continue to hide the traceback field.
72 # TODO(mriedem): Change this to not rely on the admin user to get the event
73 # details once that microversion is available in Nova.
74 credentials = ['primary', 'admin']
75
76 _api_version = 3
77 # NOTE(mriedem): The minimum required volume API version is 3.42 and the
78 # minimum required compute API microversion is 2.51, but the compute call
79 # is implicit - Cinder calls Nova at that microversion, Tempest does not.
80 min_microversion = '3.42'
81
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040082 def _find_extend_volume_instance_action(self, server_id):
83 actions = self.servers_client.list_instance_actions(
84 server_id)['instanceActions']
85 for action in actions:
86 if action['action'] == 'extend_volume':
87 return action
88
89 def _find_extend_volume_instance_action_finish_event(self, action):
90 # This has to be called by an admin client otherwise
91 # the events don't show up.
rchouhan25bf8ce2018-01-29 17:39:36 +053092 action = self.os_admin.servers_client.show_instance_action(
Matt Riedemann0cc76bf2017-07-05 17:29:31 -040093 action['instance_uuid'], action['request_id'])['instanceAction']
94 for event in action['events']:
95 if (event['event'] == 'compute_extend_volume' and
96 event['finish_time']):
97 return event
98
99 @decorators.idempotent_id('301f5a30-1c6f-4ea0-be1a-91fd28d44354')
100 @testtools.skipUnless(CONF.volume_feature_enabled.extend_attached_volume,
101 "Attached volume extend is disabled.")
lkuchlanb5992422017-11-26 16:49:18 +0200102 @utils.services('compute')
Matt Riedemann0cc76bf2017-07-05 17:29:31 -0400103 def test_extend_attached_volume(self):
104 """This is a happy path test which does the following:
105
106 * Create a volume at the configured volume_size.
107 * Create a server instance.
108 * Attach the volume to the server.
109 * Wait for the volume status to be "in-use".
110 * Extend the size of the volume and wait for the volume status to go
111 back to "in-use".
112 * Assert the volume size change is reflected in the volume API.
113 * Wait for the "compute_extend_volume" instance action event to show
114 up in the compute API with the success or failure status. We fail
115 if we timeout waiting for the instance action event to show up, or
116 if the action on the server fails.
117 """
118 # Create a test volume. Will be automatically cleaned up on teardown.
119 volume = self.create_volume()
120 # Create a test server. Will be automatically cleaned up on teardown.
121 server = self.create_server()
122 # Attach the volume to the server and wait for the volume status to be
123 # "in-use".
124 self.attach_volume(server['id'], volume['id'])
125 # Extend the size of the volume. If this is successful, the volume API
126 # will change the status on the volume to "extending" before doing an
127 # RPC cast to the volume manager on the backend. Note that we multiply
128 # the size of the volume since certain Cinder backends, e.g. ScaleIO,
129 # require multiples of 8GB.
130 extend_size = volume['size'] * 2
131 self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
132 # The volume status should go back to in-use since it is still attached
133 # to the server instance.
134 waiters.wait_for_volume_resource_status(self.volumes_client,
135 volume['id'], 'in-use')
136 # Assert that the volume size has changed in the volume API.
137 volume = self.volumes_client.show_volume(volume['id'])['volume']
138 self.assertEqual(extend_size, volume['size'])
139 # Now we wait for the "compute_extend_volume" instance action event
140 # to show up for the server instance. This is our indication that the
141 # asynchronous operation is complete on the compute side.
142 start_time = int(time.time())
143 timeout = self.servers_client.build_timeout
144 action = self._find_extend_volume_instance_action(server['id'])
145 while action is None and int(time.time()) - start_time < timeout:
146 time.sleep(self.servers_client.build_interval)
147 action = self._find_extend_volume_instance_action(server['id'])
148
149 if action is None:
150 msg = ("Timed out waiting to get 'extend_volume' instance action "
151 "record for server %(server)s after %(timeout)s seconds." %
152 {'server': server['id'], 'timeout': timeout})
153 raise lib_exc.TimeoutException(msg)
154
155 # Now that we found the extend_volume instance action, we can wait for
156 # the compute_extend_volume instance action event to show up to
157 # indicate the operation is complete.
158 start_time = int(time.time())
159 event = self._find_extend_volume_instance_action_finish_event(action)
160 while event is None and int(time.time()) - start_time < timeout:
161 time.sleep(self.servers_client.build_interval)
162 event = self._find_extend_volume_instance_action_finish_event(
163 action)
164
165 if event is None:
166 msg = ("Timed out waiting to get 'compute_extend_volume' instance "
167 "action event record for server %(server)s and request "
168 "%(request_id)s after %(timeout)s seconds." %
169 {'server': server['id'],
170 'request_id': action['request_id'],
171 'timeout': timeout})
172 raise lib_exc.TimeoutException(msg)
173
174 # Finally, assert that the action completed successfully.
175 self.assertTrue(
176 event['result'].lower() == 'success',
177 "Unexpected compute_extend_volume result '%(result)s' for request "
178 "%(request_id)s." %
179 {'result': event['result'],
180 'request_id': action['request_id']})