manila-tempest-plugin/manila_tempest_tests/common/waiters.py

253 lines
10 KiB
Python

# Copyright 2021 Red Hat, Inc.
# All Rights Reserved.
#
# 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 import config
from tempest.lib import exceptions
from manila_tempest_tests.services.share.v2.json import shares_client
from manila_tempest_tests import share_exceptions
CONF = config.CONF
LATEST_MICROVERSION = CONF.share.max_api_microversion
def _get_access_rule(body, rule_id):
for rule in body:
if rule['id'] in rule_id:
return rule
def _get_name_of_raise_method(resource_name):
if resource_name == 'snapshot_access':
return 'AccessRuleBuildErrorException'
if resource_name == 'share_replica':
return 'ShareInstanceBuildErrorException'
resource_name = resource_name.title()
name = resource_name.replace('_', '')
return name + 'BuildErrorException'
def wait_for_resource_status(client, resource_id, status,
resource_name='share', rule_id=None,
status_attr='status',
raise_rule_in_error_state=True,
version=LATEST_MICROVERSION,
timeout=None):
"""Waits for a resource to reach a given status."""
get_resource_action = {
'share': 'get_share',
'snapshot': 'get_snapshot',
'share_server': 'show_share_server',
'share_instance': 'get_share_instance',
'snapshot_instance': 'get_snapshot_instance',
'access_rule': 'list_access_rules',
'snapshot_access': 'list_snapshot_access_rules',
'share_group': 'get_share_group',
'share_group_snapshot': 'get_share_group_snapshot',
'share_replica': 'get_share_replica',
'share_backup': 'get_share_backup'
}
action_name = get_resource_action[resource_name]
# This code snippet is intended to set the dictionary key of the returned
# response for share access rule and for snapshot access rule.
if 'access' in resource_name:
rn = '_'.join(action_name.split('_')[1:-1]) + '_list'
else:
rn = resource_name
# Since API v2 requests require an additional parameter for micro-versions,
# it's necessary to pass the required parameters according to the version.
resource_action = getattr(client, action_name)
method_args = [resource_id]
method_kwargs = {}
if isinstance(client, shares_client.SharesV2Client):
method_kwargs.update({'version': version})
body = resource_action(*method_args, **method_kwargs)[rn]
if 'access' in resource_name:
status_attr = 'state'
body = _get_access_rule(body, rule_id)
resource_status = body[status_attr]
start = int(time.time())
exp_status = status if isinstance(status, list) else [status]
resource_status_check_time_out = client.build_timeout
if timeout is not None:
resource_status_check_time_out = timeout
while resource_status not in exp_status:
time.sleep(client.build_interval)
body = resource_action(*method_args, **method_kwargs)[rn]
if 'access' in resource_name:
status_attr = 'state'
body = _get_access_rule(body, rule_id)
resource_status = body[status_attr]
if resource_status in exp_status:
return
elif 'error' in resource_status.lower() and raise_rule_in_error_state:
raise_method = _get_name_of_raise_method(resource_name)
resource_exception = getattr(share_exceptions, raise_method)
raise resource_exception(resource_id=resource_id)
if int(time.time()) - start >= resource_status_check_time_out:
message = ('%s %s failed to reach %s status (current %s) '
'within the required time (%s s).' %
(resource_name.replace('_', ' '), resource_id, status,
resource_status, resource_status_check_time_out))
raise exceptions.TimeoutException(message)
def wait_for_migration_status(client, share_id, dest_host, status_to_wait,
version=LATEST_MICROVERSION):
"""Waits for a share to migrate to a certain host."""
statuses = ((status_to_wait,)
if not isinstance(status_to_wait, (tuple, list, set))
else status_to_wait)
share = client.get_share(share_id, version=version)['share']
migration_timeout = CONF.share.migration_timeout
start = int(time.time())
while share['task_state'] not in statuses:
time.sleep(client.build_interval)
share = client.get_share(share_id, version=version)['share']
if share['task_state'] in statuses:
break
elif share['task_state'] == 'migration_error':
raise share_exceptions.ShareMigrationException(
share_id=share['id'], src=share['host'], dest=dest_host)
elif int(time.time()) - start >= migration_timeout:
message = ('Share %(share_id)s failed to reach a status in'
'%(status)s when migrating from host %(src)s to '
'host %(dest)s within the required time '
'%(timeout)s.' % {
'src': share['host'],
'dest': dest_host,
'share_id': share['id'],
'timeout': client.build_timeout,
'status': str(statuses),
})
raise exceptions.TimeoutException(message)
return share
def wait_for_snapshot_access_rule_deletion(client, snapshot_id, rule_id):
rule = client.get_snapshot_access_rule(snapshot_id, rule_id)
start = int(time.time())
while rule is not None:
time.sleep(client.build_interval)
rule = client.get_snapshot_access_rule(snapshot_id, rule_id)
if rule is None:
return
if int(time.time()) - start >= client.build_timeout:
message = ('The snapshot access rule %(id)s failed to delete '
'within the required time (%(time)ss).' %
{
'time': client.build_timeout,
'id': rule_id,
})
raise exceptions.TimeoutException(message)
def wait_for_message(client, resource_id):
"""Waits until a message for a resource with given id exists"""
start = int(time.time())
message = None
while not message:
time.sleep(client.build_interval)
for msg in client.list_messages()['messages']:
if msg['resource_id'] == resource_id:
return msg
if int(time.time()) - start >= client.build_timeout:
message = ('No message for resource with id %s was created in'
' the required time (%s s).' %
(resource_id, client.build_timeout))
raise exceptions.TimeoutException(message)
def wait_for_soft_delete(client, share_id, version=LATEST_MICROVERSION):
"""Wait for a share soft delete to recycle bin."""
share = client.get_share(share_id, version=version)['share']
start = int(time.time())
while not share['is_soft_deleted']:
time.sleep(client.build_interval)
share = client.get_share(share_id, version=version)['share']
if share['is_soft_deleted']:
break
elif int(time.time()) - start >= client.build_timeout:
message = ('Share %(share_id)s failed to be soft deleted to '
'recycle bin within the required time '
'%(timeout)s.' % {
'share_id': share['id'],
'timeout': client.build_timeout,
})
raise exceptions.TimeoutException(message)
def wait_for_restore(client, share_id, version=LATEST_MICROVERSION):
"""Wait for a share restore from recycle bin."""
share = client.get_share(share_id, version=version)['share']
start = int(time.time())
while share['is_soft_deleted']:
time.sleep(client.build_interval)
share = client.get_share(share_id, version=version)['share']
if not share['is_soft_deleted']:
break
elif int(time.time()) - start >= client.build_timeout:
message = ('Share %(share_id)s failed to restore from '
'recycle bin within the required time '
'%(timeout)s.' % {
'share_id': share['id'],
'timeout': client.build_timeout,
})
raise exceptions.TimeoutException(message)
def wait_for_subnet_create_check(client, share_network_id,
neutron_net_id=None,
neutron_subnet_id=None,
availability_zone=None):
result = client.subnet_create_check(
share_network_id, neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id,
availability_zone=availability_zone)
start = int(time.time())
while not result['compatible']:
time.sleep(client.build_interval)
result = client.subnet_create_check(
share_network_id, neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id,
availability_zone=availability_zone)
if result['compatible']:
break
elif int(time.time()) - start >= client.build_timeout or (
result['compatible'] is False):
message = ('Subnet create check failed within the '
'required time %(timeout)s seconds for share network '
'%(share_network)s.' % {
'timeout': client.build_timeout,
'share_network': share_network_id,
})
raise exceptions.TimeoutException(message)