blob: 2c42bfd3ed0184e11e487a3d401069d7af7dd9d2 [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
Takashi Kajinami397f49c2024-01-25 20:32:34 +0900330 message = 'Delete from last store location not allowed'
Maxim Savabd9cbd32023-10-17 13:13:33 +0300331 raise exc_cls(message)
332 start = int(time.time())
333 while int(time.time()) - start < client.build_timeout:
334 image = client.show_image(image['id'])
335 image_stores = image['stores'].split(",")
336 if image_store_deleted not in image_stores:
337 return
338 time.sleep(client.build_interval)
339 message = ('Failed to delete %s from requested store location: %s '
340 'within the required time: (%s s)' %
341 (image, image_store_deleted, client.build_timeout))
342 caller = test_utils.find_test_caller()
343 if caller:
344 message = '(%s) %s' % (caller, message)
345 raise exc_cls(message)
346
347
Dan Smithaeacd8c2023-02-21 13:34:20 -0800348def wait_for_volume_resource_status(client, resource_id, status,
349 server_id=None, servers_client=None):
Matt Riedemannb36186b2017-12-04 17:54:08 +0000350 """Waits for a volume resource to reach a given status.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200351
352 This function is a common function for volume, snapshot and backup
353 resources. The function extracts the name of the desired resource from
354 the client class name of the resource.
Dan Smithaeacd8c2023-02-21 13:34:20 -0800355
356 If server_id and servers_client are provided, dump the console for that
357 server on failure.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200358 """
xing-yang41ed7152017-05-03 06:52:56 -0400359 resource_name = re.findall(
360 r'(volume|group-snapshot|snapshot|backup|group)',
361 client.resource_type)[-1].replace('-', '_')
lkuchlan52d7b0d2016-11-07 20:53:19 +0200362 show_resource = getattr(client, 'show_' + resource_name)
363 resource_status = show_resource(resource_id)[resource_name]['status']
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000364 start = int(time.time())
365
Matt Riedemannb36186b2017-12-04 17:54:08 +0000366 while resource_status != status:
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000367 time.sleep(client.build_interval)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200368 resource_status = show_resource(resource_id)[
369 '{}'.format(resource_name)]['status']
Matt Riedemannb36186b2017-12-04 17:54:08 +0000370 if resource_status == 'error' and resource_status != status:
lkuchlan52d7b0d2016-11-07 20:53:19 +0200371 raise exceptions.VolumeResourceBuildErrorException(
372 resource_name=resource_name, resource_id=resource_id)
373 if resource_name == 'volume' and resource_status == 'error_restoring':
374 raise exceptions.VolumeRestoreErrorException(volume_id=resource_id)
zhufl0ea2c012019-06-03 15:37:13 +0800375 if resource_status == 'error_extending' and resource_status != status:
376 raise exceptions.VolumeExtendErrorException(volume_id=resource_id)
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000377
378 if int(time.time()) - start >= client.build_timeout:
Dan Smithaeacd8c2023-02-21 13:34:20 -0800379 if server_id and servers_client:
380 console_output = servers_client.get_console_output(
381 server_id)['output']
382 LOG.debug('Console output for %s\nbody=\n%s',
383 server_id, console_output)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200384 message = ('%s %s failed to reach %s status (current %s) '
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000385 'within the required time (%s s).' %
Matt Riedemannb36186b2017-12-04 17:54:08 +0000386 (resource_name, resource_id, status, resource_status,
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000387 client.build_timeout))
guo yunxianebb15f22016-11-01 21:03:35 +0800388 raise lib_exc.TimeoutException(message)
zhufl019ad732017-08-28 13:51:22 +0800389 LOG.info('%s %s reached %s after waiting for %f seconds',
Matt Riedemannb36186b2017-12-04 17:54:08 +0000390 resource_name, resource_id, status, time.time() - start)
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000391
392
Peter Penchev5c243a92020-09-06 02:26:03 +0300393def wait_for_volume_attachment_create(client, volume_id, server_id):
394 """Waits for a volume attachment to be created at a given volume."""
395 start = int(time.time())
396 while True:
397 attachments = client.show_volume(volume_id)['volume']['attachments']
398 found = [a for a in attachments if a['server_id'] == server_id]
399 if found:
400 LOG.info('Attachment %s created for volume %s to server %s after '
401 'waiting for %f seconds', found[0]['attachment_id'],
402 volume_id, server_id, time.time() - start)
403 return found[0]
404 time.sleep(client.build_interval)
405 if int(time.time()) - start >= client.build_timeout:
406 message = ('Failed to attach volume %s to server %s '
407 'within the required time (%s s).' %
408 (volume_id, server_id, client.build_timeout))
409 raise lib_exc.TimeoutException(message)
410
411
Lee Yarwoodc1b2a4a2020-01-08 17:02:49 +0000412def wait_for_volume_attachment_remove(client, volume_id, attachment_id):
413 """Waits for a volume attachment to be removed from a given volume."""
414 start = int(time.time())
415 attachments = client.show_volume(volume_id)['volume']['attachments']
416 while any(attachment_id == a['attachment_id'] for a in attachments):
417 time.sleep(client.build_interval)
418 if int(time.time()) - start >= client.build_timeout:
zhufl747300b2020-04-14 14:23:47 +0800419 message = ('Failed to remove attachment %s from volume %s '
Lee Yarwoodc1b2a4a2020-01-08 17:02:49 +0000420 'within the required time (%s s).' %
421 (attachment_id, volume_id, client.build_timeout))
422 raise lib_exc.TimeoutException(message)
423 attachments = client.show_volume(volume_id)['volume']['attachments']
424 LOG.info('Attachment %s removed from volume %s after waiting for %f '
425 'seconds', attachment_id, volume_id, time.time() - start)
426
427
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100428def wait_for_volume_attachment_remove_from_server(
429 client, server_id, volume_id):
430 """Waits for a volume to be removed from a given server.
431
432 This waiter checks the compute API if the volume attachment is removed.
433 """
434 start = int(time.time())
Lee Yarwood1bd60592021-06-04 10:18:35 +0100435
436 try:
437 volumes = client.list_volume_attachments(
438 server_id)['volumeAttachments']
439 except lib_exc.NotFound:
440 # Ignore 404s on detach in case the server is deleted or the volume
441 # is already detached.
442 return
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100443
444 while any(volume for volume in volumes if volume['volumeId'] == volume_id):
445 time.sleep(client.build_interval)
446
447 timed_out = int(time.time()) - start >= client.build_timeout
448 if timed_out:
Lee Yarwood1bd60592021-06-04 10:18:35 +0100449 console_output = client.get_console_output(server_id)['output']
450 LOG.debug('Console output for %s\nbody=\n%s',
451 server_id, console_output)
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100452 message = ('Volume %s failed to detach from server %s within '
453 'the required time (%s s) from the compute API '
454 'perspective' %
455 (volume_id, server_id, client.build_timeout))
456 raise lib_exc.TimeoutException(message)
Lee Yarwood1bd60592021-06-04 10:18:35 +0100457 try:
458 volumes = client.list_volume_attachments(
459 server_id)['volumeAttachments']
460 except lib_exc.NotFound:
461 # Ignore 404s on detach in case the server is deleted or the volume
462 # is already detached.
463 return
464 return
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100465
466
Lee Yarwoode5597402019-02-15 20:17:00 +0000467def wait_for_volume_migration(client, volume_id, new_host):
468 """Waits for a Volume to move to a new host."""
469 body = client.show_volume(volume_id)['volume']
470 host = body['os-vol-host-attr:host']
471 migration_status = body['migration_status']
472 start = int(time.time())
473
474 # new_host is hostname@backend while current_host is hostname@backend#type
475 while migration_status != 'success' or new_host not in host:
476 time.sleep(client.build_interval)
477 body = client.show_volume(volume_id)['volume']
478 host = body['os-vol-host-attr:host']
479 migration_status = body['migration_status']
480
481 if migration_status == 'error':
482 message = ('volume %s failed to migrate.' % (volume_id))
483 raise lib_exc.TempestException(message)
484
485 if int(time.time()) - start >= client.build_timeout:
486 message = ('Volume %s failed to migrate to %s (current %s) '
487 'within the required time (%s s).' %
488 (volume_id, new_host, host, client.build_timeout))
489 raise lib_exc.TimeoutException(message)
490
491
scottda61f68ac2016-06-07 12:07:55 -0600492def wait_for_volume_retype(client, volume_id, new_volume_type):
493 """Waits for a Volume to have a new volume type."""
494 body = client.show_volume(volume_id)['volume']
495 current_volume_type = body['volume_type']
496 start = int(time.time())
497
498 while current_volume_type != new_volume_type:
499 time.sleep(client.build_interval)
500 body = client.show_volume(volume_id)['volume']
501 current_volume_type = body['volume_type']
502
503 if int(time.time()) - start >= client.build_timeout:
504 message = ('Volume %s failed to reach %s volume type (current %s) '
505 'within the required time (%s s).' %
506 (volume_id, new_volume_type, current_volume_type,
507 client.build_timeout))
Matt Riedemann74eb3b52017-02-14 11:34:30 -0500508 raise lib_exc.TimeoutException(message)
scottda61f68ac2016-06-07 12:07:55 -0600509
510
lkuchlanec1ba4f2016-09-06 10:28:18 +0300511def wait_for_qos_operations(client, qos_id, operation, args=None):
512 """Waits for a qos operations to be completed.
513
514 NOTE : operation value is required for wait_for_qos_operations()
515 operation = 'qos-key' / 'disassociate' / 'disassociate-all'
516 args = keys[] when operation = 'qos-key'
517 args = volume-type-id disassociated when operation = 'disassociate'
518 args = None when operation = 'disassociate-all'
519 """
520 start_time = int(time.time())
521 while True:
522 if operation == 'qos-key-unset':
523 body = client.show_qos(qos_id)['qos_specs']
524 if not any(key in body['specs'] for key in args):
525 return
526 elif operation == 'disassociate':
527 body = client.show_association_qos(qos_id)['qos_associations']
528 if not any(args in body[i]['id'] for i in range(0, len(body))):
529 return
530 elif operation == 'disassociate-all':
531 body = client.show_association_qos(qos_id)['qos_associations']
532 if not body:
533 return
534 else:
535 msg = (" operation value is either not defined or incorrect.")
536 raise lib_exc.UnprocessableEntity(msg)
537
538 if int(time.time()) - start_time >= client.build_timeout:
guo yunxianebb15f22016-11-01 21:03:35 +0800539 raise lib_exc.TimeoutException
lkuchlanec1ba4f2016-09-06 10:28:18 +0300540 time.sleep(client.build_interval)
zhufl7b638332016-11-14 10:23:30 +0800541
542
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400543def wait_for_interface_status(client, server_id, port_id, status):
zhufl7b638332016-11-14 10:23:30 +0800544 """Waits for an interface to reach a given status."""
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400545 body = (client.show_interface(server_id, port_id)
zhufl7b638332016-11-14 10:23:30 +0800546 ['interfaceAttachment'])
547 interface_status = body['port_state']
548 start = int(time.time())
549
Takashi Kajinami397f49c2024-01-25 20:32:34 +0900550 while interface_status != status:
zhufl7b638332016-11-14 10:23:30 +0800551 time.sleep(client.build_interval)
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400552 body = (client.show_interface(server_id, port_id)
zhufl7b638332016-11-14 10:23:30 +0800553 ['interfaceAttachment'])
554 interface_status = body['port_state']
555
556 timed_out = int(time.time()) - start >= client.build_timeout
557
558 if interface_status != status and timed_out:
559 message = ('Interface %s failed to reach %s status '
560 '(current %s) within the required time (%s s).' %
561 (port_id, status, interface_status,
562 client.build_timeout))
563 raise lib_exc.TimeoutException(message)
564
565 return body
Artom Lifshitz3306d422018-03-22 12:20:54 -0400566
567
Balazs Gibizer55414582021-10-05 11:22:30 +0200568def wait_for_interface_detach(client, server_id, port_id, detach_request_id):
Artom Lifshitz3306d422018-03-22 12:20:54 -0400569 """Waits for an interface to be detached from a server."""
Balazs Gibizer55414582021-10-05 11:22:30 +0200570 def _get_detach_event_results():
571 # NOTE(gibi): The obvious choice for this waiter would be to wait
572 # until the interface disappears from the client.list_interfaces()
573 # response. However that response is based on the binding status of the
574 # port in Neutron. Nova deallocates the port resources _after the port
575 # was unbound in Neutron. This can cause that the naive waiter would
576 # return before the port is fully deallocated. Wait instead of the
577 # os-instance-action to succeed as that is recorded after both the
578 # port is fully deallocated.
579 events = client.show_instance_action(
580 server_id, detach_request_id)['instanceAction'].get('events', [])
581 return [
582 event['result'] for event in events
583 if event['event'] == 'compute_detach_interface'
584 ]
585
586 detach_event_results = _get_detach_event_results()
587
Artom Lifshitz3306d422018-03-22 12:20:54 -0400588 start = int(time.time())
589
Balazs Gibizer55414582021-10-05 11:22:30 +0200590 while "Success" not in detach_event_results:
Artom Lifshitz3306d422018-03-22 12:20:54 -0400591 time.sleep(client.build_interval)
Balazs Gibizer55414582021-10-05 11:22:30 +0200592 detach_event_results = _get_detach_event_results()
593 if "Success" in detach_event_results:
594 return client.show_instance_action(
595 server_id, detach_request_id)['instanceAction']
Artom Lifshitz3306d422018-03-22 12:20:54 -0400596
597 timed_out = int(time.time()) - start >= client.build_timeout
598 if timed_out:
599 message = ('Interface %s failed to detach from server %s within '
600 'the required time (%s s)' % (port_id, server_id,
601 client.build_timeout))
602 raise lib_exc.TimeoutException(message)
Slawek Kaplonskie3405ba2020-11-09 17:24:13 +0100603
604
Artom Lifshitz8a959ea2021-09-27 12:09:12 -0400605def wait_for_server_floating_ip(servers_client, server, floating_ip,
606 wait_for_disassociate=False):
607 """Wait for floating IP association or disassociation.
608
609 :param servers_client: The servers client to use when querying the server's
610 floating IPs.
611 :param server: The server JSON dict on which to wait.
612 :param floating_ip: The floating IP JSON dict on which to wait.
Rajesh Tailora85bdb42024-04-02 12:01:53 +0530613 :param wait_for_disassociate: Boolean indicating whether to wait for
Artom Lifshitz8a959ea2021-09-27 12:09:12 -0400614 disassociation instead of association.
615 """
616
617 def _get_floating_ip_in_server_addresses(floating_ip, server):
618 for addresses in server['addresses'].values():
619 for address in addresses:
620 if (
621 address['OS-EXT-IPS:type'] == 'floating' and
622 address['addr'] == floating_ip['floating_ip_address']
623 ):
624 return address
625 return None
626
627 start_time = int(time.time())
628 while True:
629 server = servers_client.show_server(server['id'])['server']
630 address = _get_floating_ip_in_server_addresses(floating_ip, server)
631 if address is None and wait_for_disassociate:
632 return None
633 if not wait_for_disassociate and address:
634 return address
635
636 if int(time.time()) - start_time >= servers_client.build_timeout:
637 if wait_for_disassociate:
638 msg = ('Floating ip %s failed to disassociate from server %s '
639 'in time.' % (floating_ip, server['id']))
640 else:
641 msg = ('Floating ip %s failed to associate with server %s '
642 'in time.' % (floating_ip, server['id']))
643 raise lib_exc.TimeoutException(msg)
644 time.sleep(servers_client.build_interval)
Lee Yarwood0b4bc3d2021-11-11 17:45:25 +0000645
646
647def wait_for_ping(server_ip, timeout=30, interval=1):
648 """Waits for an address to become pingable"""
649 start_time = int(time.time())
650 while int(time.time()) - start_time < timeout:
651 response = os.system("ping -c 1 " + server_ip)
652 if response == 0:
653 return
654 time.sleep(interval)
655 raise lib_exc.TimeoutException()
656
657
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000658def wait_for_port_status(client, port_id, status):
659 """Wait for a port reach a certain status : ["BUILD" | "DOWN" | "ACTIVE"]
660 :param client: The network client to use when querying the port's
661 status
662 :param status: A string to compare the current port status-to.
663 :param port_id: The uuid of the port we would like queried for status.
664 """
665 start_time = time.time()
666 while (time.time() - start_time <= client.build_timeout):
667 result = client.show_port(port_id)
668 if result['port']['status'].lower() == status.lower():
669 return result
670 time.sleep(client.build_interval)
671 raise lib_exc.TimeoutException
672
673
Brian Haleyd6437c92024-09-06 16:09:26 -0400674def wait_for_server_ports_active(client, server_id, is_active, **kwargs):
675 """Wait for all server ports to reach active status
676 :param client: The network client to use when querying the port's status
677 :param server_id: The uuid of the server's ports we need to verify.
678 :param is_active: A function to call to the check port active status.
679 :param kwargs: Additional arguments, if any, to pass to list_ports()
680 """
681 start_time = time.time()
682 while (time.time() - start_time <= client.build_timeout):
683 ports = client.list_ports(device_id=server_id, **kwargs)['ports']
684 if all(is_active(port) for port in ports):
685 LOG.debug("Server ID %s ports are all ACTIVE %s: ",
686 server_id, ports)
687 return ports
688 LOG.warning("Server ID %s has ports that are not ACTIVE, waiting "
689 "for state to change on all: %s", server_id, ports)
690 time.sleep(client.build_interval)
691 LOG.error("Server ID %s ports have failed to transition to ACTIVE, "
692 "timing out: %s", server_id, ports)
693 raise lib_exc.TimeoutException
694
695
Lee Yarwood0b4bc3d2021-11-11 17:45:25 +0000696def wait_for_ssh(ssh_client, timeout=30):
697 """Waits for SSH connection to become usable"""
698 start_time = int(time.time())
699 while int(time.time()) - start_time < timeout:
700 try:
701 ssh_client.validate_authentication()
702 return
703 except lib_exc.SSHTimeout:
704 pass
705 raise lib_exc.TimeoutException()
Abhishek Kekane0188f462022-01-18 06:47:28 +0000706
707
708def wait_for_caching(client, cache_client, image_id):
709 """Waits until image is cached"""
710 start = int(time.time())
711 while int(time.time()) - start < client.build_timeout:
712 caching = cache_client.list_cache()
713 output = [image['image_id'] for image in caching['cached_images']]
714 if output and image_id in output:
715 return caching
716
717 time.sleep(client.build_interval)
718
719 message = ('Image %s failed to cache in time.' % image_id)
720 caller = test_utils.find_test_caller()
721 if caller:
722 message = '(%s) %s' % (caller, message)
723 raise lib_exc.TimeoutException(message)
Bas de Bruijnec9f9a032022-07-19 11:16:02 -0300724
725
726def wait_for_object_create(object_client, container_name, object_name,
727 interval=1):
728 """Waits for created object to become available"""
729 start_time = time.time()
730 while time.time() - start_time < object_client.build_timeout:
731 try:
732 return object_client.get_object(container_name, object_name)
733 except lib_exc.NotFound:
734 time.sleep(interval)
735 message = ('Object %s failed to create within the required time (%s s).' %
736 (object_name, object_client.build_timeout))
737 raise lib_exc.TimeoutException(message)