blob: a4892a88fcf2769a433ab462f37d6ce1c4299c85 [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,
ghanshyam2aea7c32017-12-11 00:03:56 +0000180 resource_prefix='tempest',
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100181 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
haobing101c7fee2018-03-09 16:33:00 +0800293 resource = {
294 "type": "share_network",
295 "id": share_network_id,
296 "client": cls.shares_v2_client,
297 }
298 cls.class_resources.insert(0, resource)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300299
300 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200301 def resource_setup(cls):
302 if not (any(p in CONF.share.enable_protocols
303 for p in cls.protocols) and
304 CONF.service_available.manila):
305 skip_msg = "Manila is disabled"
306 raise cls.skipException(skip_msg)
307 super(BaseSharesTest, cls).resource_setup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200308
309 def setUp(self):
310 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200311 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200312 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300313 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200314
315 @classmethod
316 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200317 cls.clear_resources(cls.class_resources)
318 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400319 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200320
321 @classmethod
322 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200323 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300324 isolated_creds_client=None,
325 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200326 """Used for finding/creating share network for multitenant driver.
327
328 This method creates/gets entity share-network for one tenant. This
329 share-network will be used for creation of service vm.
330
331 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200332 :param networks_client: network client from same tenant as shares
333 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200334 If provided, then its networking will be used if needed.
335 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300336 :param ignore_multitenancy_config: provide a share network regardless
337 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200338 :returns: str -- share network id for shares_client tenant
339 :returns: None -- if single-tenant driver used
340 """
341
342 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300343 search_word = "reusable"
344 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200345
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300346 if (not ignore_multitenancy_config and
347 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200348 # Assumed usage of a single-tenant driver
349 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200350 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300351 if sc.share_network_id:
352 # Share-network already exists, use it
353 share_network_id = sc.share_network_id
354 elif not CONF.share.create_networks_when_multitenancy_enabled:
355 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200356
357 # Try get suitable share-network
358 share_networks = sc.list_share_networks_with_detail()
359 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300360 if (sn["neutron_net_id"] is None and
361 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200362 sn["name"] and search_word in sn["name"]):
363 share_network_id = sn["id"]
364 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200365
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300366 # Create new share-network if one was not found
367 if share_network_id is None:
368 sn_desc = "This share-network was created by tempest"
369 sn = sc.create_share_network(name=sn_name,
370 description=sn_desc)
371 share_network_id = sn["id"]
372 else:
373 net_id = subnet_id = share_network_id = None
374
375 if not isolated_creds_client:
376 # Search for networks, created in previous runs
377 service_net_name = "share-service"
378 networks = networks_client.list_networks()
379 if "networks" in networks.keys():
380 networks = networks["networks"]
381 for network in networks:
382 if (service_net_name in network["name"] and
383 sc.tenant_id == network['tenant_id']):
384 net_id = network["id"]
385 if len(network["subnets"]) > 0:
386 subnet_id = network["subnets"][0]
387 break
388
389 # Create suitable network
390 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500391 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300392 net_data = ic._create_network_resources(sc.tenant_id)
393 network, subnet, router = net_data
394 net_id = network["id"]
395 subnet_id = subnet["id"]
396
397 # Try get suitable share-network
398 share_networks = sc.list_share_networks_with_detail()
399 for sn in share_networks:
400 if (net_id == sn["neutron_net_id"] and
401 subnet_id == sn["neutron_subnet_id"] and
402 sn["name"] and search_word in sn["name"]):
403 share_network_id = sn["id"]
404 break
405 else:
406 sn_name = "autogenerated_by_tempest_for_isolated_creds"
407 # Use precreated network and subnet from isolated creds
408 net_id = isolated_creds_client.get_credentials(
409 isolated_creds_client.type_of_creds).network['id']
410 subnet_id = isolated_creds_client.get_credentials(
411 isolated_creds_client.type_of_creds).subnet['id']
412
413 # Create suitable share-network
414 if share_network_id is None:
415 sn_desc = "This share-network was created by tempest"
416 sn = sc.create_share_network(name=sn_name,
417 description=sn_desc,
418 neutron_net_id=net_id,
419 neutron_subnet_id=subnet_id)
420 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200421
422 return share_network_id
423
424 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300425 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200426 snapshot_id=None, description=None, metadata=None,
427 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400428 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400429 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300430 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200431 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400432 share_network_id = (share_network_id or
433 CONF.share.share_network_id or
434 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200435 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300436 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400437 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200438 'share_protocol': share_protocol,
439 'size': size,
440 'name': name,
441 'snapshot_id': snapshot_id,
442 'description': description,
443 'metadata': metadata,
444 'share_network_id': share_network_id,
445 'share_type_id': share_type_id,
446 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400447 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400448 if share_group_id:
449 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400450
Marc Koderer0abc93b2015-07-15 09:18:35 +0200451 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400452 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400453 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200454 cleanup_list = (cls.class_resources if cleanup_in_class else
455 cls.method_resources)
456 cleanup_list.insert(0, resource)
457 return share
458
459 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300460 def migrate_share(
461 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200462 force_host_assisted_migration=False, writable=False,
463 nondisruptive=False, preserve_metadata=False,
464 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300465 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400466 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300467 client.migrate_share(
468 share_id, dest_host,
469 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200470 writable=writable, preserve_metadata=preserve_metadata,
471 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300472 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300473 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200474 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300475 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200476 return share
477
478 @classmethod
479 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
480 client = client or cls.shares_v2_client
481 client.migration_complete(share_id, **kwargs)
482 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300483 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300484 return share
485
486 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300487 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
488 client = client or cls.shares_v2_client
489 client.migration_cancel(share_id, **kwargs)
490 share = client.wait_for_migration_status(
491 share_id, dest_host, 'migration_cancelled', **kwargs)
492 return share
493
494 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200495 def create_share(cls, *args, **kwargs):
496 """Create one share and wait for available state. Retry if allowed."""
497 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
498 return result[0]
499
500 @classmethod
501 def create_shares(cls, share_data_list):
502 """Creates several shares in parallel with retries.
503
504 Use this method when you want to create more than one share at same
505 time. Especially if config option 'share.share_creation_retry_number'
506 has value more than zero (0).
507 All shares will be expected to have 'available' status with or without
508 recreation else error will be raised.
509
510 :param share_data_list: list -- list of dictionaries with 'args' and
511 'kwargs' for '_create_share' method of this base class.
512 example of data:
513 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
514 :returns: list -- list of shares created using provided data.
515 """
516
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300517 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200518 if not isinstance(d, dict):
519 raise exceptions.TempestException(
520 "Expected 'dict', got '%s'" % type(d))
521 if "args" not in d:
522 d["args"] = []
523 if "kwargs" not in d:
524 d["kwargs"] = {}
525 if len(d) > 2:
526 raise exceptions.TempestException(
527 "Expected only 'args' and 'kwargs' keys. "
528 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300529
530 data = []
531 for d in share_data_list:
532 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400533 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300534 local_d = {
535 "args": d["args"],
536 "kwargs": copy.deepcopy(d["kwargs"]),
537 }
538 local_d["kwargs"]["client"] = client
539 local_d["share"] = cls._create_share(
540 *local_d["args"], **local_d["kwargs"])
541 local_d["cnt"] = 0
542 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400543 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300544 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200545
546 while not all(d["available"] for d in data):
547 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400548 if not d["wait_for_status"]:
549 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200550 if d["available"]:
551 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300552 client = d["kwargs"]["client"]
553 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200554 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300555 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200556 d["available"] = True
557 except (share_exceptions.ShareBuildErrorException,
558 exceptions.TimeoutException) as e:
559 if CONF.share.share_creation_retry_number > d["cnt"]:
560 d["cnt"] += 1
561 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300562 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200563 LOG.error(msg)
564 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300565 cg_id = d["kwargs"].get("consistency_group_id")
566 if cg_id:
567 # NOTE(vponomaryov): delete errored share
568 # immediately in case share is part of CG.
569 client.delete_share(
570 share_id,
571 params={"consistency_group_id": cg_id})
572 client.wait_for_resource_deletion(
573 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200574 d["share"] = cls._create_share(
575 *d["args"], **d["kwargs"])
576 else:
gecong197358663802016-08-25 11:08:45 +0800577 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200578
579 return [d["share"] for d in data]
580
581 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400582 def create_share_group(cls, client=None, cleanup_in_class=True,
583 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400584 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400585 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400586 kwargs['share_network_id'] = (share_network_id or
587 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400588 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400589 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400590 "type": "share_group",
591 "id": share_group["id"],
592 "client": client,
593 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400594 if cleanup_in_class:
595 cls.class_resources.insert(0, resource)
596 else:
597 cls.method_resources.insert(0, resource)
598
Andrew Kerrb8436922016-06-01 15:32:43 -0400599 if kwargs.get('source_share_group_snapshot_id'):
600 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400601 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400602 params={'share_group_id': share_group['id']},
603 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400604
Andrew Kerrb8436922016-06-01 15:32:43 -0400605 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400606 resource = {"type": "share",
607 "id": share["id"],
608 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400609 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400610 if cleanup_in_class:
611 cls.class_resources.insert(0, resource)
612 else:
613 cls.method_resources.insert(0, resource)
614
Andrew Kerrb8436922016-06-01 15:32:43 -0400615 client.wait_for_share_group_status(share_group['id'], 'available')
616 return share_group
617
618 @classmethod
619 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
620 group_specs=None, client=None,
621 cleanup_in_class=True, **kwargs):
622 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300623 if (group_specs is None and
624 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300625 group_specs = {
626 'consistent_snapshot_support': (
627 CONF.share.capability_sg_consistent_snapshot_support),
628 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400629 share_group_type = client.create_share_group_type(
630 name=name,
631 share_types=share_types,
632 is_public=is_public,
633 group_specs=group_specs,
634 **kwargs)
635 resource = {
636 "type": "share_group_type",
637 "id": share_group_type["id"],
638 "client": client,
639 }
640 if cleanup_in_class:
641 cls.class_resources.insert(0, resource)
642 else:
643 cls.method_resources.insert(0, resource)
644 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400645
646 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200647 def create_snapshot_wait_for_active(cls, share_id, name=None,
648 description=None, force=False,
649 client=None, cleanup_in_class=True):
650 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400651 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200652 if description is None:
653 description = "Tempest's snapshot"
654 snapshot = client.create_snapshot(share_id, name, description, force)
655 resource = {
656 "type": "snapshot",
657 "id": snapshot["id"],
658 "client": client,
659 }
660 if cleanup_in_class:
661 cls.class_resources.insert(0, resource)
662 else:
663 cls.method_resources.insert(0, resource)
664 client.wait_for_snapshot_status(snapshot["id"], "available")
665 return snapshot
666
667 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400668 def create_share_group_snapshot_wait_for_active(
669 cls, share_group_id, name=None, description=None, client=None,
670 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400671 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400672 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400673 description = "Tempest's share group snapshot"
674 sg_snapshot = client.create_share_group_snapshot(
675 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400676 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400677 "type": "share_group_snapshot",
678 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400679 "client": client,
680 }
681 if cleanup_in_class:
682 cls.class_resources.insert(0, resource)
683 else:
684 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400685 client.wait_for_share_group_snapshot_status(
686 sg_snapshot["id"], "available")
687 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400688
689 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400690 def get_availability_zones(cls, client=None):
691 """List the availability zones for "manila-share" services
692
693 that are currently in "up" state.
694 """
695 client = client or cls.shares_v2_client
696 cls.services = client.list_services()
697 zones = [service['zone'] for service in cls.services if
698 service['binary'] == "manila-share" and
699 service['state'] == 'up']
700 return zones
701
Yogesh1f931ff2015-09-29 23:41:02 -0400702 def get_pools_for_replication_domain(self):
703 # Get the list of pools for the replication domain
704 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500705 instance_host = self.admin_client.get_share(
706 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400707 host_pool = [p for p in pools if p['name'] == instance_host][0]
708 rep_domain = host_pool['capabilities']['replication_domain']
709 pools_in_rep_domain = [p for p in pools if p['capabilities'][
710 'replication_domain'] == rep_domain]
711 return rep_domain, pools_in_rep_domain
712
Yogeshbdb88102015-09-29 23:41:02 -0400713 @classmethod
714 def create_share_replica(cls, share_id, availability_zone, client=None,
715 cleanup_in_class=False, cleanup=True):
716 client = client or cls.shares_v2_client
717 replica = client.create_share_replica(share_id, availability_zone)
718 resource = {
719 "type": "share_replica",
720 "id": replica["id"],
721 "client": client,
722 "share_id": share_id,
723 }
724 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
725 if cleanup:
726 if cleanup_in_class:
727 cls.class_resources.insert(0, resource)
728 else:
729 cls.method_resources.insert(0, resource)
730 client.wait_for_share_replica_status(
731 replica["id"], constants.STATUS_AVAILABLE)
732 return replica
733
734 @classmethod
735 def delete_share_replica(cls, replica_id, client=None):
736 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400737 try:
738 client.delete_share_replica(replica_id)
739 client.wait_for_resource_deletion(replica_id=replica_id)
740 except exceptions.NotFound:
741 pass
Yogeshbdb88102015-09-29 23:41:02 -0400742
743 @classmethod
744 def promote_share_replica(cls, replica_id, client=None):
745 client = client or cls.shares_v2_client
746 replica = client.promote_share_replica(replica_id)
747 client.wait_for_share_replica_status(
748 replica["id"],
749 constants.REPLICATION_STATE_ACTIVE,
750 status_attr="replica_state")
751 return replica
752
yogeshdb32f462016-09-28 15:09:50 -0400753 def _get_access_rule_data_from_config(self):
754 """Get the first available access type/to combination from config.
755
756 This method opportunistically picks the first configured protocol
757 to create the share. Do not use this method in tests where you need
758 to test depth and breadth in the access types and access recipients.
759 """
760 protocol = self.shares_v2_client.share_protocol
761
762 if protocol in CONF.share.enable_ip_rules_for_protocols:
763 access_type = "ip"
764 access_to = utils.rand_ip()
765 elif protocol in CONF.share.enable_user_rules_for_protocols:
766 access_type = "user"
767 access_to = CONF.share.username_for_user_rules
768 elif protocol in CONF.share.enable_cert_rules_for_protocols:
769 access_type = "cert"
770 access_to = "client3.com"
771 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
772 access_type = "cephx"
773 access_to = "eve"
774 else:
775 message = "Unrecognized protocol and access rules configuration."
776 raise self.skipException(message)
777
778 return access_type, access_to
779
Yogeshbdb88102015-09-29 23:41:02 -0400780 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200781 def create_share_network(cls, client=None,
782 cleanup_in_class=False, **kwargs):
783 if client is None:
784 client = cls.shares_client
785 share_network = client.create_share_network(**kwargs)
786 resource = {
787 "type": "share_network",
788 "id": share_network["id"],
789 "client": client,
790 }
791 if cleanup_in_class:
792 cls.class_resources.insert(0, resource)
793 else:
794 cls.method_resources.insert(0, resource)
795 return share_network
796
797 @classmethod
798 def create_security_service(cls, ss_type="ldap", client=None,
799 cleanup_in_class=False, **kwargs):
800 if client is None:
801 client = cls.shares_client
802 security_service = client.create_security_service(ss_type, **kwargs)
803 resource = {
804 "type": "security_service",
805 "id": security_service["id"],
806 "client": client,
807 }
808 if cleanup_in_class:
809 cls.class_resources.insert(0, resource)
810 else:
811 cls.method_resources.insert(0, resource)
812 return security_service
813
814 @classmethod
815 def create_share_type(cls, name, is_public=True, client=None,
816 cleanup_in_class=True, **kwargs):
817 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200818 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200819 share_type = client.create_share_type(name, is_public, **kwargs)
820 resource = {
821 "type": "share_type",
822 "id": share_type["share_type"]["id"],
823 "client": client,
824 }
825 if cleanup_in_class:
826 cls.class_resources.insert(0, resource)
827 else:
828 cls.method_resources.insert(0, resource)
829 return share_type
830
831 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400832 def add_extra_specs_to_dict(extra_specs=None):
833 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300834 dhss = six.text_type(CONF.share.multitenancy_enabled)
835 snapshot_support = six.text_type(
836 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400837 create_from_snapshot_support = six.text_type(
838 CONF.share.capability_create_share_from_snapshot_support)
839
840 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300841 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200842 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400843
844 optional = {
845 "snapshot_support": snapshot_support,
846 "create_share_from_snapshot_support": create_from_snapshot_support,
847 }
848 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
849 # required extra-spec
850 extra_specs_dict.update(optional)
851
Marc Koderer0abc93b2015-07-15 09:18:35 +0200852 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400853 extra_specs_dict.update(extra_specs)
854
855 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200856
857 @classmethod
858 def clear_isolated_creds(cls, creds=None):
859 if creds is None:
860 creds = cls.method_isolated_creds
861 for ic in creds:
862 if "deleted" not in ic.keys():
863 ic["deleted"] = False
864 if not ic["deleted"]:
865 with handle_cleanup_exceptions():
866 ic["method"]()
867 ic["deleted"] = True
868
869 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400870 def clear_share_replicas(cls, share_id, client=None):
871 client = client or cls.shares_v2_client
872 share_replicas = client.list_share_replicas(
873 share_id=share_id)
874
875 for replica in share_replicas:
876 try:
877 cls.delete_share_replica(replica['id'])
878 except exceptions.BadRequest:
879 # Ignore the exception due to deletion of last active replica
880 pass
881
882 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200883 def clear_resources(cls, resources=None):
884 """Deletes resources, that were created in test suites.
885
886 This method tries to remove resources from resource list,
887 if it is not found, assumed it was deleted in test itself.
888 It is expected, that all resources were added as LIFO
889 due to restriction of deletion resources, that is in the chain.
890
891 :param resources: dict with keys 'type','id','client' and 'deleted'
892 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200893 if resources is None:
894 resources = cls.method_resources
895 for res in resources:
896 if "deleted" not in res.keys():
897 res["deleted"] = False
898 if "client" not in res.keys():
899 res["client"] = cls.shares_client
900 if not(res["deleted"]):
901 res_id = res['id']
902 client = res["client"]
903 with handle_cleanup_exceptions():
904 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400905 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400906 share_group_id = res.get('share_group_id')
907 if share_group_id:
908 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400909 client.delete_share(res_id, params=params)
910 else:
911 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200912 client.wait_for_resource_deletion(share_id=res_id)
913 elif res["type"] is "snapshot":
914 client.delete_snapshot(res_id)
915 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400916 elif (res["type"] is "share_network" and
917 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400918 client.delete_share_network(res_id)
919 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200920 elif res["type"] is "security_service":
921 client.delete_security_service(res_id)
922 client.wait_for_resource_deletion(ss_id=res_id)
923 elif res["type"] is "share_type":
924 client.delete_share_type(res_id)
925 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400926 elif res["type"] is "share_group":
927 client.delete_share_group(res_id)
928 client.wait_for_resource_deletion(
929 share_group_id=res_id)
930 elif res["type"] is "share_group_type":
931 client.delete_share_group_type(res_id)
932 client.wait_for_resource_deletion(
933 share_group_type_id=res_id)
934 elif res["type"] is "share_group_snapshot":
935 client.delete_share_group_snapshot(res_id)
936 client.wait_for_resource_deletion(
937 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400938 elif res["type"] is "share_replica":
939 client.delete_share_replica(res_id)
940 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200941 else:
huayue97bacbf2016-01-04 09:57:39 +0800942 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800943 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200944 res["deleted"] = True
945
946 @classmethod
947 def generate_share_network_data(self):
948 data = {
949 "name": data_utils.rand_name("sn-name"),
950 "description": data_utils.rand_name("sn-desc"),
951 "neutron_net_id": data_utils.rand_name("net-id"),
952 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
953 }
954 return data
955
956 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100957 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200958 data = {
959 "name": data_utils.rand_name("ss-name"),
960 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200961 "dns_ip": utils.rand_ip(),
962 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200963 "domain": data_utils.rand_name("ss-domain"),
964 "user": data_utils.rand_name("ss-user"),
965 "password": data_utils.rand_name("ss-password"),
966 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100967 if set_ou:
968 data["ou"] = data_utils.rand_name("ss-ou")
969
Marc Koderer0abc93b2015-07-15 09:18:35 +0200970 return data
971
972 # Useful assertions
973 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
974 """Assert two dicts are equivalent.
975
976 This is a 'deep' match in the sense that it handles nested
977 dictionaries appropriately.
978
979 NOTE:
980
981 If you don't care (or don't know) a given value, you can specify
982 the string DONTCARE as the value. This will cause that dict-item
983 to be skipped.
984
985 """
986 def raise_assertion(msg):
987 d1str = str(d1)
988 d2str = str(d2)
989 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
990 'd2: %(d2str)s' %
991 {"msg": msg, "d1str": d1str, "d2str": d2str})
992 raise AssertionError(base_msg)
993
994 d1keys = set(d1.keys())
995 d2keys = set(d2.keys())
996 if d1keys != d2keys:
997 d1only = d1keys - d2keys
998 d2only = d2keys - d1keys
999 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1000 'Keys in d2 and not d1: %(d2only)s' %
1001 {"d1only": d1only, "d2only": d2only})
1002
1003 for key in d1keys:
1004 d1value = d1[key]
1005 d2value = d2[key]
1006 try:
1007 error = abs(float(d1value) - float(d2value))
1008 within_tolerance = error <= tolerance
1009 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001010 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001011 # ValueError if arg is a str, TypeError if it's something else
1012 # (like None)
1013 within_tolerance = False
1014
1015 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1016 self.assertDictMatch(d1value, d2value)
1017 elif 'DONTCARE' in (d1value, d2value):
1018 continue
1019 elif approx_equal and within_tolerance:
1020 continue
1021 elif d1value != d2value:
1022 raise_assertion("d1['%(key)s']=%(d1value)s != "
1023 "d2['%(key)s']=%(d2value)s" %
1024 {
1025 "key": key,
1026 "d1value": d1value,
1027 "d2value": d2value
1028 })
1029
Alex Meadeba8a1602016-05-06 09:33:09 -04001030 def create_user_message(self):
1031 """Trigger a 'no valid host' situation to generate a message."""
1032 extra_specs = {
1033 'vendor_name': 'foobar',
1034 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1035 }
1036 share_type_name = data_utils.rand_name("share-type")
1037
1038 bogus_type = self.create_share_type(
1039 name=share_type_name,
1040 extra_specs=extra_specs)['share_type']
1041
1042 params = {'share_type_id': bogus_type['id'],
1043 'share_network_id': self.shares_v2_client.share_network_id}
1044 share = self.shares_v2_client.create_share(**params)
1045 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1046 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1047 return self.shares_v2_client.wait_for_message(share['id'])
1048
Marc Koderer0abc93b2015-07-15 09:18:35 +02001049
1050class BaseSharesAltTest(BaseSharesTest):
1051 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001052 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001053
1054
1055class BaseSharesAdminTest(BaseSharesTest):
1056 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001057 credentials = ('admin', )
1058
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001059 @classmethod
1060 def setup_clients(cls):
1061 super(BaseSharesAdminTest, cls).setup_clients()
1062 # Initialise share clients
1063 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1064
1065 @classmethod
1066 def _create_share_type(cls, specs=None):
1067 name = data_utils.rand_name("unique_st_name")
1068 extra_specs = cls.add_extra_specs_to_dict(specs)
1069 return cls.create_share_type(
1070 name, extra_specs=extra_specs,
1071 client=cls.admin_shares_v2_client)['share_type']
1072
1073 @classmethod
1074 def _create_share_group_type(cls):
1075 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1076 return cls.create_share_group_type(
1077 name=share_group_type_name, share_types=[cls.share_type_id],
1078 client=cls.admin_shares_v2_client)
1079
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001080
1081class BaseSharesMixedTest(BaseSharesTest):
1082 """Base test case class for all Shares API tests with all user roles."""
1083 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001084
1085 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001086 def setup_clients(cls):
1087 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001088 # Initialise share clients
1089 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1090 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1091 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1092 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1093 # Initialise network clients
1094 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1095 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001096
1097 if CONF.share.multitenancy_enabled:
1098 admin_share_network_id = cls.provide_share_network(
1099 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1100 cls.admin_shares_client.share_network_id = admin_share_network_id
1101 cls.admin_shares_v2_client.share_network_id = (
1102 admin_share_network_id)
1103
1104 alt_share_network_id = cls.provide_share_network(
1105 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1106 cls.alt_shares_client.share_network_id = alt_share_network_id
1107 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001108 resource = {
1109 "type": "share_network",
1110 "id": alt_share_network_id,
1111 "client": cls.alt_shares_v2_client,
1112 }
1113 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001114
1115 @classmethod
1116 def _create_share_type(cls, specs=None):
1117 name = data_utils.rand_name("unique_st_name")
1118 extra_specs = cls.add_extra_specs_to_dict(specs)
1119 return cls.create_share_type(
1120 name, extra_specs=extra_specs,
1121 client=cls.admin_shares_v2_client)['share_type']
1122
1123 @classmethod
1124 def _create_share_group_type(cls):
1125 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1126 return cls.create_share_group_type(
1127 name=share_group_type_name, share_types=[cls.share_type_id],
1128 client=cls.admin_shares_v2_client)