blob: 9e97f4797ed28f0607c7acde27f8d678516a974d [file] [log] [blame]
Attila Fazekas0abbc952013-07-01 19:19:42 +02001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
Lee Yarwood0b4bc3d2021-11-11 17:45:25 +000013import os
lkuchlan52d7b0d2016-11-07 20:53:19 +020014import re
Attila Fazekas0abbc952013-07-01 19:19:42 +020015import time
16
Doug Hellmann583ce2c2015-03-11 14:55:46 +000017from oslo_log import log as logging
Matthew Treinish01472ff2015-02-20 17:26:52 -050018
Attila Fazekas0abbc952013-07-01 19:19:42 +020019from tempest import config
20from tempest import exceptions
zhufl88c89b52016-07-01 18:09:05 +080021from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050022from tempest.lib import exceptions as lib_exc
Attila Fazekas0abbc952013-07-01 19:19:42 +020023
Sean Dague86bd8422013-12-20 09:56:44 -050024CONF = config.CONF
Attila Fazekas0abbc952013-07-01 19:19:42 +020025LOG = logging.getLogger(__name__)
26
27
Matt Riedemannf748c112017-02-03 11:33:11 -050028def _get_task_state(body):
29 return body.get('OS-EXT-STS:task_state', None)
30
31
Attila Fazekas0abbc952013-07-01 19:19:42 +020032# NOTE(afazekas): This function needs to know a token and a subject.
Ken'ichi Ohmichi39437e22013-10-06 00:21:38 +090033def wait_for_server_status(client, server_id, status, ready_wait=True,
Artom Lifshitzda48e4e2021-11-22 15:59:15 -050034 extra_timeout=0, raise_on_error=True,
35 request_id=None):
Attila Fazekas0abbc952013-07-01 19:19:42 +020036 """Waits for a server to reach a given status."""
37
Attila Fazekas0abbc952013-07-01 19:19:42 +020038 # NOTE(afazekas): UNKNOWN status possible on ERROR
39 # or in a very early stage.
ghanshyam0f825252015-08-25 16:02:50 +090040 body = client.show_server(server_id)['server']
Attila Fazekas0abbc952013-07-01 19:19:42 +020041 old_status = server_status = body['status']
42 old_task_state = task_state = _get_task_state(body)
43 start_time = int(time.time())
Ken'ichi Ohmichi39437e22013-10-06 00:21:38 +090044 timeout = client.build_timeout + extra_timeout
Attila Fazekas0abbc952013-07-01 19:19:42 +020045 while True:
46 # NOTE(afazekas): Now the BUILD status only reached
Shane Wang111f86c2014-02-20 16:40:22 +080047 # between the UNKNOWN->ACTIVE transition.
Attila Fazekas0abbc952013-07-01 19:19:42 +020048 # TODO(afazekas): enumerate and validate the stable status set
49 if status == 'BUILD' and server_status != 'UNKNOWN':
Marian Krcmarikca5ddb42022-08-29 17:01:01 +020050 return body
Attila Fazekas0abbc952013-07-01 19:19:42 +020051 if server_status == status:
52 if ready_wait:
53 if status == 'BUILD':
Marian Krcmarikca5ddb42022-08-29 17:01:01 +020054 return body
Attila Fazekas0abbc952013-07-01 19:19:42 +020055 # NOTE(afazekas): The instance is in "ready for action state"
56 # when no task in progress
Ken'ichi Ohmichi534a05e2016-08-03 14:45:15 -070057 if task_state is None:
Attila Fazekas0abbc952013-07-01 19:19:42 +020058 # without state api extension 3 sec usually enough
Sean Dague86bd8422013-12-20 09:56:44 -050059 time.sleep(CONF.compute.ready_wait)
Marian Krcmarikca5ddb42022-08-29 17:01:01 +020060 return body
Attila Fazekas0abbc952013-07-01 19:19:42 +020061 else:
Marian Krcmarikca5ddb42022-08-29 17:01:01 +020062 return body
Attila Fazekas0abbc952013-07-01 19:19:42 +020063
64 time.sleep(client.build_interval)
ghanshyam0f825252015-08-25 16:02:50 +090065 body = client.show_server(server_id)['server']
Attila Fazekas0abbc952013-07-01 19:19:42 +020066 server_status = body['status']
67 task_state = _get_task_state(body)
68 if (server_status != old_status) or (task_state != old_task_state):
69 LOG.info('State transition "%s" ==> "%s" after %d second wait',
70 '/'.join((old_status, str(old_task_state))),
71 '/'.join((server_status, str(task_state))),
72 time.time() - start_time)
Zhi Kun Liue5401762013-09-11 20:45:48 +080073 if (server_status == 'ERROR') and raise_on_error:
Artom Lifshitzda48e4e2021-11-22 15:59:15 -050074 details = ''
Attila Fazekas0462a7f2014-06-20 07:38:06 +020075 if 'fault' in body:
Artom Lifshitzda48e4e2021-11-22 15:59:15 -050076 details += 'Fault: %s.' % body['fault']
77 if request_id:
Martin Kopec3f844b22023-05-24 17:00:25 +020078 details += ' Request ID of server operation performed before'
79 details += ' checking the server status %s.' % request_id
Artom Lifshitzda48e4e2021-11-22 15:59:15 -050080 raise exceptions.BuildErrorException(details, server_id=server_id)
Attila Fazekas0abbc952013-07-01 19:19:42 +020081
Ken'ichi Ohmichi39437e22013-10-06 00:21:38 +090082 timed_out = int(time.time()) - start_time >= timeout
Attila Fazekas0abbc952013-07-01 19:19:42 +020083
84 if timed_out:
Matt Riedemann629fa7c2013-12-11 18:20:56 -080085 expected_task_state = 'None' if ready_wait else 'n/a'
86 message = ('Server %(server_id)s failed to reach %(status)s '
87 'status and task state "%(expected_task_state)s" '
88 'within the required time (%(timeout)s s).' %
89 {'server_id': server_id,
90 'status': status,
91 'expected_task_state': expected_task_state,
92 'timeout': timeout})
Artom Lifshitzda48e4e2021-11-22 15:59:15 -050093 if request_id:
Martin Kopec3f844b22023-05-24 17:00:25 +020094 message += ' Request ID of server operation performed before'
95 message += ' checking the server status %s.' % request_id
Attila Fazekas0abbc952013-07-01 19:19:42 +020096 message += ' Current status: %s.' % server_status
Matt Riedemann629fa7c2013-12-11 18:20:56 -080097 message += ' Current task state: %s.' % task_state
zhufl88c89b52016-07-01 18:09:05 +080098 caller = test_utils.find_test_caller()
Matt Riedemann128c23e2014-05-02 13:46:39 -070099 if caller:
100 message = '(%s) %s' % (caller, message)
guo yunxianebb15f22016-11-01 21:03:35 +0800101 raise lib_exc.TimeoutException(message)
Attila Fazekas0abbc952013-07-01 19:19:42 +0200102 old_status = server_status
103 old_task_state = task_state
Matt Riedemannc00f3262013-12-14 12:03:55 -0800104
105
Amit Uniyalf696d832024-05-07 12:24:09 +0000106def wait_for_server_termination(client, server_id, ignore_error=False,
107 request_id=None):
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000108 """Waits for server to reach termination."""
Matt Riedemannf748c112017-02-03 11:33:11 -0500109 try:
110 body = client.show_server(server_id)['server']
111 except lib_exc.NotFound:
112 return
zhuflf5cff8b2019-04-28 15:26:32 +0800113 old_status = body['status']
114 old_task_state = _get_task_state(body)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000115 start_time = int(time.time())
116 while True:
Matt Riedemannf748c112017-02-03 11:33:11 -0500117 time.sleep(client.build_interval)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000118 try:
ghanshyam0f825252015-08-25 16:02:50 +0900119 body = client.show_server(server_id)['server']
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000120 except lib_exc.NotFound:
121 return
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000122 server_status = body['status']
Matt Riedemannf748c112017-02-03 11:33:11 -0500123 task_state = _get_task_state(body)
124 if (server_status != old_status) or (task_state != old_task_state):
125 LOG.info('State transition "%s" ==> "%s" after %d second wait',
126 '/'.join((old_status, str(old_task_state))),
127 '/'.join((server_status, str(task_state))),
128 time.time() - start_time)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000129 if server_status == 'ERROR' and not ignore_error:
Amit Uniyalf696d832024-05-07 12:24:09 +0000130 details = ("Server %s failed to delete and is in ERROR status." %
131 server_id)
132 if 'fault' in body:
133 details += ' Fault: %s.' % body['fault']
134 if request_id:
135 details += ' Server delete request ID: %s.' % request_id
136 raise lib_exc.DeleteErrorException(details, server_id=server_id)
ericxiett5f65bf52020-09-25 08:42:26 +0000137
ericxiettd0ef93e2019-12-16 08:55:05 +0000138 if server_status == 'SOFT_DELETED':
139 # Soft-deleted instances need to be forcibly deleted to
140 # prevent some test cases from failing.
141 LOG.debug("Automatically force-deleting soft-deleted server %s",
142 server_id)
ericxiett5f65bf52020-09-25 08:42:26 +0000143 try:
144 client.force_delete_server(server_id)
145 except lib_exc.NotFound:
146 # The instance may have been deleted so ignore
147 # NotFound exception
148 return
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000149
150 if int(time.time()) - start_time >= client.build_timeout:
guo yunxianebb15f22016-11-01 21:03:35 +0800151 raise lib_exc.TimeoutException
Matt Riedemannf748c112017-02-03 11:33:11 -0500152 old_status = server_status
153 old_task_state = task_state
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000154
155
Matt Riedemannc00f3262013-12-14 12:03:55 -0800156def wait_for_image_status(client, image_id, status):
157 """Waits for an image to reach a given status.
158
Ken'ichi Ohmichi5d410762015-05-22 01:10:03 +0000159 The client should have a show_image(image_id) method to get the image.
Matt Riedemannc00f3262013-12-14 12:03:55 -0800160 The client should also have build_interval and build_timeout attributes.
161 """
Ghanshyam Mann3562cd02023-08-05 17:22:03 -0700162 show_image = client.show_image
Matt Riedemannc00f3262013-12-14 12:03:55 -0800163
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300164 current_status = 'An unknown status'
165 start = int(time.time())
166 while int(time.time()) - start < client.build_timeout:
167 image = show_image(image_id)
168 # Compute image client returns response wrapped in 'image' element
Xiangfei Zhua8fd20a2016-07-26 21:28:12 -0700169 # which is not the case with Glance image client.
ghanshyam1756e0b2015-08-18 19:19:05 +0900170 if 'image' in image:
171 image = image['image']
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300172
173 current_status = image['status']
174 if current_status == status:
175 return
176 if current_status.lower() == 'killed':
177 raise exceptions.ImageKilledException(image_id=image_id,
178 status=status)
179 if current_status.lower() == 'error':
Matt Riedemannc00f3262013-12-14 12:03:55 -0800180 raise exceptions.AddImageException(image_id=image_id)
181
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300182 time.sleep(client.build_interval)
Matt Riedemannc00f3262013-12-14 12:03:55 -0800183
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300184 message = ('Image %(image_id)s failed to reach %(status)s state '
185 '(current state %(current_status)s) within the required '
186 'time (%(timeout)s s).' % {'image_id': image_id,
187 'status': status,
188 'current_status': current_status,
189 'timeout': client.build_timeout})
zhufl88c89b52016-07-01 18:09:05 +0800190 caller = test_utils.find_test_caller()
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300191 if caller:
192 message = '(%s) %s' % (caller, message)
guo yunxianebb15f22016-11-01 21:03:35 +0800193 raise lib_exc.TimeoutException(message)
Adam Gandelman00682612014-09-02 17:10:36 -0700194
195
Ghanshyam Mannb15b58e2021-04-29 19:45:29 -0500196def wait_for_image_tasks_status(client, image_id, status):
197 """Waits for an image tasks to reach a given status."""
198 pending_tasks = []
199 start = int(time.time())
200 while int(time.time()) - start < client.build_timeout:
201 tasks = client.show_image_tasks(image_id)['tasks']
202
203 pending_tasks = [task for task in tasks if task['status'] != status]
204 if not pending_tasks:
205 return tasks
206 time.sleep(client.build_interval)
207
208 message = ('Image %(image_id)s tasks: %(pending_tasks)s '
209 'failed to reach %(status)s state within the required '
210 'time (%(timeout)s s).' % {'image_id': image_id,
211 'pending_tasks': pending_tasks,
212 'status': status,
213 'timeout': client.build_timeout})
214 caller = test_utils.find_test_caller()
215 if caller:
216 message = '(%s) %s' % (caller, message)
217 raise lib_exc.TimeoutException(message)
218
219
msava88660d42023-07-18 11:27:04 +0300220def wait_for_tasks_status(client, task_id, status):
221 start = int(time.time())
222 while int(time.time()) - start < client.build_timeout:
223 task = client.show_tasks(task_id)
224 if task['status'] == status:
225 return task
226 time.sleep(client.build_interval)
227 message = ('Task %(task_id)s tasks: '
228 'failed to reach %(status)s state within the required '
229 'time (%(timeout)s s).' % {'task_id': task_id,
230 'status': status,
231 'timeout': client.build_timeout})
232 caller = test_utils.find_test_caller()
233 if caller:
234 message = '(%s) %s' % (caller, message)
235 raise lib_exc.TimeoutException(message)
236
237
Dan Smithef8e0542021-02-05 13:05:45 -0800238def wait_for_image_imported_to_stores(client, image_id, stores=None):
PranaliD491d63e2020-08-18 13:29:21 +0000239 """Waits for an image to be imported to all requested stores.
240
Dan Smithef8e0542021-02-05 13:05:45 -0800241 Short circuits to fail if the serer reports failure of any store.
242 If stores is None, just wait for status==active.
243
PranaliD491d63e2020-08-18 13:29:21 +0000244 The client should also have build_interval and build_timeout attributes.
245 """
246
Dan Smithef8e0542021-02-05 13:05:45 -0800247 exc_cls = lib_exc.TimeoutException
PranaliD491d63e2020-08-18 13:29:21 +0000248 start = int(time.time())
Dan Smith466f7062022-10-10 15:25:39 -0700249
250 # NOTE(danms): Don't wait for stores that are read-only as those
251 # will never complete
252 try:
253 store_info = client.info_stores()['stores']
254 stores = ','.join(sorted([
255 store['id'] for store in store_info
256 if store.get('read-only') != 'true' and
257 (not stores or store['id'] in stores.split(','))]))
258 except lib_exc.NotFound:
259 # If multi-store is not enabled, then we can not resolve which
260 # ones are read-only, and stores must have been passed as None
261 # anyway for us to succeed. If not, then we should raise right
262 # now and avoid waiting since we will never see the stores
263 # appear.
264 if stores is not None:
265 raise lib_exc.TimeoutException(
266 'Image service has no store support; '
267 'cowardly refusing to wait for them.')
268
PranaliD491d63e2020-08-18 13:29:21 +0000269 while int(time.time()) - start < client.build_timeout:
270 image = client.show_image(image_id)
Dan Smithef8e0542021-02-05 13:05:45 -0800271 if image['status'] == 'active' and (stores is None or
272 image['stores'] == stores):
PranaliD491d63e2020-08-18 13:29:21 +0000273 return
Dan Smithef8e0542021-02-05 13:05:45 -0800274 if image.get('os_glance_failed_import'):
275 exc_cls = lib_exc.OtherRestClientException
276 break
PranaliD491d63e2020-08-18 13:29:21 +0000277
278 time.sleep(client.build_interval)
279
zhufl414ffba2020-11-19 16:57:06 +0800280 message = ('Image %s failed to import on stores: %s' %
Dan Smithef8e0542021-02-05 13:05:45 -0800281 (image_id, str(image.get('os_glance_failed_import'))))
PranaliD491d63e2020-08-18 13:29:21 +0000282 caller = test_utils.find_test_caller()
283 if caller:
284 message = '(%s) %s' % (caller, message)
Dan Smithef8e0542021-02-05 13:05:45 -0800285 raise exc_cls(message)
PranaliD491d63e2020-08-18 13:29:21 +0000286
287
Ghanshyam Mann4346a822020-07-29 13:45:04 -0500288def wait_for_image_copied_to_stores(client, image_id):
289 """Waits for an image to be copied on all requested stores.
290
291 The client should also have build_interval and build_timeout attributes.
292 This return the list of stores where copy is failed.
293 """
294
295 start = int(time.time())
296 store_left = []
297 while int(time.time()) - start < client.build_timeout:
298 image = client.show_image(image_id)
299 store_left = image.get('os_glance_importing_to_stores')
300 # NOTE(danms): If os_glance_importing_to_stores is None, then
301 # we've raced with the startup of the task and should continue
302 # to wait.
303 if store_left is not None and not store_left:
304 return image['os_glance_failed_import']
305 if image['status'].lower() == 'killed':
306 raise exceptions.ImageKilledException(image_id=image_id,
307 status=image['status'])
308
309 time.sleep(client.build_interval)
310
zhufl414ffba2020-11-19 16:57:06 +0800311 message = ('Image %s failed to finish the copy operation '
312 'on stores: %s' % (image_id, str(store_left)))
Ghanshyam Mann4346a822020-07-29 13:45:04 -0500313 caller = test_utils.find_test_caller()
314 if caller:
315 message = '(%s) %s' % (caller, message)
316 raise lib_exc.TimeoutException(message)
317
318
Maxim Savabd9cbd32023-10-17 13:13:33 +0300319def wait_for_image_deleted_from_store(client, image, available_stores,
320 image_store_deleted):
321 """Waits for an image to be deleted from specific store.
322
323 API will not allow deletion of the last location for an image.
324 This return image if image deleted from store.
325 """
326
327 # Check if image have last store location
328 if len(available_stores) == 1:
329 exc_cls = lib_exc.OtherRestClientException
330 message = ('Delete from last store location not allowed'
331 % (image, image_store_deleted))
332 raise exc_cls(message)
333 start = int(time.time())
334 while int(time.time()) - start < client.build_timeout:
335 image = client.show_image(image['id'])
336 image_stores = image['stores'].split(",")
337 if image_store_deleted not in image_stores:
338 return
339 time.sleep(client.build_interval)
340 message = ('Failed to delete %s from requested store location: %s '
341 'within the required time: (%s s)' %
342 (image, image_store_deleted, client.build_timeout))
343 caller = test_utils.find_test_caller()
344 if caller:
345 message = '(%s) %s' % (caller, message)
346 raise exc_cls(message)
347
348
Dan Smithaeacd8c2023-02-21 13:34:20 -0800349def wait_for_volume_resource_status(client, resource_id, status,
350 server_id=None, servers_client=None):
Matt Riedemannb36186b2017-12-04 17:54:08 +0000351 """Waits for a volume resource to reach a given status.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200352
353 This function is a common function for volume, snapshot and backup
354 resources. The function extracts the name of the desired resource from
355 the client class name of the resource.
Dan Smithaeacd8c2023-02-21 13:34:20 -0800356
357 If server_id and servers_client are provided, dump the console for that
358 server on failure.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200359 """
xing-yang41ed7152017-05-03 06:52:56 -0400360 resource_name = re.findall(
361 r'(volume|group-snapshot|snapshot|backup|group)',
362 client.resource_type)[-1].replace('-', '_')
lkuchlan52d7b0d2016-11-07 20:53:19 +0200363 show_resource = getattr(client, 'show_' + resource_name)
364 resource_status = show_resource(resource_id)[resource_name]['status']
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000365 start = int(time.time())
366
Matt Riedemannb36186b2017-12-04 17:54:08 +0000367 while resource_status != status:
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000368 time.sleep(client.build_interval)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200369 resource_status = show_resource(resource_id)[
370 '{}'.format(resource_name)]['status']
Matt Riedemannb36186b2017-12-04 17:54:08 +0000371 if resource_status == 'error' and resource_status != status:
lkuchlan52d7b0d2016-11-07 20:53:19 +0200372 raise exceptions.VolumeResourceBuildErrorException(
373 resource_name=resource_name, resource_id=resource_id)
374 if resource_name == 'volume' and resource_status == 'error_restoring':
375 raise exceptions.VolumeRestoreErrorException(volume_id=resource_id)
zhufl0ea2c012019-06-03 15:37:13 +0800376 if resource_status == 'error_extending' and resource_status != status:
377 raise exceptions.VolumeExtendErrorException(volume_id=resource_id)
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000378
379 if int(time.time()) - start >= client.build_timeout:
Dan Smithaeacd8c2023-02-21 13:34:20 -0800380 if server_id and servers_client:
381 console_output = servers_client.get_console_output(
382 server_id)['output']
383 LOG.debug('Console output for %s\nbody=\n%s',
384 server_id, console_output)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200385 message = ('%s %s failed to reach %s status (current %s) '
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000386 'within the required time (%s s).' %
Matt Riedemannb36186b2017-12-04 17:54:08 +0000387 (resource_name, resource_id, status, resource_status,
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000388 client.build_timeout))
guo yunxianebb15f22016-11-01 21:03:35 +0800389 raise lib_exc.TimeoutException(message)
zhufl019ad732017-08-28 13:51:22 +0800390 LOG.info('%s %s reached %s after waiting for %f seconds',
Matt Riedemannb36186b2017-12-04 17:54:08 +0000391 resource_name, resource_id, status, time.time() - start)
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000392
393
Peter Penchev5c243a92020-09-06 02:26:03 +0300394def wait_for_volume_attachment_create(client, volume_id, server_id):
395 """Waits for a volume attachment to be created at a given volume."""
396 start = int(time.time())
397 while True:
398 attachments = client.show_volume(volume_id)['volume']['attachments']
399 found = [a for a in attachments if a['server_id'] == server_id]
400 if found:
401 LOG.info('Attachment %s created for volume %s to server %s after '
402 'waiting for %f seconds', found[0]['attachment_id'],
403 volume_id, server_id, time.time() - start)
404 return found[0]
405 time.sleep(client.build_interval)
406 if int(time.time()) - start >= client.build_timeout:
407 message = ('Failed to attach volume %s to server %s '
408 'within the required time (%s s).' %
409 (volume_id, server_id, client.build_timeout))
410 raise lib_exc.TimeoutException(message)
411
412
Lee Yarwoodc1b2a4a2020-01-08 17:02:49 +0000413def wait_for_volume_attachment_remove(client, volume_id, attachment_id):
414 """Waits for a volume attachment to be removed from a given volume."""
415 start = int(time.time())
416 attachments = client.show_volume(volume_id)['volume']['attachments']
417 while any(attachment_id == a['attachment_id'] for a in attachments):
418 time.sleep(client.build_interval)
419 if int(time.time()) - start >= client.build_timeout:
zhufl747300b2020-04-14 14:23:47 +0800420 message = ('Failed to remove attachment %s from volume %s '
Lee Yarwoodc1b2a4a2020-01-08 17:02:49 +0000421 'within the required time (%s s).' %
422 (attachment_id, volume_id, client.build_timeout))
423 raise lib_exc.TimeoutException(message)
424 attachments = client.show_volume(volume_id)['volume']['attachments']
425 LOG.info('Attachment %s removed from volume %s after waiting for %f '
426 'seconds', attachment_id, volume_id, time.time() - start)
427
428
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100429def wait_for_volume_attachment_remove_from_server(
430 client, server_id, volume_id):
431 """Waits for a volume to be removed from a given server.
432
433 This waiter checks the compute API if the volume attachment is removed.
434 """
435 start = int(time.time())
Lee Yarwood1bd60592021-06-04 10:18:35 +0100436
437 try:
438 volumes = client.list_volume_attachments(
439 server_id)['volumeAttachments']
440 except lib_exc.NotFound:
441 # Ignore 404s on detach in case the server is deleted or the volume
442 # is already detached.
443 return
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100444
445 while any(volume for volume in volumes if volume['volumeId'] == volume_id):
446 time.sleep(client.build_interval)
447
448 timed_out = int(time.time()) - start >= client.build_timeout
449 if timed_out:
Lee Yarwood1bd60592021-06-04 10:18:35 +0100450 console_output = client.get_console_output(server_id)['output']
451 LOG.debug('Console output for %s\nbody=\n%s',
452 server_id, console_output)
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100453 message = ('Volume %s failed to detach from server %s within '
454 'the required time (%s s) from the compute API '
455 'perspective' %
456 (volume_id, server_id, client.build_timeout))
457 raise lib_exc.TimeoutException(message)
Lee Yarwood1bd60592021-06-04 10:18:35 +0100458 try:
459 volumes = client.list_volume_attachments(
460 server_id)['volumeAttachments']
461 except lib_exc.NotFound:
462 # Ignore 404s on detach in case the server is deleted or the volume
463 # is already detached.
464 return
465 return
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100466
467
Lee Yarwoode5597402019-02-15 20:17:00 +0000468def wait_for_volume_migration(client, volume_id, new_host):
469 """Waits for a Volume to move to a new host."""
470 body = client.show_volume(volume_id)['volume']
471 host = body['os-vol-host-attr:host']
472 migration_status = body['migration_status']
473 start = int(time.time())
474
475 # new_host is hostname@backend while current_host is hostname@backend#type
476 while migration_status != 'success' or new_host not in host:
477 time.sleep(client.build_interval)
478 body = client.show_volume(volume_id)['volume']
479 host = body['os-vol-host-attr:host']
480 migration_status = body['migration_status']
481
482 if migration_status == 'error':
483 message = ('volume %s failed to migrate.' % (volume_id))
484 raise lib_exc.TempestException(message)
485
486 if int(time.time()) - start >= client.build_timeout:
487 message = ('Volume %s failed to migrate to %s (current %s) '
488 'within the required time (%s s).' %
489 (volume_id, new_host, host, client.build_timeout))
490 raise lib_exc.TimeoutException(message)
491
492
scottda61f68ac2016-06-07 12:07:55 -0600493def wait_for_volume_retype(client, volume_id, new_volume_type):
494 """Waits for a Volume to have a new volume type."""
495 body = client.show_volume(volume_id)['volume']
496 current_volume_type = body['volume_type']
497 start = int(time.time())
498
499 while current_volume_type != new_volume_type:
500 time.sleep(client.build_interval)
501 body = client.show_volume(volume_id)['volume']
502 current_volume_type = body['volume_type']
503
504 if int(time.time()) - start >= client.build_timeout:
505 message = ('Volume %s failed to reach %s volume type (current %s) '
506 'within the required time (%s s).' %
507 (volume_id, new_volume_type, current_volume_type,
508 client.build_timeout))
Matt Riedemann74eb3b52017-02-14 11:34:30 -0500509 raise lib_exc.TimeoutException(message)
scottda61f68ac2016-06-07 12:07:55 -0600510
511
lkuchlanec1ba4f2016-09-06 10:28:18 +0300512def wait_for_qos_operations(client, qos_id, operation, args=None):
513 """Waits for a qos operations to be completed.
514
515 NOTE : operation value is required for wait_for_qos_operations()
516 operation = 'qos-key' / 'disassociate' / 'disassociate-all'
517 args = keys[] when operation = 'qos-key'
518 args = volume-type-id disassociated when operation = 'disassociate'
519 args = None when operation = 'disassociate-all'
520 """
521 start_time = int(time.time())
522 while True:
523 if operation == 'qos-key-unset':
524 body = client.show_qos(qos_id)['qos_specs']
525 if not any(key in body['specs'] for key in args):
526 return
527 elif operation == 'disassociate':
528 body = client.show_association_qos(qos_id)['qos_associations']
529 if not any(args in body[i]['id'] for i in range(0, len(body))):
530 return
531 elif operation == 'disassociate-all':
532 body = client.show_association_qos(qos_id)['qos_associations']
533 if not body:
534 return
535 else:
536 msg = (" operation value is either not defined or incorrect.")
537 raise lib_exc.UnprocessableEntity(msg)
538
539 if int(time.time()) - start_time >= client.build_timeout:
guo yunxianebb15f22016-11-01 21:03:35 +0800540 raise lib_exc.TimeoutException
lkuchlanec1ba4f2016-09-06 10:28:18 +0300541 time.sleep(client.build_interval)
zhufl7b638332016-11-14 10:23:30 +0800542
543
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400544def wait_for_interface_status(client, server_id, port_id, status):
zhufl7b638332016-11-14 10:23:30 +0800545 """Waits for an interface to reach a given status."""
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400546 body = (client.show_interface(server_id, port_id)
zhufl7b638332016-11-14 10:23:30 +0800547 ['interfaceAttachment'])
548 interface_status = body['port_state']
549 start = int(time.time())
550
551 while(interface_status != status):
552 time.sleep(client.build_interval)
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400553 body = (client.show_interface(server_id, port_id)
zhufl7b638332016-11-14 10:23:30 +0800554 ['interfaceAttachment'])
555 interface_status = body['port_state']
556
557 timed_out = int(time.time()) - start >= client.build_timeout
558
559 if interface_status != status and timed_out:
560 message = ('Interface %s failed to reach %s status '
561 '(current %s) within the required time (%s s).' %
562 (port_id, status, interface_status,
563 client.build_timeout))
564 raise lib_exc.TimeoutException(message)
565
566 return body
Artom Lifshitz3306d422018-03-22 12:20:54 -0400567
568
Balazs Gibizer55414582021-10-05 11:22:30 +0200569def wait_for_interface_detach(client, server_id, port_id, detach_request_id):
Artom Lifshitz3306d422018-03-22 12:20:54 -0400570 """Waits for an interface to be detached from a server."""
Balazs Gibizer55414582021-10-05 11:22:30 +0200571 def _get_detach_event_results():
572 # NOTE(gibi): The obvious choice for this waiter would be to wait
573 # until the interface disappears from the client.list_interfaces()
574 # response. However that response is based on the binding status of the
575 # port in Neutron. Nova deallocates the port resources _after the port
576 # was unbound in Neutron. This can cause that the naive waiter would
577 # return before the port is fully deallocated. Wait instead of the
578 # os-instance-action to succeed as that is recorded after both the
579 # port is fully deallocated.
580 events = client.show_instance_action(
581 server_id, detach_request_id)['instanceAction'].get('events', [])
582 return [
583 event['result'] for event in events
584 if event['event'] == 'compute_detach_interface'
585 ]
586
587 detach_event_results = _get_detach_event_results()
588
Artom Lifshitz3306d422018-03-22 12:20:54 -0400589 start = int(time.time())
590
Balazs Gibizer55414582021-10-05 11:22:30 +0200591 while "Success" not in detach_event_results:
Artom Lifshitz3306d422018-03-22 12:20:54 -0400592 time.sleep(client.build_interval)
Balazs Gibizer55414582021-10-05 11:22:30 +0200593 detach_event_results = _get_detach_event_results()
594 if "Success" in detach_event_results:
595 return client.show_instance_action(
596 server_id, detach_request_id)['instanceAction']
Artom Lifshitz3306d422018-03-22 12:20:54 -0400597
598 timed_out = int(time.time()) - start >= client.build_timeout
599 if timed_out:
600 message = ('Interface %s failed to detach from server %s within '
601 'the required time (%s s)' % (port_id, server_id,
602 client.build_timeout))
603 raise lib_exc.TimeoutException(message)
Slawek Kaplonskie3405ba2020-11-09 17:24:13 +0100604
605
Artom Lifshitz8a959ea2021-09-27 12:09:12 -0400606def wait_for_server_floating_ip(servers_client, server, floating_ip,
607 wait_for_disassociate=False):
608 """Wait for floating IP association or disassociation.
609
610 :param servers_client: The servers client to use when querying the server's
611 floating IPs.
612 :param server: The server JSON dict on which to wait.
613 :param floating_ip: The floating IP JSON dict on which to wait.
Rajesh Tailora85bdb42024-04-02 12:01:53 +0530614 :param wait_for_disassociate: Boolean indicating whether to wait for
Artom Lifshitz8a959ea2021-09-27 12:09:12 -0400615 disassociation instead of association.
616 """
617
618 def _get_floating_ip_in_server_addresses(floating_ip, server):
619 for addresses in server['addresses'].values():
620 for address in addresses:
621 if (
622 address['OS-EXT-IPS:type'] == 'floating' and
623 address['addr'] == floating_ip['floating_ip_address']
624 ):
625 return address
626 return None
627
628 start_time = int(time.time())
629 while True:
630 server = servers_client.show_server(server['id'])['server']
631 address = _get_floating_ip_in_server_addresses(floating_ip, server)
632 if address is None and wait_for_disassociate:
633 return None
634 if not wait_for_disassociate and address:
635 return address
636
637 if int(time.time()) - start_time >= servers_client.build_timeout:
638 if wait_for_disassociate:
639 msg = ('Floating ip %s failed to disassociate from server %s '
640 'in time.' % (floating_ip, server['id']))
641 else:
642 msg = ('Floating ip %s failed to associate with server %s '
643 'in time.' % (floating_ip, server['id']))
644 raise lib_exc.TimeoutException(msg)
645 time.sleep(servers_client.build_interval)
Lee Yarwood0b4bc3d2021-11-11 17:45:25 +0000646
647
648def wait_for_ping(server_ip, timeout=30, interval=1):
649 """Waits for an address to become pingable"""
650 start_time = int(time.time())
651 while int(time.time()) - start_time < timeout:
652 response = os.system("ping -c 1 " + server_ip)
653 if response == 0:
654 return
655 time.sleep(interval)
656 raise lib_exc.TimeoutException()
657
658
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000659def wait_for_port_status(client, port_id, status):
660 """Wait for a port reach a certain status : ["BUILD" | "DOWN" | "ACTIVE"]
661 :param client: The network client to use when querying the port's
662 status
663 :param status: A string to compare the current port status-to.
664 :param port_id: The uuid of the port we would like queried for status.
665 """
666 start_time = time.time()
667 while (time.time() - start_time <= client.build_timeout):
668 result = client.show_port(port_id)
669 if result['port']['status'].lower() == status.lower():
670 return result
671 time.sleep(client.build_interval)
672 raise lib_exc.TimeoutException
673
674
Brian Haleyd6437c92024-09-06 16:09:26 -0400675def wait_for_server_ports_active(client, server_id, is_active, **kwargs):
676 """Wait for all server ports to reach active status
677 :param client: The network client to use when querying the port's status
678 :param server_id: The uuid of the server's ports we need to verify.
679 :param is_active: A function to call to the check port active status.
680 :param kwargs: Additional arguments, if any, to pass to list_ports()
681 """
682 start_time = time.time()
683 while (time.time() - start_time <= client.build_timeout):
684 ports = client.list_ports(device_id=server_id, **kwargs)['ports']
685 if all(is_active(port) for port in ports):
686 LOG.debug("Server ID %s ports are all ACTIVE %s: ",
687 server_id, ports)
688 return ports
689 LOG.warning("Server ID %s has ports that are not ACTIVE, waiting "
690 "for state to change on all: %s", server_id, ports)
691 time.sleep(client.build_interval)
692 LOG.error("Server ID %s ports have failed to transition to ACTIVE, "
693 "timing out: %s", server_id, ports)
694 raise lib_exc.TimeoutException
695
696
Lee Yarwood0b4bc3d2021-11-11 17:45:25 +0000697def wait_for_ssh(ssh_client, timeout=30):
698 """Waits for SSH connection to become usable"""
699 start_time = int(time.time())
700 while int(time.time()) - start_time < timeout:
701 try:
702 ssh_client.validate_authentication()
703 return
704 except lib_exc.SSHTimeout:
705 pass
706 raise lib_exc.TimeoutException()
Abhishek Kekane0188f462022-01-18 06:47:28 +0000707
708
709def wait_for_caching(client, cache_client, image_id):
710 """Waits until image is cached"""
711 start = int(time.time())
712 while int(time.time()) - start < client.build_timeout:
713 caching = cache_client.list_cache()
714 output = [image['image_id'] for image in caching['cached_images']]
715 if output and image_id in output:
716 return caching
717
718 time.sleep(client.build_interval)
719
720 message = ('Image %s failed to cache in time.' % image_id)
721 caller = test_utils.find_test_caller()
722 if caller:
723 message = '(%s) %s' % (caller, message)
724 raise lib_exc.TimeoutException(message)
Bas de Bruijnec9f9a032022-07-19 11:16:02 -0300725
726
727def wait_for_object_create(object_client, container_name, object_name,
728 interval=1):
729 """Waits for created object to become available"""
730 start_time = time.time()
731 while time.time() - start_time < object_client.build_timeout:
732 try:
733 return object_client.get_object(container_name, object_name)
734 except lib_exc.NotFound:
735 time.sleep(interval)
736 message = ('Object %s failed to create within the required time (%s s).' %
737 (object_name, object_client.build_timeout))
738 raise lib_exc.TimeoutException(message)