blob: 7915eaf370bb57b232a76f9e381a14d45b7a3d83 [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,
681 cleanup_in_class=False, cleanup=True):
682 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300683 replica = client.create_share_replica(
684 share_id, availability_zone=availability_zone)
Yogeshbdb88102015-09-29 23:41:02 -0400685 resource = {
686 "type": "share_replica",
687 "id": replica["id"],
688 "client": client,
689 "share_id": share_id,
690 }
691 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
692 if cleanup:
693 if cleanup_in_class:
694 cls.class_resources.insert(0, resource)
695 else:
696 cls.method_resources.insert(0, resource)
697 client.wait_for_share_replica_status(
698 replica["id"], constants.STATUS_AVAILABLE)
699 return replica
700
701 @classmethod
702 def delete_share_replica(cls, replica_id, client=None):
703 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400704 try:
705 client.delete_share_replica(replica_id)
706 client.wait_for_resource_deletion(replica_id=replica_id)
707 except exceptions.NotFound:
708 pass
Yogeshbdb88102015-09-29 23:41:02 -0400709
710 @classmethod
711 def promote_share_replica(cls, replica_id, client=None):
712 client = client or cls.shares_v2_client
713 replica = client.promote_share_replica(replica_id)
714 client.wait_for_share_replica_status(
715 replica["id"],
716 constants.REPLICATION_STATE_ACTIVE,
717 status_attr="replica_state")
718 return replica
719
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700720 @classmethod
721 def _get_access_rule_data_from_config(cls):
yogeshdb32f462016-09-28 15:09:50 -0400722 """Get the first available access type/to combination from config.
723
724 This method opportunistically picks the first configured protocol
725 to create the share. Do not use this method in tests where you need
726 to test depth and breadth in the access types and access recipients.
727 """
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700728 protocol = cls.shares_v2_client.share_protocol
yogeshdb32f462016-09-28 15:09:50 -0400729
730 if protocol in CONF.share.enable_ip_rules_for_protocols:
731 access_type = "ip"
732 access_to = utils.rand_ip()
733 elif protocol in CONF.share.enable_user_rules_for_protocols:
734 access_type = "user"
735 access_to = CONF.share.username_for_user_rules
736 elif protocol in CONF.share.enable_cert_rules_for_protocols:
737 access_type = "cert"
738 access_to = "client3.com"
739 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
740 access_type = "cephx"
741 access_to = "eve"
742 else:
743 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700744 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400745
746 return access_type, access_to
747
Yogeshbdb88102015-09-29 23:41:02 -0400748 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200749 def create_share_network(cls, client=None,
debeltrami1753a592020-05-11 18:27:30 +0000750 cleanup_in_class=False,
751 add_security_services=True, **kwargs):
752
Marc Koderer0abc93b2015-07-15 09:18:35 +0200753 if client is None:
754 client = cls.shares_client
755 share_network = client.create_share_network(**kwargs)
756 resource = {
757 "type": "share_network",
758 "id": share_network["id"],
759 "client": client,
760 }
debeltrami1753a592020-05-11 18:27:30 +0000761
Marc Koderer0abc93b2015-07-15 09:18:35 +0200762 if cleanup_in_class:
763 cls.class_resources.insert(0, resource)
764 else:
765 cls.method_resources.insert(0, resource)
debeltrami1753a592020-05-11 18:27:30 +0000766
767 if add_security_services:
768 cls.provide_and_associate_security_services(
769 client, share_network["id"], cleanup_in_class=cleanup_in_class)
770
Marc Koderer0abc93b2015-07-15 09:18:35 +0200771 return share_network
772
773 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000774 def create_share_network_subnet(cls,
775 client=None,
776 cleanup_in_class=False,
777 **kwargs):
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300778 if client is None:
779 client = cls.shares_v2_client
780 share_network_subnet = client.create_subnet(**kwargs)
781 resource = {
782 "type": "share-network-subnet",
783 "id": share_network_subnet["id"],
784 "extra_params": {
785 "share_network_id": share_network_subnet["share_network_id"]
786 },
787 "client": client,
788 }
789 if cleanup_in_class:
790 cls.class_resources.insert(0, resource)
791 else:
792 cls.method_resources.insert(0, resource)
793 return share_network_subnet
794
795 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200796 def create_security_service(cls, ss_type="ldap", client=None,
797 cleanup_in_class=False, **kwargs):
798 if client is None:
799 client = cls.shares_client
800 security_service = client.create_security_service(ss_type, **kwargs)
801 resource = {
802 "type": "security_service",
803 "id": security_service["id"],
804 "client": client,
805 }
806 if cleanup_in_class:
807 cls.class_resources.insert(0, resource)
808 else:
809 cls.method_resources.insert(0, resource)
810 return security_service
811
812 @classmethod
813 def create_share_type(cls, name, is_public=True, client=None,
814 cleanup_in_class=True, **kwargs):
815 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200816 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200817 share_type = client.create_share_type(name, is_public, **kwargs)
818 resource = {
819 "type": "share_type",
820 "id": share_type["share_type"]["id"],
821 "client": client,
822 }
823 if cleanup_in_class:
824 cls.class_resources.insert(0, resource)
825 else:
826 cls.method_resources.insert(0, resource)
827 return share_type
828
haixin0d1d29f2019-08-02 16:50:45 +0800829 @classmethod
830 def update_share_type(cls, share_type_id, name=None,
831 is_public=None, description=None,
832 client=None):
833 if client is None:
834 client = cls.shares_v2_client
835 share_type = client.update_share_type(share_type_id, name,
836 is_public, description)
837 return share_type
838
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700839 @classmethod
840 def update_quotas(cls, project_id, user_id=None, cleanup=True,
841 client=None, **kwargs):
842 client = client or cls.shares_v2_client
843 updated_quotas = client.update_quotas(project_id,
844 user_id=user_id,
845 **kwargs)
846 resource = {
847 "type": "quotas",
848 "id": project_id,
849 "client": client,
850 "user_id": user_id,
851 }
852 if cleanup:
853 cls.method_resources.insert(0, resource)
854 return updated_quotas
855
Marc Koderer0abc93b2015-07-15 09:18:35 +0200856 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400857 def add_extra_specs_to_dict(extra_specs=None):
858 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300859 dhss = six.text_type(CONF.share.multitenancy_enabled)
860 snapshot_support = six.text_type(
861 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400862 create_from_snapshot_support = six.text_type(
863 CONF.share.capability_create_share_from_snapshot_support)
864
865 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300866 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200867 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400868
869 optional = {
870 "snapshot_support": snapshot_support,
871 "create_share_from_snapshot_support": create_from_snapshot_support,
872 }
873 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
874 # required extra-spec
875 extra_specs_dict.update(optional)
876
Marc Koderer0abc93b2015-07-15 09:18:35 +0200877 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400878 extra_specs_dict.update(extra_specs)
879
880 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200881
882 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400883 def clear_share_replicas(cls, share_id, client=None):
884 client = client or cls.shares_v2_client
885 share_replicas = client.list_share_replicas(
886 share_id=share_id)
887
888 for replica in share_replicas:
889 try:
890 cls.delete_share_replica(replica['id'])
891 except exceptions.BadRequest:
892 # Ignore the exception due to deletion of last active replica
893 pass
894
895 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200896 def clear_resources(cls, resources=None):
897 """Deletes resources, that were created in test suites.
898
899 This method tries to remove resources from resource list,
900 if it is not found, assumed it was deleted in test itself.
901 It is expected, that all resources were added as LIFO
902 due to restriction of deletion resources, that is in the chain.
903
904 :param resources: dict with keys 'type','id','client' and 'deleted'
905 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200906 if resources is None:
907 resources = cls.method_resources
908 for res in resources:
909 if "deleted" not in res.keys():
910 res["deleted"] = False
911 if "client" not in res.keys():
912 res["client"] = cls.shares_client
913 if not(res["deleted"]):
914 res_id = res['id']
915 client = res["client"]
916 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200917 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400918 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400919 share_group_id = res.get('share_group_id')
920 if share_group_id:
921 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400922 client.delete_share(res_id, params=params)
923 else:
924 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200925 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200926 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200927 client.delete_snapshot(res_id)
928 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200929 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400930 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400931 client.delete_share_network(res_id)
932 client.wait_for_resource_deletion(sn_id=res_id)
debeltrami1753a592020-05-11 18:27:30 +0000933 elif res["type"] == "dissociate_security_service":
934 sn_id = res["extra_params"]["share_network_id"]
935 client.remove_sec_service_from_share_network(
936 sn_id=sn_id, ss_id=res_id
937 )
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200938 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200939 client.delete_security_service(res_id)
940 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200941 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200942 client.delete_share_type(res_id)
943 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200944 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400945 client.delete_share_group(res_id)
946 client.wait_for_resource_deletion(
947 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200948 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400949 client.delete_share_group_type(res_id)
950 client.wait_for_resource_deletion(
951 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200952 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400953 client.delete_share_group_snapshot(res_id)
954 client.wait_for_resource_deletion(
955 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200956 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400957 client.delete_share_replica(res_id)
958 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200959 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300960 sn_id = res["extra_params"]["share_network_id"]
961 client.delete_subnet(sn_id, res_id)
962 client.wait_for_resource_deletion(
963 share_network_subnet_id=res_id,
964 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700965 elif res["type"] == "quotas":
966 user_id = res.get('user_id')
967 client.reset_quotas(res_id, user_id=user_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200968 else:
huayue97bacbf2016-01-04 09:57:39 +0800969 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800970 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200971 res["deleted"] = True
972
973 @classmethod
974 def generate_share_network_data(self):
975 data = {
976 "name": data_utils.rand_name("sn-name"),
977 "description": data_utils.rand_name("sn-desc"),
978 "neutron_net_id": data_utils.rand_name("net-id"),
979 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
980 }
981 return data
982
983 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300984 def generate_subnet_data(self):
985 data = {
986 "neutron_net_id": data_utils.rand_name("net-id"),
987 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
988 }
989 return data
990
991 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100992 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200993 data = {
994 "name": data_utils.rand_name("ss-name"),
995 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200996 "dns_ip": utils.rand_ip(),
997 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200998 "domain": data_utils.rand_name("ss-domain"),
999 "user": data_utils.rand_name("ss-user"),
1000 "password": data_utils.rand_name("ss-password"),
1001 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001002 if set_ou:
1003 data["ou"] = data_utils.rand_name("ss-ou")
1004
Marc Koderer0abc93b2015-07-15 09:18:35 +02001005 return data
1006
1007 # Useful assertions
1008 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1009 """Assert two dicts are equivalent.
1010
1011 This is a 'deep' match in the sense that it handles nested
1012 dictionaries appropriately.
1013
1014 NOTE:
1015
1016 If you don't care (or don't know) a given value, you can specify
1017 the string DONTCARE as the value. This will cause that dict-item
1018 to be skipped.
1019
1020 """
1021 def raise_assertion(msg):
1022 d1str = str(d1)
1023 d2str = str(d2)
1024 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1025 'd2: %(d2str)s' %
1026 {"msg": msg, "d1str": d1str, "d2str": d2str})
1027 raise AssertionError(base_msg)
1028
1029 d1keys = set(d1.keys())
1030 d2keys = set(d2.keys())
1031 if d1keys != d2keys:
1032 d1only = d1keys - d2keys
1033 d2only = d2keys - d1keys
1034 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1035 'Keys in d2 and not d1: %(d2only)s' %
1036 {"d1only": d1only, "d2only": d2only})
1037
1038 for key in d1keys:
1039 d1value = d1[key]
1040 d2value = d2[key]
1041 try:
1042 error = abs(float(d1value) - float(d2value))
1043 within_tolerance = error <= tolerance
1044 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001045 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001046 # ValueError if arg is a str, TypeError if it's something else
1047 # (like None)
1048 within_tolerance = False
1049
1050 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1051 self.assertDictMatch(d1value, d2value)
1052 elif 'DONTCARE' in (d1value, d2value):
1053 continue
1054 elif approx_equal and within_tolerance:
1055 continue
1056 elif d1value != d2value:
1057 raise_assertion("d1['%(key)s']=%(d1value)s != "
1058 "d2['%(key)s']=%(d2value)s" %
1059 {
1060 "key": key,
1061 "d1value": d1value,
1062 "d2value": d2value
1063 })
1064
Alex Meadeba8a1602016-05-06 09:33:09 -04001065 def create_user_message(self):
1066 """Trigger a 'no valid host' situation to generate a message."""
1067 extra_specs = {
1068 'vendor_name': 'foobar',
1069 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1070 }
1071 share_type_name = data_utils.rand_name("share-type")
1072
1073 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001074 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001075 name=share_type_name,
1076 extra_specs=extra_specs)['share_type']
1077
1078 params = {'share_type_id': bogus_type['id'],
1079 'share_network_id': self.shares_v2_client.share_network_id}
1080 share = self.shares_v2_client.create_share(**params)
1081 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1082 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1083 return self.shares_v2_client.wait_for_message(share['id'])
1084
Marc Koderer0abc93b2015-07-15 09:18:35 +02001085
1086class BaseSharesAltTest(BaseSharesTest):
1087 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001088 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001089
1090
1091class BaseSharesAdminTest(BaseSharesTest):
1092 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001093 credentials = ('admin', )
1094
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001095 @classmethod
1096 def setup_clients(cls):
1097 super(BaseSharesAdminTest, cls).setup_clients()
1098 # Initialise share clients
1099 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1100
1101 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001102 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001103 name = data_utils.rand_name("unique_st_name")
1104 extra_specs = cls.add_extra_specs_to_dict(specs)
1105 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001106 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001107 client=cls.admin_shares_v2_client)['share_type']
1108
1109 @classmethod
1110 def _create_share_group_type(cls):
1111 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1112 return cls.create_share_group_type(
1113 name=share_group_type_name, share_types=[cls.share_type_id],
1114 client=cls.admin_shares_v2_client)
1115
Lucio Seki37056942019-01-24 15:40:20 -02001116 def _create_share_for_manage(self):
1117 creation_data = {
1118 'share_type_id': self.st['share_type']['id'],
1119 'share_protocol': self.protocol,
1120 }
1121
1122 share = self.create_share(**creation_data)
1123 share = self.shares_v2_client.get_share(share['id'])
1124
1125 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1126 el = self.shares_v2_client.list_share_export_locations(share["id"])
1127 share["export_locations"] = el
1128
1129 return share
1130
1131 def _unmanage_share_and_wait(self, share):
1132 self.shares_v2_client.unmanage_share(share['id'])
1133 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1134
1135 def _reset_state_and_delete_share(self, share):
1136 self.shares_v2_client.reset_state(share['id'])
1137 self._delete_share_and_wait(share)
1138
1139 def _delete_snapshot_and_wait(self, snap):
1140 self.shares_v2_client.delete_snapshot(snap['id'])
1141 self.shares_v2_client.wait_for_resource_deletion(
1142 snapshot_id=snap['id']
1143 )
1144 self.assertRaises(exceptions.NotFound,
1145 self.shares_v2_client.get_snapshot,
1146 snap['id'])
1147
1148 def _delete_share_and_wait(self, share):
1149 self.shares_v2_client.delete_share(share['id'])
1150 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1151 self.assertRaises(exceptions.NotFound,
1152 self.shares_v2_client.get_share,
1153 share['id'])
1154
1155 def _manage_share(self, share, name, description, share_server_id):
1156 managed_share = self.shares_v2_client.manage_share(
1157 service_host=share['host'],
1158 export_path=share['export_locations'][0],
1159 protocol=share['share_proto'],
1160 share_type_id=self.share_type['share_type']['id'],
1161 name=name,
1162 description=description,
1163 share_server_id=share_server_id
1164 )
1165 self.shares_v2_client.wait_for_share_status(
1166 managed_share['id'], constants.STATUS_AVAILABLE
1167 )
1168
1169 return managed_share
1170
1171 def _unmanage_share_server_and_wait(self, server):
1172 self.shares_v2_client.unmanage_share_server(server['id'])
1173 self.shares_v2_client.wait_for_resource_deletion(
1174 server_id=server['id']
1175 )
1176
1177 def _manage_share_server(self, share_server, fields=None):
1178 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001179 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001180 managed_share_server = self.shares_v2_client.manage_share_server(
1181 params.get('host', share_server['host']),
1182 params.get('share_network_id', share_server['share_network_id']),
1183 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001184 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001185 )
1186 self.shares_v2_client.wait_for_share_server_status(
1187 managed_share_server['id'],
1188 constants.SERVER_STATE_ACTIVE,
1189 )
1190
1191 return managed_share_server
1192
1193 def _delete_share_server_and_wait(self, share_server_id):
1194 self.shares_v2_client.delete_share_server(
1195 share_server_id
1196 )
1197 self.shares_v2_client.wait_for_resource_deletion(
1198 server_id=share_server_id)
1199
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001200
1201class BaseSharesMixedTest(BaseSharesTest):
1202 """Base test case class for all Shares API tests with all user roles."""
1203 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001204
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001205 # Will be cleaned up in resource_cleanup if the class
1206 class_project_users_created = []
1207
1208 @classmethod
1209 def resource_cleanup(cls):
1210 cls.clear_project_users(cls.class_project_users_created)
1211 super(BaseSharesMixedTest, cls).resource_cleanup()
1212
1213 @classmethod
1214 def clear_project_users(cls, users=None):
1215 users = users or cls.class_project_users_created
1216 for user in users:
1217 with handle_cleanup_exceptions():
1218 cls.os_admin.creds_client.delete_user(user['id'])
1219
Marc Koderer0abc93b2015-07-15 09:18:35 +02001220 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001221 def setup_clients(cls):
1222 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001223 # Initialise share clients
1224 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1225 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1226 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1227 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1228 # Initialise network clients
1229 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1230 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001231 # Initialise identity clients
1232 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1233 identity_clients = getattr(
1234 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1235 cls.os_admin.identity_client = identity_clients.IdentityClient()
1236 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1237 cls.os_admin.users_client = identity_clients.UsersClient()
1238 cls.os_admin.roles_client = identity_clients.RolesClient()
1239 cls.os_admin.domains_client = (
1240 cls.os_admin.identity_v3.DomainsClient() if
1241 CONF.identity.auth_version == 'v3' else None)
1242 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001243
1244 if CONF.share.multitenancy_enabled:
1245 admin_share_network_id = cls.provide_share_network(
1246 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1247 cls.admin_shares_client.share_network_id = admin_share_network_id
1248 cls.admin_shares_v2_client.share_network_id = (
1249 admin_share_network_id)
1250
1251 alt_share_network_id = cls.provide_share_network(
1252 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1253 cls.alt_shares_client.share_network_id = alt_share_network_id
1254 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001255
1256 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001257 def create_user_and_get_client(cls, project=None):
1258 """Create a user in specified project & set share clients for user
1259
1260 The user will have all roles specified in tempest.conf
1261 :param: project: a dictionary with project ID and name, if not
1262 specified, the value will be cls.admin_project
1263 """
1264 project_domain_name = (
1265 cls.os_admin.identity_client.auth_provider.credentials.get(
1266 'project_domain_name', 'Default'))
1267 cls.os_admin.creds_client = cred_client.get_creds_client(
1268 cls.os_admin.identity_client, cls.os_admin.projects_client,
1269 cls.os_admin.users_client, cls.os_admin.roles_client,
1270 cls.os_admin.domains_client, project_domain_name)
1271
1272 # User info
1273 project = project or cls.admin_project
1274 username = data_utils.rand_name('manila_%s' % project['id'])
1275 password = data_utils.rand_password()
1276 email = '%s@example.org' % username
1277
1278 user = cls.os_admin.creds_client.create_user(
1279 username, password, project, email)
1280 cls.class_project_users_created.append(user)
1281
1282 for conf_role in CONF.auth.tempest_roles:
1283 cls.os_admin.creds_client.assign_user_role(
1284 user, project, conf_role)
1285
1286 user_creds = cls.os_admin.creds_client.get_credentials(
1287 user, project, password)
1288 os = clients.Clients(user_creds)
1289 os.shares_v1_client = os.share_v1.SharesClient()
1290 os.shares_v2_client = os.share_v2.SharesV2Client()
1291 return os
1292
1293 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001294 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001295 name = data_utils.rand_name("unique_st_name")
1296 extra_specs = cls.add_extra_specs_to_dict(specs)
1297 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001298 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001299 client=cls.admin_shares_v2_client)['share_type']
1300
1301 @classmethod
1302 def _create_share_group_type(cls):
1303 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1304 return cls.create_share_group_type(
1305 name=share_group_type_name, share_types=[cls.share_type_id],
1306 client=cls.admin_shares_v2_client)