lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 1 | # Copyright 2021 Red Hat, Inc. |
| 2 | # All Rights Reserved. |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 5 | # not use this file except in compliance with the License. You may obtain |
| 6 | # a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | # License for the specific language governing permissions and limitations |
| 14 | # under the License. |
| 15 | |
| 16 | import time |
| 17 | |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 18 | from tempest import config |
| 19 | from tempest.lib import exceptions |
| 20 | |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 21 | from manila_tempest_tests.services.share.v2.json import shares_client |
| 22 | from manila_tempest_tests import share_exceptions |
| 23 | |
| 24 | CONF = config.CONF |
| 25 | LATEST_MICROVERSION = CONF.share.max_api_microversion |
| 26 | |
| 27 | |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 28 | def _get_access_rule(body, rule_id): |
| 29 | for rule in body: |
| 30 | if rule['id'] in rule_id: |
| 31 | return rule |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 32 | |
| 33 | |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 34 | def _get_name_of_raise_method(resource_name): |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 35 | if resource_name == 'snapshot_access': |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 36 | return 'AccessRuleBuildErrorException' |
| 37 | if resource_name == 'share_replica': |
| 38 | return 'ShareInstanceBuildErrorException' |
| 39 | resource_name = resource_name.title() |
| 40 | name = resource_name.replace('_', '') |
| 41 | return name + 'BuildErrorException' |
| 42 | |
| 43 | |
| 44 | def wait_for_resource_status(client, resource_id, status, |
| 45 | resource_name='share', rule_id=None, |
| 46 | status_attr='status', |
| 47 | raise_rule_in_error_state=True, |
| 48 | version=LATEST_MICROVERSION): |
| 49 | """Waits for a resource to reach a given status.""" |
| 50 | |
| 51 | get_resource_action = { |
| 52 | 'share': 'get_share', |
| 53 | 'snapshot': 'get_snapshot', |
| 54 | 'share_server': 'show_share_server', |
| 55 | 'share_instance': 'get_share_instance', |
| 56 | 'snapshot_instance': 'get_snapshot_instance', |
| 57 | 'access_rule': 'list_access_rules', |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 58 | 'snapshot_access': 'list_snapshot_access_rules', |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 59 | 'share_group': 'get_share_group', |
| 60 | 'share_group_snapshot': 'get_share_group_snapshot', |
| 61 | 'share_replica': 'get_share_replica', |
| 62 | } |
| 63 | |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 64 | action_name = get_resource_action[resource_name] |
| 65 | # This code snippet is intended to set the dictionary key of the returned |
| 66 | # response for share access rule and for snapshot access rule. |
| 67 | if 'access' in resource_name: |
| 68 | rn = '_'.join(action_name.split('_')[1:-1]) + '_list' |
| 69 | else: |
| 70 | rn = resource_name |
| 71 | |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 72 | # Since API v2 requests require an additional parameter for micro-versions, |
| 73 | # it's necessary to pass the required parameters according to the version. |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 74 | resource_action = getattr(client, action_name) |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 75 | method_args = [resource_id] |
| 76 | method_kwargs = {} |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 77 | if isinstance(client, shares_client.SharesV2Client): |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 78 | method_kwargs.update({'version': version}) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 79 | body = resource_action(*method_args, **method_kwargs)[rn] |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 80 | |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 81 | if 'access' in resource_name: |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 82 | status_attr = 'state' |
| 83 | body = _get_access_rule(body, rule_id) |
| 84 | |
| 85 | resource_status = body[status_attr] |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 86 | start = int(time.time()) |
| 87 | |
| 88 | exp_status = status if isinstance(status, list) else [status] |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 89 | while resource_status not in exp_status: |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 90 | time.sleep(client.build_interval) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 91 | body = resource_action(*method_args, **method_kwargs)[rn] |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 92 | |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 93 | if 'access' in resource_name: |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 94 | status_attr = 'state' |
| 95 | body = _get_access_rule(body, rule_id) |
| 96 | |
| 97 | resource_status = body[status_attr] |
| 98 | |
| 99 | if resource_status in exp_status: |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 100 | return |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 101 | elif 'error' in resource_status.lower() and raise_rule_in_error_state: |
| 102 | raise_method = _get_name_of_raise_method(resource_name) |
| 103 | resource_exception = getattr(share_exceptions, raise_method) |
| 104 | raise resource_exception(resource_id=resource_id) |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 105 | if int(time.time()) - start >= client.build_timeout: |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 106 | message = ('%s %s failed to reach %s status (current %s) ' |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 107 | 'within the required time (%s s).' % |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 108 | (resource_name.replace('_', ' '), resource_id, status, |
| 109 | resource_status, client.build_timeout)) |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 110 | raise exceptions.TimeoutException(message) |
| 111 | |
| 112 | |
| 113 | def wait_for_migration_status(client, share_id, dest_host, status_to_wait, |
| 114 | version=LATEST_MICROVERSION): |
| 115 | """Waits for a share to migrate to a certain host.""" |
| 116 | statuses = ((status_to_wait,) |
| 117 | if not isinstance(status_to_wait, (tuple, list, set)) |
| 118 | else status_to_wait) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 119 | share = client.get_share(share_id, version=version)['share'] |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 120 | migration_timeout = CONF.share.migration_timeout |
| 121 | start = int(time.time()) |
| 122 | while share['task_state'] not in statuses: |
| 123 | time.sleep(client.build_interval) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 124 | share = client.get_share(share_id, version=version)['share'] |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 125 | if share['task_state'] in statuses: |
| 126 | break |
| 127 | elif share['task_state'] == 'migration_error': |
| 128 | raise share_exceptions.ShareMigrationException( |
| 129 | share_id=share['id'], src=share['host'], dest=dest_host) |
| 130 | elif int(time.time()) - start >= migration_timeout: |
| 131 | message = ('Share %(share_id)s failed to reach a status in' |
| 132 | '%(status)s when migrating from host %(src)s to ' |
| 133 | 'host %(dest)s within the required time ' |
| 134 | '%(timeout)s.' % { |
| 135 | 'src': share['host'], |
| 136 | 'dest': dest_host, |
| 137 | 'share_id': share['id'], |
| 138 | 'timeout': client.build_timeout, |
haixin | 4889581 | 2020-09-30 13:50:37 +0800 | [diff] [blame] | 139 | 'status': str(statuses), |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 140 | }) |
| 141 | raise exceptions.TimeoutException(message) |
| 142 | return share |
| 143 | |
| 144 | |
| 145 | def wait_for_snapshot_access_rule_deletion(client, snapshot_id, rule_id): |
| 146 | rule = client.get_snapshot_access_rule(snapshot_id, rule_id) |
| 147 | start = int(time.time()) |
| 148 | |
| 149 | while rule is not None: |
| 150 | time.sleep(client.build_interval) |
| 151 | |
| 152 | rule = client.get_snapshot_access_rule(snapshot_id, rule_id) |
| 153 | |
| 154 | if rule is None: |
| 155 | return |
| 156 | if int(time.time()) - start >= client.build_timeout: |
| 157 | message = ('The snapshot access rule %(id)s failed to delete ' |
| 158 | 'within the required time (%(time)ss).' % |
| 159 | { |
| 160 | 'time': client.build_timeout, |
| 161 | 'id': rule_id, |
| 162 | }) |
| 163 | raise exceptions.TimeoutException(message) |
| 164 | |
| 165 | |
| 166 | def wait_for_message(client, resource_id): |
| 167 | """Waits until a message for a resource with given id exists""" |
| 168 | start = int(time.time()) |
| 169 | message = None |
| 170 | |
| 171 | while not message: |
| 172 | time.sleep(client.build_interval) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 173 | for msg in client.list_messages()['messages']: |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 174 | if msg['resource_id'] == resource_id: |
| 175 | return msg |
| 176 | |
| 177 | if int(time.time()) - start >= client.build_timeout: |
| 178 | message = ('No message for resource with id %s was created in' |
| 179 | ' the required time (%s s).' % |
| 180 | (resource_id, client.build_timeout)) |
| 181 | raise exceptions.TimeoutException(message) |
haixin | 1c5e68d | 2021-07-28 09:54:34 +0800 | [diff] [blame] | 182 | |
| 183 | |
| 184 | def wait_for_soft_delete(client, share_id, version=LATEST_MICROVERSION): |
| 185 | """Wait for a share soft delete to recycle bin.""" |
| 186 | share = client.get_share(share_id, version=version)['share'] |
| 187 | start = int(time.time()) |
| 188 | while not share['is_soft_deleted']: |
| 189 | time.sleep(client.build_interval) |
| 190 | share = client.get_share(share_id, version=version)['share'] |
| 191 | if share['is_soft_deleted']: |
| 192 | break |
| 193 | elif int(time.time()) - start >= client.build_timeout: |
| 194 | message = ('Share %(share_id)s failed to be soft deleted to ' |
| 195 | 'recycle bin within the required time ' |
| 196 | '%(timeout)s.' % { |
| 197 | 'share_id': share['id'], |
| 198 | 'timeout': client.build_timeout, |
| 199 | }) |
| 200 | raise exceptions.TimeoutException(message) |
| 201 | |
| 202 | |
| 203 | def wait_for_restore(client, share_id, version=LATEST_MICROVERSION): |
| 204 | """Wait for a share restore from recycle bin.""" |
| 205 | share = client.get_share(share_id, version=version)['share'] |
| 206 | start = int(time.time()) |
| 207 | while share['is_soft_deleted']: |
| 208 | time.sleep(client.build_interval) |
| 209 | share = client.get_share(share_id, version=version)['share'] |
| 210 | if not share['is_soft_deleted']: |
| 211 | break |
| 212 | elif int(time.time()) - start >= client.build_timeout: |
| 213 | message = ('Share %(share_id)s failed to restore from ' |
| 214 | 'recycle bin within the required time ' |
| 215 | '%(timeout)s.' % { |
| 216 | 'share_id': share['id'], |
| 217 | 'timeout': client.build_timeout, |
| 218 | }) |
| 219 | raise exceptions.TimeoutException(message) |
Andre | c1a3c0e | 2022-01-29 14:46:53 +0000 | [diff] [blame] | 220 | |
| 221 | |
| 222 | def wait_for_subnet_create_check(client, share_network_id, |
| 223 | neutron_net_id=None, |
| 224 | neutron_subnet_id=None, |
| 225 | availability_zone=None): |
| 226 | result = client.subnet_create_check( |
| 227 | share_network_id, neutron_net_id=neutron_net_id, |
| 228 | neutron_subnet_id=neutron_subnet_id, |
| 229 | availability_zone=availability_zone) |
| 230 | start = int(time.time()) |
| 231 | while not result['compatible']: |
| 232 | time.sleep(client.build_interval) |
| 233 | result = client.subnet_create_check( |
| 234 | share_network_id, neutron_net_id=neutron_net_id, |
| 235 | neutron_subnet_id=neutron_subnet_id, |
| 236 | availability_zone=availability_zone) |
| 237 | if result['compatible']: |
| 238 | break |
| 239 | elif int(time.time()) - start >= client.build_timeout or ( |
| 240 | result['compatible'] is False): |
| 241 | message = ('Subnet create check failed within the ' |
| 242 | 'required time %(timeout)s seconds for share network ' |
| 243 | '%(share_network)s.' % { |
| 244 | 'timeout': client.build_timeout, |
| 245 | 'share_network': share_network_id, |
| 246 | }) |
| 247 | raise exceptions.TimeoutException(message) |