blob: 671ccf308989115a2a621602fc9e818ab744c760 [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(
andrebeltrami3b4d4852020-02-04 19:11:54 +0000721 detail=True,
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800722 search_opts={'share_type': share_type['id']})['pools']
723
724 pools = client.list_pools(detail=True)['pools']
725 share_type = client.get_share_type(share_type['id'])['share_type']
726 extra_specs = {}
727 for k, v in share_type['extra_specs'].items():
728 extra_specs[k] = (
729 True if six.text_type(v).lower() == 'true'
730 else False if six.text_type(v).lower() == 'false' else v
731 )
732 return [
733 pool for pool in pools if all(y in pool['capabilities'].items()
734 for y in extra_specs.items())
735 ]
736
737 @classmethod
738 def get_availability_zones_matching_share_type(cls, share_type,
739 client=None):
740
741 client = client or cls.admin_shares_v2_client
742 pools_matching_share_type = cls.get_pools_matching_share_type(
743 share_type, client=client)
744 backends_matching_share_type = set(
745 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
746 )
747 azs = cls.get_availability_zones(backends=backends_matching_share_type)
748 return azs
749
Yogesh1f931ff2015-09-29 23:41:02 -0400750 def get_pools_for_replication_domain(self):
751 # Get the list of pools for the replication domain
752 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500753 instance_host = self.admin_client.get_share(
754 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400755 host_pool = [p for p in pools if p['name'] == instance_host][0]
756 rep_domain = host_pool['capabilities']['replication_domain']
757 pools_in_rep_domain = [p for p in pools if p['capabilities'][
758 'replication_domain'] == rep_domain]
759 return rep_domain, pools_in_rep_domain
760
Yogeshbdb88102015-09-29 23:41:02 -0400761 @classmethod
762 def create_share_replica(cls, share_id, availability_zone, client=None,
763 cleanup_in_class=False, cleanup=True):
764 client = client or cls.shares_v2_client
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300765 replica = client.create_share_replica(
766 share_id, availability_zone=availability_zone)
Yogeshbdb88102015-09-29 23:41:02 -0400767 resource = {
768 "type": "share_replica",
769 "id": replica["id"],
770 "client": client,
771 "share_id": share_id,
772 }
773 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
774 if cleanup:
775 if cleanup_in_class:
776 cls.class_resources.insert(0, resource)
777 else:
778 cls.method_resources.insert(0, resource)
779 client.wait_for_share_replica_status(
780 replica["id"], constants.STATUS_AVAILABLE)
781 return replica
782
783 @classmethod
784 def delete_share_replica(cls, replica_id, client=None):
785 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400786 try:
787 client.delete_share_replica(replica_id)
788 client.wait_for_resource_deletion(replica_id=replica_id)
789 except exceptions.NotFound:
790 pass
Yogeshbdb88102015-09-29 23:41:02 -0400791
792 @classmethod
793 def promote_share_replica(cls, replica_id, client=None):
794 client = client or cls.shares_v2_client
795 replica = client.promote_share_replica(replica_id)
796 client.wait_for_share_replica_status(
797 replica["id"],
798 constants.REPLICATION_STATE_ACTIVE,
799 status_attr="replica_state")
800 return replica
801
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700802 @classmethod
803 def _get_access_rule_data_from_config(cls):
yogeshdb32f462016-09-28 15:09:50 -0400804 """Get the first available access type/to combination from config.
805
806 This method opportunistically picks the first configured protocol
807 to create the share. Do not use this method in tests where you need
808 to test depth and breadth in the access types and access recipients.
809 """
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700810 protocol = cls.shares_v2_client.share_protocol
yogeshdb32f462016-09-28 15:09:50 -0400811
812 if protocol in CONF.share.enable_ip_rules_for_protocols:
813 access_type = "ip"
814 access_to = utils.rand_ip()
815 elif protocol in CONF.share.enable_user_rules_for_protocols:
816 access_type = "user"
817 access_to = CONF.share.username_for_user_rules
818 elif protocol in CONF.share.enable_cert_rules_for_protocols:
819 access_type = "cert"
820 access_to = "client3.com"
821 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
822 access_type = "cephx"
823 access_to = "eve"
824 else:
825 message = "Unrecognized protocol and access rules configuration."
Goutham Pacha Ravi1727f7a2020-05-22 13:41:40 -0700826 raise cls.skipException(message)
yogeshdb32f462016-09-28 15:09:50 -0400827
828 return access_type, access_to
829
Yogeshbdb88102015-09-29 23:41:02 -0400830 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200831 def create_share_network(cls, client=None,
832 cleanup_in_class=False, **kwargs):
833 if client is None:
834 client = cls.shares_client
835 share_network = client.create_share_network(**kwargs)
836 resource = {
837 "type": "share_network",
838 "id": share_network["id"],
839 "client": client,
840 }
841 if cleanup_in_class:
842 cls.class_resources.insert(0, resource)
843 else:
844 cls.method_resources.insert(0, resource)
845 return share_network
846
847 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300848 def create_share_network_subnet(cls, client=None,
849 cleanup_in_class=False, **kwargs):
850 if client is None:
851 client = cls.shares_v2_client
852 share_network_subnet = client.create_subnet(**kwargs)
853 resource = {
854 "type": "share-network-subnet",
855 "id": share_network_subnet["id"],
856 "extra_params": {
857 "share_network_id": share_network_subnet["share_network_id"]
858 },
859 "client": client,
860 }
861 if cleanup_in_class:
862 cls.class_resources.insert(0, resource)
863 else:
864 cls.method_resources.insert(0, resource)
865 return share_network_subnet
866
867 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200868 def create_security_service(cls, ss_type="ldap", client=None,
869 cleanup_in_class=False, **kwargs):
870 if client is None:
871 client = cls.shares_client
872 security_service = client.create_security_service(ss_type, **kwargs)
873 resource = {
874 "type": "security_service",
875 "id": security_service["id"],
876 "client": client,
877 }
878 if cleanup_in_class:
879 cls.class_resources.insert(0, resource)
880 else:
881 cls.method_resources.insert(0, resource)
882 return security_service
883
884 @classmethod
885 def create_share_type(cls, name, is_public=True, client=None,
886 cleanup_in_class=True, **kwargs):
887 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200888 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200889 share_type = client.create_share_type(name, is_public, **kwargs)
890 resource = {
891 "type": "share_type",
892 "id": share_type["share_type"]["id"],
893 "client": client,
894 }
895 if cleanup_in_class:
896 cls.class_resources.insert(0, resource)
897 else:
898 cls.method_resources.insert(0, resource)
899 return share_type
900
haixin0d1d29f2019-08-02 16:50:45 +0800901 @classmethod
902 def update_share_type(cls, share_type_id, name=None,
903 is_public=None, description=None,
904 client=None):
905 if client is None:
906 client = cls.shares_v2_client
907 share_type = client.update_share_type(share_type_id, name,
908 is_public, description)
909 return share_type
910
Marc Koderer0abc93b2015-07-15 09:18:35 +0200911 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400912 def add_extra_specs_to_dict(extra_specs=None):
913 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300914 dhss = six.text_type(CONF.share.multitenancy_enabled)
915 snapshot_support = six.text_type(
916 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400917 create_from_snapshot_support = six.text_type(
918 CONF.share.capability_create_share_from_snapshot_support)
919
920 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300921 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200922 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400923
924 optional = {
925 "snapshot_support": snapshot_support,
926 "create_share_from_snapshot_support": create_from_snapshot_support,
927 }
928 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
929 # required extra-spec
930 extra_specs_dict.update(optional)
931
Marc Koderer0abc93b2015-07-15 09:18:35 +0200932 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400933 extra_specs_dict.update(extra_specs)
934
935 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200936
937 @classmethod
938 def clear_isolated_creds(cls, creds=None):
939 if creds is None:
940 creds = cls.method_isolated_creds
941 for ic in creds:
942 if "deleted" not in ic.keys():
943 ic["deleted"] = False
944 if not ic["deleted"]:
945 with handle_cleanup_exceptions():
946 ic["method"]()
947 ic["deleted"] = True
948
949 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400950 def clear_share_replicas(cls, share_id, client=None):
951 client = client or cls.shares_v2_client
952 share_replicas = client.list_share_replicas(
953 share_id=share_id)
954
955 for replica in share_replicas:
956 try:
957 cls.delete_share_replica(replica['id'])
958 except exceptions.BadRequest:
959 # Ignore the exception due to deletion of last active replica
960 pass
961
962 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200963 def clear_resources(cls, resources=None):
964 """Deletes resources, that were created in test suites.
965
966 This method tries to remove resources from resource list,
967 if it is not found, assumed it was deleted in test itself.
968 It is expected, that all resources were added as LIFO
969 due to restriction of deletion resources, that is in the chain.
970
971 :param resources: dict with keys 'type','id','client' and 'deleted'
972 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200973 if resources is None:
974 resources = cls.method_resources
975 for res in resources:
976 if "deleted" not in res.keys():
977 res["deleted"] = False
978 if "client" not in res.keys():
979 res["client"] = cls.shares_client
980 if not(res["deleted"]):
981 res_id = res['id']
982 client = res["client"]
983 with handle_cleanup_exceptions():
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200984 if res["type"] == "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400985 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400986 share_group_id = res.get('share_group_id')
987 if share_group_id:
988 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400989 client.delete_share(res_id, params=params)
990 else:
991 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200992 client.wait_for_resource_deletion(share_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200993 elif res["type"] == "snapshot":
Marc Koderer0abc93b2015-07-15 09:18:35 +0200994 client.delete_snapshot(res_id)
995 client.wait_for_resource_deletion(snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +0200996 elif (res["type"] == "share_network" and
yogesh06f519f2017-06-26 13:16:12 -0400997 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400998 client.delete_share_network(res_id)
999 client.wait_for_resource_deletion(sn_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001000 elif res["type"] == "security_service":
Marc Koderer0abc93b2015-07-15 09:18:35 +02001001 client.delete_security_service(res_id)
1002 client.wait_for_resource_deletion(ss_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001003 elif res["type"] == "share_type":
Marc Koderer0abc93b2015-07-15 09:18:35 +02001004 client.delete_share_type(res_id)
1005 client.wait_for_resource_deletion(st_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001006 elif res["type"] == "share_group":
Andrew Kerrb8436922016-06-01 15:32:43 -04001007 client.delete_share_group(res_id)
1008 client.wait_for_resource_deletion(
1009 share_group_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001010 elif res["type"] == "share_group_type":
Andrew Kerrb8436922016-06-01 15:32:43 -04001011 client.delete_share_group_type(res_id)
1012 client.wait_for_resource_deletion(
1013 share_group_type_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001014 elif res["type"] == "share_group_snapshot":
Andrew Kerrb8436922016-06-01 15:32:43 -04001015 client.delete_share_group_snapshot(res_id)
1016 client.wait_for_resource_deletion(
1017 share_group_snapshot_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001018 elif res["type"] == "share_replica":
Yogeshbdb88102015-09-29 23:41:02 -04001019 client.delete_share_replica(res_id)
1020 client.wait_for_resource_deletion(replica_id=res_id)
Andreas Jaeger0cb685b2020-04-01 13:38:38 +02001021 elif res["type"] == "share_network_subnet":
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001022 sn_id = res["extra_params"]["share_network_id"]
1023 client.delete_subnet(sn_id, res_id)
1024 client.wait_for_resource_deletion(
1025 share_network_subnet_id=res_id,
1026 sn_id=sn_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +02001027 else:
huayue97bacbf2016-01-04 09:57:39 +08001028 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +08001029 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +02001030 res["deleted"] = True
1031
1032 @classmethod
1033 def generate_share_network_data(self):
1034 data = {
1035 "name": data_utils.rand_name("sn-name"),
1036 "description": data_utils.rand_name("sn-desc"),
1037 "neutron_net_id": data_utils.rand_name("net-id"),
1038 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1039 }
1040 return data
1041
1042 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001043 def generate_subnet_data(self):
1044 data = {
1045 "neutron_net_id": data_utils.rand_name("net-id"),
1046 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1047 }
1048 return data
1049
1050 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001051 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001052 data = {
1053 "name": data_utils.rand_name("ss-name"),
1054 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001055 "dns_ip": utils.rand_ip(),
1056 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001057 "domain": data_utils.rand_name("ss-domain"),
1058 "user": data_utils.rand_name("ss-user"),
1059 "password": data_utils.rand_name("ss-password"),
1060 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001061 if set_ou:
1062 data["ou"] = data_utils.rand_name("ss-ou")
1063
Marc Koderer0abc93b2015-07-15 09:18:35 +02001064 return data
1065
1066 # Useful assertions
1067 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1068 """Assert two dicts are equivalent.
1069
1070 This is a 'deep' match in the sense that it handles nested
1071 dictionaries appropriately.
1072
1073 NOTE:
1074
1075 If you don't care (or don't know) a given value, you can specify
1076 the string DONTCARE as the value. This will cause that dict-item
1077 to be skipped.
1078
1079 """
1080 def raise_assertion(msg):
1081 d1str = str(d1)
1082 d2str = str(d2)
1083 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1084 'd2: %(d2str)s' %
1085 {"msg": msg, "d1str": d1str, "d2str": d2str})
1086 raise AssertionError(base_msg)
1087
1088 d1keys = set(d1.keys())
1089 d2keys = set(d2.keys())
1090 if d1keys != d2keys:
1091 d1only = d1keys - d2keys
1092 d2only = d2keys - d1keys
1093 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1094 'Keys in d2 and not d1: %(d2only)s' %
1095 {"d1only": d1only, "d2only": d2only})
1096
1097 for key in d1keys:
1098 d1value = d1[key]
1099 d2value = d2[key]
1100 try:
1101 error = abs(float(d1value) - float(d2value))
1102 within_tolerance = error <= tolerance
1103 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001104 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001105 # ValueError if arg is a str, TypeError if it's something else
1106 # (like None)
1107 within_tolerance = False
1108
1109 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1110 self.assertDictMatch(d1value, d2value)
1111 elif 'DONTCARE' in (d1value, d2value):
1112 continue
1113 elif approx_equal and within_tolerance:
1114 continue
1115 elif d1value != d2value:
1116 raise_assertion("d1['%(key)s']=%(d1value)s != "
1117 "d2['%(key)s']=%(d2value)s" %
1118 {
1119 "key": key,
1120 "d1value": d1value,
1121 "d2value": d2value
1122 })
1123
Alex Meadeba8a1602016-05-06 09:33:09 -04001124 def create_user_message(self):
1125 """Trigger a 'no valid host' situation to generate a message."""
1126 extra_specs = {
1127 'vendor_name': 'foobar',
1128 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1129 }
1130 share_type_name = data_utils.rand_name("share-type")
1131
1132 bogus_type = self.create_share_type(
1133 name=share_type_name,
1134 extra_specs=extra_specs)['share_type']
1135
1136 params = {'share_type_id': bogus_type['id'],
1137 'share_network_id': self.shares_v2_client.share_network_id}
1138 share = self.shares_v2_client.create_share(**params)
1139 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1140 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1141 return self.shares_v2_client.wait_for_message(share['id'])
1142
Marc Koderer0abc93b2015-07-15 09:18:35 +02001143
1144class BaseSharesAltTest(BaseSharesTest):
1145 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001146 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001147
1148
1149class BaseSharesAdminTest(BaseSharesTest):
1150 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001151 credentials = ('admin', )
1152
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001153 @classmethod
1154 def setup_clients(cls):
1155 super(BaseSharesAdminTest, cls).setup_clients()
1156 # Initialise share clients
1157 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1158
1159 @classmethod
1160 def _create_share_type(cls, specs=None):
1161 name = data_utils.rand_name("unique_st_name")
1162 extra_specs = cls.add_extra_specs_to_dict(specs)
1163 return cls.create_share_type(
1164 name, extra_specs=extra_specs,
1165 client=cls.admin_shares_v2_client)['share_type']
1166
1167 @classmethod
1168 def _create_share_group_type(cls):
1169 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1170 return cls.create_share_group_type(
1171 name=share_group_type_name, share_types=[cls.share_type_id],
1172 client=cls.admin_shares_v2_client)
1173
Lucio Seki37056942019-01-24 15:40:20 -02001174 def _create_share_for_manage(self):
1175 creation_data = {
1176 'share_type_id': self.st['share_type']['id'],
1177 'share_protocol': self.protocol,
1178 }
1179
1180 share = self.create_share(**creation_data)
1181 share = self.shares_v2_client.get_share(share['id'])
1182
1183 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1184 el = self.shares_v2_client.list_share_export_locations(share["id"])
1185 share["export_locations"] = el
1186
1187 return share
1188
1189 def _unmanage_share_and_wait(self, share):
1190 self.shares_v2_client.unmanage_share(share['id'])
1191 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1192
1193 def _reset_state_and_delete_share(self, share):
1194 self.shares_v2_client.reset_state(share['id'])
1195 self._delete_share_and_wait(share)
1196
1197 def _delete_snapshot_and_wait(self, snap):
1198 self.shares_v2_client.delete_snapshot(snap['id'])
1199 self.shares_v2_client.wait_for_resource_deletion(
1200 snapshot_id=snap['id']
1201 )
1202 self.assertRaises(exceptions.NotFound,
1203 self.shares_v2_client.get_snapshot,
1204 snap['id'])
1205
1206 def _delete_share_and_wait(self, share):
1207 self.shares_v2_client.delete_share(share['id'])
1208 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1209 self.assertRaises(exceptions.NotFound,
1210 self.shares_v2_client.get_share,
1211 share['id'])
1212
1213 def _manage_share(self, share, name, description, share_server_id):
1214 managed_share = self.shares_v2_client.manage_share(
1215 service_host=share['host'],
1216 export_path=share['export_locations'][0],
1217 protocol=share['share_proto'],
1218 share_type_id=self.share_type['share_type']['id'],
1219 name=name,
1220 description=description,
1221 share_server_id=share_server_id
1222 )
1223 self.shares_v2_client.wait_for_share_status(
1224 managed_share['id'], constants.STATUS_AVAILABLE
1225 )
1226
1227 return managed_share
1228
1229 def _unmanage_share_server_and_wait(self, server):
1230 self.shares_v2_client.unmanage_share_server(server['id'])
1231 self.shares_v2_client.wait_for_resource_deletion(
1232 server_id=server['id']
1233 )
1234
1235 def _manage_share_server(self, share_server, fields=None):
1236 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001237 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001238 managed_share_server = self.shares_v2_client.manage_share_server(
1239 params.get('host', share_server['host']),
1240 params.get('share_network_id', share_server['share_network_id']),
1241 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001242 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001243 )
1244 self.shares_v2_client.wait_for_share_server_status(
1245 managed_share_server['id'],
1246 constants.SERVER_STATE_ACTIVE,
1247 )
1248
1249 return managed_share_server
1250
1251 def _delete_share_server_and_wait(self, share_server_id):
1252 self.shares_v2_client.delete_share_server(
1253 share_server_id
1254 )
1255 self.shares_v2_client.wait_for_resource_deletion(
1256 server_id=share_server_id)
1257
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001258
1259class BaseSharesMixedTest(BaseSharesTest):
1260 """Base test case class for all Shares API tests with all user roles."""
1261 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001262
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001263 # Will be cleaned up in resource_cleanup if the class
1264 class_project_users_created = []
1265
1266 @classmethod
1267 def resource_cleanup(cls):
1268 cls.clear_project_users(cls.class_project_users_created)
1269 super(BaseSharesMixedTest, cls).resource_cleanup()
1270
1271 @classmethod
1272 def clear_project_users(cls, users=None):
1273 users = users or cls.class_project_users_created
1274 for user in users:
1275 with handle_cleanup_exceptions():
1276 cls.os_admin.creds_client.delete_user(user['id'])
1277
Marc Koderer0abc93b2015-07-15 09:18:35 +02001278 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001279 def setup_clients(cls):
1280 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001281 # Initialise share clients
1282 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1283 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1284 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1285 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1286 # Initialise network clients
1287 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1288 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001289 # Initialise identity clients
1290 cls.admin_project = cls.os_admin.auth_provider.auth_data[1]['project']
1291 identity_clients = getattr(
1292 cls.os_admin, 'identity_%s' % CONF.identity.auth_version)
1293 cls.os_admin.identity_client = identity_clients.IdentityClient()
1294 cls.os_admin.projects_client = identity_clients.ProjectsClient()
1295 cls.os_admin.users_client = identity_clients.UsersClient()
1296 cls.os_admin.roles_client = identity_clients.RolesClient()
1297 cls.os_admin.domains_client = (
1298 cls.os_admin.identity_v3.DomainsClient() if
1299 CONF.identity.auth_version == 'v3' else None)
1300 cls.admin_project_member_client = cls.create_user_and_get_client()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001301
1302 if CONF.share.multitenancy_enabled:
1303 admin_share_network_id = cls.provide_share_network(
1304 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1305 cls.admin_shares_client.share_network_id = admin_share_network_id
1306 cls.admin_shares_v2_client.share_network_id = (
1307 admin_share_network_id)
1308
1309 alt_share_network_id = cls.provide_share_network(
1310 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1311 cls.alt_shares_client.share_network_id = alt_share_network_id
1312 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001313 resource = {
1314 "type": "share_network",
1315 "id": alt_share_network_id,
1316 "client": cls.alt_shares_v2_client,
1317 }
1318 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001319
1320 @classmethod
Goutham Pacha Ravic678e212020-03-20 11:13:47 -07001321 def create_user_and_get_client(cls, project=None):
1322 """Create a user in specified project & set share clients for user
1323
1324 The user will have all roles specified in tempest.conf
1325 :param: project: a dictionary with project ID and name, if not
1326 specified, the value will be cls.admin_project
1327 """
1328 project_domain_name = (
1329 cls.os_admin.identity_client.auth_provider.credentials.get(
1330 'project_domain_name', 'Default'))
1331 cls.os_admin.creds_client = cred_client.get_creds_client(
1332 cls.os_admin.identity_client, cls.os_admin.projects_client,
1333 cls.os_admin.users_client, cls.os_admin.roles_client,
1334 cls.os_admin.domains_client, project_domain_name)
1335
1336 # User info
1337 project = project or cls.admin_project
1338 username = data_utils.rand_name('manila_%s' % project['id'])
1339 password = data_utils.rand_password()
1340 email = '%s@example.org' % username
1341
1342 user = cls.os_admin.creds_client.create_user(
1343 username, password, project, email)
1344 cls.class_project_users_created.append(user)
1345
1346 for conf_role in CONF.auth.tempest_roles:
1347 cls.os_admin.creds_client.assign_user_role(
1348 user, project, conf_role)
1349
1350 user_creds = cls.os_admin.creds_client.get_credentials(
1351 user, project, password)
1352 os = clients.Clients(user_creds)
1353 os.shares_v1_client = os.share_v1.SharesClient()
1354 os.shares_v2_client = os.share_v2.SharesV2Client()
1355 return os
1356
1357 @classmethod
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001358 def _create_share_type(cls, specs=None):
1359 name = data_utils.rand_name("unique_st_name")
1360 extra_specs = cls.add_extra_specs_to_dict(specs)
1361 return cls.create_share_type(
1362 name, extra_specs=extra_specs,
1363 client=cls.admin_shares_v2_client)['share_type']
1364
1365 @classmethod
1366 def _create_share_group_type(cls):
1367 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1368 return cls.create_share_group_type(
1369 name=share_group_type_name, share_types=[cls.share_type_id],
1370 client=cls.admin_shares_v2_client)