| # 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 tempest.common.utils import misc as misc_utils |
| from tempest import config |
| from tempest import exceptions |
| from tempest.openstack.common import log as logging |
| |
| 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): |
| if client.service == CONF.compute.catalog_v3_type: |
| task_state = body.get("os-extended-status:task_state", None) |
| else: |
| task_state = body.get('OS-EXT-STS:task_state', None) |
| return task_state |
| |
| # NOTE(afazekas): UNKNOWN status possible on ERROR |
| # or in a very early stage. |
| resp, body = client.get_server(server_id) |
| 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 bacuse 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) |
| resp, body = client.get_server(server_id) |
| 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_image_status(client, image_id, status): |
| """Waits for an image to reach a given status. |
| |
| The client should have a get_image(image_id) method to get the image. |
| The client should also have build_interval and build_timeout attributes. |
| """ |
| resp, image = client.get_image(image_id) |
| start = int(time.time()) |
| |
| while image['status'] != status: |
| time.sleep(client.build_interval) |
| resp, image = client.get_image(image_id) |
| if image['status'] == '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 image['status'] == status: |
| return |
| |
| if int(time.time()) - start >= client.build_timeout: |
| message = ('Image %(image_id)s failed to reach %(status)s ' |
| 'status within the required time (%(timeout)s s).' % |
| {'image_id': image_id, |
| 'status': status, |
| 'timeout': client.build_timeout}) |
| message += ' Current status: %s.' % image['status'] |
| caller = misc_utils.find_test_caller() |
| if caller: |
| message = '(%s) %s' % (caller, message) |
| raise exceptions.TimeoutException(message) |