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, |
agireesh | 541c6cd | 2023-10-17 14:57:32 +0530 | [diff] [blame] | 48 | version=LATEST_MICROVERSION, |
| 49 | timeout=None): |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 50 | """Waits for a resource to reach a given status.""" |
| 51 | |
| 52 | get_resource_action = { |
| 53 | 'share': 'get_share', |
| 54 | 'snapshot': 'get_snapshot', |
| 55 | 'share_server': 'show_share_server', |
| 56 | 'share_instance': 'get_share_instance', |
| 57 | 'snapshot_instance': 'get_snapshot_instance', |
| 58 | 'access_rule': 'list_access_rules', |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 59 | 'snapshot_access': 'list_snapshot_access_rules', |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 60 | 'share_group': 'get_share_group', |
| 61 | 'share_group_snapshot': 'get_share_group_snapshot', |
| 62 | 'share_replica': 'get_share_replica', |
| 63 | } |
| 64 | |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 65 | action_name = get_resource_action[resource_name] |
| 66 | # This code snippet is intended to set the dictionary key of the returned |
| 67 | # response for share access rule and for snapshot access rule. |
| 68 | if 'access' in resource_name: |
| 69 | rn = '_'.join(action_name.split('_')[1:-1]) + '_list' |
| 70 | else: |
| 71 | rn = resource_name |
| 72 | |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 73 | # Since API v2 requests require an additional parameter for micro-versions, |
| 74 | # it's necessary to pass the required parameters according to the version. |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 75 | resource_action = getattr(client, action_name) |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 76 | method_args = [resource_id] |
| 77 | method_kwargs = {} |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 78 | if isinstance(client, shares_client.SharesV2Client): |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 79 | method_kwargs.update({'version': version}) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 80 | body = resource_action(*method_args, **method_kwargs)[rn] |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 81 | |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 82 | if 'access' in resource_name: |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 83 | status_attr = 'state' |
| 84 | body = _get_access_rule(body, rule_id) |
| 85 | |
| 86 | resource_status = body[status_attr] |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 87 | start = int(time.time()) |
| 88 | |
| 89 | exp_status = status if isinstance(status, list) else [status] |
agireesh | 541c6cd | 2023-10-17 14:57:32 +0530 | [diff] [blame] | 90 | resource_status_check_time_out = client.build_timeout |
| 91 | if timeout is not None: |
| 92 | resource_status_check_time_out = timeout |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 93 | while resource_status not in exp_status: |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 94 | time.sleep(client.build_interval) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 95 | body = resource_action(*method_args, **method_kwargs)[rn] |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 96 | |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 97 | if 'access' in resource_name: |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 98 | status_attr = 'state' |
| 99 | body = _get_access_rule(body, rule_id) |
| 100 | |
| 101 | resource_status = body[status_attr] |
| 102 | |
| 103 | if resource_status in exp_status: |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 104 | return |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 105 | elif 'error' in resource_status.lower() and raise_rule_in_error_state: |
| 106 | raise_method = _get_name_of_raise_method(resource_name) |
| 107 | resource_exception = getattr(share_exceptions, raise_method) |
| 108 | raise resource_exception(resource_id=resource_id) |
agireesh | 541c6cd | 2023-10-17 14:57:32 +0530 | [diff] [blame] | 109 | if int(time.time()) - start >= resource_status_check_time_out: |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 110 | message = ('%s %s failed to reach %s status (current %s) ' |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 111 | 'within the required time (%s s).' % |
lkuchlan | f7fc5b6 | 2021-01-26 14:53:43 +0200 | [diff] [blame] | 112 | (resource_name.replace('_', ' '), resource_id, status, |
agireesh | 541c6cd | 2023-10-17 14:57:32 +0530 | [diff] [blame] | 113 | resource_status, resource_status_check_time_out)) |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 114 | raise exceptions.TimeoutException(message) |
| 115 | |
| 116 | |
| 117 | def wait_for_migration_status(client, share_id, dest_host, status_to_wait, |
| 118 | version=LATEST_MICROVERSION): |
| 119 | """Waits for a share to migrate to a certain host.""" |
| 120 | statuses = ((status_to_wait,) |
| 121 | if not isinstance(status_to_wait, (tuple, list, set)) |
| 122 | else status_to_wait) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 123 | share = client.get_share(share_id, version=version)['share'] |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 124 | migration_timeout = CONF.share.migration_timeout |
| 125 | start = int(time.time()) |
| 126 | while share['task_state'] not in statuses: |
| 127 | time.sleep(client.build_interval) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 128 | share = client.get_share(share_id, version=version)['share'] |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 129 | if share['task_state'] in statuses: |
| 130 | break |
| 131 | elif share['task_state'] == 'migration_error': |
| 132 | raise share_exceptions.ShareMigrationException( |
| 133 | share_id=share['id'], src=share['host'], dest=dest_host) |
| 134 | elif int(time.time()) - start >= migration_timeout: |
| 135 | message = ('Share %(share_id)s failed to reach a status in' |
| 136 | '%(status)s when migrating from host %(src)s to ' |
| 137 | 'host %(dest)s within the required time ' |
| 138 | '%(timeout)s.' % { |
| 139 | 'src': share['host'], |
| 140 | 'dest': dest_host, |
| 141 | 'share_id': share['id'], |
| 142 | 'timeout': client.build_timeout, |
haixin | 4889581 | 2020-09-30 13:50:37 +0800 | [diff] [blame] | 143 | 'status': str(statuses), |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 144 | }) |
| 145 | raise exceptions.TimeoutException(message) |
| 146 | return share |
| 147 | |
| 148 | |
| 149 | def wait_for_snapshot_access_rule_deletion(client, snapshot_id, rule_id): |
| 150 | rule = client.get_snapshot_access_rule(snapshot_id, rule_id) |
| 151 | start = int(time.time()) |
| 152 | |
| 153 | while rule is not None: |
| 154 | time.sleep(client.build_interval) |
| 155 | |
| 156 | rule = client.get_snapshot_access_rule(snapshot_id, rule_id) |
| 157 | |
| 158 | if rule is None: |
| 159 | return |
| 160 | if int(time.time()) - start >= client.build_timeout: |
| 161 | message = ('The snapshot access rule %(id)s failed to delete ' |
| 162 | 'within the required time (%(time)ss).' % |
| 163 | { |
| 164 | 'time': client.build_timeout, |
| 165 | 'id': rule_id, |
| 166 | }) |
| 167 | raise exceptions.TimeoutException(message) |
| 168 | |
| 169 | |
| 170 | def wait_for_message(client, resource_id): |
| 171 | """Waits until a message for a resource with given id exists""" |
| 172 | start = int(time.time()) |
| 173 | message = None |
| 174 | |
| 175 | while not message: |
| 176 | time.sleep(client.build_interval) |
lkuchlan | 86f2432 | 2021-04-27 14:23:05 +0300 | [diff] [blame] | 177 | for msg in client.list_messages()['messages']: |
lkuchlan | 540e74a | 2021-01-19 18:08:25 +0200 | [diff] [blame] | 178 | if msg['resource_id'] == resource_id: |
| 179 | return msg |
| 180 | |
| 181 | if int(time.time()) - start >= client.build_timeout: |
| 182 | message = ('No message for resource with id %s was created in' |
| 183 | ' the required time (%s s).' % |
| 184 | (resource_id, client.build_timeout)) |
| 185 | raise exceptions.TimeoutException(message) |
haixin | 1c5e68d | 2021-07-28 09:54:34 +0800 | [diff] [blame] | 186 | |
| 187 | |
| 188 | def wait_for_soft_delete(client, share_id, version=LATEST_MICROVERSION): |
| 189 | """Wait for a share soft delete to recycle bin.""" |
| 190 | share = client.get_share(share_id, version=version)['share'] |
| 191 | start = int(time.time()) |
| 192 | while not share['is_soft_deleted']: |
| 193 | time.sleep(client.build_interval) |
| 194 | share = client.get_share(share_id, version=version)['share'] |
| 195 | if share['is_soft_deleted']: |
| 196 | break |
| 197 | elif int(time.time()) - start >= client.build_timeout: |
| 198 | message = ('Share %(share_id)s failed to be soft deleted to ' |
| 199 | 'recycle bin within the required time ' |
| 200 | '%(timeout)s.' % { |
| 201 | 'share_id': share['id'], |
| 202 | 'timeout': client.build_timeout, |
| 203 | }) |
| 204 | raise exceptions.TimeoutException(message) |
| 205 | |
| 206 | |
| 207 | def wait_for_restore(client, share_id, version=LATEST_MICROVERSION): |
| 208 | """Wait for a share restore from recycle bin.""" |
| 209 | share = client.get_share(share_id, version=version)['share'] |
| 210 | start = int(time.time()) |
| 211 | while share['is_soft_deleted']: |
| 212 | time.sleep(client.build_interval) |
| 213 | share = client.get_share(share_id, version=version)['share'] |
| 214 | if not share['is_soft_deleted']: |
| 215 | break |
| 216 | elif int(time.time()) - start >= client.build_timeout: |
| 217 | message = ('Share %(share_id)s failed to restore from ' |
| 218 | 'recycle bin within the required time ' |
| 219 | '%(timeout)s.' % { |
| 220 | 'share_id': share['id'], |
| 221 | 'timeout': client.build_timeout, |
| 222 | }) |
| 223 | raise exceptions.TimeoutException(message) |
Andre | c1a3c0e | 2022-01-29 14:46:53 +0000 | [diff] [blame] | 224 | |
| 225 | |
| 226 | def wait_for_subnet_create_check(client, share_network_id, |
| 227 | neutron_net_id=None, |
| 228 | neutron_subnet_id=None, |
| 229 | availability_zone=None): |
| 230 | result = client.subnet_create_check( |
| 231 | share_network_id, neutron_net_id=neutron_net_id, |
| 232 | neutron_subnet_id=neutron_subnet_id, |
| 233 | availability_zone=availability_zone) |
| 234 | start = int(time.time()) |
| 235 | while not result['compatible']: |
| 236 | time.sleep(client.build_interval) |
| 237 | result = client.subnet_create_check( |
| 238 | share_network_id, neutron_net_id=neutron_net_id, |
| 239 | neutron_subnet_id=neutron_subnet_id, |
| 240 | availability_zone=availability_zone) |
| 241 | if result['compatible']: |
| 242 | break |
| 243 | elif int(time.time()) - start >= client.build_timeout or ( |
| 244 | result['compatible'] is False): |
| 245 | message = ('Subnet create check failed within the ' |
| 246 | 'required time %(timeout)s seconds for share network ' |
| 247 | '%(share_network)s.' % { |
| 248 | 'timeout': client.build_timeout, |
| 249 | 'share_network': share_network_id, |
| 250 | }) |
| 251 | raise exceptions.TimeoutException(message) |