blob: e81c5d4a46dec963e7e09d85192bbdba539f63a4 [file] [log] [blame]
Marc Koderer0abc93b2015-07-15 09:18:35 +02001# Copyright 2014 Mirantis Inc.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import copy
17import inspect
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030018import re
Marc Koderer0abc93b2015-07-15 09:18:35 +020019import traceback
20
21from oslo_concurrency import lockutils
22from oslo_log import log
23import six
Sam Wanc7b7f1f2015-11-25 00:22:28 -050024from tempest.common import credentials_factory as common_creds
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010025
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020026from tempest import config
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +010027from tempest.lib.common import dynamic_creds
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050028from tempest.lib.common.utils import data_utils
29from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020030from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020031
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010032from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040033from manila_tempest_tests.common import constants
Marc Koderer0abc93b2015-07-15 09:18:35 +020034from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020035from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020036
37CONF = config.CONF
38LOG = log.getLogger(__name__)
39
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030040# Test tags related to test direction
41TAG_POSITIVE = "positive"
42TAG_NEGATIVE = "negative"
43
44# Test tags related to service involvement
45TAG_API = "api"
46TAG_BACKEND = "backend"
47TAG_API_WITH_BACKEND = "api_with_backend"
48
49TAGS_MAPPER = {
50 "p": TAG_POSITIVE,
51 "n": TAG_NEGATIVE,
52 "a": TAG_API,
53 "b": TAG_BACKEND,
54 "ab": TAG_API_WITH_BACKEND,
55}
56TAGS_PATTERN = re.compile(
57 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
58 TAGS_MAPPER)
59
60
61def verify_test_has_appropriate_tags(self):
62 if not TAGS_PATTERN.match(self.id()):
63 msg = (
64 "Required attributes either not set or set improperly. "
65 "Two test attributes are expected:\n"
66 " - one of '%(p)s' or '%(n)s' and \n"
67 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
68 ) % TAGS_MAPPER
69 raise self.failureException(msg)
70
Marc Koderer0abc93b2015-07-15 09:18:35 +020071
72class handle_cleanup_exceptions(object):
73 """Handle exceptions raised with cleanup operations.
74
75 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
76 are raised.
77 Suppress all other exceptions only in case config opt
78 'suppress_errors_in_cleanup' in config group 'share' is True.
79 """
80
81 def __enter__(self):
82 return self
83
84 def __exit__(self, exc_type, exc_value, exc_traceback):
85 if not (isinstance(exc_value,
86 (exceptions.NotFound, exceptions.Forbidden)) or
87 CONF.share.suppress_errors_in_cleanup):
88 return False # Do not suppress error if any
89 if exc_traceback:
90 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080091 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020092 return True # Suppress error if any
93
94
95def network_synchronized(f):
96
97 def wrapped_func(self, *args, **kwargs):
98 with_isolated_creds = True if len(args) > 2 else False
99 no_lock_required = kwargs.get(
100 "isolated_creds_client", with_isolated_creds)
101 if no_lock_required:
102 # Usage of not reusable network. No need in lock.
103 return f(self, *args, **kwargs)
104
105 # Use lock assuming reusage of common network.
106 @lockutils.synchronized("manila_network_lock", external=True)
107 def source_func(self, *args, **kwargs):
108 return f(self, *args, **kwargs)
109
110 return source_func(self, *args, **kwargs)
111
112 return wrapped_func
113
114
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200115skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500116skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200117
118
Marc Koderer0abc93b2015-07-15 09:18:35 +0200119class BaseSharesTest(test.BaseTestCase):
120 """Base test case class for all Manila API tests."""
121
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300122 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200123 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200124 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200125
126 # Will be cleaned up in resource_cleanup
127 class_resources = []
128
129 # Will be cleaned up in tearDown method
130 method_resources = []
131
132 # Will be cleaned up in resource_cleanup
133 class_isolated_creds = []
134
135 # Will be cleaned up in tearDown method
136 method_isolated_creds = []
137
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100138 # NOTE(andreaf) Override the client manager class to be used, so that
139 # a stable class is used, which includes plugin registered services as well
140 client_manager = clients.Clients
141
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200142 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200143 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200144 raise self.skipException(
145 "Microversion '%s' is not supported." % microversion)
146
Xing Yang69b00b52015-11-22 16:10:44 -0500147 def skip_if_microversion_lt(self, microversion):
148 if utils.is_microversion_lt(CONF.share.max_api_microversion,
149 microversion):
150 raise self.skipException(
151 "Microversion must be greater than or equal to '%s'." %
152 microversion)
153
Marc Koderer0abc93b2015-07-15 09:18:35 +0200154 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500155 def _get_dynamic_creds(cls, name, network_resources=None):
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100156 identity_version = CONF.identity.auth_version
157 if identity_version == 'v3':
158 identity_uri = CONF.identity.uri_v3
159 identity_admin_endpoint_type = CONF.identity.v3_endpoint_type
160 elif identity_version == 'v2':
161 identity_uri = CONF.identity.uri
162 identity_admin_endpoint_type = CONF.identity.v2_admin_endpoint_type
163
Tom Barron4b8834a2017-02-02 11:02:20 -0500164 return dynamic_creds.DynamicCredentialProvider(
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100165 identity_version=identity_version,
Tom Barron4b8834a2017-02-02 11:02:20 -0500166 name=name,
167 network_resources=network_resources,
168 credentials_domain=CONF.auth.default_credentials_domain_name,
169 admin_role=CONF.identity.admin_role,
170 admin_creds=common_creds.get_configured_admin_credentials(),
171 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
172 identity_admin_role=CONF.identity.admin_role,
173 extra_roles=None,
174 neutron_available=CONF.service_available.neutron,
175 create_networks=(
176 CONF.share.create_networks_when_multitenancy_enabled),
177 project_network_cidr=CONF.network.project_network_cidr,
178 project_network_mask_bits=CONF.network.project_network_mask_bits,
179 public_network_id=CONF.network.public_network_id,
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100180 resource_prefix=CONF.resources_prefix,
181 identity_admin_endpoint_type=identity_admin_endpoint_type,
182 identity_uri=identity_uri)
Tom Barron4b8834a2017-02-02 11:02:20 -0500183
184 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200185 def get_client_with_isolated_creds(cls,
186 name=None,
187 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400188 cleanup_in_class=False,
189 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200190 """Creates isolated creds.
191
192 :param name: name, will be used for naming ic and related stuff
193 :param type_of_creds: admin, alt or primary
194 :param cleanup_in_class: defines place where to delete
195 :returns: SharesClient -- shares client with isolated creds.
196 :returns: To client added dict attr 'creds' with
197 :returns: key elements 'tenant' and 'user'.
198 """
199 if name is None:
200 # Get name of test method
201 name = inspect.stack()[1][3]
202 if len(name) > 32:
203 name = name[0:32]
204
205 # Choose type of isolated creds
Tom Barron4b8834a2017-02-02 11:02:20 -0500206 ic = cls._get_dynamic_creds(name)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200207 if "admin" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200208 creds = ic.get_admin_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200209 elif "alt" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200210 creds = ic.get_alt_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200211 else:
Marc Koderer5880b362016-07-06 10:59:07 +0200212 creds = ic.get_credentials(type_of_creds).credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200213 ic.type_of_creds = type_of_creds
214
215 # create client with isolated creds
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100216 os = clients.Clients(creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400217 if client_version == '1':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100218 client = os.share_v1.SharesClient()
Clinton Knighte5c8f092015-08-27 15:00:23 -0400219 elif client_version == '2':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100220 client = os.share_v2.SharesV2Client()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200221
222 # Set place where will be deleted isolated creds
223 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200224 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200225 "deleted": False,
226 }
227 if cleanup_in_class:
228 cls.class_isolated_creds.insert(0, ic_res)
229 else:
230 cls.method_isolated_creds.insert(0, ic_res)
231
232 # Provide share network
233 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300234 if (not CONF.service_available.neutron and
235 CONF.share.create_networks_when_multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200236 raise cls.skipException("Neutron support is required")
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100237 nc = os.network.NetworksClient()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200238 share_network_id = cls.provide_share_network(client, nc, ic)
239 client.share_network_id = share_network_id
240 resource = {
241 "type": "share_network",
242 "id": client.share_network_id,
243 "client": client,
244 }
245 if cleanup_in_class:
246 cls.class_resources.insert(0, resource)
247 else:
248 cls.method_resources.insert(0, resource)
249 return client
250
251 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000252 def skip_checks(cls):
253 super(BaseSharesTest, cls).skip_checks()
254 if not CONF.service_available.manila:
255 raise cls.skipException("Manila support is required")
256
257 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200258 def verify_nonempty(cls, *args):
259 if not all(args):
260 msg = "Missing API credentials in configuration."
261 raise cls.skipException(msg)
262
263 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300264 def setup_clients(cls):
265 super(BaseSharesTest, cls).setup_clients()
266 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100267 # Initialise share clients for test credentials
268 cls.shares_client = os.share_v1.SharesClient()
269 cls.shares_v2_client = os.share_v2.SharesV2Client()
270 # Initialise network clients for test credentials
271 if CONF.service_available.neutron:
272 cls.networks_client = os.network.NetworksClient()
273 cls.subnets_client = os.network.SubnetsClient()
274 else:
275 cls.networks_client = None
276 cls.subnets_client = None
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300277
278 if CONF.identity.auth_version == 'v3':
279 project_id = os.auth_provider.auth_data[1]['project']['id']
280 else:
281 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
282 cls.tenant_id = project_id
283 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
284
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300285 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300286 if (not CONF.service_available.neutron and
287 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300288 raise cls.skipException("Neutron support is required")
289 share_network_id = cls.provide_share_network(
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100290 cls.shares_v2_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300291 cls.shares_client.share_network_id = share_network_id
292 cls.shares_v2_client.share_network_id = share_network_id
293
294 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200295 def resource_setup(cls):
296 if not (any(p in CONF.share.enable_protocols
297 for p in cls.protocols) and
298 CONF.service_available.manila):
299 skip_msg = "Manila is disabled"
300 raise cls.skipException(skip_msg)
301 super(BaseSharesTest, cls).resource_setup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200302
303 def setUp(self):
304 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200305 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200306 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300307 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200308
309 @classmethod
310 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200311 cls.clear_resources(cls.class_resources)
312 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400313 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200314
315 @classmethod
316 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200317 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300318 isolated_creds_client=None,
319 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200320 """Used for finding/creating share network for multitenant driver.
321
322 This method creates/gets entity share-network for one tenant. This
323 share-network will be used for creation of service vm.
324
325 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200326 :param networks_client: network client from same tenant as shares
327 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200328 If provided, then its networking will be used if needed.
329 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300330 :param ignore_multitenancy_config: provide a share network regardless
331 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200332 :returns: str -- share network id for shares_client tenant
333 :returns: None -- if single-tenant driver used
334 """
335
336 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300337 search_word = "reusable"
338 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200339
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300340 if (not ignore_multitenancy_config and
341 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200342 # Assumed usage of a single-tenant driver
343 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200344 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300345 if sc.share_network_id:
346 # Share-network already exists, use it
347 share_network_id = sc.share_network_id
348 elif not CONF.share.create_networks_when_multitenancy_enabled:
349 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200350
351 # Try get suitable share-network
352 share_networks = sc.list_share_networks_with_detail()
353 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300354 if (sn["neutron_net_id"] is None and
355 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200356 sn["name"] and search_word in sn["name"]):
357 share_network_id = sn["id"]
358 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200359
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300360 # Create new share-network if one was not found
361 if share_network_id is None:
362 sn_desc = "This share-network was created by tempest"
363 sn = sc.create_share_network(name=sn_name,
364 description=sn_desc)
365 share_network_id = sn["id"]
366 else:
367 net_id = subnet_id = share_network_id = None
368
369 if not isolated_creds_client:
370 # Search for networks, created in previous runs
371 service_net_name = "share-service"
372 networks = networks_client.list_networks()
373 if "networks" in networks.keys():
374 networks = networks["networks"]
375 for network in networks:
376 if (service_net_name in network["name"] and
377 sc.tenant_id == network['tenant_id']):
378 net_id = network["id"]
379 if len(network["subnets"]) > 0:
380 subnet_id = network["subnets"][0]
381 break
382
383 # Create suitable network
384 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500385 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300386 net_data = ic._create_network_resources(sc.tenant_id)
387 network, subnet, router = net_data
388 net_id = network["id"]
389 subnet_id = subnet["id"]
390
391 # Try get suitable share-network
392 share_networks = sc.list_share_networks_with_detail()
393 for sn in share_networks:
394 if (net_id == sn["neutron_net_id"] and
395 subnet_id == sn["neutron_subnet_id"] and
396 sn["name"] and search_word in sn["name"]):
397 share_network_id = sn["id"]
398 break
399 else:
400 sn_name = "autogenerated_by_tempest_for_isolated_creds"
401 # Use precreated network and subnet from isolated creds
402 net_id = isolated_creds_client.get_credentials(
403 isolated_creds_client.type_of_creds).network['id']
404 subnet_id = isolated_creds_client.get_credentials(
405 isolated_creds_client.type_of_creds).subnet['id']
406
407 # Create suitable share-network
408 if share_network_id is None:
409 sn_desc = "This share-network was created by tempest"
410 sn = sc.create_share_network(name=sn_name,
411 description=sn_desc,
412 neutron_net_id=net_id,
413 neutron_subnet_id=subnet_id)
414 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200415
416 return share_network_id
417
418 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300419 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200420 snapshot_id=None, description=None, metadata=None,
421 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400422 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400423 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300424 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200425 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400426 share_network_id = (share_network_id or
427 CONF.share.share_network_id or
428 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200429 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300430 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400431 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200432 'share_protocol': share_protocol,
433 'size': size,
434 'name': name,
435 'snapshot_id': snapshot_id,
436 'description': description,
437 'metadata': metadata,
438 'share_network_id': share_network_id,
439 'share_type_id': share_type_id,
440 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400441 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400442 if share_group_id:
443 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400444
Marc Koderer0abc93b2015-07-15 09:18:35 +0200445 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400446 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400447 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200448 cleanup_list = (cls.class_resources if cleanup_in_class else
449 cls.method_resources)
450 cleanup_list.insert(0, resource)
451 return share
452
453 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300454 def migrate_share(
455 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200456 force_host_assisted_migration=False, writable=False,
457 nondisruptive=False, preserve_metadata=False,
458 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300459 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400460 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300461 client.migrate_share(
462 share_id, dest_host,
463 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200464 writable=writable, preserve_metadata=preserve_metadata,
465 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300466 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300467 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200468 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300469 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200470 return share
471
472 @classmethod
473 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
474 client = client or cls.shares_v2_client
475 client.migration_complete(share_id, **kwargs)
476 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300477 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300478 return share
479
480 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300481 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
482 client = client or cls.shares_v2_client
483 client.migration_cancel(share_id, **kwargs)
484 share = client.wait_for_migration_status(
485 share_id, dest_host, 'migration_cancelled', **kwargs)
486 return share
487
488 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200489 def create_share(cls, *args, **kwargs):
490 """Create one share and wait for available state. Retry if allowed."""
491 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
492 return result[0]
493
494 @classmethod
495 def create_shares(cls, share_data_list):
496 """Creates several shares in parallel with retries.
497
498 Use this method when you want to create more than one share at same
499 time. Especially if config option 'share.share_creation_retry_number'
500 has value more than zero (0).
501 All shares will be expected to have 'available' status with or without
502 recreation else error will be raised.
503
504 :param share_data_list: list -- list of dictionaries with 'args' and
505 'kwargs' for '_create_share' method of this base class.
506 example of data:
507 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
508 :returns: list -- list of shares created using provided data.
509 """
510
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300511 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200512 if not isinstance(d, dict):
513 raise exceptions.TempestException(
514 "Expected 'dict', got '%s'" % type(d))
515 if "args" not in d:
516 d["args"] = []
517 if "kwargs" not in d:
518 d["kwargs"] = {}
519 if len(d) > 2:
520 raise exceptions.TempestException(
521 "Expected only 'args' and 'kwargs' keys. "
522 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300523
524 data = []
525 for d in share_data_list:
526 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400527 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300528 local_d = {
529 "args": d["args"],
530 "kwargs": copy.deepcopy(d["kwargs"]),
531 }
532 local_d["kwargs"]["client"] = client
533 local_d["share"] = cls._create_share(
534 *local_d["args"], **local_d["kwargs"])
535 local_d["cnt"] = 0
536 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400537 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300538 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200539
540 while not all(d["available"] for d in data):
541 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400542 if not d["wait_for_status"]:
543 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200544 if d["available"]:
545 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300546 client = d["kwargs"]["client"]
547 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200548 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300549 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200550 d["available"] = True
551 except (share_exceptions.ShareBuildErrorException,
552 exceptions.TimeoutException) as e:
553 if CONF.share.share_creation_retry_number > d["cnt"]:
554 d["cnt"] += 1
555 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300556 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200557 LOG.error(msg)
558 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300559 cg_id = d["kwargs"].get("consistency_group_id")
560 if cg_id:
561 # NOTE(vponomaryov): delete errored share
562 # immediately in case share is part of CG.
563 client.delete_share(
564 share_id,
565 params={"consistency_group_id": cg_id})
566 client.wait_for_resource_deletion(
567 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200568 d["share"] = cls._create_share(
569 *d["args"], **d["kwargs"])
570 else:
gecong197358663802016-08-25 11:08:45 +0800571 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200572
573 return [d["share"] for d in data]
574
575 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400576 def create_share_group(cls, client=None, cleanup_in_class=True,
577 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400578 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400579 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400580 kwargs['share_network_id'] = (share_network_id or
581 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400582 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400583 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400584 "type": "share_group",
585 "id": share_group["id"],
586 "client": client,
587 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400588 if cleanup_in_class:
589 cls.class_resources.insert(0, resource)
590 else:
591 cls.method_resources.insert(0, resource)
592
Andrew Kerrb8436922016-06-01 15:32:43 -0400593 if kwargs.get('source_share_group_snapshot_id'):
594 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400595 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400596 params={'share_group_id': share_group['id']},
597 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400598
Andrew Kerrb8436922016-06-01 15:32:43 -0400599 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400600 resource = {"type": "share",
601 "id": share["id"],
602 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400603 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400604 if cleanup_in_class:
605 cls.class_resources.insert(0, resource)
606 else:
607 cls.method_resources.insert(0, resource)
608
Andrew Kerrb8436922016-06-01 15:32:43 -0400609 client.wait_for_share_group_status(share_group['id'], 'available')
610 return share_group
611
612 @classmethod
613 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
614 group_specs=None, client=None,
615 cleanup_in_class=True, **kwargs):
616 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300617 if (group_specs is None and
618 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300619 group_specs = {
620 'consistent_snapshot_support': (
621 CONF.share.capability_sg_consistent_snapshot_support),
622 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400623 share_group_type = client.create_share_group_type(
624 name=name,
625 share_types=share_types,
626 is_public=is_public,
627 group_specs=group_specs,
628 **kwargs)
629 resource = {
630 "type": "share_group_type",
631 "id": share_group_type["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 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400639
640 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200641 def create_snapshot_wait_for_active(cls, share_id, name=None,
642 description=None, force=False,
643 client=None, cleanup_in_class=True):
644 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400645 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200646 if description is None:
647 description = "Tempest's snapshot"
648 snapshot = client.create_snapshot(share_id, name, description, force)
649 resource = {
650 "type": "snapshot",
651 "id": snapshot["id"],
652 "client": client,
653 }
654 if cleanup_in_class:
655 cls.class_resources.insert(0, resource)
656 else:
657 cls.method_resources.insert(0, resource)
658 client.wait_for_snapshot_status(snapshot["id"], "available")
659 return snapshot
660
661 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400662 def create_share_group_snapshot_wait_for_active(
663 cls, share_group_id, name=None, description=None, client=None,
664 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400665 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400666 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400667 description = "Tempest's share group snapshot"
668 sg_snapshot = client.create_share_group_snapshot(
669 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400670 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400671 "type": "share_group_snapshot",
672 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400673 "client": client,
674 }
675 if cleanup_in_class:
676 cls.class_resources.insert(0, resource)
677 else:
678 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400679 client.wait_for_share_group_snapshot_status(
680 sg_snapshot["id"], "available")
681 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400682
683 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400684 def get_availability_zones(cls, client=None):
685 """List the availability zones for "manila-share" services
686
687 that are currently in "up" state.
688 """
689 client = client or cls.shares_v2_client
690 cls.services = client.list_services()
691 zones = [service['zone'] for service in cls.services if
692 service['binary'] == "manila-share" and
693 service['state'] == 'up']
694 return zones
695
Yogesh1f931ff2015-09-29 23:41:02 -0400696 def get_pools_for_replication_domain(self):
697 # Get the list of pools for the replication domain
698 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500699 instance_host = self.admin_client.get_share(
700 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400701 host_pool = [p for p in pools if p['name'] == instance_host][0]
702 rep_domain = host_pool['capabilities']['replication_domain']
703 pools_in_rep_domain = [p for p in pools if p['capabilities'][
704 'replication_domain'] == rep_domain]
705 return rep_domain, pools_in_rep_domain
706
Yogeshbdb88102015-09-29 23:41:02 -0400707 @classmethod
708 def create_share_replica(cls, share_id, availability_zone, client=None,
709 cleanup_in_class=False, cleanup=True):
710 client = client or cls.shares_v2_client
711 replica = client.create_share_replica(share_id, availability_zone)
712 resource = {
713 "type": "share_replica",
714 "id": replica["id"],
715 "client": client,
716 "share_id": share_id,
717 }
718 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
719 if cleanup:
720 if cleanup_in_class:
721 cls.class_resources.insert(0, resource)
722 else:
723 cls.method_resources.insert(0, resource)
724 client.wait_for_share_replica_status(
725 replica["id"], constants.STATUS_AVAILABLE)
726 return replica
727
728 @classmethod
729 def delete_share_replica(cls, replica_id, client=None):
730 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400731 try:
732 client.delete_share_replica(replica_id)
733 client.wait_for_resource_deletion(replica_id=replica_id)
734 except exceptions.NotFound:
735 pass
Yogeshbdb88102015-09-29 23:41:02 -0400736
737 @classmethod
738 def promote_share_replica(cls, replica_id, client=None):
739 client = client or cls.shares_v2_client
740 replica = client.promote_share_replica(replica_id)
741 client.wait_for_share_replica_status(
742 replica["id"],
743 constants.REPLICATION_STATE_ACTIVE,
744 status_attr="replica_state")
745 return replica
746
yogeshdb32f462016-09-28 15:09:50 -0400747 def _get_access_rule_data_from_config(self):
748 """Get the first available access type/to combination from config.
749
750 This method opportunistically picks the first configured protocol
751 to create the share. Do not use this method in tests where you need
752 to test depth and breadth in the access types and access recipients.
753 """
754 protocol = self.shares_v2_client.share_protocol
755
756 if protocol in CONF.share.enable_ip_rules_for_protocols:
757 access_type = "ip"
758 access_to = utils.rand_ip()
759 elif protocol in CONF.share.enable_user_rules_for_protocols:
760 access_type = "user"
761 access_to = CONF.share.username_for_user_rules
762 elif protocol in CONF.share.enable_cert_rules_for_protocols:
763 access_type = "cert"
764 access_to = "client3.com"
765 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
766 access_type = "cephx"
767 access_to = "eve"
768 else:
769 message = "Unrecognized protocol and access rules configuration."
770 raise self.skipException(message)
771
772 return access_type, access_to
773
Yogeshbdb88102015-09-29 23:41:02 -0400774 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200775 def create_share_network(cls, client=None,
776 cleanup_in_class=False, **kwargs):
777 if client is None:
778 client = cls.shares_client
779 share_network = client.create_share_network(**kwargs)
780 resource = {
781 "type": "share_network",
782 "id": share_network["id"],
783 "client": client,
784 }
785 if cleanup_in_class:
786 cls.class_resources.insert(0, resource)
787 else:
788 cls.method_resources.insert(0, resource)
789 return share_network
790
791 @classmethod
792 def create_security_service(cls, ss_type="ldap", client=None,
793 cleanup_in_class=False, **kwargs):
794 if client is None:
795 client = cls.shares_client
796 security_service = client.create_security_service(ss_type, **kwargs)
797 resource = {
798 "type": "security_service",
799 "id": security_service["id"],
800 "client": client,
801 }
802 if cleanup_in_class:
803 cls.class_resources.insert(0, resource)
804 else:
805 cls.method_resources.insert(0, resource)
806 return security_service
807
808 @classmethod
809 def create_share_type(cls, name, is_public=True, client=None,
810 cleanup_in_class=True, **kwargs):
811 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200812 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200813 share_type = client.create_share_type(name, is_public, **kwargs)
814 resource = {
815 "type": "share_type",
816 "id": share_type["share_type"]["id"],
817 "client": client,
818 }
819 if cleanup_in_class:
820 cls.class_resources.insert(0, resource)
821 else:
822 cls.method_resources.insert(0, resource)
823 return share_type
824
825 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400826 def add_extra_specs_to_dict(extra_specs=None):
827 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300828 dhss = six.text_type(CONF.share.multitenancy_enabled)
829 snapshot_support = six.text_type(
830 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400831 create_from_snapshot_support = six.text_type(
832 CONF.share.capability_create_share_from_snapshot_support)
833
834 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300835 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200836 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400837
838 optional = {
839 "snapshot_support": snapshot_support,
840 "create_share_from_snapshot_support": create_from_snapshot_support,
841 }
842 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
843 # required extra-spec
844 extra_specs_dict.update(optional)
845
Marc Koderer0abc93b2015-07-15 09:18:35 +0200846 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400847 extra_specs_dict.update(extra_specs)
848
849 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200850
851 @classmethod
852 def clear_isolated_creds(cls, creds=None):
853 if creds is None:
854 creds = cls.method_isolated_creds
855 for ic in creds:
856 if "deleted" not in ic.keys():
857 ic["deleted"] = False
858 if not ic["deleted"]:
859 with handle_cleanup_exceptions():
860 ic["method"]()
861 ic["deleted"] = True
862
863 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400864 def clear_share_replicas(cls, share_id, client=None):
865 client = client or cls.shares_v2_client
866 share_replicas = client.list_share_replicas(
867 share_id=share_id)
868
869 for replica in share_replicas:
870 try:
871 cls.delete_share_replica(replica['id'])
872 except exceptions.BadRequest:
873 # Ignore the exception due to deletion of last active replica
874 pass
875
876 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200877 def clear_resources(cls, resources=None):
878 """Deletes resources, that were created in test suites.
879
880 This method tries to remove resources from resource list,
881 if it is not found, assumed it was deleted in test itself.
882 It is expected, that all resources were added as LIFO
883 due to restriction of deletion resources, that is in the chain.
884
885 :param resources: dict with keys 'type','id','client' and 'deleted'
886 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200887 if resources is None:
888 resources = cls.method_resources
889 for res in resources:
890 if "deleted" not in res.keys():
891 res["deleted"] = False
892 if "client" not in res.keys():
893 res["client"] = cls.shares_client
894 if not(res["deleted"]):
895 res_id = res['id']
896 client = res["client"]
897 with handle_cleanup_exceptions():
898 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400899 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400900 share_group_id = res.get('share_group_id')
901 if share_group_id:
902 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400903 client.delete_share(res_id, params=params)
904 else:
905 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200906 client.wait_for_resource_deletion(share_id=res_id)
907 elif res["type"] is "snapshot":
908 client.delete_snapshot(res_id)
909 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400910 elif (res["type"] is "share_network" and
911 res_id != CONF.share.share_network_id):
912 client.delete_share_network(res_id)
913 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200914 elif res["type"] is "security_service":
915 client.delete_security_service(res_id)
916 client.wait_for_resource_deletion(ss_id=res_id)
917 elif res["type"] is "share_type":
918 client.delete_share_type(res_id)
919 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400920 elif res["type"] is "share_group":
921 client.delete_share_group(res_id)
922 client.wait_for_resource_deletion(
923 share_group_id=res_id)
924 elif res["type"] is "share_group_type":
925 client.delete_share_group_type(res_id)
926 client.wait_for_resource_deletion(
927 share_group_type_id=res_id)
928 elif res["type"] is "share_group_snapshot":
929 client.delete_share_group_snapshot(res_id)
930 client.wait_for_resource_deletion(
931 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400932 elif res["type"] is "share_replica":
933 client.delete_share_replica(res_id)
934 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200935 else:
huayue97bacbf2016-01-04 09:57:39 +0800936 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800937 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200938 res["deleted"] = True
939
940 @classmethod
941 def generate_share_network_data(self):
942 data = {
943 "name": data_utils.rand_name("sn-name"),
944 "description": data_utils.rand_name("sn-desc"),
945 "neutron_net_id": data_utils.rand_name("net-id"),
946 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
947 }
948 return data
949
950 @classmethod
951 def generate_security_service_data(self):
952 data = {
953 "name": data_utils.rand_name("ss-name"),
954 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200955 "dns_ip": utils.rand_ip(),
956 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200957 "domain": data_utils.rand_name("ss-domain"),
958 "user": data_utils.rand_name("ss-user"),
959 "password": data_utils.rand_name("ss-password"),
960 }
961 return data
962
963 # Useful assertions
964 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
965 """Assert two dicts are equivalent.
966
967 This is a 'deep' match in the sense that it handles nested
968 dictionaries appropriately.
969
970 NOTE:
971
972 If you don't care (or don't know) a given value, you can specify
973 the string DONTCARE as the value. This will cause that dict-item
974 to be skipped.
975
976 """
977 def raise_assertion(msg):
978 d1str = str(d1)
979 d2str = str(d2)
980 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
981 'd2: %(d2str)s' %
982 {"msg": msg, "d1str": d1str, "d2str": d2str})
983 raise AssertionError(base_msg)
984
985 d1keys = set(d1.keys())
986 d2keys = set(d2.keys())
987 if d1keys != d2keys:
988 d1only = d1keys - d2keys
989 d2only = d2keys - d1keys
990 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
991 'Keys in d2 and not d1: %(d2only)s' %
992 {"d1only": d1only, "d2only": d2only})
993
994 for key in d1keys:
995 d1value = d1[key]
996 d2value = d2[key]
997 try:
998 error = abs(float(d1value) - float(d2value))
999 within_tolerance = error <= tolerance
1000 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001001 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001002 # ValueError if arg is a str, TypeError if it's something else
1003 # (like None)
1004 within_tolerance = False
1005
1006 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1007 self.assertDictMatch(d1value, d2value)
1008 elif 'DONTCARE' in (d1value, d2value):
1009 continue
1010 elif approx_equal and within_tolerance:
1011 continue
1012 elif d1value != d2value:
1013 raise_assertion("d1['%(key)s']=%(d1value)s != "
1014 "d2['%(key)s']=%(d2value)s" %
1015 {
1016 "key": key,
1017 "d1value": d1value,
1018 "d2value": d2value
1019 })
1020
Alex Meadeba8a1602016-05-06 09:33:09 -04001021 def create_user_message(self):
1022 """Trigger a 'no valid host' situation to generate a message."""
1023 extra_specs = {
1024 'vendor_name': 'foobar',
1025 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1026 }
1027 share_type_name = data_utils.rand_name("share-type")
1028
1029 bogus_type = self.create_share_type(
1030 name=share_type_name,
1031 extra_specs=extra_specs)['share_type']
1032
1033 params = {'share_type_id': bogus_type['id'],
1034 'share_network_id': self.shares_v2_client.share_network_id}
1035 share = self.shares_v2_client.create_share(**params)
1036 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1037 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1038 return self.shares_v2_client.wait_for_message(share['id'])
1039
Marc Koderer0abc93b2015-07-15 09:18:35 +02001040
1041class BaseSharesAltTest(BaseSharesTest):
1042 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001043 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001044
1045
1046class BaseSharesAdminTest(BaseSharesTest):
1047 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001048 credentials = ('admin', )
1049
1050
1051class BaseSharesMixedTest(BaseSharesTest):
1052 """Base test case class for all Shares API tests with all user roles."""
1053 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001054
1055 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001056 def setup_clients(cls):
1057 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001058 # Initialise share clients
1059 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1060 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1061 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1062 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1063 # Initialise network clients
1064 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1065 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001066
1067 if CONF.share.multitenancy_enabled:
1068 admin_share_network_id = cls.provide_share_network(
1069 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1070 cls.admin_shares_client.share_network_id = admin_share_network_id
1071 cls.admin_shares_v2_client.share_network_id = (
1072 admin_share_network_id)
1073
1074 alt_share_network_id = cls.provide_share_network(
1075 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1076 cls.alt_shares_client.share_network_id = alt_share_network_id
1077 cls.alt_shares_v2_client.share_network_id = alt_share_network_id