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