blob: c84e51300c81889617a732bb9d9f2531c0ff2f69 [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
598 share_group_type = client.create_share_group_type(
599 name=name,
600 share_types=share_types,
601 is_public=is_public,
602 group_specs=group_specs,
603 **kwargs)
604 resource = {
605 "type": "share_group_type",
606 "id": share_group_type["id"],
607 "client": client,
608 }
609 if cleanup_in_class:
610 cls.class_resources.insert(0, resource)
611 else:
612 cls.method_resources.insert(0, resource)
613 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400614
615 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200616 def create_snapshot_wait_for_active(cls, share_id, name=None,
617 description=None, force=False,
618 client=None, cleanup_in_class=True):
619 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400620 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200621 if description is None:
622 description = "Tempest's snapshot"
623 snapshot = client.create_snapshot(share_id, name, description, force)
624 resource = {
625 "type": "snapshot",
626 "id": snapshot["id"],
627 "client": client,
628 }
629 if cleanup_in_class:
630 cls.class_resources.insert(0, resource)
631 else:
632 cls.method_resources.insert(0, resource)
633 client.wait_for_snapshot_status(snapshot["id"], "available")
634 return snapshot
635
636 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400637 def create_share_group_snapshot_wait_for_active(
638 cls, share_group_id, name=None, description=None, client=None,
639 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400640 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400641 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400642 description = "Tempest's share group snapshot"
643 sg_snapshot = client.create_share_group_snapshot(
644 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400645 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400646 "type": "share_group_snapshot",
647 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400648 "client": client,
649 }
650 if cleanup_in_class:
651 cls.class_resources.insert(0, resource)
652 else:
653 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400654 client.wait_for_share_group_snapshot_status(
655 sg_snapshot["id"], "available")
656 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400657
658 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400659 def get_availability_zones(cls, client=None):
660 """List the availability zones for "manila-share" services
661
662 that are currently in "up" state.
663 """
664 client = client or cls.shares_v2_client
665 cls.services = client.list_services()
666 zones = [service['zone'] for service in cls.services if
667 service['binary'] == "manila-share" and
668 service['state'] == 'up']
669 return zones
670
Yogesh1f931ff2015-09-29 23:41:02 -0400671 def get_pools_for_replication_domain(self):
672 # Get the list of pools for the replication domain
673 pools = self.admin_client.list_pools(detail=True)['pools']
674 instance_host = self.shares[0]['host']
675 host_pool = [p for p in pools if p['name'] == instance_host][0]
676 rep_domain = host_pool['capabilities']['replication_domain']
677 pools_in_rep_domain = [p for p in pools if p['capabilities'][
678 'replication_domain'] == rep_domain]
679 return rep_domain, pools_in_rep_domain
680
Yogeshbdb88102015-09-29 23:41:02 -0400681 @classmethod
682 def create_share_replica(cls, share_id, availability_zone, client=None,
683 cleanup_in_class=False, cleanup=True):
684 client = client or cls.shares_v2_client
685 replica = client.create_share_replica(share_id, availability_zone)
686 resource = {
687 "type": "share_replica",
688 "id": replica["id"],
689 "client": client,
690 "share_id": share_id,
691 }
692 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
693 if cleanup:
694 if cleanup_in_class:
695 cls.class_resources.insert(0, resource)
696 else:
697 cls.method_resources.insert(0, resource)
698 client.wait_for_share_replica_status(
699 replica["id"], constants.STATUS_AVAILABLE)
700 return replica
701
702 @classmethod
703 def delete_share_replica(cls, replica_id, client=None):
704 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400705 try:
706 client.delete_share_replica(replica_id)
707 client.wait_for_resource_deletion(replica_id=replica_id)
708 except exceptions.NotFound:
709 pass
Yogeshbdb88102015-09-29 23:41:02 -0400710
711 @classmethod
712 def promote_share_replica(cls, replica_id, client=None):
713 client = client or cls.shares_v2_client
714 replica = client.promote_share_replica(replica_id)
715 client.wait_for_share_replica_status(
716 replica["id"],
717 constants.REPLICATION_STATE_ACTIVE,
718 status_attr="replica_state")
719 return replica
720
yogeshdb32f462016-09-28 15:09:50 -0400721 def _get_access_rule_data_from_config(self):
722 """Get the first available access type/to combination from config.
723
724 This method opportunistically picks the first configured protocol
725 to create the share. Do not use this method in tests where you need
726 to test depth and breadth in the access types and access recipients.
727 """
728 protocol = self.shares_v2_client.share_protocol
729
730 if protocol in CONF.share.enable_ip_rules_for_protocols:
731 access_type = "ip"
732 access_to = utils.rand_ip()
733 elif protocol in CONF.share.enable_user_rules_for_protocols:
734 access_type = "user"
735 access_to = CONF.share.username_for_user_rules
736 elif protocol in CONF.share.enable_cert_rules_for_protocols:
737 access_type = "cert"
738 access_to = "client3.com"
739 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
740 access_type = "cephx"
741 access_to = "eve"
742 else:
743 message = "Unrecognized protocol and access rules configuration."
744 raise self.skipException(message)
745
746 return access_type, access_to
747
Yogeshbdb88102015-09-29 23:41:02 -0400748 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200749 def create_share_network(cls, client=None,
750 cleanup_in_class=False, **kwargs):
751 if client is None:
752 client = cls.shares_client
753 share_network = client.create_share_network(**kwargs)
754 resource = {
755 "type": "share_network",
756 "id": share_network["id"],
757 "client": client,
758 }
759 if cleanup_in_class:
760 cls.class_resources.insert(0, resource)
761 else:
762 cls.method_resources.insert(0, resource)
763 return share_network
764
765 @classmethod
766 def create_security_service(cls, ss_type="ldap", client=None,
767 cleanup_in_class=False, **kwargs):
768 if client is None:
769 client = cls.shares_client
770 security_service = client.create_security_service(ss_type, **kwargs)
771 resource = {
772 "type": "security_service",
773 "id": security_service["id"],
774 "client": client,
775 }
776 if cleanup_in_class:
777 cls.class_resources.insert(0, resource)
778 else:
779 cls.method_resources.insert(0, resource)
780 return security_service
781
782 @classmethod
783 def create_share_type(cls, name, is_public=True, client=None,
784 cleanup_in_class=True, **kwargs):
785 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200786 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200787 share_type = client.create_share_type(name, is_public, **kwargs)
788 resource = {
789 "type": "share_type",
790 "id": share_type["share_type"]["id"],
791 "client": client,
792 }
793 if cleanup_in_class:
794 cls.class_resources.insert(0, resource)
795 else:
796 cls.method_resources.insert(0, resource)
797 return share_type
798
799 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400800 def add_extra_specs_to_dict(extra_specs=None):
801 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300802 dhss = six.text_type(CONF.share.multitenancy_enabled)
803 snapshot_support = six.text_type(
804 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400805 create_from_snapshot_support = six.text_type(
806 CONF.share.capability_create_share_from_snapshot_support)
Clinton Knight7f16b8c2016-06-08 13:46:51 -0700807 revert_to_snapshot_support = six.text_type(
808 CONF.share.capability_revert_to_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400809
810 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300811 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200812 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400813
814 optional = {
815 "snapshot_support": snapshot_support,
816 "create_share_from_snapshot_support": create_from_snapshot_support,
Clinton Knight7f16b8c2016-06-08 13:46:51 -0700817 "revert_to_snapshot_support": revert_to_snapshot_support,
Clinton Knight4699a8c2016-08-16 22:36:13 -0400818 }
819 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
820 # required extra-spec
821 extra_specs_dict.update(optional)
822
Marc Koderer0abc93b2015-07-15 09:18:35 +0200823 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400824 extra_specs_dict.update(extra_specs)
825
826 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200827
828 @classmethod
829 def clear_isolated_creds(cls, creds=None):
830 if creds is None:
831 creds = cls.method_isolated_creds
832 for ic in creds:
833 if "deleted" not in ic.keys():
834 ic["deleted"] = False
835 if not ic["deleted"]:
836 with handle_cleanup_exceptions():
837 ic["method"]()
838 ic["deleted"] = True
839
840 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400841 def clear_share_replicas(cls, share_id, client=None):
842 client = client or cls.shares_v2_client
843 share_replicas = client.list_share_replicas(
844 share_id=share_id)
845
846 for replica in share_replicas:
847 try:
848 cls.delete_share_replica(replica['id'])
849 except exceptions.BadRequest:
850 # Ignore the exception due to deletion of last active replica
851 pass
852
853 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200854 def clear_resources(cls, resources=None):
855 """Deletes resources, that were created in test suites.
856
857 This method tries to remove resources from resource list,
858 if it is not found, assumed it was deleted in test itself.
859 It is expected, that all resources were added as LIFO
860 due to restriction of deletion resources, that is in the chain.
861
862 :param resources: dict with keys 'type','id','client' and 'deleted'
863 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200864 if resources is None:
865 resources = cls.method_resources
866 for res in resources:
867 if "deleted" not in res.keys():
868 res["deleted"] = False
869 if "client" not in res.keys():
870 res["client"] = cls.shares_client
871 if not(res["deleted"]):
872 res_id = res['id']
873 client = res["client"]
874 with handle_cleanup_exceptions():
875 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400876 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400877 share_group_id = res.get('share_group_id')
878 if share_group_id:
879 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400880 client.delete_share(res_id, params=params)
881 else:
882 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200883 client.wait_for_resource_deletion(share_id=res_id)
884 elif res["type"] is "snapshot":
885 client.delete_snapshot(res_id)
886 client.wait_for_resource_deletion(snapshot_id=res_id)
887 elif res["type"] is "share_network":
888 client.delete_share_network(res_id)
889 client.wait_for_resource_deletion(sn_id=res_id)
890 elif res["type"] is "security_service":
891 client.delete_security_service(res_id)
892 client.wait_for_resource_deletion(ss_id=res_id)
893 elif res["type"] is "share_type":
894 client.delete_share_type(res_id)
895 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400896 elif res["type"] is "share_group":
897 client.delete_share_group(res_id)
898 client.wait_for_resource_deletion(
899 share_group_id=res_id)
900 elif res["type"] is "share_group_type":
901 client.delete_share_group_type(res_id)
902 client.wait_for_resource_deletion(
903 share_group_type_id=res_id)
904 elif res["type"] is "share_group_snapshot":
905 client.delete_share_group_snapshot(res_id)
906 client.wait_for_resource_deletion(
907 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400908 elif res["type"] is "share_replica":
909 client.delete_share_replica(res_id)
910 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200911 else:
huayue97bacbf2016-01-04 09:57:39 +0800912 LOG.warning("Provided unsupported resource type for "
913 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200914 res["deleted"] = True
915
916 @classmethod
917 def generate_share_network_data(self):
918 data = {
919 "name": data_utils.rand_name("sn-name"),
920 "description": data_utils.rand_name("sn-desc"),
921 "neutron_net_id": data_utils.rand_name("net-id"),
922 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
923 }
924 return data
925
926 @classmethod
927 def generate_security_service_data(self):
928 data = {
929 "name": data_utils.rand_name("ss-name"),
930 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200931 "dns_ip": utils.rand_ip(),
932 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200933 "domain": data_utils.rand_name("ss-domain"),
934 "user": data_utils.rand_name("ss-user"),
935 "password": data_utils.rand_name("ss-password"),
936 }
937 return data
938
939 # Useful assertions
940 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
941 """Assert two dicts are equivalent.
942
943 This is a 'deep' match in the sense that it handles nested
944 dictionaries appropriately.
945
946 NOTE:
947
948 If you don't care (or don't know) a given value, you can specify
949 the string DONTCARE as the value. This will cause that dict-item
950 to be skipped.
951
952 """
953 def raise_assertion(msg):
954 d1str = str(d1)
955 d2str = str(d2)
956 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
957 'd2: %(d2str)s' %
958 {"msg": msg, "d1str": d1str, "d2str": d2str})
959 raise AssertionError(base_msg)
960
961 d1keys = set(d1.keys())
962 d2keys = set(d2.keys())
963 if d1keys != d2keys:
964 d1only = d1keys - d2keys
965 d2only = d2keys - d1keys
966 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
967 'Keys in d2 and not d1: %(d2only)s' %
968 {"d1only": d1only, "d2only": d2only})
969
970 for key in d1keys:
971 d1value = d1[key]
972 d2value = d2[key]
973 try:
974 error = abs(float(d1value) - float(d2value))
975 within_tolerance = error <= tolerance
976 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +0900977 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +0200978 # ValueError if arg is a str, TypeError if it's something else
979 # (like None)
980 within_tolerance = False
981
982 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
983 self.assertDictMatch(d1value, d2value)
984 elif 'DONTCARE' in (d1value, d2value):
985 continue
986 elif approx_equal and within_tolerance:
987 continue
988 elif d1value != d2value:
989 raise_assertion("d1['%(key)s']=%(d1value)s != "
990 "d2['%(key)s']=%(d2value)s" %
991 {
992 "key": key,
993 "d1value": d1value,
994 "d2value": d2value
995 })
996
997
998class BaseSharesAltTest(BaseSharesTest):
999 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001000 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001001
1002
1003class BaseSharesAdminTest(BaseSharesTest):
1004 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001005 credentials = ('admin', )
1006
1007
1008class BaseSharesMixedTest(BaseSharesTest):
1009 """Base test case class for all Shares API tests with all user roles."""
1010 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001011
1012 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001013 def setup_clients(cls):
1014 super(BaseSharesMixedTest, cls).setup_clients()
1015 cls.admin_shares_client = shares_client.SharesClient(
1016 cls.os_admin.auth_provider)
1017 cls.admin_shares_v2_client = shares_v2_client.SharesV2Client(
1018 cls.os_admin.auth_provider)
1019 cls.alt_shares_client = shares_client.SharesClient(
1020 cls.os_alt.auth_provider)
1021 cls.alt_shares_v2_client = shares_v2_client.SharesV2Client(
1022 cls.os_alt.auth_provider)
1023
1024 if CONF.share.multitenancy_enabled:
1025 admin_share_network_id = cls.provide_share_network(
1026 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1027 cls.admin_shares_client.share_network_id = admin_share_network_id
1028 cls.admin_shares_v2_client.share_network_id = (
1029 admin_share_network_id)
1030
1031 alt_share_network_id = cls.provide_share_network(
1032 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1033 cls.alt_shares_client.share_network_id = alt_share_network_id
1034 cls.alt_shares_v2_client.share_network_id = alt_share_network_id