blob: 5afe11ca05ed4b547a8e6bae2346e7a92cec3497 [file] [log] [blame]
Marc Koderer0abc93b2015-07-15 09:18:35 +02001# Copyright 2014 Mirantis 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
16import copy
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030017import re
Marc Koderer0abc93b2015-07-15 09:18:35 +020018import traceback
19
Marc Koderer0abc93b2015-07-15 09:18:35 +020020from oslo_log import log
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020021from tempest import config
Goutham Pacha Ravic678e212020-03-20 11:13:47 -070022from tempest.lib.common import cred_client
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050023from tempest.lib.common.utils import data_utils
24from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020025from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020026
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010027from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040028from manila_tempest_tests.common import constants
lkuchlan540e74a2021-01-19 18:08:25 +020029from manila_tempest_tests.common import waiters
Marc Koderer0abc93b2015-07-15 09:18:35 +020030from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020031from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020032
lkuchlan1d1461d2020-08-04 11:19:11 +030033
Marc Koderer0abc93b2015-07-15 09:18:35 +020034CONF = config.CONF
35LOG = log.getLogger(__name__)
36
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030037# Test tags related to test direction
38TAG_POSITIVE = "positive"
39TAG_NEGATIVE = "negative"
40
41# Test tags related to service involvement
Tom Barron69f96962019-07-29 17:07:03 -040042# Only requires that manila-api service running.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030043TAG_API = "api"
Tom Barron69f96962019-07-29 17:07:03 -040044# Requires all manila services running, intended to test back-end
45# (manila-share) behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030046TAG_BACKEND = "backend"
Tom Barron69f96962019-07-29 17:07:03 -040047# Requires all manila services running, intended to test API behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030048TAG_API_WITH_BACKEND = "api_with_backend"
49
50TAGS_MAPPER = {
51 "p": TAG_POSITIVE,
52 "n": TAG_NEGATIVE,
53 "a": TAG_API,
54 "b": TAG_BACKEND,
55 "ab": TAG_API_WITH_BACKEND,
56}
57TAGS_PATTERN = re.compile(
58 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
59 TAGS_MAPPER)
60
lkuchlanad511b62022-05-08 09:23:23 +030061LATEST_MICROVERSION = CONF.share.max_api_microversion
62
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030063
64def verify_test_has_appropriate_tags(self):
65 if not TAGS_PATTERN.match(self.id()):
66 msg = (
67 "Required attributes either not set or set improperly. "
68 "Two test attributes are expected:\n"
69 " - one of '%(p)s' or '%(n)s' and \n"
70 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
71 ) % TAGS_MAPPER
72 raise self.failureException(msg)
73
Marc Koderer0abc93b2015-07-15 09:18:35 +020074
75class handle_cleanup_exceptions(object):
76 """Handle exceptions raised with cleanup operations.
77
78 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
79 are raised.
80 Suppress all other exceptions only in case config opt
81 'suppress_errors_in_cleanup' in config group 'share' is True.
82 """
83
84 def __enter__(self):
85 return self
86
87 def __exit__(self, exc_type, exc_value, exc_traceback):
88 if not (isinstance(exc_value,
89 (exceptions.NotFound, exceptions.Forbidden)) or
90 CONF.share.suppress_errors_in_cleanup):
91 return False # Do not suppress error if any
92 if exc_traceback:
93 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080094 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020095 return True # Suppress error if any
96
97
Marc Koderer0abc93b2015-07-15 09:18:35 +020098class BaseSharesTest(test.BaseTestCase):
99 """Base test case class for all Manila API tests."""
100
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300101 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200102 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200103 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200104
105 # Will be cleaned up in resource_cleanup
106 class_resources = []
107
108 # Will be cleaned up in tearDown method
109 method_resources = []
110
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100111 # NOTE(andreaf) Override the client manager class to be used, so that
112 # a stable class is used, which includes plugin registered services as well
113 client_manager = clients.Clients
114
Marc Koderer0abc93b2015-07-15 09:18:35 +0200115 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000116 def skip_checks(cls):
117 super(BaseSharesTest, cls).skip_checks()
118 if not CONF.service_available.manila:
119 raise cls.skipException("Manila support is required")
lkuchlana3b6f7a2020-01-07 10:45:45 +0200120 if not any(p in CONF.share.enable_protocols for p in cls.protocols):
121 skip_msg = "%s tests are disabled" % CONF.share.enable_protocols
122 raise cls.skipException(skip_msg)
Daniel Melladoe5269142017-01-12 12:17:58 +0000123
124 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200125 def verify_nonempty(cls, *args):
126 if not all(args):
127 msg = "Missing API credentials in configuration."
128 raise cls.skipException(msg)
129
130 @classmethod
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700131 def setup_credentials(cls):
132 # This call is used to tell the credential allocator to create
133 # network resources for this test case. NOTE: it must go before the
134 # super call, to override decisions in the base classes.
135 network_resources = {}
136 if (CONF.share.multitenancy_enabled and
137 CONF.share.create_networks_when_multitenancy_enabled):
138 # We're testing a DHSS=True driver, and manila is configured with
139 # NeutronNetworkPlugin (or a derivative) that supports creating
140 # share networks with project neutron networks, so lets ask for
141 # neutron network resources to be created with test credentials
142 network_resources.update({'network': True,
143 'subnet': True,
144 'router': True})
145 cls.set_network_resources(**network_resources)
146 super(BaseSharesTest, cls).setup_credentials()
147
148 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300149 def setup_clients(cls):
150 super(BaseSharesTest, cls).setup_clients()
151 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100152 # Initialise share clients for test credentials
153 cls.shares_client = os.share_v1.SharesClient()
154 cls.shares_v2_client = os.share_v2.SharesV2Client()
155 # Initialise network clients for test credentials
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700156 cls.networks_client = None
157 cls.subnets_client = None
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100158 if CONF.service_available.neutron:
159 cls.networks_client = os.network.NetworksClient()
160 cls.subnets_client = os.network.SubnetsClient()
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300161
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700162 # If DHSS=True, create a share network and set it in the client
163 # for easy access.
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300164 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300165 if (not CONF.service_available.neutron and
166 CONF.share.create_networks_when_multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700167 raise cls.skipException(
168 "Neutron support is required when "
169 "CONF.share.create_networks_when_multitenancy_enabled "
170 "is set to True")
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300171 share_network_id = cls.provide_share_network(
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700172 cls.shares_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300173 cls.shares_client.share_network_id = share_network_id
174 cls.shares_v2_client.share_network_id = share_network_id
175
Marc Koderer0abc93b2015-07-15 09:18:35 +0200176 def setUp(self):
177 super(BaseSharesTest, self).setUp()
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200178 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300179 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200180
181 @classmethod
182 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200183 cls.clear_resources(cls.class_resources)
Sam Wan241029c2016-07-26 03:37:42 -0400184 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200185
186 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000187 def provide_and_associate_security_services(
188 cls, shares_client, share_network_id, cleanup_in_class=True):
189 """Creates a security service and associates to a share network.
190
191 This method creates security services based on the Multiopt
192 defined in tempest configuration named security_service. When this
193 configuration is not provided, the method will return None.
194 After the security service creation, this method also associates
195 the security service to a share network.
196
197 :param shares_client: shares client, which requires the provisioning
198 :param share_network_id: id of the share network to associate the
199 security service
200 :param cleanup_in_class: if the security service and the association
201 will be removed in the method teardown or class teardown
202 :returns: None -- if the security service configuration is not
203 defined
204 """
205
206 ss_configs = CONF.share.security_service
207 if not ss_configs:
208 return
209
210 for ss_config in ss_configs:
211 ss_name = "ss_autogenerated_by_tempest_%s" % (
212 ss_config.get("ss_type"))
213
214 ss_params = {
215 "name": ss_name,
216 "dns_ip": ss_config.get("ss_dns_ip"),
217 "server": ss_config.get("ss_server"),
218 "domain": ss_config.get("ss_domain"),
219 "user": ss_config.get("ss_user"),
220 "password": ss_config.get("ss_password")
221 }
222 ss_type = ss_config.get("ss_type")
223 security_service = cls.create_security_service(
224 ss_type,
225 client=shares_client,
226 cleanup_in_class=cleanup_in_class,
227 **ss_params)
228
229 cls.add_sec_service_to_share_network(
230 shares_client, share_network_id,
231 security_service["id"],
232 cleanup_in_class=cleanup_in_class)
233
234 @classmethod
235 def add_sec_service_to_share_network(
236 cls, client, share_network_id,
237 security_service_id, cleanup_in_class=True):
238 """Associates a security service to a share network.
239
240 This method associates a security service provided by
241 the security service configuration with a specific
242 share network.
243
244 :param share_network_id: the share network id to be
245 associate with a given security service
246 :param security_service_id: the security service id
247 to be associate with a given share network
248 :param cleanup_in_class: if the resources will be
249 dissociate in the method teardown or class teardown
250 """
251
252 client.add_sec_service_to_share_network(
253 share_network_id,
254 security_service_id)
255 resource = {
256 "type": "dissociate_security_service",
257 "id": security_service_id,
258 "extra_params": {
259 "share_network_id": share_network_id
260 },
261 "client": client,
262 }
263
264 if cleanup_in_class:
265 cls.class_resources.insert(0, resource)
266 else:
267 cls.method_resources.insert(0, resource)
268
269 @classmethod
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200270 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300271 ignore_multitenancy_config=False):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700272 """Get or create share network for DHSS=True drivers
Marc Koderer0abc93b2015-07-15 09:18:35 +0200273
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700274 When testing DHSS=True (multitenancy_enabled) drivers, shares must
275 be requested on share networks.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200276 :returns: str -- share network id for shares_client tenant
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700277 :returns: None -- if single-tenant driver (DHSS=False) is used
Marc Koderer0abc93b2015-07-15 09:18:35 +0200278 """
279
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300280 if (not ignore_multitenancy_config and
281 not CONF.share.multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700282 # Assumed usage of a single-tenant driver (DHSS=False)
debeltrami1753a592020-05-11 18:27:30 +0000283 return None
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700284
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700285 if shares_client.share_network_id:
286 # Share-network already exists, use it
287 return shares_client.share_network_id
debeltrami1753a592020-05-11 18:27:30 +0000288
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700289 sn_name = "autogenerated_by_tempest"
290 sn_desc = "This share-network was created by tempest"
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300291
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700292 if not CONF.share.create_networks_when_multitenancy_enabled:
293 # We need a new share network, but don't need to associate
294 # any neutron networks to it - this configuration is used
295 # when manila is configured with "StandaloneNetworkPlugin"
296 # or "NeutronSingleNetworkPlugin" where all tenants share
297 # a single backend network where shares are exported.
298 sn = cls.create_share_network(cleanup_in_class=True,
299 client=shares_client,
300 add_security_services=True,
301 name=sn_name,
302 description=sn_desc)
303 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200304
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700305 # Retrieve non-public network list owned by the tenant
306 filters = {'project_id': shares_client.tenant_id,
307 'shared': False}
308 tenant_networks = (
309 networks_client.list_networks(**filters).get('networks', [])
310 )
311 tenant_networks_with_subnet = (
312 [n for n in tenant_networks if n['subnets']]
313 )
debeltrami1753a592020-05-11 18:27:30 +0000314
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700315 if not tenant_networks_with_subnet:
316 # This can only occur if using tempest's pre-provisioned
317 # credentials and not allocating networks to them
318 raise cls.skipException(
319 "Test credentials must provide at least one "
320 "non-shared project network with a valid subnet when "
321 "CONF.share.create_networks_when_multitenancy_enabled is "
322 "set to True.")
debeltrami1753a592020-05-11 18:27:30 +0000323
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700324 net_id = tenant_networks_with_subnet[0]['id']
325 subnet_id = tenant_networks_with_subnet[0]['subnets'][0]
debeltrami1753a592020-05-11 18:27:30 +0000326
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700327 # Create suitable share-network
328 sn = cls.create_share_network(cleanup_in_class=True,
329 client=shares_client,
330 add_security_services=True,
331 name=sn_name,
332 description=sn_desc,
333 neutron_net_id=net_id,
334 neutron_subnet_id=subnet_id)
debeltrami1753a592020-05-11 18:27:30 +0000335
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700336 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200337
338 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300339 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200340 snapshot_id=None, description=None, metadata=None,
341 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400342 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400343 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300344 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200345 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400346 share_network_id = (share_network_id or
347 CONF.share.share_network_id or
348 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200349 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300350 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400351 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200352 'share_protocol': share_protocol,
353 'size': size,
354 'name': name,
355 'snapshot_id': snapshot_id,
356 'description': description,
357 'metadata': metadata,
358 'share_network_id': share_network_id,
359 'share_type_id': share_type_id,
360 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400361 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400362 if share_group_id:
363 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400364
lkuchlan86f24322021-04-27 14:23:05 +0300365 share = client.create_share(**kwargs)['share']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400366 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400367 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200368 cleanup_list = (cls.class_resources if cleanup_in_class else
369 cls.method_resources)
370 cleanup_list.insert(0, resource)
371 return share
372
373 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300374 def migrate_share(
375 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200376 force_host_assisted_migration=False, writable=False,
377 nondisruptive=False, preserve_metadata=False,
378 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300379 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400380 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300381 client.migrate_share(
382 share_id, dest_host,
383 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200384 writable=writable, preserve_metadata=preserve_metadata,
385 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300386 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300387 new_share_type_id=new_share_type_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200388 share = waiters.wait_for_migration_status(
389 client, share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200390 return share
391
392 @classmethod
393 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
394 client = client or cls.shares_v2_client
395 client.migration_complete(share_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200396 share = waiters.wait_for_migration_status(
397 client, share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300398 return share
399
400 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300401 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
402 client = client or cls.shares_v2_client
403 client.migration_cancel(share_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200404 share = waiters.wait_for_migration_status(
405 client, share_id, dest_host, 'migration_cancelled', **kwargs)
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300406 return share
407
408 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200409 def create_share(cls, *args, **kwargs):
410 """Create one share and wait for available state. Retry if allowed."""
411 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
412 return result[0]
413
414 @classmethod
415 def create_shares(cls, share_data_list):
416 """Creates several shares in parallel with retries.
417
418 Use this method when you want to create more than one share at same
419 time. Especially if config option 'share.share_creation_retry_number'
420 has value more than zero (0).
421 All shares will be expected to have 'available' status with or without
422 recreation else error will be raised.
423
424 :param share_data_list: list -- list of dictionaries with 'args' and
425 'kwargs' for '_create_share' method of this base class.
426 example of data:
427 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
428 :returns: list -- list of shares created using provided data.
429 """
430
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300431 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200432 if not isinstance(d, dict):
433 raise exceptions.TempestException(
434 "Expected 'dict', got '%s'" % type(d))
435 if "args" not in d:
436 d["args"] = []
437 if "kwargs" not in d:
438 d["kwargs"] = {}
439 if len(d) > 2:
440 raise exceptions.TempestException(
441 "Expected only 'args' and 'kwargs' keys. "
442 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300443
444 data = []
445 for d in share_data_list:
446 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400447 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300448 local_d = {
449 "args": d["args"],
450 "kwargs": copy.deepcopy(d["kwargs"]),
451 }
452 local_d["kwargs"]["client"] = client
453 local_d["share"] = cls._create_share(
454 *local_d["args"], **local_d["kwargs"])
455 local_d["cnt"] = 0
456 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400457 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300458 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200459
460 while not all(d["available"] for d in data):
461 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400462 if not d["wait_for_status"]:
463 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200464 if d["available"]:
465 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300466 client = d["kwargs"]["client"]
467 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200468 try:
lkuchlanf7fc5b62021-01-26 14:53:43 +0200469 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +0200470 client, share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200471 d["available"] = True
472 except (share_exceptions.ShareBuildErrorException,
473 exceptions.TimeoutException) as e:
474 if CONF.share.share_creation_retry_number > d["cnt"]:
475 d["cnt"] += 1
476 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300477 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200478 LOG.error(msg)
479 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300480 cg_id = d["kwargs"].get("consistency_group_id")
481 if cg_id:
482 # NOTE(vponomaryov): delete errored share
483 # immediately in case share is part of CG.
484 client.delete_share(
485 share_id,
486 params={"consistency_group_id": cg_id})
487 client.wait_for_resource_deletion(
488 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200489 d["share"] = cls._create_share(
490 *d["args"], **d["kwargs"])
491 else:
gecong197358663802016-08-25 11:08:45 +0800492 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200493
494 return [d["share"] for d in data]
495
496 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400497 def create_share_group(cls, client=None, cleanup_in_class=True,
498 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400499 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400500 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400501 kwargs['share_network_id'] = (share_network_id or
502 client.share_network_id or None)
lkuchlan86f24322021-04-27 14:23:05 +0300503 share_group = client.create_share_group(**kwargs)['share_group']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400504 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400505 "type": "share_group",
506 "id": share_group["id"],
507 "client": client,
508 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400509 if cleanup_in_class:
510 cls.class_resources.insert(0, resource)
511 else:
512 cls.method_resources.insert(0, resource)
513
Andrew Kerrb8436922016-06-01 15:32:43 -0400514 if kwargs.get('source_share_group_snapshot_id'):
515 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400516 detailed=True,
lkuchlan86f24322021-04-27 14:23:05 +0300517 params={'share_group_id': share_group['id']})['shares']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400518
Andrew Kerrb8436922016-06-01 15:32:43 -0400519 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400520 resource = {"type": "share",
521 "id": share["id"],
522 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400523 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400524 if cleanup_in_class:
525 cls.class_resources.insert(0, resource)
526 else:
527 cls.method_resources.insert(0, resource)
528
lkuchlanf7fc5b62021-01-26 14:53:43 +0200529 waiters.wait_for_resource_status(
530 client, share_group['id'], 'available',
531 resource_name='share_group')
Andrew Kerrb8436922016-06-01 15:32:43 -0400532 return share_group
533
534 @classmethod
535 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
536 group_specs=None, client=None,
537 cleanup_in_class=True, **kwargs):
538 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300539 if (group_specs is None and
540 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300541 group_specs = {
542 'consistent_snapshot_support': (
543 CONF.share.capability_sg_consistent_snapshot_support),
544 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400545 share_group_type = client.create_share_group_type(
546 name=name,
547 share_types=share_types,
548 is_public=is_public,
549 group_specs=group_specs,
lkuchlan86f24322021-04-27 14:23:05 +0300550 **kwargs)['share_group_type']
Andrew Kerrb8436922016-06-01 15:32:43 -0400551 resource = {
552 "type": "share_group_type",
553 "id": share_group_type["id"],
554 "client": client,
555 }
556 if cleanup_in_class:
557 cls.class_resources.insert(0, resource)
558 else:
559 cls.method_resources.insert(0, resource)
560 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400561
562 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200563 def create_snapshot_wait_for_active(cls, share_id, name=None,
564 description=None, force=False,
Ashley Rodriguezc45cb4b2022-02-04 21:29:33 +0000565 metadata=None, client=None,
566 cleanup_in_class=True):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200567 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400568 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200569 if description is None:
570 description = "Tempest's snapshot"
lkuchlan86f24322021-04-27 14:23:05 +0300571 snapshot = client.create_snapshot(
Ashley Rodriguezc45cb4b2022-02-04 21:29:33 +0000572 share_id, name, description, force, metadata)['snapshot']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200573 resource = {
574 "type": "snapshot",
575 "id": snapshot["id"],
576 "client": client,
577 }
578 if cleanup_in_class:
579 cls.class_resources.insert(0, resource)
580 else:
581 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200582 waiters.wait_for_resource_status(client, snapshot["id"], "available",
583 resource_name='snapshot')
Marc Koderer0abc93b2015-07-15 09:18:35 +0200584 return snapshot
585
586 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400587 def create_share_group_snapshot_wait_for_active(
588 cls, share_group_id, name=None, description=None, client=None,
589 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400590 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400591 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400592 description = "Tempest's share group snapshot"
593 sg_snapshot = client.create_share_group_snapshot(
lkuchlan86f24322021-04-27 14:23:05 +0300594 share_group_id, name=name, description=description,
595 **kwargs)['share_group_snapshot']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400596 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400597 "type": "share_group_snapshot",
598 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400599 "client": client,
600 }
601 if cleanup_in_class:
602 cls.class_resources.insert(0, resource)
603 else:
604 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200605 waiters.wait_for_resource_status(
606 client, sg_snapshot["id"], "available",
607 resource_name="share_group_snapshot")
Andrew Kerrb8436922016-06-01 15:32:43 -0400608 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400609
610 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800611 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400612 """List the availability zones for "manila-share" services
613
614 that are currently in "up" state.
615 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800616 client = client or cls.admin_shares_v2_client
617 backends = (
618 '|'.join(['^%s$' % backend for backend in backends])
619 if backends else '.*'
620 )
lkuchlan86f24322021-04-27 14:23:05 +0300621 cls.services = client.list_services()['services']
Yogeshbdb88102015-09-29 23:41:02 -0400622 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800623 service['binary'] == 'manila-share' and
624 service['state'] == 'up' and
625 re.search(backends, service['host'])]
Douglas Viroel161f1802020-04-25 17:18:14 -0300626 return list(set(zones))
Yogeshbdb88102015-09-29 23:41:02 -0400627
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800628 @classmethod
629 def get_pools_matching_share_type(cls, share_type, client=None):
630 client = client or cls.admin_shares_v2_client
631 if utils.is_microversion_supported('2.23'):
632 return client.list_pools(
andrebeltrami3b4d4852020-02-04 19:11:54 +0000633 detail=True,
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800634 search_opts={'share_type': share_type['id']})['pools']
635
636 pools = client.list_pools(detail=True)['pools']
637 share_type = client.get_share_type(share_type['id'])['share_type']
638 extra_specs = {}
639 for k, v in share_type['extra_specs'].items():
640 extra_specs[k] = (
haixin48895812020-09-30 13:50:37 +0800641 True if str(v).lower() == 'true'
642 else False if str(v).lower() == 'false' else v
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800643 )
644 return [
645 pool for pool in pools if all(y in pool['capabilities'].items()
646 for y in extra_specs.items())
647 ]
648
649 @classmethod
650 def get_availability_zones_matching_share_type(cls, share_type,
651 client=None):
652
653 client = client or cls.admin_shares_v2_client
654 pools_matching_share_type = cls.get_pools_matching_share_type(
655 share_type, client=client)
656 backends_matching_share_type = set(
657 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
658 )
659 azs = cls.get_availability_zones(backends=backends_matching_share_type)
660 return azs
661
Kiran Pawar59745062021-11-01 12:37:49 +0000662 def get_pools_for_replication_domain(self, share=None):
Yogesh1f931ff2015-09-29 23:41:02 -0400663 # Get the list of pools for the replication domain
664 pools = self.admin_client.list_pools(detail=True)['pools']
Kiran Pawar59745062021-11-01 12:37:49 +0000665 if share:
666 instance_host = self.admin_client.get_share(
667 share['id'])['share']['host']
668 else:
669 instance_host = self.admin_client.get_share(
670 self.shares[0]['id'])['share']['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400671 host_pool = [p for p in pools if p['name'] == instance_host][0]
672 rep_domain = host_pool['capabilities']['replication_domain']
673 pools_in_rep_domain = [p for p in pools if p['capabilities'][
674 'replication_domain'] == rep_domain]
675 return rep_domain, pools_in_rep_domain
676
Yogeshbdb88102015-09-29 23:41:02 -0400677 @classmethod
debeltrami0d523bb2020-08-20 12:48:49 +0000678 def create_share_replica(cls, share_id, availability_zone=None,
kpdev2521fbf2021-08-15 21:54:41 +0200679 scheduler_hints=None,
680 share_network_id=None,
debeltrami0d523bb2020-08-20 12:48:49 +0000681 client=None, cleanup_in_class=False,
682 cleanup=True,
kpdev2521fbf2021-08-15 21:54:41 +0200683 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400684 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300685 replica = client.create_share_replica(
lkuchlan86f24322021-04-27 14:23:05 +0300686 share_id, availability_zone=availability_zone,
kpdev2521fbf2021-08-15 21:54:41 +0200687 scheduler_hints=scheduler_hints,
688 share_network_id=share_network_id,
689 version=version)['share_replica']
Yogeshbdb88102015-09-29 23:41:02 -0400690 resource = {
691 "type": "share_replica",
692 "id": replica["id"],
693 "client": client,
694 "share_id": share_id,
695 }
696 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
697 if cleanup:
698 if cleanup_in_class:
699 cls.class_resources.insert(0, resource)
700 else:
701 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200702 waiters.wait_for_resource_status(
703 client, replica["id"], constants.STATUS_AVAILABLE,
704 resource_name='share_replica')
Yogeshbdb88102015-09-29 23:41:02 -0400705 return replica
706
707 @classmethod
Kiran Pawaredd82c72022-05-07 14:48:36 +0000708 def create_backup_wait_for_active(cls, share_id, client=None,
709 cleanup_in_class=False, cleanup=True,
710 version=CONF.share.max_api_microversion):
711 client = client or cls.shares_v2_client
712 backup_name = data_utils.rand_name('Backup')
713 backup_options = CONF.share.driver_assisted_backup_test_driver_options
714 backup = client.create_share_backup(
715 share_id,
716 name=backup_name,
717 backup_options=backup_options)['share_backup']
718 resource = {
719 "type": "share_backup",
720 "id": backup["id"],
721 "client": client,
722 }
723 if cleanup:
724 if cleanup_in_class:
725 cls.class_resources.insert(0, resource)
726 else:
727 cls.method_resources.insert(0, resource)
728 waiters.wait_for_resource_status(client, backup["id"], "available",
729 resource_name='share_backup')
730 return client.get_share_backup(backup['id'])['share_backup']
731
732 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000733 def delete_share_replica(cls, replica_id, client=None,
734 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400735 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400736 try:
silvacarlossd354d672020-08-23 18:49:52 +0000737 client.delete_share_replica(replica_id, version=version)
Yogesh1f931ff2015-09-29 23:41:02 -0400738 client.wait_for_resource_deletion(replica_id=replica_id)
739 except exceptions.NotFound:
740 pass
Yogeshbdb88102015-09-29 23:41:02 -0400741
742 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000743 def promote_share_replica(cls, replica_id, client=None,
744 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400745 client = client or cls.shares_v2_client
lkuchlan86f24322021-04-27 14:23:05 +0300746 replica = client.promote_share_replica(
747 replica_id, version=version)['share_replica']
lkuchlanf7fc5b62021-01-26 14:53:43 +0200748 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +0200749 client, replica["id"], constants.REPLICATION_STATE_ACTIVE,
lkuchlanf7fc5b62021-01-26 14:53:43 +0200750 resource_name='share_replica', status_attr="replica_state")
Yogeshbdb88102015-09-29 23:41:02 -0400751 return replica
752
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700753 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200754 def create_share_network(cls, client=None,
debeltrami1753a592020-05-11 18:27:30 +0000755 cleanup_in_class=False,
756 add_security_services=True, **kwargs):
757
Marc Koderer0abc93b2015-07-15 09:18:35 +0200758 if client is None:
759 client = cls.shares_client
lkuchlan86f24322021-04-27 14:23:05 +0300760 share_network = client.create_share_network(**kwargs)['share_network']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200761 resource = {
762 "type": "share_network",
763 "id": share_network["id"],
764 "client": client,
765 }
debeltrami1753a592020-05-11 18:27:30 +0000766
Marc Koderer0abc93b2015-07-15 09:18:35 +0200767 if cleanup_in_class:
768 cls.class_resources.insert(0, resource)
769 else:
770 cls.method_resources.insert(0, resource)
debeltrami1753a592020-05-11 18:27:30 +0000771
772 if add_security_services:
773 cls.provide_and_associate_security_services(
774 client, share_network["id"], cleanup_in_class=cleanup_in_class)
775
Marc Koderer0abc93b2015-07-15 09:18:35 +0200776 return share_network
777
778 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000779 def create_share_network_subnet(cls,
780 client=None,
781 cleanup_in_class=False,
782 **kwargs):
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300783 if client is None:
784 client = cls.shares_v2_client
lkuchlan86f24322021-04-27 14:23:05 +0300785 share_network_subnet = client.create_subnet(
786 **kwargs)['share_network_subnet']
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300787 resource = {
Goutham Pacha Raviad43e932023-01-16 13:06:46 -0800788 "type": "share_network_subnet",
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300789 "id": share_network_subnet["id"],
790 "extra_params": {
791 "share_network_id": share_network_subnet["share_network_id"]
792 },
793 "client": client,
794 }
795 if cleanup_in_class:
796 cls.class_resources.insert(0, resource)
797 else:
798 cls.method_resources.insert(0, resource)
799 return share_network_subnet
800
801 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200802 def create_security_service(cls, ss_type="ldap", client=None,
803 cleanup_in_class=False, **kwargs):
804 if client is None:
805 client = cls.shares_client
lkuchlan86f24322021-04-27 14:23:05 +0300806 security_service = client.create_security_service(
807 ss_type, **kwargs)['security_service']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200808 resource = {
809 "type": "security_service",
810 "id": security_service["id"],
811 "client": client,
812 }
813 if cleanup_in_class:
814 cls.class_resources.insert(0, resource)
815 else:
816 cls.method_resources.insert(0, resource)
817 return security_service
818
819 @classmethod
Goutham Pacha Ravi646fcd22023-07-14 12:47:47 -0700820 def create_resource_lock(cls, resource_id, resource_type='share',
821 resource_action='delete', lock_reason=None,
822 client=None, version=LATEST_MICROVERSION,
823 cleanup_in_class=True):
824 lock_reason = lock_reason or "locked by tempest tests"
825 client = client or cls.shares_v2_client
826
827 lock = client.create_resource_lock(resource_id,
828 resource_type,
829 resource_action=resource_action,
830 lock_reason=lock_reason,
831 version=version)['resource_lock']
832 resource = {
833 "type": "resource_lock",
834 "id": lock["id"],
835 "client": client,
836 }
837 if cleanup_in_class:
838 cls.class_resources.insert(0, resource)
839 else:
840 cls.method_resources.insert(0, resource)
841 return lock
842
843 @classmethod
haixin0d1d29f2019-08-02 16:50:45 +0800844 def update_share_type(cls, share_type_id, name=None,
845 is_public=None, description=None,
846 client=None):
847 if client is None:
848 client = cls.shares_v2_client
849 share_type = client.update_share_type(share_type_id, name,
850 is_public, description)
851 return share_type
852
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700853 @classmethod
854 def update_quotas(cls, project_id, user_id=None, cleanup=True,
855 client=None, **kwargs):
856 client = client or cls.shares_v2_client
857 updated_quotas = client.update_quotas(project_id,
858 user_id=user_id,
lkuchlan86f24322021-04-27 14:23:05 +0300859 **kwargs)['quota_set']
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700860 resource = {
861 "type": "quotas",
862 "id": project_id,
863 "client": client,
864 "user_id": user_id,
865 }
866 if cleanup:
867 cls.method_resources.insert(0, resource)
868 return updated_quotas
869
Marc Koderer0abc93b2015-07-15 09:18:35 +0200870 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400871 def clear_share_replicas(cls, share_id, client=None):
872 client = client or cls.shares_v2_client
873 share_replicas = client.list_share_replicas(
lkuchlan86f24322021-04-27 14:23:05 +0300874 share_id=share_id)['share_replicas']
Yogesh1f931ff2015-09-29 23:41:02 -0400875
876 for replica in share_replicas:
877 try:
878 cls.delete_share_replica(replica['id'])
879 except exceptions.BadRequest:
880 # Ignore the exception due to deletion of last active replica
881 pass
882
883 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200884 def clear_resources(cls, resources=None):
885 """Deletes resources, that were created in test suites.
886
887 This method tries to remove resources from resource list,
888 if it is not found, assumed it was deleted in test itself.
889 It is expected, that all resources were added as LIFO
890 due to restriction of deletion resources, that is in the chain.
891
892 :param resources: dict with keys 'type','id','client' and 'deleted'
893 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200894 if resources is None:
895 resources = cls.method_resources
896 for res in resources:
897 if "deleted" not in res.keys():
898 res["deleted"] = False
899 if "client" not in res.keys():
900 res["client"] = cls.shares_client
Takashi Kajinami6304dcd2024-11-16 15:45:58 +0900901 if not res["deleted"]:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200902 res_id = res['id']
903 client = res["client"]
904 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200905 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400906 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400907 share_group_id = res.get('share_group_id')
908 if share_group_id:
909 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400910 client.delete_share(res_id, params=params)
911 else:
912 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200913 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200914 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200915 client.delete_snapshot(res_id)
916 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200917 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400918 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400919 client.delete_share_network(res_id)
920 client.wait_for_resource_deletion(sn_id=res_id)
debeltrami1753a592020-05-11 18:27:30 +0000921 elif res["type"] == "dissociate_security_service":
922 sn_id = res["extra_params"]["share_network_id"]
923 client.remove_sec_service_from_share_network(
924 sn_id=sn_id, ss_id=res_id
925 )
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200926 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200927 client.delete_security_service(res_id)
928 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200929 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200930 client.delete_share_type(res_id)
931 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200932 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400933 client.delete_share_group(res_id)
934 client.wait_for_resource_deletion(
935 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200936 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400937 client.delete_share_group_type(res_id)
938 client.wait_for_resource_deletion(
939 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200940 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400941 client.delete_share_group_snapshot(res_id)
942 client.wait_for_resource_deletion(
943 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200944 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400945 client.delete_share_replica(res_id)
946 client.wait_for_resource_deletion(replica_id=res_id)
Kiran Pawaredd82c72022-05-07 14:48:36 +0000947 elif res["type"] == "share_backup":
948 client.delete_share_backup(res_id)
949 client.wait_for_resource_deletion(backup_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200950 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300951 sn_id = res["extra_params"]["share_network_id"]
952 client.delete_subnet(sn_id, res_id)
953 client.wait_for_resource_deletion(
954 share_network_subnet_id=res_id,
955 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700956 elif res["type"] == "quotas":
957 user_id = res.get('user_id')
958 client.reset_quotas(res_id, user_id=user_id)
Goutham Pacha Ravi646fcd22023-07-14 12:47:47 -0700959 elif res["type"] == "resource_lock":
960 client.delete_resource_lock(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200961 else:
huayue97bacbf2016-01-04 09:57:39 +0800962 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800963 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200964 res["deleted"] = True
965
Marc Koderer0abc93b2015-07-15 09:18:35 +0200966 # Useful assertions
967 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
968 """Assert two dicts are equivalent.
969
970 This is a 'deep' match in the sense that it handles nested
971 dictionaries appropriately.
972
973 NOTE:
974
975 If you don't care (or don't know) a given value, you can specify
976 the string DONTCARE as the value. This will cause that dict-item
977 to be skipped.
978
979 """
980 def raise_assertion(msg):
981 d1str = str(d1)
982 d2str = str(d2)
983 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
984 'd2: %(d2str)s' %
985 {"msg": msg, "d1str": d1str, "d2str": d2str})
986 raise AssertionError(base_msg)
987
988 d1keys = set(d1.keys())
989 d2keys = set(d2.keys())
990 if d1keys != d2keys:
991 d1only = d1keys - d2keys
992 d2only = d2keys - d1keys
993 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
994 'Keys in d2 and not d1: %(d2only)s' %
995 {"d1only": d1only, "d2only": d2only})
996
997 for key in d1keys:
998 d1value = d1[key]
999 d2value = d2[key]
1000 try:
1001 error = abs(float(d1value) - float(d2value))
1002 within_tolerance = error <= tolerance
1003 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001004 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001005 # ValueError if arg is a str, TypeError if it's something else
1006 # (like None)
1007 within_tolerance = False
1008
1009 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1010 self.assertDictMatch(d1value, d2value)
1011 elif 'DONTCARE' in (d1value, d2value):
1012 continue
1013 elif approx_equal and within_tolerance:
1014 continue
1015 elif d1value != d2value:
1016 raise_assertion("d1['%(key)s']=%(d1value)s != "
1017 "d2['%(key)s']=%(d2value)s" %
1018 {
1019 "key": key,
1020 "d1value": d1value,
1021 "d2value": d2value
1022 })
1023
Alex Meadeba8a1602016-05-06 09:33:09 -04001024 def create_user_message(self):
1025 """Trigger a 'no valid host' situation to generate a message."""
1026 extra_specs = {
1027 'vendor_name': 'foobar',
1028 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1029 }
1030 share_type_name = data_utils.rand_name("share-type")
1031
1032 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001033 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001034 name=share_type_name,
1035 extra_specs=extra_specs)['share_type']
1036
1037 params = {'share_type_id': bogus_type['id'],
1038 'share_network_id': self.shares_v2_client.share_network_id}
lkuchlan86f24322021-04-27 14:23:05 +03001039 share = self.shares_v2_client.create_share(**params)['share']
Alex Meadeba8a1602016-05-06 09:33:09 -04001040 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
lkuchlanf7fc5b62021-01-26 14:53:43 +02001041 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001042 self.shares_v2_client, share['id'], "error")
1043 return waiters.wait_for_message(self.shares_v2_client, share['id'])
Alex Meadeba8a1602016-05-06 09:33:09 -04001044
lkuchlan5af7cb42020-07-14 18:05:09 +03001045 def allow_access(self, share_id, client=None, access_type=None,
lkuchlanad511b62022-05-08 09:23:23 +03001046 access_level='rw', access_to=None, metadata=None,
1047 version=LATEST_MICROVERSION, status='active',
silvacarlossd10819b2023-07-24 13:53:32 -03001048 raise_rule_in_error_state=True, lock_visibility=False,
1049 lock_deletion=False, cleanup=True):
lkuchlan5af7cb42020-07-14 18:05:09 +03001050
1051 client = client or self.shares_v2_client
lkuchlan4a803162022-11-16 11:34:33 +02001052 a_type, a_to = utils.get_access_rule_data_from_config(
1053 client.share_protocol)
lkuchlan5af7cb42020-07-14 18:05:09 +03001054 access_type = access_type or a_type
1055 access_to = access_to or a_to
1056
lkuchlanad511b62022-05-08 09:23:23 +03001057 kwargs = {
1058 'access_type': access_type,
1059 'access_to': access_to,
1060 'access_level': access_level
1061 }
silvacarlossd10819b2023-07-24 13:53:32 -03001062 delete_kwargs = (
1063 {'unrestrict': True} if lock_deletion else {}
1064 )
lkuchlanad511b62022-05-08 09:23:23 +03001065 if client is self.shares_v2_client:
1066 kwargs.update({'metadata': metadata, 'version': version})
silvacarlossd10819b2023-07-24 13:53:32 -03001067 if lock_visibility:
1068 kwargs.update({'lock_visibility': True})
1069 if lock_deletion:
1070 kwargs.update({'lock_deletion': True})
lkuchlanad511b62022-05-08 09:23:23 +03001071
1072 rule = client.create_access_rule(share_id, **kwargs)['access']
lkuchlanf7fc5b62021-01-26 14:53:43 +02001073 waiters.wait_for_resource_status(
1074 client, share_id, status, resource_name='access_rule',
lkuchlanad511b62022-05-08 09:23:23 +03001075 rule_id=rule['id'], version=version,
lkuchlanf7fc5b62021-01-26 14:53:43 +02001076 raise_rule_in_error_state=raise_rule_in_error_state)
lkuchlan5af7cb42020-07-14 18:05:09 +03001077 if cleanup:
lkuchlanad511b62022-05-08 09:23:23 +03001078 self.addCleanup(
1079 client.wait_for_resource_deletion, rule_id=rule['id'],
1080 share_id=share_id, version=version)
silvacarlossd10819b2023-07-24 13:53:32 -03001081 self.addCleanup(
1082 client.delete_access_rule, share_id, rule['id'],
1083 **delete_kwargs)
lkuchlan5af7cb42020-07-14 18:05:09 +03001084 return rule
1085
Marc Koderer0abc93b2015-07-15 09:18:35 +02001086
Marc Koderer0abc93b2015-07-15 09:18:35 +02001087class BaseSharesAdminTest(BaseSharesTest):
1088 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001089 credentials = ('admin', )
1090
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001091 @classmethod
1092 def setup_clients(cls):
1093 super(BaseSharesAdminTest, cls).setup_clients()
1094 # Initialise share clients
lkuchlan3af822b2021-06-06 10:35:30 +03001095 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001096 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1097
lkuchlan3af822b2021-06-06 10:35:30 +03001098 @staticmethod
1099 def add_extra_specs_to_dict(extra_specs=None):
1100 """Add any required extra-specs to share type dictionary"""
haixin48895812020-09-30 13:50:37 +08001101 dhss = str(CONF.share.multitenancy_enabled)
lkuchlan3af822b2021-06-06 10:35:30 +03001102 extra_specs_dict = {"driver_handles_share_servers": dhss}
1103 if extra_specs:
1104 extra_specs_dict.update(extra_specs)
Felipe Rodrigues6e566772021-08-23 14:57:50 -03001105 if CONF.share.capability_thin_provisioned:
1106 extra_specs_dict['thin_provisioning'] = 'True'
lkuchlan3af822b2021-06-06 10:35:30 +03001107 return extra_specs_dict
1108
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001109 @classmethod
lkuchlan3af822b2021-06-06 10:35:30 +03001110 def create_share_type(cls, name=None, is_public=True, client=None,
1111 cleanup_in_class=True, extra_specs=None, **kwargs):
1112 name = name or data_utils.rand_name(
1113 cls.__class__.__name__ + 'share-type')
1114 client = client or cls.admin_shares_v2_client
1115 extra_specs = cls.add_extra_specs_to_dict(extra_specs=extra_specs)
1116 share_type = client.create_share_type(name, is_public,
1117 extra_specs=extra_specs,
1118 **kwargs)['share_type']
1119 resource = {
1120 "type": "share_type",
1121 "id": share_type["id"],
1122 "client": client,
1123 }
1124 if cleanup_in_class:
1125 cls.class_resources.insert(0, resource)
1126 else:
1127 cls.method_resources.insert(0, resource)
1128 return share_type
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001129
1130 @classmethod
1131 def _create_share_group_type(cls):
1132 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1133 return cls.create_share_group_type(
1134 name=share_group_type_name, share_types=[cls.share_type_id],
1135 client=cls.admin_shares_v2_client)
1136
Lucio Seki37056942019-01-24 15:40:20 -02001137 def _create_share_for_manage(self):
1138 creation_data = {
lkuchlan3af822b2021-06-06 10:35:30 +03001139 'share_type_id': self.st['id'],
Lucio Seki37056942019-01-24 15:40:20 -02001140 'share_protocol': self.protocol,
1141 }
1142
1143 share = self.create_share(**creation_data)
lkuchlan86f24322021-04-27 14:23:05 +03001144 share = self.shares_v2_client.get_share(share['id'])['share']
Lucio Seki37056942019-01-24 15:40:20 -02001145
1146 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
lkuchlan86f24322021-04-27 14:23:05 +03001147 el = self.shares_v2_client.list_share_export_locations(
1148 share["id"])['export_locations']
Lucio Seki37056942019-01-24 15:40:20 -02001149 share["export_locations"] = el
1150
1151 return share
1152
1153 def _unmanage_share_and_wait(self, share):
1154 self.shares_v2_client.unmanage_share(share['id'])
1155 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1156
1157 def _reset_state_and_delete_share(self, share):
1158 self.shares_v2_client.reset_state(share['id'])
1159 self._delete_share_and_wait(share)
1160
1161 def _delete_snapshot_and_wait(self, snap):
1162 self.shares_v2_client.delete_snapshot(snap['id'])
1163 self.shares_v2_client.wait_for_resource_deletion(
1164 snapshot_id=snap['id']
1165 )
1166 self.assertRaises(exceptions.NotFound,
1167 self.shares_v2_client.get_snapshot,
1168 snap['id'])
1169
1170 def _delete_share_and_wait(self, share):
1171 self.shares_v2_client.delete_share(share['id'])
1172 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1173 self.assertRaises(exceptions.NotFound,
1174 self.shares_v2_client.get_share,
1175 share['id'])
1176
1177 def _manage_share(self, share, name, description, share_server_id):
1178 managed_share = self.shares_v2_client.manage_share(
1179 service_host=share['host'],
1180 export_path=share['export_locations'][0],
1181 protocol=share['share_proto'],
lkuchlan3af822b2021-06-06 10:35:30 +03001182 share_type_id=self.share_type['id'],
Lucio Seki37056942019-01-24 15:40:20 -02001183 name=name,
1184 description=description,
1185 share_server_id=share_server_id
lkuchlan86f24322021-04-27 14:23:05 +03001186 )['share']
lkuchlanf7fc5b62021-01-26 14:53:43 +02001187 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001188 self.shares_v2_client, managed_share['id'],
1189 constants.STATUS_AVAILABLE
Lucio Seki37056942019-01-24 15:40:20 -02001190 )
1191
1192 return managed_share
1193
1194 def _unmanage_share_server_and_wait(self, server):
1195 self.shares_v2_client.unmanage_share_server(server['id'])
1196 self.shares_v2_client.wait_for_resource_deletion(
1197 server_id=server['id']
1198 )
1199
1200 def _manage_share_server(self, share_server, fields=None):
1201 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001202 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001203 managed_share_server = self.shares_v2_client.manage_share_server(
1204 params.get('host', share_server['host']),
1205 params.get('share_network_id', share_server['share_network_id']),
1206 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001207 share_network_subnet_id=subnet_id,
lkuchlan86f24322021-04-27 14:23:05 +03001208 )['share_server']
lkuchlanf7fc5b62021-01-26 14:53:43 +02001209 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001210 self.shares_v2_client, managed_share_server['id'],
lkuchlanf7fc5b62021-01-26 14:53:43 +02001211 constants.SERVER_STATE_ACTIVE, resource_name='share_server'
Lucio Seki37056942019-01-24 15:40:20 -02001212 )
1213
1214 return managed_share_server
1215
1216 def _delete_share_server_and_wait(self, share_server_id):
1217 self.shares_v2_client.delete_share_server(
1218 share_server_id
1219 )
1220 self.shares_v2_client.wait_for_resource_deletion(
1221 server_id=share_server_id)
1222
lkuchlan3af822b2021-06-06 10:35:30 +03001223 def create_user_message(self):
1224 """Trigger a 'no valid host' situation to generate a message."""
1225 extra_specs = {
1226 'vendor_name': 'foobar',
1227 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1228 }
1229 share_type_name = data_utils.rand_name("share-type")
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001230
lkuchlan3af822b2021-06-06 10:35:30 +03001231 bogus_type = self.create_share_type(
1232 client=self.admin_shares_v2_client,
1233 name=share_type_name,
1234 extra_specs=extra_specs)
1235
1236 params = {'share_type_id': bogus_type['id'],
1237 'share_network_id': self.shares_v2_client.share_network_id}
lkuchlan86f24322021-04-27 14:23:05 +03001238 share = self.shares_v2_client.create_share(**params)['share']
lkuchlan3af822b2021-06-06 10:35:30 +03001239 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1240 waiters.wait_for_resource_status(
1241 self.shares_v2_client, share['id'], "error")
1242 return waiters.wait_for_message(self.shares_v2_client, share['id'])
1243
1244
1245class BaseSharesMixedTest(BaseSharesAdminTest):
1246 """Base test case class for all Shares API tests with all user roles.
1247
1248 Tests deriving from this class can use the primary project's clients
1249 (self.shares_client, self.shares_v2_client) and the alt project user's
1250 clients (self.alt_shares_client, self.alt_shares_v2_client) to perform
1251 API calls and validations. Although admin clients are available for use,
1252 their use should be limited to performing bootstrapping (e.g., creating
1253 a share type, or resetting state of a resource, etc.). No API validation
1254 must be performed against admin APIs. Use BaseAdminTest as a base class
1255 for such tests.
1256 """
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001257 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001258
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001259 # Will be cleaned up in resource_cleanup if the class
1260 class_project_users_created = []
1261
1262 @classmethod
1263 def resource_cleanup(cls):
1264 cls.clear_project_users(cls.class_project_users_created)
1265 super(BaseSharesMixedTest, cls).resource_cleanup()
1266
1267 @classmethod
1268 def clear_project_users(cls, users=None):
1269 users = users or cls.class_project_users_created
1270 for user in users:
1271 with handle_cleanup_exceptions():
1272 cls.os_admin.creds_client.delete_user(user['id'])
1273
Marc Koderer0abc93b2015-07-15 09:18:35 +02001274 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001275 def setup_clients(cls):
1276 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001277 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1278 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1279 # Initialise network clients
1280 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1281 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001282 # Initialise identity clients
1283 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1284 identity_clients = getattr(
1285 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1286 cls.os_admin.identity_client = identity_clients.IdentityClient()
1287 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1288 cls.os_admin.users_client = identity_clients.UsersClient()
1289 cls.os_admin.roles_client = identity_clients.RolesClient()
1290 cls.os_admin.domains_client = (
1291 cls.os_admin.identity_v3.DomainsClient() if
1292 CONF.identity.auth_version == 'v3' else None)
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001293 cls.admin_project_member_client = cls.create_user_and_get_client(
1294 project=cls.admin_project, add_member_role=True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001295
1296 if CONF.share.multitenancy_enabled:
1297 admin_share_network_id = cls.provide_share_network(
1298 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1299 cls.admin_shares_client.share_network_id = admin_share_network_id
1300 cls.admin_shares_v2_client.share_network_id = (
1301 admin_share_network_id)
1302
1303 alt_share_network_id = cls.provide_share_network(
1304 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1305 cls.alt_shares_client.share_network_id = alt_share_network_id
1306 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001307
1308 @classmethod
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001309 def create_user_and_get_client(cls, project=None, add_member_role=True):
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001310 """Create a user in specified project & set share clients for user
1311
1312 The user will have all roles specified in tempest.conf
1313 :param: project: a dictionary with project ID and name, if not
1314 specified, the value will be cls.admin_project
1315 """
1316 project_domain_name = (
1317 cls.os_admin.identity_client.auth_provider.credentials.get(
1318 'project_domain_name', 'Default'))
1319 cls.os_admin.creds_client = cred_client.get_creds_client(
1320 cls.os_admin.identity_client, cls.os_admin.projects_client,
1321 cls.os_admin.users_client, cls.os_admin.roles_client,
1322 cls.os_admin.domains_client, project_domain_name)
1323
1324 # User info
1325 project = project or cls.admin_project
1326 username = data_utils.rand_name('manila_%s' % project['id'])
1327 password = data_utils.rand_password()
1328 email = '%s@example.org' % username
1329
1330 user = cls.os_admin.creds_client.create_user(
1331 username, password, project, email)
1332 cls.class_project_users_created.append(user)
1333
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001334 tempest_roles_to_assign = CONF.auth.tempest_roles or []
1335 if "member" not in tempest_roles_to_assign and add_member_role:
1336 tempest_roles_to_assign.append("member")
1337
1338 for role in tempest_roles_to_assign:
1339 cls.os_admin.creds_client.assign_user_role(user, project, role)
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001340
1341 user_creds = cls.os_admin.creds_client.get_credentials(
1342 user, project, password)
1343 os = clients.Clients(user_creds)
1344 os.shares_v1_client = os.share_v1.SharesClient()
1345 os.shares_v2_client = os.share_v2.SharesV2Client()
1346 return os