blob: 232b15a39875c0150928983baf8a7b98a568a32d [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
17import inspect
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030018import re
Marc Koderer0abc93b2015-07-15 09:18:35 +020019import traceback
20
21from oslo_concurrency import lockutils
22from oslo_log import log
23import six
Sam Wanc7b7f1f2015-11-25 00:22:28 -050024from tempest.common import credentials_factory as common_creds
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010025
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020026from tempest import config
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):
102 with_isolated_creds = True if len(args) > 2 else False
103 no_lock_required = kwargs.get(
104 "isolated_creds_client", with_isolated_creds)
105 if no_lock_required:
106 # Usage of not reusable network. No need in lock.
107 return f(self, *args, **kwargs)
108
109 # Use lock assuming reusage of common network.
110 @lockutils.synchronized("manila_network_lock", external=True)
111 def source_func(self, *args, **kwargs):
112 return f(self, *args, **kwargs)
113
114 return source_func(self, *args, **kwargs)
115
116 return wrapped_func
117
118
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200119skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500120skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200121
122
Marc Koderer0abc93b2015-07-15 09:18:35 +0200123class BaseSharesTest(test.BaseTestCase):
124 """Base test case class for all Manila API tests."""
125
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300126 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200127 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200128 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200129
130 # Will be cleaned up in resource_cleanup
131 class_resources = []
132
133 # Will be cleaned up in tearDown method
134 method_resources = []
135
136 # Will be cleaned up in resource_cleanup
137 class_isolated_creds = []
138
139 # Will be cleaned up in tearDown method
140 method_isolated_creds = []
141
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100142 # NOTE(andreaf) Override the client manager class to be used, so that
143 # a stable class is used, which includes plugin registered services as well
144 client_manager = clients.Clients
145
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200146 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200147 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200148 raise self.skipException(
149 "Microversion '%s' is not supported." % microversion)
150
Xing Yang69b00b52015-11-22 16:10:44 -0500151 def skip_if_microversion_lt(self, microversion):
152 if utils.is_microversion_lt(CONF.share.max_api_microversion,
153 microversion):
154 raise self.skipException(
155 "Microversion must be greater than or equal to '%s'." %
156 microversion)
157
Marc Koderer0abc93b2015-07-15 09:18:35 +0200158 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500159 def _get_dynamic_creds(cls, name, network_resources=None):
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100160 identity_version = CONF.identity.auth_version
161 if identity_version == 'v3':
162 identity_uri = CONF.identity.uri_v3
163 identity_admin_endpoint_type = CONF.identity.v3_endpoint_type
164 elif identity_version == 'v2':
165 identity_uri = CONF.identity.uri
166 identity_admin_endpoint_type = CONF.identity.v2_admin_endpoint_type
167
Tom Barron4b8834a2017-02-02 11:02:20 -0500168 return dynamic_creds.DynamicCredentialProvider(
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100169 identity_version=identity_version,
Tom Barron4b8834a2017-02-02 11:02:20 -0500170 name=name,
171 network_resources=network_resources,
172 credentials_domain=CONF.auth.default_credentials_domain_name,
173 admin_role=CONF.identity.admin_role,
174 admin_creds=common_creds.get_configured_admin_credentials(),
175 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
176 identity_admin_role=CONF.identity.admin_role,
177 extra_roles=None,
178 neutron_available=CONF.service_available.neutron,
179 create_networks=(
180 CONF.share.create_networks_when_multitenancy_enabled),
181 project_network_cidr=CONF.network.project_network_cidr,
182 project_network_mask_bits=CONF.network.project_network_mask_bits,
183 public_network_id=CONF.network.public_network_id,
ghanshyam2aea7c32017-12-11 00:03:56 +0000184 resource_prefix='tempest',
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100185 identity_admin_endpoint_type=identity_admin_endpoint_type,
186 identity_uri=identity_uri)
Tom Barron4b8834a2017-02-02 11:02:20 -0500187
188 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200189 def get_client_with_isolated_creds(cls,
190 name=None,
191 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400192 cleanup_in_class=False,
193 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200194 """Creates isolated creds.
195
196 :param name: name, will be used for naming ic and related stuff
197 :param type_of_creds: admin, alt or primary
198 :param cleanup_in_class: defines place where to delete
199 :returns: SharesClient -- shares client with isolated creds.
200 :returns: To client added dict attr 'creds' with
201 :returns: key elements 'tenant' and 'user'.
202 """
203 if name is None:
204 # Get name of test method
205 name = inspect.stack()[1][3]
206 if len(name) > 32:
207 name = name[0:32]
208
209 # Choose type of isolated creds
Tom Barron4b8834a2017-02-02 11:02:20 -0500210 ic = cls._get_dynamic_creds(name)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200211 if "admin" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200212 creds = ic.get_admin_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200213 elif "alt" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200214 creds = ic.get_alt_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200215 else:
Marc Koderer5880b362016-07-06 10:59:07 +0200216 creds = ic.get_credentials(type_of_creds).credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200217 ic.type_of_creds = type_of_creds
218
219 # create client with isolated creds
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100220 os = clients.Clients(creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400221 if client_version == '1':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100222 client = os.share_v1.SharesClient()
Clinton Knighte5c8f092015-08-27 15:00:23 -0400223 elif client_version == '2':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100224 client = os.share_v2.SharesV2Client()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200225
226 # Set place where will be deleted isolated creds
227 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200228 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200229 "deleted": False,
230 }
231 if cleanup_in_class:
232 cls.class_isolated_creds.insert(0, ic_res)
233 else:
234 cls.method_isolated_creds.insert(0, ic_res)
235
236 # Provide share network
237 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300238 if (not CONF.service_available.neutron and
239 CONF.share.create_networks_when_multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200240 raise cls.skipException("Neutron support is required")
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100241 nc = os.network.NetworksClient()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200242 share_network_id = cls.provide_share_network(client, nc, ic)
243 client.share_network_id = share_network_id
244 resource = {
245 "type": "share_network",
246 "id": client.share_network_id,
247 "client": client,
248 }
249 if cleanup_in_class:
250 cls.class_resources.insert(0, resource)
251 else:
252 cls.method_resources.insert(0, resource)
253 return client
254
255 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000256 def skip_checks(cls):
257 super(BaseSharesTest, cls).skip_checks()
258 if not CONF.service_available.manila:
259 raise cls.skipException("Manila support is required")
lkuchlana3b6f7a2020-01-07 10:45:45 +0200260 if not any(p in CONF.share.enable_protocols for p in cls.protocols):
261 skip_msg = "%s tests are disabled" % CONF.share.enable_protocols
262 raise cls.skipException(skip_msg)
Daniel Melladoe5269142017-01-12 12:17:58 +0000263
264 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200265 def verify_nonempty(cls, *args):
266 if not all(args):
267 msg = "Missing API credentials in configuration."
268 raise cls.skipException(msg)
269
270 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300271 def setup_clients(cls):
272 super(BaseSharesTest, cls).setup_clients()
273 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100274 # Initialise share clients for test credentials
275 cls.shares_client = os.share_v1.SharesClient()
276 cls.shares_v2_client = os.share_v2.SharesV2Client()
277 # Initialise network clients for test credentials
278 if CONF.service_available.neutron:
279 cls.networks_client = os.network.NetworksClient()
280 cls.subnets_client = os.network.SubnetsClient()
281 else:
282 cls.networks_client = None
283 cls.subnets_client = None
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300284
285 if CONF.identity.auth_version == 'v3':
286 project_id = os.auth_provider.auth_data[1]['project']['id']
287 else:
288 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
289 cls.tenant_id = project_id
290 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
291
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300292 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300293 if (not CONF.service_available.neutron and
294 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300295 raise cls.skipException("Neutron support is required")
296 share_network_id = cls.provide_share_network(
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100297 cls.shares_v2_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300298 cls.shares_client.share_network_id = share_network_id
299 cls.shares_v2_client.share_network_id = share_network_id
haobing101c7fee2018-03-09 16:33:00 +0800300 resource = {
301 "type": "share_network",
302 "id": share_network_id,
303 "client": cls.shares_v2_client,
304 }
305 cls.class_resources.insert(0, resource)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300306
Marc Koderer0abc93b2015-07-15 09:18:35 +0200307 def setUp(self):
308 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200309 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200310 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300311 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200312
313 @classmethod
314 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200315 cls.clear_resources(cls.class_resources)
316 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400317 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200318
319 @classmethod
320 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200321 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300322 isolated_creds_client=None,
323 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200324 """Used for finding/creating share network for multitenant driver.
325
326 This method creates/gets entity share-network for one tenant. This
327 share-network will be used for creation of service vm.
328
329 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200330 :param networks_client: network client from same tenant as shares
331 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200332 If provided, then its networking will be used if needed.
333 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300334 :param ignore_multitenancy_config: provide a share network regardless
335 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200336 :returns: str -- share network id for shares_client tenant
337 :returns: None -- if single-tenant driver used
338 """
339
340 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300341 search_word = "reusable"
342 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200343
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300344 if (not ignore_multitenancy_config and
345 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200346 # Assumed usage of a single-tenant driver
347 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200348 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300349 if sc.share_network_id:
350 # Share-network already exists, use it
351 share_network_id = sc.share_network_id
352 elif not CONF.share.create_networks_when_multitenancy_enabled:
353 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200354
355 # Try get suitable share-network
356 share_networks = sc.list_share_networks_with_detail()
357 for sn in share_networks:
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300358 net_info = (
359 utils.share_network_get_default_subnet(sn)
360 if utils.share_network_subnets_are_supported() else sn)
361 if net_info is None:
362 continue
363 if(net_info["neutron_net_id"] is None and
364 net_info["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200365 sn["name"] and search_word in sn["name"]):
366 share_network_id = sn["id"]
367 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200368
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300369 # Create new share-network if one was not found
370 if share_network_id is None:
371 sn_desc = "This share-network was created by tempest"
372 sn = sc.create_share_network(name=sn_name,
373 description=sn_desc)
374 share_network_id = sn["id"]
375 else:
376 net_id = subnet_id = share_network_id = None
377
378 if not isolated_creds_client:
379 # Search for networks, created in previous runs
380 service_net_name = "share-service"
381 networks = networks_client.list_networks()
382 if "networks" in networks.keys():
383 networks = networks["networks"]
384 for network in networks:
385 if (service_net_name in network["name"] and
386 sc.tenant_id == network['tenant_id']):
387 net_id = network["id"]
388 if len(network["subnets"]) > 0:
389 subnet_id = network["subnets"][0]
390 break
391
392 # Create suitable network
393 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500394 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300395 net_data = ic._create_network_resources(sc.tenant_id)
396 network, subnet, router = net_data
397 net_id = network["id"]
398 subnet_id = subnet["id"]
399
400 # Try get suitable share-network
401 share_networks = sc.list_share_networks_with_detail()
402 for sn in share_networks:
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300403 net_info = (
404 utils.share_network_get_default_subnet(sn)
405 if utils.share_network_subnets_are_supported()
406 else sn)
407 if net_info is None:
408 continue
409 if (net_id == net_info["neutron_net_id"] and
410 subnet_id == net_info["neutron_subnet_id"] and
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300411 sn["name"] and search_word in sn["name"]):
412 share_network_id = sn["id"]
413 break
414 else:
415 sn_name = "autogenerated_by_tempest_for_isolated_creds"
416 # Use precreated network and subnet from isolated creds
417 net_id = isolated_creds_client.get_credentials(
418 isolated_creds_client.type_of_creds).network['id']
419 subnet_id = isolated_creds_client.get_credentials(
420 isolated_creds_client.type_of_creds).subnet['id']
421
422 # Create suitable share-network
423 if share_network_id is None:
424 sn_desc = "This share-network was created by tempest"
425 sn = sc.create_share_network(name=sn_name,
426 description=sn_desc,
427 neutron_net_id=net_id,
428 neutron_subnet_id=subnet_id)
429 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200430
431 return share_network_id
432
433 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300434 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200435 snapshot_id=None, description=None, metadata=None,
436 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400437 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400438 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300439 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200440 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400441 share_network_id = (share_network_id or
442 CONF.share.share_network_id or
443 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200444 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300445 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400446 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200447 'share_protocol': share_protocol,
448 'size': size,
449 'name': name,
450 'snapshot_id': snapshot_id,
451 'description': description,
452 'metadata': metadata,
453 'share_network_id': share_network_id,
454 'share_type_id': share_type_id,
455 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400456 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400457 if share_group_id:
458 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400459
Marc Koderer0abc93b2015-07-15 09:18:35 +0200460 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400461 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400462 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200463 cleanup_list = (cls.class_resources if cleanup_in_class else
464 cls.method_resources)
465 cleanup_list.insert(0, resource)
466 return share
467
468 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300469 def migrate_share(
470 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200471 force_host_assisted_migration=False, writable=False,
472 nondisruptive=False, preserve_metadata=False,
473 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300474 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400475 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300476 client.migrate_share(
477 share_id, dest_host,
478 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200479 writable=writable, preserve_metadata=preserve_metadata,
480 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300481 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300482 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200483 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300484 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200485 return share
486
487 @classmethod
488 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
489 client = client or cls.shares_v2_client
490 client.migration_complete(share_id, **kwargs)
491 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300492 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300493 return share
494
495 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300496 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
497 client = client or cls.shares_v2_client
498 client.migration_cancel(share_id, **kwargs)
499 share = client.wait_for_migration_status(
500 share_id, dest_host, 'migration_cancelled', **kwargs)
501 return share
502
503 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200504 def create_share(cls, *args, **kwargs):
505 """Create one share and wait for available state. Retry if allowed."""
506 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
507 return result[0]
508
509 @classmethod
510 def create_shares(cls, share_data_list):
511 """Creates several shares in parallel with retries.
512
513 Use this method when you want to create more than one share at same
514 time. Especially if config option 'share.share_creation_retry_number'
515 has value more than zero (0).
516 All shares will be expected to have 'available' status with or without
517 recreation else error will be raised.
518
519 :param share_data_list: list -- list of dictionaries with 'args' and
520 'kwargs' for '_create_share' method of this base class.
521 example of data:
522 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
523 :returns: list -- list of shares created using provided data.
524 """
525
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300526 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200527 if not isinstance(d, dict):
528 raise exceptions.TempestException(
529 "Expected 'dict', got '%s'" % type(d))
530 if "args" not in d:
531 d["args"] = []
532 if "kwargs" not in d:
533 d["kwargs"] = {}
534 if len(d) > 2:
535 raise exceptions.TempestException(
536 "Expected only 'args' and 'kwargs' keys. "
537 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300538
539 data = []
540 for d in share_data_list:
541 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400542 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300543 local_d = {
544 "args": d["args"],
545 "kwargs": copy.deepcopy(d["kwargs"]),
546 }
547 local_d["kwargs"]["client"] = client
548 local_d["share"] = cls._create_share(
549 *local_d["args"], **local_d["kwargs"])
550 local_d["cnt"] = 0
551 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400552 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300553 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200554
555 while not all(d["available"] for d in data):
556 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400557 if not d["wait_for_status"]:
558 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200559 if d["available"]:
560 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300561 client = d["kwargs"]["client"]
562 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200563 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300564 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200565 d["available"] = True
566 except (share_exceptions.ShareBuildErrorException,
567 exceptions.TimeoutException) as e:
568 if CONF.share.share_creation_retry_number > d["cnt"]:
569 d["cnt"] += 1
570 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300571 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200572 LOG.error(msg)
573 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300574 cg_id = d["kwargs"].get("consistency_group_id")
575 if cg_id:
576 # NOTE(vponomaryov): delete errored share
577 # immediately in case share is part of CG.
578 client.delete_share(
579 share_id,
580 params={"consistency_group_id": cg_id})
581 client.wait_for_resource_deletion(
582 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200583 d["share"] = cls._create_share(
584 *d["args"], **d["kwargs"])
585 else:
gecong197358663802016-08-25 11:08:45 +0800586 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200587
588 return [d["share"] for d in data]
589
590 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400591 def create_share_group(cls, client=None, cleanup_in_class=True,
592 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400593 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400594 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400595 kwargs['share_network_id'] = (share_network_id or
596 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400597 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400598 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400599 "type": "share_group",
600 "id": share_group["id"],
601 "client": client,
602 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400603 if cleanup_in_class:
604 cls.class_resources.insert(0, resource)
605 else:
606 cls.method_resources.insert(0, resource)
607
Andrew Kerrb8436922016-06-01 15:32:43 -0400608 if kwargs.get('source_share_group_snapshot_id'):
609 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400610 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400611 params={'share_group_id': share_group['id']},
612 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400613
Andrew Kerrb8436922016-06-01 15:32:43 -0400614 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400615 resource = {"type": "share",
616 "id": share["id"],
617 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400618 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400619 if cleanup_in_class:
620 cls.class_resources.insert(0, resource)
621 else:
622 cls.method_resources.insert(0, resource)
623
Andrew Kerrb8436922016-06-01 15:32:43 -0400624 client.wait_for_share_group_status(share_group['id'], 'available')
625 return share_group
626
627 @classmethod
628 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
629 group_specs=None, client=None,
630 cleanup_in_class=True, **kwargs):
631 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300632 if (group_specs is None and
633 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300634 group_specs = {
635 'consistent_snapshot_support': (
636 CONF.share.capability_sg_consistent_snapshot_support),
637 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400638 share_group_type = client.create_share_group_type(
639 name=name,
640 share_types=share_types,
641 is_public=is_public,
642 group_specs=group_specs,
643 **kwargs)
644 resource = {
645 "type": "share_group_type",
646 "id": share_group_type["id"],
647 "client": client,
648 }
649 if cleanup_in_class:
650 cls.class_resources.insert(0, resource)
651 else:
652 cls.method_resources.insert(0, resource)
653 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400654
655 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200656 def create_snapshot_wait_for_active(cls, share_id, name=None,
657 description=None, force=False,
658 client=None, cleanup_in_class=True):
659 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400660 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200661 if description is None:
662 description = "Tempest's snapshot"
663 snapshot = client.create_snapshot(share_id, name, description, force)
664 resource = {
665 "type": "snapshot",
666 "id": snapshot["id"],
667 "client": client,
668 }
669 if cleanup_in_class:
670 cls.class_resources.insert(0, resource)
671 else:
672 cls.method_resources.insert(0, resource)
673 client.wait_for_snapshot_status(snapshot["id"], "available")
674 return snapshot
675
676 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400677 def create_share_group_snapshot_wait_for_active(
678 cls, share_group_id, name=None, description=None, client=None,
679 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400680 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400681 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400682 description = "Tempest's share group snapshot"
683 sg_snapshot = client.create_share_group_snapshot(
684 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400685 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400686 "type": "share_group_snapshot",
687 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400688 "client": client,
689 }
690 if cleanup_in_class:
691 cls.class_resources.insert(0, resource)
692 else:
693 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400694 client.wait_for_share_group_snapshot_status(
695 sg_snapshot["id"], "available")
696 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400697
698 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800699 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400700 """List the availability zones for "manila-share" services
701
702 that are currently in "up" state.
703 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800704 client = client or cls.admin_shares_v2_client
705 backends = (
706 '|'.join(['^%s$' % backend for backend in backends])
707 if backends else '.*'
708 )
Yogeshbdb88102015-09-29 23:41:02 -0400709 cls.services = client.list_services()
710 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800711 service['binary'] == 'manila-share' and
712 service['state'] == 'up' and
713 re.search(backends, service['host'])]
Yogeshbdb88102015-09-29 23:41:02 -0400714 return zones
715
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800716 @classmethod
717 def get_pools_matching_share_type(cls, share_type, client=None):
718 client = client or cls.admin_shares_v2_client
719 if utils.is_microversion_supported('2.23'):
720 return client.list_pools(
721 search_opts={'share_type': share_type['id']})['pools']
722
723 pools = client.list_pools(detail=True)['pools']
724 share_type = client.get_share_type(share_type['id'])['share_type']
725 extra_specs = {}
726 for k, v in share_type['extra_specs'].items():
727 extra_specs[k] = (
728 True if six.text_type(v).lower() == 'true'
729 else False if six.text_type(v).lower() == 'false' else v
730 )
731 return [
732 pool for pool in pools if all(y in pool['capabilities'].items()
733 for y in extra_specs.items())
734 ]
735
736 @classmethod
737 def get_availability_zones_matching_share_type(cls, share_type,
738 client=None):
739
740 client = client or cls.admin_shares_v2_client
741 pools_matching_share_type = cls.get_pools_matching_share_type(
742 share_type, client=client)
743 backends_matching_share_type = set(
744 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
745 )
746 azs = cls.get_availability_zones(backends=backends_matching_share_type)
747 return azs
748
Yogesh1f931ff2015-09-29 23:41:02 -0400749 def get_pools_for_replication_domain(self):
750 # Get the list of pools for the replication domain
751 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500752 instance_host = self.admin_client.get_share(
753 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400754 host_pool = [p for p in pools if p['name'] == instance_host][0]
755 rep_domain = host_pool['capabilities']['replication_domain']
756 pools_in_rep_domain = [p for p in pools if p['capabilities'][
757 'replication_domain'] == rep_domain]
758 return rep_domain, pools_in_rep_domain
759
Yogeshbdb88102015-09-29 23:41:02 -0400760 @classmethod
761 def create_share_replica(cls, share_id, availability_zone, client=None,
762 cleanup_in_class=False, cleanup=True):
763 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300764 replica = client.create_share_replica(
765 share_id, availability_zone=availability_zone)
Yogeshbdb88102015-09-29 23:41:02 -0400766 resource = {
767 "type": "share_replica",
768 "id": replica["id"],
769 "client": client,
770 "share_id": share_id,
771 }
772 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
773 if cleanup:
774 if cleanup_in_class:
775 cls.class_resources.insert(0, resource)
776 else:
777 cls.method_resources.insert(0, resource)
778 client.wait_for_share_replica_status(
779 replica["id"], constants.STATUS_AVAILABLE)
780 return replica
781
782 @classmethod
783 def delete_share_replica(cls, replica_id, client=None):
784 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400785 try:
786 client.delete_share_replica(replica_id)
787 client.wait_for_resource_deletion(replica_id=replica_id)
788 except exceptions.NotFound:
789 pass
Yogeshbdb88102015-09-29 23:41:02 -0400790
791 @classmethod
792 def promote_share_replica(cls, replica_id, client=None):
793 client = client or cls.shares_v2_client
794 replica = client.promote_share_replica(replica_id)
795 client.wait_for_share_replica_status(
796 replica["id"],
797 constants.REPLICATION_STATE_ACTIVE,
798 status_attr="replica_state")
799 return replica
800
yogeshdb32f462016-09-28 15:09:50 -0400801 def _get_access_rule_data_from_config(self):
802 """Get the first available access type/to combination from config.
803
804 This method opportunistically picks the first configured protocol
805 to create the share. Do not use this method in tests where you need
806 to test depth and breadth in the access types and access recipients.
807 """
808 protocol = self.shares_v2_client.share_protocol
809
810 if protocol in CONF.share.enable_ip_rules_for_protocols:
811 access_type = "ip"
812 access_to = utils.rand_ip()
813 elif protocol in CONF.share.enable_user_rules_for_protocols:
814 access_type = "user"
815 access_to = CONF.share.username_for_user_rules
816 elif protocol in CONF.share.enable_cert_rules_for_protocols:
817 access_type = "cert"
818 access_to = "client3.com"
819 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
820 access_type = "cephx"
821 access_to = "eve"
822 else:
823 message = "Unrecognized protocol and access rules configuration."
824 raise self.skipException(message)
825
826 return access_type, access_to
827
Yogeshbdb88102015-09-29 23:41:02 -0400828 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200829 def create_share_network(cls, client=None,
830 cleanup_in_class=False, **kwargs):
831 if client is None:
832 client = cls.shares_client
833 share_network = client.create_share_network(**kwargs)
834 resource = {
835 "type": "share_network",
836 "id": share_network["id"],
837 "client": client,
838 }
839 if cleanup_in_class:
840 cls.class_resources.insert(0, resource)
841 else:
842 cls.method_resources.insert(0, resource)
843 return share_network
844
845 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300846 def create_share_network_subnet(cls, client=None,
847 cleanup_in_class=False, **kwargs):
848 if client is None:
849 client = cls.shares_v2_client
850 share_network_subnet = client.create_subnet(**kwargs)
851 resource = {
852 "type": "share-network-subnet",
853 "id": share_network_subnet["id"],
854 "extra_params": {
855 "share_network_id": share_network_subnet["share_network_id"]
856 },
857 "client": client,
858 }
859 if cleanup_in_class:
860 cls.class_resources.insert(0, resource)
861 else:
862 cls.method_resources.insert(0, resource)
863 return share_network_subnet
864
865 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200866 def create_security_service(cls, ss_type="ldap", client=None,
867 cleanup_in_class=False, **kwargs):
868 if client is None:
869 client = cls.shares_client
870 security_service = client.create_security_service(ss_type, **kwargs)
871 resource = {
872 "type": "security_service",
873 "id": security_service["id"],
874 "client": client,
875 }
876 if cleanup_in_class:
877 cls.class_resources.insert(0, resource)
878 else:
879 cls.method_resources.insert(0, resource)
880 return security_service
881
882 @classmethod
883 def create_share_type(cls, name, is_public=True, client=None,
884 cleanup_in_class=True, **kwargs):
885 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200886 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200887 share_type = client.create_share_type(name, is_public, **kwargs)
888 resource = {
889 "type": "share_type",
890 "id": share_type["share_type"]["id"],
891 "client": client,
892 }
893 if cleanup_in_class:
894 cls.class_resources.insert(0, resource)
895 else:
896 cls.method_resources.insert(0, resource)
897 return share_type
898
haixin0d1d29f2019-08-02 16:50:45 +0800899 @classmethod
900 def update_share_type(cls, share_type_id, name=None,
901 is_public=None, description=None,
902 client=None):
903 if client is None:
904 client = cls.shares_v2_client
905 share_type = client.update_share_type(share_type_id, name,
906 is_public, description)
907 return share_type
908
Marc Koderer0abc93b2015-07-15 09:18:35 +0200909 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400910 def add_extra_specs_to_dict(extra_specs=None):
911 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300912 dhss = six.text_type(CONF.share.multitenancy_enabled)
913 snapshot_support = six.text_type(
914 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400915 create_from_snapshot_support = six.text_type(
916 CONF.share.capability_create_share_from_snapshot_support)
917
918 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300919 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200920 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400921
922 optional = {
923 "snapshot_support": snapshot_support,
924 "create_share_from_snapshot_support": create_from_snapshot_support,
925 }
926 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
927 # required extra-spec
928 extra_specs_dict.update(optional)
929
Marc Koderer0abc93b2015-07-15 09:18:35 +0200930 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400931 extra_specs_dict.update(extra_specs)
932
933 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200934
935 @classmethod
936 def clear_isolated_creds(cls, creds=None):
937 if creds is None:
938 creds = cls.method_isolated_creds
939 for ic in creds:
940 if "deleted" not in ic.keys():
941 ic["deleted"] = False
942 if not ic["deleted"]:
943 with handle_cleanup_exceptions():
944 ic["method"]()
945 ic["deleted"] = True
946
947 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400948 def clear_share_replicas(cls, share_id, client=None):
949 client = client or cls.shares_v2_client
950 share_replicas = client.list_share_replicas(
951 share_id=share_id)
952
953 for replica in share_replicas:
954 try:
955 cls.delete_share_replica(replica['id'])
956 except exceptions.BadRequest:
957 # Ignore the exception due to deletion of last active replica
958 pass
959
960 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200961 def clear_resources(cls, resources=None):
962 """Deletes resources, that were created in test suites.
963
964 This method tries to remove resources from resource list,
965 if it is not found, assumed it was deleted in test itself.
966 It is expected, that all resources were added as LIFO
967 due to restriction of deletion resources, that is in the chain.
968
969 :param resources: dict with keys 'type','id','client' and 'deleted'
970 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200971 if resources is None:
972 resources = cls.method_resources
973 for res in resources:
974 if "deleted" not in res.keys():
975 res["deleted"] = False
976 if "client" not in res.keys():
977 res["client"] = cls.shares_client
978 if not(res["deleted"]):
979 res_id = res['id']
980 client = res["client"]
981 with handle_cleanup_exceptions():
982 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400983 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400984 share_group_id = res.get('share_group_id')
985 if share_group_id:
986 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400987 client.delete_share(res_id, params=params)
988 else:
989 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200990 client.wait_for_resource_deletion(share_id=res_id)
991 elif res["type"] is "snapshot":
992 client.delete_snapshot(res_id)
993 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400994 elif (res["type"] is "share_network" and
995 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400996 client.delete_share_network(res_id)
997 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200998 elif res["type"] is "security_service":
999 client.delete_security_service(res_id)
1000 client.wait_for_resource_deletion(ss_id=res_id)
1001 elif res["type"] is "share_type":
1002 client.delete_share_type(res_id)
1003 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -04001004 elif res["type"] is "share_group":
1005 client.delete_share_group(res_id)
1006 client.wait_for_resource_deletion(
1007 share_group_id=res_id)
1008 elif res["type"] is "share_group_type":
1009 client.delete_share_group_type(res_id)
1010 client.wait_for_resource_deletion(
1011 share_group_type_id=res_id)
1012 elif res["type"] is "share_group_snapshot":
1013 client.delete_share_group_snapshot(res_id)
1014 client.wait_for_resource_deletion(
1015 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -04001016 elif res["type"] is "share_replica":
1017 client.delete_share_replica(res_id)
1018 client.wait_for_resource_deletion(replica_id=res_id)
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001019 elif res["type"] is "share_network_subnet":
1020 sn_id = res["extra_params"]["share_network_id"]
1021 client.delete_subnet(sn_id, res_id)
1022 client.wait_for_resource_deletion(
1023 share_network_subnet_id=res_id,
1024 sn_id=sn_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +02001025 else:
huayue97bacbf2016-01-04 09:57:39 +08001026 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +08001027 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +02001028 res["deleted"] = True
1029
1030 @classmethod
1031 def generate_share_network_data(self):
1032 data = {
1033 "name": data_utils.rand_name("sn-name"),
1034 "description": data_utils.rand_name("sn-desc"),
1035 "neutron_net_id": data_utils.rand_name("net-id"),
1036 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1037 }
1038 return data
1039
1040 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001041 def generate_subnet_data(self):
1042 data = {
1043 "neutron_net_id": data_utils.rand_name("net-id"),
1044 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1045 }
1046 return data
1047
1048 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001049 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001050 data = {
1051 "name": data_utils.rand_name("ss-name"),
1052 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001053 "dns_ip": utils.rand_ip(),
1054 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001055 "domain": data_utils.rand_name("ss-domain"),
1056 "user": data_utils.rand_name("ss-user"),
1057 "password": data_utils.rand_name("ss-password"),
1058 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001059 if set_ou:
1060 data["ou"] = data_utils.rand_name("ss-ou")
1061
Marc Koderer0abc93b2015-07-15 09:18:35 +02001062 return data
1063
1064 # Useful assertions
1065 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1066 """Assert two dicts are equivalent.
1067
1068 This is a 'deep' match in the sense that it handles nested
1069 dictionaries appropriately.
1070
1071 NOTE:
1072
1073 If you don't care (or don't know) a given value, you can specify
1074 the string DONTCARE as the value. This will cause that dict-item
1075 to be skipped.
1076
1077 """
1078 def raise_assertion(msg):
1079 d1str = str(d1)
1080 d2str = str(d2)
1081 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1082 'd2: %(d2str)s' %
1083 {"msg": msg, "d1str": d1str, "d2str": d2str})
1084 raise AssertionError(base_msg)
1085
1086 d1keys = set(d1.keys())
1087 d2keys = set(d2.keys())
1088 if d1keys != d2keys:
1089 d1only = d1keys - d2keys
1090 d2only = d2keys - d1keys
1091 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1092 'Keys in d2 and not d1: %(d2only)s' %
1093 {"d1only": d1only, "d2only": d2only})
1094
1095 for key in d1keys:
1096 d1value = d1[key]
1097 d2value = d2[key]
1098 try:
1099 error = abs(float(d1value) - float(d2value))
1100 within_tolerance = error <= tolerance
1101 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001102 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001103 # ValueError if arg is a str, TypeError if it's something else
1104 # (like None)
1105 within_tolerance = False
1106
1107 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1108 self.assertDictMatch(d1value, d2value)
1109 elif 'DONTCARE' in (d1value, d2value):
1110 continue
1111 elif approx_equal and within_tolerance:
1112 continue
1113 elif d1value != d2value:
1114 raise_assertion("d1['%(key)s']=%(d1value)s != "
1115 "d2['%(key)s']=%(d2value)s" %
1116 {
1117 "key": key,
1118 "d1value": d1value,
1119 "d2value": d2value
1120 })
1121
Alex Meadeba8a1602016-05-06 09:33:09 -04001122 def create_user_message(self):
1123 """Trigger a 'no valid host' situation to generate a message."""
1124 extra_specs = {
1125 'vendor_name': 'foobar',
1126 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1127 }
1128 share_type_name = data_utils.rand_name("share-type")
1129
1130 bogus_type = self.create_share_type(
1131 name=share_type_name,
1132 extra_specs=extra_specs)['share_type']
1133
1134 params = {'share_type_id': bogus_type['id'],
1135 'share_network_id': self.shares_v2_client.share_network_id}
1136 share = self.shares_v2_client.create_share(**params)
1137 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1138 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1139 return self.shares_v2_client.wait_for_message(share['id'])
1140
Marc Koderer0abc93b2015-07-15 09:18:35 +02001141
1142class BaseSharesAltTest(BaseSharesTest):
1143 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001144 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001145
1146
1147class BaseSharesAdminTest(BaseSharesTest):
1148 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001149 credentials = ('admin', )
1150
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001151 @classmethod
1152 def setup_clients(cls):
1153 super(BaseSharesAdminTest, cls).setup_clients()
1154 # Initialise share clients
1155 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1156
1157 @classmethod
1158 def _create_share_type(cls, specs=None):
1159 name = data_utils.rand_name("unique_st_name")
1160 extra_specs = cls.add_extra_specs_to_dict(specs)
1161 return cls.create_share_type(
1162 name, extra_specs=extra_specs,
1163 client=cls.admin_shares_v2_client)['share_type']
1164
1165 @classmethod
1166 def _create_share_group_type(cls):
1167 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1168 return cls.create_share_group_type(
1169 name=share_group_type_name, share_types=[cls.share_type_id],
1170 client=cls.admin_shares_v2_client)
1171
Lucio Seki37056942019-01-24 15:40:20 -02001172 def _create_share_for_manage(self):
1173 creation_data = {
1174 'share_type_id': self.st['share_type']['id'],
1175 'share_protocol': self.protocol,
1176 }
1177
1178 share = self.create_share(**creation_data)
1179 share = self.shares_v2_client.get_share(share['id'])
1180
1181 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1182 el = self.shares_v2_client.list_share_export_locations(share["id"])
1183 share["export_locations"] = el
1184
1185 return share
1186
1187 def _unmanage_share_and_wait(self, share):
1188 self.shares_v2_client.unmanage_share(share['id'])
1189 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1190
1191 def _reset_state_and_delete_share(self, share):
1192 self.shares_v2_client.reset_state(share['id'])
1193 self._delete_share_and_wait(share)
1194
1195 def _delete_snapshot_and_wait(self, snap):
1196 self.shares_v2_client.delete_snapshot(snap['id'])
1197 self.shares_v2_client.wait_for_resource_deletion(
1198 snapshot_id=snap['id']
1199 )
1200 self.assertRaises(exceptions.NotFound,
1201 self.shares_v2_client.get_snapshot,
1202 snap['id'])
1203
1204 def _delete_share_and_wait(self, share):
1205 self.shares_v2_client.delete_share(share['id'])
1206 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1207 self.assertRaises(exceptions.NotFound,
1208 self.shares_v2_client.get_share,
1209 share['id'])
1210
1211 def _manage_share(self, share, name, description, share_server_id):
1212 managed_share = self.shares_v2_client.manage_share(
1213 service_host=share['host'],
1214 export_path=share['export_locations'][0],
1215 protocol=share['share_proto'],
1216 share_type_id=self.share_type['share_type']['id'],
1217 name=name,
1218 description=description,
1219 share_server_id=share_server_id
1220 )
1221 self.shares_v2_client.wait_for_share_status(
1222 managed_share['id'], constants.STATUS_AVAILABLE
1223 )
1224
1225 return managed_share
1226
1227 def _unmanage_share_server_and_wait(self, server):
1228 self.shares_v2_client.unmanage_share_server(server['id'])
1229 self.shares_v2_client.wait_for_resource_deletion(
1230 server_id=server['id']
1231 )
1232
1233 def _manage_share_server(self, share_server, fields=None):
1234 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001235 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001236 managed_share_server = self.shares_v2_client.manage_share_server(
1237 params.get('host', share_server['host']),
1238 params.get('share_network_id', share_server['share_network_id']),
1239 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001240 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001241 )
1242 self.shares_v2_client.wait_for_share_server_status(
1243 managed_share_server['id'],
1244 constants.SERVER_STATE_ACTIVE,
1245 )
1246
1247 return managed_share_server
1248
1249 def _delete_share_server_and_wait(self, share_server_id):
1250 self.shares_v2_client.delete_share_server(
1251 share_server_id
1252 )
1253 self.shares_v2_client.wait_for_resource_deletion(
1254 server_id=share_server_id)
1255
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001256
1257class BaseSharesMixedTest(BaseSharesTest):
1258 """Base test case class for all Shares API tests with all user roles."""
1259 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001260
1261 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001262 def setup_clients(cls):
1263 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001264 # Initialise share clients
1265 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1266 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1267 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1268 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1269 # Initialise network clients
1270 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1271 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
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
haobing101c7fee2018-03-09 16:33:00 +08001284 resource = {
1285 "type": "share_network",
1286 "id": alt_share_network_id,
1287 "client": cls.alt_shares_v2_client,
1288 }
1289 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001290
1291 @classmethod
1292 def _create_share_type(cls, specs=None):
1293 name = data_utils.rand_name("unique_st_name")
1294 extra_specs = cls.add_extra_specs_to_dict(specs)
1295 return cls.create_share_type(
1296 name, extra_specs=extra_specs,
1297 client=cls.admin_shares_v2_client)['share_type']
1298
1299 @classmethod
1300 def _create_share_group_type(cls):
1301 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1302 return cls.create_share_group_type(
1303 name=share_group_type_name, share_types=[cls.share_type_id],
1304 client=cls.admin_shares_v2_client)