blob: 764a8529a9b5a3ac97457026154cfee02eb02149 [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 Ponomaryove92f09f2017-03-16 17:25:47 +0300598 if (group_specs is None and
599 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300600 group_specs = {
601 'consistent_snapshot_support': (
602 CONF.share.capability_sg_consistent_snapshot_support),
603 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400604 share_group_type = client.create_share_group_type(
605 name=name,
606 share_types=share_types,
607 is_public=is_public,
608 group_specs=group_specs,
609 **kwargs)
610 resource = {
611 "type": "share_group_type",
612 "id": share_group_type["id"],
613 "client": client,
614 }
615 if cleanup_in_class:
616 cls.class_resources.insert(0, resource)
617 else:
618 cls.method_resources.insert(0, resource)
619 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400620
621 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200622 def create_snapshot_wait_for_active(cls, share_id, name=None,
623 description=None, force=False,
624 client=None, cleanup_in_class=True):
625 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400626 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200627 if description is None:
628 description = "Tempest's snapshot"
629 snapshot = client.create_snapshot(share_id, name, description, force)
630 resource = {
631 "type": "snapshot",
632 "id": snapshot["id"],
633 "client": client,
634 }
635 if cleanup_in_class:
636 cls.class_resources.insert(0, resource)
637 else:
638 cls.method_resources.insert(0, resource)
639 client.wait_for_snapshot_status(snapshot["id"], "available")
640 return snapshot
641
642 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400643 def create_share_group_snapshot_wait_for_active(
644 cls, share_group_id, name=None, description=None, client=None,
645 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400646 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400647 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400648 description = "Tempest's share group snapshot"
649 sg_snapshot = client.create_share_group_snapshot(
650 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400651 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400652 "type": "share_group_snapshot",
653 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400654 "client": client,
655 }
656 if cleanup_in_class:
657 cls.class_resources.insert(0, resource)
658 else:
659 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400660 client.wait_for_share_group_snapshot_status(
661 sg_snapshot["id"], "available")
662 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400663
664 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400665 def get_availability_zones(cls, client=None):
666 """List the availability zones for "manila-share" services
667
668 that are currently in "up" state.
669 """
670 client = client or cls.shares_v2_client
671 cls.services = client.list_services()
672 zones = [service['zone'] for service in cls.services if
673 service['binary'] == "manila-share" and
674 service['state'] == 'up']
675 return zones
676
Yogesh1f931ff2015-09-29 23:41:02 -0400677 def get_pools_for_replication_domain(self):
678 # Get the list of pools for the replication domain
679 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500680 instance_host = self.admin_client.get_share(
681 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400682 host_pool = [p for p in pools if p['name'] == instance_host][0]
683 rep_domain = host_pool['capabilities']['replication_domain']
684 pools_in_rep_domain = [p for p in pools if p['capabilities'][
685 'replication_domain'] == rep_domain]
686 return rep_domain, pools_in_rep_domain
687
Yogeshbdb88102015-09-29 23:41:02 -0400688 @classmethod
689 def create_share_replica(cls, share_id, availability_zone, client=None,
690 cleanup_in_class=False, cleanup=True):
691 client = client or cls.shares_v2_client
692 replica = client.create_share_replica(share_id, availability_zone)
693 resource = {
694 "type": "share_replica",
695 "id": replica["id"],
696 "client": client,
697 "share_id": share_id,
698 }
699 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
700 if cleanup:
701 if cleanup_in_class:
702 cls.class_resources.insert(0, resource)
703 else:
704 cls.method_resources.insert(0, resource)
705 client.wait_for_share_replica_status(
706 replica["id"], constants.STATUS_AVAILABLE)
707 return replica
708
709 @classmethod
710 def delete_share_replica(cls, replica_id, client=None):
711 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400712 try:
713 client.delete_share_replica(replica_id)
714 client.wait_for_resource_deletion(replica_id=replica_id)
715 except exceptions.NotFound:
716 pass
Yogeshbdb88102015-09-29 23:41:02 -0400717
718 @classmethod
719 def promote_share_replica(cls, replica_id, client=None):
720 client = client or cls.shares_v2_client
721 replica = client.promote_share_replica(replica_id)
722 client.wait_for_share_replica_status(
723 replica["id"],
724 constants.REPLICATION_STATE_ACTIVE,
725 status_attr="replica_state")
726 return replica
727
yogeshdb32f462016-09-28 15:09:50 -0400728 def _get_access_rule_data_from_config(self):
729 """Get the first available access type/to combination from config.
730
731 This method opportunistically picks the first configured protocol
732 to create the share. Do not use this method in tests where you need
733 to test depth and breadth in the access types and access recipients.
734 """
735 protocol = self.shares_v2_client.share_protocol
736
737 if protocol in CONF.share.enable_ip_rules_for_protocols:
738 access_type = "ip"
739 access_to = utils.rand_ip()
740 elif protocol in CONF.share.enable_user_rules_for_protocols:
741 access_type = "user"
742 access_to = CONF.share.username_for_user_rules
743 elif protocol in CONF.share.enable_cert_rules_for_protocols:
744 access_type = "cert"
745 access_to = "client3.com"
746 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
747 access_type = "cephx"
748 access_to = "eve"
749 else:
750 message = "Unrecognized protocol and access rules configuration."
751 raise self.skipException(message)
752
753 return access_type, access_to
754
Yogeshbdb88102015-09-29 23:41:02 -0400755 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200756 def create_share_network(cls, client=None,
757 cleanup_in_class=False, **kwargs):
758 if client is None:
759 client = cls.shares_client
760 share_network = client.create_share_network(**kwargs)
761 resource = {
762 "type": "share_network",
763 "id": share_network["id"],
764 "client": client,
765 }
766 if cleanup_in_class:
767 cls.class_resources.insert(0, resource)
768 else:
769 cls.method_resources.insert(0, resource)
770 return share_network
771
772 @classmethod
773 def create_security_service(cls, ss_type="ldap", client=None,
774 cleanup_in_class=False, **kwargs):
775 if client is None:
776 client = cls.shares_client
777 security_service = client.create_security_service(ss_type, **kwargs)
778 resource = {
779 "type": "security_service",
780 "id": security_service["id"],
781 "client": client,
782 }
783 if cleanup_in_class:
784 cls.class_resources.insert(0, resource)
785 else:
786 cls.method_resources.insert(0, resource)
787 return security_service
788
789 @classmethod
790 def create_share_type(cls, name, is_public=True, client=None,
791 cleanup_in_class=True, **kwargs):
792 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200793 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200794 share_type = client.create_share_type(name, is_public, **kwargs)
795 resource = {
796 "type": "share_type",
797 "id": share_type["share_type"]["id"],
798 "client": client,
799 }
800 if cleanup_in_class:
801 cls.class_resources.insert(0, resource)
802 else:
803 cls.method_resources.insert(0, resource)
804 return share_type
805
806 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400807 def add_extra_specs_to_dict(extra_specs=None):
808 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300809 dhss = six.text_type(CONF.share.multitenancy_enabled)
810 snapshot_support = six.text_type(
811 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400812 create_from_snapshot_support = six.text_type(
813 CONF.share.capability_create_share_from_snapshot_support)
814
815 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300816 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200817 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400818
819 optional = {
820 "snapshot_support": snapshot_support,
821 "create_share_from_snapshot_support": create_from_snapshot_support,
822 }
823 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
824 # required extra-spec
825 extra_specs_dict.update(optional)
826
Marc Koderer0abc93b2015-07-15 09:18:35 +0200827 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400828 extra_specs_dict.update(extra_specs)
829
830 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200831
832 @classmethod
833 def clear_isolated_creds(cls, creds=None):
834 if creds is None:
835 creds = cls.method_isolated_creds
836 for ic in creds:
837 if "deleted" not in ic.keys():
838 ic["deleted"] = False
839 if not ic["deleted"]:
840 with handle_cleanup_exceptions():
841 ic["method"]()
842 ic["deleted"] = True
843
844 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400845 def clear_share_replicas(cls, share_id, client=None):
846 client = client or cls.shares_v2_client
847 share_replicas = client.list_share_replicas(
848 share_id=share_id)
849
850 for replica in share_replicas:
851 try:
852 cls.delete_share_replica(replica['id'])
853 except exceptions.BadRequest:
854 # Ignore the exception due to deletion of last active replica
855 pass
856
857 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200858 def clear_resources(cls, resources=None):
859 """Deletes resources, that were created in test suites.
860
861 This method tries to remove resources from resource list,
862 if it is not found, assumed it was deleted in test itself.
863 It is expected, that all resources were added as LIFO
864 due to restriction of deletion resources, that is in the chain.
865
866 :param resources: dict with keys 'type','id','client' and 'deleted'
867 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200868 if resources is None:
869 resources = cls.method_resources
870 for res in resources:
871 if "deleted" not in res.keys():
872 res["deleted"] = False
873 if "client" not in res.keys():
874 res["client"] = cls.shares_client
875 if not(res["deleted"]):
876 res_id = res['id']
877 client = res["client"]
878 with handle_cleanup_exceptions():
879 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400880 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400881 share_group_id = res.get('share_group_id')
882 if share_group_id:
883 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400884 client.delete_share(res_id, params=params)
885 else:
886 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200887 client.wait_for_resource_deletion(share_id=res_id)
888 elif res["type"] is "snapshot":
889 client.delete_snapshot(res_id)
890 client.wait_for_resource_deletion(snapshot_id=res_id)
891 elif res["type"] is "share_network":
892 client.delete_share_network(res_id)
893 client.wait_for_resource_deletion(sn_id=res_id)
894 elif res["type"] is "security_service":
895 client.delete_security_service(res_id)
896 client.wait_for_resource_deletion(ss_id=res_id)
897 elif res["type"] is "share_type":
898 client.delete_share_type(res_id)
899 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400900 elif res["type"] is "share_group":
901 client.delete_share_group(res_id)
902 client.wait_for_resource_deletion(
903 share_group_id=res_id)
904 elif res["type"] is "share_group_type":
905 client.delete_share_group_type(res_id)
906 client.wait_for_resource_deletion(
907 share_group_type_id=res_id)
908 elif res["type"] is "share_group_snapshot":
909 client.delete_share_group_snapshot(res_id)
910 client.wait_for_resource_deletion(
911 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400912 elif res["type"] is "share_replica":
913 client.delete_share_replica(res_id)
914 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200915 else:
huayue97bacbf2016-01-04 09:57:39 +0800916 LOG.warning("Provided unsupported resource type for "
917 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200918 res["deleted"] = True
919
920 @classmethod
921 def generate_share_network_data(self):
922 data = {
923 "name": data_utils.rand_name("sn-name"),
924 "description": data_utils.rand_name("sn-desc"),
925 "neutron_net_id": data_utils.rand_name("net-id"),
926 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
927 }
928 return data
929
930 @classmethod
931 def generate_security_service_data(self):
932 data = {
933 "name": data_utils.rand_name("ss-name"),
934 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200935 "dns_ip": utils.rand_ip(),
936 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200937 "domain": data_utils.rand_name("ss-domain"),
938 "user": data_utils.rand_name("ss-user"),
939 "password": data_utils.rand_name("ss-password"),
940 }
941 return data
942
943 # Useful assertions
944 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
945 """Assert two dicts are equivalent.
946
947 This is a 'deep' match in the sense that it handles nested
948 dictionaries appropriately.
949
950 NOTE:
951
952 If you don't care (or don't know) a given value, you can specify
953 the string DONTCARE as the value. This will cause that dict-item
954 to be skipped.
955
956 """
957 def raise_assertion(msg):
958 d1str = str(d1)
959 d2str = str(d2)
960 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
961 'd2: %(d2str)s' %
962 {"msg": msg, "d1str": d1str, "d2str": d2str})
963 raise AssertionError(base_msg)
964
965 d1keys = set(d1.keys())
966 d2keys = set(d2.keys())
967 if d1keys != d2keys:
968 d1only = d1keys - d2keys
969 d2only = d2keys - d1keys
970 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
971 'Keys in d2 and not d1: %(d2only)s' %
972 {"d1only": d1only, "d2only": d2only})
973
974 for key in d1keys:
975 d1value = d1[key]
976 d2value = d2[key]
977 try:
978 error = abs(float(d1value) - float(d2value))
979 within_tolerance = error <= tolerance
980 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +0900981 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +0200982 # ValueError if arg is a str, TypeError if it's something else
983 # (like None)
984 within_tolerance = False
985
986 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
987 self.assertDictMatch(d1value, d2value)
988 elif 'DONTCARE' in (d1value, d2value):
989 continue
990 elif approx_equal and within_tolerance:
991 continue
992 elif d1value != d2value:
993 raise_assertion("d1['%(key)s']=%(d1value)s != "
994 "d2['%(key)s']=%(d2value)s" %
995 {
996 "key": key,
997 "d1value": d1value,
998 "d2value": d2value
999 })
1000
Alex Meadeba8a1602016-05-06 09:33:09 -04001001 def create_user_message(self):
1002 """Trigger a 'no valid host' situation to generate a message."""
1003 extra_specs = {
1004 'vendor_name': 'foobar',
1005 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1006 }
1007 share_type_name = data_utils.rand_name("share-type")
1008
1009 bogus_type = self.create_share_type(
1010 name=share_type_name,
1011 extra_specs=extra_specs)['share_type']
1012
1013 params = {'share_type_id': bogus_type['id'],
1014 'share_network_id': self.shares_v2_client.share_network_id}
1015 share = self.shares_v2_client.create_share(**params)
1016 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1017 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1018 return self.shares_v2_client.wait_for_message(share['id'])
1019
Marc Koderer0abc93b2015-07-15 09:18:35 +02001020
1021class BaseSharesAltTest(BaseSharesTest):
1022 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001023 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001024
1025
1026class BaseSharesAdminTest(BaseSharesTest):
1027 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001028 credentials = ('admin', )
1029
1030
1031class BaseSharesMixedTest(BaseSharesTest):
1032 """Base test case class for all Shares API tests with all user roles."""
1033 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001034
1035 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001036 def setup_clients(cls):
1037 super(BaseSharesMixedTest, cls).setup_clients()
1038 cls.admin_shares_client = shares_client.SharesClient(
1039 cls.os_admin.auth_provider)
1040 cls.admin_shares_v2_client = shares_v2_client.SharesV2Client(
1041 cls.os_admin.auth_provider)
1042 cls.alt_shares_client = shares_client.SharesClient(
1043 cls.os_alt.auth_provider)
1044 cls.alt_shares_v2_client = shares_v2_client.SharesV2Client(
1045 cls.os_alt.auth_provider)
1046
1047 if CONF.share.multitenancy_enabled:
1048 admin_share_network_id = cls.provide_share_network(
1049 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1050 cls.admin_shares_client.share_network_id = admin_share_network_id
1051 cls.admin_shares_v2_client.share_network_id = (
1052 admin_share_network_id)
1053
1054 alt_share_network_id = cls.provide_share_network(
1055 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1056 cls.alt_shares_client.share_network_id = alt_share_network_id
1057 cls.alt_shares_v2_client.share_network_id = alt_share_network_id