blob: bdd48f8cf7d4c3373faf5ba5e9586c195d6e72e5 [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
Goutham Pacha Ravic678e212020-03-20 11:13:47 -070027from tempest.lib.common import cred_client
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010028from tempest.lib.common import dynamic_creds
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050029from tempest.lib.common.utils import data_utils
30from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020031from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020032
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010033from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040034from manila_tempest_tests.common import constants
Marc Koderer0abc93b2015-07-15 09:18:35 +020035from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020036from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020037
38CONF = config.CONF
39LOG = log.getLogger(__name__)
40
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030041# Test tags related to test direction
42TAG_POSITIVE = "positive"
43TAG_NEGATIVE = "negative"
44
45# Test tags related to service involvement
Tom Barron69f96962019-07-29 17:07:03 -040046# Only requires that manila-api service running.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030047TAG_API = "api"
Tom Barron69f96962019-07-29 17:07:03 -040048# Requires all manila services running, intended to test back-end
49# (manila-share) behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030050TAG_BACKEND = "backend"
Tom Barron69f96962019-07-29 17:07:03 -040051# Requires all manila services running, intended to test API behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030052TAG_API_WITH_BACKEND = "api_with_backend"
53
54TAGS_MAPPER = {
55 "p": TAG_POSITIVE,
56 "n": TAG_NEGATIVE,
57 "a": TAG_API,
58 "b": TAG_BACKEND,
59 "ab": TAG_API_WITH_BACKEND,
60}
61TAGS_PATTERN = re.compile(
62 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
63 TAGS_MAPPER)
64
65
66def verify_test_has_appropriate_tags(self):
67 if not TAGS_PATTERN.match(self.id()):
68 msg = (
69 "Required attributes either not set or set improperly. "
70 "Two test attributes are expected:\n"
71 " - one of '%(p)s' or '%(n)s' and \n"
72 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
73 ) % TAGS_MAPPER
74 raise self.failureException(msg)
75
Marc Koderer0abc93b2015-07-15 09:18:35 +020076
77class handle_cleanup_exceptions(object):
78 """Handle exceptions raised with cleanup operations.
79
80 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
81 are raised.
82 Suppress all other exceptions only in case config opt
83 'suppress_errors_in_cleanup' in config group 'share' is True.
84 """
85
86 def __enter__(self):
87 return self
88
89 def __exit__(self, exc_type, exc_value, exc_traceback):
90 if not (isinstance(exc_value,
91 (exceptions.NotFound, exceptions.Forbidden)) or
92 CONF.share.suppress_errors_in_cleanup):
93 return False # Do not suppress error if any
94 if exc_traceback:
95 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080096 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020097 return True # Suppress error if any
98
99
100def network_synchronized(f):
101
102 def wrapped_func(self, *args, **kwargs):
103 with_isolated_creds = True if len(args) > 2 else False
104 no_lock_required = kwargs.get(
105 "isolated_creds_client", with_isolated_creds)
106 if no_lock_required:
107 # Usage of not reusable network. No need in lock.
108 return f(self, *args, **kwargs)
109
110 # Use lock assuming reusage of common network.
111 @lockutils.synchronized("manila_network_lock", external=True)
112 def source_func(self, *args, **kwargs):
113 return f(self, *args, **kwargs)
114
115 return source_func(self, *args, **kwargs)
116
117 return wrapped_func
118
119
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200120skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500121skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200122
123
Marc Koderer0abc93b2015-07-15 09:18:35 +0200124class BaseSharesTest(test.BaseTestCase):
125 """Base test case class for all Manila API tests."""
126
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300127 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200128 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200129 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200130
131 # Will be cleaned up in resource_cleanup
132 class_resources = []
133
134 # Will be cleaned up in tearDown method
135 method_resources = []
136
137 # Will be cleaned up in resource_cleanup
138 class_isolated_creds = []
139
140 # Will be cleaned up in tearDown method
141 method_isolated_creds = []
142
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100143 # NOTE(andreaf) Override the client manager class to be used, so that
144 # a stable class is used, which includes plugin registered services as well
145 client_manager = clients.Clients
146
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200147 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200148 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200149 raise self.skipException(
150 "Microversion '%s' is not supported." % microversion)
151
Xing Yang69b00b52015-11-22 16:10:44 -0500152 def skip_if_microversion_lt(self, microversion):
153 if utils.is_microversion_lt(CONF.share.max_api_microversion,
154 microversion):
155 raise self.skipException(
156 "Microversion must be greater than or equal to '%s'." %
157 microversion)
158
Marc Koderer0abc93b2015-07-15 09:18:35 +0200159 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500160 def _get_dynamic_creds(cls, name, network_resources=None):
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100161 identity_version = CONF.identity.auth_version
162 if identity_version == 'v3':
163 identity_uri = CONF.identity.uri_v3
164 identity_admin_endpoint_type = CONF.identity.v3_endpoint_type
165 elif identity_version == 'v2':
166 identity_uri = CONF.identity.uri
167 identity_admin_endpoint_type = CONF.identity.v2_admin_endpoint_type
168
Tom Barron4b8834a2017-02-02 11:02:20 -0500169 return dynamic_creds.DynamicCredentialProvider(
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100170 identity_version=identity_version,
Tom Barron4b8834a2017-02-02 11:02:20 -0500171 name=name,
172 network_resources=network_resources,
173 credentials_domain=CONF.auth.default_credentials_domain_name,
174 admin_role=CONF.identity.admin_role,
175 admin_creds=common_creds.get_configured_admin_credentials(),
176 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
177 identity_admin_role=CONF.identity.admin_role,
178 extra_roles=None,
179 neutron_available=CONF.service_available.neutron,
180 create_networks=(
181 CONF.share.create_networks_when_multitenancy_enabled),
182 project_network_cidr=CONF.network.project_network_cidr,
183 project_network_mask_bits=CONF.network.project_network_mask_bits,
184 public_network_id=CONF.network.public_network_id,
ghanshyam2aea7c32017-12-11 00:03:56 +0000185 resource_prefix='tempest',
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100186 identity_admin_endpoint_type=identity_admin_endpoint_type,
187 identity_uri=identity_uri)
Tom Barron4b8834a2017-02-02 11:02:20 -0500188
189 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200190 def get_client_with_isolated_creds(cls,
191 name=None,
192 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400193 cleanup_in_class=False,
194 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200195 """Creates isolated creds.
196
197 :param name: name, will be used for naming ic and related stuff
198 :param type_of_creds: admin, alt or primary
199 :param cleanup_in_class: defines place where to delete
200 :returns: SharesClient -- shares client with isolated creds.
201 :returns: To client added dict attr 'creds' with
202 :returns: key elements 'tenant' and 'user'.
203 """
204 if name is None:
205 # Get name of test method
206 name = inspect.stack()[1][3]
207 if len(name) > 32:
208 name = name[0:32]
209
210 # Choose type of isolated creds
Tom Barron4b8834a2017-02-02 11:02:20 -0500211 ic = cls._get_dynamic_creds(name)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200212 if "admin" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200213 creds = ic.get_admin_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200214 elif "alt" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200215 creds = ic.get_alt_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200216 else:
Marc Koderer5880b362016-07-06 10:59:07 +0200217 creds = ic.get_credentials(type_of_creds).credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200218 ic.type_of_creds = type_of_creds
219
220 # create client with isolated creds
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100221 os = clients.Clients(creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400222 if client_version == '1':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100223 client = os.share_v1.SharesClient()
Clinton Knighte5c8f092015-08-27 15:00:23 -0400224 elif client_version == '2':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100225 client = os.share_v2.SharesV2Client()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200226
227 # Set place where will be deleted isolated creds
228 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200229 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200230 "deleted": False,
231 }
232 if cleanup_in_class:
233 cls.class_isolated_creds.insert(0, ic_res)
234 else:
235 cls.method_isolated_creds.insert(0, ic_res)
236
237 # Provide share network
238 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300239 if (not CONF.service_available.neutron and
240 CONF.share.create_networks_when_multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200241 raise cls.skipException("Neutron support is required")
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100242 nc = os.network.NetworksClient()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200243 share_network_id = cls.provide_share_network(client, nc, ic)
244 client.share_network_id = share_network_id
245 resource = {
246 "type": "share_network",
247 "id": client.share_network_id,
248 "client": client,
249 }
250 if cleanup_in_class:
251 cls.class_resources.insert(0, resource)
252 else:
253 cls.method_resources.insert(0, resource)
254 return client
255
256 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000257 def skip_checks(cls):
258 super(BaseSharesTest, cls).skip_checks()
259 if not CONF.service_available.manila:
260 raise cls.skipException("Manila support is required")
lkuchlana3b6f7a2020-01-07 10:45:45 +0200261 if not any(p in CONF.share.enable_protocols for p in cls.protocols):
262 skip_msg = "%s tests are disabled" % CONF.share.enable_protocols
263 raise cls.skipException(skip_msg)
Daniel Melladoe5269142017-01-12 12:17:58 +0000264
265 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200266 def verify_nonempty(cls, *args):
267 if not all(args):
268 msg = "Missing API credentials in configuration."
269 raise cls.skipException(msg)
270
271 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300272 def setup_clients(cls):
273 super(BaseSharesTest, cls).setup_clients()
274 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100275 # Initialise share clients for test credentials
276 cls.shares_client = os.share_v1.SharesClient()
277 cls.shares_v2_client = os.share_v2.SharesV2Client()
278 # Initialise network clients for test credentials
279 if CONF.service_available.neutron:
280 cls.networks_client = os.network.NetworksClient()
281 cls.subnets_client = os.network.SubnetsClient()
282 else:
283 cls.networks_client = None
284 cls.subnets_client = None
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300285
286 if CONF.identity.auth_version == 'v3':
287 project_id = os.auth_provider.auth_data[1]['project']['id']
288 else:
289 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
290 cls.tenant_id = project_id
291 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
292
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300293 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300294 if (not CONF.service_available.neutron and
295 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300296 raise cls.skipException("Neutron support is required")
297 share_network_id = cls.provide_share_network(
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100298 cls.shares_v2_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300299 cls.shares_client.share_network_id = share_network_id
300 cls.shares_v2_client.share_network_id = share_network_id
haobing101c7fee2018-03-09 16:33:00 +0800301 resource = {
302 "type": "share_network",
303 "id": share_network_id,
304 "client": cls.shares_v2_client,
305 }
306 cls.class_resources.insert(0, resource)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300307
Marc Koderer0abc93b2015-07-15 09:18:35 +0200308 def setUp(self):
309 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200310 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200311 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300312 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200313
314 @classmethod
315 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200316 cls.clear_resources(cls.class_resources)
317 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400318 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200319
320 @classmethod
321 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200322 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300323 isolated_creds_client=None,
324 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200325 """Used for finding/creating share network for multitenant driver.
326
327 This method creates/gets entity share-network for one tenant. This
328 share-network will be used for creation of service vm.
329
330 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200331 :param networks_client: network client from same tenant as shares
332 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200333 If provided, then its networking will be used if needed.
334 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300335 :param ignore_multitenancy_config: provide a share network regardless
336 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200337 :returns: str -- share network id for shares_client tenant
338 :returns: None -- if single-tenant driver used
339 """
340
341 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300342 search_word = "reusable"
343 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200344
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300345 if (not ignore_multitenancy_config and
346 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200347 # Assumed usage of a single-tenant driver
348 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200349 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300350 if sc.share_network_id:
351 # Share-network already exists, use it
352 share_network_id = sc.share_network_id
353 elif not CONF.share.create_networks_when_multitenancy_enabled:
354 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200355
356 # Try get suitable share-network
357 share_networks = sc.list_share_networks_with_detail()
358 for sn in share_networks:
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300359 net_info = (
360 utils.share_network_get_default_subnet(sn)
361 if utils.share_network_subnets_are_supported() else sn)
362 if net_info is None:
363 continue
364 if(net_info["neutron_net_id"] is None and
365 net_info["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200366 sn["name"] and search_word in sn["name"]):
367 share_network_id = sn["id"]
368 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200369
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300370 # Create new share-network if one was not found
371 if share_network_id is None:
372 sn_desc = "This share-network was created by tempest"
373 sn = sc.create_share_network(name=sn_name,
374 description=sn_desc)
375 share_network_id = sn["id"]
376 else:
377 net_id = subnet_id = share_network_id = None
378
379 if not isolated_creds_client:
380 # Search for networks, created in previous runs
381 service_net_name = "share-service"
382 networks = networks_client.list_networks()
383 if "networks" in networks.keys():
384 networks = networks["networks"]
385 for network in networks:
386 if (service_net_name in network["name"] and
387 sc.tenant_id == network['tenant_id']):
388 net_id = network["id"]
389 if len(network["subnets"]) > 0:
390 subnet_id = network["subnets"][0]
391 break
392
393 # Create suitable network
394 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500395 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300396 net_data = ic._create_network_resources(sc.tenant_id)
397 network, subnet, router = net_data
398 net_id = network["id"]
399 subnet_id = subnet["id"]
400
401 # Try get suitable share-network
402 share_networks = sc.list_share_networks_with_detail()
403 for sn in share_networks:
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300404 net_info = (
405 utils.share_network_get_default_subnet(sn)
406 if utils.share_network_subnets_are_supported()
407 else sn)
408 if net_info is None:
409 continue
410 if (net_id == net_info["neutron_net_id"] and
411 subnet_id == net_info["neutron_subnet_id"] and
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300412 sn["name"] and search_word in sn["name"]):
413 share_network_id = sn["id"]
414 break
415 else:
416 sn_name = "autogenerated_by_tempest_for_isolated_creds"
417 # Use precreated network and subnet from isolated creds
418 net_id = isolated_creds_client.get_credentials(
419 isolated_creds_client.type_of_creds).network['id']
420 subnet_id = isolated_creds_client.get_credentials(
421 isolated_creds_client.type_of_creds).subnet['id']
422
423 # Create suitable share-network
424 if share_network_id is None:
425 sn_desc = "This share-network was created by tempest"
426 sn = sc.create_share_network(name=sn_name,
427 description=sn_desc,
428 neutron_net_id=net_id,
429 neutron_subnet_id=subnet_id)
430 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200431
432 return share_network_id
433
434 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300435 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200436 snapshot_id=None, description=None, metadata=None,
437 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400438 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400439 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300440 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200441 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400442 share_network_id = (share_network_id or
443 CONF.share.share_network_id or
444 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200445 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300446 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400447 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200448 'share_protocol': share_protocol,
449 'size': size,
450 'name': name,
451 'snapshot_id': snapshot_id,
452 'description': description,
453 'metadata': metadata,
454 'share_network_id': share_network_id,
455 'share_type_id': share_type_id,
456 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400457 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400458 if share_group_id:
459 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400460
Marc Koderer0abc93b2015-07-15 09:18:35 +0200461 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400462 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400463 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200464 cleanup_list = (cls.class_resources if cleanup_in_class else
465 cls.method_resources)
466 cleanup_list.insert(0, resource)
467 return share
468
469 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300470 def migrate_share(
471 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200472 force_host_assisted_migration=False, writable=False,
473 nondisruptive=False, preserve_metadata=False,
474 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300475 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400476 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300477 client.migrate_share(
478 share_id, dest_host,
479 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200480 writable=writable, preserve_metadata=preserve_metadata,
481 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300482 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300483 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200484 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300485 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200486 return share
487
488 @classmethod
489 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
490 client = client or cls.shares_v2_client
491 client.migration_complete(share_id, **kwargs)
492 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300493 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300494 return share
495
496 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300497 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
498 client = client or cls.shares_v2_client
499 client.migration_cancel(share_id, **kwargs)
500 share = client.wait_for_migration_status(
501 share_id, dest_host, 'migration_cancelled', **kwargs)
502 return share
503
504 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200505 def create_share(cls, *args, **kwargs):
506 """Create one share and wait for available state. Retry if allowed."""
507 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
508 return result[0]
509
510 @classmethod
511 def create_shares(cls, share_data_list):
512 """Creates several shares in parallel with retries.
513
514 Use this method when you want to create more than one share at same
515 time. Especially if config option 'share.share_creation_retry_number'
516 has value more than zero (0).
517 All shares will be expected to have 'available' status with or without
518 recreation else error will be raised.
519
520 :param share_data_list: list -- list of dictionaries with 'args' and
521 'kwargs' for '_create_share' method of this base class.
522 example of data:
523 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
524 :returns: list -- list of shares created using provided data.
525 """
526
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300527 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200528 if not isinstance(d, dict):
529 raise exceptions.TempestException(
530 "Expected 'dict', got '%s'" % type(d))
531 if "args" not in d:
532 d["args"] = []
533 if "kwargs" not in d:
534 d["kwargs"] = {}
535 if len(d) > 2:
536 raise exceptions.TempestException(
537 "Expected only 'args' and 'kwargs' keys. "
538 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300539
540 data = []
541 for d in share_data_list:
542 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400543 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300544 local_d = {
545 "args": d["args"],
546 "kwargs": copy.deepcopy(d["kwargs"]),
547 }
548 local_d["kwargs"]["client"] = client
549 local_d["share"] = cls._create_share(
550 *local_d["args"], **local_d["kwargs"])
551 local_d["cnt"] = 0
552 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400553 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300554 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200555
556 while not all(d["available"] for d in data):
557 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400558 if not d["wait_for_status"]:
559 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200560 if d["available"]:
561 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300562 client = d["kwargs"]["client"]
563 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200564 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300565 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200566 d["available"] = True
567 except (share_exceptions.ShareBuildErrorException,
568 exceptions.TimeoutException) as e:
569 if CONF.share.share_creation_retry_number > d["cnt"]:
570 d["cnt"] += 1
571 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300572 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200573 LOG.error(msg)
574 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300575 cg_id = d["kwargs"].get("consistency_group_id")
576 if cg_id:
577 # NOTE(vponomaryov): delete errored share
578 # immediately in case share is part of CG.
579 client.delete_share(
580 share_id,
581 params={"consistency_group_id": cg_id})
582 client.wait_for_resource_deletion(
583 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200584 d["share"] = cls._create_share(
585 *d["args"], **d["kwargs"])
586 else:
gecong197358663802016-08-25 11:08:45 +0800587 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200588
589 return [d["share"] for d in data]
590
591 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400592 def create_share_group(cls, client=None, cleanup_in_class=True,
593 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400594 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400595 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400596 kwargs['share_network_id'] = (share_network_id or
597 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400598 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400599 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400600 "type": "share_group",
601 "id": share_group["id"],
602 "client": client,
603 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400604 if cleanup_in_class:
605 cls.class_resources.insert(0, resource)
606 else:
607 cls.method_resources.insert(0, resource)
608
Andrew Kerrb8436922016-06-01 15:32:43 -0400609 if kwargs.get('source_share_group_snapshot_id'):
610 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400611 detailed=True,
silvacarloss6e575682020-02-18 19:52:35 -0300612 params={'share_group_id': share_group['id']})
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'])]
Douglas Viroel161f1802020-04-25 17:18:14 -0300714 return list(set(zones))
Yogeshbdb88102015-09-29 23:41:02 -0400715
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():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200982 if res["type"] == "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)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200991 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200992 client.delete_snapshot(res_id)
993 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200994 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400995 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)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200998 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200999 client.delete_security_service(res_id)
1000 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001001 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +02001002 client.delete_share_type(res_id)
1003 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001004 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -04001005 client.delete_share_group(res_id)
1006 client.wait_for_resource_deletion(
1007 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001008 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -04001009 client.delete_share_group_type(res_id)
1010 client.wait_for_resource_deletion(
1011 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001012 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -04001013 client.delete_share_group_snapshot(res_id)
1014 client.wait_for_resource_deletion(
1015 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001016 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -04001017 client.delete_share_replica(res_id)
1018 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001019 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001020 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
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001261 # Will be cleaned up in resource_cleanup if the class
1262 class_project_users_created = []
1263
1264 @classmethod
1265 def resource_cleanup(cls):
1266 cls.clear_project_users(cls.class_project_users_created)
1267 super(BaseSharesMixedTest, cls).resource_cleanup()
1268
1269 @classmethod
1270 def clear_project_users(cls, users=None):
1271 users = users or cls.class_project_users_created
1272 for user in users:
1273 with handle_cleanup_exceptions():
1274 cls.os_admin.creds_client.delete_user(user['id'])
1275
Marc Koderer0abc93b2015-07-15 09:18:35 +02001276 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001277 def setup_clients(cls):
1278 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001279 # Initialise share clients
1280 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1281 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1282 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1283 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1284 # Initialise network clients
1285 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1286 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001287 # Initialise identity clients
1288 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1289 identity_clients = getattr(
1290 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1291 cls.os_admin.identity_client = identity_clients.IdentityClient()
1292 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1293 cls.os_admin.users_client = identity_clients.UsersClient()
1294 cls.os_admin.roles_client = identity_clients.RolesClient()
1295 cls.os_admin.domains_client = (
1296 cls.os_admin.identity_v3.DomainsClient() if
1297 CONF.identity.auth_version == 'v3' else None)
1298 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001299
1300 if CONF.share.multitenancy_enabled:
1301 admin_share_network_id = cls.provide_share_network(
1302 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1303 cls.admin_shares_client.share_network_id = admin_share_network_id
1304 cls.admin_shares_v2_client.share_network_id = (
1305 admin_share_network_id)
1306
1307 alt_share_network_id = cls.provide_share_network(
1308 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1309 cls.alt_shares_client.share_network_id = alt_share_network_id
1310 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001311 resource = {
1312 "type": "share_network",
1313 "id": alt_share_network_id,
1314 "client": cls.alt_shares_v2_client,
1315 }
1316 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001317
1318 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001319 def create_user_and_get_client(cls, project=None):
1320 """Create a user in specified project & set share clients for user
1321
1322 The user will have all roles specified in tempest.conf
1323 :param: project: a dictionary with project ID and name, if not
1324 specified, the value will be cls.admin_project
1325 """
1326 project_domain_name = (
1327 cls.os_admin.identity_client.auth_provider.credentials.get(
1328 'project_domain_name', 'Default'))
1329 cls.os_admin.creds_client = cred_client.get_creds_client(
1330 cls.os_admin.identity_client, cls.os_admin.projects_client,
1331 cls.os_admin.users_client, cls.os_admin.roles_client,
1332 cls.os_admin.domains_client, project_domain_name)
1333
1334 # User info
1335 project = project or cls.admin_project
1336 username = data_utils.rand_name('manila_%s' % project['id'])
1337 password = data_utils.rand_password()
1338 email = '%s@example.org' % username
1339
1340 user = cls.os_admin.creds_client.create_user(
1341 username, password, project, email)
1342 cls.class_project_users_created.append(user)
1343
1344 for conf_role in CONF.auth.tempest_roles:
1345 cls.os_admin.creds_client.assign_user_role(
1346 user, project, conf_role)
1347
1348 user_creds = cls.os_admin.creds_client.get_credentials(
1349 user, project, password)
1350 os = clients.Clients(user_creds)
1351 os.shares_v1_client = os.share_v1.SharesClient()
1352 os.shares_v2_client = os.share_v2.SharesV2Client()
1353 return os
1354
1355 @classmethod
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001356 def _create_share_type(cls, specs=None):
1357 name = data_utils.rand_name("unique_st_name")
1358 extra_specs = cls.add_extra_specs_to_dict(specs)
1359 return cls.create_share_type(
1360 name, extra_specs=extra_specs,
1361 client=cls.admin_shares_v2_client)['share_type']
1362
1363 @classmethod
1364 def _create_share_group_type(cls):
1365 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1366 return cls.create_share_group_type(
1367 name=share_group_type_name, share_types=[cls.share_type_id],
1368 client=cls.admin_shares_v2_client)