blob: ddc60472c7b882f5d17ab10957c1ed00b40c5073 [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
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000106def wait_for_server_termination(client, server_id, ignore_error=False):
107 """Waits for server to reach termination."""
Matt Riedemannf748c112017-02-03 11:33:11 -0500108 try:
109 body = client.show_server(server_id)['server']
110 except lib_exc.NotFound:
111 return
zhuflf5cff8b2019-04-28 15:26:32 +0800112 old_status = body['status']
113 old_task_state = _get_task_state(body)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000114 start_time = int(time.time())
115 while True:
Matt Riedemannf748c112017-02-03 11:33:11 -0500116 time.sleep(client.build_interval)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000117 try:
ghanshyam0f825252015-08-25 16:02:50 +0900118 body = client.show_server(server_id)['server']
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000119 except lib_exc.NotFound:
120 return
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000121 server_status = body['status']
Matt Riedemannf748c112017-02-03 11:33:11 -0500122 task_state = _get_task_state(body)
123 if (server_status != old_status) or (task_state != old_task_state):
124 LOG.info('State transition "%s" ==> "%s" after %d second wait',
125 '/'.join((old_status, str(old_task_state))),
126 '/'.join((server_status, str(task_state))),
127 time.time() - start_time)
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000128 if server_status == 'ERROR' and not ignore_error:
zhufl0ded98f2019-06-13 11:44:24 +0800129 raise lib_exc.DeleteErrorException(
130 "Server %s failed to delete and is in ERROR status" %
131 server_id)
ericxiett5f65bf52020-09-25 08:42:26 +0000132
ericxiettd0ef93e2019-12-16 08:55:05 +0000133 if server_status == 'SOFT_DELETED':
134 # Soft-deleted instances need to be forcibly deleted to
135 # prevent some test cases from failing.
136 LOG.debug("Automatically force-deleting soft-deleted server %s",
137 server_id)
ericxiett5f65bf52020-09-25 08:42:26 +0000138 try:
139 client.force_delete_server(server_id)
140 except lib_exc.NotFound:
141 # The instance may have been deleted so ignore
142 # NotFound exception
143 return
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000144
145 if int(time.time()) - start_time >= client.build_timeout:
guo yunxianebb15f22016-11-01 21:03:35 +0800146 raise lib_exc.TimeoutException
Matt Riedemannf748c112017-02-03 11:33:11 -0500147 old_status = server_status
148 old_task_state = task_state
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000149
150
Matt Riedemannc00f3262013-12-14 12:03:55 -0800151def wait_for_image_status(client, image_id, status):
152 """Waits for an image to reach a given status.
153
Ken'ichi Ohmichi5d410762015-05-22 01:10:03 +0000154 The client should have a show_image(image_id) method to get the image.
Matt Riedemannc00f3262013-12-14 12:03:55 -0800155 The client should also have build_interval and build_timeout attributes.
156 """
Ghanshyam Mann3562cd02023-08-05 17:22:03 -0700157 show_image = client.show_image
Matt Riedemannc00f3262013-12-14 12:03:55 -0800158
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300159 current_status = 'An unknown status'
160 start = int(time.time())
161 while int(time.time()) - start < client.build_timeout:
162 image = show_image(image_id)
163 # Compute image client returns response wrapped in 'image' element
Xiangfei Zhua8fd20a2016-07-26 21:28:12 -0700164 # which is not the case with Glance image client.
ghanshyam1756e0b2015-08-18 19:19:05 +0900165 if 'image' in image:
166 image = image['image']
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300167
168 current_status = image['status']
169 if current_status == status:
170 return
171 if current_status.lower() == 'killed':
172 raise exceptions.ImageKilledException(image_id=image_id,
173 status=status)
174 if current_status.lower() == 'error':
Matt Riedemannc00f3262013-12-14 12:03:55 -0800175 raise exceptions.AddImageException(image_id=image_id)
176
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300177 time.sleep(client.build_interval)
Matt Riedemannc00f3262013-12-14 12:03:55 -0800178
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300179 message = ('Image %(image_id)s failed to reach %(status)s state '
180 '(current state %(current_status)s) within the required '
181 'time (%(timeout)s s).' % {'image_id': image_id,
182 'status': status,
183 'current_status': current_status,
184 'timeout': client.build_timeout})
zhufl88c89b52016-07-01 18:09:05 +0800185 caller = test_utils.find_test_caller()
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300186 if caller:
187 message = '(%s) %s' % (caller, message)
guo yunxianebb15f22016-11-01 21:03:35 +0800188 raise lib_exc.TimeoutException(message)
Adam Gandelman00682612014-09-02 17:10:36 -0700189
190
Ghanshyam Mannb15b58e2021-04-29 19:45:29 -0500191def wait_for_image_tasks_status(client, image_id, status):
192 """Waits for an image tasks to reach a given status."""
193 pending_tasks = []
194 start = int(time.time())
195 while int(time.time()) - start < client.build_timeout:
196 tasks = client.show_image_tasks(image_id)['tasks']
197
198 pending_tasks = [task for task in tasks if task['status'] != status]
199 if not pending_tasks:
200 return tasks
201 time.sleep(client.build_interval)
202
203 message = ('Image %(image_id)s tasks: %(pending_tasks)s '
204 'failed to reach %(status)s state within the required '
205 'time (%(timeout)s s).' % {'image_id': image_id,
206 'pending_tasks': pending_tasks,
207 'status': status,
208 'timeout': client.build_timeout})
209 caller = test_utils.find_test_caller()
210 if caller:
211 message = '(%s) %s' % (caller, message)
212 raise lib_exc.TimeoutException(message)
213
214
msava88660d42023-07-18 11:27:04 +0300215def wait_for_tasks_status(client, task_id, status):
216 start = int(time.time())
217 while int(time.time()) - start < client.build_timeout:
218 task = client.show_tasks(task_id)
219 if task['status'] == status:
220 return task
221 time.sleep(client.build_interval)
222 message = ('Task %(task_id)s tasks: '
223 'failed to reach %(status)s state within the required '
224 'time (%(timeout)s s).' % {'task_id': task_id,
225 'status': status,
226 'timeout': client.build_timeout})
227 caller = test_utils.find_test_caller()
228 if caller:
229 message = '(%s) %s' % (caller, message)
230 raise lib_exc.TimeoutException(message)
231
232
Dan Smithef8e0542021-02-05 13:05:45 -0800233def wait_for_image_imported_to_stores(client, image_id, stores=None):
PranaliD491d63e2020-08-18 13:29:21 +0000234 """Waits for an image to be imported to all requested stores.
235
Dan Smithef8e0542021-02-05 13:05:45 -0800236 Short circuits to fail if the serer reports failure of any store.
237 If stores is None, just wait for status==active.
238
PranaliD491d63e2020-08-18 13:29:21 +0000239 The client should also have build_interval and build_timeout attributes.
240 """
241
Dan Smithef8e0542021-02-05 13:05:45 -0800242 exc_cls = lib_exc.TimeoutException
PranaliD491d63e2020-08-18 13:29:21 +0000243 start = int(time.time())
Dan Smith466f7062022-10-10 15:25:39 -0700244
245 # NOTE(danms): Don't wait for stores that are read-only as those
246 # will never complete
247 try:
248 store_info = client.info_stores()['stores']
249 stores = ','.join(sorted([
250 store['id'] for store in store_info
251 if store.get('read-only') != 'true' and
252 (not stores or store['id'] in stores.split(','))]))
253 except lib_exc.NotFound:
254 # If multi-store is not enabled, then we can not resolve which
255 # ones are read-only, and stores must have been passed as None
256 # anyway for us to succeed. If not, then we should raise right
257 # now and avoid waiting since we will never see the stores
258 # appear.
259 if stores is not None:
260 raise lib_exc.TimeoutException(
261 'Image service has no store support; '
262 'cowardly refusing to wait for them.')
263
PranaliD491d63e2020-08-18 13:29:21 +0000264 while int(time.time()) - start < client.build_timeout:
265 image = client.show_image(image_id)
Dan Smithef8e0542021-02-05 13:05:45 -0800266 if image['status'] == 'active' and (stores is None or
267 image['stores'] == stores):
PranaliD491d63e2020-08-18 13:29:21 +0000268 return
Dan Smithef8e0542021-02-05 13:05:45 -0800269 if image.get('os_glance_failed_import'):
270 exc_cls = lib_exc.OtherRestClientException
271 break
PranaliD491d63e2020-08-18 13:29:21 +0000272
273 time.sleep(client.build_interval)
274
zhufl414ffba2020-11-19 16:57:06 +0800275 message = ('Image %s failed to import on stores: %s' %
Dan Smithef8e0542021-02-05 13:05:45 -0800276 (image_id, str(image.get('os_glance_failed_import'))))
PranaliD491d63e2020-08-18 13:29:21 +0000277 caller = test_utils.find_test_caller()
278 if caller:
279 message = '(%s) %s' % (caller, message)
Dan Smithef8e0542021-02-05 13:05:45 -0800280 raise exc_cls(message)
PranaliD491d63e2020-08-18 13:29:21 +0000281
282
Ghanshyam Mann4346a822020-07-29 13:45:04 -0500283def wait_for_image_copied_to_stores(client, image_id):
284 """Waits for an image to be copied on all requested stores.
285
286 The client should also have build_interval and build_timeout attributes.
287 This return the list of stores where copy is failed.
288 """
289
290 start = int(time.time())
291 store_left = []
292 while int(time.time()) - start < client.build_timeout:
293 image = client.show_image(image_id)
294 store_left = image.get('os_glance_importing_to_stores')
295 # NOTE(danms): If os_glance_importing_to_stores is None, then
296 # we've raced with the startup of the task and should continue
297 # to wait.
298 if store_left is not None and not store_left:
299 return image['os_glance_failed_import']
300 if image['status'].lower() == 'killed':
301 raise exceptions.ImageKilledException(image_id=image_id,
302 status=image['status'])
303
304 time.sleep(client.build_interval)
305
zhufl414ffba2020-11-19 16:57:06 +0800306 message = ('Image %s failed to finish the copy operation '
307 'on stores: %s' % (image_id, str(store_left)))
Ghanshyam Mann4346a822020-07-29 13:45:04 -0500308 caller = test_utils.find_test_caller()
309 if caller:
310 message = '(%s) %s' % (caller, message)
311 raise lib_exc.TimeoutException(message)
312
313
Maxim Savabd9cbd32023-10-17 13:13:33 +0300314def wait_for_image_deleted_from_store(client, image, available_stores,
315 image_store_deleted):
316 """Waits for an image to be deleted from specific store.
317
318 API will not allow deletion of the last location for an image.
319 This return image if image deleted from store.
320 """
321
322 # Check if image have last store location
323 if len(available_stores) == 1:
324 exc_cls = lib_exc.OtherRestClientException
325 message = ('Delete from last store location not allowed'
326 % (image, image_store_deleted))
327 raise exc_cls(message)
328 start = int(time.time())
329 while int(time.time()) - start < client.build_timeout:
330 image = client.show_image(image['id'])
331 image_stores = image['stores'].split(",")
332 if image_store_deleted not in image_stores:
333 return
334 time.sleep(client.build_interval)
335 message = ('Failed to delete %s from requested store location: %s '
336 'within the required time: (%s s)' %
337 (image, image_store_deleted, client.build_timeout))
338 caller = test_utils.find_test_caller()
339 if caller:
340 message = '(%s) %s' % (caller, message)
341 raise exc_cls(message)
342
343
Dan Smithaeacd8c2023-02-21 13:34:20 -0800344def wait_for_volume_resource_status(client, resource_id, status,
345 server_id=None, servers_client=None):
Matt Riedemannb36186b2017-12-04 17:54:08 +0000346 """Waits for a volume resource to reach a given status.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200347
348 This function is a common function for volume, snapshot and backup
349 resources. The function extracts the name of the desired resource from
350 the client class name of the resource.
Dan Smithaeacd8c2023-02-21 13:34:20 -0800351
352 If server_id and servers_client are provided, dump the console for that
353 server on failure.
lkuchlan52d7b0d2016-11-07 20:53:19 +0200354 """
xing-yang41ed7152017-05-03 06:52:56 -0400355 resource_name = re.findall(
356 r'(volume|group-snapshot|snapshot|backup|group)',
357 client.resource_type)[-1].replace('-', '_')
lkuchlan52d7b0d2016-11-07 20:53:19 +0200358 show_resource = getattr(client, 'show_' + resource_name)
359 resource_status = show_resource(resource_id)[resource_name]['status']
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000360 start = int(time.time())
361
Matt Riedemannb36186b2017-12-04 17:54:08 +0000362 while resource_status != status:
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000363 time.sleep(client.build_interval)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200364 resource_status = show_resource(resource_id)[
365 '{}'.format(resource_name)]['status']
Matt Riedemannb36186b2017-12-04 17:54:08 +0000366 if resource_status == 'error' and resource_status != status:
lkuchlan52d7b0d2016-11-07 20:53:19 +0200367 raise exceptions.VolumeResourceBuildErrorException(
368 resource_name=resource_name, resource_id=resource_id)
369 if resource_name == 'volume' and resource_status == 'error_restoring':
370 raise exceptions.VolumeRestoreErrorException(volume_id=resource_id)
zhufl0ea2c012019-06-03 15:37:13 +0800371 if resource_status == 'error_extending' and resource_status != status:
372 raise exceptions.VolumeExtendErrorException(volume_id=resource_id)
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000373
374 if int(time.time()) - start >= client.build_timeout:
Dan Smithaeacd8c2023-02-21 13:34:20 -0800375 if server_id and servers_client:
376 console_output = servers_client.get_console_output(
377 server_id)['output']
378 LOG.debug('Console output for %s\nbody=\n%s',
379 server_id, console_output)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200380 message = ('%s %s failed to reach %s status (current %s) '
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000381 'within the required time (%s s).' %
Matt Riedemannb36186b2017-12-04 17:54:08 +0000382 (resource_name, resource_id, status, resource_status,
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000383 client.build_timeout))
guo yunxianebb15f22016-11-01 21:03:35 +0800384 raise lib_exc.TimeoutException(message)
zhufl019ad732017-08-28 13:51:22 +0800385 LOG.info('%s %s reached %s after waiting for %f seconds',
Matt Riedemannb36186b2017-12-04 17:54:08 +0000386 resource_name, resource_id, status, time.time() - start)
Ken'ichi Ohmichib942be62015-07-08 08:16:12 +0000387
388
Peter Penchev5c243a92020-09-06 02:26:03 +0300389def wait_for_volume_attachment_create(client, volume_id, server_id):
390 """Waits for a volume attachment to be created at a given volume."""
391 start = int(time.time())
392 while True:
393 attachments = client.show_volume(volume_id)['volume']['attachments']
394 found = [a for a in attachments if a['server_id'] == server_id]
395 if found:
396 LOG.info('Attachment %s created for volume %s to server %s after '
397 'waiting for %f seconds', found[0]['attachment_id'],
398 volume_id, server_id, time.time() - start)
399 return found[0]
400 time.sleep(client.build_interval)
401 if int(time.time()) - start >= client.build_timeout:
402 message = ('Failed to attach volume %s to server %s '
403 'within the required time (%s s).' %
404 (volume_id, server_id, client.build_timeout))
405 raise lib_exc.TimeoutException(message)
406
407
Lee Yarwoodc1b2a4a2020-01-08 17:02:49 +0000408def wait_for_volume_attachment_remove(client, volume_id, attachment_id):
409 """Waits for a volume attachment to be removed from a given volume."""
410 start = int(time.time())
411 attachments = client.show_volume(volume_id)['volume']['attachments']
412 while any(attachment_id == a['attachment_id'] for a in attachments):
413 time.sleep(client.build_interval)
414 if int(time.time()) - start >= client.build_timeout:
zhufl747300b2020-04-14 14:23:47 +0800415 message = ('Failed to remove attachment %s from volume %s '
Lee Yarwoodc1b2a4a2020-01-08 17:02:49 +0000416 'within the required time (%s s).' %
417 (attachment_id, volume_id, client.build_timeout))
418 raise lib_exc.TimeoutException(message)
419 attachments = client.show_volume(volume_id)['volume']['attachments']
420 LOG.info('Attachment %s removed from volume %s after waiting for %f '
421 'seconds', attachment_id, volume_id, time.time() - start)
422
423
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100424def wait_for_volume_attachment_remove_from_server(
425 client, server_id, volume_id):
426 """Waits for a volume to be removed from a given server.
427
428 This waiter checks the compute API if the volume attachment is removed.
429 """
430 start = int(time.time())
Lee Yarwood1bd60592021-06-04 10:18:35 +0100431
432 try:
433 volumes = client.list_volume_attachments(
434 server_id)['volumeAttachments']
435 except lib_exc.NotFound:
436 # Ignore 404s on detach in case the server is deleted or the volume
437 # is already detached.
438 return
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100439
440 while any(volume for volume in volumes if volume['volumeId'] == volume_id):
441 time.sleep(client.build_interval)
442
443 timed_out = int(time.time()) - start >= client.build_timeout
444 if timed_out:
Lee Yarwood1bd60592021-06-04 10:18:35 +0100445 console_output = client.get_console_output(server_id)['output']
446 LOG.debug('Console output for %s\nbody=\n%s',
447 server_id, console_output)
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100448 message = ('Volume %s failed to detach from server %s within '
449 'the required time (%s s) from the compute API '
450 'perspective' %
451 (volume_id, server_id, client.build_timeout))
452 raise lib_exc.TimeoutException(message)
Lee Yarwood1bd60592021-06-04 10:18:35 +0100453 try:
454 volumes = client.list_volume_attachments(
455 server_id)['volumeAttachments']
456 except lib_exc.NotFound:
457 # Ignore 404s on detach in case the server is deleted or the volume
458 # is already detached.
459 return
460 return
Balazs Gibizer2e515fe2020-12-07 15:10:11 +0100461
462
Lee Yarwoode5597402019-02-15 20:17:00 +0000463def wait_for_volume_migration(client, volume_id, new_host):
464 """Waits for a Volume to move to a new host."""
465 body = client.show_volume(volume_id)['volume']
466 host = body['os-vol-host-attr:host']
467 migration_status = body['migration_status']
468 start = int(time.time())
469
470 # new_host is hostname@backend while current_host is hostname@backend#type
471 while migration_status != 'success' or new_host not in host:
472 time.sleep(client.build_interval)
473 body = client.show_volume(volume_id)['volume']
474 host = body['os-vol-host-attr:host']
475 migration_status = body['migration_status']
476
477 if migration_status == 'error':
478 message = ('volume %s failed to migrate.' % (volume_id))
479 raise lib_exc.TempestException(message)
480
481 if int(time.time()) - start >= client.build_timeout:
482 message = ('Volume %s failed to migrate to %s (current %s) '
483 'within the required time (%s s).' %
484 (volume_id, new_host, host, client.build_timeout))
485 raise lib_exc.TimeoutException(message)
486
487
scottda61f68ac2016-06-07 12:07:55 -0600488def wait_for_volume_retype(client, volume_id, new_volume_type):
489 """Waits for a Volume to have a new volume type."""
490 body = client.show_volume(volume_id)['volume']
491 current_volume_type = body['volume_type']
492 start = int(time.time())
493
494 while current_volume_type != new_volume_type:
495 time.sleep(client.build_interval)
496 body = client.show_volume(volume_id)['volume']
497 current_volume_type = body['volume_type']
498
499 if int(time.time()) - start >= client.build_timeout:
500 message = ('Volume %s failed to reach %s volume type (current %s) '
501 'within the required time (%s s).' %
502 (volume_id, new_volume_type, current_volume_type,
503 client.build_timeout))
Matt Riedemann74eb3b52017-02-14 11:34:30 -0500504 raise lib_exc.TimeoutException(message)
scottda61f68ac2016-06-07 12:07:55 -0600505
506
lkuchlanec1ba4f2016-09-06 10:28:18 +0300507def wait_for_qos_operations(client, qos_id, operation, args=None):
508 """Waits for a qos operations to be completed.
509
510 NOTE : operation value is required for wait_for_qos_operations()
511 operation = 'qos-key' / 'disassociate' / 'disassociate-all'
512 args = keys[] when operation = 'qos-key'
513 args = volume-type-id disassociated when operation = 'disassociate'
514 args = None when operation = 'disassociate-all'
515 """
516 start_time = int(time.time())
517 while True:
518 if operation == 'qos-key-unset':
519 body = client.show_qos(qos_id)['qos_specs']
520 if not any(key in body['specs'] for key in args):
521 return
522 elif operation == 'disassociate':
523 body = client.show_association_qos(qos_id)['qos_associations']
524 if not any(args in body[i]['id'] for i in range(0, len(body))):
525 return
526 elif operation == 'disassociate-all':
527 body = client.show_association_qos(qos_id)['qos_associations']
528 if not body:
529 return
530 else:
531 msg = (" operation value is either not defined or incorrect.")
532 raise lib_exc.UnprocessableEntity(msg)
533
534 if int(time.time()) - start_time >= client.build_timeout:
guo yunxianebb15f22016-11-01 21:03:35 +0800535 raise lib_exc.TimeoutException
lkuchlanec1ba4f2016-09-06 10:28:18 +0300536 time.sleep(client.build_interval)
zhufl7b638332016-11-14 10:23:30 +0800537
538
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400539def wait_for_interface_status(client, server_id, port_id, status):
zhufl7b638332016-11-14 10:23:30 +0800540 """Waits for an interface to reach a given status."""
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400541 body = (client.show_interface(server_id, port_id)
zhufl7b638332016-11-14 10:23:30 +0800542 ['interfaceAttachment'])
543 interface_status = body['port_state']
544 start = int(time.time())
545
546 while(interface_status != status):
547 time.sleep(client.build_interval)
Matt Riedemann2ea48f02017-04-03 11:36:19 -0400548 body = (client.show_interface(server_id, port_id)
zhufl7b638332016-11-14 10:23:30 +0800549 ['interfaceAttachment'])
550 interface_status = body['port_state']
551
552 timed_out = int(time.time()) - start >= client.build_timeout
553
554 if interface_status != status and timed_out:
555 message = ('Interface %s failed to reach %s status '
556 '(current %s) within the required time (%s s).' %
557 (port_id, status, interface_status,
558 client.build_timeout))
559 raise lib_exc.TimeoutException(message)
560
561 return body
Artom Lifshitz3306d422018-03-22 12:20:54 -0400562
563
Balazs Gibizer55414582021-10-05 11:22:30 +0200564def wait_for_interface_detach(client, server_id, port_id, detach_request_id):
Artom Lifshitz3306d422018-03-22 12:20:54 -0400565 """Waits for an interface to be detached from a server."""
Balazs Gibizer55414582021-10-05 11:22:30 +0200566 def _get_detach_event_results():
567 # NOTE(gibi): The obvious choice for this waiter would be to wait
568 # until the interface disappears from the client.list_interfaces()
569 # response. However that response is based on the binding status of the
570 # port in Neutron. Nova deallocates the port resources _after the port
571 # was unbound in Neutron. This can cause that the naive waiter would
572 # return before the port is fully deallocated. Wait instead of the
573 # os-instance-action to succeed as that is recorded after both the
574 # port is fully deallocated.
575 events = client.show_instance_action(
576 server_id, detach_request_id)['instanceAction'].get('events', [])
577 return [
578 event['result'] for event in events
579 if event['event'] == 'compute_detach_interface'
580 ]
581
582 detach_event_results = _get_detach_event_results()
583
Artom Lifshitz3306d422018-03-22 12:20:54 -0400584 start = int(time.time())
585
Balazs Gibizer55414582021-10-05 11:22:30 +0200586 while "Success" not in detach_event_results:
Artom Lifshitz3306d422018-03-22 12:20:54 -0400587 time.sleep(client.build_interval)
Balazs Gibizer55414582021-10-05 11:22:30 +0200588 detach_event_results = _get_detach_event_results()
589 if "Success" in detach_event_results:
590 return client.show_instance_action(
591 server_id, detach_request_id)['instanceAction']
Artom Lifshitz3306d422018-03-22 12:20:54 -0400592
593 timed_out = int(time.time()) - start >= client.build_timeout
594 if timed_out:
595 message = ('Interface %s failed to detach from server %s within '
596 'the required time (%s s)' % (port_id, server_id,
597 client.build_timeout))
598 raise lib_exc.TimeoutException(message)
Slawek Kaplonskie3405ba2020-11-09 17:24:13 +0100599
600
Artom Lifshitz8a959ea2021-09-27 12:09:12 -0400601def wait_for_server_floating_ip(servers_client, server, floating_ip,
602 wait_for_disassociate=False):
603 """Wait for floating IP association or disassociation.
604
605 :param servers_client: The servers client to use when querying the server's
606 floating IPs.
607 :param server: The server JSON dict on which to wait.
608 :param floating_ip: The floating IP JSON dict on which to wait.
609 :param wait_for_disassociate: Boolean indiating whether to wait for
610 disassociation instead of association.
611 """
612
613 def _get_floating_ip_in_server_addresses(floating_ip, server):
614 for addresses in server['addresses'].values():
615 for address in addresses:
616 if (
617 address['OS-EXT-IPS:type'] == 'floating' and
618 address['addr'] == floating_ip['floating_ip_address']
619 ):
620 return address
621 return None
622
623 start_time = int(time.time())
624 while True:
625 server = servers_client.show_server(server['id'])['server']
626 address = _get_floating_ip_in_server_addresses(floating_ip, server)
627 if address is None and wait_for_disassociate:
628 return None
629 if not wait_for_disassociate and address:
630 return address
631
632 if int(time.time()) - start_time >= servers_client.build_timeout:
633 if wait_for_disassociate:
634 msg = ('Floating ip %s failed to disassociate from server %s '
635 'in time.' % (floating_ip, server['id']))
636 else:
637 msg = ('Floating ip %s failed to associate with server %s '
638 'in time.' % (floating_ip, server['id']))
639 raise lib_exc.TimeoutException(msg)
640 time.sleep(servers_client.build_interval)
Lee Yarwood0b4bc3d2021-11-11 17:45:25 +0000641
642
643def wait_for_ping(server_ip, timeout=30, interval=1):
644 """Waits for an address to become pingable"""
645 start_time = int(time.time())
646 while int(time.time()) - start_time < timeout:
647 response = os.system("ping -c 1 " + server_ip)
648 if response == 0:
649 return
650 time.sleep(interval)
651 raise lib_exc.TimeoutException()
652
653
Eliad Cohenbec2d4d2022-09-14 17:52:59 +0000654def wait_for_port_status(client, port_id, status):
655 """Wait for a port reach a certain status : ["BUILD" | "DOWN" | "ACTIVE"]
656 :param client: The network client to use when querying the port's
657 status
658 :param status: A string to compare the current port status-to.
659 :param port_id: The uuid of the port we would like queried for status.
660 """
661 start_time = time.time()
662 while (time.time() - start_time <= client.build_timeout):
663 result = client.show_port(port_id)
664 if result['port']['status'].lower() == status.lower():
665 return result
666 time.sleep(client.build_interval)
667 raise lib_exc.TimeoutException
668
669
Lee Yarwood0b4bc3d2021-11-11 17:45:25 +0000670def wait_for_ssh(ssh_client, timeout=30):
671 """Waits for SSH connection to become usable"""
672 start_time = int(time.time())
673 while int(time.time()) - start_time < timeout:
674 try:
675 ssh_client.validate_authentication()
676 return
677 except lib_exc.SSHTimeout:
678 pass
679 raise lib_exc.TimeoutException()
Abhishek Kekane0188f462022-01-18 06:47:28 +0000680
681
682def wait_for_caching(client, cache_client, image_id):
683 """Waits until image is cached"""
684 start = int(time.time())
685 while int(time.time()) - start < client.build_timeout:
686 caching = cache_client.list_cache()
687 output = [image['image_id'] for image in caching['cached_images']]
688 if output and image_id in output:
689 return caching
690
691 time.sleep(client.build_interval)
692
693 message = ('Image %s failed to cache in time.' % image_id)
694 caller = test_utils.find_test_caller()
695 if caller:
696 message = '(%s) %s' % (caller, message)
697 raise lib_exc.TimeoutException(message)
Bas de Bruijnec9f9a032022-07-19 11:16:02 -0300698
699
700def wait_for_object_create(object_client, container_name, object_name,
701 interval=1):
702 """Waits for created object to become available"""
703 start_time = time.time()
704 while time.time() - start_time < object_client.build_timeout:
705 try:
706 return object_client.get_object(container_name, object_name)
707 except lib_exc.NotFound:
708 time.sleep(interval)
709 message = ('Object %s failed to create within the required time (%s s).' %
710 (object_name, object_client.build_timeout))
711 raise lib_exc.TimeoutException(message)