blob: fefdc81451e364e3527db7d6670bf2fa5bb6b517 [file] [log] [blame]
Marc Koderer0abc93b2015-07-15 09:18:35 +02001# Copyright 2014 Mirantis Inc.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import copy
17import inspect
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030018import re
Marc Koderer0abc93b2015-07-15 09:18:35 +020019import traceback
20
21from oslo_concurrency import lockutils
22from oslo_log import log
23import six
Sam Wanc7b7f1f2015-11-25 00:22:28 -050024from tempest.common import credentials_factory as common_creds
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010025
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020026from tempest import config
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010027from tempest.lib.common import dynamic_creds
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050028from tempest.lib.common.utils import data_utils
29from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020030from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020031
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010032from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040033from manila_tempest_tests.common import constants
Marc Koderer0abc93b2015-07-15 09:18:35 +020034from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020035from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020036
37CONF = config.CONF
38LOG = log.getLogger(__name__)
39
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030040# Test tags related to test direction
41TAG_POSITIVE = "positive"
42TAG_NEGATIVE = "negative"
43
44# Test tags related to service involvement
45TAG_API = "api"
46TAG_BACKEND = "backend"
47TAG_API_WITH_BACKEND = "api_with_backend"
48
49TAGS_MAPPER = {
50 "p": TAG_POSITIVE,
51 "n": TAG_NEGATIVE,
52 "a": TAG_API,
53 "b": TAG_BACKEND,
54 "ab": TAG_API_WITH_BACKEND,
55}
56TAGS_PATTERN = re.compile(
57 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
58 TAGS_MAPPER)
59
60
61def verify_test_has_appropriate_tags(self):
62 if not TAGS_PATTERN.match(self.id()):
63 msg = (
64 "Required attributes either not set or set improperly. "
65 "Two test attributes are expected:\n"
66 " - one of '%(p)s' or '%(n)s' and \n"
67 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
68 ) % TAGS_MAPPER
69 raise self.failureException(msg)
70
Marc Koderer0abc93b2015-07-15 09:18:35 +020071
72class handle_cleanup_exceptions(object):
73 """Handle exceptions raised with cleanup operations.
74
75 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
76 are raised.
77 Suppress all other exceptions only in case config opt
78 'suppress_errors_in_cleanup' in config group 'share' is True.
79 """
80
81 def __enter__(self):
82 return self
83
84 def __exit__(self, exc_type, exc_value, exc_traceback):
85 if not (isinstance(exc_value,
86 (exceptions.NotFound, exceptions.Forbidden)) or
87 CONF.share.suppress_errors_in_cleanup):
88 return False # Do not suppress error if any
89 if exc_traceback:
90 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080091 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020092 return True # Suppress error if any
93
94
95def network_synchronized(f):
96
97 def wrapped_func(self, *args, **kwargs):
98 with_isolated_creds = True if len(args) > 2 else False
99 no_lock_required = kwargs.get(
100 "isolated_creds_client", with_isolated_creds)
101 if no_lock_required:
102 # Usage of not reusable network. No need in lock.
103 return f(self, *args, **kwargs)
104
105 # Use lock assuming reusage of common network.
106 @lockutils.synchronized("manila_network_lock", external=True)
107 def source_func(self, *args, **kwargs):
108 return f(self, *args, **kwargs)
109
110 return source_func(self, *args, **kwargs)
111
112 return wrapped_func
113
114
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200115skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500116skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200117
118
Marc Koderer0abc93b2015-07-15 09:18:35 +0200119class BaseSharesTest(test.BaseTestCase):
120 """Base test case class for all Manila API tests."""
121
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300122 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200123 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200124 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200125
126 # Will be cleaned up in resource_cleanup
127 class_resources = []
128
129 # Will be cleaned up in tearDown method
130 method_resources = []
131
132 # Will be cleaned up in resource_cleanup
133 class_isolated_creds = []
134
135 # Will be cleaned up in tearDown method
136 method_isolated_creds = []
137
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100138 # NOTE(andreaf) Override the client manager class to be used, so that
139 # a stable class is used, which includes plugin registered services as well
140 client_manager = clients.Clients
141
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200142 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200143 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200144 raise self.skipException(
145 "Microversion '%s' is not supported." % microversion)
146
Xing Yang69b00b52015-11-22 16:10:44 -0500147 def skip_if_microversion_lt(self, microversion):
148 if utils.is_microversion_lt(CONF.share.max_api_microversion,
149 microversion):
150 raise self.skipException(
151 "Microversion must be greater than or equal to '%s'." %
152 microversion)
153
Marc Koderer0abc93b2015-07-15 09:18:35 +0200154 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500155 def _get_dynamic_creds(cls, name, network_resources=None):
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100156 identity_version = CONF.identity.auth_version
157 if identity_version == 'v3':
158 identity_uri = CONF.identity.uri_v3
159 identity_admin_endpoint_type = CONF.identity.v3_endpoint_type
160 elif identity_version == 'v2':
161 identity_uri = CONF.identity.uri
162 identity_admin_endpoint_type = CONF.identity.v2_admin_endpoint_type
163
Tom Barron4b8834a2017-02-02 11:02:20 -0500164 return dynamic_creds.DynamicCredentialProvider(
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100165 identity_version=identity_version,
Tom Barron4b8834a2017-02-02 11:02:20 -0500166 name=name,
167 network_resources=network_resources,
168 credentials_domain=CONF.auth.default_credentials_domain_name,
169 admin_role=CONF.identity.admin_role,
170 admin_creds=common_creds.get_configured_admin_credentials(),
171 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
172 identity_admin_role=CONF.identity.admin_role,
173 extra_roles=None,
174 neutron_available=CONF.service_available.neutron,
175 create_networks=(
176 CONF.share.create_networks_when_multitenancy_enabled),
177 project_network_cidr=CONF.network.project_network_cidr,
178 project_network_mask_bits=CONF.network.project_network_mask_bits,
179 public_network_id=CONF.network.public_network_id,
ghanshyam2aea7c32017-12-11 00:03:56 +0000180 resource_prefix='tempest',
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100181 identity_admin_endpoint_type=identity_admin_endpoint_type,
182 identity_uri=identity_uri)
Tom Barron4b8834a2017-02-02 11:02:20 -0500183
184 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200185 def get_client_with_isolated_creds(cls,
186 name=None,
187 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400188 cleanup_in_class=False,
189 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200190 """Creates isolated creds.
191
192 :param name: name, will be used for naming ic and related stuff
193 :param type_of_creds: admin, alt or primary
194 :param cleanup_in_class: defines place where to delete
195 :returns: SharesClient -- shares client with isolated creds.
196 :returns: To client added dict attr 'creds' with
197 :returns: key elements 'tenant' and 'user'.
198 """
199 if name is None:
200 # Get name of test method
201 name = inspect.stack()[1][3]
202 if len(name) > 32:
203 name = name[0:32]
204
205 # Choose type of isolated creds
Tom Barron4b8834a2017-02-02 11:02:20 -0500206 ic = cls._get_dynamic_creds(name)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200207 if "admin" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200208 creds = ic.get_admin_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200209 elif "alt" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200210 creds = ic.get_alt_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200211 else:
Marc Koderer5880b362016-07-06 10:59:07 +0200212 creds = ic.get_credentials(type_of_creds).credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200213 ic.type_of_creds = type_of_creds
214
215 # create client with isolated creds
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100216 os = clients.Clients(creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400217 if client_version == '1':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100218 client = os.share_v1.SharesClient()
Clinton Knighte5c8f092015-08-27 15:00:23 -0400219 elif client_version == '2':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100220 client = os.share_v2.SharesV2Client()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200221
222 # Set place where will be deleted isolated creds
223 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200224 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200225 "deleted": False,
226 }
227 if cleanup_in_class:
228 cls.class_isolated_creds.insert(0, ic_res)
229 else:
230 cls.method_isolated_creds.insert(0, ic_res)
231
232 # Provide share network
233 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300234 if (not CONF.service_available.neutron and
235 CONF.share.create_networks_when_multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200236 raise cls.skipException("Neutron support is required")
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100237 nc = os.network.NetworksClient()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200238 share_network_id = cls.provide_share_network(client, nc, ic)
239 client.share_network_id = share_network_id
240 resource = {
241 "type": "share_network",
242 "id": client.share_network_id,
243 "client": client,
244 }
245 if cleanup_in_class:
246 cls.class_resources.insert(0, resource)
247 else:
248 cls.method_resources.insert(0, resource)
249 return client
250
251 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000252 def skip_checks(cls):
253 super(BaseSharesTest, cls).skip_checks()
254 if not CONF.service_available.manila:
255 raise cls.skipException("Manila support is required")
256
257 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200258 def verify_nonempty(cls, *args):
259 if not all(args):
260 msg = "Missing API credentials in configuration."
261 raise cls.skipException(msg)
262
263 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300264 def setup_clients(cls):
265 super(BaseSharesTest, cls).setup_clients()
266 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100267 # Initialise share clients for test credentials
268 cls.shares_client = os.share_v1.SharesClient()
269 cls.shares_v2_client = os.share_v2.SharesV2Client()
270 # Initialise network clients for test credentials
271 if CONF.service_available.neutron:
272 cls.networks_client = os.network.NetworksClient()
273 cls.subnets_client = os.network.SubnetsClient()
274 else:
275 cls.networks_client = None
276 cls.subnets_client = None
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300277
278 if CONF.identity.auth_version == 'v3':
279 project_id = os.auth_provider.auth_data[1]['project']['id']
280 else:
281 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
282 cls.tenant_id = project_id
283 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
284
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300285 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300286 if (not CONF.service_available.neutron and
287 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300288 raise cls.skipException("Neutron support is required")
289 share_network_id = cls.provide_share_network(
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100290 cls.shares_v2_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300291 cls.shares_client.share_network_id = share_network_id
292 cls.shares_v2_client.share_network_id = share_network_id
haobing101c7fee2018-03-09 16:33:00 +0800293 resource = {
294 "type": "share_network",
295 "id": share_network_id,
296 "client": cls.shares_v2_client,
297 }
298 cls.class_resources.insert(0, resource)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300299
300 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200301 def resource_setup(cls):
302 if not (any(p in CONF.share.enable_protocols
303 for p in cls.protocols) and
304 CONF.service_available.manila):
305 skip_msg = "Manila is disabled"
306 raise cls.skipException(skip_msg)
307 super(BaseSharesTest, cls).resource_setup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200308
309 def setUp(self):
310 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200311 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200312 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300313 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200314
315 @classmethod
316 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200317 cls.clear_resources(cls.class_resources)
318 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400319 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200320
321 @classmethod
322 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200323 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300324 isolated_creds_client=None,
325 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200326 """Used for finding/creating share network for multitenant driver.
327
328 This method creates/gets entity share-network for one tenant. This
329 share-network will be used for creation of service vm.
330
331 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200332 :param networks_client: network client from same tenant as shares
333 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200334 If provided, then its networking will be used if needed.
335 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300336 :param ignore_multitenancy_config: provide a share network regardless
337 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200338 :returns: str -- share network id for shares_client tenant
339 :returns: None -- if single-tenant driver used
340 """
341
342 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300343 search_word = "reusable"
344 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200345
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300346 if (not ignore_multitenancy_config and
347 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200348 # Assumed usage of a single-tenant driver
349 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200350 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300351 if sc.share_network_id:
352 # Share-network already exists, use it
353 share_network_id = sc.share_network_id
354 elif not CONF.share.create_networks_when_multitenancy_enabled:
355 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200356
357 # Try get suitable share-network
358 share_networks = sc.list_share_networks_with_detail()
359 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300360 if (sn["neutron_net_id"] is None and
361 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200362 sn["name"] and search_word in sn["name"]):
363 share_network_id = sn["id"]
364 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200365
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300366 # Create new share-network if one was not found
367 if share_network_id is None:
368 sn_desc = "This share-network was created by tempest"
369 sn = sc.create_share_network(name=sn_name,
370 description=sn_desc)
371 share_network_id = sn["id"]
372 else:
373 net_id = subnet_id = share_network_id = None
374
375 if not isolated_creds_client:
376 # Search for networks, created in previous runs
377 service_net_name = "share-service"
378 networks = networks_client.list_networks()
379 if "networks" in networks.keys():
380 networks = networks["networks"]
381 for network in networks:
382 if (service_net_name in network["name"] and
383 sc.tenant_id == network['tenant_id']):
384 net_id = network["id"]
385 if len(network["subnets"]) > 0:
386 subnet_id = network["subnets"][0]
387 break
388
389 # Create suitable network
390 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500391 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300392 net_data = ic._create_network_resources(sc.tenant_id)
393 network, subnet, router = net_data
394 net_id = network["id"]
395 subnet_id = subnet["id"]
396
397 # Try get suitable share-network
398 share_networks = sc.list_share_networks_with_detail()
399 for sn in share_networks:
400 if (net_id == sn["neutron_net_id"] and
401 subnet_id == sn["neutron_subnet_id"] and
402 sn["name"] and search_word in sn["name"]):
403 share_network_id = sn["id"]
404 break
405 else:
406 sn_name = "autogenerated_by_tempest_for_isolated_creds"
407 # Use precreated network and subnet from isolated creds
408 net_id = isolated_creds_client.get_credentials(
409 isolated_creds_client.type_of_creds).network['id']
410 subnet_id = isolated_creds_client.get_credentials(
411 isolated_creds_client.type_of_creds).subnet['id']
412
413 # Create suitable share-network
414 if share_network_id is None:
415 sn_desc = "This share-network was created by tempest"
416 sn = sc.create_share_network(name=sn_name,
417 description=sn_desc,
418 neutron_net_id=net_id,
419 neutron_subnet_id=subnet_id)
420 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200421
422 return share_network_id
423
424 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300425 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200426 snapshot_id=None, description=None, metadata=None,
427 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400428 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400429 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300430 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200431 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400432 share_network_id = (share_network_id or
433 CONF.share.share_network_id or
434 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200435 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300436 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400437 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200438 'share_protocol': share_protocol,
439 'size': size,
440 'name': name,
441 'snapshot_id': snapshot_id,
442 'description': description,
443 'metadata': metadata,
444 'share_network_id': share_network_id,
445 'share_type_id': share_type_id,
446 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400447 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400448 if share_group_id:
449 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400450
Marc Koderer0abc93b2015-07-15 09:18:35 +0200451 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400452 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400453 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200454 cleanup_list = (cls.class_resources if cleanup_in_class else
455 cls.method_resources)
456 cleanup_list.insert(0, resource)
457 return share
458
459 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300460 def migrate_share(
461 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200462 force_host_assisted_migration=False, writable=False,
463 nondisruptive=False, preserve_metadata=False,
464 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300465 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400466 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300467 client.migrate_share(
468 share_id, dest_host,
469 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200470 writable=writable, preserve_metadata=preserve_metadata,
471 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300472 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300473 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200474 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300475 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200476 return share
477
478 @classmethod
479 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
480 client = client or cls.shares_v2_client
481 client.migration_complete(share_id, **kwargs)
482 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300483 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300484 return share
485
486 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300487 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
488 client = client or cls.shares_v2_client
489 client.migration_cancel(share_id, **kwargs)
490 share = client.wait_for_migration_status(
491 share_id, dest_host, 'migration_cancelled', **kwargs)
492 return share
493
494 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200495 def create_share(cls, *args, **kwargs):
496 """Create one share and wait for available state. Retry if allowed."""
497 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
498 return result[0]
499
500 @classmethod
501 def create_shares(cls, share_data_list):
502 """Creates several shares in parallel with retries.
503
504 Use this method when you want to create more than one share at same
505 time. Especially if config option 'share.share_creation_retry_number'
506 has value more than zero (0).
507 All shares will be expected to have 'available' status with or without
508 recreation else error will be raised.
509
510 :param share_data_list: list -- list of dictionaries with 'args' and
511 'kwargs' for '_create_share' method of this base class.
512 example of data:
513 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
514 :returns: list -- list of shares created using provided data.
515 """
516
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300517 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200518 if not isinstance(d, dict):
519 raise exceptions.TempestException(
520 "Expected 'dict', got '%s'" % type(d))
521 if "args" not in d:
522 d["args"] = []
523 if "kwargs" not in d:
524 d["kwargs"] = {}
525 if len(d) > 2:
526 raise exceptions.TempestException(
527 "Expected only 'args' and 'kwargs' keys. "
528 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300529
530 data = []
531 for d in share_data_list:
532 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400533 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300534 local_d = {
535 "args": d["args"],
536 "kwargs": copy.deepcopy(d["kwargs"]),
537 }
538 local_d["kwargs"]["client"] = client
539 local_d["share"] = cls._create_share(
540 *local_d["args"], **local_d["kwargs"])
541 local_d["cnt"] = 0
542 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400543 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300544 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200545
546 while not all(d["available"] for d in data):
547 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400548 if not d["wait_for_status"]:
549 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200550 if d["available"]:
551 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300552 client = d["kwargs"]["client"]
553 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200554 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300555 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200556 d["available"] = True
557 except (share_exceptions.ShareBuildErrorException,
558 exceptions.TimeoutException) as e:
559 if CONF.share.share_creation_retry_number > d["cnt"]:
560 d["cnt"] += 1
561 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300562 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200563 LOG.error(msg)
564 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300565 cg_id = d["kwargs"].get("consistency_group_id")
566 if cg_id:
567 # NOTE(vponomaryov): delete errored share
568 # immediately in case share is part of CG.
569 client.delete_share(
570 share_id,
571 params={"consistency_group_id": cg_id})
572 client.wait_for_resource_deletion(
573 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200574 d["share"] = cls._create_share(
575 *d["args"], **d["kwargs"])
576 else:
gecong197358663802016-08-25 11:08:45 +0800577 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200578
579 return [d["share"] for d in data]
580
581 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400582 def create_share_group(cls, client=None, cleanup_in_class=True,
583 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400584 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400585 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400586 kwargs['share_network_id'] = (share_network_id or
587 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400588 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400589 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400590 "type": "share_group",
591 "id": share_group["id"],
592 "client": client,
593 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400594 if cleanup_in_class:
595 cls.class_resources.insert(0, resource)
596 else:
597 cls.method_resources.insert(0, resource)
598
Andrew Kerrb8436922016-06-01 15:32:43 -0400599 if kwargs.get('source_share_group_snapshot_id'):
600 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400601 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400602 params={'share_group_id': share_group['id']},
603 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400604
Andrew Kerrb8436922016-06-01 15:32:43 -0400605 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400606 resource = {"type": "share",
607 "id": share["id"],
608 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400609 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400610 if cleanup_in_class:
611 cls.class_resources.insert(0, resource)
612 else:
613 cls.method_resources.insert(0, resource)
614
Andrew Kerrb8436922016-06-01 15:32:43 -0400615 client.wait_for_share_group_status(share_group['id'], 'available')
616 return share_group
617
618 @classmethod
619 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
620 group_specs=None, client=None,
621 cleanup_in_class=True, **kwargs):
622 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300623 if (group_specs is None and
624 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300625 group_specs = {
626 'consistent_snapshot_support': (
627 CONF.share.capability_sg_consistent_snapshot_support),
628 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400629 share_group_type = client.create_share_group_type(
630 name=name,
631 share_types=share_types,
632 is_public=is_public,
633 group_specs=group_specs,
634 **kwargs)
635 resource = {
636 "type": "share_group_type",
637 "id": share_group_type["id"],
638 "client": client,
639 }
640 if cleanup_in_class:
641 cls.class_resources.insert(0, resource)
642 else:
643 cls.method_resources.insert(0, resource)
644 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400645
646 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200647 def create_snapshot_wait_for_active(cls, share_id, name=None,
648 description=None, force=False,
649 client=None, cleanup_in_class=True):
650 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400651 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200652 if description is None:
653 description = "Tempest's snapshot"
654 snapshot = client.create_snapshot(share_id, name, description, force)
655 resource = {
656 "type": "snapshot",
657 "id": snapshot["id"],
658 "client": client,
659 }
660 if cleanup_in_class:
661 cls.class_resources.insert(0, resource)
662 else:
663 cls.method_resources.insert(0, resource)
664 client.wait_for_snapshot_status(snapshot["id"], "available")
665 return snapshot
666
667 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400668 def create_share_group_snapshot_wait_for_active(
669 cls, share_group_id, name=None, description=None, client=None,
670 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400671 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400672 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400673 description = "Tempest's share group snapshot"
674 sg_snapshot = client.create_share_group_snapshot(
675 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400676 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400677 "type": "share_group_snapshot",
678 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400679 "client": client,
680 }
681 if cleanup_in_class:
682 cls.class_resources.insert(0, resource)
683 else:
684 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400685 client.wait_for_share_group_snapshot_status(
686 sg_snapshot["id"], "available")
687 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400688
689 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800690 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400691 """List the availability zones for "manila-share" services
692
693 that are currently in "up" state.
694 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800695 client = client or cls.admin_shares_v2_client
696 backends = (
697 '|'.join(['^%s$' % backend for backend in backends])
698 if backends else '.*'
699 )
Yogeshbdb88102015-09-29 23:41:02 -0400700 cls.services = client.list_services()
701 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800702 service['binary'] == 'manila-share' and
703 service['state'] == 'up' and
704 re.search(backends, service['host'])]
Yogeshbdb88102015-09-29 23:41:02 -0400705 return zones
706
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800707 @classmethod
708 def get_pools_matching_share_type(cls, share_type, client=None):
709 client = client or cls.admin_shares_v2_client
710 if utils.is_microversion_supported('2.23'):
711 return client.list_pools(
712 search_opts={'share_type': share_type['id']})['pools']
713
714 pools = client.list_pools(detail=True)['pools']
715 share_type = client.get_share_type(share_type['id'])['share_type']
716 extra_specs = {}
717 for k, v in share_type['extra_specs'].items():
718 extra_specs[k] = (
719 True if six.text_type(v).lower() == 'true'
720 else False if six.text_type(v).lower() == 'false' else v
721 )
722 return [
723 pool for pool in pools if all(y in pool['capabilities'].items()
724 for y in extra_specs.items())
725 ]
726
727 @classmethod
728 def get_availability_zones_matching_share_type(cls, share_type,
729 client=None):
730
731 client = client or cls.admin_shares_v2_client
732 pools_matching_share_type = cls.get_pools_matching_share_type(
733 share_type, client=client)
734 backends_matching_share_type = set(
735 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
736 )
737 azs = cls.get_availability_zones(backends=backends_matching_share_type)
738 return azs
739
Yogesh1f931ff2015-09-29 23:41:02 -0400740 def get_pools_for_replication_domain(self):
741 # Get the list of pools for the replication domain
742 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500743 instance_host = self.admin_client.get_share(
744 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400745 host_pool = [p for p in pools if p['name'] == instance_host][0]
746 rep_domain = host_pool['capabilities']['replication_domain']
747 pools_in_rep_domain = [p for p in pools if p['capabilities'][
748 'replication_domain'] == rep_domain]
749 return rep_domain, pools_in_rep_domain
750
Yogeshbdb88102015-09-29 23:41:02 -0400751 @classmethod
752 def create_share_replica(cls, share_id, availability_zone, client=None,
753 cleanup_in_class=False, cleanup=True):
754 client = client or cls.shares_v2_client
755 replica = client.create_share_replica(share_id, availability_zone)
756 resource = {
757 "type": "share_replica",
758 "id": replica["id"],
759 "client": client,
760 "share_id": share_id,
761 }
762 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
763 if cleanup:
764 if cleanup_in_class:
765 cls.class_resources.insert(0, resource)
766 else:
767 cls.method_resources.insert(0, resource)
768 client.wait_for_share_replica_status(
769 replica["id"], constants.STATUS_AVAILABLE)
770 return replica
771
772 @classmethod
773 def delete_share_replica(cls, replica_id, client=None):
774 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400775 try:
776 client.delete_share_replica(replica_id)
777 client.wait_for_resource_deletion(replica_id=replica_id)
778 except exceptions.NotFound:
779 pass
Yogeshbdb88102015-09-29 23:41:02 -0400780
781 @classmethod
782 def promote_share_replica(cls, replica_id, client=None):
783 client = client or cls.shares_v2_client
784 replica = client.promote_share_replica(replica_id)
785 client.wait_for_share_replica_status(
786 replica["id"],
787 constants.REPLICATION_STATE_ACTIVE,
788 status_attr="replica_state")
789 return replica
790
yogeshdb32f462016-09-28 15:09:50 -0400791 def _get_access_rule_data_from_config(self):
792 """Get the first available access type/to combination from config.
793
794 This method opportunistically picks the first configured protocol
795 to create the share. Do not use this method in tests where you need
796 to test depth and breadth in the access types and access recipients.
797 """
798 protocol = self.shares_v2_client.share_protocol
799
800 if protocol in CONF.share.enable_ip_rules_for_protocols:
801 access_type = "ip"
802 access_to = utils.rand_ip()
803 elif protocol in CONF.share.enable_user_rules_for_protocols:
804 access_type = "user"
805 access_to = CONF.share.username_for_user_rules
806 elif protocol in CONF.share.enable_cert_rules_for_protocols:
807 access_type = "cert"
808 access_to = "client3.com"
809 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
810 access_type = "cephx"
811 access_to = "eve"
812 else:
813 message = "Unrecognized protocol and access rules configuration."
814 raise self.skipException(message)
815
816 return access_type, access_to
817
Yogeshbdb88102015-09-29 23:41:02 -0400818 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200819 def create_share_network(cls, client=None,
820 cleanup_in_class=False, **kwargs):
821 if client is None:
822 client = cls.shares_client
823 share_network = client.create_share_network(**kwargs)
824 resource = {
825 "type": "share_network",
826 "id": share_network["id"],
827 "client": client,
828 }
829 if cleanup_in_class:
830 cls.class_resources.insert(0, resource)
831 else:
832 cls.method_resources.insert(0, resource)
833 return share_network
834
835 @classmethod
836 def create_security_service(cls, ss_type="ldap", client=None,
837 cleanup_in_class=False, **kwargs):
838 if client is None:
839 client = cls.shares_client
840 security_service = client.create_security_service(ss_type, **kwargs)
841 resource = {
842 "type": "security_service",
843 "id": security_service["id"],
844 "client": client,
845 }
846 if cleanup_in_class:
847 cls.class_resources.insert(0, resource)
848 else:
849 cls.method_resources.insert(0, resource)
850 return security_service
851
852 @classmethod
853 def create_share_type(cls, name, is_public=True, client=None,
854 cleanup_in_class=True, **kwargs):
855 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200856 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200857 share_type = client.create_share_type(name, is_public, **kwargs)
858 resource = {
859 "type": "share_type",
860 "id": share_type["share_type"]["id"],
861 "client": client,
862 }
863 if cleanup_in_class:
864 cls.class_resources.insert(0, resource)
865 else:
866 cls.method_resources.insert(0, resource)
867 return share_type
868
869 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400870 def add_extra_specs_to_dict(extra_specs=None):
871 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300872 dhss = six.text_type(CONF.share.multitenancy_enabled)
873 snapshot_support = six.text_type(
874 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400875 create_from_snapshot_support = six.text_type(
876 CONF.share.capability_create_share_from_snapshot_support)
877
878 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300879 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200880 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400881
882 optional = {
883 "snapshot_support": snapshot_support,
884 "create_share_from_snapshot_support": create_from_snapshot_support,
885 }
886 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
887 # required extra-spec
888 extra_specs_dict.update(optional)
889
Marc Koderer0abc93b2015-07-15 09:18:35 +0200890 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400891 extra_specs_dict.update(extra_specs)
892
893 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200894
895 @classmethod
896 def clear_isolated_creds(cls, creds=None):
897 if creds is None:
898 creds = cls.method_isolated_creds
899 for ic in creds:
900 if "deleted" not in ic.keys():
901 ic["deleted"] = False
902 if not ic["deleted"]:
903 with handle_cleanup_exceptions():
904 ic["method"]()
905 ic["deleted"] = True
906
907 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400908 def clear_share_replicas(cls, share_id, client=None):
909 client = client or cls.shares_v2_client
910 share_replicas = client.list_share_replicas(
911 share_id=share_id)
912
913 for replica in share_replicas:
914 try:
915 cls.delete_share_replica(replica['id'])
916 except exceptions.BadRequest:
917 # Ignore the exception due to deletion of last active replica
918 pass
919
920 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200921 def clear_resources(cls, resources=None):
922 """Deletes resources, that were created in test suites.
923
924 This method tries to remove resources from resource list,
925 if it is not found, assumed it was deleted in test itself.
926 It is expected, that all resources were added as LIFO
927 due to restriction of deletion resources, that is in the chain.
928
929 :param resources: dict with keys 'type','id','client' and 'deleted'
930 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200931 if resources is None:
932 resources = cls.method_resources
933 for res in resources:
934 if "deleted" not in res.keys():
935 res["deleted"] = False
936 if "client" not in res.keys():
937 res["client"] = cls.shares_client
938 if not(res["deleted"]):
939 res_id = res['id']
940 client = res["client"]
941 with handle_cleanup_exceptions():
942 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400943 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400944 share_group_id = res.get('share_group_id')
945 if share_group_id:
946 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400947 client.delete_share(res_id, params=params)
948 else:
949 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200950 client.wait_for_resource_deletion(share_id=res_id)
951 elif res["type"] is "snapshot":
952 client.delete_snapshot(res_id)
953 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400954 elif (res["type"] is "share_network" and
955 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400956 client.delete_share_network(res_id)
957 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200958 elif res["type"] is "security_service":
959 client.delete_security_service(res_id)
960 client.wait_for_resource_deletion(ss_id=res_id)
961 elif res["type"] is "share_type":
962 client.delete_share_type(res_id)
963 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400964 elif res["type"] is "share_group":
965 client.delete_share_group(res_id)
966 client.wait_for_resource_deletion(
967 share_group_id=res_id)
968 elif res["type"] is "share_group_type":
969 client.delete_share_group_type(res_id)
970 client.wait_for_resource_deletion(
971 share_group_type_id=res_id)
972 elif res["type"] is "share_group_snapshot":
973 client.delete_share_group_snapshot(res_id)
974 client.wait_for_resource_deletion(
975 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400976 elif res["type"] is "share_replica":
977 client.delete_share_replica(res_id)
978 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200979 else:
huayue97bacbf2016-01-04 09:57:39 +0800980 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800981 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200982 res["deleted"] = True
983
984 @classmethod
985 def generate_share_network_data(self):
986 data = {
987 "name": data_utils.rand_name("sn-name"),
988 "description": data_utils.rand_name("sn-desc"),
989 "neutron_net_id": data_utils.rand_name("net-id"),
990 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
991 }
992 return data
993
994 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100995 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200996 data = {
997 "name": data_utils.rand_name("ss-name"),
998 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200999 "dns_ip": utils.rand_ip(),
1000 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001001 "domain": data_utils.rand_name("ss-domain"),
1002 "user": data_utils.rand_name("ss-user"),
1003 "password": data_utils.rand_name("ss-password"),
1004 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001005 if set_ou:
1006 data["ou"] = data_utils.rand_name("ss-ou")
1007
Marc Koderer0abc93b2015-07-15 09:18:35 +02001008 return data
1009
1010 # Useful assertions
1011 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1012 """Assert two dicts are equivalent.
1013
1014 This is a 'deep' match in the sense that it handles nested
1015 dictionaries appropriately.
1016
1017 NOTE:
1018
1019 If you don't care (or don't know) a given value, you can specify
1020 the string DONTCARE as the value. This will cause that dict-item
1021 to be skipped.
1022
1023 """
1024 def raise_assertion(msg):
1025 d1str = str(d1)
1026 d2str = str(d2)
1027 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1028 'd2: %(d2str)s' %
1029 {"msg": msg, "d1str": d1str, "d2str": d2str})
1030 raise AssertionError(base_msg)
1031
1032 d1keys = set(d1.keys())
1033 d2keys = set(d2.keys())
1034 if d1keys != d2keys:
1035 d1only = d1keys - d2keys
1036 d2only = d2keys - d1keys
1037 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1038 'Keys in d2 and not d1: %(d2only)s' %
1039 {"d1only": d1only, "d2only": d2only})
1040
1041 for key in d1keys:
1042 d1value = d1[key]
1043 d2value = d2[key]
1044 try:
1045 error = abs(float(d1value) - float(d2value))
1046 within_tolerance = error <= tolerance
1047 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001048 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001049 # ValueError if arg is a str, TypeError if it's something else
1050 # (like None)
1051 within_tolerance = False
1052
1053 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1054 self.assertDictMatch(d1value, d2value)
1055 elif 'DONTCARE' in (d1value, d2value):
1056 continue
1057 elif approx_equal and within_tolerance:
1058 continue
1059 elif d1value != d2value:
1060 raise_assertion("d1['%(key)s']=%(d1value)s != "
1061 "d2['%(key)s']=%(d2value)s" %
1062 {
1063 "key": key,
1064 "d1value": d1value,
1065 "d2value": d2value
1066 })
1067
Alex Meadeba8a1602016-05-06 09:33:09 -04001068 def create_user_message(self):
1069 """Trigger a 'no valid host' situation to generate a message."""
1070 extra_specs = {
1071 'vendor_name': 'foobar',
1072 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1073 }
1074 share_type_name = data_utils.rand_name("share-type")
1075
1076 bogus_type = self.create_share_type(
1077 name=share_type_name,
1078 extra_specs=extra_specs)['share_type']
1079
1080 params = {'share_type_id': bogus_type['id'],
1081 'share_network_id': self.shares_v2_client.share_network_id}
1082 share = self.shares_v2_client.create_share(**params)
1083 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1084 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1085 return self.shares_v2_client.wait_for_message(share['id'])
1086
Marc Koderer0abc93b2015-07-15 09:18:35 +02001087
1088class BaseSharesAltTest(BaseSharesTest):
1089 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001090 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001091
1092
1093class BaseSharesAdminTest(BaseSharesTest):
1094 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001095 credentials = ('admin', )
1096
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001097 @classmethod
1098 def setup_clients(cls):
1099 super(BaseSharesAdminTest, cls).setup_clients()
1100 # Initialise share clients
1101 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1102
1103 @classmethod
1104 def _create_share_type(cls, specs=None):
1105 name = data_utils.rand_name("unique_st_name")
1106 extra_specs = cls.add_extra_specs_to_dict(specs)
1107 return cls.create_share_type(
1108 name, extra_specs=extra_specs,
1109 client=cls.admin_shares_v2_client)['share_type']
1110
1111 @classmethod
1112 def _create_share_group_type(cls):
1113 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1114 return cls.create_share_group_type(
1115 name=share_group_type_name, share_types=[cls.share_type_id],
1116 client=cls.admin_shares_v2_client)
1117
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001118
1119class BaseSharesMixedTest(BaseSharesTest):
1120 """Base test case class for all Shares API tests with all user roles."""
1121 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001122
1123 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001124 def setup_clients(cls):
1125 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001126 # Initialise share clients
1127 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1128 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1129 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1130 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1131 # Initialise network clients
1132 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1133 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001134
1135 if CONF.share.multitenancy_enabled:
1136 admin_share_network_id = cls.provide_share_network(
1137 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1138 cls.admin_shares_client.share_network_id = admin_share_network_id
1139 cls.admin_shares_v2_client.share_network_id = (
1140 admin_share_network_id)
1141
1142 alt_share_network_id = cls.provide_share_network(
1143 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1144 cls.alt_shares_client.share_network_id = alt_share_network_id
1145 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001146 resource = {
1147 "type": "share_network",
1148 "id": alt_share_network_id,
1149 "client": cls.alt_shares_v2_client,
1150 }
1151 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001152
1153 @classmethod
1154 def _create_share_type(cls, specs=None):
1155 name = data_utils.rand_name("unique_st_name")
1156 extra_specs = cls.add_extra_specs_to_dict(specs)
1157 return cls.create_share_type(
1158 name, extra_specs=extra_specs,
1159 client=cls.admin_shares_v2_client)['share_type']
1160
1161 @classmethod
1162 def _create_share_group_type(cls):
1163 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1164 return cls.create_share_group_type(
1165 name=share_group_type_name, share_types=[cls.share_type_id],
1166 client=cls.admin_shares_v2_client)