blob: b834d999db3ab9cb0d9158e29bbef47c999592e7 [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])
Goutham Pacha Ravi64060262025-09-05 09:40:45 +0100152 # Initialise share client for test credentials
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100153 cls.shares_v2_client = os.share_v2.SharesV2Client()
154 # Initialise network clients for test credentials
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700155 cls.networks_client = None
156 cls.subnets_client = None
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100157 if CONF.service_available.neutron:
158 cls.networks_client = os.network.NetworksClient()
159 cls.subnets_client = os.network.SubnetsClient()
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300160
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700161 # If DHSS=True, create a share network and set it in the client
162 # for easy access.
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300163 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300164 if (not CONF.service_available.neutron and
165 CONF.share.create_networks_when_multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700166 raise cls.skipException(
167 "Neutron support is required when "
168 "CONF.share.create_networks_when_multitenancy_enabled "
169 "is set to True")
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300170 share_network_id = cls.provide_share_network(
Yaguang Tangcc4b8ac2024-12-16 09:55:40 +0800171 cls.shares_v2_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300172 cls.shares_v2_client.share_network_id = share_network_id
173
Marc Koderer0abc93b2015-07-15 09:18:35 +0200174 def setUp(self):
175 super(BaseSharesTest, self).setUp()
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200176 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300177 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200178
179 @classmethod
180 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200181 cls.clear_resources(cls.class_resources)
Sam Wan241029c2016-07-26 03:37:42 -0400182 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200183
184 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000185 def provide_and_associate_security_services(
Stephen Finucane878716d2025-09-05 08:18:03 +0100186 cls, shares_client, share_network_id, cleanup_in_class=True
187 ):
debeltrami1753a592020-05-11 18:27:30 +0000188 """Creates a security service and associates to a share network.
189
190 This method creates security services based on the Multiopt
191 defined in tempest configuration named security_service. When this
192 configuration is not provided, the method will return None.
193 After the security service creation, this method also associates
194 the security service to a share network.
195
196 :param shares_client: shares client, which requires the provisioning
197 :param share_network_id: id of the share network to associate the
198 security service
199 :param cleanup_in_class: if the security service and the association
200 will be removed in the method teardown or class teardown
201 :returns: None -- if the security service configuration is not
202 defined
203 """
204
205 ss_configs = CONF.share.security_service
206 if not ss_configs:
207 return
208
209 for ss_config in ss_configs:
210 ss_name = "ss_autogenerated_by_tempest_%s" % (
211 ss_config.get("ss_type"))
212
213 ss_params = {
214 "name": ss_name,
215 "dns_ip": ss_config.get("ss_dns_ip"),
216 "server": ss_config.get("ss_server"),
217 "domain": ss_config.get("ss_domain"),
218 "user": ss_config.get("ss_user"),
219 "password": ss_config.get("ss_password")
220 }
221 ss_type = ss_config.get("ss_type")
222 security_service = cls.create_security_service(
223 ss_type,
224 client=shares_client,
225 cleanup_in_class=cleanup_in_class,
226 **ss_params)
227
228 cls.add_sec_service_to_share_network(
229 shares_client, share_network_id,
230 security_service["id"],
231 cleanup_in_class=cleanup_in_class)
232
233 @classmethod
234 def add_sec_service_to_share_network(
235 cls, client, share_network_id,
236 security_service_id, cleanup_in_class=True):
237 """Associates a security service to a share network.
238
239 This method associates a security service provided by
240 the security service configuration with a specific
241 share network.
242
243 :param share_network_id: the share network id to be
244 associate with a given security service
245 :param security_service_id: the security service id
246 to be associate with a given share network
247 :param cleanup_in_class: if the resources will be
248 dissociate in the method teardown or class teardown
249 """
250
251 client.add_sec_service_to_share_network(
252 share_network_id,
253 security_service_id)
254 resource = {
255 "type": "dissociate_security_service",
256 "id": security_service_id,
257 "extra_params": {
258 "share_network_id": share_network_id
259 },
260 "client": client,
261 }
262
263 if cleanup_in_class:
264 cls.class_resources.insert(0, resource)
265 else:
266 cls.method_resources.insert(0, resource)
267
268 @classmethod
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200269 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300270 ignore_multitenancy_config=False):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700271 """Get or create share network for DHSS=True drivers
Marc Koderer0abc93b2015-07-15 09:18:35 +0200272
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700273 When testing DHSS=True (multitenancy_enabled) drivers, shares must
274 be requested on share networks.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200275 :returns: str -- share network id for shares_client tenant
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700276 :returns: None -- if single-tenant driver (DHSS=False) is used
Marc Koderer0abc93b2015-07-15 09:18:35 +0200277 """
278
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300279 if (not ignore_multitenancy_config and
280 not CONF.share.multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700281 # Assumed usage of a single-tenant driver (DHSS=False)
debeltrami1753a592020-05-11 18:27:30 +0000282 return None
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700283
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700284 if shares_client.share_network_id:
285 # Share-network already exists, use it
286 return shares_client.share_network_id
debeltrami1753a592020-05-11 18:27:30 +0000287
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700288 sn_name = "autogenerated_by_tempest"
289 sn_desc = "This share-network was created by tempest"
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300290
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700291 if not CONF.share.create_networks_when_multitenancy_enabled:
292 # We need a new share network, but don't need to associate
293 # any neutron networks to it - this configuration is used
294 # when manila is configured with "StandaloneNetworkPlugin"
295 # or "NeutronSingleNetworkPlugin" where all tenants share
296 # a single backend network where shares are exported.
297 sn = cls.create_share_network(cleanup_in_class=True,
298 client=shares_client,
299 add_security_services=True,
300 name=sn_name,
301 description=sn_desc)
302 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200303
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700304 # Retrieve non-public network list owned by the tenant
305 filters = {'project_id': shares_client.tenant_id,
306 'shared': False}
307 tenant_networks = (
308 networks_client.list_networks(**filters).get('networks', [])
309 )
310 tenant_networks_with_subnet = (
311 [n for n in tenant_networks if n['subnets']]
312 )
debeltrami1753a592020-05-11 18:27:30 +0000313
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700314 if not tenant_networks_with_subnet:
315 # This can only occur if using tempest's pre-provisioned
316 # credentials and not allocating networks to them
317 raise cls.skipException(
318 "Test credentials must provide at least one "
319 "non-shared project network with a valid subnet when "
320 "CONF.share.create_networks_when_multitenancy_enabled is "
321 "set to True.")
debeltrami1753a592020-05-11 18:27:30 +0000322
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700323 net_id = tenant_networks_with_subnet[0]['id']
324 subnet_id = tenant_networks_with_subnet[0]['subnets'][0]
debeltrami1753a592020-05-11 18:27:30 +0000325
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700326 # Create suitable share-network
327 sn = cls.create_share_network(cleanup_in_class=True,
328 client=shares_client,
329 add_security_services=True,
330 name=sn_name,
331 description=sn_desc,
332 neutron_net_id=net_id,
333 neutron_subnet_id=subnet_id)
debeltrami1753a592020-05-11 18:27:30 +0000334
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700335 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200336
337 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300338 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200339 snapshot_id=None, description=None, metadata=None,
340 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400341 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400342 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300343 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200344 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400345 share_network_id = (share_network_id or
346 CONF.share.share_network_id or
347 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200348 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300349 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400350 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200351 'share_protocol': share_protocol,
352 'size': size,
353 'name': name,
354 'snapshot_id': snapshot_id,
355 'description': description,
356 'metadata': metadata,
357 'share_network_id': share_network_id,
358 'share_type_id': share_type_id,
359 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400360 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400361 if share_group_id:
362 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400363
lkuchlan86f24322021-04-27 14:23:05 +0300364 share = client.create_share(**kwargs)['share']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400365 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400366 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200367 cleanup_list = (cls.class_resources if cleanup_in_class else
368 cls.method_resources)
369 cleanup_list.insert(0, resource)
370 return share
371
372 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300373 def migrate_share(
374 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200375 force_host_assisted_migration=False, writable=False,
376 nondisruptive=False, preserve_metadata=False,
377 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300378 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400379 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300380 client.migrate_share(
381 share_id, dest_host,
382 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200383 writable=writable, preserve_metadata=preserve_metadata,
384 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300385 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300386 new_share_type_id=new_share_type_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200387 share = waiters.wait_for_migration_status(
388 client, share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200389 return share
390
391 @classmethod
392 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
393 client = client or cls.shares_v2_client
394 client.migration_complete(share_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200395 share = waiters.wait_for_migration_status(
396 client, share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300397 return share
398
399 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300400 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
401 client = client or cls.shares_v2_client
402 client.migration_cancel(share_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200403 share = waiters.wait_for_migration_status(
404 client, share_id, dest_host, 'migration_cancelled', **kwargs)
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300405 return share
406
407 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200408 def create_share(cls, *args, **kwargs):
409 """Create one share and wait for available state. Retry if allowed."""
410 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
411 return result[0]
412
413 @classmethod
414 def create_shares(cls, share_data_list):
415 """Creates several shares in parallel with retries.
416
417 Use this method when you want to create more than one share at same
418 time. Especially if config option 'share.share_creation_retry_number'
419 has value more than zero (0).
420 All shares will be expected to have 'available' status with or without
421 recreation else error will be raised.
422
423 :param share_data_list: list -- list of dictionaries with 'args' and
424 'kwargs' for '_create_share' method of this base class.
425 example of data:
426 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
427 :returns: list -- list of shares created using provided data.
428 """
429
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300430 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200431 if not isinstance(d, dict):
432 raise exceptions.TempestException(
433 "Expected 'dict', got '%s'" % type(d))
434 if "args" not in d:
435 d["args"] = []
436 if "kwargs" not in d:
437 d["kwargs"] = {}
438 if len(d) > 2:
439 raise exceptions.TempestException(
440 "Expected only 'args' and 'kwargs' keys. "
441 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300442
443 data = []
444 for d in share_data_list:
445 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400446 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300447 local_d = {
448 "args": d["args"],
449 "kwargs": copy.deepcopy(d["kwargs"]),
450 }
451 local_d["kwargs"]["client"] = client
452 local_d["share"] = cls._create_share(
453 *local_d["args"], **local_d["kwargs"])
454 local_d["cnt"] = 0
455 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400456 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300457 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200458
459 while not all(d["available"] for d in data):
460 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400461 if not d["wait_for_status"]:
462 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200463 if d["available"]:
464 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300465 client = d["kwargs"]["client"]
466 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200467 try:
lkuchlanf7fc5b62021-01-26 14:53:43 +0200468 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +0200469 client, share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200470 d["available"] = True
471 except (share_exceptions.ShareBuildErrorException,
472 exceptions.TimeoutException) as e:
473 if CONF.share.share_creation_retry_number > d["cnt"]:
474 d["cnt"] += 1
475 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300476 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200477 LOG.error(msg)
478 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300479 cg_id = d["kwargs"].get("consistency_group_id")
480 if cg_id:
481 # NOTE(vponomaryov): delete errored share
482 # immediately in case share is part of CG.
483 client.delete_share(
484 share_id,
485 params={"consistency_group_id": cg_id})
486 client.wait_for_resource_deletion(
487 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200488 d["share"] = cls._create_share(
489 *d["args"], **d["kwargs"])
490 else:
gecong197358663802016-08-25 11:08:45 +0800491 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200492
493 return [d["share"] for d in data]
494
495 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400496 def create_share_group(cls, client=None, cleanup_in_class=True,
497 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400498 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400499 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400500 kwargs['share_network_id'] = (share_network_id or
501 client.share_network_id or None)
lkuchlan86f24322021-04-27 14:23:05 +0300502 share_group = client.create_share_group(**kwargs)['share_group']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400503 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400504 "type": "share_group",
505 "id": share_group["id"],
506 "client": client,
507 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400508 if cleanup_in_class:
509 cls.class_resources.insert(0, resource)
510 else:
511 cls.method_resources.insert(0, resource)
512
Andrew Kerrb8436922016-06-01 15:32:43 -0400513 if kwargs.get('source_share_group_snapshot_id'):
514 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400515 detailed=True,
lkuchlan86f24322021-04-27 14:23:05 +0300516 params={'share_group_id': share_group['id']})['shares']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400517
Andrew Kerrb8436922016-06-01 15:32:43 -0400518 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400519 resource = {"type": "share",
520 "id": share["id"],
521 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400522 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400523 if cleanup_in_class:
524 cls.class_resources.insert(0, resource)
525 else:
526 cls.method_resources.insert(0, resource)
527
lkuchlanf7fc5b62021-01-26 14:53:43 +0200528 waiters.wait_for_resource_status(
529 client, share_group['id'], 'available',
530 resource_name='share_group')
Andrew Kerrb8436922016-06-01 15:32:43 -0400531 return share_group
532
533 @classmethod
534 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
535 group_specs=None, client=None,
536 cleanup_in_class=True, **kwargs):
537 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300538 if (group_specs is None and
539 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300540 group_specs = {
541 'consistent_snapshot_support': (
542 CONF.share.capability_sg_consistent_snapshot_support),
543 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400544 share_group_type = client.create_share_group_type(
545 name=name,
546 share_types=share_types,
547 is_public=is_public,
548 group_specs=group_specs,
lkuchlan86f24322021-04-27 14:23:05 +0300549 **kwargs)['share_group_type']
Andrew Kerrb8436922016-06-01 15:32:43 -0400550 resource = {
551 "type": "share_group_type",
552 "id": share_group_type["id"],
553 "client": client,
554 }
555 if cleanup_in_class:
556 cls.class_resources.insert(0, resource)
557 else:
558 cls.method_resources.insert(0, resource)
559 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400560
561 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200562 def create_snapshot_wait_for_active(cls, share_id, name=None,
563 description=None, force=False,
Ashley Rodriguezc45cb4b2022-02-04 21:29:33 +0000564 metadata=None, client=None,
565 cleanup_in_class=True):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200566 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400567 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200568 if description is None:
569 description = "Tempest's snapshot"
lkuchlan86f24322021-04-27 14:23:05 +0300570 snapshot = client.create_snapshot(
Ashley Rodriguezc45cb4b2022-02-04 21:29:33 +0000571 share_id, name, description, force, metadata)['snapshot']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200572 resource = {
573 "type": "snapshot",
574 "id": snapshot["id"],
575 "client": client,
576 }
577 if cleanup_in_class:
578 cls.class_resources.insert(0, resource)
579 else:
580 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200581 waiters.wait_for_resource_status(client, snapshot["id"], "available",
582 resource_name='snapshot')
Marc Koderer0abc93b2015-07-15 09:18:35 +0200583 return snapshot
584
585 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400586 def create_share_group_snapshot_wait_for_active(
587 cls, share_group_id, name=None, description=None, client=None,
588 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400589 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400590 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400591 description = "Tempest's share group snapshot"
592 sg_snapshot = client.create_share_group_snapshot(
lkuchlan86f24322021-04-27 14:23:05 +0300593 share_group_id, name=name, description=description,
594 **kwargs)['share_group_snapshot']
Andrew Kerrbf31e912015-07-29 10:39:38 -0400595 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400596 "type": "share_group_snapshot",
597 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400598 "client": client,
599 }
600 if cleanup_in_class:
601 cls.class_resources.insert(0, resource)
602 else:
603 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200604 waiters.wait_for_resource_status(
605 client, sg_snapshot["id"], "available",
606 resource_name="share_group_snapshot")
Andrew Kerrb8436922016-06-01 15:32:43 -0400607 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400608
609 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800610 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400611 """List the availability zones for "manila-share" services
612
613 that are currently in "up" state.
614 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800615 client = client or cls.admin_shares_v2_client
616 backends = (
617 '|'.join(['^%s$' % backend for backend in backends])
618 if backends else '.*'
619 )
lkuchlan86f24322021-04-27 14:23:05 +0300620 cls.services = client.list_services()['services']
Yogeshbdb88102015-09-29 23:41:02 -0400621 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800622 service['binary'] == 'manila-share' and
623 service['state'] == 'up' and
624 re.search(backends, service['host'])]
Douglas Viroel161f1802020-04-25 17:18:14 -0300625 return list(set(zones))
Yogeshbdb88102015-09-29 23:41:02 -0400626
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800627 @classmethod
628 def get_pools_matching_share_type(cls, share_type, client=None):
629 client = client or cls.admin_shares_v2_client
630 if utils.is_microversion_supported('2.23'):
631 return client.list_pools(
andrebeltrami3b4d4852020-02-04 19:11:54 +0000632 detail=True,
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800633 search_opts={'share_type': share_type['id']})['pools']
634
635 pools = client.list_pools(detail=True)['pools']
636 share_type = client.get_share_type(share_type['id'])['share_type']
637 extra_specs = {}
638 for k, v in share_type['extra_specs'].items():
639 extra_specs[k] = (
haixin48895812020-09-30 13:50:37 +0800640 True if str(v).lower() == 'true'
641 else False if str(v).lower() == 'false' else v
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800642 )
643 return [
644 pool for pool in pools if all(y in pool['capabilities'].items()
645 for y in extra_specs.items())
646 ]
647
648 @classmethod
649 def get_availability_zones_matching_share_type(cls, share_type,
650 client=None):
651
652 client = client or cls.admin_shares_v2_client
653 pools_matching_share_type = cls.get_pools_matching_share_type(
654 share_type, client=client)
655 backends_matching_share_type = set(
656 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
657 )
658 azs = cls.get_availability_zones(backends=backends_matching_share_type)
659 return azs
660
Kiran Pawar59745062021-11-01 12:37:49 +0000661 def get_pools_for_replication_domain(self, share=None):
Yogesh1f931ff2015-09-29 23:41:02 -0400662 # Get the list of pools for the replication domain
663 pools = self.admin_client.list_pools(detail=True)['pools']
Kiran Pawar59745062021-11-01 12:37:49 +0000664 if share:
665 instance_host = self.admin_client.get_share(
666 share['id'])['share']['host']
667 else:
668 instance_host = self.admin_client.get_share(
669 self.shares[0]['id'])['share']['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400670 host_pool = [p for p in pools if p['name'] == instance_host][0]
671 rep_domain = host_pool['capabilities']['replication_domain']
672 pools_in_rep_domain = [p for p in pools if p['capabilities'][
673 'replication_domain'] == rep_domain]
674 return rep_domain, pools_in_rep_domain
675
Yogeshbdb88102015-09-29 23:41:02 -0400676 @classmethod
debeltrami0d523bb2020-08-20 12:48:49 +0000677 def create_share_replica(cls, share_id, availability_zone=None,
kpdev2521fbf2021-08-15 21:54:41 +0200678 scheduler_hints=None,
679 share_network_id=None,
debeltrami0d523bb2020-08-20 12:48:49 +0000680 client=None, cleanup_in_class=False,
681 cleanup=True,
kpdev2521fbf2021-08-15 21:54:41 +0200682 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400683 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300684 replica = client.create_share_replica(
lkuchlan86f24322021-04-27 14:23:05 +0300685 share_id, availability_zone=availability_zone,
kpdev2521fbf2021-08-15 21:54:41 +0200686 scheduler_hints=scheduler_hints,
687 share_network_id=share_network_id,
688 version=version)['share_replica']
Yogeshbdb88102015-09-29 23:41:02 -0400689 resource = {
690 "type": "share_replica",
691 "id": replica["id"],
692 "client": client,
693 "share_id": share_id,
694 }
695 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
696 if cleanup:
697 if cleanup_in_class:
698 cls.class_resources.insert(0, resource)
699 else:
700 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200701 waiters.wait_for_resource_status(
702 client, replica["id"], constants.STATUS_AVAILABLE,
703 resource_name='share_replica')
Yogeshbdb88102015-09-29 23:41:02 -0400704 return replica
705
706 @classmethod
Kiran Pawaredd82c72022-05-07 14:48:36 +0000707 def create_backup_wait_for_active(cls, share_id, client=None,
708 cleanup_in_class=False, cleanup=True,
709 version=CONF.share.max_api_microversion):
710 client = client or cls.shares_v2_client
711 backup_name = data_utils.rand_name('Backup')
712 backup_options = CONF.share.driver_assisted_backup_test_driver_options
713 backup = client.create_share_backup(
714 share_id,
715 name=backup_name,
716 backup_options=backup_options)['share_backup']
717 resource = {
718 "type": "share_backup",
719 "id": backup["id"],
720 "client": client,
721 }
722 if cleanup:
723 if cleanup_in_class:
724 cls.class_resources.insert(0, resource)
725 else:
726 cls.method_resources.insert(0, resource)
727 waiters.wait_for_resource_status(client, backup["id"], "available",
728 resource_name='share_backup')
729 return client.get_share_backup(backup['id'])['share_backup']
730
731 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000732 def delete_share_replica(cls, replica_id, client=None,
733 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400734 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400735 try:
silvacarlossd354d672020-08-23 18:49:52 +0000736 client.delete_share_replica(replica_id, version=version)
Yogesh1f931ff2015-09-29 23:41:02 -0400737 client.wait_for_resource_deletion(replica_id=replica_id)
738 except exceptions.NotFound:
739 pass
Yogeshbdb88102015-09-29 23:41:02 -0400740
741 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000742 def promote_share_replica(cls, replica_id, client=None,
743 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400744 client = client or cls.shares_v2_client
lkuchlan86f24322021-04-27 14:23:05 +0300745 replica = client.promote_share_replica(
746 replica_id, version=version)['share_replica']
lkuchlanf7fc5b62021-01-26 14:53:43 +0200747 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +0200748 client, replica["id"], constants.REPLICATION_STATE_ACTIVE,
lkuchlanf7fc5b62021-01-26 14:53:43 +0200749 resource_name='share_replica', status_attr="replica_state")
Yogeshbdb88102015-09-29 23:41:02 -0400750 return replica
751
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700752 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200753 def create_share_network(cls, client=None,
debeltrami1753a592020-05-11 18:27:30 +0000754 cleanup_in_class=False,
755 add_security_services=True, **kwargs):
756
Marc Koderer0abc93b2015-07-15 09:18:35 +0200757 if client is None:
Stephen Finucane878716d2025-09-05 08:18:03 +0100758 client = cls.shares_v2_client
lkuchlan86f24322021-04-27 14:23:05 +0300759 share_network = client.create_share_network(**kwargs)['share_network']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200760 resource = {
761 "type": "share_network",
762 "id": share_network["id"],
763 "client": client,
764 }
debeltrami1753a592020-05-11 18:27:30 +0000765
Marc Koderer0abc93b2015-07-15 09:18:35 +0200766 if cleanup_in_class:
767 cls.class_resources.insert(0, resource)
768 else:
769 cls.method_resources.insert(0, resource)
debeltrami1753a592020-05-11 18:27:30 +0000770
771 if add_security_services:
772 cls.provide_and_associate_security_services(
773 client, share_network["id"], cleanup_in_class=cleanup_in_class)
774
Marc Koderer0abc93b2015-07-15 09:18:35 +0200775 return share_network
776
777 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000778 def create_share_network_subnet(cls,
779 client=None,
780 cleanup_in_class=False,
renanpiranguinho0fec91a2023-03-10 19:58:31 +0000781 metadata=None,
debeltrami1753a592020-05-11 18:27:30 +0000782 **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(
renanpiranguinho0fec91a2023-03-10 19:58:31 +0000786 metadata=metadata, **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:
Stephen Finucane878716d2025-09-05 08:18:03 +0100805 client = cls.shares_v2_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():
Stephen Finucane878716d2025-09-05 08:18:03 +0100900 res["client"] = cls.shares_v2_client
Felipe Rodrigues9cdeda12023-02-09 21:45:49 -0300901 if not (res["deleted"]):
902 try:
903 res_id = res['id']
904 client = res["client"]
905 with handle_cleanup_exceptions():
906 if res["type"] == "share":
907 cls.clear_share_replicas(res_id)
908 share_group_id = res.get('share_group_id')
909 if share_group_id:
910 params = {'share_group_id': share_group_id}
911 client.delete_share(res_id, params=params)
912 else:
913 client.delete_share(res_id)
914 client.wait_for_resource_deletion(share_id=res_id)
915 elif res["type"] == "snapshot":
916 client.delete_snapshot(res_id)
917 client.wait_for_resource_deletion(
918 snapshot_id=res_id)
919 elif (res["type"] == "share_network" and
920 res_id != CONF.share.share_network_id):
921 client.delete_share_network(res_id)
922 client.wait_for_resource_deletion(sn_id=res_id)
923 elif res["type"] == "dissociate_security_service":
924 sn_id = res["extra_params"]["share_network_id"]
925 client.remove_sec_service_from_share_network(
926 sn_id=sn_id, ss_id=res_id
927 )
928 elif res["type"] == "security_service":
929 client.delete_security_service(res_id)
930 client.wait_for_resource_deletion(ss_id=res_id)
931 elif res["type"] == "share_type":
Goutham Pacha Ravi78daf102025-11-13 14:30:16 -0800932 # Check if there are still shares using this
933 # share type before attempting deletion to avoid
934 # cascading cleanup issues
935 shares_using_type = []
936 try:
937 shares_using_type = client.list_shares(
938 params={'share_type_id': res_id}
939 )['shares']
940 except Exception:
941 pass
942 if shares_using_type:
943 # Skip deletion if any shares exist
944 LOG.warning("Skipping share type deletion "
945 "for %s , still has %d shares "
946 "using it.",
947 res_id,
948 len(shares_using_type))
949 res["deleted"] = True
950 continue
Felipe Rodrigues9cdeda12023-02-09 21:45:49 -0300951 client.delete_share_type(res_id)
952 client.wait_for_resource_deletion(st_id=res_id)
953 elif res["type"] == "share_group":
954 client.delete_share_group(res_id)
955 client.wait_for_resource_deletion(
956 share_group_id=res_id)
957 elif res["type"] == "share_group_type":
958 client.delete_share_group_type(res_id)
959 client.wait_for_resource_deletion(
960 share_group_type_id=res_id)
961 elif res["type"] == "share_group_snapshot":
962 client.delete_share_group_snapshot(res_id)
963 client.wait_for_resource_deletion(
964 share_group_snapshot_id=res_id)
965 elif res["type"] == "share_replica":
966 client.delete_share_replica(res_id)
967 client.wait_for_resource_deletion(
968 replica_id=res_id)
969 elif res["type"] == "share_backup":
970 client.delete_share_backup(res_id)
971 client.wait_for_resource_deletion(backup_id=res_id)
972 elif res["type"] == "share_network_subnet":
973 sn_id = res["extra_params"]["share_network_id"]
974 client.delete_subnet(sn_id, res_id)
975 client.wait_for_resource_deletion(
976 share_network_subnet_id=res_id,
977 sn_id=sn_id)
978 elif res["type"] == "quotas":
979 user_id = res.get('user_id')
980 client.reset_quotas(res_id, user_id=user_id)
981 elif res["type"] == "resource_lock":
982 client.delete_resource_lock(res_id)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400983 else:
Felipe Rodrigues9cdeda12023-02-09 21:45:49 -0300984 LOG.warning("Provided unsupported resource type "
985 "for cleanup '%s'. Skipping.",
986 res["type"])
987 except share_exceptions.ResourceReleaseFailed as e:
988 # Resource is on error deleting state, so we remove it from
989 # the list to delete, since it cannot be deleted anymore.
990 # It raises because the current cleanup class or method
991 # must fail.
992 res["deleted"] = True
993 raise e
Marc Koderer0abc93b2015-07-15 09:18:35 +0200994 res["deleted"] = True
995
Marc Koderer0abc93b2015-07-15 09:18:35 +0200996 # Useful assertions
997 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
998 """Assert two dicts are equivalent.
999
1000 This is a 'deep' match in the sense that it handles nested
1001 dictionaries appropriately.
1002
1003 NOTE:
1004
1005 If you don't care (or don't know) a given value, you can specify
1006 the string DONTCARE as the value. This will cause that dict-item
1007 to be skipped.
1008
1009 """
1010 def raise_assertion(msg):
1011 d1str = str(d1)
1012 d2str = str(d2)
1013 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1014 'd2: %(d2str)s' %
1015 {"msg": msg, "d1str": d1str, "d2str": d2str})
1016 raise AssertionError(base_msg)
1017
1018 d1keys = set(d1.keys())
1019 d2keys = set(d2.keys())
1020 if d1keys != d2keys:
1021 d1only = d1keys - d2keys
1022 d2only = d2keys - d1keys
1023 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1024 'Keys in d2 and not d1: %(d2only)s' %
1025 {"d1only": d1only, "d2only": d2only})
1026
1027 for key in d1keys:
1028 d1value = d1[key]
1029 d2value = d2[key]
1030 try:
1031 error = abs(float(d1value) - float(d2value))
1032 within_tolerance = error <= tolerance
1033 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001034 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001035 # ValueError if arg is a str, TypeError if it's something else
1036 # (like None)
1037 within_tolerance = False
1038
1039 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1040 self.assertDictMatch(d1value, d2value)
1041 elif 'DONTCARE' in (d1value, d2value):
1042 continue
1043 elif approx_equal and within_tolerance:
1044 continue
1045 elif d1value != d2value:
1046 raise_assertion("d1['%(key)s']=%(d1value)s != "
1047 "d2['%(key)s']=%(d2value)s" %
1048 {
1049 "key": key,
1050 "d1value": d1value,
1051 "d2value": d2value
1052 })
1053
Alex Meadeba8a1602016-05-06 09:33:09 -04001054 def create_user_message(self):
1055 """Trigger a 'no valid host' situation to generate a message."""
1056 extra_specs = {
1057 'vendor_name': 'foobar',
1058 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1059 }
1060 share_type_name = data_utils.rand_name("share-type")
1061
1062 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001063 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001064 name=share_type_name,
1065 extra_specs=extra_specs)['share_type']
1066
1067 params = {'share_type_id': bogus_type['id'],
1068 'share_network_id': self.shares_v2_client.share_network_id}
lkuchlan86f24322021-04-27 14:23:05 +03001069 share = self.shares_v2_client.create_share(**params)['share']
Alex Meadeba8a1602016-05-06 09:33:09 -04001070 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
lkuchlanf7fc5b62021-01-26 14:53:43 +02001071 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001072 self.shares_v2_client, share['id'], "error")
1073 return waiters.wait_for_message(self.shares_v2_client, share['id'])
Alex Meadeba8a1602016-05-06 09:33:09 -04001074
lkuchlan5af7cb42020-07-14 18:05:09 +03001075 def allow_access(self, share_id, client=None, access_type=None,
lkuchlanad511b62022-05-08 09:23:23 +03001076 access_level='rw', access_to=None, metadata=None,
1077 version=LATEST_MICROVERSION, status='active',
silvacarlossd10819b2023-07-24 13:53:32 -03001078 raise_rule_in_error_state=True, lock_visibility=False,
1079 lock_deletion=False, cleanup=True):
lkuchlan5af7cb42020-07-14 18:05:09 +03001080
1081 client = client or self.shares_v2_client
lkuchlan4a803162022-11-16 11:34:33 +02001082 a_type, a_to = utils.get_access_rule_data_from_config(
1083 client.share_protocol)
lkuchlan5af7cb42020-07-14 18:05:09 +03001084 access_type = access_type or a_type
1085 access_to = access_to or a_to
1086
lkuchlanad511b62022-05-08 09:23:23 +03001087 kwargs = {
1088 'access_type': access_type,
1089 'access_to': access_to,
1090 'access_level': access_level
1091 }
silvacarlossd10819b2023-07-24 13:53:32 -03001092 delete_kwargs = (
1093 {'unrestrict': True} if lock_deletion else {}
1094 )
lkuchlanad511b62022-05-08 09:23:23 +03001095 if client is self.shares_v2_client:
1096 kwargs.update({'metadata': metadata, 'version': version})
silvacarlossd10819b2023-07-24 13:53:32 -03001097 if lock_visibility:
1098 kwargs.update({'lock_visibility': True})
1099 if lock_deletion:
1100 kwargs.update({'lock_deletion': True})
lkuchlanad511b62022-05-08 09:23:23 +03001101
1102 rule = client.create_access_rule(share_id, **kwargs)['access']
lkuchlanf7fc5b62021-01-26 14:53:43 +02001103 waiters.wait_for_resource_status(
1104 client, share_id, status, resource_name='access_rule',
lkuchlanad511b62022-05-08 09:23:23 +03001105 rule_id=rule['id'], version=version,
lkuchlanf7fc5b62021-01-26 14:53:43 +02001106 raise_rule_in_error_state=raise_rule_in_error_state)
lkuchlan5af7cb42020-07-14 18:05:09 +03001107 if cleanup:
lkuchlanad511b62022-05-08 09:23:23 +03001108 self.addCleanup(
1109 client.wait_for_resource_deletion, rule_id=rule['id'],
1110 share_id=share_id, version=version)
silvacarlossd10819b2023-07-24 13:53:32 -03001111 self.addCleanup(
1112 client.delete_access_rule, share_id, rule['id'],
1113 **delete_kwargs)
lkuchlan5af7cb42020-07-14 18:05:09 +03001114 return rule
1115
Marc Koderer0abc93b2015-07-15 09:18:35 +02001116
Marc Koderer0abc93b2015-07-15 09:18:35 +02001117class BaseSharesAdminTest(BaseSharesTest):
1118 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001119 credentials = ('admin', )
1120
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001121 @classmethod
1122 def setup_clients(cls):
1123 super(BaseSharesAdminTest, cls).setup_clients()
1124 # Initialise share clients
1125 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1126
lkuchlan3af822b2021-06-06 10:35:30 +03001127 @staticmethod
1128 def add_extra_specs_to_dict(extra_specs=None):
1129 """Add any required extra-specs to share type dictionary"""
haixin48895812020-09-30 13:50:37 +08001130 dhss = str(CONF.share.multitenancy_enabled)
lkuchlan3af822b2021-06-06 10:35:30 +03001131 extra_specs_dict = {"driver_handles_share_servers": dhss}
1132 if extra_specs:
1133 extra_specs_dict.update(extra_specs)
Felipe Rodrigues6e566772021-08-23 14:57:50 -03001134 if CONF.share.capability_thin_provisioned:
1135 extra_specs_dict['thin_provisioning'] = 'True'
lkuchlan3af822b2021-06-06 10:35:30 +03001136 return extra_specs_dict
1137
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001138 @classmethod
lkuchlan3af822b2021-06-06 10:35:30 +03001139 def create_share_type(cls, name=None, is_public=True, client=None,
1140 cleanup_in_class=True, extra_specs=None, **kwargs):
1141 name = name or data_utils.rand_name(
1142 cls.__class__.__name__ + 'share-type')
1143 client = client or cls.admin_shares_v2_client
1144 extra_specs = cls.add_extra_specs_to_dict(extra_specs=extra_specs)
1145 share_type = client.create_share_type(name, is_public,
1146 extra_specs=extra_specs,
1147 **kwargs)['share_type']
1148 resource = {
1149 "type": "share_type",
1150 "id": share_type["id"],
1151 "client": client,
1152 }
1153 if cleanup_in_class:
1154 cls.class_resources.insert(0, resource)
1155 else:
1156 cls.method_resources.insert(0, resource)
1157 return share_type
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001158
1159 @classmethod
1160 def _create_share_group_type(cls):
1161 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1162 return cls.create_share_group_type(
1163 name=share_group_type_name, share_types=[cls.share_type_id],
1164 client=cls.admin_shares_v2_client)
1165
Lucio Seki37056942019-01-24 15:40:20 -02001166 def _create_share_for_manage(self):
1167 creation_data = {
lkuchlan3af822b2021-06-06 10:35:30 +03001168 'share_type_id': self.st['id'],
Lucio Seki37056942019-01-24 15:40:20 -02001169 'share_protocol': self.protocol,
1170 }
1171
1172 share = self.create_share(**creation_data)
lkuchlan86f24322021-04-27 14:23:05 +03001173 share = self.shares_v2_client.get_share(share['id'])['share']
Lucio Seki37056942019-01-24 15:40:20 -02001174
1175 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
lkuchlan86f24322021-04-27 14:23:05 +03001176 el = self.shares_v2_client.list_share_export_locations(
1177 share["id"])['export_locations']
Lucio Seki37056942019-01-24 15:40:20 -02001178 share["export_locations"] = el
1179
1180 return share
1181
1182 def _unmanage_share_and_wait(self, share):
1183 self.shares_v2_client.unmanage_share(share['id'])
1184 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1185
1186 def _reset_state_and_delete_share(self, share):
1187 self.shares_v2_client.reset_state(share['id'])
1188 self._delete_share_and_wait(share)
1189
1190 def _delete_snapshot_and_wait(self, snap):
1191 self.shares_v2_client.delete_snapshot(snap['id'])
1192 self.shares_v2_client.wait_for_resource_deletion(
1193 snapshot_id=snap['id']
1194 )
1195 self.assertRaises(exceptions.NotFound,
1196 self.shares_v2_client.get_snapshot,
1197 snap['id'])
1198
1199 def _delete_share_and_wait(self, share):
1200 self.shares_v2_client.delete_share(share['id'])
1201 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1202 self.assertRaises(exceptions.NotFound,
1203 self.shares_v2_client.get_share,
1204 share['id'])
1205
1206 def _manage_share(self, share, name, description, share_server_id):
1207 managed_share = self.shares_v2_client.manage_share(
1208 service_host=share['host'],
1209 export_path=share['export_locations'][0],
1210 protocol=share['share_proto'],
lkuchlan3af822b2021-06-06 10:35:30 +03001211 share_type_id=self.share_type['id'],
Lucio Seki37056942019-01-24 15:40:20 -02001212 name=name,
1213 description=description,
1214 share_server_id=share_server_id
lkuchlan86f24322021-04-27 14:23:05 +03001215 )['share']
lkuchlanf7fc5b62021-01-26 14:53:43 +02001216 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001217 self.shares_v2_client, managed_share['id'],
1218 constants.STATUS_AVAILABLE
Lucio Seki37056942019-01-24 15:40:20 -02001219 )
1220
1221 return managed_share
1222
1223 def _unmanage_share_server_and_wait(self, server):
1224 self.shares_v2_client.unmanage_share_server(server['id'])
1225 self.shares_v2_client.wait_for_resource_deletion(
1226 server_id=server['id']
1227 )
1228
1229 def _manage_share_server(self, share_server, fields=None):
1230 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001231 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001232 managed_share_server = self.shares_v2_client.manage_share_server(
1233 params.get('host', share_server['host']),
1234 params.get('share_network_id', share_server['share_network_id']),
1235 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001236 share_network_subnet_id=subnet_id,
lkuchlan86f24322021-04-27 14:23:05 +03001237 )['share_server']
lkuchlanf7fc5b62021-01-26 14:53:43 +02001238 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001239 self.shares_v2_client, managed_share_server['id'],
lkuchlanf7fc5b62021-01-26 14:53:43 +02001240 constants.SERVER_STATE_ACTIVE, resource_name='share_server'
Lucio Seki37056942019-01-24 15:40:20 -02001241 )
1242
1243 return managed_share_server
1244
1245 def _delete_share_server_and_wait(self, share_server_id):
1246 self.shares_v2_client.delete_share_server(
1247 share_server_id
1248 )
1249 self.shares_v2_client.wait_for_resource_deletion(
1250 server_id=share_server_id)
1251
lkuchlan3af822b2021-06-06 10:35:30 +03001252 def create_user_message(self):
1253 """Trigger a 'no valid host' situation to generate a message."""
1254 extra_specs = {
1255 'vendor_name': 'foobar',
1256 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1257 }
1258 share_type_name = data_utils.rand_name("share-type")
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001259
lkuchlan3af822b2021-06-06 10:35:30 +03001260 bogus_type = self.create_share_type(
1261 client=self.admin_shares_v2_client,
1262 name=share_type_name,
1263 extra_specs=extra_specs)
1264
1265 params = {'share_type_id': bogus_type['id'],
1266 'share_network_id': self.shares_v2_client.share_network_id}
lkuchlan86f24322021-04-27 14:23:05 +03001267 share = self.shares_v2_client.create_share(**params)['share']
lkuchlan3af822b2021-06-06 10:35:30 +03001268 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1269 waiters.wait_for_resource_status(
1270 self.shares_v2_client, share['id'], "error")
1271 return waiters.wait_for_message(self.shares_v2_client, share['id'])
1272
1273
1274class BaseSharesMixedTest(BaseSharesAdminTest):
1275 """Base test case class for all Shares API tests with all user roles.
1276
Goutham Pacha Ravi64060262025-09-05 09:40:45 +01001277 Tests deriving from this class can use the primary project's client
1278 (self.shares_v2_client) and the alt project user's client
1279 (self.alt_shares_v2_client) to perform API calls and validations.
1280 Although admin clients are available for use,
lkuchlan3af822b2021-06-06 10:35:30 +03001281 their use should be limited to performing bootstrapping (e.g., creating
1282 a share type, or resetting state of a resource, etc.). No API validation
1283 must be performed against admin APIs. Use BaseAdminTest as a base class
1284 for such tests.
1285 """
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001286 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001287
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001288 # Will be cleaned up in resource_cleanup if the class
1289 class_project_users_created = []
1290
1291 @classmethod
1292 def resource_cleanup(cls):
1293 cls.clear_project_users(cls.class_project_users_created)
1294 super(BaseSharesMixedTest, cls).resource_cleanup()
1295
1296 @classmethod
1297 def clear_project_users(cls, users=None):
1298 users = users or cls.class_project_users_created
1299 for user in users:
1300 with handle_cleanup_exceptions():
1301 cls.os_admin.creds_client.delete_user(user['id'])
1302
Marc Koderer0abc93b2015-07-15 09:18:35 +02001303 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001304 def setup_clients(cls):
1305 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001306 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1307 # Initialise network clients
1308 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1309 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001310 # Initialise identity clients
1311 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1312 identity_clients = getattr(
1313 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
hongp5f585fb2024-12-26 17:18:38 +09001314 endpoint_type = CONF.share.endpoint_type
1315 cls.os_admin.identity_client = identity_clients.IdentityClient(
1316 endpoint_type=endpoint_type)
1317 cls.os_admin.projects_client = identity_clients.ProjectsClient(
1318 endpoint_type=endpoint_type)
1319 cls.os_admin.users_client = identity_clients.UsersClient(
1320 endpoint_type=endpoint_type)
1321 cls.os_admin.roles_client = identity_clients.RolesClient(
1322 endpoint_type=endpoint_type)
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001323 cls.os_admin.domains_client = (
hongp5f585fb2024-12-26 17:18:38 +09001324 cls.os_admin.identity_v3.DomainsClient(
1325 endpoint_type=endpoint_type) if
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001326 CONF.identity.auth_version == 'v3' else None)
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001327 cls.admin_project_member_client = cls.create_user_and_get_client(
1328 project=cls.admin_project, add_member_role=True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001329
1330 if CONF.share.multitenancy_enabled:
1331 admin_share_network_id = cls.provide_share_network(
1332 cls.admin_shares_v2_client, cls.os_admin.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001333 cls.admin_shares_v2_client.share_network_id = (
1334 admin_share_network_id)
1335
1336 alt_share_network_id = cls.provide_share_network(
1337 cls.alt_shares_v2_client, cls.os_alt.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001338 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001339
1340 @classmethod
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001341 def create_user_and_get_client(cls, project=None, add_member_role=True):
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001342 """Create a user in specified project & set share clients for user
1343
1344 The user will have all roles specified in tempest.conf
1345 :param: project: a dictionary with project ID and name, if not
1346 specified, the value will be cls.admin_project
1347 """
1348 project_domain_name = (
1349 cls.os_admin.identity_client.auth_provider.credentials.get(
1350 'project_domain_name', 'Default'))
1351 cls.os_admin.creds_client = cred_client.get_creds_client(
1352 cls.os_admin.identity_client, cls.os_admin.projects_client,
1353 cls.os_admin.users_client, cls.os_admin.roles_client,
1354 cls.os_admin.domains_client, project_domain_name)
1355
1356 # User info
1357 project = project or cls.admin_project
1358 username = data_utils.rand_name('manila_%s' % project['id'])
1359 password = data_utils.rand_password()
1360 email = '%s@example.org' % username
1361
1362 user = cls.os_admin.creds_client.create_user(
1363 username, password, project, email)
1364 cls.class_project_users_created.append(user)
1365
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001366 tempest_roles_to_assign = CONF.auth.tempest_roles or []
1367 if "member" not in tempest_roles_to_assign and add_member_role:
1368 tempest_roles_to_assign.append("member")
1369
1370 for role in tempest_roles_to_assign:
1371 cls.os_admin.creds_client.assign_user_role(user, project, role)
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001372
1373 user_creds = cls.os_admin.creds_client.get_credentials(
1374 user, project, password)
1375 os = clients.Clients(user_creds)
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001376 os.shares_v2_client = os.share_v2.SharesV2Client()
1377 return os