blob: 3afd6fba827d93cc2e6078285222096146cfdcf2 [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
Tom Barron69f96962019-07-29 17:07:03 -040045# Only requires that manila-api service running.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030046TAG_API = "api"
Tom Barron69f96962019-07-29 17:07:03 -040047# Requires all manila services running, intended to test back-end
48# (manila-share) behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030049TAG_BACKEND = "backend"
Tom Barron69f96962019-07-29 17:07:03 -040050# Requires all manila services running, intended to test API behavior.
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030051TAG_API_WITH_BACKEND = "api_with_backend"
52
53TAGS_MAPPER = {
54 "p": TAG_POSITIVE,
55 "n": TAG_NEGATIVE,
56 "a": TAG_API,
57 "b": TAG_BACKEND,
58 "ab": TAG_API_WITH_BACKEND,
59}
60TAGS_PATTERN = re.compile(
61 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
62 TAGS_MAPPER)
63
64
65def verify_test_has_appropriate_tags(self):
66 if not TAGS_PATTERN.match(self.id()):
67 msg = (
68 "Required attributes either not set or set improperly. "
69 "Two test attributes are expected:\n"
70 " - one of '%(p)s' or '%(n)s' and \n"
71 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
72 ) % TAGS_MAPPER
73 raise self.failureException(msg)
74
Marc Koderer0abc93b2015-07-15 09:18:35 +020075
76class handle_cleanup_exceptions(object):
77 """Handle exceptions raised with cleanup operations.
78
79 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
80 are raised.
81 Suppress all other exceptions only in case config opt
82 'suppress_errors_in_cleanup' in config group 'share' is True.
83 """
84
85 def __enter__(self):
86 return self
87
88 def __exit__(self, exc_type, exc_value, exc_traceback):
89 if not (isinstance(exc_value,
90 (exceptions.NotFound, exceptions.Forbidden)) or
91 CONF.share.suppress_errors_in_cleanup):
92 return False # Do not suppress error if any
93 if exc_traceback:
94 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080095 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020096 return True # Suppress error if any
97
98
99def network_synchronized(f):
100
101 def wrapped_func(self, *args, **kwargs):
102 with_isolated_creds = True if len(args) > 2 else False
103 no_lock_required = kwargs.get(
104 "isolated_creds_client", with_isolated_creds)
105 if no_lock_required:
106 # Usage of not reusable network. No need in lock.
107 return f(self, *args, **kwargs)
108
109 # Use lock assuming reusage of common network.
110 @lockutils.synchronized("manila_network_lock", external=True)
111 def source_func(self, *args, **kwargs):
112 return f(self, *args, **kwargs)
113
114 return source_func(self, *args, **kwargs)
115
116 return wrapped_func
117
118
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200119skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500120skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200121
122
Marc Koderer0abc93b2015-07-15 09:18:35 +0200123class BaseSharesTest(test.BaseTestCase):
124 """Base test case class for all Manila API tests."""
125
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300126 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200127 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200128 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200129
130 # Will be cleaned up in resource_cleanup
131 class_resources = []
132
133 # Will be cleaned up in tearDown method
134 method_resources = []
135
136 # Will be cleaned up in resource_cleanup
137 class_isolated_creds = []
138
139 # Will be cleaned up in tearDown method
140 method_isolated_creds = []
141
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100142 # NOTE(andreaf) Override the client manager class to be used, so that
143 # a stable class is used, which includes plugin registered services as well
144 client_manager = clients.Clients
145
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200146 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200147 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200148 raise self.skipException(
149 "Microversion '%s' is not supported." % microversion)
150
Xing Yang69b00b52015-11-22 16:10:44 -0500151 def skip_if_microversion_lt(self, microversion):
152 if utils.is_microversion_lt(CONF.share.max_api_microversion,
153 microversion):
154 raise self.skipException(
155 "Microversion must be greater than or equal to '%s'." %
156 microversion)
157
Marc Koderer0abc93b2015-07-15 09:18:35 +0200158 @classmethod
Tom Barron4b8834a2017-02-02 11:02:20 -0500159 def _get_dynamic_creds(cls, name, network_resources=None):
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100160 identity_version = CONF.identity.auth_version
161 if identity_version == 'v3':
162 identity_uri = CONF.identity.uri_v3
163 identity_admin_endpoint_type = CONF.identity.v3_endpoint_type
164 elif identity_version == 'v2':
165 identity_uri = CONF.identity.uri
166 identity_admin_endpoint_type = CONF.identity.v2_admin_endpoint_type
167
Tom Barron4b8834a2017-02-02 11:02:20 -0500168 return dynamic_creds.DynamicCredentialProvider(
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100169 identity_version=identity_version,
Tom Barron4b8834a2017-02-02 11:02:20 -0500170 name=name,
171 network_resources=network_resources,
172 credentials_domain=CONF.auth.default_credentials_domain_name,
173 admin_role=CONF.identity.admin_role,
174 admin_creds=common_creds.get_configured_admin_credentials(),
175 identity_admin_domain_scope=CONF.identity.admin_domain_scope,
176 identity_admin_role=CONF.identity.admin_role,
177 extra_roles=None,
178 neutron_available=CONF.service_available.neutron,
179 create_networks=(
180 CONF.share.create_networks_when_multitenancy_enabled),
181 project_network_cidr=CONF.network.project_network_cidr,
182 project_network_mask_bits=CONF.network.project_network_mask_bits,
183 public_network_id=CONF.network.public_network_id,
ghanshyam2aea7c32017-12-11 00:03:56 +0000184 resource_prefix='tempest',
Raissa Sarmento7b86b9d2017-07-21 14:32:48 +0100185 identity_admin_endpoint_type=identity_admin_endpoint_type,
186 identity_uri=identity_uri)
Tom Barron4b8834a2017-02-02 11:02:20 -0500187
188 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200189 def get_client_with_isolated_creds(cls,
190 name=None,
191 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400192 cleanup_in_class=False,
193 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200194 """Creates isolated creds.
195
196 :param name: name, will be used for naming ic and related stuff
197 :param type_of_creds: admin, alt or primary
198 :param cleanup_in_class: defines place where to delete
199 :returns: SharesClient -- shares client with isolated creds.
200 :returns: To client added dict attr 'creds' with
201 :returns: key elements 'tenant' and 'user'.
202 """
203 if name is None:
204 # Get name of test method
205 name = inspect.stack()[1][3]
206 if len(name) > 32:
207 name = name[0:32]
208
209 # Choose type of isolated creds
Tom Barron4b8834a2017-02-02 11:02:20 -0500210 ic = cls._get_dynamic_creds(name)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200211 if "admin" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200212 creds = ic.get_admin_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200213 elif "alt" in type_of_creds:
Marc Koderer5880b362016-07-06 10:59:07 +0200214 creds = ic.get_alt_creds().credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200215 else:
Marc Koderer5880b362016-07-06 10:59:07 +0200216 creds = ic.get_credentials(type_of_creds).credentials
Marc Koderer0abc93b2015-07-15 09:18:35 +0200217 ic.type_of_creds = type_of_creds
218
219 # create client with isolated creds
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100220 os = clients.Clients(creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400221 if client_version == '1':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100222 client = os.share_v1.SharesClient()
Clinton Knighte5c8f092015-08-27 15:00:23 -0400223 elif client_version == '2':
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100224 client = os.share_v2.SharesV2Client()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200225
226 # Set place where will be deleted isolated creds
227 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200228 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200229 "deleted": False,
230 }
231 if cleanup_in_class:
232 cls.class_isolated_creds.insert(0, ic_res)
233 else:
234 cls.method_isolated_creds.insert(0, ic_res)
235
236 # Provide share network
237 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300238 if (not CONF.service_available.neutron and
239 CONF.share.create_networks_when_multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200240 raise cls.skipException("Neutron support is required")
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100241 nc = os.network.NetworksClient()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200242 share_network_id = cls.provide_share_network(client, nc, ic)
243 client.share_network_id = share_network_id
244 resource = {
245 "type": "share_network",
246 "id": client.share_network_id,
247 "client": client,
248 }
249 if cleanup_in_class:
250 cls.class_resources.insert(0, resource)
251 else:
252 cls.method_resources.insert(0, resource)
253 return client
254
255 @classmethod
Daniel Melladoe5269142017-01-12 12:17:58 +0000256 def skip_checks(cls):
257 super(BaseSharesTest, cls).skip_checks()
258 if not CONF.service_available.manila:
259 raise cls.skipException("Manila support is required")
260
261 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200262 def verify_nonempty(cls, *args):
263 if not all(args):
264 msg = "Missing API credentials in configuration."
265 raise cls.skipException(msg)
266
267 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300268 def setup_clients(cls):
269 super(BaseSharesTest, cls).setup_clients()
270 os = getattr(cls, 'os_%s' % cls.credentials[0])
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100271 # Initialise share clients for test credentials
272 cls.shares_client = os.share_v1.SharesClient()
273 cls.shares_v2_client = os.share_v2.SharesV2Client()
274 # Initialise network clients for test credentials
275 if CONF.service_available.neutron:
276 cls.networks_client = os.network.NetworksClient()
277 cls.subnets_client = os.network.SubnetsClient()
278 else:
279 cls.networks_client = None
280 cls.subnets_client = None
Valeriy Ponomaryov4fb305f2016-10-21 13:46:47 +0300281
282 if CONF.identity.auth_version == 'v3':
283 project_id = os.auth_provider.auth_data[1]['project']['id']
284 else:
285 project_id = os.auth_provider.auth_data[1]['token']['tenant']['id']
286 cls.tenant_id = project_id
287 cls.user_id = os.auth_provider.auth_data[1]['user']['id']
288
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300289 if CONF.share.multitenancy_enabled:
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300290 if (not CONF.service_available.neutron and
291 CONF.share.create_networks_when_multitenancy_enabled):
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300292 raise cls.skipException("Neutron support is required")
293 share_network_id = cls.provide_share_network(
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +0100294 cls.shares_v2_client, cls.networks_client)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300295 cls.shares_client.share_network_id = share_network_id
296 cls.shares_v2_client.share_network_id = share_network_id
haobing101c7fee2018-03-09 16:33:00 +0800297 resource = {
298 "type": "share_network",
299 "id": share_network_id,
300 "client": cls.shares_v2_client,
301 }
302 cls.class_resources.insert(0, resource)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300303
304 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200305 def resource_setup(cls):
306 if not (any(p in CONF.share.enable_protocols
307 for p in cls.protocols) and
308 CONF.service_available.manila):
309 skip_msg = "Manila is disabled"
310 raise cls.skipException(skip_msg)
311 super(BaseSharesTest, cls).resource_setup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200312
313 def setUp(self):
314 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200315 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200316 self.addCleanup(self.clear_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300317 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200318
319 @classmethod
320 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200321 cls.clear_resources(cls.class_resources)
322 cls.clear_isolated_creds(cls.class_isolated_creds)
Sam Wan241029c2016-07-26 03:37:42 -0400323 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200324
325 @classmethod
326 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200327 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300328 isolated_creds_client=None,
329 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200330 """Used for finding/creating share network for multitenant driver.
331
332 This method creates/gets entity share-network for one tenant. This
333 share-network will be used for creation of service vm.
334
335 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200336 :param networks_client: network client from same tenant as shares
337 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200338 If provided, then its networking will be used if needed.
339 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300340 :param ignore_multitenancy_config: provide a share network regardless
341 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200342 :returns: str -- share network id for shares_client tenant
343 :returns: None -- if single-tenant driver used
344 """
345
346 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300347 search_word = "reusable"
348 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200349
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300350 if (not ignore_multitenancy_config and
351 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200352 # Assumed usage of a single-tenant driver
353 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200354 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300355 if sc.share_network_id:
356 # Share-network already exists, use it
357 share_network_id = sc.share_network_id
358 elif not CONF.share.create_networks_when_multitenancy_enabled:
359 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200360
361 # Try get suitable share-network
362 share_networks = sc.list_share_networks_with_detail()
363 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300364 if (sn["neutron_net_id"] is None and
365 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200366 sn["name"] and search_word in sn["name"]):
367 share_network_id = sn["id"]
368 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200369
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300370 # Create new share-network if one was not found
371 if share_network_id is None:
372 sn_desc = "This share-network was created by tempest"
373 sn = sc.create_share_network(name=sn_name,
374 description=sn_desc)
375 share_network_id = sn["id"]
376 else:
377 net_id = subnet_id = share_network_id = None
378
379 if not isolated_creds_client:
380 # Search for networks, created in previous runs
381 service_net_name = "share-service"
382 networks = networks_client.list_networks()
383 if "networks" in networks.keys():
384 networks = networks["networks"]
385 for network in networks:
386 if (service_net_name in network["name"] and
387 sc.tenant_id == network['tenant_id']):
388 net_id = network["id"]
389 if len(network["subnets"]) > 0:
390 subnet_id = network["subnets"][0]
391 break
392
393 # Create suitable network
394 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500395 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300396 net_data = ic._create_network_resources(sc.tenant_id)
397 network, subnet, router = net_data
398 net_id = network["id"]
399 subnet_id = subnet["id"]
400
401 # Try get suitable share-network
402 share_networks = sc.list_share_networks_with_detail()
403 for sn in share_networks:
404 if (net_id == sn["neutron_net_id"] and
405 subnet_id == sn["neutron_subnet_id"] and
406 sn["name"] and search_word in sn["name"]):
407 share_network_id = sn["id"]
408 break
409 else:
410 sn_name = "autogenerated_by_tempest_for_isolated_creds"
411 # Use precreated network and subnet from isolated creds
412 net_id = isolated_creds_client.get_credentials(
413 isolated_creds_client.type_of_creds).network['id']
414 subnet_id = isolated_creds_client.get_credentials(
415 isolated_creds_client.type_of_creds).subnet['id']
416
417 # Create suitable share-network
418 if share_network_id is None:
419 sn_desc = "This share-network was created by tempest"
420 sn = sc.create_share_network(name=sn_name,
421 description=sn_desc,
422 neutron_net_id=net_id,
423 neutron_subnet_id=subnet_id)
424 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200425
426 return share_network_id
427
428 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300429 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200430 snapshot_id=None, description=None, metadata=None,
431 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400432 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400433 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300434 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200435 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400436 share_network_id = (share_network_id or
437 CONF.share.share_network_id or
438 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200439 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300440 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400441 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200442 'share_protocol': share_protocol,
443 'size': size,
444 'name': name,
445 'snapshot_id': snapshot_id,
446 'description': description,
447 'metadata': metadata,
448 'share_network_id': share_network_id,
449 'share_type_id': share_type_id,
450 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400451 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400452 if share_group_id:
453 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400454
Marc Koderer0abc93b2015-07-15 09:18:35 +0200455 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400456 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400457 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200458 cleanup_list = (cls.class_resources if cleanup_in_class else
459 cls.method_resources)
460 cleanup_list.insert(0, resource)
461 return share
462
463 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300464 def migrate_share(
465 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200466 force_host_assisted_migration=False, writable=False,
467 nondisruptive=False, preserve_metadata=False,
468 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300469 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400470 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300471 client.migrate_share(
472 share_id, dest_host,
473 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200474 writable=writable, preserve_metadata=preserve_metadata,
475 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300476 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300477 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200478 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300479 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200480 return share
481
482 @classmethod
483 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
484 client = client or cls.shares_v2_client
485 client.migration_complete(share_id, **kwargs)
486 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300487 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300488 return share
489
490 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300491 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
492 client = client or cls.shares_v2_client
493 client.migration_cancel(share_id, **kwargs)
494 share = client.wait_for_migration_status(
495 share_id, dest_host, 'migration_cancelled', **kwargs)
496 return share
497
498 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200499 def create_share(cls, *args, **kwargs):
500 """Create one share and wait for available state. Retry if allowed."""
501 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
502 return result[0]
503
504 @classmethod
505 def create_shares(cls, share_data_list):
506 """Creates several shares in parallel with retries.
507
508 Use this method when you want to create more than one share at same
509 time. Especially if config option 'share.share_creation_retry_number'
510 has value more than zero (0).
511 All shares will be expected to have 'available' status with or without
512 recreation else error will be raised.
513
514 :param share_data_list: list -- list of dictionaries with 'args' and
515 'kwargs' for '_create_share' method of this base class.
516 example of data:
517 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
518 :returns: list -- list of shares created using provided data.
519 """
520
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300521 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200522 if not isinstance(d, dict):
523 raise exceptions.TempestException(
524 "Expected 'dict', got '%s'" % type(d))
525 if "args" not in d:
526 d["args"] = []
527 if "kwargs" not in d:
528 d["kwargs"] = {}
529 if len(d) > 2:
530 raise exceptions.TempestException(
531 "Expected only 'args' and 'kwargs' keys. "
532 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300533
534 data = []
535 for d in share_data_list:
536 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400537 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300538 local_d = {
539 "args": d["args"],
540 "kwargs": copy.deepcopy(d["kwargs"]),
541 }
542 local_d["kwargs"]["client"] = client
543 local_d["share"] = cls._create_share(
544 *local_d["args"], **local_d["kwargs"])
545 local_d["cnt"] = 0
546 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400547 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300548 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200549
550 while not all(d["available"] for d in data):
551 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400552 if not d["wait_for_status"]:
553 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200554 if d["available"]:
555 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300556 client = d["kwargs"]["client"]
557 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200558 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300559 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200560 d["available"] = True
561 except (share_exceptions.ShareBuildErrorException,
562 exceptions.TimeoutException) as e:
563 if CONF.share.share_creation_retry_number > d["cnt"]:
564 d["cnt"] += 1
565 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300566 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200567 LOG.error(msg)
568 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300569 cg_id = d["kwargs"].get("consistency_group_id")
570 if cg_id:
571 # NOTE(vponomaryov): delete errored share
572 # immediately in case share is part of CG.
573 client.delete_share(
574 share_id,
575 params={"consistency_group_id": cg_id})
576 client.wait_for_resource_deletion(
577 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200578 d["share"] = cls._create_share(
579 *d["args"], **d["kwargs"])
580 else:
gecong197358663802016-08-25 11:08:45 +0800581 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200582
583 return [d["share"] for d in data]
584
585 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400586 def create_share_group(cls, client=None, cleanup_in_class=True,
587 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400588 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400589 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400590 kwargs['share_network_id'] = (share_network_id or
591 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400592 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400593 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400594 "type": "share_group",
595 "id": share_group["id"],
596 "client": client,
597 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400598 if cleanup_in_class:
599 cls.class_resources.insert(0, resource)
600 else:
601 cls.method_resources.insert(0, resource)
602
Andrew Kerrb8436922016-06-01 15:32:43 -0400603 if kwargs.get('source_share_group_snapshot_id'):
604 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400605 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400606 params={'share_group_id': share_group['id']},
607 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400608
Andrew Kerrb8436922016-06-01 15:32:43 -0400609 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400610 resource = {"type": "share",
611 "id": share["id"],
612 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400613 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400614 if cleanup_in_class:
615 cls.class_resources.insert(0, resource)
616 else:
617 cls.method_resources.insert(0, resource)
618
Andrew Kerrb8436922016-06-01 15:32:43 -0400619 client.wait_for_share_group_status(share_group['id'], 'available')
620 return share_group
621
622 @classmethod
623 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
624 group_specs=None, client=None,
625 cleanup_in_class=True, **kwargs):
626 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300627 if (group_specs is None and
628 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300629 group_specs = {
630 'consistent_snapshot_support': (
631 CONF.share.capability_sg_consistent_snapshot_support),
632 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400633 share_group_type = client.create_share_group_type(
634 name=name,
635 share_types=share_types,
636 is_public=is_public,
637 group_specs=group_specs,
638 **kwargs)
639 resource = {
640 "type": "share_group_type",
641 "id": share_group_type["id"],
642 "client": client,
643 }
644 if cleanup_in_class:
645 cls.class_resources.insert(0, resource)
646 else:
647 cls.method_resources.insert(0, resource)
648 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400649
650 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200651 def create_snapshot_wait_for_active(cls, share_id, name=None,
652 description=None, force=False,
653 client=None, cleanup_in_class=True):
654 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400655 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200656 if description is None:
657 description = "Tempest's snapshot"
658 snapshot = client.create_snapshot(share_id, name, description, force)
659 resource = {
660 "type": "snapshot",
661 "id": snapshot["id"],
662 "client": client,
663 }
664 if cleanup_in_class:
665 cls.class_resources.insert(0, resource)
666 else:
667 cls.method_resources.insert(0, resource)
668 client.wait_for_snapshot_status(snapshot["id"], "available")
669 return snapshot
670
671 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400672 def create_share_group_snapshot_wait_for_active(
673 cls, share_group_id, name=None, description=None, client=None,
674 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400675 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400676 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400677 description = "Tempest's share group snapshot"
678 sg_snapshot = client.create_share_group_snapshot(
679 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400680 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400681 "type": "share_group_snapshot",
682 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400683 "client": client,
684 }
685 if cleanup_in_class:
686 cls.class_resources.insert(0, resource)
687 else:
688 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400689 client.wait_for_share_group_snapshot_status(
690 sg_snapshot["id"], "available")
691 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400692
693 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800694 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400695 """List the availability zones for "manila-share" services
696
697 that are currently in "up" state.
698 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800699 client = client or cls.admin_shares_v2_client
700 backends = (
701 '|'.join(['^%s$' % backend for backend in backends])
702 if backends else '.*'
703 )
Yogeshbdb88102015-09-29 23:41:02 -0400704 cls.services = client.list_services()
705 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800706 service['binary'] == 'manila-share' and
707 service['state'] == 'up' and
708 re.search(backends, service['host'])]
Yogeshbdb88102015-09-29 23:41:02 -0400709 return zones
710
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800711 @classmethod
712 def get_pools_matching_share_type(cls, share_type, client=None):
713 client = client or cls.admin_shares_v2_client
714 if utils.is_microversion_supported('2.23'):
715 return client.list_pools(
716 search_opts={'share_type': share_type['id']})['pools']
717
718 pools = client.list_pools(detail=True)['pools']
719 share_type = client.get_share_type(share_type['id'])['share_type']
720 extra_specs = {}
721 for k, v in share_type['extra_specs'].items():
722 extra_specs[k] = (
723 True if six.text_type(v).lower() == 'true'
724 else False if six.text_type(v).lower() == 'false' else v
725 )
726 return [
727 pool for pool in pools if all(y in pool['capabilities'].items()
728 for y in extra_specs.items())
729 ]
730
731 @classmethod
732 def get_availability_zones_matching_share_type(cls, share_type,
733 client=None):
734
735 client = client or cls.admin_shares_v2_client
736 pools_matching_share_type = cls.get_pools_matching_share_type(
737 share_type, client=client)
738 backends_matching_share_type = set(
739 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
740 )
741 azs = cls.get_availability_zones(backends=backends_matching_share_type)
742 return azs
743
Yogesh1f931ff2015-09-29 23:41:02 -0400744 def get_pools_for_replication_domain(self):
745 # Get the list of pools for the replication domain
746 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500747 instance_host = self.admin_client.get_share(
748 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400749 host_pool = [p for p in pools if p['name'] == instance_host][0]
750 rep_domain = host_pool['capabilities']['replication_domain']
751 pools_in_rep_domain = [p for p in pools if p['capabilities'][
752 'replication_domain'] == rep_domain]
753 return rep_domain, pools_in_rep_domain
754
Yogeshbdb88102015-09-29 23:41:02 -0400755 @classmethod
756 def create_share_replica(cls, share_id, availability_zone, client=None,
757 cleanup_in_class=False, cleanup=True):
758 client = client or cls.shares_v2_client
759 replica = client.create_share_replica(share_id, availability_zone)
760 resource = {
761 "type": "share_replica",
762 "id": replica["id"],
763 "client": client,
764 "share_id": share_id,
765 }
766 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
767 if cleanup:
768 if cleanup_in_class:
769 cls.class_resources.insert(0, resource)
770 else:
771 cls.method_resources.insert(0, resource)
772 client.wait_for_share_replica_status(
773 replica["id"], constants.STATUS_AVAILABLE)
774 return replica
775
776 @classmethod
777 def delete_share_replica(cls, replica_id, client=None):
778 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400779 try:
780 client.delete_share_replica(replica_id)
781 client.wait_for_resource_deletion(replica_id=replica_id)
782 except exceptions.NotFound:
783 pass
Yogeshbdb88102015-09-29 23:41:02 -0400784
785 @classmethod
786 def promote_share_replica(cls, replica_id, client=None):
787 client = client or cls.shares_v2_client
788 replica = client.promote_share_replica(replica_id)
789 client.wait_for_share_replica_status(
790 replica["id"],
791 constants.REPLICATION_STATE_ACTIVE,
792 status_attr="replica_state")
793 return replica
794
yogeshdb32f462016-09-28 15:09:50 -0400795 def _get_access_rule_data_from_config(self):
796 """Get the first available access type/to combination from config.
797
798 This method opportunistically picks the first configured protocol
799 to create the share. Do not use this method in tests where you need
800 to test depth and breadth in the access types and access recipients.
801 """
802 protocol = self.shares_v2_client.share_protocol
803
804 if protocol in CONF.share.enable_ip_rules_for_protocols:
805 access_type = "ip"
806 access_to = utils.rand_ip()
807 elif protocol in CONF.share.enable_user_rules_for_protocols:
808 access_type = "user"
809 access_to = CONF.share.username_for_user_rules
810 elif protocol in CONF.share.enable_cert_rules_for_protocols:
811 access_type = "cert"
812 access_to = "client3.com"
813 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
814 access_type = "cephx"
815 access_to = "eve"
816 else:
817 message = "Unrecognized protocol and access rules configuration."
818 raise self.skipException(message)
819
820 return access_type, access_to
821
Yogeshbdb88102015-09-29 23:41:02 -0400822 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200823 def create_share_network(cls, client=None,
824 cleanup_in_class=False, **kwargs):
825 if client is None:
826 client = cls.shares_client
827 share_network = client.create_share_network(**kwargs)
828 resource = {
829 "type": "share_network",
830 "id": share_network["id"],
831 "client": client,
832 }
833 if cleanup_in_class:
834 cls.class_resources.insert(0, resource)
835 else:
836 cls.method_resources.insert(0, resource)
837 return share_network
838
839 @classmethod
840 def create_security_service(cls, ss_type="ldap", client=None,
841 cleanup_in_class=False, **kwargs):
842 if client is None:
843 client = cls.shares_client
844 security_service = client.create_security_service(ss_type, **kwargs)
845 resource = {
846 "type": "security_service",
847 "id": security_service["id"],
848 "client": client,
849 }
850 if cleanup_in_class:
851 cls.class_resources.insert(0, resource)
852 else:
853 cls.method_resources.insert(0, resource)
854 return security_service
855
856 @classmethod
857 def create_share_type(cls, name, is_public=True, client=None,
858 cleanup_in_class=True, **kwargs):
859 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200860 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200861 share_type = client.create_share_type(name, is_public, **kwargs)
862 resource = {
863 "type": "share_type",
864 "id": share_type["share_type"]["id"],
865 "client": client,
866 }
867 if cleanup_in_class:
868 cls.class_resources.insert(0, resource)
869 else:
870 cls.method_resources.insert(0, resource)
871 return share_type
872
haixin0d1d29f2019-08-02 16:50:45 +0800873 @classmethod
874 def update_share_type(cls, share_type_id, name=None,
875 is_public=None, description=None,
876 client=None):
877 if client is None:
878 client = cls.shares_v2_client
879 share_type = client.update_share_type(share_type_id, name,
880 is_public, description)
881 return share_type
882
Marc Koderer0abc93b2015-07-15 09:18:35 +0200883 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400884 def add_extra_specs_to_dict(extra_specs=None):
885 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300886 dhss = six.text_type(CONF.share.multitenancy_enabled)
887 snapshot_support = six.text_type(
888 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400889 create_from_snapshot_support = six.text_type(
890 CONF.share.capability_create_share_from_snapshot_support)
891
892 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300893 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200894 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400895
896 optional = {
897 "snapshot_support": snapshot_support,
898 "create_share_from_snapshot_support": create_from_snapshot_support,
899 }
900 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
901 # required extra-spec
902 extra_specs_dict.update(optional)
903
Marc Koderer0abc93b2015-07-15 09:18:35 +0200904 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400905 extra_specs_dict.update(extra_specs)
906
907 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200908
909 @classmethod
910 def clear_isolated_creds(cls, creds=None):
911 if creds is None:
912 creds = cls.method_isolated_creds
913 for ic in creds:
914 if "deleted" not in ic.keys():
915 ic["deleted"] = False
916 if not ic["deleted"]:
917 with handle_cleanup_exceptions():
918 ic["method"]()
919 ic["deleted"] = True
920
921 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400922 def clear_share_replicas(cls, share_id, client=None):
923 client = client or cls.shares_v2_client
924 share_replicas = client.list_share_replicas(
925 share_id=share_id)
926
927 for replica in share_replicas:
928 try:
929 cls.delete_share_replica(replica['id'])
930 except exceptions.BadRequest:
931 # Ignore the exception due to deletion of last active replica
932 pass
933
934 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200935 def clear_resources(cls, resources=None):
936 """Deletes resources, that were created in test suites.
937
938 This method tries to remove resources from resource list,
939 if it is not found, assumed it was deleted in test itself.
940 It is expected, that all resources were added as LIFO
941 due to restriction of deletion resources, that is in the chain.
942
943 :param resources: dict with keys 'type','id','client' and 'deleted'
944 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200945 if resources is None:
946 resources = cls.method_resources
947 for res in resources:
948 if "deleted" not in res.keys():
949 res["deleted"] = False
950 if "client" not in res.keys():
951 res["client"] = cls.shares_client
952 if not(res["deleted"]):
953 res_id = res['id']
954 client = res["client"]
955 with handle_cleanup_exceptions():
956 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400957 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400958 share_group_id = res.get('share_group_id')
959 if share_group_id:
960 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400961 client.delete_share(res_id, params=params)
962 else:
963 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200964 client.wait_for_resource_deletion(share_id=res_id)
965 elif res["type"] is "snapshot":
966 client.delete_snapshot(res_id)
967 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400968 elif (res["type"] is "share_network" and
969 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400970 client.delete_share_network(res_id)
971 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200972 elif res["type"] is "security_service":
973 client.delete_security_service(res_id)
974 client.wait_for_resource_deletion(ss_id=res_id)
975 elif res["type"] is "share_type":
976 client.delete_share_type(res_id)
977 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400978 elif res["type"] is "share_group":
979 client.delete_share_group(res_id)
980 client.wait_for_resource_deletion(
981 share_group_id=res_id)
982 elif res["type"] is "share_group_type":
983 client.delete_share_group_type(res_id)
984 client.wait_for_resource_deletion(
985 share_group_type_id=res_id)
986 elif res["type"] is "share_group_snapshot":
987 client.delete_share_group_snapshot(res_id)
988 client.wait_for_resource_deletion(
989 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400990 elif res["type"] is "share_replica":
991 client.delete_share_replica(res_id)
992 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200993 else:
huayue97bacbf2016-01-04 09:57:39 +0800994 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800995 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200996 res["deleted"] = True
997
998 @classmethod
999 def generate_share_network_data(self):
1000 data = {
1001 "name": data_utils.rand_name("sn-name"),
1002 "description": data_utils.rand_name("sn-desc"),
1003 "neutron_net_id": data_utils.rand_name("net-id"),
1004 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1005 }
1006 return data
1007
1008 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001009 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001010 data = {
1011 "name": data_utils.rand_name("ss-name"),
1012 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001013 "dns_ip": utils.rand_ip(),
1014 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001015 "domain": data_utils.rand_name("ss-domain"),
1016 "user": data_utils.rand_name("ss-user"),
1017 "password": data_utils.rand_name("ss-password"),
1018 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001019 if set_ou:
1020 data["ou"] = data_utils.rand_name("ss-ou")
1021
Marc Koderer0abc93b2015-07-15 09:18:35 +02001022 return data
1023
1024 # Useful assertions
1025 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1026 """Assert two dicts are equivalent.
1027
1028 This is a 'deep' match in the sense that it handles nested
1029 dictionaries appropriately.
1030
1031 NOTE:
1032
1033 If you don't care (or don't know) a given value, you can specify
1034 the string DONTCARE as the value. This will cause that dict-item
1035 to be skipped.
1036
1037 """
1038 def raise_assertion(msg):
1039 d1str = str(d1)
1040 d2str = str(d2)
1041 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1042 'd2: %(d2str)s' %
1043 {"msg": msg, "d1str": d1str, "d2str": d2str})
1044 raise AssertionError(base_msg)
1045
1046 d1keys = set(d1.keys())
1047 d2keys = set(d2.keys())
1048 if d1keys != d2keys:
1049 d1only = d1keys - d2keys
1050 d2only = d2keys - d1keys
1051 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1052 'Keys in d2 and not d1: %(d2only)s' %
1053 {"d1only": d1only, "d2only": d2only})
1054
1055 for key in d1keys:
1056 d1value = d1[key]
1057 d2value = d2[key]
1058 try:
1059 error = abs(float(d1value) - float(d2value))
1060 within_tolerance = error <= tolerance
1061 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001062 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001063 # ValueError if arg is a str, TypeError if it's something else
1064 # (like None)
1065 within_tolerance = False
1066
1067 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1068 self.assertDictMatch(d1value, d2value)
1069 elif 'DONTCARE' in (d1value, d2value):
1070 continue
1071 elif approx_equal and within_tolerance:
1072 continue
1073 elif d1value != d2value:
1074 raise_assertion("d1['%(key)s']=%(d1value)s != "
1075 "d2['%(key)s']=%(d2value)s" %
1076 {
1077 "key": key,
1078 "d1value": d1value,
1079 "d2value": d2value
1080 })
1081
Alex Meadeba8a1602016-05-06 09:33:09 -04001082 def create_user_message(self):
1083 """Trigger a 'no valid host' situation to generate a message."""
1084 extra_specs = {
1085 'vendor_name': 'foobar',
1086 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1087 }
1088 share_type_name = data_utils.rand_name("share-type")
1089
1090 bogus_type = self.create_share_type(
1091 name=share_type_name,
1092 extra_specs=extra_specs)['share_type']
1093
1094 params = {'share_type_id': bogus_type['id'],
1095 'share_network_id': self.shares_v2_client.share_network_id}
1096 share = self.shares_v2_client.create_share(**params)
1097 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1098 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1099 return self.shares_v2_client.wait_for_message(share['id'])
1100
Marc Koderer0abc93b2015-07-15 09:18:35 +02001101
1102class BaseSharesAltTest(BaseSharesTest):
1103 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001104 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001105
1106
1107class BaseSharesAdminTest(BaseSharesTest):
1108 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001109 credentials = ('admin', )
1110
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001111 @classmethod
1112 def setup_clients(cls):
1113 super(BaseSharesAdminTest, cls).setup_clients()
1114 # Initialise share clients
1115 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1116
1117 @classmethod
1118 def _create_share_type(cls, specs=None):
1119 name = data_utils.rand_name("unique_st_name")
1120 extra_specs = cls.add_extra_specs_to_dict(specs)
1121 return cls.create_share_type(
1122 name, extra_specs=extra_specs,
1123 client=cls.admin_shares_v2_client)['share_type']
1124
1125 @classmethod
1126 def _create_share_group_type(cls):
1127 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1128 return cls.create_share_group_type(
1129 name=share_group_type_name, share_types=[cls.share_type_id],
1130 client=cls.admin_shares_v2_client)
1131
Lucio Seki37056942019-01-24 15:40:20 -02001132 def _create_share_for_manage(self):
1133 creation_data = {
1134 'share_type_id': self.st['share_type']['id'],
1135 'share_protocol': self.protocol,
1136 }
1137
1138 share = self.create_share(**creation_data)
1139 share = self.shares_v2_client.get_share(share['id'])
1140
1141 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1142 el = self.shares_v2_client.list_share_export_locations(share["id"])
1143 share["export_locations"] = el
1144
1145 return share
1146
1147 def _unmanage_share_and_wait(self, share):
1148 self.shares_v2_client.unmanage_share(share['id'])
1149 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1150
1151 def _reset_state_and_delete_share(self, share):
1152 self.shares_v2_client.reset_state(share['id'])
1153 self._delete_share_and_wait(share)
1154
1155 def _delete_snapshot_and_wait(self, snap):
1156 self.shares_v2_client.delete_snapshot(snap['id'])
1157 self.shares_v2_client.wait_for_resource_deletion(
1158 snapshot_id=snap['id']
1159 )
1160 self.assertRaises(exceptions.NotFound,
1161 self.shares_v2_client.get_snapshot,
1162 snap['id'])
1163
1164 def _delete_share_and_wait(self, share):
1165 self.shares_v2_client.delete_share(share['id'])
1166 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1167 self.assertRaises(exceptions.NotFound,
1168 self.shares_v2_client.get_share,
1169 share['id'])
1170
1171 def _manage_share(self, share, name, description, share_server_id):
1172 managed_share = self.shares_v2_client.manage_share(
1173 service_host=share['host'],
1174 export_path=share['export_locations'][0],
1175 protocol=share['share_proto'],
1176 share_type_id=self.share_type['share_type']['id'],
1177 name=name,
1178 description=description,
1179 share_server_id=share_server_id
1180 )
1181 self.shares_v2_client.wait_for_share_status(
1182 managed_share['id'], constants.STATUS_AVAILABLE
1183 )
1184
1185 return managed_share
1186
1187 def _unmanage_share_server_and_wait(self, server):
1188 self.shares_v2_client.unmanage_share_server(server['id'])
1189 self.shares_v2_client.wait_for_resource_deletion(
1190 server_id=server['id']
1191 )
1192
1193 def _manage_share_server(self, share_server, fields=None):
1194 params = fields or {}
1195 managed_share_server = self.shares_v2_client.manage_share_server(
1196 params.get('host', share_server['host']),
1197 params.get('share_network_id', share_server['share_network_id']),
1198 params.get('identifier', share_server['identifier']),
1199 )
1200 self.shares_v2_client.wait_for_share_server_status(
1201 managed_share_server['id'],
1202 constants.SERVER_STATE_ACTIVE,
1203 )
1204
1205 return managed_share_server
1206
1207 def _delete_share_server_and_wait(self, share_server_id):
1208 self.shares_v2_client.delete_share_server(
1209 share_server_id
1210 )
1211 self.shares_v2_client.wait_for_resource_deletion(
1212 server_id=share_server_id)
1213
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001214
1215class BaseSharesMixedTest(BaseSharesTest):
1216 """Base test case class for all Shares API tests with all user roles."""
1217 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001218
1219 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001220 def setup_clients(cls):
1221 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001222 # Initialise share clients
1223 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1224 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1225 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1226 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1227 # Initialise network clients
1228 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1229 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001230
1231 if CONF.share.multitenancy_enabled:
1232 admin_share_network_id = cls.provide_share_network(
1233 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1234 cls.admin_shares_client.share_network_id = admin_share_network_id
1235 cls.admin_shares_v2_client.share_network_id = (
1236 admin_share_network_id)
1237
1238 alt_share_network_id = cls.provide_share_network(
1239 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1240 cls.alt_shares_client.share_network_id = alt_share_network_id
1241 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001242 resource = {
1243 "type": "share_network",
1244 "id": alt_share_network_id,
1245 "client": cls.alt_shares_v2_client,
1246 }
1247 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001248
1249 @classmethod
1250 def _create_share_type(cls, specs=None):
1251 name = data_utils.rand_name("unique_st_name")
1252 extra_specs = cls.add_extra_specs_to_dict(specs)
1253 return cls.create_share_type(
1254 name, extra_specs=extra_specs,
1255 client=cls.admin_shares_v2_client)['share_type']
1256
1257 @classmethod
1258 def _create_share_group_type(cls):
1259 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1260 return cls.create_share_group_type(
1261 name=share_group_type_name, share_types=[cls.share_type_id],
1262 client=cls.admin_shares_v2_client)