blob: e90852b788ed2e09d6b1bebb666a5ea532f124cb [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
21import six
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010022
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020023from tempest import config
Goutham Pacha Ravic678e212020-03-20 11:13:47 -070024from tempest.lib.common import cred_client
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050025from tempest.lib.common.utils import data_utils
26from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020027from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020028
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010029from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040030from manila_tempest_tests.common import constants
Marc Koderer0abc93b2015-07-15 09:18:35 +020031from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020032from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020033
34CONF = 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
61
62def verify_test_has_appropriate_tags(self):
63 if not TAGS_PATTERN.match(self.id()):
64 msg = (
65 "Required attributes either not set or set improperly. "
66 "Two test attributes are expected:\n"
67 " - one of '%(p)s' or '%(n)s' and \n"
68 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
69 ) % TAGS_MAPPER
70 raise self.failureException(msg)
71
Marc Koderer0abc93b2015-07-15 09:18:35 +020072
73class handle_cleanup_exceptions(object):
74 """Handle exceptions raised with cleanup operations.
75
76 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
77 are raised.
78 Suppress all other exceptions only in case config opt
79 'suppress_errors_in_cleanup' in config group 'share' is True.
80 """
81
82 def __enter__(self):
83 return self
84
85 def __exit__(self, exc_type, exc_value, exc_traceback):
86 if not (isinstance(exc_value,
87 (exceptions.NotFound, exceptions.Forbidden)) or
88 CONF.share.suppress_errors_in_cleanup):
89 return False # Do not suppress error if any
90 if exc_traceback:
91 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080092 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020093 return True # Suppress error if any
94
95
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020096skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -050097skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +020098
99
Marc Koderer0abc93b2015-07-15 09:18:35 +0200100class BaseSharesTest(test.BaseTestCase):
101 """Base test case class for all Manila API tests."""
102
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300103 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200104 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200105 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200106
107 # Will be cleaned up in resource_cleanup
108 class_resources = []
109
110 # Will be cleaned up in tearDown method
111 method_resources = []
112
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100113 # NOTE(andreaf) Override the client manager class to be used, so that
114 # a stable class is used, which includes plugin registered services as well
115 client_manager = clients.Clients
116
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200117 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200118 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200119 raise self.skipException(
120 "Microversion '%s' is not supported." % microversion)
121
Xing Yang69b00b52015-11-22 16:10:44 -0500122 def skip_if_microversion_lt(self, microversion):
123 if utils.is_microversion_lt(CONF.share.max_api_microversion,
124 microversion):
125 raise self.skipException(
126 "Microversion must be greater than or equal to '%s'." %
127 microversion)
128
Marc Koderer0abc93b2015-07-15 09:18:35 +0200129 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000130 def skip_checks(cls):
131 super(BaseSharesTest, cls).skip_checks()
132 if not CONF.service_available.manila:
133 raise cls.skipException("Manila support is required")
lkuchlana3b6f7a2020-01-07 10:45:45 +0200134 if not any(p in CONF.share.enable_protocols for p in cls.protocols):
135 skip_msg = "%s tests are disabled" % CONF.share.enable_protocols
136 raise cls.skipException(skip_msg)
Daniel Melladoe5269142017-01-12 12:17:58 +0000137
138 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200139 def verify_nonempty(cls, *args):
140 if not all(args):
141 msg = "Missing API credentials in configuration."
142 raise cls.skipException(msg)
143
144 @classmethod
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700145 def setup_credentials(cls):
146 # This call is used to tell the credential allocator to create
147 # network resources for this test case. NOTE: it must go before the
148 # super call, to override decisions in the base classes.
149 network_resources = {}
150 if (CONF.share.multitenancy_enabled and
151 CONF.share.create_networks_when_multitenancy_enabled):
152 # We're testing a DHSS=True driver, and manila is configured with
153 # NeutronNetworkPlugin (or a derivative) that supports creating
154 # share networks with project neutron networks, so lets ask for
155 # neutron network resources to be created with test credentials
156 network_resources.update({'network': True,
157 'subnet': True,
158 'router': True})
159 cls.set_network_resources(**network_resources)
160 super(BaseSharesTest, cls).setup_credentials()
161
162 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300163 def setup_clients(cls):
164 super(BaseSharesTest, cls).setup_clients()
165 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100166 # Initialise share clients for test credentials
167 cls.shares_client = os.share_v1.SharesClient()
168 cls.shares_v2_client = os.share_v2.SharesV2Client()
169 # Initialise network clients for test credentials
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700170 cls.networks_client = None
171 cls.subnets_client = None
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100172 if CONF.service_available.neutron:
173 cls.networks_client = os.network.NetworksClient()
174 cls.subnets_client = os.network.SubnetsClient()
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300175
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700176 # If DHSS=True, create a share network and set it in the client
177 # for easy access.
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300178 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300179 if (not CONF.service_available.neutron and
180 CONF.share.create_networks_when_multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700181 raise cls.skipException(
182 "Neutron support is required when "
183 "CONF.share.create_networks_when_multitenancy_enabled "
184 "is set to True")
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300185 share_network_id = cls.provide_share_network(
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700186 cls.shares_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300187 cls.shares_client.share_network_id = share_network_id
188 cls.shares_v2_client.share_network_id = share_network_id
189
Marc Koderer0abc93b2015-07-15 09:18:35 +0200190 def setUp(self):
191 super(BaseSharesTest, self).setUp()
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200192 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300193 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200194
195 @classmethod
196 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200197 cls.clear_resources(cls.class_resources)
Sam Wan241029c2016-07-26 03:37:42 -0400198 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200199
200 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000201 def provide_and_associate_security_services(
202 cls, shares_client, share_network_id, cleanup_in_class=True):
203 """Creates a security service and associates to a share network.
204
205 This method creates security services based on the Multiopt
206 defined in tempest configuration named security_service. When this
207 configuration is not provided, the method will return None.
208 After the security service creation, this method also associates
209 the security service to a share network.
210
211 :param shares_client: shares client, which requires the provisioning
212 :param share_network_id: id of the share network to associate the
213 security service
214 :param cleanup_in_class: if the security service and the association
215 will be removed in the method teardown or class teardown
216 :returns: None -- if the security service configuration is not
217 defined
218 """
219
220 ss_configs = CONF.share.security_service
221 if not ss_configs:
222 return
223
224 for ss_config in ss_configs:
225 ss_name = "ss_autogenerated_by_tempest_%s" % (
226 ss_config.get("ss_type"))
227
228 ss_params = {
229 "name": ss_name,
230 "dns_ip": ss_config.get("ss_dns_ip"),
231 "server": ss_config.get("ss_server"),
232 "domain": ss_config.get("ss_domain"),
233 "user": ss_config.get("ss_user"),
234 "password": ss_config.get("ss_password")
235 }
236 ss_type = ss_config.get("ss_type")
237 security_service = cls.create_security_service(
238 ss_type,
239 client=shares_client,
240 cleanup_in_class=cleanup_in_class,
241 **ss_params)
242
243 cls.add_sec_service_to_share_network(
244 shares_client, share_network_id,
245 security_service["id"],
246 cleanup_in_class=cleanup_in_class)
247
248 @classmethod
249 def add_sec_service_to_share_network(
250 cls, client, share_network_id,
251 security_service_id, cleanup_in_class=True):
252 """Associates a security service to a share network.
253
254 This method associates a security service provided by
255 the security service configuration with a specific
256 share network.
257
258 :param share_network_id: the share network id to be
259 associate with a given security service
260 :param security_service_id: the security service id
261 to be associate with a given share network
262 :param cleanup_in_class: if the resources will be
263 dissociate in the method teardown or class teardown
264 """
265
266 client.add_sec_service_to_share_network(
267 share_network_id,
268 security_service_id)
269 resource = {
270 "type": "dissociate_security_service",
271 "id": security_service_id,
272 "extra_params": {
273 "share_network_id": share_network_id
274 },
275 "client": client,
276 }
277
278 if cleanup_in_class:
279 cls.class_resources.insert(0, resource)
280 else:
281 cls.method_resources.insert(0, resource)
282
283 @classmethod
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200284 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300285 ignore_multitenancy_config=False):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700286 """Get or create share network for DHSS=True drivers
Marc Koderer0abc93b2015-07-15 09:18:35 +0200287
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700288 When testing DHSS=True (multitenancy_enabled) drivers, shares must
289 be requested on share networks.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200290 :returns: str -- share network id for shares_client tenant
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700291 :returns: None -- if single-tenant driver (DHSS=False) is used
Marc Koderer0abc93b2015-07-15 09:18:35 +0200292 """
293
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300294 if (not ignore_multitenancy_config and
295 not CONF.share.multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700296 # Assumed usage of a single-tenant driver (DHSS=False)
debeltrami1753a592020-05-11 18:27:30 +0000297 return None
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700298
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700299 if shares_client.share_network_id:
300 # Share-network already exists, use it
301 return shares_client.share_network_id
debeltrami1753a592020-05-11 18:27:30 +0000302
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700303 sn_name = "autogenerated_by_tempest"
304 sn_desc = "This share-network was created by tempest"
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300305
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700306 if not CONF.share.create_networks_when_multitenancy_enabled:
307 # We need a new share network, but don't need to associate
308 # any neutron networks to it - this configuration is used
309 # when manila is configured with "StandaloneNetworkPlugin"
310 # or "NeutronSingleNetworkPlugin" where all tenants share
311 # a single backend network where shares are exported.
312 sn = cls.create_share_network(cleanup_in_class=True,
313 client=shares_client,
314 add_security_services=True,
315 name=sn_name,
316 description=sn_desc)
317 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200318
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700319 # Retrieve non-public network list owned by the tenant
320 filters = {'project_id': shares_client.tenant_id,
321 'shared': False}
322 tenant_networks = (
323 networks_client.list_networks(**filters).get('networks', [])
324 )
325 tenant_networks_with_subnet = (
326 [n for n in tenant_networks if n['subnets']]
327 )
debeltrami1753a592020-05-11 18:27:30 +0000328
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700329 if not tenant_networks_with_subnet:
330 # This can only occur if using tempest's pre-provisioned
331 # credentials and not allocating networks to them
332 raise cls.skipException(
333 "Test credentials must provide at least one "
334 "non-shared project network with a valid subnet when "
335 "CONF.share.create_networks_when_multitenancy_enabled is "
336 "set to True.")
debeltrami1753a592020-05-11 18:27:30 +0000337
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700338 net_id = tenant_networks_with_subnet[0]['id']
339 subnet_id = tenant_networks_with_subnet[0]['subnets'][0]
debeltrami1753a592020-05-11 18:27:30 +0000340
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700341 # Create suitable share-network
342 sn = cls.create_share_network(cleanup_in_class=True,
343 client=shares_client,
344 add_security_services=True,
345 name=sn_name,
346 description=sn_desc,
347 neutron_net_id=net_id,
348 neutron_subnet_id=subnet_id)
debeltrami1753a592020-05-11 18:27:30 +0000349
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700350 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200351
352 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300353 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200354 snapshot_id=None, description=None, metadata=None,
355 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400356 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400357 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300358 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200359 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400360 share_network_id = (share_network_id or
361 CONF.share.share_network_id or
362 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200363 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300364 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400365 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200366 'share_protocol': share_protocol,
367 'size': size,
368 'name': name,
369 'snapshot_id': snapshot_id,
370 'description': description,
371 'metadata': metadata,
372 'share_network_id': share_network_id,
373 'share_type_id': share_type_id,
374 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400375 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400376 if share_group_id:
377 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400378
Marc Koderer0abc93b2015-07-15 09:18:35 +0200379 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400380 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400381 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200382 cleanup_list = (cls.class_resources if cleanup_in_class else
383 cls.method_resources)
384 cleanup_list.insert(0, resource)
385 return share
386
387 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300388 def migrate_share(
389 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200390 force_host_assisted_migration=False, writable=False,
391 nondisruptive=False, preserve_metadata=False,
392 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300393 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400394 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300395 client.migrate_share(
396 share_id, dest_host,
397 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200398 writable=writable, preserve_metadata=preserve_metadata,
399 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300400 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300401 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200402 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300403 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200404 return share
405
406 @classmethod
407 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
408 client = client or cls.shares_v2_client
409 client.migration_complete(share_id, **kwargs)
410 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300411 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300412 return share
413
414 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300415 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
416 client = client or cls.shares_v2_client
417 client.migration_cancel(share_id, **kwargs)
418 share = client.wait_for_migration_status(
419 share_id, dest_host, 'migration_cancelled', **kwargs)
420 return share
421
422 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200423 def create_share(cls, *args, **kwargs):
424 """Create one share and wait for available state. Retry if allowed."""
425 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
426 return result[0]
427
428 @classmethod
429 def create_shares(cls, share_data_list):
430 """Creates several shares in parallel with retries.
431
432 Use this method when you want to create more than one share at same
433 time. Especially if config option 'share.share_creation_retry_number'
434 has value more than zero (0).
435 All shares will be expected to have 'available' status with or without
436 recreation else error will be raised.
437
438 :param share_data_list: list -- list of dictionaries with 'args' and
439 'kwargs' for '_create_share' method of this base class.
440 example of data:
441 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
442 :returns: list -- list of shares created using provided data.
443 """
444
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300445 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200446 if not isinstance(d, dict):
447 raise exceptions.TempestException(
448 "Expected 'dict', got '%s'" % type(d))
449 if "args" not in d:
450 d["args"] = []
451 if "kwargs" not in d:
452 d["kwargs"] = {}
453 if len(d) > 2:
454 raise exceptions.TempestException(
455 "Expected only 'args' and 'kwargs' keys. "
456 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300457
458 data = []
459 for d in share_data_list:
460 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400461 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300462 local_d = {
463 "args": d["args"],
464 "kwargs": copy.deepcopy(d["kwargs"]),
465 }
466 local_d["kwargs"]["client"] = client
467 local_d["share"] = cls._create_share(
468 *local_d["args"], **local_d["kwargs"])
469 local_d["cnt"] = 0
470 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400471 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300472 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200473
474 while not all(d["available"] for d in data):
475 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400476 if not d["wait_for_status"]:
477 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200478 if d["available"]:
479 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300480 client = d["kwargs"]["client"]
481 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200482 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300483 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200484 d["available"] = True
485 except (share_exceptions.ShareBuildErrorException,
486 exceptions.TimeoutException) as e:
487 if CONF.share.share_creation_retry_number > d["cnt"]:
488 d["cnt"] += 1
489 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300490 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200491 LOG.error(msg)
492 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300493 cg_id = d["kwargs"].get("consistency_group_id")
494 if cg_id:
495 # NOTE(vponomaryov): delete errored share
496 # immediately in case share is part of CG.
497 client.delete_share(
498 share_id,
499 params={"consistency_group_id": cg_id})
500 client.wait_for_resource_deletion(
501 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200502 d["share"] = cls._create_share(
503 *d["args"], **d["kwargs"])
504 else:
gecong197358663802016-08-25 11:08:45 +0800505 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200506
507 return [d["share"] for d in data]
508
509 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400510 def create_share_group(cls, client=None, cleanup_in_class=True,
511 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400512 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400513 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400514 kwargs['share_network_id'] = (share_network_id or
515 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400516 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400517 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400518 "type": "share_group",
519 "id": share_group["id"],
520 "client": client,
521 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400522 if cleanup_in_class:
523 cls.class_resources.insert(0, resource)
524 else:
525 cls.method_resources.insert(0, resource)
526
Andrew Kerrb8436922016-06-01 15:32:43 -0400527 if kwargs.get('source_share_group_snapshot_id'):
528 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400529 detailed=True,
silvacarloss6e575682020-02-18 19:52:35 -0300530 params={'share_group_id': share_group['id']})
Andrew Kerrbf31e912015-07-29 10:39:38 -0400531
Andrew Kerrb8436922016-06-01 15:32:43 -0400532 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400533 resource = {"type": "share",
534 "id": share["id"],
535 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400536 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400537 if cleanup_in_class:
538 cls.class_resources.insert(0, resource)
539 else:
540 cls.method_resources.insert(0, resource)
541
Andrew Kerrb8436922016-06-01 15:32:43 -0400542 client.wait_for_share_group_status(share_group['id'], 'available')
543 return share_group
544
545 @classmethod
546 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
547 group_specs=None, client=None,
548 cleanup_in_class=True, **kwargs):
549 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300550 if (group_specs is None and
551 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300552 group_specs = {
553 'consistent_snapshot_support': (
554 CONF.share.capability_sg_consistent_snapshot_support),
555 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400556 share_group_type = client.create_share_group_type(
557 name=name,
558 share_types=share_types,
559 is_public=is_public,
560 group_specs=group_specs,
561 **kwargs)
562 resource = {
563 "type": "share_group_type",
564 "id": share_group_type["id"],
565 "client": client,
566 }
567 if cleanup_in_class:
568 cls.class_resources.insert(0, resource)
569 else:
570 cls.method_resources.insert(0, resource)
571 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400572
573 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200574 def create_snapshot_wait_for_active(cls, share_id, name=None,
575 description=None, force=False,
576 client=None, cleanup_in_class=True):
577 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400578 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200579 if description is None:
580 description = "Tempest's snapshot"
581 snapshot = client.create_snapshot(share_id, name, description, force)
582 resource = {
583 "type": "snapshot",
584 "id": snapshot["id"],
585 "client": client,
586 }
587 if cleanup_in_class:
588 cls.class_resources.insert(0, resource)
589 else:
590 cls.method_resources.insert(0, resource)
591 client.wait_for_snapshot_status(snapshot["id"], "available")
592 return snapshot
593
594 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400595 def create_share_group_snapshot_wait_for_active(
596 cls, share_group_id, name=None, description=None, client=None,
597 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400598 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400599 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400600 description = "Tempest's share group snapshot"
601 sg_snapshot = client.create_share_group_snapshot(
602 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400603 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400604 "type": "share_group_snapshot",
605 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400606 "client": client,
607 }
608 if cleanup_in_class:
609 cls.class_resources.insert(0, resource)
610 else:
611 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400612 client.wait_for_share_group_snapshot_status(
613 sg_snapshot["id"], "available")
614 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400615
616 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800617 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400618 """List the availability zones for "manila-share" services
619
620 that are currently in "up" state.
621 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800622 client = client or cls.admin_shares_v2_client
623 backends = (
624 '|'.join(['^%s$' % backend for backend in backends])
625 if backends else '.*'
626 )
Yogeshbdb88102015-09-29 23:41:02 -0400627 cls.services = client.list_services()
628 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800629 service['binary'] == 'manila-share' and
630 service['state'] == 'up' and
631 re.search(backends, service['host'])]
Douglas Viroel161f1802020-04-25 17:18:14 -0300632 return list(set(zones))
Yogeshbdb88102015-09-29 23:41:02 -0400633
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800634 @classmethod
635 def get_pools_matching_share_type(cls, share_type, client=None):
636 client = client or cls.admin_shares_v2_client
637 if utils.is_microversion_supported('2.23'):
638 return client.list_pools(
andrebeltrami3b4d4852020-02-04 19:11:54 +0000639 detail=True,
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800640 search_opts={'share_type': share_type['id']})['pools']
641
642 pools = client.list_pools(detail=True)['pools']
643 share_type = client.get_share_type(share_type['id'])['share_type']
644 extra_specs = {}
645 for k, v in share_type['extra_specs'].items():
646 extra_specs[k] = (
647 True if six.text_type(v).lower() == 'true'
648 else False if six.text_type(v).lower() == 'false' else v
649 )
650 return [
651 pool for pool in pools if all(y in pool['capabilities'].items()
652 for y in extra_specs.items())
653 ]
654
655 @classmethod
656 def get_availability_zones_matching_share_type(cls, share_type,
657 client=None):
658
659 client = client or cls.admin_shares_v2_client
660 pools_matching_share_type = cls.get_pools_matching_share_type(
661 share_type, client=client)
662 backends_matching_share_type = set(
663 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
664 )
665 azs = cls.get_availability_zones(backends=backends_matching_share_type)
666 return azs
667
Yogesh1f931ff2015-09-29 23:41:02 -0400668 def get_pools_for_replication_domain(self):
669 # Get the list of pools for the replication domain
670 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500671 instance_host = self.admin_client.get_share(
672 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400673 host_pool = [p for p in pools if p['name'] == instance_host][0]
674 rep_domain = host_pool['capabilities']['replication_domain']
675 pools_in_rep_domain = [p for p in pools if p['capabilities'][
676 'replication_domain'] == rep_domain]
677 return rep_domain, pools_in_rep_domain
678
Yogeshbdb88102015-09-29 23:41:02 -0400679 @classmethod
680 def create_share_replica(cls, share_id, availability_zone, client=None,
silvacarlossd354d672020-08-23 18:49:52 +0000681 cleanup_in_class=False, cleanup=True,
682 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(
silvacarlossd354d672020-08-23 18:49:52 +0000685 share_id, availability_zone=availability_zone, version=version)
Yogeshbdb88102015-09-29 23:41:02 -0400686 resource = {
687 "type": "share_replica",
688 "id": replica["id"],
689 "client": client,
690 "share_id": share_id,
691 }
692 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
693 if cleanup:
694 if cleanup_in_class:
695 cls.class_resources.insert(0, resource)
696 else:
697 cls.method_resources.insert(0, resource)
698 client.wait_for_share_replica_status(
699 replica["id"], constants.STATUS_AVAILABLE)
700 return replica
701
702 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000703 def delete_share_replica(cls, replica_id, client=None,
704 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400705 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400706 try:
silvacarlossd354d672020-08-23 18:49:52 +0000707 client.delete_share_replica(replica_id, version=version)
Yogesh1f931ff2015-09-29 23:41:02 -0400708 client.wait_for_resource_deletion(replica_id=replica_id)
709 except exceptions.NotFound:
710 pass
Yogeshbdb88102015-09-29 23:41:02 -0400711
712 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000713 def promote_share_replica(cls, replica_id, client=None,
714 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400715 client = client or cls.shares_v2_client
silvacarlossd354d672020-08-23 18:49:52 +0000716 replica = client.promote_share_replica(replica_id, version=version)
Yogeshbdb88102015-09-29 23:41:02 -0400717 client.wait_for_share_replica_status(
718 replica["id"],
719 constants.REPLICATION_STATE_ACTIVE,
720 status_attr="replica_state")
721 return replica
722
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700723 @classmethod
724 def _get_access_rule_data_from_config(cls):
yogeshdb32f462016-09-28 15:09:50 -0400725 """Get the first available access type/to combination from config.
726
727 This method opportunistically picks the first configured protocol
728 to create the share. Do not use this method in tests where you need
729 to test depth and breadth in the access types and access recipients.
730 """
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700731 protocol = cls.shares_v2_client.share_protocol
yogeshdb32f462016-09-28 15:09:50 -0400732
733 if protocol in CONF.share.enable_ip_rules_for_protocols:
734 access_type = "ip"
735 access_to = utils.rand_ip()
736 elif protocol in CONF.share.enable_user_rules_for_protocols:
737 access_type = "user"
738 access_to = CONF.share.username_for_user_rules
739 elif protocol in CONF.share.enable_cert_rules_for_protocols:
740 access_type = "cert"
741 access_to = "client3.com"
742 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
743 access_type = "cephx"
lkuchlan5af7cb42020-07-14 18:05:09 +0300744 access_to = data_utils.rand_name(
745 cls.__class__.__name__ + '-cephx-id')
yogeshdb32f462016-09-28 15:09:50 -0400746 else:
747 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700748 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400749
750 return access_type, access_to
751
Yogeshbdb88102015-09-29 23:41:02 -0400752 @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:
758 client = cls.shares_client
759 share_network = client.create_share_network(**kwargs)
760 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,
781 **kwargs):
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300782 if client is None:
783 client = cls.shares_v2_client
784 share_network_subnet = client.create_subnet(**kwargs)
785 resource = {
786 "type": "share-network-subnet",
787 "id": share_network_subnet["id"],
788 "extra_params": {
789 "share_network_id": share_network_subnet["share_network_id"]
790 },
791 "client": client,
792 }
793 if cleanup_in_class:
794 cls.class_resources.insert(0, resource)
795 else:
796 cls.method_resources.insert(0, resource)
797 return share_network_subnet
798
799 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200800 def create_security_service(cls, ss_type="ldap", client=None,
801 cleanup_in_class=False, **kwargs):
802 if client is None:
803 client = cls.shares_client
804 security_service = client.create_security_service(ss_type, **kwargs)
805 resource = {
806 "type": "security_service",
807 "id": security_service["id"],
808 "client": client,
809 }
810 if cleanup_in_class:
811 cls.class_resources.insert(0, resource)
812 else:
813 cls.method_resources.insert(0, resource)
814 return security_service
815
816 @classmethod
817 def create_share_type(cls, name, is_public=True, client=None,
818 cleanup_in_class=True, **kwargs):
819 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200820 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200821 share_type = client.create_share_type(name, is_public, **kwargs)
822 resource = {
823 "type": "share_type",
824 "id": share_type["share_type"]["id"],
825 "client": client,
826 }
827 if cleanup_in_class:
828 cls.class_resources.insert(0, resource)
829 else:
830 cls.method_resources.insert(0, resource)
831 return share_type
832
haixin0d1d29f2019-08-02 16:50:45 +0800833 @classmethod
834 def update_share_type(cls, share_type_id, name=None,
835 is_public=None, description=None,
836 client=None):
837 if client is None:
838 client = cls.shares_v2_client
839 share_type = client.update_share_type(share_type_id, name,
840 is_public, description)
841 return share_type
842
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700843 @classmethod
844 def update_quotas(cls, project_id, user_id=None, cleanup=True,
845 client=None, **kwargs):
846 client = client or cls.shares_v2_client
847 updated_quotas = client.update_quotas(project_id,
848 user_id=user_id,
849 **kwargs)
850 resource = {
851 "type": "quotas",
852 "id": project_id,
853 "client": client,
854 "user_id": user_id,
855 }
856 if cleanup:
857 cls.method_resources.insert(0, resource)
858 return updated_quotas
859
Marc Koderer0abc93b2015-07-15 09:18:35 +0200860 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400861 def add_extra_specs_to_dict(extra_specs=None):
862 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300863 dhss = six.text_type(CONF.share.multitenancy_enabled)
864 snapshot_support = six.text_type(
865 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400866 create_from_snapshot_support = six.text_type(
867 CONF.share.capability_create_share_from_snapshot_support)
868
869 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300870 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200871 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400872
873 optional = {
874 "snapshot_support": snapshot_support,
875 "create_share_from_snapshot_support": create_from_snapshot_support,
876 }
877 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
878 # required extra-spec
879 extra_specs_dict.update(optional)
880
Marc Koderer0abc93b2015-07-15 09:18:35 +0200881 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400882 extra_specs_dict.update(extra_specs)
883
884 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200885
886 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400887 def clear_share_replicas(cls, share_id, client=None):
888 client = client or cls.shares_v2_client
889 share_replicas = client.list_share_replicas(
890 share_id=share_id)
891
892 for replica in share_replicas:
893 try:
894 cls.delete_share_replica(replica['id'])
895 except exceptions.BadRequest:
896 # Ignore the exception due to deletion of last active replica
897 pass
898
899 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200900 def clear_resources(cls, resources=None):
901 """Deletes resources, that were created in test suites.
902
903 This method tries to remove resources from resource list,
904 if it is not found, assumed it was deleted in test itself.
905 It is expected, that all resources were added as LIFO
906 due to restriction of deletion resources, that is in the chain.
907
908 :param resources: dict with keys 'type','id','client' and 'deleted'
909 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200910 if resources is None:
911 resources = cls.method_resources
912 for res in resources:
913 if "deleted" not in res.keys():
914 res["deleted"] = False
915 if "client" not in res.keys():
916 res["client"] = cls.shares_client
917 if not(res["deleted"]):
918 res_id = res['id']
919 client = res["client"]
920 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200921 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400922 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400923 share_group_id = res.get('share_group_id')
924 if share_group_id:
925 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400926 client.delete_share(res_id, params=params)
927 else:
928 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200929 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200930 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200931 client.delete_snapshot(res_id)
932 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200933 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400934 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400935 client.delete_share_network(res_id)
936 client.wait_for_resource_deletion(sn_id=res_id)
debeltrami1753a592020-05-11 18:27:30 +0000937 elif res["type"] == "dissociate_security_service":
938 sn_id = res["extra_params"]["share_network_id"]
939 client.remove_sec_service_from_share_network(
940 sn_id=sn_id, ss_id=res_id
941 )
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200942 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200943 client.delete_security_service(res_id)
944 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200945 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200946 client.delete_share_type(res_id)
947 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200948 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400949 client.delete_share_group(res_id)
950 client.wait_for_resource_deletion(
951 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200952 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400953 client.delete_share_group_type(res_id)
954 client.wait_for_resource_deletion(
955 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200956 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400957 client.delete_share_group_snapshot(res_id)
958 client.wait_for_resource_deletion(
959 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200960 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400961 client.delete_share_replica(res_id)
962 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200963 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300964 sn_id = res["extra_params"]["share_network_id"]
965 client.delete_subnet(sn_id, res_id)
966 client.wait_for_resource_deletion(
967 share_network_subnet_id=res_id,
968 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700969 elif res["type"] == "quotas":
970 user_id = res.get('user_id')
971 client.reset_quotas(res_id, user_id=user_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200972 else:
huayue97bacbf2016-01-04 09:57:39 +0800973 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800974 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200975 res["deleted"] = True
976
977 @classmethod
978 def generate_share_network_data(self):
979 data = {
980 "name": data_utils.rand_name("sn-name"),
981 "description": data_utils.rand_name("sn-desc"),
982 "neutron_net_id": data_utils.rand_name("net-id"),
983 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
984 }
985 return data
986
987 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300988 def generate_subnet_data(self):
989 data = {
990 "neutron_net_id": data_utils.rand_name("net-id"),
991 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
992 }
993 return data
994
995 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100996 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200997 data = {
998 "name": data_utils.rand_name("ss-name"),
999 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001000 "dns_ip": utils.rand_ip(),
1001 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001002 "domain": data_utils.rand_name("ss-domain"),
1003 "user": data_utils.rand_name("ss-user"),
1004 "password": data_utils.rand_name("ss-password"),
1005 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001006 if set_ou:
1007 data["ou"] = data_utils.rand_name("ss-ou")
1008
Marc Koderer0abc93b2015-07-15 09:18:35 +02001009 return data
1010
1011 # Useful assertions
1012 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1013 """Assert two dicts are equivalent.
1014
1015 This is a 'deep' match in the sense that it handles nested
1016 dictionaries appropriately.
1017
1018 NOTE:
1019
1020 If you don't care (or don't know) a given value, you can specify
1021 the string DONTCARE as the value. This will cause that dict-item
1022 to be skipped.
1023
1024 """
1025 def raise_assertion(msg):
1026 d1str = str(d1)
1027 d2str = str(d2)
1028 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1029 'd2: %(d2str)s' %
1030 {"msg": msg, "d1str": d1str, "d2str": d2str})
1031 raise AssertionError(base_msg)
1032
1033 d1keys = set(d1.keys())
1034 d2keys = set(d2.keys())
1035 if d1keys != d2keys:
1036 d1only = d1keys - d2keys
1037 d2only = d2keys - d1keys
1038 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1039 'Keys in d2 and not d1: %(d2only)s' %
1040 {"d1only": d1only, "d2only": d2only})
1041
1042 for key in d1keys:
1043 d1value = d1[key]
1044 d2value = d2[key]
1045 try:
1046 error = abs(float(d1value) - float(d2value))
1047 within_tolerance = error <= tolerance
1048 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001049 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001050 # ValueError if arg is a str, TypeError if it's something else
1051 # (like None)
1052 within_tolerance = False
1053
1054 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1055 self.assertDictMatch(d1value, d2value)
1056 elif 'DONTCARE' in (d1value, d2value):
1057 continue
1058 elif approx_equal and within_tolerance:
1059 continue
1060 elif d1value != d2value:
1061 raise_assertion("d1['%(key)s']=%(d1value)s != "
1062 "d2['%(key)s']=%(d2value)s" %
1063 {
1064 "key": key,
1065 "d1value": d1value,
1066 "d2value": d2value
1067 })
1068
Alex Meadeba8a1602016-05-06 09:33:09 -04001069 def create_user_message(self):
1070 """Trigger a 'no valid host' situation to generate a message."""
1071 extra_specs = {
1072 'vendor_name': 'foobar',
1073 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1074 }
1075 share_type_name = data_utils.rand_name("share-type")
1076
1077 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001078 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001079 name=share_type_name,
1080 extra_specs=extra_specs)['share_type']
1081
1082 params = {'share_type_id': bogus_type['id'],
1083 'share_network_id': self.shares_v2_client.share_network_id}
1084 share = self.shares_v2_client.create_share(**params)
1085 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1086 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1087 return self.shares_v2_client.wait_for_message(share['id'])
1088
lkuchlan5af7cb42020-07-14 18:05:09 +03001089 def allow_access(self, share_id, client=None, access_type=None,
1090 access_level='rw', access_to=None, status='active',
1091 raise_rule_in_error_state=True, cleanup=True):
1092
1093 client = client or self.shares_v2_client
1094 a_type, a_to = self._get_access_rule_data_from_config()
1095 access_type = access_type or a_type
1096 access_to = access_to or a_to
1097
1098 rule = client.create_access_rule(share_id, access_type, access_to,
1099 access_level)
1100 client.wait_for_access_rule_status(share_id, rule['id'], status,
1101 raise_rule_in_error_state)
1102 if cleanup:
1103 self.addCleanup(client.wait_for_resource_deletion,
1104 rule_id=rule['id'], share_id=share_id)
1105 self.addCleanup(client.delete_access_rule, share_id, rule['id'])
1106 return rule
1107
Marc Koderer0abc93b2015-07-15 09:18:35 +02001108
1109class BaseSharesAltTest(BaseSharesTest):
1110 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001111 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001112
1113
1114class BaseSharesAdminTest(BaseSharesTest):
1115 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001116 credentials = ('admin', )
1117
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001118 @classmethod
1119 def setup_clients(cls):
1120 super(BaseSharesAdminTest, cls).setup_clients()
1121 # Initialise share clients
1122 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1123
1124 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001125 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001126 name = data_utils.rand_name("unique_st_name")
1127 extra_specs = cls.add_extra_specs_to_dict(specs)
1128 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001129 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001130 client=cls.admin_shares_v2_client)['share_type']
1131
1132 @classmethod
1133 def _create_share_group_type(cls):
1134 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1135 return cls.create_share_group_type(
1136 name=share_group_type_name, share_types=[cls.share_type_id],
1137 client=cls.admin_shares_v2_client)
1138
Lucio Seki37056942019-01-24 15:40:20 -02001139 def _create_share_for_manage(self):
1140 creation_data = {
1141 'share_type_id': self.st['share_type']['id'],
1142 'share_protocol': self.protocol,
1143 }
1144
1145 share = self.create_share(**creation_data)
1146 share = self.shares_v2_client.get_share(share['id'])
1147
1148 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1149 el = self.shares_v2_client.list_share_export_locations(share["id"])
1150 share["export_locations"] = el
1151
1152 return share
1153
1154 def _unmanage_share_and_wait(self, share):
1155 self.shares_v2_client.unmanage_share(share['id'])
1156 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1157
1158 def _reset_state_and_delete_share(self, share):
1159 self.shares_v2_client.reset_state(share['id'])
1160 self._delete_share_and_wait(share)
1161
1162 def _delete_snapshot_and_wait(self, snap):
1163 self.shares_v2_client.delete_snapshot(snap['id'])
1164 self.shares_v2_client.wait_for_resource_deletion(
1165 snapshot_id=snap['id']
1166 )
1167 self.assertRaises(exceptions.NotFound,
1168 self.shares_v2_client.get_snapshot,
1169 snap['id'])
1170
1171 def _delete_share_and_wait(self, share):
1172 self.shares_v2_client.delete_share(share['id'])
1173 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1174 self.assertRaises(exceptions.NotFound,
1175 self.shares_v2_client.get_share,
1176 share['id'])
1177
1178 def _manage_share(self, share, name, description, share_server_id):
1179 managed_share = self.shares_v2_client.manage_share(
1180 service_host=share['host'],
1181 export_path=share['export_locations'][0],
1182 protocol=share['share_proto'],
1183 share_type_id=self.share_type['share_type']['id'],
1184 name=name,
1185 description=description,
1186 share_server_id=share_server_id
1187 )
1188 self.shares_v2_client.wait_for_share_status(
1189 managed_share['id'], constants.STATUS_AVAILABLE
1190 )
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,
Lucio Seki37056942019-01-24 15:40:20 -02001208 )
1209 self.shares_v2_client.wait_for_share_server_status(
1210 managed_share_server['id'],
1211 constants.SERVER_STATE_ACTIVE,
1212 )
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
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001223
1224class BaseSharesMixedTest(BaseSharesTest):
1225 """Base test case class for all Shares API tests with all user roles."""
1226 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001227
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001228 # Will be cleaned up in resource_cleanup if the class
1229 class_project_users_created = []
1230
1231 @classmethod
1232 def resource_cleanup(cls):
1233 cls.clear_project_users(cls.class_project_users_created)
1234 super(BaseSharesMixedTest, cls).resource_cleanup()
1235
1236 @classmethod
1237 def clear_project_users(cls, users=None):
1238 users = users or cls.class_project_users_created
1239 for user in users:
1240 with handle_cleanup_exceptions():
1241 cls.os_admin.creds_client.delete_user(user['id'])
1242
Marc Koderer0abc93b2015-07-15 09:18:35 +02001243 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001244 def setup_clients(cls):
1245 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001246 # Initialise share clients
1247 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1248 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1249 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1250 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1251 # Initialise network clients
1252 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1253 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001254 # Initialise identity clients
1255 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1256 identity_clients = getattr(
1257 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1258 cls.os_admin.identity_client = identity_clients.IdentityClient()
1259 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1260 cls.os_admin.users_client = identity_clients.UsersClient()
1261 cls.os_admin.roles_client = identity_clients.RolesClient()
1262 cls.os_admin.domains_client = (
1263 cls.os_admin.identity_v3.DomainsClient() if
1264 CONF.identity.auth_version == 'v3' else None)
1265 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001266
1267 if CONF.share.multitenancy_enabled:
1268 admin_share_network_id = cls.provide_share_network(
1269 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1270 cls.admin_shares_client.share_network_id = admin_share_network_id
1271 cls.admin_shares_v2_client.share_network_id = (
1272 admin_share_network_id)
1273
1274 alt_share_network_id = cls.provide_share_network(
1275 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1276 cls.alt_shares_client.share_network_id = alt_share_network_id
1277 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001278
1279 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001280 def create_user_and_get_client(cls, project=None):
1281 """Create a user in specified project & set share clients for user
1282
1283 The user will have all roles specified in tempest.conf
1284 :param: project: a dictionary with project ID and name, if not
1285 specified, the value will be cls.admin_project
1286 """
1287 project_domain_name = (
1288 cls.os_admin.identity_client.auth_provider.credentials.get(
1289 'project_domain_name', 'Default'))
1290 cls.os_admin.creds_client = cred_client.get_creds_client(
1291 cls.os_admin.identity_client, cls.os_admin.projects_client,
1292 cls.os_admin.users_client, cls.os_admin.roles_client,
1293 cls.os_admin.domains_client, project_domain_name)
1294
1295 # User info
1296 project = project or cls.admin_project
1297 username = data_utils.rand_name('manila_%s' % project['id'])
1298 password = data_utils.rand_password()
1299 email = '%s@example.org' % username
1300
1301 user = cls.os_admin.creds_client.create_user(
1302 username, password, project, email)
1303 cls.class_project_users_created.append(user)
1304
1305 for conf_role in CONF.auth.tempest_roles:
1306 cls.os_admin.creds_client.assign_user_role(
1307 user, project, conf_role)
1308
1309 user_creds = cls.os_admin.creds_client.get_credentials(
1310 user, project, password)
1311 os = clients.Clients(user_creds)
1312 os.shares_v1_client = os.share_v1.SharesClient()
1313 os.shares_v2_client = os.share_v2.SharesV2Client()
1314 return os
1315
1316 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001317 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001318 name = data_utils.rand_name("unique_st_name")
1319 extra_specs = cls.add_extra_specs_to_dict(specs)
1320 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001321 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001322 client=cls.admin_shares_v2_client)['share_type']
1323
1324 @classmethod
1325 def _create_share_group_type(cls):
1326 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1327 return cls.create_share_group_type(
1328 name=share_group_type_name, share_types=[cls.share_type_id],
1329 client=cls.admin_shares_v2_client)