blob: 717228cded2277898632a39348cfc5239e11c359 [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
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"
lkuchlan5af7cb42020-07-14 18:05:09 +0300741 access_to = data_utils.rand_name(
742 cls.__class__.__name__ + '-cephx-id')
yogeshdb32f462016-09-28 15:09:50 -0400743 else:
744 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700745 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400746
747 return access_type, access_to
748
Yogeshbdb88102015-09-29 23:41:02 -0400749 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200750 def create_share_network(cls, client=None,
debeltrami1753a592020-05-11 18:27:30 +0000751 cleanup_in_class=False,
752 add_security_services=True, **kwargs):
753
Marc Koderer0abc93b2015-07-15 09:18:35 +0200754 if client is None:
755 client = cls.shares_client
756 share_network = client.create_share_network(**kwargs)
757 resource = {
758 "type": "share_network",
759 "id": share_network["id"],
760 "client": client,
761 }
debeltrami1753a592020-05-11 18:27:30 +0000762
Marc Koderer0abc93b2015-07-15 09:18:35 +0200763 if cleanup_in_class:
764 cls.class_resources.insert(0, resource)
765 else:
766 cls.method_resources.insert(0, resource)
debeltrami1753a592020-05-11 18:27:30 +0000767
768 if add_security_services:
769 cls.provide_and_associate_security_services(
770 client, share_network["id"], cleanup_in_class=cleanup_in_class)
771
Marc Koderer0abc93b2015-07-15 09:18:35 +0200772 return share_network
773
774 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000775 def create_share_network_subnet(cls,
776 client=None,
777 cleanup_in_class=False,
778 **kwargs):
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300779 if client is None:
780 client = cls.shares_v2_client
781 share_network_subnet = client.create_subnet(**kwargs)
782 resource = {
783 "type": "share-network-subnet",
784 "id": share_network_subnet["id"],
785 "extra_params": {
786 "share_network_id": share_network_subnet["share_network_id"]
787 },
788 "client": client,
789 }
790 if cleanup_in_class:
791 cls.class_resources.insert(0, resource)
792 else:
793 cls.method_resources.insert(0, resource)
794 return share_network_subnet
795
796 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200797 def create_security_service(cls, ss_type="ldap", client=None,
798 cleanup_in_class=False, **kwargs):
799 if client is None:
800 client = cls.shares_client
801 security_service = client.create_security_service(ss_type, **kwargs)
802 resource = {
803 "type": "security_service",
804 "id": security_service["id"],
805 "client": client,
806 }
807 if cleanup_in_class:
808 cls.class_resources.insert(0, resource)
809 else:
810 cls.method_resources.insert(0, resource)
811 return security_service
812
813 @classmethod
814 def create_share_type(cls, name, is_public=True, client=None,
815 cleanup_in_class=True, **kwargs):
816 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200817 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200818 share_type = client.create_share_type(name, is_public, **kwargs)
819 resource = {
820 "type": "share_type",
821 "id": share_type["share_type"]["id"],
822 "client": client,
823 }
824 if cleanup_in_class:
825 cls.class_resources.insert(0, resource)
826 else:
827 cls.method_resources.insert(0, resource)
828 return share_type
829
haixin0d1d29f2019-08-02 16:50:45 +0800830 @classmethod
831 def update_share_type(cls, share_type_id, name=None,
832 is_public=None, description=None,
833 client=None):
834 if client is None:
835 client = cls.shares_v2_client
836 share_type = client.update_share_type(share_type_id, name,
837 is_public, description)
838 return share_type
839
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700840 @classmethod
841 def update_quotas(cls, project_id, user_id=None, cleanup=True,
842 client=None, **kwargs):
843 client = client or cls.shares_v2_client
844 updated_quotas = client.update_quotas(project_id,
845 user_id=user_id,
846 **kwargs)
847 resource = {
848 "type": "quotas",
849 "id": project_id,
850 "client": client,
851 "user_id": user_id,
852 }
853 if cleanup:
854 cls.method_resources.insert(0, resource)
855 return updated_quotas
856
Marc Koderer0abc93b2015-07-15 09:18:35 +0200857 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400858 def add_extra_specs_to_dict(extra_specs=None):
859 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300860 dhss = six.text_type(CONF.share.multitenancy_enabled)
861 snapshot_support = six.text_type(
862 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400863 create_from_snapshot_support = six.text_type(
864 CONF.share.capability_create_share_from_snapshot_support)
865
866 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300867 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200868 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400869
870 optional = {
871 "snapshot_support": snapshot_support,
872 "create_share_from_snapshot_support": create_from_snapshot_support,
873 }
874 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
875 # required extra-spec
876 extra_specs_dict.update(optional)
877
Marc Koderer0abc93b2015-07-15 09:18:35 +0200878 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400879 extra_specs_dict.update(extra_specs)
880
881 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200882
883 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400884 def clear_share_replicas(cls, share_id, client=None):
885 client = client or cls.shares_v2_client
886 share_replicas = client.list_share_replicas(
887 share_id=share_id)
888
889 for replica in share_replicas:
890 try:
891 cls.delete_share_replica(replica['id'])
892 except exceptions.BadRequest:
893 # Ignore the exception due to deletion of last active replica
894 pass
895
896 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200897 def clear_resources(cls, resources=None):
898 """Deletes resources, that were created in test suites.
899
900 This method tries to remove resources from resource list,
901 if it is not found, assumed it was deleted in test itself.
902 It is expected, that all resources were added as LIFO
903 due to restriction of deletion resources, that is in the chain.
904
905 :param resources: dict with keys 'type','id','client' and 'deleted'
906 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200907 if resources is None:
908 resources = cls.method_resources
909 for res in resources:
910 if "deleted" not in res.keys():
911 res["deleted"] = False
912 if "client" not in res.keys():
913 res["client"] = cls.shares_client
914 if not(res["deleted"]):
915 res_id = res['id']
916 client = res["client"]
917 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200918 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400919 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400920 share_group_id = res.get('share_group_id')
921 if share_group_id:
922 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400923 client.delete_share(res_id, params=params)
924 else:
925 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200926 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200927 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200928 client.delete_snapshot(res_id)
929 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200930 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400931 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400932 client.delete_share_network(res_id)
933 client.wait_for_resource_deletion(sn_id=res_id)
debeltrami1753a592020-05-11 18:27:30 +0000934 elif res["type"] == "dissociate_security_service":
935 sn_id = res["extra_params"]["share_network_id"]
936 client.remove_sec_service_from_share_network(
937 sn_id=sn_id, ss_id=res_id
938 )
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200939 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200940 client.delete_security_service(res_id)
941 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200942 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200943 client.delete_share_type(res_id)
944 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200945 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400946 client.delete_share_group(res_id)
947 client.wait_for_resource_deletion(
948 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200949 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400950 client.delete_share_group_type(res_id)
951 client.wait_for_resource_deletion(
952 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200953 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400954 client.delete_share_group_snapshot(res_id)
955 client.wait_for_resource_deletion(
956 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200957 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400958 client.delete_share_replica(res_id)
959 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200960 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300961 sn_id = res["extra_params"]["share_network_id"]
962 client.delete_subnet(sn_id, res_id)
963 client.wait_for_resource_deletion(
964 share_network_subnet_id=res_id,
965 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700966 elif res["type"] == "quotas":
967 user_id = res.get('user_id')
968 client.reset_quotas(res_id, user_id=user_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200969 else:
huayue97bacbf2016-01-04 09:57:39 +0800970 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800971 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200972 res["deleted"] = True
973
974 @classmethod
975 def generate_share_network_data(self):
976 data = {
977 "name": data_utils.rand_name("sn-name"),
978 "description": data_utils.rand_name("sn-desc"),
979 "neutron_net_id": data_utils.rand_name("net-id"),
980 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
981 }
982 return data
983
984 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300985 def generate_subnet_data(self):
986 data = {
987 "neutron_net_id": data_utils.rand_name("net-id"),
988 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
989 }
990 return data
991
992 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100993 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200994 data = {
995 "name": data_utils.rand_name("ss-name"),
996 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200997 "dns_ip": utils.rand_ip(),
998 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200999 "domain": data_utils.rand_name("ss-domain"),
1000 "user": data_utils.rand_name("ss-user"),
1001 "password": data_utils.rand_name("ss-password"),
1002 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001003 if set_ou:
1004 data["ou"] = data_utils.rand_name("ss-ou")
1005
Marc Koderer0abc93b2015-07-15 09:18:35 +02001006 return data
1007
1008 # Useful assertions
1009 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1010 """Assert two dicts are equivalent.
1011
1012 This is a 'deep' match in the sense that it handles nested
1013 dictionaries appropriately.
1014
1015 NOTE:
1016
1017 If you don't care (or don't know) a given value, you can specify
1018 the string DONTCARE as the value. This will cause that dict-item
1019 to be skipped.
1020
1021 """
1022 def raise_assertion(msg):
1023 d1str = str(d1)
1024 d2str = str(d2)
1025 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1026 'd2: %(d2str)s' %
1027 {"msg": msg, "d1str": d1str, "d2str": d2str})
1028 raise AssertionError(base_msg)
1029
1030 d1keys = set(d1.keys())
1031 d2keys = set(d2.keys())
1032 if d1keys != d2keys:
1033 d1only = d1keys - d2keys
1034 d2only = d2keys - d1keys
1035 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1036 'Keys in d2 and not d1: %(d2only)s' %
1037 {"d1only": d1only, "d2only": d2only})
1038
1039 for key in d1keys:
1040 d1value = d1[key]
1041 d2value = d2[key]
1042 try:
1043 error = abs(float(d1value) - float(d2value))
1044 within_tolerance = error <= tolerance
1045 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001046 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001047 # ValueError if arg is a str, TypeError if it's something else
1048 # (like None)
1049 within_tolerance = False
1050
1051 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1052 self.assertDictMatch(d1value, d2value)
1053 elif 'DONTCARE' in (d1value, d2value):
1054 continue
1055 elif approx_equal and within_tolerance:
1056 continue
1057 elif d1value != d2value:
1058 raise_assertion("d1['%(key)s']=%(d1value)s != "
1059 "d2['%(key)s']=%(d2value)s" %
1060 {
1061 "key": key,
1062 "d1value": d1value,
1063 "d2value": d2value
1064 })
1065
Alex Meadeba8a1602016-05-06 09:33:09 -04001066 def create_user_message(self):
1067 """Trigger a 'no valid host' situation to generate a message."""
1068 extra_specs = {
1069 'vendor_name': 'foobar',
1070 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1071 }
1072 share_type_name = data_utils.rand_name("share-type")
1073
1074 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001075 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001076 name=share_type_name,
1077 extra_specs=extra_specs)['share_type']
1078
1079 params = {'share_type_id': bogus_type['id'],
1080 'share_network_id': self.shares_v2_client.share_network_id}
1081 share = self.shares_v2_client.create_share(**params)
1082 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1083 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1084 return self.shares_v2_client.wait_for_message(share['id'])
1085
lkuchlan5af7cb42020-07-14 18:05:09 +03001086 def allow_access(self, share_id, client=None, access_type=None,
1087 access_level='rw', access_to=None, status='active',
1088 raise_rule_in_error_state=True, cleanup=True):
1089
1090 client = client or self.shares_v2_client
1091 a_type, a_to = self._get_access_rule_data_from_config()
1092 access_type = access_type or a_type
1093 access_to = access_to or a_to
1094
1095 rule = client.create_access_rule(share_id, access_type, access_to,
1096 access_level)
1097 client.wait_for_access_rule_status(share_id, rule['id'], status,
1098 raise_rule_in_error_state)
1099 if cleanup:
1100 self.addCleanup(client.wait_for_resource_deletion,
1101 rule_id=rule['id'], share_id=share_id)
1102 self.addCleanup(client.delete_access_rule, share_id, rule['id'])
1103 return rule
1104
Marc Koderer0abc93b2015-07-15 09:18:35 +02001105
1106class BaseSharesAltTest(BaseSharesTest):
1107 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001108 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001109
1110
1111class BaseSharesAdminTest(BaseSharesTest):
1112 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001113 credentials = ('admin', )
1114
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001115 @classmethod
1116 def setup_clients(cls):
1117 super(BaseSharesAdminTest, cls).setup_clients()
1118 # Initialise share clients
1119 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1120
1121 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001122 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001123 name = data_utils.rand_name("unique_st_name")
1124 extra_specs = cls.add_extra_specs_to_dict(specs)
1125 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001126 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001127 client=cls.admin_shares_v2_client)['share_type']
1128
1129 @classmethod
1130 def _create_share_group_type(cls):
1131 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1132 return cls.create_share_group_type(
1133 name=share_group_type_name, share_types=[cls.share_type_id],
1134 client=cls.admin_shares_v2_client)
1135
Lucio Seki37056942019-01-24 15:40:20 -02001136 def _create_share_for_manage(self):
1137 creation_data = {
1138 'share_type_id': self.st['share_type']['id'],
1139 'share_protocol': self.protocol,
1140 }
1141
1142 share = self.create_share(**creation_data)
1143 share = self.shares_v2_client.get_share(share['id'])
1144
1145 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1146 el = self.shares_v2_client.list_share_export_locations(share["id"])
1147 share["export_locations"] = el
1148
1149 return share
1150
1151 def _unmanage_share_and_wait(self, share):
1152 self.shares_v2_client.unmanage_share(share['id'])
1153 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1154
1155 def _reset_state_and_delete_share(self, share):
1156 self.shares_v2_client.reset_state(share['id'])
1157 self._delete_share_and_wait(share)
1158
1159 def _delete_snapshot_and_wait(self, snap):
1160 self.shares_v2_client.delete_snapshot(snap['id'])
1161 self.shares_v2_client.wait_for_resource_deletion(
1162 snapshot_id=snap['id']
1163 )
1164 self.assertRaises(exceptions.NotFound,
1165 self.shares_v2_client.get_snapshot,
1166 snap['id'])
1167
1168 def _delete_share_and_wait(self, share):
1169 self.shares_v2_client.delete_share(share['id'])
1170 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1171 self.assertRaises(exceptions.NotFound,
1172 self.shares_v2_client.get_share,
1173 share['id'])
1174
1175 def _manage_share(self, share, name, description, share_server_id):
1176 managed_share = self.shares_v2_client.manage_share(
1177 service_host=share['host'],
1178 export_path=share['export_locations'][0],
1179 protocol=share['share_proto'],
1180 share_type_id=self.share_type['share_type']['id'],
1181 name=name,
1182 description=description,
1183 share_server_id=share_server_id
1184 )
1185 self.shares_v2_client.wait_for_share_status(
1186 managed_share['id'], constants.STATUS_AVAILABLE
1187 )
1188
1189 return managed_share
1190
1191 def _unmanage_share_server_and_wait(self, server):
1192 self.shares_v2_client.unmanage_share_server(server['id'])
1193 self.shares_v2_client.wait_for_resource_deletion(
1194 server_id=server['id']
1195 )
1196
1197 def _manage_share_server(self, share_server, fields=None):
1198 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001199 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001200 managed_share_server = self.shares_v2_client.manage_share_server(
1201 params.get('host', share_server['host']),
1202 params.get('share_network_id', share_server['share_network_id']),
1203 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001204 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001205 )
1206 self.shares_v2_client.wait_for_share_server_status(
1207 managed_share_server['id'],
1208 constants.SERVER_STATE_ACTIVE,
1209 )
1210
1211 return managed_share_server
1212
1213 def _delete_share_server_and_wait(self, share_server_id):
1214 self.shares_v2_client.delete_share_server(
1215 share_server_id
1216 )
1217 self.shares_v2_client.wait_for_resource_deletion(
1218 server_id=share_server_id)
1219
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001220
1221class BaseSharesMixedTest(BaseSharesTest):
1222 """Base test case class for all Shares API tests with all user roles."""
1223 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001224
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001225 # Will be cleaned up in resource_cleanup if the class
1226 class_project_users_created = []
1227
1228 @classmethod
1229 def resource_cleanup(cls):
1230 cls.clear_project_users(cls.class_project_users_created)
1231 super(BaseSharesMixedTest, cls).resource_cleanup()
1232
1233 @classmethod
1234 def clear_project_users(cls, users=None):
1235 users = users or cls.class_project_users_created
1236 for user in users:
1237 with handle_cleanup_exceptions():
1238 cls.os_admin.creds_client.delete_user(user['id'])
1239
Marc Koderer0abc93b2015-07-15 09:18:35 +02001240 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001241 def setup_clients(cls):
1242 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001243 # Initialise share clients
1244 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1245 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1246 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1247 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1248 # Initialise network clients
1249 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1250 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001251 # Initialise identity clients
1252 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1253 identity_clients = getattr(
1254 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1255 cls.os_admin.identity_client = identity_clients.IdentityClient()
1256 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1257 cls.os_admin.users_client = identity_clients.UsersClient()
1258 cls.os_admin.roles_client = identity_clients.RolesClient()
1259 cls.os_admin.domains_client = (
1260 cls.os_admin.identity_v3.DomainsClient() if
1261 CONF.identity.auth_version == 'v3' else None)
1262 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001263
1264 if CONF.share.multitenancy_enabled:
1265 admin_share_network_id = cls.provide_share_network(
1266 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1267 cls.admin_shares_client.share_network_id = admin_share_network_id
1268 cls.admin_shares_v2_client.share_network_id = (
1269 admin_share_network_id)
1270
1271 alt_share_network_id = cls.provide_share_network(
1272 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1273 cls.alt_shares_client.share_network_id = alt_share_network_id
1274 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001275
1276 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001277 def create_user_and_get_client(cls, project=None):
1278 """Create a user in specified project & set share clients for user
1279
1280 The user will have all roles specified in tempest.conf
1281 :param: project: a dictionary with project ID and name, if not
1282 specified, the value will be cls.admin_project
1283 """
1284 project_domain_name = (
1285 cls.os_admin.identity_client.auth_provider.credentials.get(
1286 'project_domain_name', 'Default'))
1287 cls.os_admin.creds_client = cred_client.get_creds_client(
1288 cls.os_admin.identity_client, cls.os_admin.projects_client,
1289 cls.os_admin.users_client, cls.os_admin.roles_client,
1290 cls.os_admin.domains_client, project_domain_name)
1291
1292 # User info
1293 project = project or cls.admin_project
1294 username = data_utils.rand_name('manila_%s' % project['id'])
1295 password = data_utils.rand_password()
1296 email = '%s@example.org' % username
1297
1298 user = cls.os_admin.creds_client.create_user(
1299 username, password, project, email)
1300 cls.class_project_users_created.append(user)
1301
1302 for conf_role in CONF.auth.tempest_roles:
1303 cls.os_admin.creds_client.assign_user_role(
1304 user, project, conf_role)
1305
1306 user_creds = cls.os_admin.creds_client.get_credentials(
1307 user, project, password)
1308 os = clients.Clients(user_creds)
1309 os.shares_v1_client = os.share_v1.SharesClient()
1310 os.shares_v2_client = os.share_v2.SharesV2Client()
1311 return os
1312
1313 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001314 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001315 name = data_utils.rand_name("unique_st_name")
1316 extra_specs = cls.add_extra_specs_to_dict(specs)
1317 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001318 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001319 client=cls.admin_shares_v2_client)['share_type']
1320
1321 @classmethod
1322 def _create_share_group_type(cls):
1323 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1324 return cls.create_share_group_type(
1325 name=share_group_type_name, share_types=[cls.share_type_id],
1326 client=cls.admin_shares_v2_client)