blob: b6579359421172a40788c7a7a580f0cd7b87ce18 [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:
lkuchlanf7fc5b62021-01-26 14:53:43 +0200484 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +0200485 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
lkuchlanf7fc5b62021-01-26 14:53:43 +0200544 waiters.wait_for_resource_status(
545 client, share_group['id'], 'available',
546 resource_name='share_group')
Andrew Kerrb8436922016-06-01 15:32:43 -0400547 return share_group
548
549 @classmethod
550 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
551 group_specs=None, client=None,
552 cleanup_in_class=True, **kwargs):
553 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300554 if (group_specs is None and
555 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300556 group_specs = {
557 'consistent_snapshot_support': (
558 CONF.share.capability_sg_consistent_snapshot_support),
559 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400560 share_group_type = client.create_share_group_type(
561 name=name,
562 share_types=share_types,
563 is_public=is_public,
564 group_specs=group_specs,
565 **kwargs)
566 resource = {
567 "type": "share_group_type",
568 "id": share_group_type["id"],
569 "client": client,
570 }
571 if cleanup_in_class:
572 cls.class_resources.insert(0, resource)
573 else:
574 cls.method_resources.insert(0, resource)
575 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400576
577 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200578 def create_snapshot_wait_for_active(cls, share_id, name=None,
579 description=None, force=False,
580 client=None, cleanup_in_class=True):
581 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400582 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200583 if description is None:
584 description = "Tempest's snapshot"
585 snapshot = client.create_snapshot(share_id, name, description, force)
586 resource = {
587 "type": "snapshot",
588 "id": snapshot["id"],
589 "client": client,
590 }
591 if cleanup_in_class:
592 cls.class_resources.insert(0, resource)
593 else:
594 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200595 waiters.wait_for_resource_status(client, snapshot["id"], "available",
596 resource_name='snapshot')
Marc Koderer0abc93b2015-07-15 09:18:35 +0200597 return snapshot
598
599 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400600 def create_share_group_snapshot_wait_for_active(
601 cls, share_group_id, name=None, description=None, client=None,
602 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400603 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400604 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400605 description = "Tempest's share group snapshot"
606 sg_snapshot = client.create_share_group_snapshot(
607 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400608 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400609 "type": "share_group_snapshot",
610 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400611 "client": client,
612 }
613 if cleanup_in_class:
614 cls.class_resources.insert(0, resource)
615 else:
616 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200617 waiters.wait_for_resource_status(
618 client, sg_snapshot["id"], "available",
619 resource_name="share_group_snapshot")
Andrew Kerrb8436922016-06-01 15:32:43 -0400620 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400621
622 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800623 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400624 """List the availability zones for "manila-share" services
625
626 that are currently in "up" state.
627 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800628 client = client or cls.admin_shares_v2_client
629 backends = (
630 '|'.join(['^%s$' % backend for backend in backends])
631 if backends else '.*'
632 )
Yogeshbdb88102015-09-29 23:41:02 -0400633 cls.services = client.list_services()
634 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800635 service['binary'] == 'manila-share' and
636 service['state'] == 'up' and
637 re.search(backends, service['host'])]
Douglas Viroel161f1802020-04-25 17:18:14 -0300638 return list(set(zones))
Yogeshbdb88102015-09-29 23:41:02 -0400639
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800640 @classmethod
641 def get_pools_matching_share_type(cls, share_type, client=None):
642 client = client or cls.admin_shares_v2_client
643 if utils.is_microversion_supported('2.23'):
644 return client.list_pools(
andrebeltrami3b4d4852020-02-04 19:11:54 +0000645 detail=True,
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800646 search_opts={'share_type': share_type['id']})['pools']
647
648 pools = client.list_pools(detail=True)['pools']
649 share_type = client.get_share_type(share_type['id'])['share_type']
650 extra_specs = {}
651 for k, v in share_type['extra_specs'].items():
652 extra_specs[k] = (
653 True if six.text_type(v).lower() == 'true'
654 else False if six.text_type(v).lower() == 'false' else v
655 )
656 return [
657 pool for pool in pools if all(y in pool['capabilities'].items()
658 for y in extra_specs.items())
659 ]
660
661 @classmethod
662 def get_availability_zones_matching_share_type(cls, share_type,
663 client=None):
664
665 client = client or cls.admin_shares_v2_client
666 pools_matching_share_type = cls.get_pools_matching_share_type(
667 share_type, client=client)
668 backends_matching_share_type = set(
669 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
670 )
671 azs = cls.get_availability_zones(backends=backends_matching_share_type)
672 return azs
673
Yogesh1f931ff2015-09-29 23:41:02 -0400674 def get_pools_for_replication_domain(self):
675 # Get the list of pools for the replication domain
676 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500677 instance_host = self.admin_client.get_share(
678 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400679 host_pool = [p for p in pools if p['name'] == instance_host][0]
680 rep_domain = host_pool['capabilities']['replication_domain']
681 pools_in_rep_domain = [p for p in pools if p['capabilities'][
682 'replication_domain'] == rep_domain]
683 return rep_domain, pools_in_rep_domain
684
Yogeshbdb88102015-09-29 23:41:02 -0400685 @classmethod
debeltrami0d523bb2020-08-20 12:48:49 +0000686 def create_share_replica(cls, share_id, availability_zone=None,
687 client=None, cleanup_in_class=False,
688 cleanup=True,
silvacarlossd354d672020-08-23 18:49:52 +0000689 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400690 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300691 replica = client.create_share_replica(
silvacarlossd354d672020-08-23 18:49:52 +0000692 share_id, availability_zone=availability_zone, version=version)
Yogeshbdb88102015-09-29 23:41:02 -0400693 resource = {
694 "type": "share_replica",
695 "id": replica["id"],
696 "client": client,
697 "share_id": share_id,
698 }
699 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
700 if cleanup:
701 if cleanup_in_class:
702 cls.class_resources.insert(0, resource)
703 else:
704 cls.method_resources.insert(0, resource)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200705 waiters.wait_for_resource_status(
706 client, replica["id"], constants.STATUS_AVAILABLE,
707 resource_name='share_replica')
Yogeshbdb88102015-09-29 23:41:02 -0400708 return replica
709
710 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000711 def delete_share_replica(cls, replica_id, client=None,
712 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400713 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400714 try:
silvacarlossd354d672020-08-23 18:49:52 +0000715 client.delete_share_replica(replica_id, version=version)
Yogesh1f931ff2015-09-29 23:41:02 -0400716 client.wait_for_resource_deletion(replica_id=replica_id)
717 except exceptions.NotFound:
718 pass
Yogeshbdb88102015-09-29 23:41:02 -0400719
720 @classmethod
silvacarlossd354d672020-08-23 18:49:52 +0000721 def promote_share_replica(cls, replica_id, client=None,
722 version=CONF.share.max_api_microversion):
Yogeshbdb88102015-09-29 23:41:02 -0400723 client = client or cls.shares_v2_client
silvacarlossd354d672020-08-23 18:49:52 +0000724 replica = client.promote_share_replica(replica_id, version=version)
lkuchlanf7fc5b62021-01-26 14:53:43 +0200725 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +0200726 client, replica["id"], constants.REPLICATION_STATE_ACTIVE,
lkuchlanf7fc5b62021-01-26 14:53:43 +0200727 resource_name='share_replica', status_attr="replica_state")
Yogeshbdb88102015-09-29 23:41:02 -0400728 return replica
729
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700730 @classmethod
731 def _get_access_rule_data_from_config(cls):
yogeshdb32f462016-09-28 15:09:50 -0400732 """Get the first available access type/to combination from config.
733
734 This method opportunistically picks the first configured protocol
735 to create the share. Do not use this method in tests where you need
736 to test depth and breadth in the access types and access recipients.
737 """
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700738 protocol = cls.shares_v2_client.share_protocol
yogeshdb32f462016-09-28 15:09:50 -0400739
740 if protocol in CONF.share.enable_ip_rules_for_protocols:
741 access_type = "ip"
742 access_to = utils.rand_ip()
743 elif protocol in CONF.share.enable_user_rules_for_protocols:
744 access_type = "user"
745 access_to = CONF.share.username_for_user_rules
746 elif protocol in CONF.share.enable_cert_rules_for_protocols:
747 access_type = "cert"
748 access_to = "client3.com"
749 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
750 access_type = "cephx"
lkuchlan5af7cb42020-07-14 18:05:09 +0300751 access_to = data_utils.rand_name(
752 cls.__class__.__name__ + '-cephx-id')
yogeshdb32f462016-09-28 15:09:50 -0400753 else:
754 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700755 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400756
757 return access_type, access_to
758
Yogeshbdb88102015-09-29 23:41:02 -0400759 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200760 def create_share_network(cls, client=None,
debeltrami1753a592020-05-11 18:27:30 +0000761 cleanup_in_class=False,
762 add_security_services=True, **kwargs):
763
Marc Koderer0abc93b2015-07-15 09:18:35 +0200764 if client is None:
765 client = cls.shares_client
766 share_network = client.create_share_network(**kwargs)
767 resource = {
768 "type": "share_network",
769 "id": share_network["id"],
770 "client": client,
771 }
debeltrami1753a592020-05-11 18:27:30 +0000772
Marc Koderer0abc93b2015-07-15 09:18:35 +0200773 if cleanup_in_class:
774 cls.class_resources.insert(0, resource)
775 else:
776 cls.method_resources.insert(0, resource)
debeltrami1753a592020-05-11 18:27:30 +0000777
778 if add_security_services:
779 cls.provide_and_associate_security_services(
780 client, share_network["id"], cleanup_in_class=cleanup_in_class)
781
Marc Koderer0abc93b2015-07-15 09:18:35 +0200782 return share_network
783
784 @classmethod
debeltrami1753a592020-05-11 18:27:30 +0000785 def create_share_network_subnet(cls,
786 client=None,
787 cleanup_in_class=False,
788 **kwargs):
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300789 if client is None:
790 client = cls.shares_v2_client
791 share_network_subnet = client.create_subnet(**kwargs)
792 resource = {
793 "type": "share-network-subnet",
794 "id": share_network_subnet["id"],
795 "extra_params": {
796 "share_network_id": share_network_subnet["share_network_id"]
797 },
798 "client": client,
799 }
800 if cleanup_in_class:
801 cls.class_resources.insert(0, resource)
802 else:
803 cls.method_resources.insert(0, resource)
804 return share_network_subnet
805
806 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200807 def create_security_service(cls, ss_type="ldap", client=None,
808 cleanup_in_class=False, **kwargs):
809 if client is None:
810 client = cls.shares_client
811 security_service = client.create_security_service(ss_type, **kwargs)
812 resource = {
813 "type": "security_service",
814 "id": security_service["id"],
815 "client": client,
816 }
817 if cleanup_in_class:
818 cls.class_resources.insert(0, resource)
819 else:
820 cls.method_resources.insert(0, resource)
821 return security_service
822
823 @classmethod
824 def create_share_type(cls, name, is_public=True, client=None,
825 cleanup_in_class=True, **kwargs):
826 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200827 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200828 share_type = client.create_share_type(name, is_public, **kwargs)
829 resource = {
830 "type": "share_type",
831 "id": share_type["share_type"]["id"],
832 "client": client,
833 }
834 if cleanup_in_class:
835 cls.class_resources.insert(0, resource)
836 else:
837 cls.method_resources.insert(0, resource)
838 return share_type
839
haixin0d1d29f2019-08-02 16:50:45 +0800840 @classmethod
841 def update_share_type(cls, share_type_id, name=None,
842 is_public=None, description=None,
843 client=None):
844 if client is None:
845 client = cls.shares_v2_client
846 share_type = client.update_share_type(share_type_id, name,
847 is_public, description)
848 return share_type
849
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700850 @classmethod
851 def update_quotas(cls, project_id, user_id=None, cleanup=True,
852 client=None, **kwargs):
853 client = client or cls.shares_v2_client
854 updated_quotas = client.update_quotas(project_id,
855 user_id=user_id,
856 **kwargs)
857 resource = {
858 "type": "quotas",
859 "id": project_id,
860 "client": client,
861 "user_id": user_id,
862 }
863 if cleanup:
864 cls.method_resources.insert(0, resource)
865 return updated_quotas
866
Marc Koderer0abc93b2015-07-15 09:18:35 +0200867 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400868 def add_extra_specs_to_dict(extra_specs=None):
869 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300870 dhss = six.text_type(CONF.share.multitenancy_enabled)
871 snapshot_support = six.text_type(
872 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400873 create_from_snapshot_support = six.text_type(
874 CONF.share.capability_create_share_from_snapshot_support)
875
876 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300877 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200878 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400879
880 optional = {
881 "snapshot_support": snapshot_support,
882 "create_share_from_snapshot_support": create_from_snapshot_support,
883 }
884 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
885 # required extra-spec
886 extra_specs_dict.update(optional)
887
Marc Koderer0abc93b2015-07-15 09:18:35 +0200888 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400889 extra_specs_dict.update(extra_specs)
890
891 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200892
893 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400894 def clear_share_replicas(cls, share_id, client=None):
895 client = client or cls.shares_v2_client
896 share_replicas = client.list_share_replicas(
897 share_id=share_id)
898
899 for replica in share_replicas:
900 try:
901 cls.delete_share_replica(replica['id'])
902 except exceptions.BadRequest:
903 # Ignore the exception due to deletion of last active replica
904 pass
905
906 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200907 def clear_resources(cls, resources=None):
908 """Deletes resources, that were created in test suites.
909
910 This method tries to remove resources from resource list,
911 if it is not found, assumed it was deleted in test itself.
912 It is expected, that all resources were added as LIFO
913 due to restriction of deletion resources, that is in the chain.
914
915 :param resources: dict with keys 'type','id','client' and 'deleted'
916 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200917 if resources is None:
918 resources = cls.method_resources
919 for res in resources:
920 if "deleted" not in res.keys():
921 res["deleted"] = False
922 if "client" not in res.keys():
923 res["client"] = cls.shares_client
924 if not(res["deleted"]):
925 res_id = res['id']
926 client = res["client"]
927 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200928 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400929 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400930 share_group_id = res.get('share_group_id')
931 if share_group_id:
932 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400933 client.delete_share(res_id, params=params)
934 else:
935 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200936 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200937 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200938 client.delete_snapshot(res_id)
939 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200940 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400941 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400942 client.delete_share_network(res_id)
943 client.wait_for_resource_deletion(sn_id=res_id)
debeltrami1753a592020-05-11 18:27:30 +0000944 elif res["type"] == "dissociate_security_service":
945 sn_id = res["extra_params"]["share_network_id"]
946 client.remove_sec_service_from_share_network(
947 sn_id=sn_id, ss_id=res_id
948 )
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200949 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200950 client.delete_security_service(res_id)
951 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200952 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200953 client.delete_share_type(res_id)
954 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200955 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400956 client.delete_share_group(res_id)
957 client.wait_for_resource_deletion(
958 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200959 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400960 client.delete_share_group_type(res_id)
961 client.wait_for_resource_deletion(
962 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200963 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400964 client.delete_share_group_snapshot(res_id)
965 client.wait_for_resource_deletion(
966 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200967 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400968 client.delete_share_replica(res_id)
969 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200970 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300971 sn_id = res["extra_params"]["share_network_id"]
972 client.delete_subnet(sn_id, res_id)
973 client.wait_for_resource_deletion(
974 share_network_subnet_id=res_id,
975 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700976 elif res["type"] == "quotas":
977 user_id = res.get('user_id')
978 client.reset_quotas(res_id, user_id=user_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200979 else:
huayue97bacbf2016-01-04 09:57:39 +0800980 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800981 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200982 res["deleted"] = True
983
984 @classmethod
985 def generate_share_network_data(self):
986 data = {
987 "name": data_utils.rand_name("sn-name"),
988 "description": data_utils.rand_name("sn-desc"),
989 "neutron_net_id": data_utils.rand_name("net-id"),
990 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
991 }
992 return data
993
994 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300995 def generate_subnet_data(self):
996 data = {
997 "neutron_net_id": data_utils.rand_name("net-id"),
998 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
999 }
1000 return data
1001
1002 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001003 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001004 data = {
1005 "name": data_utils.rand_name("ss-name"),
1006 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001007 "dns_ip": utils.rand_ip(),
1008 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001009 "domain": data_utils.rand_name("ss-domain"),
1010 "user": data_utils.rand_name("ss-user"),
1011 "password": data_utils.rand_name("ss-password"),
1012 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001013 if set_ou:
1014 data["ou"] = data_utils.rand_name("ss-ou")
1015
Marc Koderer0abc93b2015-07-15 09:18:35 +02001016 return data
1017
1018 # Useful assertions
1019 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1020 """Assert two dicts are equivalent.
1021
1022 This is a 'deep' match in the sense that it handles nested
1023 dictionaries appropriately.
1024
1025 NOTE:
1026
1027 If you don't care (or don't know) a given value, you can specify
1028 the string DONTCARE as the value. This will cause that dict-item
1029 to be skipped.
1030
1031 """
1032 def raise_assertion(msg):
1033 d1str = str(d1)
1034 d2str = str(d2)
1035 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1036 'd2: %(d2str)s' %
1037 {"msg": msg, "d1str": d1str, "d2str": d2str})
1038 raise AssertionError(base_msg)
1039
1040 d1keys = set(d1.keys())
1041 d2keys = set(d2.keys())
1042 if d1keys != d2keys:
1043 d1only = d1keys - d2keys
1044 d2only = d2keys - d1keys
1045 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1046 'Keys in d2 and not d1: %(d2only)s' %
1047 {"d1only": d1only, "d2only": d2only})
1048
1049 for key in d1keys:
1050 d1value = d1[key]
1051 d2value = d2[key]
1052 try:
1053 error = abs(float(d1value) - float(d2value))
1054 within_tolerance = error <= tolerance
1055 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001056 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001057 # ValueError if arg is a str, TypeError if it's something else
1058 # (like None)
1059 within_tolerance = False
1060
1061 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1062 self.assertDictMatch(d1value, d2value)
1063 elif 'DONTCARE' in (d1value, d2value):
1064 continue
1065 elif approx_equal and within_tolerance:
1066 continue
1067 elif d1value != d2value:
1068 raise_assertion("d1['%(key)s']=%(d1value)s != "
1069 "d2['%(key)s']=%(d2value)s" %
1070 {
1071 "key": key,
1072 "d1value": d1value,
1073 "d2value": d2value
1074 })
1075
Alex Meadeba8a1602016-05-06 09:33:09 -04001076 def create_user_message(self):
1077 """Trigger a 'no valid host' situation to generate a message."""
1078 extra_specs = {
1079 'vendor_name': 'foobar',
1080 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1081 }
1082 share_type_name = data_utils.rand_name("share-type")
1083
1084 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001085 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001086 name=share_type_name,
1087 extra_specs=extra_specs)['share_type']
1088
1089 params = {'share_type_id': bogus_type['id'],
1090 'share_network_id': self.shares_v2_client.share_network_id}
1091 share = self.shares_v2_client.create_share(**params)
1092 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
lkuchlanf7fc5b62021-01-26 14:53:43 +02001093 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001094 self.shares_v2_client, share['id'], "error")
1095 return waiters.wait_for_message(self.shares_v2_client, share['id'])
Alex Meadeba8a1602016-05-06 09:33:09 -04001096
lkuchlan5af7cb42020-07-14 18:05:09 +03001097 def allow_access(self, share_id, client=None, access_type=None,
1098 access_level='rw', access_to=None, status='active',
1099 raise_rule_in_error_state=True, cleanup=True):
1100
1101 client = client or self.shares_v2_client
1102 a_type, a_to = self._get_access_rule_data_from_config()
1103 access_type = access_type or a_type
1104 access_to = access_to or a_to
1105
1106 rule = client.create_access_rule(share_id, access_type, access_to,
1107 access_level)
lkuchlanf7fc5b62021-01-26 14:53:43 +02001108 waiters.wait_for_resource_status(
1109 client, share_id, status, resource_name='access_rule',
1110 rule_id=rule['id'],
1111 raise_rule_in_error_state=raise_rule_in_error_state)
lkuchlan5af7cb42020-07-14 18:05:09 +03001112 if cleanup:
1113 self.addCleanup(client.wait_for_resource_deletion,
1114 rule_id=rule['id'], share_id=share_id)
1115 self.addCleanup(client.delete_access_rule, share_id, rule['id'])
1116 return rule
1117
Marc Koderer0abc93b2015-07-15 09:18:35 +02001118
Marc Koderer0abc93b2015-07-15 09:18:35 +02001119class BaseSharesAdminTest(BaseSharesTest):
1120 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001121 credentials = ('admin', )
1122
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001123 @classmethod
1124 def setup_clients(cls):
1125 super(BaseSharesAdminTest, cls).setup_clients()
1126 # Initialise share clients
1127 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1128
1129 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001130 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001131 name = data_utils.rand_name("unique_st_name")
1132 extra_specs = cls.add_extra_specs_to_dict(specs)
1133 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001134 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001135 client=cls.admin_shares_v2_client)['share_type']
1136
1137 @classmethod
1138 def _create_share_group_type(cls):
1139 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1140 return cls.create_share_group_type(
1141 name=share_group_type_name, share_types=[cls.share_type_id],
1142 client=cls.admin_shares_v2_client)
1143
Lucio Seki37056942019-01-24 15:40:20 -02001144 def _create_share_for_manage(self):
1145 creation_data = {
1146 'share_type_id': self.st['share_type']['id'],
1147 'share_protocol': self.protocol,
1148 }
1149
1150 share = self.create_share(**creation_data)
1151 share = self.shares_v2_client.get_share(share['id'])
1152
1153 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1154 el = self.shares_v2_client.list_share_export_locations(share["id"])
1155 share["export_locations"] = el
1156
1157 return share
1158
1159 def _unmanage_share_and_wait(self, share):
1160 self.shares_v2_client.unmanage_share(share['id'])
1161 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1162
1163 def _reset_state_and_delete_share(self, share):
1164 self.shares_v2_client.reset_state(share['id'])
1165 self._delete_share_and_wait(share)
1166
1167 def _delete_snapshot_and_wait(self, snap):
1168 self.shares_v2_client.delete_snapshot(snap['id'])
1169 self.shares_v2_client.wait_for_resource_deletion(
1170 snapshot_id=snap['id']
1171 )
1172 self.assertRaises(exceptions.NotFound,
1173 self.shares_v2_client.get_snapshot,
1174 snap['id'])
1175
1176 def _delete_share_and_wait(self, share):
1177 self.shares_v2_client.delete_share(share['id'])
1178 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1179 self.assertRaises(exceptions.NotFound,
1180 self.shares_v2_client.get_share,
1181 share['id'])
1182
1183 def _manage_share(self, share, name, description, share_server_id):
1184 managed_share = self.shares_v2_client.manage_share(
1185 service_host=share['host'],
1186 export_path=share['export_locations'][0],
1187 protocol=share['share_proto'],
1188 share_type_id=self.share_type['share_type']['id'],
1189 name=name,
1190 description=description,
1191 share_server_id=share_server_id
1192 )
lkuchlanf7fc5b62021-01-26 14:53:43 +02001193 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001194 self.shares_v2_client, managed_share['id'],
1195 constants.STATUS_AVAILABLE
Lucio Seki37056942019-01-24 15:40:20 -02001196 )
1197
1198 return managed_share
1199
1200 def _unmanage_share_server_and_wait(self, server):
1201 self.shares_v2_client.unmanage_share_server(server['id'])
1202 self.shares_v2_client.wait_for_resource_deletion(
1203 server_id=server['id']
1204 )
1205
1206 def _manage_share_server(self, share_server, fields=None):
1207 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001208 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001209 managed_share_server = self.shares_v2_client.manage_share_server(
1210 params.get('host', share_server['host']),
1211 params.get('share_network_id', share_server['share_network_id']),
1212 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001213 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001214 )
lkuchlanf7fc5b62021-01-26 14:53:43 +02001215 waiters.wait_for_resource_status(
lkuchlan540e74a2021-01-19 18:08:25 +02001216 self.shares_v2_client, managed_share_server['id'],
lkuchlanf7fc5b62021-01-26 14:53:43 +02001217 constants.SERVER_STATE_ACTIVE, resource_name='share_server'
Lucio Seki37056942019-01-24 15:40:20 -02001218 )
1219
1220 return managed_share_server
1221
1222 def _delete_share_server_and_wait(self, share_server_id):
1223 self.shares_v2_client.delete_share_server(
1224 share_server_id
1225 )
1226 self.shares_v2_client.wait_for_resource_deletion(
1227 server_id=share_server_id)
1228
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001229
1230class BaseSharesMixedTest(BaseSharesTest):
1231 """Base test case class for all Shares API tests with all user roles."""
1232 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001233
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001234 # Will be cleaned up in resource_cleanup if the class
1235 class_project_users_created = []
1236
1237 @classmethod
1238 def resource_cleanup(cls):
1239 cls.clear_project_users(cls.class_project_users_created)
1240 super(BaseSharesMixedTest, cls).resource_cleanup()
1241
1242 @classmethod
1243 def clear_project_users(cls, users=None):
1244 users = users or cls.class_project_users_created
1245 for user in users:
1246 with handle_cleanup_exceptions():
1247 cls.os_admin.creds_client.delete_user(user['id'])
1248
Marc Koderer0abc93b2015-07-15 09:18:35 +02001249 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001250 def setup_clients(cls):
1251 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001252 # Initialise share clients
1253 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1254 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1255 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1256 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1257 # Initialise network clients
1258 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1259 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001260 # Initialise identity clients
1261 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1262 identity_clients = getattr(
1263 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1264 cls.os_admin.identity_client = identity_clients.IdentityClient()
1265 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1266 cls.os_admin.users_client = identity_clients.UsersClient()
1267 cls.os_admin.roles_client = identity_clients.RolesClient()
1268 cls.os_admin.domains_client = (
1269 cls.os_admin.identity_v3.DomainsClient() if
1270 CONF.identity.auth_version == 'v3' else None)
1271 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001272
1273 if CONF.share.multitenancy_enabled:
1274 admin_share_network_id = cls.provide_share_network(
1275 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1276 cls.admin_shares_client.share_network_id = admin_share_network_id
1277 cls.admin_shares_v2_client.share_network_id = (
1278 admin_share_network_id)
1279
1280 alt_share_network_id = cls.provide_share_network(
1281 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1282 cls.alt_shares_client.share_network_id = alt_share_network_id
1283 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001284
1285 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001286 def create_user_and_get_client(cls, project=None):
1287 """Create a user in specified project & set share clients for user
1288
1289 The user will have all roles specified in tempest.conf
1290 :param: project: a dictionary with project ID and name, if not
1291 specified, the value will be cls.admin_project
1292 """
1293 project_domain_name = (
1294 cls.os_admin.identity_client.auth_provider.credentials.get(
1295 'project_domain_name', 'Default'))
1296 cls.os_admin.creds_client = cred_client.get_creds_client(
1297 cls.os_admin.identity_client, cls.os_admin.projects_client,
1298 cls.os_admin.users_client, cls.os_admin.roles_client,
1299 cls.os_admin.domains_client, project_domain_name)
1300
1301 # User info
1302 project = project or cls.admin_project
1303 username = data_utils.rand_name('manila_%s' % project['id'])
1304 password = data_utils.rand_password()
1305 email = '%s@example.org' % username
1306
1307 user = cls.os_admin.creds_client.create_user(
1308 username, password, project, email)
1309 cls.class_project_users_created.append(user)
1310
1311 for conf_role in CONF.auth.tempest_roles:
1312 cls.os_admin.creds_client.assign_user_role(
1313 user, project, conf_role)
1314
1315 user_creds = cls.os_admin.creds_client.get_credentials(
1316 user, project, password)
1317 os = clients.Clients(user_creds)
1318 os.shares_v1_client = os.share_v1.SharesClient()
1319 os.shares_v2_client = os.share_v2.SharesV2Client()
1320 return os
1321
1322 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001323 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001324 name = data_utils.rand_name("unique_st_name")
1325 extra_specs = cls.add_extra_specs_to_dict(specs)
1326 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001327 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001328 client=cls.admin_shares_v2_client)['share_type']
1329
1330 @classmethod
1331 def _create_share_group_type(cls):
1332 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1333 return cls.create_share_group_type(
1334 name=share_group_type_name, share_types=[cls.share_type_id],
1335 client=cls.admin_shares_v2_client)