blob: fe4616ee4adf5a67a0fa69e2ccf15ae59953c0dc [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
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020022from tempest import config
Goutham Pacha Ravic678e212020-03-20 11:13:47 -070023from tempest.lib.common import cred_client
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050024from tempest.lib.common.utils import data_utils
25from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020026from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020027
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010028from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040029from manila_tempest_tests.common import constants
Marc Koderer0abc93b2015-07-15 09:18:35 +020030from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020031from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020032
lkuchlan1d1461d2020-08-04 11:19:11 +030033
Marc Koderer0abc93b2015-07-15 09:18:35 +020034CONF = config.CONF
35LOG = log.getLogger(__name__)
36
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030037# Test tags related to test direction
38TAG_POSITIVE = "positive"
39TAG_NEGATIVE = "negative"
40
41# Test tags related to service involvement
Tom Barron69f96962019-07-29 17:07:03 -040042# Only requires that manila-api service running.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030043TAG_API = "api"
Tom Barron69f96962019-07-29 17:07:03 -040044# Requires all manila services running, intended to test back-end
45# (manila-share) behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030046TAG_BACKEND = "backend"
Tom Barron69f96962019-07-29 17:07:03 -040047# Requires all manila services running, intended to test API behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030048TAG_API_WITH_BACKEND = "api_with_backend"
49
50TAGS_MAPPER = {
51 "p": TAG_POSITIVE,
52 "n": TAG_NEGATIVE,
53 "a": TAG_API,
54 "b": TAG_BACKEND,
55 "ab": TAG_API_WITH_BACKEND,
56}
57TAGS_PATTERN = re.compile(
58 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
59 TAGS_MAPPER)
60
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
debeltrami0d523bb2020-08-20 12:48:49 +0000680 def create_share_replica(cls, share_id, availability_zone=None,
681 client=None, cleanup_in_class=False,
682 cleanup=True,
silvacarlossd354d672020-08-23 18:49:52 +0000683 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400684 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300685 replica = client.create_share_replica(
silvacarlossd354d672020-08-23 18:49:52 +0000686 share_id, availability_zone=availability_zone, version=version)
Yogeshbdb88102015-09-29 23:41:02 -0400687 resource = {
688 "type": "share_replica",
689 "id": replica["id"],
690 "client": client,
691 "share_id": share_id,
692 }
693 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
694 if cleanup:
695 if cleanup_in_class:
696 cls.class_resources.insert(0, resource)
697 else:
698 cls.method_resources.insert(0, resource)
699 client.wait_for_share_replica_status(
700 replica["id"], constants.STATUS_AVAILABLE)
701 return replica
702
703 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000704 def delete_share_replica(cls, replica_id, client=None,
705 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400706 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400707 try:
silvacarlossd354d672020-08-23 18:49:52 +0000708 client.delete_share_replica(replica_id, version=version)
Yogesh1f931ff2015-09-29 23:41:02 -0400709 client.wait_for_resource_deletion(replica_id=replica_id)
710 except exceptions.NotFound:
711 pass
Yogeshbdb88102015-09-29 23:41:02 -0400712
713 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000714 def promote_share_replica(cls, replica_id, client=None,
715 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400716 client = client or cls.shares_v2_client
silvacarlossd354d672020-08-23 18:49:52 +0000717 replica = client.promote_share_replica(replica_id, version=version)
Yogeshbdb88102015-09-29 23:41:02 -0400718 client.wait_for_share_replica_status(
719 replica["id"],
720 constants.REPLICATION_STATE_ACTIVE,
721 status_attr="replica_state")
722 return replica
723
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700724 @classmethod
725 def _get_access_rule_data_from_config(cls):
yogeshdb32f462016-09-28 15:09:50 -0400726 """Get the first available access type/to combination from config.
727
728 This method opportunistically picks the first configured protocol
729 to create the share. Do not use this method in tests where you need
730 to test depth and breadth in the access types and access recipients.
731 """
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700732 protocol = cls.shares_v2_client.share_protocol
yogeshdb32f462016-09-28 15:09:50 -0400733
734 if protocol in CONF.share.enable_ip_rules_for_protocols:
735 access_type = "ip"
736 access_to = utils.rand_ip()
737 elif protocol in CONF.share.enable_user_rules_for_protocols:
738 access_type = "user"
739 access_to = CONF.share.username_for_user_rules
740 elif protocol in CONF.share.enable_cert_rules_for_protocols:
741 access_type = "cert"
742 access_to = "client3.com"
743 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
744 access_type = "cephx"
lkuchlan5af7cb42020-07-14 18:05:09 +0300745 access_to = data_utils.rand_name(
746 cls.__class__.__name__ + '-cephx-id')
yogeshdb32f462016-09-28 15:09:50 -0400747 else:
748 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700749 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400750
751 return access_type, access_to
752
Yogeshbdb88102015-09-29 23:41:02 -0400753 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200754 def create_share_network(cls, client=None,
debeltrami1753a592020-05-11 18:27:30 +0000755 cleanup_in_class=False,
756 add_security_services=True, **kwargs):
757
Marc Koderer0abc93b2015-07-15 09:18:35 +0200758 if client is None:
759 client = cls.shares_client
760 share_network = client.create_share_network(**kwargs)
761 resource = {
762 "type": "share_network",
763 "id": share_network["id"],
764 "client": client,
765 }
debeltrami1753a592020-05-11 18:27:30 +0000766
Marc Koderer0abc93b2015-07-15 09:18:35 +0200767 if cleanup_in_class:
768 cls.class_resources.insert(0, resource)
769 else:
770 cls.method_resources.insert(0, resource)
debeltrami1753a592020-05-11 18:27:30 +0000771
772 if add_security_services:
773 cls.provide_and_associate_security_services(
774 client, share_network["id"], cleanup_in_class=cleanup_in_class)
775
Marc Koderer0abc93b2015-07-15 09:18:35 +0200776 return share_network
777
778 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000779 def create_share_network_subnet(cls,
780 client=None,
781 cleanup_in_class=False,
782 **kwargs):
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300783 if client is None:
784 client = cls.shares_v2_client
785 share_network_subnet = client.create_subnet(**kwargs)
786 resource = {
787 "type": "share-network-subnet",
788 "id": share_network_subnet["id"],
789 "extra_params": {
790 "share_network_id": share_network_subnet["share_network_id"]
791 },
792 "client": client,
793 }
794 if cleanup_in_class:
795 cls.class_resources.insert(0, resource)
796 else:
797 cls.method_resources.insert(0, resource)
798 return share_network_subnet
799
800 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200801 def create_security_service(cls, ss_type="ldap", client=None,
802 cleanup_in_class=False, **kwargs):
803 if client is None:
804 client = cls.shares_client
805 security_service = client.create_security_service(ss_type, **kwargs)
806 resource = {
807 "type": "security_service",
808 "id": security_service["id"],
809 "client": client,
810 }
811 if cleanup_in_class:
812 cls.class_resources.insert(0, resource)
813 else:
814 cls.method_resources.insert(0, resource)
815 return security_service
816
817 @classmethod
818 def create_share_type(cls, name, is_public=True, client=None,
819 cleanup_in_class=True, **kwargs):
820 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200821 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200822 share_type = client.create_share_type(name, is_public, **kwargs)
823 resource = {
824 "type": "share_type",
825 "id": share_type["share_type"]["id"],
826 "client": client,
827 }
828 if cleanup_in_class:
829 cls.class_resources.insert(0, resource)
830 else:
831 cls.method_resources.insert(0, resource)
832 return share_type
833
haixin0d1d29f2019-08-02 16:50:45 +0800834 @classmethod
835 def update_share_type(cls, share_type_id, name=None,
836 is_public=None, description=None,
837 client=None):
838 if client is None:
839 client = cls.shares_v2_client
840 share_type = client.update_share_type(share_type_id, name,
841 is_public, description)
842 return share_type
843
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700844 @classmethod
845 def update_quotas(cls, project_id, user_id=None, cleanup=True,
846 client=None, **kwargs):
847 client = client or cls.shares_v2_client
848 updated_quotas = client.update_quotas(project_id,
849 user_id=user_id,
850 **kwargs)
851 resource = {
852 "type": "quotas",
853 "id": project_id,
854 "client": client,
855 "user_id": user_id,
856 }
857 if cleanup:
858 cls.method_resources.insert(0, resource)
859 return updated_quotas
860
Marc Koderer0abc93b2015-07-15 09:18:35 +0200861 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400862 def add_extra_specs_to_dict(extra_specs=None):
863 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300864 dhss = six.text_type(CONF.share.multitenancy_enabled)
865 snapshot_support = six.text_type(
866 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400867 create_from_snapshot_support = six.text_type(
868 CONF.share.capability_create_share_from_snapshot_support)
869
870 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300871 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200872 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400873
874 optional = {
875 "snapshot_support": snapshot_support,
876 "create_share_from_snapshot_support": create_from_snapshot_support,
877 }
878 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
879 # required extra-spec
880 extra_specs_dict.update(optional)
881
Marc Koderer0abc93b2015-07-15 09:18:35 +0200882 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400883 extra_specs_dict.update(extra_specs)
884
885 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200886
887 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400888 def clear_share_replicas(cls, share_id, client=None):
889 client = client or cls.shares_v2_client
890 share_replicas = client.list_share_replicas(
891 share_id=share_id)
892
893 for replica in share_replicas:
894 try:
895 cls.delete_share_replica(replica['id'])
896 except exceptions.BadRequest:
897 # Ignore the exception due to deletion of last active replica
898 pass
899
900 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200901 def clear_resources(cls, resources=None):
902 """Deletes resources, that were created in test suites.
903
904 This method tries to remove resources from resource list,
905 if it is not found, assumed it was deleted in test itself.
906 It is expected, that all resources were added as LIFO
907 due to restriction of deletion resources, that is in the chain.
908
909 :param resources: dict with keys 'type','id','client' and 'deleted'
910 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200911 if resources is None:
912 resources = cls.method_resources
913 for res in resources:
914 if "deleted" not in res.keys():
915 res["deleted"] = False
916 if "client" not in res.keys():
917 res["client"] = cls.shares_client
918 if not(res["deleted"]):
919 res_id = res['id']
920 client = res["client"]
921 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200922 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400923 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400924 share_group_id = res.get('share_group_id')
925 if share_group_id:
926 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400927 client.delete_share(res_id, params=params)
928 else:
929 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200930 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200931 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200932 client.delete_snapshot(res_id)
933 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200934 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400935 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400936 client.delete_share_network(res_id)
937 client.wait_for_resource_deletion(sn_id=res_id)
debeltrami1753a592020-05-11 18:27:30 +0000938 elif res["type"] == "dissociate_security_service":
939 sn_id = res["extra_params"]["share_network_id"]
940 client.remove_sec_service_from_share_network(
941 sn_id=sn_id, ss_id=res_id
942 )
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200943 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200944 client.delete_security_service(res_id)
945 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200946 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200947 client.delete_share_type(res_id)
948 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200949 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400950 client.delete_share_group(res_id)
951 client.wait_for_resource_deletion(
952 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200953 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400954 client.delete_share_group_type(res_id)
955 client.wait_for_resource_deletion(
956 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200957 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400958 client.delete_share_group_snapshot(res_id)
959 client.wait_for_resource_deletion(
960 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200961 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400962 client.delete_share_replica(res_id)
963 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200964 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300965 sn_id = res["extra_params"]["share_network_id"]
966 client.delete_subnet(sn_id, res_id)
967 client.wait_for_resource_deletion(
968 share_network_subnet_id=res_id,
969 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700970 elif res["type"] == "quotas":
971 user_id = res.get('user_id')
972 client.reset_quotas(res_id, user_id=user_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200973 else:
huayue97bacbf2016-01-04 09:57:39 +0800974 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800975 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200976 res["deleted"] = True
977
978 @classmethod
979 def generate_share_network_data(self):
980 data = {
981 "name": data_utils.rand_name("sn-name"),
982 "description": data_utils.rand_name("sn-desc"),
983 "neutron_net_id": data_utils.rand_name("net-id"),
984 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
985 }
986 return data
987
988 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300989 def generate_subnet_data(self):
990 data = {
991 "neutron_net_id": data_utils.rand_name("net-id"),
992 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
993 }
994 return data
995
996 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100997 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200998 data = {
999 "name": data_utils.rand_name("ss-name"),
1000 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001001 "dns_ip": utils.rand_ip(),
1002 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001003 "domain": data_utils.rand_name("ss-domain"),
1004 "user": data_utils.rand_name("ss-user"),
1005 "password": data_utils.rand_name("ss-password"),
1006 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001007 if set_ou:
1008 data["ou"] = data_utils.rand_name("ss-ou")
1009
Marc Koderer0abc93b2015-07-15 09:18:35 +02001010 return data
1011
1012 # Useful assertions
1013 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1014 """Assert two dicts are equivalent.
1015
1016 This is a 'deep' match in the sense that it handles nested
1017 dictionaries appropriately.
1018
1019 NOTE:
1020
1021 If you don't care (or don't know) a given value, you can specify
1022 the string DONTCARE as the value. This will cause that dict-item
1023 to be skipped.
1024
1025 """
1026 def raise_assertion(msg):
1027 d1str = str(d1)
1028 d2str = str(d2)
1029 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1030 'd2: %(d2str)s' %
1031 {"msg": msg, "d1str": d1str, "d2str": d2str})
1032 raise AssertionError(base_msg)
1033
1034 d1keys = set(d1.keys())
1035 d2keys = set(d2.keys())
1036 if d1keys != d2keys:
1037 d1only = d1keys - d2keys
1038 d2only = d2keys - d1keys
1039 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1040 'Keys in d2 and not d1: %(d2only)s' %
1041 {"d1only": d1only, "d2only": d2only})
1042
1043 for key in d1keys:
1044 d1value = d1[key]
1045 d2value = d2[key]
1046 try:
1047 error = abs(float(d1value) - float(d2value))
1048 within_tolerance = error <= tolerance
1049 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001050 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001051 # ValueError if arg is a str, TypeError if it's something else
1052 # (like None)
1053 within_tolerance = False
1054
1055 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1056 self.assertDictMatch(d1value, d2value)
1057 elif 'DONTCARE' in (d1value, d2value):
1058 continue
1059 elif approx_equal and within_tolerance:
1060 continue
1061 elif d1value != d2value:
1062 raise_assertion("d1['%(key)s']=%(d1value)s != "
1063 "d2['%(key)s']=%(d2value)s" %
1064 {
1065 "key": key,
1066 "d1value": d1value,
1067 "d2value": d2value
1068 })
1069
Alex Meadeba8a1602016-05-06 09:33:09 -04001070 def create_user_message(self):
1071 """Trigger a 'no valid host' situation to generate a message."""
1072 extra_specs = {
1073 'vendor_name': 'foobar',
1074 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1075 }
1076 share_type_name = data_utils.rand_name("share-type")
1077
1078 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001079 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001080 name=share_type_name,
1081 extra_specs=extra_specs)['share_type']
1082
1083 params = {'share_type_id': bogus_type['id'],
1084 'share_network_id': self.shares_v2_client.share_network_id}
1085 share = self.shares_v2_client.create_share(**params)
1086 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1087 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1088 return self.shares_v2_client.wait_for_message(share['id'])
1089
lkuchlan5af7cb42020-07-14 18:05:09 +03001090 def allow_access(self, share_id, client=None, access_type=None,
1091 access_level='rw', access_to=None, status='active',
1092 raise_rule_in_error_state=True, cleanup=True):
1093
1094 client = client or self.shares_v2_client
1095 a_type, a_to = self._get_access_rule_data_from_config()
1096 access_type = access_type or a_type
1097 access_to = access_to or a_to
1098
1099 rule = client.create_access_rule(share_id, access_type, access_to,
1100 access_level)
1101 client.wait_for_access_rule_status(share_id, rule['id'], status,
1102 raise_rule_in_error_state)
1103 if cleanup:
1104 self.addCleanup(client.wait_for_resource_deletion,
1105 rule_id=rule['id'], share_id=share_id)
1106 self.addCleanup(client.delete_access_rule, share_id, rule['id'])
1107 return rule
1108
Marc Koderer0abc93b2015-07-15 09:18:35 +02001109
1110class BaseSharesAltTest(BaseSharesTest):
1111 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001112 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001113
1114
1115class BaseSharesAdminTest(BaseSharesTest):
1116 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001117 credentials = ('admin', )
1118
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001119 @classmethod
1120 def setup_clients(cls):
1121 super(BaseSharesAdminTest, cls).setup_clients()
1122 # Initialise share clients
1123 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1124
1125 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001126 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001127 name = data_utils.rand_name("unique_st_name")
1128 extra_specs = cls.add_extra_specs_to_dict(specs)
1129 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001130 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001131 client=cls.admin_shares_v2_client)['share_type']
1132
1133 @classmethod
1134 def _create_share_group_type(cls):
1135 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1136 return cls.create_share_group_type(
1137 name=share_group_type_name, share_types=[cls.share_type_id],
1138 client=cls.admin_shares_v2_client)
1139
Lucio Seki37056942019-01-24 15:40:20 -02001140 def _create_share_for_manage(self):
1141 creation_data = {
1142 'share_type_id': self.st['share_type']['id'],
1143 'share_protocol': self.protocol,
1144 }
1145
1146 share = self.create_share(**creation_data)
1147 share = self.shares_v2_client.get_share(share['id'])
1148
1149 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1150 el = self.shares_v2_client.list_share_export_locations(share["id"])
1151 share["export_locations"] = el
1152
1153 return share
1154
1155 def _unmanage_share_and_wait(self, share):
1156 self.shares_v2_client.unmanage_share(share['id'])
1157 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1158
1159 def _reset_state_and_delete_share(self, share):
1160 self.shares_v2_client.reset_state(share['id'])
1161 self._delete_share_and_wait(share)
1162
1163 def _delete_snapshot_and_wait(self, snap):
1164 self.shares_v2_client.delete_snapshot(snap['id'])
1165 self.shares_v2_client.wait_for_resource_deletion(
1166 snapshot_id=snap['id']
1167 )
1168 self.assertRaises(exceptions.NotFound,
1169 self.shares_v2_client.get_snapshot,
1170 snap['id'])
1171
1172 def _delete_share_and_wait(self, share):
1173 self.shares_v2_client.delete_share(share['id'])
1174 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1175 self.assertRaises(exceptions.NotFound,
1176 self.shares_v2_client.get_share,
1177 share['id'])
1178
1179 def _manage_share(self, share, name, description, share_server_id):
1180 managed_share = self.shares_v2_client.manage_share(
1181 service_host=share['host'],
1182 export_path=share['export_locations'][0],
1183 protocol=share['share_proto'],
1184 share_type_id=self.share_type['share_type']['id'],
1185 name=name,
1186 description=description,
1187 share_server_id=share_server_id
1188 )
1189 self.shares_v2_client.wait_for_share_status(
1190 managed_share['id'], constants.STATUS_AVAILABLE
1191 )
1192
1193 return managed_share
1194
1195 def _unmanage_share_server_and_wait(self, server):
1196 self.shares_v2_client.unmanage_share_server(server['id'])
1197 self.shares_v2_client.wait_for_resource_deletion(
1198 server_id=server['id']
1199 )
1200
1201 def _manage_share_server(self, share_server, fields=None):
1202 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001203 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001204 managed_share_server = self.shares_v2_client.manage_share_server(
1205 params.get('host', share_server['host']),
1206 params.get('share_network_id', share_server['share_network_id']),
1207 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001208 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001209 )
1210 self.shares_v2_client.wait_for_share_server_status(
1211 managed_share_server['id'],
1212 constants.SERVER_STATE_ACTIVE,
1213 )
1214
1215 return managed_share_server
1216
1217 def _delete_share_server_and_wait(self, share_server_id):
1218 self.shares_v2_client.delete_share_server(
1219 share_server_id
1220 )
1221 self.shares_v2_client.wait_for_resource_deletion(
1222 server_id=share_server_id)
1223
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001224
1225class BaseSharesMixedTest(BaseSharesTest):
1226 """Base test case class for all Shares API tests with all user roles."""
1227 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001228
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001229 # Will be cleaned up in resource_cleanup if the class
1230 class_project_users_created = []
1231
1232 @classmethod
1233 def resource_cleanup(cls):
1234 cls.clear_project_users(cls.class_project_users_created)
1235 super(BaseSharesMixedTest, cls).resource_cleanup()
1236
1237 @classmethod
1238 def clear_project_users(cls, users=None):
1239 users = users or cls.class_project_users_created
1240 for user in users:
1241 with handle_cleanup_exceptions():
1242 cls.os_admin.creds_client.delete_user(user['id'])
1243
Marc Koderer0abc93b2015-07-15 09:18:35 +02001244 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001245 def setup_clients(cls):
1246 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001247 # Initialise share clients
1248 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1249 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1250 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1251 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1252 # Initialise network clients
1253 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1254 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001255 # Initialise identity clients
1256 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1257 identity_clients = getattr(
1258 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1259 cls.os_admin.identity_client = identity_clients.IdentityClient()
1260 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1261 cls.os_admin.users_client = identity_clients.UsersClient()
1262 cls.os_admin.roles_client = identity_clients.RolesClient()
1263 cls.os_admin.domains_client = (
1264 cls.os_admin.identity_v3.DomainsClient() if
1265 CONF.identity.auth_version == 'v3' else None)
1266 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001267
1268 if CONF.share.multitenancy_enabled:
1269 admin_share_network_id = cls.provide_share_network(
1270 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1271 cls.admin_shares_client.share_network_id = admin_share_network_id
1272 cls.admin_shares_v2_client.share_network_id = (
1273 admin_share_network_id)
1274
1275 alt_share_network_id = cls.provide_share_network(
1276 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1277 cls.alt_shares_client.share_network_id = alt_share_network_id
1278 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001279
1280 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001281 def create_user_and_get_client(cls, project=None):
1282 """Create a user in specified project & set share clients for user
1283
1284 The user will have all roles specified in tempest.conf
1285 :param: project: a dictionary with project ID and name, if not
1286 specified, the value will be cls.admin_project
1287 """
1288 project_domain_name = (
1289 cls.os_admin.identity_client.auth_provider.credentials.get(
1290 'project_domain_name', 'Default'))
1291 cls.os_admin.creds_client = cred_client.get_creds_client(
1292 cls.os_admin.identity_client, cls.os_admin.projects_client,
1293 cls.os_admin.users_client, cls.os_admin.roles_client,
1294 cls.os_admin.domains_client, project_domain_name)
1295
1296 # User info
1297 project = project or cls.admin_project
1298 username = data_utils.rand_name('manila_%s' % project['id'])
1299 password = data_utils.rand_password()
1300 email = '%s@example.org' % username
1301
1302 user = cls.os_admin.creds_client.create_user(
1303 username, password, project, email)
1304 cls.class_project_users_created.append(user)
1305
1306 for conf_role in CONF.auth.tempest_roles:
1307 cls.os_admin.creds_client.assign_user_role(
1308 user, project, conf_role)
1309
1310 user_creds = cls.os_admin.creds_client.get_credentials(
1311 user, project, password)
1312 os = clients.Clients(user_creds)
1313 os.shares_v1_client = os.share_v1.SharesClient()
1314 os.shares_v2_client = os.share_v2.SharesV2Client()
1315 return os
1316
1317 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001318 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001319 name = data_utils.rand_name("unique_st_name")
1320 extra_specs = cls.add_extra_specs_to_dict(specs)
1321 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001322 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001323 client=cls.admin_shares_v2_client)['share_type']
1324
1325 @classmethod
1326 def _create_share_group_type(cls):
1327 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1328 return cls.create_share_group_type(
1329 name=share_group_type_name, share_types=[cls.share_type_id],
1330 client=cls.admin_shares_v2_client)