blob: 1f173fb5b8128f8d59cdb49411153d47f53d07e1 [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: "
91 "\n%s" % traceback.format_exc())
92 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,
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100180 resource_prefix=CONF.resources_prefix,
181 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
293
294 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200295 def resource_setup(cls):
296 if not (any(p in CONF.share.enable_protocols
297 for p in cls.protocols) and
298 CONF.service_available.manila):
299 skip_msg = "Manila is disabled"
300 raise cls.skipException(skip_msg)
301 super(BaseSharesTest, cls).resource_setup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200302
303 def setUp(self):
304 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200305 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200306 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300307 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200308
309 @classmethod
310 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200311 cls.clear_resources(cls.class_resources)
312 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400313 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200314
315 @classmethod
316 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200317 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300318 isolated_creds_client=None,
319 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200320 """Used for finding/creating share network for multitenant driver.
321
322 This method creates/gets entity share-network for one tenant. This
323 share-network will be used for creation of service vm.
324
325 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200326 :param networks_client: network client from same tenant as shares
327 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200328 If provided, then its networking will be used if needed.
329 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300330 :param ignore_multitenancy_config: provide a share network regardless
331 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200332 :returns: str -- share network id for shares_client tenant
333 :returns: None -- if single-tenant driver used
334 """
335
336 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300337 search_word = "reusable"
338 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200339
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300340 if (not ignore_multitenancy_config and
341 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200342 # Assumed usage of a single-tenant driver
343 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200344 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300345 if sc.share_network_id:
346 # Share-network already exists, use it
347 share_network_id = sc.share_network_id
348 elif not CONF.share.create_networks_when_multitenancy_enabled:
349 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200350
351 # Try get suitable share-network
352 share_networks = sc.list_share_networks_with_detail()
353 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300354 if (sn["neutron_net_id"] is None and
355 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200356 sn["name"] and search_word in sn["name"]):
357 share_network_id = sn["id"]
358 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200359
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300360 # Create new share-network if one was not found
361 if share_network_id is None:
362 sn_desc = "This share-network was created by tempest"
363 sn = sc.create_share_network(name=sn_name,
364 description=sn_desc)
365 share_network_id = sn["id"]
366 else:
367 net_id = subnet_id = share_network_id = None
368
369 if not isolated_creds_client:
370 # Search for networks, created in previous runs
371 service_net_name = "share-service"
372 networks = networks_client.list_networks()
373 if "networks" in networks.keys():
374 networks = networks["networks"]
375 for network in networks:
376 if (service_net_name in network["name"] and
377 sc.tenant_id == network['tenant_id']):
378 net_id = network["id"]
379 if len(network["subnets"]) > 0:
380 subnet_id = network["subnets"][0]
381 break
382
383 # Create suitable network
384 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500385 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300386 net_data = ic._create_network_resources(sc.tenant_id)
387 network, subnet, router = net_data
388 net_id = network["id"]
389 subnet_id = subnet["id"]
390
391 # Try get suitable share-network
392 share_networks = sc.list_share_networks_with_detail()
393 for sn in share_networks:
394 if (net_id == sn["neutron_net_id"] and
395 subnet_id == sn["neutron_subnet_id"] and
396 sn["name"] and search_word in sn["name"]):
397 share_network_id = sn["id"]
398 break
399 else:
400 sn_name = "autogenerated_by_tempest_for_isolated_creds"
401 # Use precreated network and subnet from isolated creds
402 net_id = isolated_creds_client.get_credentials(
403 isolated_creds_client.type_of_creds).network['id']
404 subnet_id = isolated_creds_client.get_credentials(
405 isolated_creds_client.type_of_creds).subnet['id']
406
407 # Create suitable share-network
408 if share_network_id is None:
409 sn_desc = "This share-network was created by tempest"
410 sn = sc.create_share_network(name=sn_name,
411 description=sn_desc,
412 neutron_net_id=net_id,
413 neutron_subnet_id=subnet_id)
414 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200415
416 return share_network_id
417
418 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300419 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200420 snapshot_id=None, description=None, metadata=None,
421 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400422 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400423 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300424 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200425 description = description or "Tempest's share"
426 share_network_id = share_network_id or client.share_network_id or None
427 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300428 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400429 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200430 'share_protocol': share_protocol,
431 'size': size,
432 'name': name,
433 'snapshot_id': snapshot_id,
434 'description': description,
435 'metadata': metadata,
436 'share_network_id': share_network_id,
437 'share_type_id': share_type_id,
438 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400439 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400440 if share_group_id:
441 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400442
Marc Koderer0abc93b2015-07-15 09:18:35 +0200443 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400444 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400445 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200446 cleanup_list = (cls.class_resources if cleanup_in_class else
447 cls.method_resources)
448 cleanup_list.insert(0, resource)
449 return share
450
451 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300452 def migrate_share(
453 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200454 force_host_assisted_migration=False, writable=False,
455 nondisruptive=False, preserve_metadata=False,
456 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300457 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400458 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300459 client.migrate_share(
460 share_id, dest_host,
461 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200462 writable=writable, preserve_metadata=preserve_metadata,
463 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300464 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300465 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200466 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300467 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200468 return share
469
470 @classmethod
471 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
472 client = client or cls.shares_v2_client
473 client.migration_complete(share_id, **kwargs)
474 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300475 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300476 return share
477
478 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300479 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
480 client = client or cls.shares_v2_client
481 client.migration_cancel(share_id, **kwargs)
482 share = client.wait_for_migration_status(
483 share_id, dest_host, 'migration_cancelled', **kwargs)
484 return share
485
486 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200487 def create_share(cls, *args, **kwargs):
488 """Create one share and wait for available state. Retry if allowed."""
489 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
490 return result[0]
491
492 @classmethod
493 def create_shares(cls, share_data_list):
494 """Creates several shares in parallel with retries.
495
496 Use this method when you want to create more than one share at same
497 time. Especially if config option 'share.share_creation_retry_number'
498 has value more than zero (0).
499 All shares will be expected to have 'available' status with or without
500 recreation else error will be raised.
501
502 :param share_data_list: list -- list of dictionaries with 'args' and
503 'kwargs' for '_create_share' method of this base class.
504 example of data:
505 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
506 :returns: list -- list of shares created using provided data.
507 """
508
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300509 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200510 if not isinstance(d, dict):
511 raise exceptions.TempestException(
512 "Expected 'dict', got '%s'" % type(d))
513 if "args" not in d:
514 d["args"] = []
515 if "kwargs" not in d:
516 d["kwargs"] = {}
517 if len(d) > 2:
518 raise exceptions.TempestException(
519 "Expected only 'args' and 'kwargs' keys. "
520 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300521
522 data = []
523 for d in share_data_list:
524 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400525 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300526 local_d = {
527 "args": d["args"],
528 "kwargs": copy.deepcopy(d["kwargs"]),
529 }
530 local_d["kwargs"]["client"] = client
531 local_d["share"] = cls._create_share(
532 *local_d["args"], **local_d["kwargs"])
533 local_d["cnt"] = 0
534 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400535 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300536 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200537
538 while not all(d["available"] for d in data):
539 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400540 if not d["wait_for_status"]:
541 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200542 if d["available"]:
543 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300544 client = d["kwargs"]["client"]
545 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200546 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300547 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200548 d["available"] = True
549 except (share_exceptions.ShareBuildErrorException,
550 exceptions.TimeoutException) as e:
551 if CONF.share.share_creation_retry_number > d["cnt"]:
552 d["cnt"] += 1
553 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300554 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200555 LOG.error(msg)
556 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300557 cg_id = d["kwargs"].get("consistency_group_id")
558 if cg_id:
559 # NOTE(vponomaryov): delete errored share
560 # immediately in case share is part of CG.
561 client.delete_share(
562 share_id,
563 params={"consistency_group_id": cg_id})
564 client.wait_for_resource_deletion(
565 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200566 d["share"] = cls._create_share(
567 *d["args"], **d["kwargs"])
568 else:
gecong197358663802016-08-25 11:08:45 +0800569 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200570
571 return [d["share"] for d in data]
572
573 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400574 def create_share_group(cls, client=None, cleanup_in_class=True,
575 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400576 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400577 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400578 kwargs['share_network_id'] = (share_network_id or
579 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400580 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400581 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400582 "type": "share_group",
583 "id": share_group["id"],
584 "client": client,
585 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400586 if cleanup_in_class:
587 cls.class_resources.insert(0, resource)
588 else:
589 cls.method_resources.insert(0, resource)
590
Andrew Kerrb8436922016-06-01 15:32:43 -0400591 if kwargs.get('source_share_group_snapshot_id'):
592 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400593 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400594 params={'share_group_id': share_group['id']},
595 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400596
Andrew Kerrb8436922016-06-01 15:32:43 -0400597 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400598 resource = {"type": "share",
599 "id": share["id"],
600 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400601 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400602 if cleanup_in_class:
603 cls.class_resources.insert(0, resource)
604 else:
605 cls.method_resources.insert(0, resource)
606
Andrew Kerrb8436922016-06-01 15:32:43 -0400607 client.wait_for_share_group_status(share_group['id'], 'available')
608 return share_group
609
610 @classmethod
611 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
612 group_specs=None, client=None,
613 cleanup_in_class=True, **kwargs):
614 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300615 if (group_specs is None and
616 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300617 group_specs = {
618 'consistent_snapshot_support': (
619 CONF.share.capability_sg_consistent_snapshot_support),
620 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400621 share_group_type = client.create_share_group_type(
622 name=name,
623 share_types=share_types,
624 is_public=is_public,
625 group_specs=group_specs,
626 **kwargs)
627 resource = {
628 "type": "share_group_type",
629 "id": share_group_type["id"],
630 "client": client,
631 }
632 if cleanup_in_class:
633 cls.class_resources.insert(0, resource)
634 else:
635 cls.method_resources.insert(0, resource)
636 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400637
638 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200639 def create_snapshot_wait_for_active(cls, share_id, name=None,
640 description=None, force=False,
641 client=None, cleanup_in_class=True):
642 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400643 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200644 if description is None:
645 description = "Tempest's snapshot"
646 snapshot = client.create_snapshot(share_id, name, description, force)
647 resource = {
648 "type": "snapshot",
649 "id": snapshot["id"],
650 "client": client,
651 }
652 if cleanup_in_class:
653 cls.class_resources.insert(0, resource)
654 else:
655 cls.method_resources.insert(0, resource)
656 client.wait_for_snapshot_status(snapshot["id"], "available")
657 return snapshot
658
659 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400660 def create_share_group_snapshot_wait_for_active(
661 cls, share_group_id, name=None, description=None, client=None,
662 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400663 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400664 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400665 description = "Tempest's share group snapshot"
666 sg_snapshot = client.create_share_group_snapshot(
667 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400668 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400669 "type": "share_group_snapshot",
670 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400671 "client": client,
672 }
673 if cleanup_in_class:
674 cls.class_resources.insert(0, resource)
675 else:
676 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400677 client.wait_for_share_group_snapshot_status(
678 sg_snapshot["id"], "available")
679 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400680
681 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400682 def get_availability_zones(cls, client=None):
683 """List the availability zones for "manila-share" services
684
685 that are currently in "up" state.
686 """
687 client = client or cls.shares_v2_client
688 cls.services = client.list_services()
689 zones = [service['zone'] for service in cls.services if
690 service['binary'] == "manila-share" and
691 service['state'] == 'up']
692 return zones
693
Yogesh1f931ff2015-09-29 23:41:02 -0400694 def get_pools_for_replication_domain(self):
695 # Get the list of pools for the replication domain
696 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500697 instance_host = self.admin_client.get_share(
698 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400699 host_pool = [p for p in pools if p['name'] == instance_host][0]
700 rep_domain = host_pool['capabilities']['replication_domain']
701 pools_in_rep_domain = [p for p in pools if p['capabilities'][
702 'replication_domain'] == rep_domain]
703 return rep_domain, pools_in_rep_domain
704
Yogeshbdb88102015-09-29 23:41:02 -0400705 @classmethod
706 def create_share_replica(cls, share_id, availability_zone, client=None,
707 cleanup_in_class=False, cleanup=True):
708 client = client or cls.shares_v2_client
709 replica = client.create_share_replica(share_id, availability_zone)
710 resource = {
711 "type": "share_replica",
712 "id": replica["id"],
713 "client": client,
714 "share_id": share_id,
715 }
716 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
717 if cleanup:
718 if cleanup_in_class:
719 cls.class_resources.insert(0, resource)
720 else:
721 cls.method_resources.insert(0, resource)
722 client.wait_for_share_replica_status(
723 replica["id"], constants.STATUS_AVAILABLE)
724 return replica
725
726 @classmethod
727 def delete_share_replica(cls, replica_id, client=None):
728 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400729 try:
730 client.delete_share_replica(replica_id)
731 client.wait_for_resource_deletion(replica_id=replica_id)
732 except exceptions.NotFound:
733 pass
Yogeshbdb88102015-09-29 23:41:02 -0400734
735 @classmethod
736 def promote_share_replica(cls, replica_id, client=None):
737 client = client or cls.shares_v2_client
738 replica = client.promote_share_replica(replica_id)
739 client.wait_for_share_replica_status(
740 replica["id"],
741 constants.REPLICATION_STATE_ACTIVE,
742 status_attr="replica_state")
743 return replica
744
yogeshdb32f462016-09-28 15:09:50 -0400745 def _get_access_rule_data_from_config(self):
746 """Get the first available access type/to combination from config.
747
748 This method opportunistically picks the first configured protocol
749 to create the share. Do not use this method in tests where you need
750 to test depth and breadth in the access types and access recipients.
751 """
752 protocol = self.shares_v2_client.share_protocol
753
754 if protocol in CONF.share.enable_ip_rules_for_protocols:
755 access_type = "ip"
756 access_to = utils.rand_ip()
757 elif protocol in CONF.share.enable_user_rules_for_protocols:
758 access_type = "user"
759 access_to = CONF.share.username_for_user_rules
760 elif protocol in CONF.share.enable_cert_rules_for_protocols:
761 access_type = "cert"
762 access_to = "client3.com"
763 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
764 access_type = "cephx"
765 access_to = "eve"
766 else:
767 message = "Unrecognized protocol and access rules configuration."
768 raise self.skipException(message)
769
770 return access_type, access_to
771
Yogeshbdb88102015-09-29 23:41:02 -0400772 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200773 def create_share_network(cls, client=None,
774 cleanup_in_class=False, **kwargs):
775 if client is None:
776 client = cls.shares_client
777 share_network = client.create_share_network(**kwargs)
778 resource = {
779 "type": "share_network",
780 "id": share_network["id"],
781 "client": client,
782 }
783 if cleanup_in_class:
784 cls.class_resources.insert(0, resource)
785 else:
786 cls.method_resources.insert(0, resource)
787 return share_network
788
789 @classmethod
790 def create_security_service(cls, ss_type="ldap", client=None,
791 cleanup_in_class=False, **kwargs):
792 if client is None:
793 client = cls.shares_client
794 security_service = client.create_security_service(ss_type, **kwargs)
795 resource = {
796 "type": "security_service",
797 "id": security_service["id"],
798 "client": client,
799 }
800 if cleanup_in_class:
801 cls.class_resources.insert(0, resource)
802 else:
803 cls.method_resources.insert(0, resource)
804 return security_service
805
806 @classmethod
807 def create_share_type(cls, name, is_public=True, client=None,
808 cleanup_in_class=True, **kwargs):
809 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200810 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200811 share_type = client.create_share_type(name, is_public, **kwargs)
812 resource = {
813 "type": "share_type",
814 "id": share_type["share_type"]["id"],
815 "client": client,
816 }
817 if cleanup_in_class:
818 cls.class_resources.insert(0, resource)
819 else:
820 cls.method_resources.insert(0, resource)
821 return share_type
822
823 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400824 def add_extra_specs_to_dict(extra_specs=None):
825 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300826 dhss = six.text_type(CONF.share.multitenancy_enabled)
827 snapshot_support = six.text_type(
828 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400829 create_from_snapshot_support = six.text_type(
830 CONF.share.capability_create_share_from_snapshot_support)
831
832 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300833 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200834 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400835
836 optional = {
837 "snapshot_support": snapshot_support,
838 "create_share_from_snapshot_support": create_from_snapshot_support,
839 }
840 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
841 # required extra-spec
842 extra_specs_dict.update(optional)
843
Marc Koderer0abc93b2015-07-15 09:18:35 +0200844 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400845 extra_specs_dict.update(extra_specs)
846
847 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200848
849 @classmethod
850 def clear_isolated_creds(cls, creds=None):
851 if creds is None:
852 creds = cls.method_isolated_creds
853 for ic in creds:
854 if "deleted" not in ic.keys():
855 ic["deleted"] = False
856 if not ic["deleted"]:
857 with handle_cleanup_exceptions():
858 ic["method"]()
859 ic["deleted"] = True
860
861 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400862 def clear_share_replicas(cls, share_id, client=None):
863 client = client or cls.shares_v2_client
864 share_replicas = client.list_share_replicas(
865 share_id=share_id)
866
867 for replica in share_replicas:
868 try:
869 cls.delete_share_replica(replica['id'])
870 except exceptions.BadRequest:
871 # Ignore the exception due to deletion of last active replica
872 pass
873
874 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200875 def clear_resources(cls, resources=None):
876 """Deletes resources, that were created in test suites.
877
878 This method tries to remove resources from resource list,
879 if it is not found, assumed it was deleted in test itself.
880 It is expected, that all resources were added as LIFO
881 due to restriction of deletion resources, that is in the chain.
882
883 :param resources: dict with keys 'type','id','client' and 'deleted'
884 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200885 if resources is None:
886 resources = cls.method_resources
887 for res in resources:
888 if "deleted" not in res.keys():
889 res["deleted"] = False
890 if "client" not in res.keys():
891 res["client"] = cls.shares_client
892 if not(res["deleted"]):
893 res_id = res['id']
894 client = res["client"]
895 with handle_cleanup_exceptions():
896 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400897 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400898 share_group_id = res.get('share_group_id')
899 if share_group_id:
900 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400901 client.delete_share(res_id, params=params)
902 else:
903 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200904 client.wait_for_resource_deletion(share_id=res_id)
905 elif res["type"] is "snapshot":
906 client.delete_snapshot(res_id)
907 client.wait_for_resource_deletion(snapshot_id=res_id)
908 elif res["type"] is "share_network":
909 client.delete_share_network(res_id)
910 client.wait_for_resource_deletion(sn_id=res_id)
911 elif res["type"] is "security_service":
912 client.delete_security_service(res_id)
913 client.wait_for_resource_deletion(ss_id=res_id)
914 elif res["type"] is "share_type":
915 client.delete_share_type(res_id)
916 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400917 elif res["type"] is "share_group":
918 client.delete_share_group(res_id)
919 client.wait_for_resource_deletion(
920 share_group_id=res_id)
921 elif res["type"] is "share_group_type":
922 client.delete_share_group_type(res_id)
923 client.wait_for_resource_deletion(
924 share_group_type_id=res_id)
925 elif res["type"] is "share_group_snapshot":
926 client.delete_share_group_snapshot(res_id)
927 client.wait_for_resource_deletion(
928 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400929 elif res["type"] is "share_replica":
930 client.delete_share_replica(res_id)
931 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200932 else:
huayue97bacbf2016-01-04 09:57:39 +0800933 LOG.warning("Provided unsupported resource type for "
934 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200935 res["deleted"] = True
936
937 @classmethod
938 def generate_share_network_data(self):
939 data = {
940 "name": data_utils.rand_name("sn-name"),
941 "description": data_utils.rand_name("sn-desc"),
942 "neutron_net_id": data_utils.rand_name("net-id"),
943 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
944 }
945 return data
946
947 @classmethod
948 def generate_security_service_data(self):
949 data = {
950 "name": data_utils.rand_name("ss-name"),
951 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200952 "dns_ip": utils.rand_ip(),
953 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200954 "domain": data_utils.rand_name("ss-domain"),
955 "user": data_utils.rand_name("ss-user"),
956 "password": data_utils.rand_name("ss-password"),
957 }
958 return data
959
960 # Useful assertions
961 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
962 """Assert two dicts are equivalent.
963
964 This is a 'deep' match in the sense that it handles nested
965 dictionaries appropriately.
966
967 NOTE:
968
969 If you don't care (or don't know) a given value, you can specify
970 the string DONTCARE as the value. This will cause that dict-item
971 to be skipped.
972
973 """
974 def raise_assertion(msg):
975 d1str = str(d1)
976 d2str = str(d2)
977 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
978 'd2: %(d2str)s' %
979 {"msg": msg, "d1str": d1str, "d2str": d2str})
980 raise AssertionError(base_msg)
981
982 d1keys = set(d1.keys())
983 d2keys = set(d2.keys())
984 if d1keys != d2keys:
985 d1only = d1keys - d2keys
986 d2only = d2keys - d1keys
987 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
988 'Keys in d2 and not d1: %(d2only)s' %
989 {"d1only": d1only, "d2only": d2only})
990
991 for key in d1keys:
992 d1value = d1[key]
993 d2value = d2[key]
994 try:
995 error = abs(float(d1value) - float(d2value))
996 within_tolerance = error <= tolerance
997 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +0900998 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +0200999 # ValueError if arg is a str, TypeError if it's something else
1000 # (like None)
1001 within_tolerance = False
1002
1003 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1004 self.assertDictMatch(d1value, d2value)
1005 elif 'DONTCARE' in (d1value, d2value):
1006 continue
1007 elif approx_equal and within_tolerance:
1008 continue
1009 elif d1value != d2value:
1010 raise_assertion("d1['%(key)s']=%(d1value)s != "
1011 "d2['%(key)s']=%(d2value)s" %
1012 {
1013 "key": key,
1014 "d1value": d1value,
1015 "d2value": d2value
1016 })
1017
Alex Meadeba8a1602016-05-06 09:33:09 -04001018 def create_user_message(self):
1019 """Trigger a 'no valid host' situation to generate a message."""
1020 extra_specs = {
1021 'vendor_name': 'foobar',
1022 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1023 }
1024 share_type_name = data_utils.rand_name("share-type")
1025
1026 bogus_type = self.create_share_type(
1027 name=share_type_name,
1028 extra_specs=extra_specs)['share_type']
1029
1030 params = {'share_type_id': bogus_type['id'],
1031 'share_network_id': self.shares_v2_client.share_network_id}
1032 share = self.shares_v2_client.create_share(**params)
1033 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1034 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1035 return self.shares_v2_client.wait_for_message(share['id'])
1036
Marc Koderer0abc93b2015-07-15 09:18:35 +02001037
1038class BaseSharesAltTest(BaseSharesTest):
1039 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001040 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001041
1042
1043class BaseSharesAdminTest(BaseSharesTest):
1044 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001045 credentials = ('admin', )
1046
1047
1048class BaseSharesMixedTest(BaseSharesTest):
1049 """Base test case class for all Shares API tests with all user roles."""
1050 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001051
1052 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001053 def setup_clients(cls):
1054 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001055 # Initialise share clients
1056 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1057 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1058 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1059 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1060 # Initialise network clients
1061 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1062 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001063
1064 if CONF.share.multitenancy_enabled:
1065 admin_share_network_id = cls.provide_share_network(
1066 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1067 cls.admin_shares_client.share_network_id = admin_share_network_id
1068 cls.admin_shares_v2_client.share_network_id = (
1069 admin_share_network_id)
1070
1071 alt_share_network_id = cls.provide_share_network(
1072 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1073 cls.alt_shares_client.share_network_id = alt_share_network_id
1074 cls.alt_shares_v2_client.share_network_id = alt_share_network_id