blob: fd8f533935e963a0b368b6f6184ca6c93b6f826c [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
lkuchlan540e74a2021-01-19 18:08:25 +020030from manila_tempest_tests.common import waiters
Marc Koderer0abc93b2015-07-15 09:18:35 +020031from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020032from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020033
lkuchlan1d1461d2020-08-04 11:19:11 +030034
Marc Koderer0abc93b2015-07-15 09:18:35 +020035CONF = config.CONF
36LOG = log.getLogger(__name__)
37
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030038# Test tags related to test direction
39TAG_POSITIVE = "positive"
40TAG_NEGATIVE = "negative"
41
42# Test tags related to service involvement
Tom Barron69f96962019-07-29 17:07:03 -040043# Only requires that manila-api service running.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030044TAG_API = "api"
Tom Barron69f96962019-07-29 17:07:03 -040045# Requires all manila services running, intended to test back-end
46# (manila-share) behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030047TAG_BACKEND = "backend"
Tom Barron69f96962019-07-29 17:07:03 -040048# Requires all manila services running, intended to test API behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030049TAG_API_WITH_BACKEND = "api_with_backend"
50
51TAGS_MAPPER = {
52 "p": TAG_POSITIVE,
53 "n": TAG_NEGATIVE,
54 "a": TAG_API,
55 "b": TAG_BACKEND,
56 "ab": TAG_API_WITH_BACKEND,
57}
58TAGS_PATTERN = re.compile(
59 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
60 TAGS_MAPPER)
61
62
63def verify_test_has_appropriate_tags(self):
64 if not TAGS_PATTERN.match(self.id()):
65 msg = (
66 "Required attributes either not set or set improperly. "
67 "Two test attributes are expected:\n"
68 " - one of '%(p)s' or '%(n)s' and \n"
69 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
70 ) % TAGS_MAPPER
71 raise self.failureException(msg)
72
Marc Koderer0abc93b2015-07-15 09:18:35 +020073
74class handle_cleanup_exceptions(object):
75 """Handle exceptions raised with cleanup operations.
76
77 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
78 are raised.
79 Suppress all other exceptions only in case config opt
80 'suppress_errors_in_cleanup' in config group 'share' is True.
81 """
82
83 def __enter__(self):
84 return self
85
86 def __exit__(self, exc_type, exc_value, exc_traceback):
87 if not (isinstance(exc_value,
88 (exceptions.NotFound, exceptions.Forbidden)) or
89 CONF.share.suppress_errors_in_cleanup):
90 return False # Do not suppress error if any
91 if exc_traceback:
92 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080093 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020094 return True # Suppress error if any
95
96
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020097skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -050098skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +020099
100
Marc Koderer0abc93b2015-07-15 09:18:35 +0200101class BaseSharesTest(test.BaseTestCase):
102 """Base test case class for all Manila API tests."""
103
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300104 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200105 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200106 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200107
108 # Will be cleaned up in resource_cleanup
109 class_resources = []
110
111 # Will be cleaned up in tearDown method
112 method_resources = []
113
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100114 # NOTE(andreaf) Override the client manager class to be used, so that
115 # a stable class is used, which includes plugin registered services as well
116 client_manager = clients.Clients
117
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200118 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200119 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200120 raise self.skipException(
121 "Microversion '%s' is not supported." % microversion)
122
Xing Yang69b00b52015-11-22 16:10:44 -0500123 def skip_if_microversion_lt(self, microversion):
124 if utils.is_microversion_lt(CONF.share.max_api_microversion,
125 microversion):
126 raise self.skipException(
127 "Microversion must be greater than or equal to '%s'." %
128 microversion)
129
Marc Koderer0abc93b2015-07-15 09:18:35 +0200130 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000131 def skip_checks(cls):
132 super(BaseSharesTest, cls).skip_checks()
133 if not CONF.service_available.manila:
134 raise cls.skipException("Manila support is required")
lkuchlana3b6f7a2020-01-07 10:45:45 +0200135 if not any(p in CONF.share.enable_protocols for p in cls.protocols):
136 skip_msg = "%s tests are disabled" % CONF.share.enable_protocols
137 raise cls.skipException(skip_msg)
Daniel Melladoe5269142017-01-12 12:17:58 +0000138
139 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200140 def verify_nonempty(cls, *args):
141 if not all(args):
142 msg = "Missing API credentials in configuration."
143 raise cls.skipException(msg)
144
145 @classmethod
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700146 def setup_credentials(cls):
147 # This call is used to tell the credential allocator to create
148 # network resources for this test case. NOTE: it must go before the
149 # super call, to override decisions in the base classes.
150 network_resources = {}
151 if (CONF.share.multitenancy_enabled and
152 CONF.share.create_networks_when_multitenancy_enabled):
153 # We're testing a DHSS=True driver, and manila is configured with
154 # NeutronNetworkPlugin (or a derivative) that supports creating
155 # share networks with project neutron networks, so lets ask for
156 # neutron network resources to be created with test credentials
157 network_resources.update({'network': True,
158 'subnet': True,
159 'router': True})
160 cls.set_network_resources(**network_resources)
161 super(BaseSharesTest, cls).setup_credentials()
162
163 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300164 def setup_clients(cls):
165 super(BaseSharesTest, cls).setup_clients()
166 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100167 # Initialise share clients for test credentials
168 cls.shares_client = os.share_v1.SharesClient()
169 cls.shares_v2_client = os.share_v2.SharesV2Client()
170 # Initialise network clients for test credentials
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700171 cls.networks_client = None
172 cls.subnets_client = None
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100173 if CONF.service_available.neutron:
174 cls.networks_client = os.network.NetworksClient()
175 cls.subnets_client = os.network.SubnetsClient()
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300176
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700177 # If DHSS=True, create a share network and set it in the client
178 # for easy access.
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300179 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300180 if (not CONF.service_available.neutron and
181 CONF.share.create_networks_when_multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700182 raise cls.skipException(
183 "Neutron support is required when "
184 "CONF.share.create_networks_when_multitenancy_enabled "
185 "is set to True")
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300186 share_network_id = cls.provide_share_network(
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700187 cls.shares_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300188 cls.shares_client.share_network_id = share_network_id
189 cls.shares_v2_client.share_network_id = share_network_id
190
Marc Koderer0abc93b2015-07-15 09:18:35 +0200191 def setUp(self):
192 super(BaseSharesTest, self).setUp()
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200193 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300194 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200195
196 @classmethod
197 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200198 cls.clear_resources(cls.class_resources)
Sam Wan241029c2016-07-26 03:37:42 -0400199 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200200
201 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000202 def provide_and_associate_security_services(
203 cls, shares_client, share_network_id, cleanup_in_class=True):
204 """Creates a security service and associates to a share network.
205
206 This method creates security services based on the Multiopt
207 defined in tempest configuration named security_service. When this
208 configuration is not provided, the method will return None.
209 After the security service creation, this method also associates
210 the security service to a share network.
211
212 :param shares_client: shares client, which requires the provisioning
213 :param share_network_id: id of the share network to associate the
214 security service
215 :param cleanup_in_class: if the security service and the association
216 will be removed in the method teardown or class teardown
217 :returns: None -- if the security service configuration is not
218 defined
219 """
220
221 ss_configs = CONF.share.security_service
222 if not ss_configs:
223 return
224
225 for ss_config in ss_configs:
226 ss_name = "ss_autogenerated_by_tempest_%s" % (
227 ss_config.get("ss_type"))
228
229 ss_params = {
230 "name": ss_name,
231 "dns_ip": ss_config.get("ss_dns_ip"),
232 "server": ss_config.get("ss_server"),
233 "domain": ss_config.get("ss_domain"),
234 "user": ss_config.get("ss_user"),
235 "password": ss_config.get("ss_password")
236 }
237 ss_type = ss_config.get("ss_type")
238 security_service = cls.create_security_service(
239 ss_type,
240 client=shares_client,
241 cleanup_in_class=cleanup_in_class,
242 **ss_params)
243
244 cls.add_sec_service_to_share_network(
245 shares_client, share_network_id,
246 security_service["id"],
247 cleanup_in_class=cleanup_in_class)
248
249 @classmethod
250 def add_sec_service_to_share_network(
251 cls, client, share_network_id,
252 security_service_id, cleanup_in_class=True):
253 """Associates a security service to a share network.
254
255 This method associates a security service provided by
256 the security service configuration with a specific
257 share network.
258
259 :param share_network_id: the share network id to be
260 associate with a given security service
261 :param security_service_id: the security service id
262 to be associate with a given share network
263 :param cleanup_in_class: if the resources will be
264 dissociate in the method teardown or class teardown
265 """
266
267 client.add_sec_service_to_share_network(
268 share_network_id,
269 security_service_id)
270 resource = {
271 "type": "dissociate_security_service",
272 "id": security_service_id,
273 "extra_params": {
274 "share_network_id": share_network_id
275 },
276 "client": client,
277 }
278
279 if cleanup_in_class:
280 cls.class_resources.insert(0, resource)
281 else:
282 cls.method_resources.insert(0, resource)
283
284 @classmethod
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200285 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300286 ignore_multitenancy_config=False):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700287 """Get or create share network for DHSS=True drivers
Marc Koderer0abc93b2015-07-15 09:18:35 +0200288
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700289 When testing DHSS=True (multitenancy_enabled) drivers, shares must
290 be requested on share networks.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200291 :returns: str -- share network id for shares_client tenant
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700292 :returns: None -- if single-tenant driver (DHSS=False) is used
Marc Koderer0abc93b2015-07-15 09:18:35 +0200293 """
294
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300295 if (not ignore_multitenancy_config and
296 not CONF.share.multitenancy_enabled):
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700297 # Assumed usage of a single-tenant driver (DHSS=False)
debeltrami1753a592020-05-11 18:27:30 +0000298 return None
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700299
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700300 if shares_client.share_network_id:
301 # Share-network already exists, use it
302 return shares_client.share_network_id
debeltrami1753a592020-05-11 18:27:30 +0000303
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700304 sn_name = "autogenerated_by_tempest"
305 sn_desc = "This share-network was created by tempest"
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300306
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700307 if not CONF.share.create_networks_when_multitenancy_enabled:
308 # We need a new share network, but don't need to associate
309 # any neutron networks to it - this configuration is used
310 # when manila is configured with "StandaloneNetworkPlugin"
311 # or "NeutronSingleNetworkPlugin" where all tenants share
312 # a single backend network where shares are exported.
313 sn = cls.create_share_network(cleanup_in_class=True,
314 client=shares_client,
315 add_security_services=True,
316 name=sn_name,
317 description=sn_desc)
318 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200319
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700320 # Retrieve non-public network list owned by the tenant
321 filters = {'project_id': shares_client.tenant_id,
322 'shared': False}
323 tenant_networks = (
324 networks_client.list_networks(**filters).get('networks', [])
325 )
326 tenant_networks_with_subnet = (
327 [n for n in tenant_networks if n['subnets']]
328 )
debeltrami1753a592020-05-11 18:27:30 +0000329
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700330 if not tenant_networks_with_subnet:
331 # This can only occur if using tempest's pre-provisioned
332 # credentials and not allocating networks to them
333 raise cls.skipException(
334 "Test credentials must provide at least one "
335 "non-shared project network with a valid subnet when "
336 "CONF.share.create_networks_when_multitenancy_enabled is "
337 "set to True.")
debeltrami1753a592020-05-11 18:27:30 +0000338
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700339 net_id = tenant_networks_with_subnet[0]['id']
340 subnet_id = tenant_networks_with_subnet[0]['subnets'][0]
debeltrami1753a592020-05-11 18:27:30 +0000341
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700342 # Create suitable share-network
343 sn = cls.create_share_network(cleanup_in_class=True,
344 client=shares_client,
345 add_security_services=True,
346 name=sn_name,
347 description=sn_desc,
348 neutron_net_id=net_id,
349 neutron_subnet_id=subnet_id)
debeltrami1753a592020-05-11 18:27:30 +0000350
Goutham Pacha Ravi4a0b7322020-06-30 19:42:45 -0700351 return sn['id']
Marc Koderer0abc93b2015-07-15 09:18:35 +0200352
353 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300354 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200355 snapshot_id=None, description=None, metadata=None,
356 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400357 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400358 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300359 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200360 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400361 share_network_id = (share_network_id or
362 CONF.share.share_network_id or
363 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200364 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300365 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400366 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200367 'share_protocol': share_protocol,
368 'size': size,
369 'name': name,
370 'snapshot_id': snapshot_id,
371 'description': description,
372 'metadata': metadata,
373 'share_network_id': share_network_id,
374 'share_type_id': share_type_id,
375 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400376 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400377 if share_group_id:
378 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400379
Marc Koderer0abc93b2015-07-15 09:18:35 +0200380 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400381 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400382 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200383 cleanup_list = (cls.class_resources if cleanup_in_class else
384 cls.method_resources)
385 cleanup_list.insert(0, resource)
386 return share
387
388 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300389 def migrate_share(
390 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200391 force_host_assisted_migration=False, writable=False,
392 nondisruptive=False, preserve_metadata=False,
393 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300394 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400395 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300396 client.migrate_share(
397 share_id, dest_host,
398 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200399 writable=writable, preserve_metadata=preserve_metadata,
400 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300401 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300402 new_share_type_id=new_share_type_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200403 share = waiters.wait_for_migration_status(
404 client, share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200405 return share
406
407 @classmethod
408 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
409 client = client or cls.shares_v2_client
410 client.migration_complete(share_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200411 share = waiters.wait_for_migration_status(
412 client, share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300413 return share
414
415 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300416 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
417 client = client or cls.shares_v2_client
418 client.migration_cancel(share_id, **kwargs)
lkuchlan540e74a2021-01-19 18:08:25 +0200419 share = waiters.wait_for_migration_status(
420 client, share_id, dest_host, 'migration_cancelled', **kwargs)
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300421 return share
422
423 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200424 def create_share(cls, *args, **kwargs):
425 """Create one share and wait for available state. Retry if allowed."""
426 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
427 return result[0]
428
429 @classmethod
430 def create_shares(cls, share_data_list):
431 """Creates several shares in parallel with retries.
432
433 Use this method when you want to create more than one share at same
434 time. Especially if config option 'share.share_creation_retry_number'
435 has value more than zero (0).
436 All shares will be expected to have 'available' status with or without
437 recreation else error will be raised.
438
439 :param share_data_list: list -- list of dictionaries with 'args' and
440 'kwargs' for '_create_share' method of this base class.
441 example of data:
442 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
443 :returns: list -- list of shares created using provided data.
444 """
445
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300446 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200447 if not isinstance(d, dict):
448 raise exceptions.TempestException(
449 "Expected 'dict', got '%s'" % type(d))
450 if "args" not in d:
451 d["args"] = []
452 if "kwargs" not in d:
453 d["kwargs"] = {}
454 if len(d) > 2:
455 raise exceptions.TempestException(
456 "Expected only 'args' and 'kwargs' keys. "
457 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300458
459 data = []
460 for d in share_data_list:
461 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400462 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300463 local_d = {
464 "args": d["args"],
465 "kwargs": copy.deepcopy(d["kwargs"]),
466 }
467 local_d["kwargs"]["client"] = client
468 local_d["share"] = cls._create_share(
469 *local_d["args"], **local_d["kwargs"])
470 local_d["cnt"] = 0
471 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400472 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300473 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200474
475 while not all(d["available"] for d in data):
476 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400477 if not d["wait_for_status"]:
478 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200479 if d["available"]:
480 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300481 client = d["kwargs"]["client"]
482 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200483 try:
lkuchlan540e74a2021-01-19 18:08:25 +0200484 waiters.wait_for_share_status(
485 client, share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200486 d["available"] = True
487 except (share_exceptions.ShareBuildErrorException,
488 exceptions.TimeoutException) as e:
489 if CONF.share.share_creation_retry_number > d["cnt"]:
490 d["cnt"] += 1
491 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300492 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200493 LOG.error(msg)
494 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300495 cg_id = d["kwargs"].get("consistency_group_id")
496 if cg_id:
497 # NOTE(vponomaryov): delete errored share
498 # immediately in case share is part of CG.
499 client.delete_share(
500 share_id,
501 params={"consistency_group_id": cg_id})
502 client.wait_for_resource_deletion(
503 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200504 d["share"] = cls._create_share(
505 *d["args"], **d["kwargs"])
506 else:
gecong197358663802016-08-25 11:08:45 +0800507 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200508
509 return [d["share"] for d in data]
510
511 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400512 def create_share_group(cls, client=None, cleanup_in_class=True,
513 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400514 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400515 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400516 kwargs['share_network_id'] = (share_network_id or
517 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400518 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400519 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400520 "type": "share_group",
521 "id": share_group["id"],
522 "client": client,
523 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400524 if cleanup_in_class:
525 cls.class_resources.insert(0, resource)
526 else:
527 cls.method_resources.insert(0, resource)
528
Andrew Kerrb8436922016-06-01 15:32:43 -0400529 if kwargs.get('source_share_group_snapshot_id'):
530 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400531 detailed=True,
silvacarloss6e575682020-02-18 19:52:35 -0300532 params={'share_group_id': share_group['id']})
Andrew Kerrbf31e912015-07-29 10:39:38 -0400533
Andrew Kerrb8436922016-06-01 15:32:43 -0400534 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400535 resource = {"type": "share",
536 "id": share["id"],
537 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400538 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400539 if cleanup_in_class:
540 cls.class_resources.insert(0, resource)
541 else:
542 cls.method_resources.insert(0, resource)
543
lkuchlan540e74a2021-01-19 18:08:25 +0200544 waiters.wait_for_share_group_status(
545 client, share_group['id'], 'available')
Andrew Kerrb8436922016-06-01 15:32:43 -0400546 return share_group
547
548 @classmethod
549 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
550 group_specs=None, client=None,
551 cleanup_in_class=True, **kwargs):
552 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300553 if (group_specs is None and
554 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300555 group_specs = {
556 'consistent_snapshot_support': (
557 CONF.share.capability_sg_consistent_snapshot_support),
558 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400559 share_group_type = client.create_share_group_type(
560 name=name,
561 share_types=share_types,
562 is_public=is_public,
563 group_specs=group_specs,
564 **kwargs)
565 resource = {
566 "type": "share_group_type",
567 "id": share_group_type["id"],
568 "client": client,
569 }
570 if cleanup_in_class:
571 cls.class_resources.insert(0, resource)
572 else:
573 cls.method_resources.insert(0, resource)
574 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400575
576 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200577 def create_snapshot_wait_for_active(cls, share_id, name=None,
578 description=None, force=False,
579 client=None, cleanup_in_class=True):
580 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400581 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200582 if description is None:
583 description = "Tempest's snapshot"
584 snapshot = client.create_snapshot(share_id, name, description, force)
585 resource = {
586 "type": "snapshot",
587 "id": snapshot["id"],
588 "client": client,
589 }
590 if cleanup_in_class:
591 cls.class_resources.insert(0, resource)
592 else:
593 cls.method_resources.insert(0, resource)
lkuchlan540e74a2021-01-19 18:08:25 +0200594 waiters.wait_for_snapshot_status(client, snapshot["id"], "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200595 return snapshot
596
597 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400598 def create_share_group_snapshot_wait_for_active(
599 cls, share_group_id, name=None, description=None, client=None,
600 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400601 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400602 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400603 description = "Tempest's share group snapshot"
604 sg_snapshot = client.create_share_group_snapshot(
605 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400606 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400607 "type": "share_group_snapshot",
608 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400609 "client": client,
610 }
611 if cleanup_in_class:
612 cls.class_resources.insert(0, resource)
613 else:
614 cls.method_resources.insert(0, resource)
lkuchlan540e74a2021-01-19 18:08:25 +0200615 waiters.wait_for_share_group_snapshot_status(
616 client, sg_snapshot["id"], "available")
Andrew Kerrb8436922016-06-01 15:32:43 -0400617 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400618
619 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800620 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400621 """List the availability zones for "manila-share" services
622
623 that are currently in "up" state.
624 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800625 client = client or cls.admin_shares_v2_client
626 backends = (
627 '|'.join(['^%s$' % backend for backend in backends])
628 if backends else '.*'
629 )
Yogeshbdb88102015-09-29 23:41:02 -0400630 cls.services = client.list_services()
631 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800632 service['binary'] == 'manila-share' and
633 service['state'] == 'up' and
634 re.search(backends, service['host'])]
Douglas Viroel161f1802020-04-25 17:18:14 -0300635 return list(set(zones))
Yogeshbdb88102015-09-29 23:41:02 -0400636
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800637 @classmethod
638 def get_pools_matching_share_type(cls, share_type, client=None):
639 client = client or cls.admin_shares_v2_client
640 if utils.is_microversion_supported('2.23'):
641 return client.list_pools(
andrebeltrami3b4d4852020-02-04 19:11:54 +0000642 detail=True,
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800643 search_opts={'share_type': share_type['id']})['pools']
644
645 pools = client.list_pools(detail=True)['pools']
646 share_type = client.get_share_type(share_type['id'])['share_type']
647 extra_specs = {}
648 for k, v in share_type['extra_specs'].items():
649 extra_specs[k] = (
650 True if six.text_type(v).lower() == 'true'
651 else False if six.text_type(v).lower() == 'false' else v
652 )
653 return [
654 pool for pool in pools if all(y in pool['capabilities'].items()
655 for y in extra_specs.items())
656 ]
657
658 @classmethod
659 def get_availability_zones_matching_share_type(cls, share_type,
660 client=None):
661
662 client = client or cls.admin_shares_v2_client
663 pools_matching_share_type = cls.get_pools_matching_share_type(
664 share_type, client=client)
665 backends_matching_share_type = set(
666 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
667 )
668 azs = cls.get_availability_zones(backends=backends_matching_share_type)
669 return azs
670
Yogesh1f931ff2015-09-29 23:41:02 -0400671 def get_pools_for_replication_domain(self):
672 # Get the list of pools for the replication domain
673 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500674 instance_host = self.admin_client.get_share(
675 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400676 host_pool = [p for p in pools if p['name'] == instance_host][0]
677 rep_domain = host_pool['capabilities']['replication_domain']
678 pools_in_rep_domain = [p for p in pools if p['capabilities'][
679 'replication_domain'] == rep_domain]
680 return rep_domain, pools_in_rep_domain
681
Yogeshbdb88102015-09-29 23:41:02 -0400682 @classmethod
debeltrami0d523bb2020-08-20 12:48:49 +0000683 def create_share_replica(cls, share_id, availability_zone=None,
684 client=None, cleanup_in_class=False,
685 cleanup=True,
silvacarlossd354d672020-08-23 18:49:52 +0000686 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400687 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300688 replica = client.create_share_replica(
silvacarlossd354d672020-08-23 18:49:52 +0000689 share_id, availability_zone=availability_zone, version=version)
Yogeshbdb88102015-09-29 23:41:02 -0400690 resource = {
691 "type": "share_replica",
692 "id": replica["id"],
693 "client": client,
694 "share_id": share_id,
695 }
696 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
697 if cleanup:
698 if cleanup_in_class:
699 cls.class_resources.insert(0, resource)
700 else:
701 cls.method_resources.insert(0, resource)
lkuchlan540e74a2021-01-19 18:08:25 +0200702 waiters.wait_for_share_replica_status(
703 client, replica["id"], constants.STATUS_AVAILABLE)
Yogeshbdb88102015-09-29 23:41:02 -0400704 return replica
705
706 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000707 def delete_share_replica(cls, replica_id, client=None,
708 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400709 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400710 try:
silvacarlossd354d672020-08-23 18:49:52 +0000711 client.delete_share_replica(replica_id, version=version)
Yogesh1f931ff2015-09-29 23:41:02 -0400712 client.wait_for_resource_deletion(replica_id=replica_id)
713 except exceptions.NotFound:
714 pass
Yogeshbdb88102015-09-29 23:41:02 -0400715
716 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000717 def promote_share_replica(cls, replica_id, client=None,
718 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400719 client = client or cls.shares_v2_client
silvacarlossd354d672020-08-23 18:49:52 +0000720 replica = client.promote_share_replica(replica_id, version=version)
lkuchlan540e74a2021-01-19 18:08:25 +0200721 waiters.wait_for_share_replica_status(
722 client, replica["id"], constants.REPLICATION_STATE_ACTIVE,
Yogeshbdb88102015-09-29 23:41:02 -0400723 status_attr="replica_state")
724 return replica
725
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700726 @classmethod
727 def _get_access_rule_data_from_config(cls):
yogeshdb32f462016-09-28 15:09:50 -0400728 """Get the first available access type/to combination from config.
729
730 This method opportunistically picks the first configured protocol
731 to create the share. Do not use this method in tests where you need
732 to test depth and breadth in the access types and access recipients.
733 """
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700734 protocol = cls.shares_v2_client.share_protocol
yogeshdb32f462016-09-28 15:09:50 -0400735
736 if protocol in CONF.share.enable_ip_rules_for_protocols:
737 access_type = "ip"
738 access_to = utils.rand_ip()
739 elif protocol in CONF.share.enable_user_rules_for_protocols:
740 access_type = "user"
741 access_to = CONF.share.username_for_user_rules
742 elif protocol in CONF.share.enable_cert_rules_for_protocols:
743 access_type = "cert"
744 access_to = "client3.com"
745 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
746 access_type = "cephx"
lkuchlan5af7cb42020-07-14 18:05:09 +0300747 access_to = data_utils.rand_name(
748 cls.__class__.__name__ + '-cephx-id')
yogeshdb32f462016-09-28 15:09:50 -0400749 else:
750 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700751 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400752
753 return access_type, access_to
754
Yogeshbdb88102015-09-29 23:41:02 -0400755 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200756 def create_share_network(cls, client=None,
debeltrami1753a592020-05-11 18:27:30 +0000757 cleanup_in_class=False,
758 add_security_services=True, **kwargs):
759
Marc Koderer0abc93b2015-07-15 09:18:35 +0200760 if client is None:
761 client = cls.shares_client
762 share_network = client.create_share_network(**kwargs)
763 resource = {
764 "type": "share_network",
765 "id": share_network["id"],
766 "client": client,
767 }
debeltrami1753a592020-05-11 18:27:30 +0000768
Marc Koderer0abc93b2015-07-15 09:18:35 +0200769 if cleanup_in_class:
770 cls.class_resources.insert(0, resource)
771 else:
772 cls.method_resources.insert(0, resource)
debeltrami1753a592020-05-11 18:27:30 +0000773
774 if add_security_services:
775 cls.provide_and_associate_security_services(
776 client, share_network["id"], cleanup_in_class=cleanup_in_class)
777
Marc Koderer0abc93b2015-07-15 09:18:35 +0200778 return share_network
779
780 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000781 def create_share_network_subnet(cls,
782 client=None,
783 cleanup_in_class=False,
784 **kwargs):
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300785 if client is None:
786 client = cls.shares_v2_client
787 share_network_subnet = client.create_subnet(**kwargs)
788 resource = {
789 "type": "share-network-subnet",
790 "id": share_network_subnet["id"],
791 "extra_params": {
792 "share_network_id": share_network_subnet["share_network_id"]
793 },
794 "client": client,
795 }
796 if cleanup_in_class:
797 cls.class_resources.insert(0, resource)
798 else:
799 cls.method_resources.insert(0, resource)
800 return share_network_subnet
801
802 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200803 def create_security_service(cls, ss_type="ldap", client=None,
804 cleanup_in_class=False, **kwargs):
805 if client is None:
806 client = cls.shares_client
807 security_service = client.create_security_service(ss_type, **kwargs)
808 resource = {
809 "type": "security_service",
810 "id": security_service["id"],
811 "client": client,
812 }
813 if cleanup_in_class:
814 cls.class_resources.insert(0, resource)
815 else:
816 cls.method_resources.insert(0, resource)
817 return security_service
818
819 @classmethod
820 def create_share_type(cls, name, is_public=True, client=None,
821 cleanup_in_class=True, **kwargs):
822 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200823 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200824 share_type = client.create_share_type(name, is_public, **kwargs)
825 resource = {
826 "type": "share_type",
827 "id": share_type["share_type"]["id"],
828 "client": client,
829 }
830 if cleanup_in_class:
831 cls.class_resources.insert(0, resource)
832 else:
833 cls.method_resources.insert(0, resource)
834 return share_type
835
haixin0d1d29f2019-08-02 16:50:45 +0800836 @classmethod
837 def update_share_type(cls, share_type_id, name=None,
838 is_public=None, description=None,
839 client=None):
840 if client is None:
841 client = cls.shares_v2_client
842 share_type = client.update_share_type(share_type_id, name,
843 is_public, description)
844 return share_type
845
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700846 @classmethod
847 def update_quotas(cls, project_id, user_id=None, cleanup=True,
848 client=None, **kwargs):
849 client = client or cls.shares_v2_client
850 updated_quotas = client.update_quotas(project_id,
851 user_id=user_id,
852 **kwargs)
853 resource = {
854 "type": "quotas",
855 "id": project_id,
856 "client": client,
857 "user_id": user_id,
858 }
859 if cleanup:
860 cls.method_resources.insert(0, resource)
861 return updated_quotas
862
Marc Koderer0abc93b2015-07-15 09:18:35 +0200863 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400864 def add_extra_specs_to_dict(extra_specs=None):
865 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300866 dhss = six.text_type(CONF.share.multitenancy_enabled)
867 snapshot_support = six.text_type(
868 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400869 create_from_snapshot_support = six.text_type(
870 CONF.share.capability_create_share_from_snapshot_support)
871
872 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300873 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200874 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400875
876 optional = {
877 "snapshot_support": snapshot_support,
878 "create_share_from_snapshot_support": create_from_snapshot_support,
879 }
880 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
881 # required extra-spec
882 extra_specs_dict.update(optional)
883
Marc Koderer0abc93b2015-07-15 09:18:35 +0200884 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400885 extra_specs_dict.update(extra_specs)
886
887 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200888
889 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400890 def clear_share_replicas(cls, share_id, client=None):
891 client = client or cls.shares_v2_client
892 share_replicas = client.list_share_replicas(
893 share_id=share_id)
894
895 for replica in share_replicas:
896 try:
897 cls.delete_share_replica(replica['id'])
898 except exceptions.BadRequest:
899 # Ignore the exception due to deletion of last active replica
900 pass
901
902 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200903 def clear_resources(cls, resources=None):
904 """Deletes resources, that were created in test suites.
905
906 This method tries to remove resources from resource list,
907 if it is not found, assumed it was deleted in test itself.
908 It is expected, that all resources were added as LIFO
909 due to restriction of deletion resources, that is in the chain.
910
911 :param resources: dict with keys 'type','id','client' and 'deleted'
912 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200913 if resources is None:
914 resources = cls.method_resources
915 for res in resources:
916 if "deleted" not in res.keys():
917 res["deleted"] = False
918 if "client" not in res.keys():
919 res["client"] = cls.shares_client
920 if not(res["deleted"]):
921 res_id = res['id']
922 client = res["client"]
923 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200924 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400925 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400926 share_group_id = res.get('share_group_id')
927 if share_group_id:
928 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400929 client.delete_share(res_id, params=params)
930 else:
931 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200932 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200933 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200934 client.delete_snapshot(res_id)
935 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200936 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400937 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400938 client.delete_share_network(res_id)
939 client.wait_for_resource_deletion(sn_id=res_id)
debeltrami1753a592020-05-11 18:27:30 +0000940 elif res["type"] == "dissociate_security_service":
941 sn_id = res["extra_params"]["share_network_id"]
942 client.remove_sec_service_from_share_network(
943 sn_id=sn_id, ss_id=res_id
944 )
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200945 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200946 client.delete_security_service(res_id)
947 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200948 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200949 client.delete_share_type(res_id)
950 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200951 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400952 client.delete_share_group(res_id)
953 client.wait_for_resource_deletion(
954 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200955 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400956 client.delete_share_group_type(res_id)
957 client.wait_for_resource_deletion(
958 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200959 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400960 client.delete_share_group_snapshot(res_id)
961 client.wait_for_resource_deletion(
962 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200963 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400964 client.delete_share_replica(res_id)
965 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200966 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300967 sn_id = res["extra_params"]["share_network_id"]
968 client.delete_subnet(sn_id, res_id)
969 client.wait_for_resource_deletion(
970 share_network_subnet_id=res_id,
971 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700972 elif res["type"] == "quotas":
973 user_id = res.get('user_id')
974 client.reset_quotas(res_id, user_id=user_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200975 else:
huayue97bacbf2016-01-04 09:57:39 +0800976 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800977 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200978 res["deleted"] = True
979
980 @classmethod
981 def generate_share_network_data(self):
982 data = {
983 "name": data_utils.rand_name("sn-name"),
984 "description": data_utils.rand_name("sn-desc"),
985 "neutron_net_id": data_utils.rand_name("net-id"),
986 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
987 }
988 return data
989
990 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300991 def generate_subnet_data(self):
992 data = {
993 "neutron_net_id": data_utils.rand_name("net-id"),
994 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
995 }
996 return data
997
998 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100999 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001000 data = {
1001 "name": data_utils.rand_name("ss-name"),
1002 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001003 "dns_ip": utils.rand_ip(),
1004 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001005 "domain": data_utils.rand_name("ss-domain"),
1006 "user": data_utils.rand_name("ss-user"),
1007 "password": data_utils.rand_name("ss-password"),
1008 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001009 if set_ou:
1010 data["ou"] = data_utils.rand_name("ss-ou")
1011
Marc Koderer0abc93b2015-07-15 09:18:35 +02001012 return data
1013
1014 # Useful assertions
1015 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1016 """Assert two dicts are equivalent.
1017
1018 This is a 'deep' match in the sense that it handles nested
1019 dictionaries appropriately.
1020
1021 NOTE:
1022
1023 If you don't care (or don't know) a given value, you can specify
1024 the string DONTCARE as the value. This will cause that dict-item
1025 to be skipped.
1026
1027 """
1028 def raise_assertion(msg):
1029 d1str = str(d1)
1030 d2str = str(d2)
1031 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1032 'd2: %(d2str)s' %
1033 {"msg": msg, "d1str": d1str, "d2str": d2str})
1034 raise AssertionError(base_msg)
1035
1036 d1keys = set(d1.keys())
1037 d2keys = set(d2.keys())
1038 if d1keys != d2keys:
1039 d1only = d1keys - d2keys
1040 d2only = d2keys - d1keys
1041 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1042 'Keys in d2 and not d1: %(d2only)s' %
1043 {"d1only": d1only, "d2only": d2only})
1044
1045 for key in d1keys:
1046 d1value = d1[key]
1047 d2value = d2[key]
1048 try:
1049 error = abs(float(d1value) - float(d2value))
1050 within_tolerance = error <= tolerance
1051 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001052 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001053 # ValueError if arg is a str, TypeError if it's something else
1054 # (like None)
1055 within_tolerance = False
1056
1057 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1058 self.assertDictMatch(d1value, d2value)
1059 elif 'DONTCARE' in (d1value, d2value):
1060 continue
1061 elif approx_equal and within_tolerance:
1062 continue
1063 elif d1value != d2value:
1064 raise_assertion("d1['%(key)s']=%(d1value)s != "
1065 "d2['%(key)s']=%(d2value)s" %
1066 {
1067 "key": key,
1068 "d1value": d1value,
1069 "d2value": d2value
1070 })
1071
Alex Meadeba8a1602016-05-06 09:33:09 -04001072 def create_user_message(self):
1073 """Trigger a 'no valid host' situation to generate a message."""
1074 extra_specs = {
1075 'vendor_name': 'foobar',
1076 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1077 }
1078 share_type_name = data_utils.rand_name("share-type")
1079
1080 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001081 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001082 name=share_type_name,
1083 extra_specs=extra_specs)['share_type']
1084
1085 params = {'share_type_id': bogus_type['id'],
1086 'share_network_id': self.shares_v2_client.share_network_id}
1087 share = self.shares_v2_client.create_share(**params)
1088 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
lkuchlan540e74a2021-01-19 18:08:25 +02001089 waiters.wait_for_share_status(
1090 self.shares_v2_client, share['id'], "error")
1091 return waiters.wait_for_message(self.shares_v2_client, share['id'])
Alex Meadeba8a1602016-05-06 09:33:09 -04001092
lkuchlan5af7cb42020-07-14 18:05:09 +03001093 def allow_access(self, share_id, client=None, access_type=None,
1094 access_level='rw', access_to=None, status='active',
1095 raise_rule_in_error_state=True, cleanup=True):
1096
1097 client = client or self.shares_v2_client
1098 a_type, a_to = self._get_access_rule_data_from_config()
1099 access_type = access_type or a_type
1100 access_to = access_to or a_to
1101
1102 rule = client.create_access_rule(share_id, access_type, access_to,
1103 access_level)
lkuchlan540e74a2021-01-19 18:08:25 +02001104 waiters.wait_for_access_rule_status(client, share_id, rule['id'],
1105 status, raise_rule_in_error_state)
lkuchlan5af7cb42020-07-14 18:05:09 +03001106 if cleanup:
1107 self.addCleanup(client.wait_for_resource_deletion,
1108 rule_id=rule['id'], share_id=share_id)
1109 self.addCleanup(client.delete_access_rule, share_id, rule['id'])
1110 return rule
1111
Marc Koderer0abc93b2015-07-15 09:18:35 +02001112
Marc Koderer0abc93b2015-07-15 09:18:35 +02001113class BaseSharesAdminTest(BaseSharesTest):
1114 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001115 credentials = ('admin', )
1116
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001117 @classmethod
1118 def setup_clients(cls):
1119 super(BaseSharesAdminTest, cls).setup_clients()
1120 # Initialise share clients
1121 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1122
1123 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001124 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001125 name = data_utils.rand_name("unique_st_name")
1126 extra_specs = cls.add_extra_specs_to_dict(specs)
1127 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001128 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001129 client=cls.admin_shares_v2_client)['share_type']
1130
1131 @classmethod
1132 def _create_share_group_type(cls):
1133 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1134 return cls.create_share_group_type(
1135 name=share_group_type_name, share_types=[cls.share_type_id],
1136 client=cls.admin_shares_v2_client)
1137
Lucio Seki37056942019-01-24 15:40:20 -02001138 def _create_share_for_manage(self):
1139 creation_data = {
1140 'share_type_id': self.st['share_type']['id'],
1141 'share_protocol': self.protocol,
1142 }
1143
1144 share = self.create_share(**creation_data)
1145 share = self.shares_v2_client.get_share(share['id'])
1146
1147 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1148 el = self.shares_v2_client.list_share_export_locations(share["id"])
1149 share["export_locations"] = el
1150
1151 return share
1152
1153 def _unmanage_share_and_wait(self, share):
1154 self.shares_v2_client.unmanage_share(share['id'])
1155 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1156
1157 def _reset_state_and_delete_share(self, share):
1158 self.shares_v2_client.reset_state(share['id'])
1159 self._delete_share_and_wait(share)
1160
1161 def _delete_snapshot_and_wait(self, snap):
1162 self.shares_v2_client.delete_snapshot(snap['id'])
1163 self.shares_v2_client.wait_for_resource_deletion(
1164 snapshot_id=snap['id']
1165 )
1166 self.assertRaises(exceptions.NotFound,
1167 self.shares_v2_client.get_snapshot,
1168 snap['id'])
1169
1170 def _delete_share_and_wait(self, share):
1171 self.shares_v2_client.delete_share(share['id'])
1172 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1173 self.assertRaises(exceptions.NotFound,
1174 self.shares_v2_client.get_share,
1175 share['id'])
1176
1177 def _manage_share(self, share, name, description, share_server_id):
1178 managed_share = self.shares_v2_client.manage_share(
1179 service_host=share['host'],
1180 export_path=share['export_locations'][0],
1181 protocol=share['share_proto'],
1182 share_type_id=self.share_type['share_type']['id'],
1183 name=name,
1184 description=description,
1185 share_server_id=share_server_id
1186 )
lkuchlan540e74a2021-01-19 18:08:25 +02001187 waiters.wait_for_share_status(
1188 self.shares_v2_client, managed_share['id'],
1189 constants.STATUS_AVAILABLE
Lucio Seki37056942019-01-24 15:40:20 -02001190 )
1191
1192 return managed_share
1193
1194 def _unmanage_share_server_and_wait(self, server):
1195 self.shares_v2_client.unmanage_share_server(server['id'])
1196 self.shares_v2_client.wait_for_resource_deletion(
1197 server_id=server['id']
1198 )
1199
1200 def _manage_share_server(self, share_server, fields=None):
1201 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001202 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001203 managed_share_server = self.shares_v2_client.manage_share_server(
1204 params.get('host', share_server['host']),
1205 params.get('share_network_id', share_server['share_network_id']),
1206 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001207 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001208 )
lkuchlan540e74a2021-01-19 18:08:25 +02001209 waiters.wait_for_share_server_status(
1210 self.shares_v2_client, managed_share_server['id'],
Lucio Seki37056942019-01-24 15:40:20 -02001211 constants.SERVER_STATE_ACTIVE,
1212 )
1213
1214 return managed_share_server
1215
1216 def _delete_share_server_and_wait(self, share_server_id):
1217 self.shares_v2_client.delete_share_server(
1218 share_server_id
1219 )
1220 self.shares_v2_client.wait_for_resource_deletion(
1221 server_id=share_server_id)
1222
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001223
1224class BaseSharesMixedTest(BaseSharesTest):
1225 """Base test case class for all Shares API tests with all user roles."""
1226 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001227
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001228 # Will be cleaned up in resource_cleanup if the class
1229 class_project_users_created = []
1230
1231 @classmethod
1232 def resource_cleanup(cls):
1233 cls.clear_project_users(cls.class_project_users_created)
1234 super(BaseSharesMixedTest, cls).resource_cleanup()
1235
1236 @classmethod
1237 def clear_project_users(cls, users=None):
1238 users = users or cls.class_project_users_created
1239 for user in users:
1240 with handle_cleanup_exceptions():
1241 cls.os_admin.creds_client.delete_user(user['id'])
1242
Marc Koderer0abc93b2015-07-15 09:18:35 +02001243 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001244 def setup_clients(cls):
1245 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001246 # Initialise share clients
1247 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1248 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1249 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1250 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1251 # Initialise network clients
1252 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1253 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001254 # Initialise identity clients
1255 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1256 identity_clients = getattr(
1257 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1258 cls.os_admin.identity_client = identity_clients.IdentityClient()
1259 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1260 cls.os_admin.users_client = identity_clients.UsersClient()
1261 cls.os_admin.roles_client = identity_clients.RolesClient()
1262 cls.os_admin.domains_client = (
1263 cls.os_admin.identity_v3.DomainsClient() if
1264 CONF.identity.auth_version == 'v3' else None)
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001265 cls.admin_project_member_client = cls.create_user_and_get_client(
1266 project=cls.admin_project, add_member_role=True)
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 Ravi10d49492021-02-23 16:05:21 -08001281 def create_user_and_get_client(cls, project=None, add_member_role=True):
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001282 """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
Goutham Pacha Ravi10d49492021-02-23 16:05:21 -08001306 tempest_roles_to_assign = CONF.auth.tempest_roles or []
1307 if "member" not in tempest_roles_to_assign and add_member_role:
1308 tempest_roles_to_assign.append("member")
1309
1310 for role in tempest_roles_to_assign:
1311 cls.os_admin.creds_client.assign_user_role(user, project, role)
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001312
1313 user_creds = cls.os_admin.creds_client.get_credentials(
1314 user, project, password)
1315 os = clients.Clients(user_creds)
1316 os.shares_v1_client = os.share_v1.SharesClient()
1317 os.shares_v2_client = os.share_v2.SharesV2Client()
1318 return os
1319
1320 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001321 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001322 name = data_utils.rand_name("unique_st_name")
1323 extra_specs = cls.add_extra_specs_to_dict(specs)
1324 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001325 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001326 client=cls.admin_shares_v2_client)['share_type']
1327
1328 @classmethod
1329 def _create_share_group_type(cls):
1330 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1331 return cls.create_share_group_type(
1332 name=share_group_type_name, share_types=[cls.share_type_id],
1333 client=cls.admin_shares_v2_client)