blob: 236351dd611c323584f8ab48122478ab261f1d2d [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
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020026from tempest.common import dynamic_creds
27from tempest import config
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
Yogeshbdb88102015-09-29 23:41:02 -040032from manila_tempest_tests.common import constants
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +030033from manila_tempest_tests.services.share.json import shares_client
34from manila_tempest_tests.services.share.v2.json import (
35 shares_client as shares_v2_client)
Marc Koderer0abc93b2015-07-15 09:18:35 +020036from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020037from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020038
39CONF = config.CONF
40LOG = log.getLogger(__name__)
41
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030042# Test tags related to test direction
43TAG_POSITIVE = "positive"
44TAG_NEGATIVE = "negative"
45
46# Test tags related to service involvement
47TAG_API = "api"
48TAG_BACKEND = "backend"
49TAG_API_WITH_BACKEND = "api_with_backend"
50
51TAGS_MAPPER = {
52 "p": TAG_POSITIVE,
53 "n": TAG_NEGATIVE,
54 "a": TAG_API,
55 "b": TAG_BACKEND,
56 "ab": TAG_API_WITH_BACKEND,
57}
58TAGS_PATTERN = re.compile(
59 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
60 TAGS_MAPPER)
61
62
63def verify_test_has_appropriate_tags(self):
64 if not TAGS_PATTERN.match(self.id()):
65 msg = (
66 "Required attributes either not set or set improperly. "
67 "Two test attributes are expected:\n"
68 " - one of '%(p)s' or '%(n)s' and \n"
69 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
70 ) % TAGS_MAPPER
71 raise self.failureException(msg)
72
Marc Koderer0abc93b2015-07-15 09:18:35 +020073
74class handle_cleanup_exceptions(object):
75 """Handle exceptions raised with cleanup operations.
76
77 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
78 are raised.
79 Suppress all other exceptions only in case config opt
80 'suppress_errors_in_cleanup' in config group 'share' is True.
81 """
82
83 def __enter__(self):
84 return self
85
86 def __exit__(self, exc_type, exc_value, exc_traceback):
87 if not (isinstance(exc_value,
88 (exceptions.NotFound, exceptions.Forbidden)) or
89 CONF.share.suppress_errors_in_cleanup):
90 return False # Do not suppress error if any
91 if exc_traceback:
92 LOG.error("Suppressed cleanup error in Manila: "
93 "\n%s" % traceback.format_exc())
94 return True # Suppress error if any
95
96
97def network_synchronized(f):
98
99 def wrapped_func(self, *args, **kwargs):
100 with_isolated_creds = True if len(args) > 2 else False
101 no_lock_required = kwargs.get(
102 "isolated_creds_client", with_isolated_creds)
103 if no_lock_required:
104 # Usage of not reusable network. No need in lock.
105 return f(self, *args, **kwargs)
106
107 # Use lock assuming reusage of common network.
108 @lockutils.synchronized("manila_network_lock", external=True)
109 def source_func(self, *args, **kwargs):
110 return f(self, *args, **kwargs)
111
112 return source_func(self, *args, **kwargs)
113
114 return wrapped_func
115
116
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200117skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500118skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200119
120
Marc Koderer0abc93b2015-07-15 09:18:35 +0200121class BaseSharesTest(test.BaseTestCase):
122 """Base test case class for all Manila API tests."""
123
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300124 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200125 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200126 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200127
128 # Will be cleaned up in resource_cleanup
129 class_resources = []
130
131 # Will be cleaned up in tearDown method
132 method_resources = []
133
134 # Will be cleaned up in resource_cleanup
135 class_isolated_creds = []
136
137 # Will be cleaned up in tearDown method
138 method_isolated_creds = []
139
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200140 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200141 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200142 raise self.skipException(
143 "Microversion '%s' is not supported." % microversion)
144
Xing Yang69b00b52015-11-22 16:10:44 -0500145 def skip_if_microversion_lt(self, microversion):
146 if utils.is_microversion_lt(CONF.share.max_api_microversion,
147 microversion):
148 raise self.skipException(
149 "Microversion must be greater than or equal to '%s'." %
150 microversion)
151
Marc Koderer0abc93b2015-07-15 09:18:35 +0200152 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500153 def _get_dynamic_creds(cls, name, network_resources=None):
154 return dynamic_creds.DynamicCredentialProvider(
155 identity_version=CONF.identity.auth_version,
156 name=name,
157 network_resources=network_resources,
158 credentials_domain=CONF.auth.default_credentials_domain_name,
159 admin_role=CONF.identity.admin_role,
160 admin_creds=common_creds.get_configured_admin_credentials(),
161 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
162 identity_admin_role=CONF.identity.admin_role,
163 extra_roles=None,
164 neutron_available=CONF.service_available.neutron,
165 create_networks=(
166 CONF.share.create_networks_when_multitenancy_enabled),
167 project_network_cidr=CONF.network.project_network_cidr,
168 project_network_mask_bits=CONF.network.project_network_mask_bits,
169 public_network_id=CONF.network.public_network_id,
170 resource_prefix=CONF.resources_prefix)
171
172 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200173 def get_client_with_isolated_creds(cls,
174 name=None,
175 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400176 cleanup_in_class=False,
177 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200178 """Creates isolated creds.
179
180 :param name: name, will be used for naming ic and related stuff
181 :param type_of_creds: admin, alt or primary
182 :param cleanup_in_class: defines place where to delete
183 :returns: SharesClient -- shares client with isolated creds.
184 :returns: To client added dict attr 'creds' with
185 :returns: key elements 'tenant' and 'user'.
186 """
187 if name is None:
188 # Get name of test method
189 name = inspect.stack()[1][3]
190 if len(name) > 32:
191 name = name[0:32]
192
193 # Choose type of isolated creds
Tom Barron4b8834a2017-02-02 11:02:20 -0500194 ic = cls._get_dynamic_creds(name)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200195 if "admin" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200196 creds = ic.get_admin_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200197 elif "alt" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200198 creds = ic.get_alt_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200199 else:
Marc Koderer5880b362016-07-06 10:59:07 +0200200 creds = ic.get_credentials(type_of_creds).credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200201 ic.type_of_creds = type_of_creds
202
203 # create client with isolated creds
204 os = clients.Manager(credentials=creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400205 if client_version == '1':
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300206 client = shares_client.SharesClient(os.auth_provider)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400207 elif client_version == '2':
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300208 client = shares_v2_client.SharesV2Client(os.auth_provider)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200209
210 # Set place where will be deleted isolated creds
211 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200212 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200213 "deleted": False,
214 }
215 if cleanup_in_class:
216 cls.class_isolated_creds.insert(0, ic_res)
217 else:
218 cls.method_isolated_creds.insert(0, ic_res)
219
220 # Provide share network
221 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300222 if (not CONF.service_available.neutron and
223 CONF.share.create_networks_when_multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200224 raise cls.skipException("Neutron support is required")
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200225 nc = os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200226 share_network_id = cls.provide_share_network(client, nc, ic)
227 client.share_network_id = share_network_id
228 resource = {
229 "type": "share_network",
230 "id": client.share_network_id,
231 "client": client,
232 }
233 if cleanup_in_class:
234 cls.class_resources.insert(0, resource)
235 else:
236 cls.method_resources.insert(0, resource)
237 return client
238
239 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000240 def skip_checks(cls):
241 super(BaseSharesTest, cls).skip_checks()
242 if not CONF.service_available.manila:
243 raise cls.skipException("Manila support is required")
244
245 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200246 def verify_nonempty(cls, *args):
247 if not all(args):
248 msg = "Missing API credentials in configuration."
249 raise cls.skipException(msg)
250
251 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300252 def setup_clients(cls):
253 super(BaseSharesTest, cls).setup_clients()
254 os = getattr(cls, 'os_%s' % cls.credentials[0])
255 os.shares_client = shares_client.SharesClient(os.auth_provider)
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300256
257 if CONF.identity.auth_version == 'v3':
258 project_id = os.auth_provider.auth_data[1]['project']['id']
259 else:
260 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
261 cls.tenant_id = project_id
262 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
263
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300264 cls.shares_client = os.shares_client
265 os.shares_v2_client = shares_v2_client.SharesV2Client(
266 os.auth_provider)
267 cls.shares_v2_client = os.shares_v2_client
268 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300269 if (not CONF.service_available.neutron and
270 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300271 raise cls.skipException("Neutron support is required")
272 share_network_id = cls.provide_share_network(
273 cls.shares_v2_client, os.networks_client)
274 cls.shares_client.share_network_id = share_network_id
275 cls.shares_v2_client.share_network_id = share_network_id
276
277 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200278 def resource_setup(cls):
279 if not (any(p in CONF.share.enable_protocols
280 for p in cls.protocols) and
281 CONF.service_available.manila):
282 skip_msg = "Manila is disabled"
283 raise cls.skipException(skip_msg)
284 super(BaseSharesTest, cls).resource_setup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200285
286 def setUp(self):
287 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200288 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200289 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300290 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200291
292 @classmethod
293 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200294 cls.clear_resources(cls.class_resources)
295 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400296 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200297
298 @classmethod
299 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200300 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300301 isolated_creds_client=None,
302 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200303 """Used for finding/creating share network for multitenant driver.
304
305 This method creates/gets entity share-network for one tenant. This
306 share-network will be used for creation of service vm.
307
308 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200309 :param networks_client: network client from same tenant as shares
310 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200311 If provided, then its networking will be used if needed.
312 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300313 :param ignore_multitenancy_config: provide a share network regardless
314 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200315 :returns: str -- share network id for shares_client tenant
316 :returns: None -- if single-tenant driver used
317 """
318
319 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300320 search_word = "reusable"
321 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200322
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300323 if (not ignore_multitenancy_config and
324 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200325 # Assumed usage of a single-tenant driver
326 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200327 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300328 if sc.share_network_id:
329 # Share-network already exists, use it
330 share_network_id = sc.share_network_id
331 elif not CONF.share.create_networks_when_multitenancy_enabled:
332 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200333
334 # Try get suitable share-network
335 share_networks = sc.list_share_networks_with_detail()
336 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300337 if (sn["neutron_net_id"] is None and
338 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200339 sn["name"] and search_word in sn["name"]):
340 share_network_id = sn["id"]
341 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200342
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300343 # Create new share-network if one was not found
344 if share_network_id is None:
345 sn_desc = "This share-network was created by tempest"
346 sn = sc.create_share_network(name=sn_name,
347 description=sn_desc)
348 share_network_id = sn["id"]
349 else:
350 net_id = subnet_id = share_network_id = None
351
352 if not isolated_creds_client:
353 # Search for networks, created in previous runs
354 service_net_name = "share-service"
355 networks = networks_client.list_networks()
356 if "networks" in networks.keys():
357 networks = networks["networks"]
358 for network in networks:
359 if (service_net_name in network["name"] and
360 sc.tenant_id == network['tenant_id']):
361 net_id = network["id"]
362 if len(network["subnets"]) > 0:
363 subnet_id = network["subnets"][0]
364 break
365
366 # Create suitable network
367 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500368 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300369 net_data = ic._create_network_resources(sc.tenant_id)
370 network, subnet, router = net_data
371 net_id = network["id"]
372 subnet_id = subnet["id"]
373
374 # Try get suitable share-network
375 share_networks = sc.list_share_networks_with_detail()
376 for sn in share_networks:
377 if (net_id == sn["neutron_net_id"] and
378 subnet_id == sn["neutron_subnet_id"] and
379 sn["name"] and search_word in sn["name"]):
380 share_network_id = sn["id"]
381 break
382 else:
383 sn_name = "autogenerated_by_tempest_for_isolated_creds"
384 # Use precreated network and subnet from isolated creds
385 net_id = isolated_creds_client.get_credentials(
386 isolated_creds_client.type_of_creds).network['id']
387 subnet_id = isolated_creds_client.get_credentials(
388 isolated_creds_client.type_of_creds).subnet['id']
389
390 # Create suitable share-network
391 if share_network_id is None:
392 sn_desc = "This share-network was created by tempest"
393 sn = sc.create_share_network(name=sn_name,
394 description=sn_desc,
395 neutron_net_id=net_id,
396 neutron_subnet_id=subnet_id)
397 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200398
399 return share_network_id
400
401 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300402 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200403 snapshot_id=None, description=None, metadata=None,
404 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400405 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400406 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300407 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200408 description = description or "Tempest's share"
409 share_network_id = share_network_id or client.share_network_id or None
410 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300411 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400412 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200413 'share_protocol': share_protocol,
414 'size': size,
415 'name': name,
416 'snapshot_id': snapshot_id,
417 'description': description,
418 'metadata': metadata,
419 'share_network_id': share_network_id,
420 'share_type_id': share_type_id,
421 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400422 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400423 if share_group_id:
424 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400425
Marc Koderer0abc93b2015-07-15 09:18:35 +0200426 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400427 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400428 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200429 cleanup_list = (cls.class_resources if cleanup_in_class else
430 cls.method_resources)
431 cleanup_list.insert(0, resource)
432 return share
433
434 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300435 def migrate_share(
436 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200437 force_host_assisted_migration=False, writable=False,
438 nondisruptive=False, preserve_metadata=False,
439 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300440 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400441 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300442 client.migrate_share(
443 share_id, dest_host,
444 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200445 writable=writable, preserve_metadata=preserve_metadata,
446 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300447 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300448 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200449 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300450 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200451 return share
452
453 @classmethod
454 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
455 client = client or cls.shares_v2_client
456 client.migration_complete(share_id, **kwargs)
457 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300458 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300459 return share
460
461 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300462 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
463 client = client or cls.shares_v2_client
464 client.migration_cancel(share_id, **kwargs)
465 share = client.wait_for_migration_status(
466 share_id, dest_host, 'migration_cancelled', **kwargs)
467 return share
468
469 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200470 def create_share(cls, *args, **kwargs):
471 """Create one share and wait for available state. Retry if allowed."""
472 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
473 return result[0]
474
475 @classmethod
476 def create_shares(cls, share_data_list):
477 """Creates several shares in parallel with retries.
478
479 Use this method when you want to create more than one share at same
480 time. Especially if config option 'share.share_creation_retry_number'
481 has value more than zero (0).
482 All shares will be expected to have 'available' status with or without
483 recreation else error will be raised.
484
485 :param share_data_list: list -- list of dictionaries with 'args' and
486 'kwargs' for '_create_share' method of this base class.
487 example of data:
488 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
489 :returns: list -- list of shares created using provided data.
490 """
491
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300492 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200493 if not isinstance(d, dict):
494 raise exceptions.TempestException(
495 "Expected 'dict', got '%s'" % type(d))
496 if "args" not in d:
497 d["args"] = []
498 if "kwargs" not in d:
499 d["kwargs"] = {}
500 if len(d) > 2:
501 raise exceptions.TempestException(
502 "Expected only 'args' and 'kwargs' keys. "
503 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300504
505 data = []
506 for d in share_data_list:
507 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400508 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300509 local_d = {
510 "args": d["args"],
511 "kwargs": copy.deepcopy(d["kwargs"]),
512 }
513 local_d["kwargs"]["client"] = client
514 local_d["share"] = cls._create_share(
515 *local_d["args"], **local_d["kwargs"])
516 local_d["cnt"] = 0
517 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400518 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300519 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200520
521 while not all(d["available"] for d in data):
522 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400523 if not d["wait_for_status"]:
524 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200525 if d["available"]:
526 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300527 client = d["kwargs"]["client"]
528 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200529 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300530 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200531 d["available"] = True
532 except (share_exceptions.ShareBuildErrorException,
533 exceptions.TimeoutException) as e:
534 if CONF.share.share_creation_retry_number > d["cnt"]:
535 d["cnt"] += 1
536 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300537 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200538 LOG.error(msg)
539 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300540 cg_id = d["kwargs"].get("consistency_group_id")
541 if cg_id:
542 # NOTE(vponomaryov): delete errored share
543 # immediately in case share is part of CG.
544 client.delete_share(
545 share_id,
546 params={"consistency_group_id": cg_id})
547 client.wait_for_resource_deletion(
548 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200549 d["share"] = cls._create_share(
550 *d["args"], **d["kwargs"])
551 else:
gecong197358663802016-08-25 11:08:45 +0800552 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200553
554 return [d["share"] for d in data]
555
556 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400557 def create_share_group(cls, client=None, cleanup_in_class=True,
558 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400559 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400560 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400561 kwargs['share_network_id'] = (share_network_id or
562 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400563 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400564 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400565 "type": "share_group",
566 "id": share_group["id"],
567 "client": client,
568 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400569 if cleanup_in_class:
570 cls.class_resources.insert(0, resource)
571 else:
572 cls.method_resources.insert(0, resource)
573
Andrew Kerrb8436922016-06-01 15:32:43 -0400574 if kwargs.get('source_share_group_snapshot_id'):
575 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400576 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400577 params={'share_group_id': share_group['id']},
578 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400579
Andrew Kerrb8436922016-06-01 15:32:43 -0400580 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400581 resource = {"type": "share",
582 "id": share["id"],
583 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400584 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400585 if cleanup_in_class:
586 cls.class_resources.insert(0, resource)
587 else:
588 cls.method_resources.insert(0, resource)
589
Andrew Kerrb8436922016-06-01 15:32:43 -0400590 client.wait_for_share_group_status(share_group['id'], 'available')
591 return share_group
592
593 @classmethod
594 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
595 group_specs=None, client=None,
596 cleanup_in_class=True, **kwargs):
597 client = client or cls.shares_v2_client
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300598 if group_specs is None:
599 group_specs = {
600 'consistent_snapshot_support': (
601 CONF.share.capability_sg_consistent_snapshot_support),
602 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400603 share_group_type = client.create_share_group_type(
604 name=name,
605 share_types=share_types,
606 is_public=is_public,
607 group_specs=group_specs,
608 **kwargs)
609 resource = {
610 "type": "share_group_type",
611 "id": share_group_type["id"],
612 "client": client,
613 }
614 if cleanup_in_class:
615 cls.class_resources.insert(0, resource)
616 else:
617 cls.method_resources.insert(0, resource)
618 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400619
620 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200621 def create_snapshot_wait_for_active(cls, share_id, name=None,
622 description=None, force=False,
623 client=None, cleanup_in_class=True):
624 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400625 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200626 if description is None:
627 description = "Tempest's snapshot"
628 snapshot = client.create_snapshot(share_id, name, description, force)
629 resource = {
630 "type": "snapshot",
631 "id": snapshot["id"],
632 "client": client,
633 }
634 if cleanup_in_class:
635 cls.class_resources.insert(0, resource)
636 else:
637 cls.method_resources.insert(0, resource)
638 client.wait_for_snapshot_status(snapshot["id"], "available")
639 return snapshot
640
641 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400642 def create_share_group_snapshot_wait_for_active(
643 cls, share_group_id, name=None, description=None, client=None,
644 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400645 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400646 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400647 description = "Tempest's share group snapshot"
648 sg_snapshot = client.create_share_group_snapshot(
649 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400650 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400651 "type": "share_group_snapshot",
652 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400653 "client": client,
654 }
655 if cleanup_in_class:
656 cls.class_resources.insert(0, resource)
657 else:
658 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400659 client.wait_for_share_group_snapshot_status(
660 sg_snapshot["id"], "available")
661 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400662
663 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400664 def get_availability_zones(cls, client=None):
665 """List the availability zones for "manila-share" services
666
667 that are currently in "up" state.
668 """
669 client = client or cls.shares_v2_client
670 cls.services = client.list_services()
671 zones = [service['zone'] for service in cls.services if
672 service['binary'] == "manila-share" and
673 service['state'] == 'up']
674 return zones
675
Yogesh1f931ff2015-09-29 23:41:02 -0400676 def get_pools_for_replication_domain(self):
677 # Get the list of pools for the replication domain
678 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500679 instance_host = self.admin_client.get_share(
680 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400681 host_pool = [p for p in pools if p['name'] == instance_host][0]
682 rep_domain = host_pool['capabilities']['replication_domain']
683 pools_in_rep_domain = [p for p in pools if p['capabilities'][
684 'replication_domain'] == rep_domain]
685 return rep_domain, pools_in_rep_domain
686
Yogeshbdb88102015-09-29 23:41:02 -0400687 @classmethod
688 def create_share_replica(cls, share_id, availability_zone, client=None,
689 cleanup_in_class=False, cleanup=True):
690 client = client or cls.shares_v2_client
691 replica = client.create_share_replica(share_id, availability_zone)
692 resource = {
693 "type": "share_replica",
694 "id": replica["id"],
695 "client": client,
696 "share_id": share_id,
697 }
698 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
699 if cleanup:
700 if cleanup_in_class:
701 cls.class_resources.insert(0, resource)
702 else:
703 cls.method_resources.insert(0, resource)
704 client.wait_for_share_replica_status(
705 replica["id"], constants.STATUS_AVAILABLE)
706 return replica
707
708 @classmethod
709 def delete_share_replica(cls, replica_id, client=None):
710 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400711 try:
712 client.delete_share_replica(replica_id)
713 client.wait_for_resource_deletion(replica_id=replica_id)
714 except exceptions.NotFound:
715 pass
Yogeshbdb88102015-09-29 23:41:02 -0400716
717 @classmethod
718 def promote_share_replica(cls, replica_id, client=None):
719 client = client or cls.shares_v2_client
720 replica = client.promote_share_replica(replica_id)
721 client.wait_for_share_replica_status(
722 replica["id"],
723 constants.REPLICATION_STATE_ACTIVE,
724 status_attr="replica_state")
725 return replica
726
yogeshdb32f462016-09-28 15:09:50 -0400727 def _get_access_rule_data_from_config(self):
728 """Get the first available access type/to combination from config.
729
730 This method opportunistically picks the first configured protocol
731 to create the share. Do not use this method in tests where you need
732 to test depth and breadth in the access types and access recipients.
733 """
734 protocol = self.shares_v2_client.share_protocol
735
736 if protocol in CONF.share.enable_ip_rules_for_protocols:
737 access_type = "ip"
738 access_to = utils.rand_ip()
739 elif protocol in CONF.share.enable_user_rules_for_protocols:
740 access_type = "user"
741 access_to = CONF.share.username_for_user_rules
742 elif protocol in CONF.share.enable_cert_rules_for_protocols:
743 access_type = "cert"
744 access_to = "client3.com"
745 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
746 access_type = "cephx"
747 access_to = "eve"
748 else:
749 message = "Unrecognized protocol and access rules configuration."
750 raise self.skipException(message)
751
752 return access_type, access_to
753
Yogeshbdb88102015-09-29 23:41:02 -0400754 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200755 def create_share_network(cls, client=None,
756 cleanup_in_class=False, **kwargs):
757 if client is None:
758 client = cls.shares_client
759 share_network = client.create_share_network(**kwargs)
760 resource = {
761 "type": "share_network",
762 "id": share_network["id"],
763 "client": client,
764 }
765 if cleanup_in_class:
766 cls.class_resources.insert(0, resource)
767 else:
768 cls.method_resources.insert(0, resource)
769 return share_network
770
771 @classmethod
772 def create_security_service(cls, ss_type="ldap", client=None,
773 cleanup_in_class=False, **kwargs):
774 if client is None:
775 client = cls.shares_client
776 security_service = client.create_security_service(ss_type, **kwargs)
777 resource = {
778 "type": "security_service",
779 "id": security_service["id"],
780 "client": client,
781 }
782 if cleanup_in_class:
783 cls.class_resources.insert(0, resource)
784 else:
785 cls.method_resources.insert(0, resource)
786 return security_service
787
788 @classmethod
789 def create_share_type(cls, name, is_public=True, client=None,
790 cleanup_in_class=True, **kwargs):
791 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200792 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200793 share_type = client.create_share_type(name, is_public, **kwargs)
794 resource = {
795 "type": "share_type",
796 "id": share_type["share_type"]["id"],
797 "client": client,
798 }
799 if cleanup_in_class:
800 cls.class_resources.insert(0, resource)
801 else:
802 cls.method_resources.insert(0, resource)
803 return share_type
804
805 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400806 def add_extra_specs_to_dict(extra_specs=None):
807 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300808 dhss = six.text_type(CONF.share.multitenancy_enabled)
809 snapshot_support = six.text_type(
810 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400811 create_from_snapshot_support = six.text_type(
812 CONF.share.capability_create_share_from_snapshot_support)
813
814 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300815 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200816 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400817
818 optional = {
819 "snapshot_support": snapshot_support,
820 "create_share_from_snapshot_support": create_from_snapshot_support,
821 }
822 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
823 # required extra-spec
824 extra_specs_dict.update(optional)
825
Marc Koderer0abc93b2015-07-15 09:18:35 +0200826 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400827 extra_specs_dict.update(extra_specs)
828
829 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200830
831 @classmethod
832 def clear_isolated_creds(cls, creds=None):
833 if creds is None:
834 creds = cls.method_isolated_creds
835 for ic in creds:
836 if "deleted" not in ic.keys():
837 ic["deleted"] = False
838 if not ic["deleted"]:
839 with handle_cleanup_exceptions():
840 ic["method"]()
841 ic["deleted"] = True
842
843 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400844 def clear_share_replicas(cls, share_id, client=None):
845 client = client or cls.shares_v2_client
846 share_replicas = client.list_share_replicas(
847 share_id=share_id)
848
849 for replica in share_replicas:
850 try:
851 cls.delete_share_replica(replica['id'])
852 except exceptions.BadRequest:
853 # Ignore the exception due to deletion of last active replica
854 pass
855
856 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200857 def clear_resources(cls, resources=None):
858 """Deletes resources, that were created in test suites.
859
860 This method tries to remove resources from resource list,
861 if it is not found, assumed it was deleted in test itself.
862 It is expected, that all resources were added as LIFO
863 due to restriction of deletion resources, that is in the chain.
864
865 :param resources: dict with keys 'type','id','client' and 'deleted'
866 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200867 if resources is None:
868 resources = cls.method_resources
869 for res in resources:
870 if "deleted" not in res.keys():
871 res["deleted"] = False
872 if "client" not in res.keys():
873 res["client"] = cls.shares_client
874 if not(res["deleted"]):
875 res_id = res['id']
876 client = res["client"]
877 with handle_cleanup_exceptions():
878 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400879 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400880 share_group_id = res.get('share_group_id')
881 if share_group_id:
882 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400883 client.delete_share(res_id, params=params)
884 else:
885 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200886 client.wait_for_resource_deletion(share_id=res_id)
887 elif res["type"] is "snapshot":
888 client.delete_snapshot(res_id)
889 client.wait_for_resource_deletion(snapshot_id=res_id)
890 elif res["type"] is "share_network":
891 client.delete_share_network(res_id)
892 client.wait_for_resource_deletion(sn_id=res_id)
893 elif res["type"] is "security_service":
894 client.delete_security_service(res_id)
895 client.wait_for_resource_deletion(ss_id=res_id)
896 elif res["type"] is "share_type":
897 client.delete_share_type(res_id)
898 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400899 elif res["type"] is "share_group":
900 client.delete_share_group(res_id)
901 client.wait_for_resource_deletion(
902 share_group_id=res_id)
903 elif res["type"] is "share_group_type":
904 client.delete_share_group_type(res_id)
905 client.wait_for_resource_deletion(
906 share_group_type_id=res_id)
907 elif res["type"] is "share_group_snapshot":
908 client.delete_share_group_snapshot(res_id)
909 client.wait_for_resource_deletion(
910 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400911 elif res["type"] is "share_replica":
912 client.delete_share_replica(res_id)
913 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200914 else:
huayue97bacbf2016-01-04 09:57:39 +0800915 LOG.warning("Provided unsupported resource type for "
916 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200917 res["deleted"] = True
918
919 @classmethod
920 def generate_share_network_data(self):
921 data = {
922 "name": data_utils.rand_name("sn-name"),
923 "description": data_utils.rand_name("sn-desc"),
924 "neutron_net_id": data_utils.rand_name("net-id"),
925 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
926 }
927 return data
928
929 @classmethod
930 def generate_security_service_data(self):
931 data = {
932 "name": data_utils.rand_name("ss-name"),
933 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200934 "dns_ip": utils.rand_ip(),
935 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200936 "domain": data_utils.rand_name("ss-domain"),
937 "user": data_utils.rand_name("ss-user"),
938 "password": data_utils.rand_name("ss-password"),
939 }
940 return data
941
942 # Useful assertions
943 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
944 """Assert two dicts are equivalent.
945
946 This is a 'deep' match in the sense that it handles nested
947 dictionaries appropriately.
948
949 NOTE:
950
951 If you don't care (or don't know) a given value, you can specify
952 the string DONTCARE as the value. This will cause that dict-item
953 to be skipped.
954
955 """
956 def raise_assertion(msg):
957 d1str = str(d1)
958 d2str = str(d2)
959 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
960 'd2: %(d2str)s' %
961 {"msg": msg, "d1str": d1str, "d2str": d2str})
962 raise AssertionError(base_msg)
963
964 d1keys = set(d1.keys())
965 d2keys = set(d2.keys())
966 if d1keys != d2keys:
967 d1only = d1keys - d2keys
968 d2only = d2keys - d1keys
969 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
970 'Keys in d2 and not d1: %(d2only)s' %
971 {"d1only": d1only, "d2only": d2only})
972
973 for key in d1keys:
974 d1value = d1[key]
975 d2value = d2[key]
976 try:
977 error = abs(float(d1value) - float(d2value))
978 within_tolerance = error <= tolerance
979 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +0900980 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +0200981 # ValueError if arg is a str, TypeError if it's something else
982 # (like None)
983 within_tolerance = False
984
985 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
986 self.assertDictMatch(d1value, d2value)
987 elif 'DONTCARE' in (d1value, d2value):
988 continue
989 elif approx_equal and within_tolerance:
990 continue
991 elif d1value != d2value:
992 raise_assertion("d1['%(key)s']=%(d1value)s != "
993 "d2['%(key)s']=%(d2value)s" %
994 {
995 "key": key,
996 "d1value": d1value,
997 "d2value": d2value
998 })
999
1000
1001class BaseSharesAltTest(BaseSharesTest):
1002 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001003 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001004
1005
1006class BaseSharesAdminTest(BaseSharesTest):
1007 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001008 credentials = ('admin', )
1009
1010
1011class BaseSharesMixedTest(BaseSharesTest):
1012 """Base test case class for all Shares API tests with all user roles."""
1013 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001014
1015 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001016 def setup_clients(cls):
1017 super(BaseSharesMixedTest, cls).setup_clients()
1018 cls.admin_shares_client = shares_client.SharesClient(
1019 cls.os_admin.auth_provider)
1020 cls.admin_shares_v2_client = shares_v2_client.SharesV2Client(
1021 cls.os_admin.auth_provider)
1022 cls.alt_shares_client = shares_client.SharesClient(
1023 cls.os_alt.auth_provider)
1024 cls.alt_shares_v2_client = shares_v2_client.SharesV2Client(
1025 cls.os_alt.auth_provider)
1026
1027 if CONF.share.multitenancy_enabled:
1028 admin_share_network_id = cls.provide_share_network(
1029 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1030 cls.admin_shares_client.share_network_id = admin_share_network_id
1031 cls.admin_shares_v2_client.share_network_id = (
1032 admin_share_network_id)
1033
1034 alt_share_network_id = cls.provide_share_network(
1035 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1036 cls.alt_shares_client.share_network_id = alt_share_network_id
1037 cls.alt_shares_v2_client.share_network_id = alt_share_network_id