blob: 65f1b305463b319baa3e5bb21f291cd842531213 [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
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +030024from tempest import clients
Sam Wanc7b7f1f2015-11-25 00:22:28 -050025from tempest.common import credentials_factory as common_creds
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010026
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020027from tempest import config
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010028from tempest.lib.common import dynamic_creds
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050029from tempest.lib.common.utils import data_utils
30from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020031from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020032
Yogeshbdb88102015-09-29 23:41:02 -040033from manila_tempest_tests.common import constants
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +030034from manila_tempest_tests.services.share.json import shares_client
35from manila_tempest_tests.services.share.v2.json import (
36 shares_client as shares_v2_client)
Marc Koderer0abc93b2015-07-15 09:18:35 +020037from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020038from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020039
40CONF = config.CONF
41LOG = log.getLogger(__name__)
42
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030043# Test tags related to test direction
44TAG_POSITIVE = "positive"
45TAG_NEGATIVE = "negative"
46
47# Test tags related to service involvement
48TAG_API = "api"
49TAG_BACKEND = "backend"
50TAG_API_WITH_BACKEND = "api_with_backend"
51
52TAGS_MAPPER = {
53 "p": TAG_POSITIVE,
54 "n": TAG_NEGATIVE,
55 "a": TAG_API,
56 "b": TAG_BACKEND,
57 "ab": TAG_API_WITH_BACKEND,
58}
59TAGS_PATTERN = re.compile(
60 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
61 TAGS_MAPPER)
62
63
64def verify_test_has_appropriate_tags(self):
65 if not TAGS_PATTERN.match(self.id()):
66 msg = (
67 "Required attributes either not set or set improperly. "
68 "Two test attributes are expected:\n"
69 " - one of '%(p)s' or '%(n)s' and \n"
70 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
71 ) % TAGS_MAPPER
72 raise self.failureException(msg)
73
Marc Koderer0abc93b2015-07-15 09:18:35 +020074
75class handle_cleanup_exceptions(object):
76 """Handle exceptions raised with cleanup operations.
77
78 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
79 are raised.
80 Suppress all other exceptions only in case config opt
81 'suppress_errors_in_cleanup' in config group 'share' is True.
82 """
83
84 def __enter__(self):
85 return self
86
87 def __exit__(self, exc_type, exc_value, exc_traceback):
88 if not (isinstance(exc_value,
89 (exceptions.NotFound, exceptions.Forbidden)) or
90 CONF.share.suppress_errors_in_cleanup):
91 return False # Do not suppress error if any
92 if exc_traceback:
93 LOG.error("Suppressed cleanup error in Manila: "
94 "\n%s" % traceback.format_exc())
95 return True # Suppress error if any
96
97
98def network_synchronized(f):
99
100 def wrapped_func(self, *args, **kwargs):
101 with_isolated_creds = True if len(args) > 2 else False
102 no_lock_required = kwargs.get(
103 "isolated_creds_client", with_isolated_creds)
104 if no_lock_required:
105 # Usage of not reusable network. No need in lock.
106 return f(self, *args, **kwargs)
107
108 # Use lock assuming reusage of common network.
109 @lockutils.synchronized("manila_network_lock", external=True)
110 def source_func(self, *args, **kwargs):
111 return f(self, *args, **kwargs)
112
113 return source_func(self, *args, **kwargs)
114
115 return wrapped_func
116
117
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200118skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500119skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200120
121
Marc Koderer0abc93b2015-07-15 09:18:35 +0200122class BaseSharesTest(test.BaseTestCase):
123 """Base test case class for all Manila API tests."""
124
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300125 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200126 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200127 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200128
129 # Will be cleaned up in resource_cleanup
130 class_resources = []
131
132 # Will be cleaned up in tearDown method
133 method_resources = []
134
135 # Will be cleaned up in resource_cleanup
136 class_isolated_creds = []
137
138 # Will be cleaned up in tearDown method
139 method_isolated_creds = []
140
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200141 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200142 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200143 raise self.skipException(
144 "Microversion '%s' is not supported." % microversion)
145
Xing Yang69b00b52015-11-22 16:10:44 -0500146 def skip_if_microversion_lt(self, microversion):
147 if utils.is_microversion_lt(CONF.share.max_api_microversion,
148 microversion):
149 raise self.skipException(
150 "Microversion must be greater than or equal to '%s'." %
151 microversion)
152
Marc Koderer0abc93b2015-07-15 09:18:35 +0200153 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500154 def _get_dynamic_creds(cls, name, network_resources=None):
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100155 identity_version = CONF.identity.auth_version
156 if identity_version == 'v3':
157 identity_uri = CONF.identity.uri_v3
158 identity_admin_endpoint_type = CONF.identity.v3_endpoint_type
159 elif identity_version == 'v2':
160 identity_uri = CONF.identity.uri
161 identity_admin_endpoint_type = CONF.identity.v2_admin_endpoint_type
162
Tom Barron4b8834a2017-02-02 11:02:20 -0500163 return dynamic_creds.DynamicCredentialProvider(
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100164 identity_version=identity_version,
Tom Barron4b8834a2017-02-02 11:02:20 -0500165 name=name,
166 network_resources=network_resources,
167 credentials_domain=CONF.auth.default_credentials_domain_name,
168 admin_role=CONF.identity.admin_role,
169 admin_creds=common_creds.get_configured_admin_credentials(),
170 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
171 identity_admin_role=CONF.identity.admin_role,
172 extra_roles=None,
173 neutron_available=CONF.service_available.neutron,
174 create_networks=(
175 CONF.share.create_networks_when_multitenancy_enabled),
176 project_network_cidr=CONF.network.project_network_cidr,
177 project_network_mask_bits=CONF.network.project_network_mask_bits,
178 public_network_id=CONF.network.public_network_id,
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100179 resource_prefix=CONF.resources_prefix,
180 identity_admin_endpoint_type=identity_admin_endpoint_type,
181 identity_uri=identity_uri)
Tom Barron4b8834a2017-02-02 11:02:20 -0500182
183 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200184 def get_client_with_isolated_creds(cls,
185 name=None,
186 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400187 cleanup_in_class=False,
188 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200189 """Creates isolated creds.
190
191 :param name: name, will be used for naming ic and related stuff
192 :param type_of_creds: admin, alt or primary
193 :param cleanup_in_class: defines place where to delete
194 :returns: SharesClient -- shares client with isolated creds.
195 :returns: To client added dict attr 'creds' with
196 :returns: key elements 'tenant' and 'user'.
197 """
198 if name is None:
199 # Get name of test method
200 name = inspect.stack()[1][3]
201 if len(name) > 32:
202 name = name[0:32]
203
204 # Choose type of isolated creds
Tom Barron4b8834a2017-02-02 11:02:20 -0500205 ic = cls._get_dynamic_creds(name)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200206 if "admin" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200207 creds = ic.get_admin_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200208 elif "alt" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200209 creds = ic.get_alt_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200210 else:
Marc Koderer5880b362016-07-06 10:59:07 +0200211 creds = ic.get_credentials(type_of_creds).credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200212 ic.type_of_creds = type_of_creds
213
214 # create client with isolated creds
215 os = clients.Manager(credentials=creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400216 if client_version == '1':
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300217 client = shares_client.SharesClient(os.auth_provider)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400218 elif client_version == '2':
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300219 client = shares_v2_client.SharesV2Client(os.auth_provider)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200220
221 # Set place where will be deleted isolated creds
222 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200223 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200224 "deleted": False,
225 }
226 if cleanup_in_class:
227 cls.class_isolated_creds.insert(0, ic_res)
228 else:
229 cls.method_isolated_creds.insert(0, ic_res)
230
231 # Provide share network
232 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300233 if (not CONF.service_available.neutron and
234 CONF.share.create_networks_when_multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200235 raise cls.skipException("Neutron support is required")
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200236 nc = os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200237 share_network_id = cls.provide_share_network(client, nc, ic)
238 client.share_network_id = share_network_id
239 resource = {
240 "type": "share_network",
241 "id": client.share_network_id,
242 "client": client,
243 }
244 if cleanup_in_class:
245 cls.class_resources.insert(0, resource)
246 else:
247 cls.method_resources.insert(0, resource)
248 return client
249
250 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000251 def skip_checks(cls):
252 super(BaseSharesTest, cls).skip_checks()
253 if not CONF.service_available.manila:
254 raise cls.skipException("Manila support is required")
255
256 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200257 def verify_nonempty(cls, *args):
258 if not all(args):
259 msg = "Missing API credentials in configuration."
260 raise cls.skipException(msg)
261
262 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300263 def setup_clients(cls):
264 super(BaseSharesTest, cls).setup_clients()
265 os = getattr(cls, 'os_%s' % cls.credentials[0])
266 os.shares_client = shares_client.SharesClient(os.auth_provider)
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300267
268 if CONF.identity.auth_version == 'v3':
269 project_id = os.auth_provider.auth_data[1]['project']['id']
270 else:
271 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
272 cls.tenant_id = project_id
273 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
274
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300275 cls.shares_client = os.shares_client
276 os.shares_v2_client = shares_v2_client.SharesV2Client(
277 os.auth_provider)
278 cls.shares_v2_client = os.shares_v2_client
279 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300280 if (not CONF.service_available.neutron and
281 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300282 raise cls.skipException("Neutron support is required")
283 share_network_id = cls.provide_share_network(
284 cls.shares_v2_client, os.networks_client)
285 cls.shares_client.share_network_id = share_network_id
286 cls.shares_v2_client.share_network_id = share_network_id
287
288 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200289 def resource_setup(cls):
290 if not (any(p in CONF.share.enable_protocols
291 for p in cls.protocols) and
292 CONF.service_available.manila):
293 skip_msg = "Manila is disabled"
294 raise cls.skipException(skip_msg)
295 super(BaseSharesTest, cls).resource_setup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200296
297 def setUp(self):
298 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200299 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200300 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300301 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200302
303 @classmethod
304 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200305 cls.clear_resources(cls.class_resources)
306 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400307 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200308
309 @classmethod
310 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200311 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300312 isolated_creds_client=None,
313 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200314 """Used for finding/creating share network for multitenant driver.
315
316 This method creates/gets entity share-network for one tenant. This
317 share-network will be used for creation of service vm.
318
319 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200320 :param networks_client: network client from same tenant as shares
321 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200322 If provided, then its networking will be used if needed.
323 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300324 :param ignore_multitenancy_config: provide a share network regardless
325 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200326 :returns: str -- share network id for shares_client tenant
327 :returns: None -- if single-tenant driver used
328 """
329
330 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300331 search_word = "reusable"
332 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200333
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300334 if (not ignore_multitenancy_config and
335 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200336 # Assumed usage of a single-tenant driver
337 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200338 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300339 if sc.share_network_id:
340 # Share-network already exists, use it
341 share_network_id = sc.share_network_id
342 elif not CONF.share.create_networks_when_multitenancy_enabled:
343 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200344
345 # Try get suitable share-network
346 share_networks = sc.list_share_networks_with_detail()
347 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300348 if (sn["neutron_net_id"] is None and
349 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200350 sn["name"] and search_word in sn["name"]):
351 share_network_id = sn["id"]
352 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200353
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300354 # Create new share-network if one was not found
355 if share_network_id is None:
356 sn_desc = "This share-network was created by tempest"
357 sn = sc.create_share_network(name=sn_name,
358 description=sn_desc)
359 share_network_id = sn["id"]
360 else:
361 net_id = subnet_id = share_network_id = None
362
363 if not isolated_creds_client:
364 # Search for networks, created in previous runs
365 service_net_name = "share-service"
366 networks = networks_client.list_networks()
367 if "networks" in networks.keys():
368 networks = networks["networks"]
369 for network in networks:
370 if (service_net_name in network["name"] and
371 sc.tenant_id == network['tenant_id']):
372 net_id = network["id"]
373 if len(network["subnets"]) > 0:
374 subnet_id = network["subnets"][0]
375 break
376
377 # Create suitable network
378 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500379 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300380 net_data = ic._create_network_resources(sc.tenant_id)
381 network, subnet, router = net_data
382 net_id = network["id"]
383 subnet_id = subnet["id"]
384
385 # Try get suitable share-network
386 share_networks = sc.list_share_networks_with_detail()
387 for sn in share_networks:
388 if (net_id == sn["neutron_net_id"] and
389 subnet_id == sn["neutron_subnet_id"] and
390 sn["name"] and search_word in sn["name"]):
391 share_network_id = sn["id"]
392 break
393 else:
394 sn_name = "autogenerated_by_tempest_for_isolated_creds"
395 # Use precreated network and subnet from isolated creds
396 net_id = isolated_creds_client.get_credentials(
397 isolated_creds_client.type_of_creds).network['id']
398 subnet_id = isolated_creds_client.get_credentials(
399 isolated_creds_client.type_of_creds).subnet['id']
400
401 # Create suitable share-network
402 if share_network_id is None:
403 sn_desc = "This share-network was created by tempest"
404 sn = sc.create_share_network(name=sn_name,
405 description=sn_desc,
406 neutron_net_id=net_id,
407 neutron_subnet_id=subnet_id)
408 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200409
410 return share_network_id
411
412 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300413 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200414 snapshot_id=None, description=None, metadata=None,
415 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400416 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400417 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300418 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200419 description = description or "Tempest's share"
420 share_network_id = share_network_id or client.share_network_id or None
421 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300422 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400423 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200424 'share_protocol': share_protocol,
425 'size': size,
426 'name': name,
427 'snapshot_id': snapshot_id,
428 'description': description,
429 'metadata': metadata,
430 'share_network_id': share_network_id,
431 'share_type_id': share_type_id,
432 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400433 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400434 if share_group_id:
435 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400436
Marc Koderer0abc93b2015-07-15 09:18:35 +0200437 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400438 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400439 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200440 cleanup_list = (cls.class_resources if cleanup_in_class else
441 cls.method_resources)
442 cleanup_list.insert(0, resource)
443 return share
444
445 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300446 def migrate_share(
447 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200448 force_host_assisted_migration=False, writable=False,
449 nondisruptive=False, preserve_metadata=False,
450 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300451 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400452 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300453 client.migrate_share(
454 share_id, dest_host,
455 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200456 writable=writable, preserve_metadata=preserve_metadata,
457 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300458 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300459 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200460 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300461 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200462 return share
463
464 @classmethod
465 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
466 client = client or cls.shares_v2_client
467 client.migration_complete(share_id, **kwargs)
468 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300469 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300470 return share
471
472 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300473 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
474 client = client or cls.shares_v2_client
475 client.migration_cancel(share_id, **kwargs)
476 share = client.wait_for_migration_status(
477 share_id, dest_host, 'migration_cancelled', **kwargs)
478 return share
479
480 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200481 def create_share(cls, *args, **kwargs):
482 """Create one share and wait for available state. Retry if allowed."""
483 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
484 return result[0]
485
486 @classmethod
487 def create_shares(cls, share_data_list):
488 """Creates several shares in parallel with retries.
489
490 Use this method when you want to create more than one share at same
491 time. Especially if config option 'share.share_creation_retry_number'
492 has value more than zero (0).
493 All shares will be expected to have 'available' status with or without
494 recreation else error will be raised.
495
496 :param share_data_list: list -- list of dictionaries with 'args' and
497 'kwargs' for '_create_share' method of this base class.
498 example of data:
499 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
500 :returns: list -- list of shares created using provided data.
501 """
502
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300503 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200504 if not isinstance(d, dict):
505 raise exceptions.TempestException(
506 "Expected 'dict', got '%s'" % type(d))
507 if "args" not in d:
508 d["args"] = []
509 if "kwargs" not in d:
510 d["kwargs"] = {}
511 if len(d) > 2:
512 raise exceptions.TempestException(
513 "Expected only 'args' and 'kwargs' keys. "
514 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300515
516 data = []
517 for d in share_data_list:
518 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400519 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300520 local_d = {
521 "args": d["args"],
522 "kwargs": copy.deepcopy(d["kwargs"]),
523 }
524 local_d["kwargs"]["client"] = client
525 local_d["share"] = cls._create_share(
526 *local_d["args"], **local_d["kwargs"])
527 local_d["cnt"] = 0
528 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400529 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300530 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200531
532 while not all(d["available"] for d in data):
533 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400534 if not d["wait_for_status"]:
535 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200536 if d["available"]:
537 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300538 client = d["kwargs"]["client"]
539 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200540 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300541 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200542 d["available"] = True
543 except (share_exceptions.ShareBuildErrorException,
544 exceptions.TimeoutException) as e:
545 if CONF.share.share_creation_retry_number > d["cnt"]:
546 d["cnt"] += 1
547 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300548 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200549 LOG.error(msg)
550 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300551 cg_id = d["kwargs"].get("consistency_group_id")
552 if cg_id:
553 # NOTE(vponomaryov): delete errored share
554 # immediately in case share is part of CG.
555 client.delete_share(
556 share_id,
557 params={"consistency_group_id": cg_id})
558 client.wait_for_resource_deletion(
559 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200560 d["share"] = cls._create_share(
561 *d["args"], **d["kwargs"])
562 else:
gecong197358663802016-08-25 11:08:45 +0800563 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200564
565 return [d["share"] for d in data]
566
567 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400568 def create_share_group(cls, client=None, cleanup_in_class=True,
569 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400570 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400571 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400572 kwargs['share_network_id'] = (share_network_id or
573 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400574 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400575 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400576 "type": "share_group",
577 "id": share_group["id"],
578 "client": client,
579 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400580 if cleanup_in_class:
581 cls.class_resources.insert(0, resource)
582 else:
583 cls.method_resources.insert(0, resource)
584
Andrew Kerrb8436922016-06-01 15:32:43 -0400585 if kwargs.get('source_share_group_snapshot_id'):
586 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400587 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400588 params={'share_group_id': share_group['id']},
589 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400590
Andrew Kerrb8436922016-06-01 15:32:43 -0400591 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400592 resource = {"type": "share",
593 "id": share["id"],
594 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400595 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400596 if cleanup_in_class:
597 cls.class_resources.insert(0, resource)
598 else:
599 cls.method_resources.insert(0, resource)
600
Andrew Kerrb8436922016-06-01 15:32:43 -0400601 client.wait_for_share_group_status(share_group['id'], 'available')
602 return share_group
603
604 @classmethod
605 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
606 group_specs=None, client=None,
607 cleanup_in_class=True, **kwargs):
608 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300609 if (group_specs is None and
610 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300611 group_specs = {
612 'consistent_snapshot_support': (
613 CONF.share.capability_sg_consistent_snapshot_support),
614 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400615 share_group_type = client.create_share_group_type(
616 name=name,
617 share_types=share_types,
618 is_public=is_public,
619 group_specs=group_specs,
620 **kwargs)
621 resource = {
622 "type": "share_group_type",
623 "id": share_group_type["id"],
624 "client": client,
625 }
626 if cleanup_in_class:
627 cls.class_resources.insert(0, resource)
628 else:
629 cls.method_resources.insert(0, resource)
630 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400631
632 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200633 def create_snapshot_wait_for_active(cls, share_id, name=None,
634 description=None, force=False,
635 client=None, cleanup_in_class=True):
636 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400637 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200638 if description is None:
639 description = "Tempest's snapshot"
640 snapshot = client.create_snapshot(share_id, name, description, force)
641 resource = {
642 "type": "snapshot",
643 "id": snapshot["id"],
644 "client": client,
645 }
646 if cleanup_in_class:
647 cls.class_resources.insert(0, resource)
648 else:
649 cls.method_resources.insert(0, resource)
650 client.wait_for_snapshot_status(snapshot["id"], "available")
651 return snapshot
652
653 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400654 def create_share_group_snapshot_wait_for_active(
655 cls, share_group_id, name=None, description=None, client=None,
656 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400657 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400658 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400659 description = "Tempest's share group snapshot"
660 sg_snapshot = client.create_share_group_snapshot(
661 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400662 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400663 "type": "share_group_snapshot",
664 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400665 "client": client,
666 }
667 if cleanup_in_class:
668 cls.class_resources.insert(0, resource)
669 else:
670 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400671 client.wait_for_share_group_snapshot_status(
672 sg_snapshot["id"], "available")
673 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400674
675 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400676 def get_availability_zones(cls, client=None):
677 """List the availability zones for "manila-share" services
678
679 that are currently in "up" state.
680 """
681 client = client or cls.shares_v2_client
682 cls.services = client.list_services()
683 zones = [service['zone'] for service in cls.services if
684 service['binary'] == "manila-share" and
685 service['state'] == 'up']
686 return zones
687
Yogesh1f931ff2015-09-29 23:41:02 -0400688 def get_pools_for_replication_domain(self):
689 # Get the list of pools for the replication domain
690 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500691 instance_host = self.admin_client.get_share(
692 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400693 host_pool = [p for p in pools if p['name'] == instance_host][0]
694 rep_domain = host_pool['capabilities']['replication_domain']
695 pools_in_rep_domain = [p for p in pools if p['capabilities'][
696 'replication_domain'] == rep_domain]
697 return rep_domain, pools_in_rep_domain
698
Yogeshbdb88102015-09-29 23:41:02 -0400699 @classmethod
700 def create_share_replica(cls, share_id, availability_zone, client=None,
701 cleanup_in_class=False, cleanup=True):
702 client = client or cls.shares_v2_client
703 replica = client.create_share_replica(share_id, availability_zone)
704 resource = {
705 "type": "share_replica",
706 "id": replica["id"],
707 "client": client,
708 "share_id": share_id,
709 }
710 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
711 if cleanup:
712 if cleanup_in_class:
713 cls.class_resources.insert(0, resource)
714 else:
715 cls.method_resources.insert(0, resource)
716 client.wait_for_share_replica_status(
717 replica["id"], constants.STATUS_AVAILABLE)
718 return replica
719
720 @classmethod
721 def delete_share_replica(cls, replica_id, client=None):
722 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400723 try:
724 client.delete_share_replica(replica_id)
725 client.wait_for_resource_deletion(replica_id=replica_id)
726 except exceptions.NotFound:
727 pass
Yogeshbdb88102015-09-29 23:41:02 -0400728
729 @classmethod
730 def promote_share_replica(cls, replica_id, client=None):
731 client = client or cls.shares_v2_client
732 replica = client.promote_share_replica(replica_id)
733 client.wait_for_share_replica_status(
734 replica["id"],
735 constants.REPLICATION_STATE_ACTIVE,
736 status_attr="replica_state")
737 return replica
738
yogeshdb32f462016-09-28 15:09:50 -0400739 def _get_access_rule_data_from_config(self):
740 """Get the first available access type/to combination from config.
741
742 This method opportunistically picks the first configured protocol
743 to create the share. Do not use this method in tests where you need
744 to test depth and breadth in the access types and access recipients.
745 """
746 protocol = self.shares_v2_client.share_protocol
747
748 if protocol in CONF.share.enable_ip_rules_for_protocols:
749 access_type = "ip"
750 access_to = utils.rand_ip()
751 elif protocol in CONF.share.enable_user_rules_for_protocols:
752 access_type = "user"
753 access_to = CONF.share.username_for_user_rules
754 elif protocol in CONF.share.enable_cert_rules_for_protocols:
755 access_type = "cert"
756 access_to = "client3.com"
757 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
758 access_type = "cephx"
759 access_to = "eve"
760 else:
761 message = "Unrecognized protocol and access rules configuration."
762 raise self.skipException(message)
763
764 return access_type, access_to
765
Yogeshbdb88102015-09-29 23:41:02 -0400766 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200767 def create_share_network(cls, client=None,
768 cleanup_in_class=False, **kwargs):
769 if client is None:
770 client = cls.shares_client
771 share_network = client.create_share_network(**kwargs)
772 resource = {
773 "type": "share_network",
774 "id": share_network["id"],
775 "client": client,
776 }
777 if cleanup_in_class:
778 cls.class_resources.insert(0, resource)
779 else:
780 cls.method_resources.insert(0, resource)
781 return share_network
782
783 @classmethod
784 def create_security_service(cls, ss_type="ldap", client=None,
785 cleanup_in_class=False, **kwargs):
786 if client is None:
787 client = cls.shares_client
788 security_service = client.create_security_service(ss_type, **kwargs)
789 resource = {
790 "type": "security_service",
791 "id": security_service["id"],
792 "client": client,
793 }
794 if cleanup_in_class:
795 cls.class_resources.insert(0, resource)
796 else:
797 cls.method_resources.insert(0, resource)
798 return security_service
799
800 @classmethod
801 def create_share_type(cls, name, is_public=True, client=None,
802 cleanup_in_class=True, **kwargs):
803 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200804 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200805 share_type = client.create_share_type(name, is_public, **kwargs)
806 resource = {
807 "type": "share_type",
808 "id": share_type["share_type"]["id"],
809 "client": client,
810 }
811 if cleanup_in_class:
812 cls.class_resources.insert(0, resource)
813 else:
814 cls.method_resources.insert(0, resource)
815 return share_type
816
817 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400818 def add_extra_specs_to_dict(extra_specs=None):
819 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300820 dhss = six.text_type(CONF.share.multitenancy_enabled)
821 snapshot_support = six.text_type(
822 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400823 create_from_snapshot_support = six.text_type(
824 CONF.share.capability_create_share_from_snapshot_support)
825
826 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300827 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200828 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400829
830 optional = {
831 "snapshot_support": snapshot_support,
832 "create_share_from_snapshot_support": create_from_snapshot_support,
833 }
834 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
835 # required extra-spec
836 extra_specs_dict.update(optional)
837
Marc Koderer0abc93b2015-07-15 09:18:35 +0200838 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400839 extra_specs_dict.update(extra_specs)
840
841 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200842
843 @classmethod
844 def clear_isolated_creds(cls, creds=None):
845 if creds is None:
846 creds = cls.method_isolated_creds
847 for ic in creds:
848 if "deleted" not in ic.keys():
849 ic["deleted"] = False
850 if not ic["deleted"]:
851 with handle_cleanup_exceptions():
852 ic["method"]()
853 ic["deleted"] = True
854
855 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400856 def clear_share_replicas(cls, share_id, client=None):
857 client = client or cls.shares_v2_client
858 share_replicas = client.list_share_replicas(
859 share_id=share_id)
860
861 for replica in share_replicas:
862 try:
863 cls.delete_share_replica(replica['id'])
864 except exceptions.BadRequest:
865 # Ignore the exception due to deletion of last active replica
866 pass
867
868 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200869 def clear_resources(cls, resources=None):
870 """Deletes resources, that were created in test suites.
871
872 This method tries to remove resources from resource list,
873 if it is not found, assumed it was deleted in test itself.
874 It is expected, that all resources were added as LIFO
875 due to restriction of deletion resources, that is in the chain.
876
877 :param resources: dict with keys 'type','id','client' and 'deleted'
878 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200879 if resources is None:
880 resources = cls.method_resources
881 for res in resources:
882 if "deleted" not in res.keys():
883 res["deleted"] = False
884 if "client" not in res.keys():
885 res["client"] = cls.shares_client
886 if not(res["deleted"]):
887 res_id = res['id']
888 client = res["client"]
889 with handle_cleanup_exceptions():
890 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400891 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400892 share_group_id = res.get('share_group_id')
893 if share_group_id:
894 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400895 client.delete_share(res_id, params=params)
896 else:
897 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200898 client.wait_for_resource_deletion(share_id=res_id)
899 elif res["type"] is "snapshot":
900 client.delete_snapshot(res_id)
901 client.wait_for_resource_deletion(snapshot_id=res_id)
902 elif res["type"] is "share_network":
903 client.delete_share_network(res_id)
904 client.wait_for_resource_deletion(sn_id=res_id)
905 elif res["type"] is "security_service":
906 client.delete_security_service(res_id)
907 client.wait_for_resource_deletion(ss_id=res_id)
908 elif res["type"] is "share_type":
909 client.delete_share_type(res_id)
910 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400911 elif res["type"] is "share_group":
912 client.delete_share_group(res_id)
913 client.wait_for_resource_deletion(
914 share_group_id=res_id)
915 elif res["type"] is "share_group_type":
916 client.delete_share_group_type(res_id)
917 client.wait_for_resource_deletion(
918 share_group_type_id=res_id)
919 elif res["type"] is "share_group_snapshot":
920 client.delete_share_group_snapshot(res_id)
921 client.wait_for_resource_deletion(
922 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400923 elif res["type"] is "share_replica":
924 client.delete_share_replica(res_id)
925 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200926 else:
huayue97bacbf2016-01-04 09:57:39 +0800927 LOG.warning("Provided unsupported resource type for "
928 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200929 res["deleted"] = True
930
931 @classmethod
932 def generate_share_network_data(self):
933 data = {
934 "name": data_utils.rand_name("sn-name"),
935 "description": data_utils.rand_name("sn-desc"),
936 "neutron_net_id": data_utils.rand_name("net-id"),
937 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
938 }
939 return data
940
941 @classmethod
942 def generate_security_service_data(self):
943 data = {
944 "name": data_utils.rand_name("ss-name"),
945 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200946 "dns_ip": utils.rand_ip(),
947 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200948 "domain": data_utils.rand_name("ss-domain"),
949 "user": data_utils.rand_name("ss-user"),
950 "password": data_utils.rand_name("ss-password"),
951 }
952 return data
953
954 # Useful assertions
955 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
956 """Assert two dicts are equivalent.
957
958 This is a 'deep' match in the sense that it handles nested
959 dictionaries appropriately.
960
961 NOTE:
962
963 If you don't care (or don't know) a given value, you can specify
964 the string DONTCARE as the value. This will cause that dict-item
965 to be skipped.
966
967 """
968 def raise_assertion(msg):
969 d1str = str(d1)
970 d2str = str(d2)
971 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
972 'd2: %(d2str)s' %
973 {"msg": msg, "d1str": d1str, "d2str": d2str})
974 raise AssertionError(base_msg)
975
976 d1keys = set(d1.keys())
977 d2keys = set(d2.keys())
978 if d1keys != d2keys:
979 d1only = d1keys - d2keys
980 d2only = d2keys - d1keys
981 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
982 'Keys in d2 and not d1: %(d2only)s' %
983 {"d1only": d1only, "d2only": d2only})
984
985 for key in d1keys:
986 d1value = d1[key]
987 d2value = d2[key]
988 try:
989 error = abs(float(d1value) - float(d2value))
990 within_tolerance = error <= tolerance
991 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +0900992 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +0200993 # ValueError if arg is a str, TypeError if it's something else
994 # (like None)
995 within_tolerance = False
996
997 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
998 self.assertDictMatch(d1value, d2value)
999 elif 'DONTCARE' in (d1value, d2value):
1000 continue
1001 elif approx_equal and within_tolerance:
1002 continue
1003 elif d1value != d2value:
1004 raise_assertion("d1['%(key)s']=%(d1value)s != "
1005 "d2['%(key)s']=%(d2value)s" %
1006 {
1007 "key": key,
1008 "d1value": d1value,
1009 "d2value": d2value
1010 })
1011
Alex Meadeba8a1602016-05-06 09:33:09 -04001012 def create_user_message(self):
1013 """Trigger a 'no valid host' situation to generate a message."""
1014 extra_specs = {
1015 'vendor_name': 'foobar',
1016 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1017 }
1018 share_type_name = data_utils.rand_name("share-type")
1019
1020 bogus_type = self.create_share_type(
1021 name=share_type_name,
1022 extra_specs=extra_specs)['share_type']
1023
1024 params = {'share_type_id': bogus_type['id'],
1025 'share_network_id': self.shares_v2_client.share_network_id}
1026 share = self.shares_v2_client.create_share(**params)
1027 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1028 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1029 return self.shares_v2_client.wait_for_message(share['id'])
1030
Marc Koderer0abc93b2015-07-15 09:18:35 +02001031
1032class BaseSharesAltTest(BaseSharesTest):
1033 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001034 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001035
1036
1037class BaseSharesAdminTest(BaseSharesTest):
1038 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001039 credentials = ('admin', )
1040
1041
1042class BaseSharesMixedTest(BaseSharesTest):
1043 """Base test case class for all Shares API tests with all user roles."""
1044 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001045
1046 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001047 def setup_clients(cls):
1048 super(BaseSharesMixedTest, cls).setup_clients()
1049 cls.admin_shares_client = shares_client.SharesClient(
1050 cls.os_admin.auth_provider)
1051 cls.admin_shares_v2_client = shares_v2_client.SharesV2Client(
1052 cls.os_admin.auth_provider)
1053 cls.alt_shares_client = shares_client.SharesClient(
1054 cls.os_alt.auth_provider)
1055 cls.alt_shares_v2_client = shares_v2_client.SharesV2Client(
1056 cls.os_alt.auth_provider)
1057
1058 if CONF.share.multitenancy_enabled:
1059 admin_share_network_id = cls.provide_share_network(
1060 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1061 cls.admin_shares_client.share_network_id = admin_share_network_id
1062 cls.admin_shares_v2_client.share_network_id = (
1063 admin_share_network_id)
1064
1065 alt_share_network_id = cls.provide_share_network(
1066 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1067 cls.alt_shares_client.share_network_id = alt_share_network_id
1068 cls.alt_shares_v2_client.share_network_id = alt_share_network_id