blob: eca162a73bd86e3f39dad79984ffa85de2bb216d [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
873 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400874 def add_extra_specs_to_dict(extra_specs=None):
875 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300876 dhss = six.text_type(CONF.share.multitenancy_enabled)
877 snapshot_support = six.text_type(
878 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400879 create_from_snapshot_support = six.text_type(
880 CONF.share.capability_create_share_from_snapshot_support)
881
882 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300883 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200884 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400885
886 optional = {
887 "snapshot_support": snapshot_support,
888 "create_share_from_snapshot_support": create_from_snapshot_support,
889 }
890 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
891 # required extra-spec
892 extra_specs_dict.update(optional)
893
Marc Koderer0abc93b2015-07-15 09:18:35 +0200894 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400895 extra_specs_dict.update(extra_specs)
896
897 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200898
899 @classmethod
900 def clear_isolated_creds(cls, creds=None):
901 if creds is None:
902 creds = cls.method_isolated_creds
903 for ic in creds:
904 if "deleted" not in ic.keys():
905 ic["deleted"] = False
906 if not ic["deleted"]:
907 with handle_cleanup_exceptions():
908 ic["method"]()
909 ic["deleted"] = True
910
911 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400912 def clear_share_replicas(cls, share_id, client=None):
913 client = client or cls.shares_v2_client
914 share_replicas = client.list_share_replicas(
915 share_id=share_id)
916
917 for replica in share_replicas:
918 try:
919 cls.delete_share_replica(replica['id'])
920 except exceptions.BadRequest:
921 # Ignore the exception due to deletion of last active replica
922 pass
923
924 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200925 def clear_resources(cls, resources=None):
926 """Deletes resources, that were created in test suites.
927
928 This method tries to remove resources from resource list,
929 if it is not found, assumed it was deleted in test itself.
930 It is expected, that all resources were added as LIFO
931 due to restriction of deletion resources, that is in the chain.
932
933 :param resources: dict with keys 'type','id','client' and 'deleted'
934 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200935 if resources is None:
936 resources = cls.method_resources
937 for res in resources:
938 if "deleted" not in res.keys():
939 res["deleted"] = False
940 if "client" not in res.keys():
941 res["client"] = cls.shares_client
942 if not(res["deleted"]):
943 res_id = res['id']
944 client = res["client"]
945 with handle_cleanup_exceptions():
946 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400947 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400948 share_group_id = res.get('share_group_id')
949 if share_group_id:
950 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400951 client.delete_share(res_id, params=params)
952 else:
953 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200954 client.wait_for_resource_deletion(share_id=res_id)
955 elif res["type"] is "snapshot":
956 client.delete_snapshot(res_id)
957 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400958 elif (res["type"] is "share_network" and
959 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400960 client.delete_share_network(res_id)
961 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200962 elif res["type"] is "security_service":
963 client.delete_security_service(res_id)
964 client.wait_for_resource_deletion(ss_id=res_id)
965 elif res["type"] is "share_type":
966 client.delete_share_type(res_id)
967 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400968 elif res["type"] is "share_group":
969 client.delete_share_group(res_id)
970 client.wait_for_resource_deletion(
971 share_group_id=res_id)
972 elif res["type"] is "share_group_type":
973 client.delete_share_group_type(res_id)
974 client.wait_for_resource_deletion(
975 share_group_type_id=res_id)
976 elif res["type"] is "share_group_snapshot":
977 client.delete_share_group_snapshot(res_id)
978 client.wait_for_resource_deletion(
979 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400980 elif res["type"] is "share_replica":
981 client.delete_share_replica(res_id)
982 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200983 else:
huayue97bacbf2016-01-04 09:57:39 +0800984 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800985 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200986 res["deleted"] = True
987
988 @classmethod
989 def generate_share_network_data(self):
990 data = {
991 "name": data_utils.rand_name("sn-name"),
992 "description": data_utils.rand_name("sn-desc"),
993 "neutron_net_id": data_utils.rand_name("net-id"),
994 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
995 }
996 return data
997
998 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +0100999 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001000 data = {
1001 "name": data_utils.rand_name("ss-name"),
1002 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001003 "dns_ip": utils.rand_ip(),
1004 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001005 "domain": data_utils.rand_name("ss-domain"),
1006 "user": data_utils.rand_name("ss-user"),
1007 "password": data_utils.rand_name("ss-password"),
1008 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001009 if set_ou:
1010 data["ou"] = data_utils.rand_name("ss-ou")
1011
Marc Koderer0abc93b2015-07-15 09:18:35 +02001012 return data
1013
1014 # Useful assertions
1015 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1016 """Assert two dicts are equivalent.
1017
1018 This is a 'deep' match in the sense that it handles nested
1019 dictionaries appropriately.
1020
1021 NOTE:
1022
1023 If you don't care (or don't know) a given value, you can specify
1024 the string DONTCARE as the value. This will cause that dict-item
1025 to be skipped.
1026
1027 """
1028 def raise_assertion(msg):
1029 d1str = str(d1)
1030 d2str = str(d2)
1031 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1032 'd2: %(d2str)s' %
1033 {"msg": msg, "d1str": d1str, "d2str": d2str})
1034 raise AssertionError(base_msg)
1035
1036 d1keys = set(d1.keys())
1037 d2keys = set(d2.keys())
1038 if d1keys != d2keys:
1039 d1only = d1keys - d2keys
1040 d2only = d2keys - d1keys
1041 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1042 'Keys in d2 and not d1: %(d2only)s' %
1043 {"d1only": d1only, "d2only": d2only})
1044
1045 for key in d1keys:
1046 d1value = d1[key]
1047 d2value = d2[key]
1048 try:
1049 error = abs(float(d1value) - float(d2value))
1050 within_tolerance = error <= tolerance
1051 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001052 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001053 # ValueError if arg is a str, TypeError if it's something else
1054 # (like None)
1055 within_tolerance = False
1056
1057 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1058 self.assertDictMatch(d1value, d2value)
1059 elif 'DONTCARE' in (d1value, d2value):
1060 continue
1061 elif approx_equal and within_tolerance:
1062 continue
1063 elif d1value != d2value:
1064 raise_assertion("d1['%(key)s']=%(d1value)s != "
1065 "d2['%(key)s']=%(d2value)s" %
1066 {
1067 "key": key,
1068 "d1value": d1value,
1069 "d2value": d2value
1070 })
1071
Alex Meadeba8a1602016-05-06 09:33:09 -04001072 def create_user_message(self):
1073 """Trigger a 'no valid host' situation to generate a message."""
1074 extra_specs = {
1075 'vendor_name': 'foobar',
1076 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1077 }
1078 share_type_name = data_utils.rand_name("share-type")
1079
1080 bogus_type = self.create_share_type(
1081 name=share_type_name,
1082 extra_specs=extra_specs)['share_type']
1083
1084 params = {'share_type_id': bogus_type['id'],
1085 'share_network_id': self.shares_v2_client.share_network_id}
1086 share = self.shares_v2_client.create_share(**params)
1087 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1088 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1089 return self.shares_v2_client.wait_for_message(share['id'])
1090
Marc Koderer0abc93b2015-07-15 09:18:35 +02001091
1092class BaseSharesAltTest(BaseSharesTest):
1093 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001094 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001095
1096
1097class BaseSharesAdminTest(BaseSharesTest):
1098 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001099 credentials = ('admin', )
1100
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001101 @classmethod
1102 def setup_clients(cls):
1103 super(BaseSharesAdminTest, cls).setup_clients()
1104 # Initialise share clients
1105 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1106
1107 @classmethod
1108 def _create_share_type(cls, specs=None):
1109 name = data_utils.rand_name("unique_st_name")
1110 extra_specs = cls.add_extra_specs_to_dict(specs)
1111 return cls.create_share_type(
1112 name, extra_specs=extra_specs,
1113 client=cls.admin_shares_v2_client)['share_type']
1114
1115 @classmethod
1116 def _create_share_group_type(cls):
1117 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1118 return cls.create_share_group_type(
1119 name=share_group_type_name, share_types=[cls.share_type_id],
1120 client=cls.admin_shares_v2_client)
1121
Lucio Seki37056942019-01-24 15:40:20 -02001122 def _create_share_for_manage(self):
1123 creation_data = {
1124 'share_type_id': self.st['share_type']['id'],
1125 'share_protocol': self.protocol,
1126 }
1127
1128 share = self.create_share(**creation_data)
1129 share = self.shares_v2_client.get_share(share['id'])
1130
1131 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1132 el = self.shares_v2_client.list_share_export_locations(share["id"])
1133 share["export_locations"] = el
1134
1135 return share
1136
1137 def _unmanage_share_and_wait(self, share):
1138 self.shares_v2_client.unmanage_share(share['id'])
1139 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1140
1141 def _reset_state_and_delete_share(self, share):
1142 self.shares_v2_client.reset_state(share['id'])
1143 self._delete_share_and_wait(share)
1144
1145 def _delete_snapshot_and_wait(self, snap):
1146 self.shares_v2_client.delete_snapshot(snap['id'])
1147 self.shares_v2_client.wait_for_resource_deletion(
1148 snapshot_id=snap['id']
1149 )
1150 self.assertRaises(exceptions.NotFound,
1151 self.shares_v2_client.get_snapshot,
1152 snap['id'])
1153
1154 def _delete_share_and_wait(self, share):
1155 self.shares_v2_client.delete_share(share['id'])
1156 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1157 self.assertRaises(exceptions.NotFound,
1158 self.shares_v2_client.get_share,
1159 share['id'])
1160
1161 def _manage_share(self, share, name, description, share_server_id):
1162 managed_share = self.shares_v2_client.manage_share(
1163 service_host=share['host'],
1164 export_path=share['export_locations'][0],
1165 protocol=share['share_proto'],
1166 share_type_id=self.share_type['share_type']['id'],
1167 name=name,
1168 description=description,
1169 share_server_id=share_server_id
1170 )
1171 self.shares_v2_client.wait_for_share_status(
1172 managed_share['id'], constants.STATUS_AVAILABLE
1173 )
1174
1175 return managed_share
1176
1177 def _unmanage_share_server_and_wait(self, server):
1178 self.shares_v2_client.unmanage_share_server(server['id'])
1179 self.shares_v2_client.wait_for_resource_deletion(
1180 server_id=server['id']
1181 )
1182
1183 def _manage_share_server(self, share_server, fields=None):
1184 params = fields or {}
1185 managed_share_server = self.shares_v2_client.manage_share_server(
1186 params.get('host', share_server['host']),
1187 params.get('share_network_id', share_server['share_network_id']),
1188 params.get('identifier', share_server['identifier']),
1189 )
1190 self.shares_v2_client.wait_for_share_server_status(
1191 managed_share_server['id'],
1192 constants.SERVER_STATE_ACTIVE,
1193 )
1194
1195 return managed_share_server
1196
1197 def _delete_share_server_and_wait(self, share_server_id):
1198 self.shares_v2_client.delete_share_server(
1199 share_server_id
1200 )
1201 self.shares_v2_client.wait_for_resource_deletion(
1202 server_id=share_server_id)
1203
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001204
1205class BaseSharesMixedTest(BaseSharesTest):
1206 """Base test case class for all Shares API tests with all user roles."""
1207 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001208
1209 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001210 def setup_clients(cls):
1211 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001212 # Initialise share clients
1213 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1214 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1215 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1216 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1217 # Initialise network clients
1218 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1219 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001220
1221 if CONF.share.multitenancy_enabled:
1222 admin_share_network_id = cls.provide_share_network(
1223 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1224 cls.admin_shares_client.share_network_id = admin_share_network_id
1225 cls.admin_shares_v2_client.share_network_id = (
1226 admin_share_network_id)
1227
1228 alt_share_network_id = cls.provide_share_network(
1229 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1230 cls.alt_shares_client.share_network_id = alt_share_network_id
1231 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001232 resource = {
1233 "type": "share_network",
1234 "id": alt_share_network_id,
1235 "client": cls.alt_shares_v2_client,
1236 }
1237 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001238
1239 @classmethod
1240 def _create_share_type(cls, specs=None):
1241 name = data_utils.rand_name("unique_st_name")
1242 extra_specs = cls.add_extra_specs_to_dict(specs)
1243 return cls.create_share_type(
1244 name, extra_specs=extra_specs,
1245 client=cls.admin_shares_v2_client)['share_type']
1246
1247 @classmethod
1248 def _create_share_group_type(cls):
1249 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1250 return cls.create_share_group_type(
1251 name=share_group_type_name, share_types=[cls.share_type_id],
1252 client=cls.admin_shares_v2_client)