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