|  | #    Licensed under the Apache License, Version 2.0 (the "License"); you may | 
|  | #    not use this file except in compliance with the License. You may obtain | 
|  | #    a copy of the License at | 
|  | # | 
|  | #         http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | #    Unless required by applicable law or agreed to in writing, software | 
|  | #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
|  | #    License for the specific language governing permissions and limitations | 
|  | #    under the License. | 
|  |  | 
|  |  | 
|  | import time | 
|  |  | 
|  | from oslo_log import log as logging | 
|  | from tempest_lib.common.utils import misc as misc_utils | 
|  | from tempest_lib import exceptions as lib_exc | 
|  |  | 
|  | from tempest import config | 
|  | from tempest import exceptions | 
|  |  | 
|  | CONF = config.CONF | 
|  | LOG = logging.getLogger(__name__) | 
|  |  | 
|  |  | 
|  | # NOTE(afazekas): This function needs to know a token and a subject. | 
|  | def wait_for_server_status(client, server_id, status, ready_wait=True, | 
|  | extra_timeout=0, raise_on_error=True): | 
|  | """Waits for a server to reach a given status.""" | 
|  |  | 
|  | def _get_task_state(body): | 
|  | return body.get('OS-EXT-STS:task_state', None) | 
|  |  | 
|  | # NOTE(afazekas): UNKNOWN status possible on ERROR | 
|  | # or in a very early stage. | 
|  | body = client.show_server(server_id)['server'] | 
|  | old_status = server_status = body['status'] | 
|  | old_task_state = task_state = _get_task_state(body) | 
|  | start_time = int(time.time()) | 
|  | timeout = client.build_timeout + extra_timeout | 
|  | while True: | 
|  | # NOTE(afazekas): Now the BUILD status only reached | 
|  | # between the UNKNOWN->ACTIVE transition. | 
|  | # TODO(afazekas): enumerate and validate the stable status set | 
|  | if status == 'BUILD' and server_status != 'UNKNOWN': | 
|  | return | 
|  | if server_status == status: | 
|  | if ready_wait: | 
|  | if status == 'BUILD': | 
|  | return | 
|  | # NOTE(afazekas): The instance is in "ready for action state" | 
|  | # when no task in progress | 
|  | # NOTE(afazekas): Converted to string because of the XML | 
|  | # responses | 
|  | if str(task_state) == "None": | 
|  | # without state api extension 3 sec usually enough | 
|  | time.sleep(CONF.compute.ready_wait) | 
|  | return | 
|  | else: | 
|  | return | 
|  |  | 
|  | time.sleep(client.build_interval) | 
|  | body = client.show_server(server_id)['server'] | 
|  | server_status = body['status'] | 
|  | task_state = _get_task_state(body) | 
|  | if (server_status != old_status) or (task_state != old_task_state): | 
|  | LOG.info('State transition "%s" ==> "%s" after %d second wait', | 
|  | '/'.join((old_status, str(old_task_state))), | 
|  | '/'.join((server_status, str(task_state))), | 
|  | time.time() - start_time) | 
|  | if (server_status == 'ERROR') and raise_on_error: | 
|  | if 'fault' in body: | 
|  | raise exceptions.BuildErrorException(body['fault'], | 
|  | server_id=server_id) | 
|  | else: | 
|  | raise exceptions.BuildErrorException(server_id=server_id) | 
|  |  | 
|  | timed_out = int(time.time()) - start_time >= timeout | 
|  |  | 
|  | if timed_out: | 
|  | expected_task_state = 'None' if ready_wait else 'n/a' | 
|  | message = ('Server %(server_id)s failed to reach %(status)s ' | 
|  | 'status and task state "%(expected_task_state)s" ' | 
|  | 'within the required time (%(timeout)s s).' % | 
|  | {'server_id': server_id, | 
|  | 'status': status, | 
|  | 'expected_task_state': expected_task_state, | 
|  | 'timeout': timeout}) | 
|  | message += ' Current status: %s.' % server_status | 
|  | message += ' Current task state: %s.' % task_state | 
|  | caller = misc_utils.find_test_caller() | 
|  | if caller: | 
|  | message = '(%s) %s' % (caller, message) | 
|  | raise exceptions.TimeoutException(message) | 
|  | old_status = server_status | 
|  | old_task_state = task_state | 
|  |  | 
|  |  | 
|  | def wait_for_server_termination(client, server_id, ignore_error=False): | 
|  | """Waits for server to reach termination.""" | 
|  | start_time = int(time.time()) | 
|  | while True: | 
|  | try: | 
|  | body = client.show_server(server_id)['server'] | 
|  | except lib_exc.NotFound: | 
|  | return | 
|  |  | 
|  | server_status = body['status'] | 
|  | if server_status == 'ERROR' and not ignore_error: | 
|  | raise exceptions.BuildErrorException(server_id=server_id) | 
|  |  | 
|  | if int(time.time()) - start_time >= client.build_timeout: | 
|  | raise exceptions.TimeoutException | 
|  |  | 
|  | time.sleep(client.build_interval) | 
|  |  | 
|  |  | 
|  | def wait_for_image_status(client, image_id, status): | 
|  | """Waits for an image to reach a given status. | 
|  |  | 
|  | The client should have a show_image(image_id) method to get the image. | 
|  | The client should also have build_interval and build_timeout attributes. | 
|  | """ | 
|  | image = client.show_image(image_id) | 
|  | # Compute image client return response wrapped in 'image' element | 
|  | # which is not case with glance image client. | 
|  | if 'image' in image: | 
|  | image = image['image'] | 
|  | start = int(time.time()) | 
|  |  | 
|  | while image['status'] != status: | 
|  | time.sleep(client.build_interval) | 
|  | image = client.show_image(image_id) | 
|  | # Compute image client return response wrapped in 'image' element | 
|  | # which is not case with glance image client. | 
|  | if 'image' in image: | 
|  | image = image['image'] | 
|  | status_curr = image['status'] | 
|  | if status_curr == 'ERROR': | 
|  | raise exceptions.AddImageException(image_id=image_id) | 
|  |  | 
|  | # check the status again to avoid a false negative where we hit | 
|  | # the timeout at the same time that the image reached the expected | 
|  | # status | 
|  | if status_curr == status: | 
|  | return | 
|  |  | 
|  | if int(time.time()) - start >= client.build_timeout: | 
|  | message = ('Image %(image_id)s failed to reach %(status)s state' | 
|  | '(current state %(status_curr)s) ' | 
|  | 'within the required time (%(timeout)s s).' % | 
|  | {'image_id': image_id, | 
|  | 'status': status, | 
|  | 'status_curr': status_curr, | 
|  | 'timeout': client.build_timeout}) | 
|  | caller = misc_utils.find_test_caller() | 
|  | if caller: | 
|  | message = '(%s) %s' % (caller, message) | 
|  | raise exceptions.TimeoutException(message) | 
|  |  | 
|  |  | 
|  | def wait_for_volume_status(client, volume_id, status): | 
|  | """Waits for a Volume to reach a given status.""" | 
|  | body = client.show_volume(volume_id)['volume'] | 
|  | volume_status = body['status'] | 
|  | start = int(time.time()) | 
|  |  | 
|  | while volume_status != status: | 
|  | time.sleep(client.build_interval) | 
|  | body = client.show_volume(volume_id)['volume'] | 
|  | volume_status = body['status'] | 
|  | if volume_status == 'error': | 
|  | raise exceptions.VolumeBuildErrorException(volume_id=volume_id) | 
|  | if volume_status == 'error_restoring': | 
|  | raise exceptions.VolumeRestoreErrorException(volume_id=volume_id) | 
|  |  | 
|  | if int(time.time()) - start >= client.build_timeout: | 
|  | message = ('Volume %s failed to reach %s status (current %s) ' | 
|  | 'within the required time (%s s).' % | 
|  | (volume_id, status, volume_status, | 
|  | client.build_timeout)) | 
|  | raise exceptions.TimeoutException(message) | 
|  |  | 
|  |  | 
|  | def wait_for_snapshot_status(client, snapshot_id, status): | 
|  | """Waits for a Snapshot to reach a given status.""" | 
|  | body = client.show_snapshot(snapshot_id)['snapshot'] | 
|  | snapshot_status = body['status'] | 
|  | start = int(time.time()) | 
|  |  | 
|  | while snapshot_status != status: | 
|  | time.sleep(client.build_interval) | 
|  | body = client.show_snapshot(snapshot_id)['snapshot'] | 
|  | snapshot_status = body['status'] | 
|  | if snapshot_status == 'error': | 
|  | raise exceptions.SnapshotBuildErrorException( | 
|  | snapshot_id=snapshot_id) | 
|  | if int(time.time()) - start >= client.build_timeout: | 
|  | message = ('Snapshot %s failed to reach %s status (current %s) ' | 
|  | 'within the required time (%s s).' % | 
|  | (snapshot_id, status, snapshot_status, | 
|  | client.build_timeout)) | 
|  | raise exceptions.TimeoutException(message) | 
|  |  | 
|  |  | 
|  | def wait_for_bm_node_status(client, node_id, attr, status): | 
|  | """Waits for a baremetal node attribute to reach given status. | 
|  |  | 
|  | The client should have a show_node(node_uuid) method to get the node. | 
|  | """ | 
|  | _, node = client.show_node(node_id) | 
|  | start = int(time.time()) | 
|  |  | 
|  | while node[attr] != status: | 
|  | time.sleep(client.build_interval) | 
|  | _, node = client.show_node(node_id) | 
|  | status_curr = node[attr] | 
|  | if status_curr == status: | 
|  | return | 
|  |  | 
|  | if int(time.time()) - start >= client.build_timeout: | 
|  | message = ('Node %(node_id)s failed to reach %(attr)s=%(status)s ' | 
|  | 'within the required time (%(timeout)s s).' % | 
|  | {'node_id': node_id, | 
|  | 'attr': attr, | 
|  | 'status': status, | 
|  | 'timeout': client.build_timeout}) | 
|  | message += ' Current state of %s: %s.' % (attr, status_curr) | 
|  | caller = misc_utils.find_test_caller() | 
|  | if caller: | 
|  | message = '(%s) %s' % (caller, message) | 
|  | raise exceptions.TimeoutException(message) |