blob: fc5c6a1c67076963c8d3c777e865536d18ede5e1 [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
20from oslo_concurrency import lockutils
21from oslo_log import log
22import six
Sam Wanc7b7f1f2015-11-25 00:22:28 -050023from tempest.common import credentials_factory as common_creds
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010024
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020025from tempest import config
Goutham Pacha Ravic678e212020-03-20 11:13:47 -070026from tempest.lib.common import cred_client
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010027from tempest.lib.common import dynamic_creds
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050028from tempest.lib.common.utils import data_utils
29from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020030from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020031
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010032from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040033from manila_tempest_tests.common import constants
Marc Koderer0abc93b2015-07-15 09:18:35 +020034from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020035from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020036
37CONF = config.CONF
38LOG = log.getLogger(__name__)
39
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030040# Test tags related to test direction
41TAG_POSITIVE = "positive"
42TAG_NEGATIVE = "negative"
43
44# Test tags related to service involvement
Tom Barron69f96962019-07-29 17:07:03 -040045# Only requires that manila-api service running.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030046TAG_API = "api"
Tom Barron69f96962019-07-29 17:07:03 -040047# Requires all manila services running, intended to test back-end
48# (manila-share) behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030049TAG_BACKEND = "backend"
Tom Barron69f96962019-07-29 17:07:03 -040050# Requires all manila services running, intended to test API behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030051TAG_API_WITH_BACKEND = "api_with_backend"
52
53TAGS_MAPPER = {
54 "p": TAG_POSITIVE,
55 "n": TAG_NEGATIVE,
56 "a": TAG_API,
57 "b": TAG_BACKEND,
58 "ab": TAG_API_WITH_BACKEND,
59}
60TAGS_PATTERN = re.compile(
61 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
62 TAGS_MAPPER)
63
64
65def verify_test_has_appropriate_tags(self):
66 if not TAGS_PATTERN.match(self.id()):
67 msg = (
68 "Required attributes either not set or set improperly. "
69 "Two test attributes are expected:\n"
70 " - one of '%(p)s' or '%(n)s' and \n"
71 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
72 ) % TAGS_MAPPER
73 raise self.failureException(msg)
74
Marc Koderer0abc93b2015-07-15 09:18:35 +020075
76class handle_cleanup_exceptions(object):
77 """Handle exceptions raised with cleanup operations.
78
79 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
80 are raised.
81 Suppress all other exceptions only in case config opt
82 'suppress_errors_in_cleanup' in config group 'share' is True.
83 """
84
85 def __enter__(self):
86 return self
87
88 def __exit__(self, exc_type, exc_value, exc_traceback):
89 if not (isinstance(exc_value,
90 (exceptions.NotFound, exceptions.Forbidden)) or
91 CONF.share.suppress_errors_in_cleanup):
92 return False # Do not suppress error if any
93 if exc_traceback:
94 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080095 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020096 return True # Suppress error if any
97
98
99def network_synchronized(f):
100
101 def wrapped_func(self, *args, **kwargs):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200102
103 # Use lock assuming reusage of common network.
104 @lockutils.synchronized("manila_network_lock", external=True)
105 def source_func(self, *args, **kwargs):
106 return f(self, *args, **kwargs)
107
108 return source_func(self, *args, **kwargs)
109
110 return wrapped_func
111
112
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200113skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500114skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200115
116
Marc Koderer0abc93b2015-07-15 09:18:35 +0200117class BaseSharesTest(test.BaseTestCase):
118 """Base test case class for all Manila API tests."""
119
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300120 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200121 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200122 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200123
124 # Will be cleaned up in resource_cleanup
125 class_resources = []
126
127 # Will be cleaned up in tearDown method
128 method_resources = []
129
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100130 # NOTE(andreaf) Override the client manager class to be used, so that
131 # a stable class is used, which includes plugin registered services as well
132 client_manager = clients.Clients
133
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200134 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200135 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200136 raise self.skipException(
137 "Microversion '%s' is not supported." % microversion)
138
Xing Yang69b00b52015-11-22 16:10:44 -0500139 def skip_if_microversion_lt(self, microversion):
140 if utils.is_microversion_lt(CONF.share.max_api_microversion,
141 microversion):
142 raise self.skipException(
143 "Microversion must be greater than or equal to '%s'." %
144 microversion)
145
Marc Koderer0abc93b2015-07-15 09:18:35 +0200146 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500147 def _get_dynamic_creds(cls, name, network_resources=None):
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100148 identity_version = CONF.identity.auth_version
149 if identity_version == 'v3':
150 identity_uri = CONF.identity.uri_v3
151 identity_admin_endpoint_type = CONF.identity.v3_endpoint_type
152 elif identity_version == 'v2':
153 identity_uri = CONF.identity.uri
154 identity_admin_endpoint_type = CONF.identity.v2_admin_endpoint_type
155
Tom Barron4b8834a2017-02-02 11:02:20 -0500156 return dynamic_creds.DynamicCredentialProvider(
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100157 identity_version=identity_version,
Tom Barron4b8834a2017-02-02 11:02:20 -0500158 name=name,
159 network_resources=network_resources,
160 credentials_domain=CONF.auth.default_credentials_domain_name,
161 admin_role=CONF.identity.admin_role,
162 admin_creds=common_creds.get_configured_admin_credentials(),
163 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
164 identity_admin_role=CONF.identity.admin_role,
165 extra_roles=None,
166 neutron_available=CONF.service_available.neutron,
167 create_networks=(
168 CONF.share.create_networks_when_multitenancy_enabled),
169 project_network_cidr=CONF.network.project_network_cidr,
170 project_network_mask_bits=CONF.network.project_network_mask_bits,
171 public_network_id=CONF.network.public_network_id,
ghanshyam2aea7c32017-12-11 00:03:56 +0000172 resource_prefix='tempest',
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100173 identity_admin_endpoint_type=identity_admin_endpoint_type,
174 identity_uri=identity_uri)
Tom Barron4b8834a2017-02-02 11:02:20 -0500175
176 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000177 def skip_checks(cls):
178 super(BaseSharesTest, cls).skip_checks()
179 if not CONF.service_available.manila:
180 raise cls.skipException("Manila support is required")
lkuchlana3b6f7a2020-01-07 10:45:45 +0200181 if not any(p in CONF.share.enable_protocols for p in cls.protocols):
182 skip_msg = "%s tests are disabled" % CONF.share.enable_protocols
183 raise cls.skipException(skip_msg)
Daniel Melladoe5269142017-01-12 12:17:58 +0000184
185 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200186 def verify_nonempty(cls, *args):
187 if not all(args):
188 msg = "Missing API credentials in configuration."
189 raise cls.skipException(msg)
190
191 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300192 def setup_clients(cls):
193 super(BaseSharesTest, cls).setup_clients()
194 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100195 # Initialise share clients for test credentials
196 cls.shares_client = os.share_v1.SharesClient()
197 cls.shares_v2_client = os.share_v2.SharesV2Client()
198 # Initialise network clients for test credentials
199 if CONF.service_available.neutron:
200 cls.networks_client = os.network.NetworksClient()
201 cls.subnets_client = os.network.SubnetsClient()
202 else:
203 cls.networks_client = None
204 cls.subnets_client = None
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300205
206 if CONF.identity.auth_version == 'v3':
207 project_id = os.auth_provider.auth_data[1]['project']['id']
208 else:
209 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
210 cls.tenant_id = project_id
211 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
212
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300213 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300214 if (not CONF.service_available.neutron and
215 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300216 raise cls.skipException("Neutron support is required")
217 share_network_id = cls.provide_share_network(
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100218 cls.shares_v2_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300219 cls.shares_client.share_network_id = share_network_id
220 cls.shares_v2_client.share_network_id = share_network_id
haobing101c7fee2018-03-09 16:33:00 +0800221 resource = {
222 "type": "share_network",
223 "id": share_network_id,
224 "client": cls.shares_v2_client,
225 }
226 cls.class_resources.insert(0, resource)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300227
Marc Koderer0abc93b2015-07-15 09:18:35 +0200228 def setUp(self):
229 super(BaseSharesTest, self).setUp()
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200230 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300231 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200232
233 @classmethod
234 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200235 cls.clear_resources(cls.class_resources)
Sam Wan241029c2016-07-26 03:37:42 -0400236 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200237
238 @classmethod
239 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200240 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300241 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200242 """Used for finding/creating share network for multitenant driver.
243
244 This method creates/gets entity share-network for one tenant. This
245 share-network will be used for creation of service vm.
246
247 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200248 :param networks_client: network client from same tenant as shares
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300249 :param ignore_multitenancy_config: provide a share network regardless
250 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200251 :returns: str -- share network id for shares_client tenant
252 :returns: None -- if single-tenant driver used
253 """
254
255 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300256 search_word = "reusable"
257 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200258
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300259 if (not ignore_multitenancy_config and
260 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200261 # Assumed usage of a single-tenant driver
262 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200263 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300264 if sc.share_network_id:
265 # Share-network already exists, use it
266 share_network_id = sc.share_network_id
267 elif not CONF.share.create_networks_when_multitenancy_enabled:
268 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200269
270 # Try get suitable share-network
271 share_networks = sc.list_share_networks_with_detail()
272 for sn in share_networks:
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300273 net_info = (
274 utils.share_network_get_default_subnet(sn)
275 if utils.share_network_subnets_are_supported() else sn)
276 if net_info is None:
277 continue
278 if(net_info["neutron_net_id"] is None and
279 net_info["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200280 sn["name"] and search_word in sn["name"]):
281 share_network_id = sn["id"]
282 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200283
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300284 # Create new share-network if one was not found
285 if share_network_id is None:
286 sn_desc = "This share-network was created by tempest"
287 sn = sc.create_share_network(name=sn_name,
288 description=sn_desc)
289 share_network_id = sn["id"]
290 else:
291 net_id = subnet_id = share_network_id = None
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700292 # Search for networks, created in previous runs
293 service_net_name = "share-service"
294 networks = networks_client.list_networks()
295 if "networks" in networks.keys():
296 networks = networks["networks"]
297 for network in networks:
298 if (service_net_name in network["name"] and
299 sc.tenant_id == network['tenant_id']):
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300300 net_id = network["id"]
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700301 if len(network["subnets"]) > 0:
302 subnet_id = network["subnets"][0]
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300303 break
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700304
305 # Create suitable network
306 if net_id is None or subnet_id is None:
307 ic = cls._get_dynamic_creds(service_net_name)
308 net_data = ic._create_network_resources(sc.tenant_id)
309 network, subnet, router = net_data
310 net_id = network["id"]
311 subnet_id = subnet["id"]
312
313 # Try get suitable share-network
314 share_networks = sc.list_share_networks_with_detail()
315 for sn in share_networks:
316 net_info = (
317 utils.share_network_get_default_subnet(sn)
318 if utils.share_network_subnets_are_supported()
319 else sn)
320 if net_info is None:
321 continue
322 if (net_id == net_info["neutron_net_id"] and
323 subnet_id == net_info["neutron_subnet_id"] and
324 sn["name"] and search_word in sn["name"]):
325 share_network_id = sn["id"]
326 break
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300327
328 # Create suitable share-network
329 if share_network_id is None:
330 sn_desc = "This share-network was created by tempest"
331 sn = sc.create_share_network(name=sn_name,
332 description=sn_desc,
333 neutron_net_id=net_id,
334 neutron_subnet_id=subnet_id)
335 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200336
337 return share_network_id
338
339 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300340 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200341 snapshot_id=None, description=None, metadata=None,
342 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400343 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400344 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300345 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200346 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400347 share_network_id = (share_network_id or
348 CONF.share.share_network_id or
349 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200350 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300351 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400352 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200353 'share_protocol': share_protocol,
354 'size': size,
355 'name': name,
356 'snapshot_id': snapshot_id,
357 'description': description,
358 'metadata': metadata,
359 'share_network_id': share_network_id,
360 'share_type_id': share_type_id,
361 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400362 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400363 if share_group_id:
364 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400365
Marc Koderer0abc93b2015-07-15 09:18:35 +0200366 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400367 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400368 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200369 cleanup_list = (cls.class_resources if cleanup_in_class else
370 cls.method_resources)
371 cleanup_list.insert(0, resource)
372 return share
373
374 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300375 def migrate_share(
376 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200377 force_host_assisted_migration=False, writable=False,
378 nondisruptive=False, preserve_metadata=False,
379 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300380 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400381 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300382 client.migrate_share(
383 share_id, dest_host,
384 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200385 writable=writable, preserve_metadata=preserve_metadata,
386 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300387 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300388 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200389 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300390 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200391 return share
392
393 @classmethod
394 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
395 client = client or cls.shares_v2_client
396 client.migration_complete(share_id, **kwargs)
397 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300398 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300399 return share
400
401 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300402 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
403 client = client or cls.shares_v2_client
404 client.migration_cancel(share_id, **kwargs)
405 share = client.wait_for_migration_status(
406 share_id, dest_host, 'migration_cancelled', **kwargs)
407 return share
408
409 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200410 def create_share(cls, *args, **kwargs):
411 """Create one share and wait for available state. Retry if allowed."""
412 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
413 return result[0]
414
415 @classmethod
416 def create_shares(cls, share_data_list):
417 """Creates several shares in parallel with retries.
418
419 Use this method when you want to create more than one share at same
420 time. Especially if config option 'share.share_creation_retry_number'
421 has value more than zero (0).
422 All shares will be expected to have 'available' status with or without
423 recreation else error will be raised.
424
425 :param share_data_list: list -- list of dictionaries with 'args' and
426 'kwargs' for '_create_share' method of this base class.
427 example of data:
428 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
429 :returns: list -- list of shares created using provided data.
430 """
431
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300432 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200433 if not isinstance(d, dict):
434 raise exceptions.TempestException(
435 "Expected 'dict', got '%s'" % type(d))
436 if "args" not in d:
437 d["args"] = []
438 if "kwargs" not in d:
439 d["kwargs"] = {}
440 if len(d) > 2:
441 raise exceptions.TempestException(
442 "Expected only 'args' and 'kwargs' keys. "
443 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300444
445 data = []
446 for d in share_data_list:
447 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400448 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300449 local_d = {
450 "args": d["args"],
451 "kwargs": copy.deepcopy(d["kwargs"]),
452 }
453 local_d["kwargs"]["client"] = client
454 local_d["share"] = cls._create_share(
455 *local_d["args"], **local_d["kwargs"])
456 local_d["cnt"] = 0
457 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400458 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300459 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200460
461 while not all(d["available"] for d in data):
462 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400463 if not d["wait_for_status"]:
464 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200465 if d["available"]:
466 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300467 client = d["kwargs"]["client"]
468 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200469 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300470 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200471 d["available"] = True
472 except (share_exceptions.ShareBuildErrorException,
473 exceptions.TimeoutException) as e:
474 if CONF.share.share_creation_retry_number > d["cnt"]:
475 d["cnt"] += 1
476 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300477 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200478 LOG.error(msg)
479 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300480 cg_id = d["kwargs"].get("consistency_group_id")
481 if cg_id:
482 # NOTE(vponomaryov): delete errored share
483 # immediately in case share is part of CG.
484 client.delete_share(
485 share_id,
486 params={"consistency_group_id": cg_id})
487 client.wait_for_resource_deletion(
488 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200489 d["share"] = cls._create_share(
490 *d["args"], **d["kwargs"])
491 else:
gecong197358663802016-08-25 11:08:45 +0800492 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200493
494 return [d["share"] for d in data]
495
496 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400497 def create_share_group(cls, client=None, cleanup_in_class=True,
498 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400499 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400500 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400501 kwargs['share_network_id'] = (share_network_id or
502 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400503 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400504 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400505 "type": "share_group",
506 "id": share_group["id"],
507 "client": client,
508 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400509 if cleanup_in_class:
510 cls.class_resources.insert(0, resource)
511 else:
512 cls.method_resources.insert(0, resource)
513
Andrew Kerrb8436922016-06-01 15:32:43 -0400514 if kwargs.get('source_share_group_snapshot_id'):
515 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400516 detailed=True,
silvacarloss6e575682020-02-18 19:52:35 -0300517 params={'share_group_id': share_group['id']})
Andrew Kerrbf31e912015-07-29 10:39:38 -0400518
Andrew Kerrb8436922016-06-01 15:32:43 -0400519 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400520 resource = {"type": "share",
521 "id": share["id"],
522 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400523 "share_group_id": share.get("share_group_id")}
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 client.wait_for_share_group_status(share_group['id'], 'available')
530 return share_group
531
532 @classmethod
533 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
534 group_specs=None, client=None,
535 cleanup_in_class=True, **kwargs):
536 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300537 if (group_specs is None and
538 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300539 group_specs = {
540 'consistent_snapshot_support': (
541 CONF.share.capability_sg_consistent_snapshot_support),
542 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400543 share_group_type = client.create_share_group_type(
544 name=name,
545 share_types=share_types,
546 is_public=is_public,
547 group_specs=group_specs,
548 **kwargs)
549 resource = {
550 "type": "share_group_type",
551 "id": share_group_type["id"],
552 "client": client,
553 }
554 if cleanup_in_class:
555 cls.class_resources.insert(0, resource)
556 else:
557 cls.method_resources.insert(0, resource)
558 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400559
560 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200561 def create_snapshot_wait_for_active(cls, share_id, name=None,
562 description=None, force=False,
563 client=None, cleanup_in_class=True):
564 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400565 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200566 if description is None:
567 description = "Tempest's snapshot"
568 snapshot = client.create_snapshot(share_id, name, description, force)
569 resource = {
570 "type": "snapshot",
571 "id": snapshot["id"],
572 "client": client,
573 }
574 if cleanup_in_class:
575 cls.class_resources.insert(0, resource)
576 else:
577 cls.method_resources.insert(0, resource)
578 client.wait_for_snapshot_status(snapshot["id"], "available")
579 return snapshot
580
581 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400582 def create_share_group_snapshot_wait_for_active(
583 cls, share_group_id, name=None, description=None, client=None,
584 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400585 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400586 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400587 description = "Tempest's share group snapshot"
588 sg_snapshot = client.create_share_group_snapshot(
589 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400590 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400591 "type": "share_group_snapshot",
592 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400593 "client": client,
594 }
595 if cleanup_in_class:
596 cls.class_resources.insert(0, resource)
597 else:
598 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400599 client.wait_for_share_group_snapshot_status(
600 sg_snapshot["id"], "available")
601 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400602
603 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800604 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400605 """List the availability zones for "manila-share" services
606
607 that are currently in "up" state.
608 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800609 client = client or cls.admin_shares_v2_client
610 backends = (
611 '|'.join(['^%s$' % backend for backend in backends])
612 if backends else '.*'
613 )
Yogeshbdb88102015-09-29 23:41:02 -0400614 cls.services = client.list_services()
615 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800616 service['binary'] == 'manila-share' and
617 service['state'] == 'up' and
618 re.search(backends, service['host'])]
Douglas Viroel161f1802020-04-25 17:18:14 -0300619 return list(set(zones))
Yogeshbdb88102015-09-29 23:41:02 -0400620
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800621 @classmethod
622 def get_pools_matching_share_type(cls, share_type, client=None):
623 client = client or cls.admin_shares_v2_client
624 if utils.is_microversion_supported('2.23'):
625 return client.list_pools(
andrebeltrami3b4d4852020-02-04 19:11:54 +0000626 detail=True,
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800627 search_opts={'share_type': share_type['id']})['pools']
628
629 pools = client.list_pools(detail=True)['pools']
630 share_type = client.get_share_type(share_type['id'])['share_type']
631 extra_specs = {}
632 for k, v in share_type['extra_specs'].items():
633 extra_specs[k] = (
634 True if six.text_type(v).lower() == 'true'
635 else False if six.text_type(v).lower() == 'false' else v
636 )
637 return [
638 pool for pool in pools if all(y in pool['capabilities'].items()
639 for y in extra_specs.items())
640 ]
641
642 @classmethod
643 def get_availability_zones_matching_share_type(cls, share_type,
644 client=None):
645
646 client = client or cls.admin_shares_v2_client
647 pools_matching_share_type = cls.get_pools_matching_share_type(
648 share_type, client=client)
649 backends_matching_share_type = set(
650 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
651 )
652 azs = cls.get_availability_zones(backends=backends_matching_share_type)
653 return azs
654
Yogesh1f931ff2015-09-29 23:41:02 -0400655 def get_pools_for_replication_domain(self):
656 # Get the list of pools for the replication domain
657 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500658 instance_host = self.admin_client.get_share(
659 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400660 host_pool = [p for p in pools if p['name'] == instance_host][0]
661 rep_domain = host_pool['capabilities']['replication_domain']
662 pools_in_rep_domain = [p for p in pools if p['capabilities'][
663 'replication_domain'] == rep_domain]
664 return rep_domain, pools_in_rep_domain
665
Yogeshbdb88102015-09-29 23:41:02 -0400666 @classmethod
667 def create_share_replica(cls, share_id, availability_zone, client=None,
668 cleanup_in_class=False, cleanup=True):
669 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300670 replica = client.create_share_replica(
671 share_id, availability_zone=availability_zone)
Yogeshbdb88102015-09-29 23:41:02 -0400672 resource = {
673 "type": "share_replica",
674 "id": replica["id"],
675 "client": client,
676 "share_id": share_id,
677 }
678 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
679 if cleanup:
680 if cleanup_in_class:
681 cls.class_resources.insert(0, resource)
682 else:
683 cls.method_resources.insert(0, resource)
684 client.wait_for_share_replica_status(
685 replica["id"], constants.STATUS_AVAILABLE)
686 return replica
687
688 @classmethod
689 def delete_share_replica(cls, replica_id, client=None):
690 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400691 try:
692 client.delete_share_replica(replica_id)
693 client.wait_for_resource_deletion(replica_id=replica_id)
694 except exceptions.NotFound:
695 pass
Yogeshbdb88102015-09-29 23:41:02 -0400696
697 @classmethod
698 def promote_share_replica(cls, replica_id, client=None):
699 client = client or cls.shares_v2_client
700 replica = client.promote_share_replica(replica_id)
701 client.wait_for_share_replica_status(
702 replica["id"],
703 constants.REPLICATION_STATE_ACTIVE,
704 status_attr="replica_state")
705 return replica
706
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700707 @classmethod
708 def _get_access_rule_data_from_config(cls):
yogeshdb32f462016-09-28 15:09:50 -0400709 """Get the first available access type/to combination from config.
710
711 This method opportunistically picks the first configured protocol
712 to create the share. Do not use this method in tests where you need
713 to test depth and breadth in the access types and access recipients.
714 """
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700715 protocol = cls.shares_v2_client.share_protocol
yogeshdb32f462016-09-28 15:09:50 -0400716
717 if protocol in CONF.share.enable_ip_rules_for_protocols:
718 access_type = "ip"
719 access_to = utils.rand_ip()
720 elif protocol in CONF.share.enable_user_rules_for_protocols:
721 access_type = "user"
722 access_to = CONF.share.username_for_user_rules
723 elif protocol in CONF.share.enable_cert_rules_for_protocols:
724 access_type = "cert"
725 access_to = "client3.com"
726 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
727 access_type = "cephx"
728 access_to = "eve"
729 else:
730 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700731 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400732
733 return access_type, access_to
734
Yogeshbdb88102015-09-29 23:41:02 -0400735 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200736 def create_share_network(cls, client=None,
737 cleanup_in_class=False, **kwargs):
738 if client is None:
739 client = cls.shares_client
740 share_network = client.create_share_network(**kwargs)
741 resource = {
742 "type": "share_network",
743 "id": share_network["id"],
744 "client": client,
745 }
746 if cleanup_in_class:
747 cls.class_resources.insert(0, resource)
748 else:
749 cls.method_resources.insert(0, resource)
750 return share_network
751
752 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300753 def create_share_network_subnet(cls, client=None,
754 cleanup_in_class=False, **kwargs):
755 if client is None:
756 client = cls.shares_v2_client
757 share_network_subnet = client.create_subnet(**kwargs)
758 resource = {
759 "type": "share-network-subnet",
760 "id": share_network_subnet["id"],
761 "extra_params": {
762 "share_network_id": share_network_subnet["share_network_id"]
763 },
764 "client": client,
765 }
766 if cleanup_in_class:
767 cls.class_resources.insert(0, resource)
768 else:
769 cls.method_resources.insert(0, resource)
770 return share_network_subnet
771
772 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200773 def create_security_service(cls, ss_type="ldap", client=None,
774 cleanup_in_class=False, **kwargs):
775 if client is None:
776 client = cls.shares_client
777 security_service = client.create_security_service(ss_type, **kwargs)
778 resource = {
779 "type": "security_service",
780 "id": security_service["id"],
781 "client": client,
782 }
783 if cleanup_in_class:
784 cls.class_resources.insert(0, resource)
785 else:
786 cls.method_resources.insert(0, resource)
787 return security_service
788
789 @classmethod
790 def create_share_type(cls, name, is_public=True, client=None,
791 cleanup_in_class=True, **kwargs):
792 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200793 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200794 share_type = client.create_share_type(name, is_public, **kwargs)
795 resource = {
796 "type": "share_type",
797 "id": share_type["share_type"]["id"],
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_type
805
haixin0d1d29f2019-08-02 16:50:45 +0800806 @classmethod
807 def update_share_type(cls, share_type_id, name=None,
808 is_public=None, description=None,
809 client=None):
810 if client is None:
811 client = cls.shares_v2_client
812 share_type = client.update_share_type(share_type_id, name,
813 is_public, description)
814 return share_type
815
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700816 @classmethod
817 def update_quotas(cls, project_id, user_id=None, cleanup=True,
818 client=None, **kwargs):
819 client = client or cls.shares_v2_client
820 updated_quotas = client.update_quotas(project_id,
821 user_id=user_id,
822 **kwargs)
823 resource = {
824 "type": "quotas",
825 "id": project_id,
826 "client": client,
827 "user_id": user_id,
828 }
829 if cleanup:
830 cls.method_resources.insert(0, resource)
831 return updated_quotas
832
Marc Koderer0abc93b2015-07-15 09:18:35 +0200833 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400834 def add_extra_specs_to_dict(extra_specs=None):
835 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300836 dhss = six.text_type(CONF.share.multitenancy_enabled)
837 snapshot_support = six.text_type(
838 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400839 create_from_snapshot_support = six.text_type(
840 CONF.share.capability_create_share_from_snapshot_support)
841
842 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300843 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200844 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400845
846 optional = {
847 "snapshot_support": snapshot_support,
848 "create_share_from_snapshot_support": create_from_snapshot_support,
849 }
850 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
851 # required extra-spec
852 extra_specs_dict.update(optional)
853
Marc Koderer0abc93b2015-07-15 09:18:35 +0200854 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400855 extra_specs_dict.update(extra_specs)
856
857 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200858
859 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400860 def clear_share_replicas(cls, share_id, client=None):
861 client = client or cls.shares_v2_client
862 share_replicas = client.list_share_replicas(
863 share_id=share_id)
864
865 for replica in share_replicas:
866 try:
867 cls.delete_share_replica(replica['id'])
868 except exceptions.BadRequest:
869 # Ignore the exception due to deletion of last active replica
870 pass
871
872 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200873 def clear_resources(cls, resources=None):
874 """Deletes resources, that were created in test suites.
875
876 This method tries to remove resources from resource list,
877 if it is not found, assumed it was deleted in test itself.
878 It is expected, that all resources were added as LIFO
879 due to restriction of deletion resources, that is in the chain.
880
881 :param resources: dict with keys 'type','id','client' and 'deleted'
882 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200883 if resources is None:
884 resources = cls.method_resources
885 for res in resources:
886 if "deleted" not in res.keys():
887 res["deleted"] = False
888 if "client" not in res.keys():
889 res["client"] = cls.shares_client
890 if not(res["deleted"]):
891 res_id = res['id']
892 client = res["client"]
893 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200894 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400895 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400896 share_group_id = res.get('share_group_id')
897 if share_group_id:
898 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400899 client.delete_share(res_id, params=params)
900 else:
901 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200902 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200903 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200904 client.delete_snapshot(res_id)
905 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200906 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400907 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400908 client.delete_share_network(res_id)
909 client.wait_for_resource_deletion(sn_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200910 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200911 client.delete_security_service(res_id)
912 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200913 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200914 client.delete_share_type(res_id)
915 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200916 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -0400917 client.delete_share_group(res_id)
918 client.wait_for_resource_deletion(
919 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200920 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -0400921 client.delete_share_group_type(res_id)
922 client.wait_for_resource_deletion(
923 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200924 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -0400925 client.delete_share_group_snapshot(res_id)
926 client.wait_for_resource_deletion(
927 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200928 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -0400929 client.delete_share_replica(res_id)
930 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200931 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300932 sn_id = res["extra_params"]["share_network_id"]
933 client.delete_subnet(sn_id, res_id)
934 client.wait_for_resource_deletion(
935 share_network_subnet_id=res_id,
936 sn_id=sn_id)
Goutham Pacha Raviaf448262020-06-29 14:24:13 -0700937 elif res["type"] == "quotas":
938 user_id = res.get('user_id')
939 client.reset_quotas(res_id, user_id=user_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200940 else:
huayue97bacbf2016-01-04 09:57:39 +0800941 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800942 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200943 res["deleted"] = True
944
945 @classmethod
946 def generate_share_network_data(self):
947 data = {
948 "name": data_utils.rand_name("sn-name"),
949 "description": data_utils.rand_name("sn-desc"),
950 "neutron_net_id": data_utils.rand_name("net-id"),
951 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
952 }
953 return data
954
955 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300956 def generate_subnet_data(self):
957 data = {
958 "neutron_net_id": data_utils.rand_name("net-id"),
959 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
960 }
961 return data
962
963 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100964 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200965 data = {
966 "name": data_utils.rand_name("ss-name"),
967 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200968 "dns_ip": utils.rand_ip(),
969 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200970 "domain": data_utils.rand_name("ss-domain"),
971 "user": data_utils.rand_name("ss-user"),
972 "password": data_utils.rand_name("ss-password"),
973 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100974 if set_ou:
975 data["ou"] = data_utils.rand_name("ss-ou")
976
Marc Koderer0abc93b2015-07-15 09:18:35 +0200977 return data
978
979 # Useful assertions
980 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
981 """Assert two dicts are equivalent.
982
983 This is a 'deep' match in the sense that it handles nested
984 dictionaries appropriately.
985
986 NOTE:
987
988 If you don't care (or don't know) a given value, you can specify
989 the string DONTCARE as the value. This will cause that dict-item
990 to be skipped.
991
992 """
993 def raise_assertion(msg):
994 d1str = str(d1)
995 d2str = str(d2)
996 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
997 'd2: %(d2str)s' %
998 {"msg": msg, "d1str": d1str, "d2str": d2str})
999 raise AssertionError(base_msg)
1000
1001 d1keys = set(d1.keys())
1002 d2keys = set(d2.keys())
1003 if d1keys != d2keys:
1004 d1only = d1keys - d2keys
1005 d2only = d2keys - d1keys
1006 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1007 'Keys in d2 and not d1: %(d2only)s' %
1008 {"d1only": d1only, "d2only": d2only})
1009
1010 for key in d1keys:
1011 d1value = d1[key]
1012 d2value = d2[key]
1013 try:
1014 error = abs(float(d1value) - float(d2value))
1015 within_tolerance = error <= tolerance
1016 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001017 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001018 # ValueError if arg is a str, TypeError if it's something else
1019 # (like None)
1020 within_tolerance = False
1021
1022 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1023 self.assertDictMatch(d1value, d2value)
1024 elif 'DONTCARE' in (d1value, d2value):
1025 continue
1026 elif approx_equal and within_tolerance:
1027 continue
1028 elif d1value != d2value:
1029 raise_assertion("d1['%(key)s']=%(d1value)s != "
1030 "d2['%(key)s']=%(d2value)s" %
1031 {
1032 "key": key,
1033 "d1value": d1value,
1034 "d2value": d2value
1035 })
1036
Alex Meadeba8a1602016-05-06 09:33:09 -04001037 def create_user_message(self):
1038 """Trigger a 'no valid host' situation to generate a message."""
1039 extra_specs = {
1040 'vendor_name': 'foobar',
1041 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1042 }
1043 share_type_name = data_utils.rand_name("share-type")
1044
1045 bogus_type = self.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001046 client=self.admin_shares_v2_client,
Alex Meadeba8a1602016-05-06 09:33:09 -04001047 name=share_type_name,
1048 extra_specs=extra_specs)['share_type']
1049
1050 params = {'share_type_id': bogus_type['id'],
1051 'share_network_id': self.shares_v2_client.share_network_id}
1052 share = self.shares_v2_client.create_share(**params)
1053 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1054 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1055 return self.shares_v2_client.wait_for_message(share['id'])
1056
Marc Koderer0abc93b2015-07-15 09:18:35 +02001057
1058class BaseSharesAltTest(BaseSharesTest):
1059 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001060 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001061
1062
1063class BaseSharesAdminTest(BaseSharesTest):
1064 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001065 credentials = ('admin', )
1066
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001067 @classmethod
1068 def setup_clients(cls):
1069 super(BaseSharesAdminTest, cls).setup_clients()
1070 # Initialise share clients
1071 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1072
1073 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001074 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001075 name = data_utils.rand_name("unique_st_name")
1076 extra_specs = cls.add_extra_specs_to_dict(specs)
1077 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001078 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001079 client=cls.admin_shares_v2_client)['share_type']
1080
1081 @classmethod
1082 def _create_share_group_type(cls):
1083 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1084 return cls.create_share_group_type(
1085 name=share_group_type_name, share_types=[cls.share_type_id],
1086 client=cls.admin_shares_v2_client)
1087
Lucio Seki37056942019-01-24 15:40:20 -02001088 def _create_share_for_manage(self):
1089 creation_data = {
1090 'share_type_id': self.st['share_type']['id'],
1091 'share_protocol': self.protocol,
1092 }
1093
1094 share = self.create_share(**creation_data)
1095 share = self.shares_v2_client.get_share(share['id'])
1096
1097 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1098 el = self.shares_v2_client.list_share_export_locations(share["id"])
1099 share["export_locations"] = el
1100
1101 return share
1102
1103 def _unmanage_share_and_wait(self, share):
1104 self.shares_v2_client.unmanage_share(share['id'])
1105 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1106
1107 def _reset_state_and_delete_share(self, share):
1108 self.shares_v2_client.reset_state(share['id'])
1109 self._delete_share_and_wait(share)
1110
1111 def _delete_snapshot_and_wait(self, snap):
1112 self.shares_v2_client.delete_snapshot(snap['id'])
1113 self.shares_v2_client.wait_for_resource_deletion(
1114 snapshot_id=snap['id']
1115 )
1116 self.assertRaises(exceptions.NotFound,
1117 self.shares_v2_client.get_snapshot,
1118 snap['id'])
1119
1120 def _delete_share_and_wait(self, share):
1121 self.shares_v2_client.delete_share(share['id'])
1122 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1123 self.assertRaises(exceptions.NotFound,
1124 self.shares_v2_client.get_share,
1125 share['id'])
1126
1127 def _manage_share(self, share, name, description, share_server_id):
1128 managed_share = self.shares_v2_client.manage_share(
1129 service_host=share['host'],
1130 export_path=share['export_locations'][0],
1131 protocol=share['share_proto'],
1132 share_type_id=self.share_type['share_type']['id'],
1133 name=name,
1134 description=description,
1135 share_server_id=share_server_id
1136 )
1137 self.shares_v2_client.wait_for_share_status(
1138 managed_share['id'], constants.STATUS_AVAILABLE
1139 )
1140
1141 return managed_share
1142
1143 def _unmanage_share_server_and_wait(self, server):
1144 self.shares_v2_client.unmanage_share_server(server['id'])
1145 self.shares_v2_client.wait_for_resource_deletion(
1146 server_id=server['id']
1147 )
1148
1149 def _manage_share_server(self, share_server, fields=None):
1150 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001151 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001152 managed_share_server = self.shares_v2_client.manage_share_server(
1153 params.get('host', share_server['host']),
1154 params.get('share_network_id', share_server['share_network_id']),
1155 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001156 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001157 )
1158 self.shares_v2_client.wait_for_share_server_status(
1159 managed_share_server['id'],
1160 constants.SERVER_STATE_ACTIVE,
1161 )
1162
1163 return managed_share_server
1164
1165 def _delete_share_server_and_wait(self, share_server_id):
1166 self.shares_v2_client.delete_share_server(
1167 share_server_id
1168 )
1169 self.shares_v2_client.wait_for_resource_deletion(
1170 server_id=share_server_id)
1171
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001172
1173class BaseSharesMixedTest(BaseSharesTest):
1174 """Base test case class for all Shares API tests with all user roles."""
1175 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001176
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001177 # Will be cleaned up in resource_cleanup if the class
1178 class_project_users_created = []
1179
1180 @classmethod
1181 def resource_cleanup(cls):
1182 cls.clear_project_users(cls.class_project_users_created)
1183 super(BaseSharesMixedTest, cls).resource_cleanup()
1184
1185 @classmethod
1186 def clear_project_users(cls, users=None):
1187 users = users or cls.class_project_users_created
1188 for user in users:
1189 with handle_cleanup_exceptions():
1190 cls.os_admin.creds_client.delete_user(user['id'])
1191
Marc Koderer0abc93b2015-07-15 09:18:35 +02001192 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001193 def setup_clients(cls):
1194 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001195 # Initialise share clients
1196 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1197 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1198 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1199 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1200 # Initialise network clients
1201 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1202 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001203 # Initialise identity clients
1204 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1205 identity_clients = getattr(
1206 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1207 cls.os_admin.identity_client = identity_clients.IdentityClient()
1208 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1209 cls.os_admin.users_client = identity_clients.UsersClient()
1210 cls.os_admin.roles_client = identity_clients.RolesClient()
1211 cls.os_admin.domains_client = (
1212 cls.os_admin.identity_v3.DomainsClient() if
1213 CONF.identity.auth_version == 'v3' else None)
1214 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001215
1216 if CONF.share.multitenancy_enabled:
1217 admin_share_network_id = cls.provide_share_network(
1218 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1219 cls.admin_shares_client.share_network_id = admin_share_network_id
1220 cls.admin_shares_v2_client.share_network_id = (
1221 admin_share_network_id)
1222
1223 alt_share_network_id = cls.provide_share_network(
1224 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1225 cls.alt_shares_client.share_network_id = alt_share_network_id
1226 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001227 resource = {
1228 "type": "share_network",
1229 "id": alt_share_network_id,
1230 "client": cls.alt_shares_v2_client,
1231 }
1232 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001233
1234 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001235 def create_user_and_get_client(cls, project=None):
1236 """Create a user in specified project & set share clients for user
1237
1238 The user will have all roles specified in tempest.conf
1239 :param: project: a dictionary with project ID and name, if not
1240 specified, the value will be cls.admin_project
1241 """
1242 project_domain_name = (
1243 cls.os_admin.identity_client.auth_provider.credentials.get(
1244 'project_domain_name', 'Default'))
1245 cls.os_admin.creds_client = cred_client.get_creds_client(
1246 cls.os_admin.identity_client, cls.os_admin.projects_client,
1247 cls.os_admin.users_client, cls.os_admin.roles_client,
1248 cls.os_admin.domains_client, project_domain_name)
1249
1250 # User info
1251 project = project or cls.admin_project
1252 username = data_utils.rand_name('manila_%s' % project['id'])
1253 password = data_utils.rand_password()
1254 email = '%s@example.org' % username
1255
1256 user = cls.os_admin.creds_client.create_user(
1257 username, password, project, email)
1258 cls.class_project_users_created.append(user)
1259
1260 for conf_role in CONF.auth.tempest_roles:
1261 cls.os_admin.creds_client.assign_user_role(
1262 user, project, conf_role)
1263
1264 user_creds = cls.os_admin.creds_client.get_credentials(
1265 user, project, password)
1266 os = clients.Clients(user_creds)
1267 os.shares_v1_client = os.share_v1.SharesClient()
1268 os.shares_v2_client = os.share_v2.SharesV2Client()
1269 return os
1270
1271 @classmethod
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001272 def _create_share_type(cls, is_public=True, specs=None):
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001273 name = data_utils.rand_name("unique_st_name")
1274 extra_specs = cls.add_extra_specs_to_dict(specs)
1275 return cls.create_share_type(
Goutham Pacha Raviaf448262020-06-29 14:24:13 -07001276 name, extra_specs=extra_specs, is_public=is_public,
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001277 client=cls.admin_shares_v2_client)['share_type']
1278
1279 @classmethod
1280 def _create_share_group_type(cls):
1281 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1282 return cls.create_share_group_type(
1283 name=share_group_type_name, share_types=[cls.share_type_id],
1284 client=cls.admin_shares_v2_client)