blob: 113a669f5385c59641dd39889c9c14f698030df6 [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
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300759 replica = client.create_share_replica(
760 share_id, availability_zone=availability_zone)
Yogeshbdb88102015-09-29 23:41:02 -0400761 resource = {
762 "type": "share_replica",
763 "id": replica["id"],
764 "client": client,
765 "share_id": share_id,
766 }
767 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
768 if cleanup:
769 if cleanup_in_class:
770 cls.class_resources.insert(0, resource)
771 else:
772 cls.method_resources.insert(0, resource)
773 client.wait_for_share_replica_status(
774 replica["id"], constants.STATUS_AVAILABLE)
775 return replica
776
777 @classmethod
778 def delete_share_replica(cls, replica_id, client=None):
779 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400780 try:
781 client.delete_share_replica(replica_id)
782 client.wait_for_resource_deletion(replica_id=replica_id)
783 except exceptions.NotFound:
784 pass
Yogeshbdb88102015-09-29 23:41:02 -0400785
786 @classmethod
787 def promote_share_replica(cls, replica_id, client=None):
788 client = client or cls.shares_v2_client
789 replica = client.promote_share_replica(replica_id)
790 client.wait_for_share_replica_status(
791 replica["id"],
792 constants.REPLICATION_STATE_ACTIVE,
793 status_attr="replica_state")
794 return replica
795
yogeshdb32f462016-09-28 15:09:50 -0400796 def _get_access_rule_data_from_config(self):
797 """Get the first available access type/to combination from config.
798
799 This method opportunistically picks the first configured protocol
800 to create the share. Do not use this method in tests where you need
801 to test depth and breadth in the access types and access recipients.
802 """
803 protocol = self.shares_v2_client.share_protocol
804
805 if protocol in CONF.share.enable_ip_rules_for_protocols:
806 access_type = "ip"
807 access_to = utils.rand_ip()
808 elif protocol in CONF.share.enable_user_rules_for_protocols:
809 access_type = "user"
810 access_to = CONF.share.username_for_user_rules
811 elif protocol in CONF.share.enable_cert_rules_for_protocols:
812 access_type = "cert"
813 access_to = "client3.com"
814 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
815 access_type = "cephx"
816 access_to = "eve"
817 else:
818 message = "Unrecognized protocol and access rules configuration."
819 raise self.skipException(message)
820
821 return access_type, access_to
822
Yogeshbdb88102015-09-29 23:41:02 -0400823 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200824 def create_share_network(cls, client=None,
825 cleanup_in_class=False, **kwargs):
826 if client is None:
827 client = cls.shares_client
828 share_network = client.create_share_network(**kwargs)
829 resource = {
830 "type": "share_network",
831 "id": share_network["id"],
832 "client": client,
833 }
834 if cleanup_in_class:
835 cls.class_resources.insert(0, resource)
836 else:
837 cls.method_resources.insert(0, resource)
838 return share_network
839
840 @classmethod
841 def create_security_service(cls, ss_type="ldap", client=None,
842 cleanup_in_class=False, **kwargs):
843 if client is None:
844 client = cls.shares_client
845 security_service = client.create_security_service(ss_type, **kwargs)
846 resource = {
847 "type": "security_service",
848 "id": security_service["id"],
849 "client": client,
850 }
851 if cleanup_in_class:
852 cls.class_resources.insert(0, resource)
853 else:
854 cls.method_resources.insert(0, resource)
855 return security_service
856
857 @classmethod
858 def create_share_type(cls, name, is_public=True, client=None,
859 cleanup_in_class=True, **kwargs):
860 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200861 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200862 share_type = client.create_share_type(name, is_public, **kwargs)
863 resource = {
864 "type": "share_type",
865 "id": share_type["share_type"]["id"],
866 "client": client,
867 }
868 if cleanup_in_class:
869 cls.class_resources.insert(0, resource)
870 else:
871 cls.method_resources.insert(0, resource)
872 return share_type
873
874 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400875 def add_extra_specs_to_dict(extra_specs=None):
876 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300877 dhss = six.text_type(CONF.share.multitenancy_enabled)
878 snapshot_support = six.text_type(
879 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400880 create_from_snapshot_support = six.text_type(
881 CONF.share.capability_create_share_from_snapshot_support)
882
883 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300884 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200885 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400886
887 optional = {
888 "snapshot_support": snapshot_support,
889 "create_share_from_snapshot_support": create_from_snapshot_support,
890 }
891 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
892 # required extra-spec
893 extra_specs_dict.update(optional)
894
Marc Koderer0abc93b2015-07-15 09:18:35 +0200895 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400896 extra_specs_dict.update(extra_specs)
897
898 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200899
900 @classmethod
901 def clear_isolated_creds(cls, creds=None):
902 if creds is None:
903 creds = cls.method_isolated_creds
904 for ic in creds:
905 if "deleted" not in ic.keys():
906 ic["deleted"] = False
907 if not ic["deleted"]:
908 with handle_cleanup_exceptions():
909 ic["method"]()
910 ic["deleted"] = True
911
912 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400913 def clear_share_replicas(cls, share_id, client=None):
914 client = client or cls.shares_v2_client
915 share_replicas = client.list_share_replicas(
916 share_id=share_id)
917
918 for replica in share_replicas:
919 try:
920 cls.delete_share_replica(replica['id'])
921 except exceptions.BadRequest:
922 # Ignore the exception due to deletion of last active replica
923 pass
924
925 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200926 def clear_resources(cls, resources=None):
927 """Deletes resources, that were created in test suites.
928
929 This method tries to remove resources from resource list,
930 if it is not found, assumed it was deleted in test itself.
931 It is expected, that all resources were added as LIFO
932 due to restriction of deletion resources, that is in the chain.
933
934 :param resources: dict with keys 'type','id','client' and 'deleted'
935 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200936 if resources is None:
937 resources = cls.method_resources
938 for res in resources:
939 if "deleted" not in res.keys():
940 res["deleted"] = False
941 if "client" not in res.keys():
942 res["client"] = cls.shares_client
943 if not(res["deleted"]):
944 res_id = res['id']
945 client = res["client"]
946 with handle_cleanup_exceptions():
947 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400948 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400949 share_group_id = res.get('share_group_id')
950 if share_group_id:
951 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400952 client.delete_share(res_id, params=params)
953 else:
954 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200955 client.wait_for_resource_deletion(share_id=res_id)
956 elif res["type"] is "snapshot":
957 client.delete_snapshot(res_id)
958 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400959 elif (res["type"] is "share_network" and
960 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400961 client.delete_share_network(res_id)
962 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200963 elif res["type"] is "security_service":
964 client.delete_security_service(res_id)
965 client.wait_for_resource_deletion(ss_id=res_id)
966 elif res["type"] is "share_type":
967 client.delete_share_type(res_id)
968 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400969 elif res["type"] is "share_group":
970 client.delete_share_group(res_id)
971 client.wait_for_resource_deletion(
972 share_group_id=res_id)
973 elif res["type"] is "share_group_type":
974 client.delete_share_group_type(res_id)
975 client.wait_for_resource_deletion(
976 share_group_type_id=res_id)
977 elif res["type"] is "share_group_snapshot":
978 client.delete_share_group_snapshot(res_id)
979 client.wait_for_resource_deletion(
980 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -0400981 elif res["type"] is "share_replica":
982 client.delete_share_replica(res_id)
983 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200984 else:
huayue97bacbf2016-01-04 09:57:39 +0800985 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +0800986 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200987 res["deleted"] = True
988
989 @classmethod
990 def generate_share_network_data(self):
991 data = {
992 "name": data_utils.rand_name("sn-name"),
993 "description": data_utils.rand_name("sn-desc"),
994 "neutron_net_id": data_utils.rand_name("net-id"),
995 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
996 }
997 return data
998
999 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001000 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001001 data = {
1002 "name": data_utils.rand_name("ss-name"),
1003 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001004 "dns_ip": utils.rand_ip(),
1005 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001006 "domain": data_utils.rand_name("ss-domain"),
1007 "user": data_utils.rand_name("ss-user"),
1008 "password": data_utils.rand_name("ss-password"),
1009 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001010 if set_ou:
1011 data["ou"] = data_utils.rand_name("ss-ou")
1012
Marc Koderer0abc93b2015-07-15 09:18:35 +02001013 return data
1014
1015 # Useful assertions
1016 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1017 """Assert two dicts are equivalent.
1018
1019 This is a 'deep' match in the sense that it handles nested
1020 dictionaries appropriately.
1021
1022 NOTE:
1023
1024 If you don't care (or don't know) a given value, you can specify
1025 the string DONTCARE as the value. This will cause that dict-item
1026 to be skipped.
1027
1028 """
1029 def raise_assertion(msg):
1030 d1str = str(d1)
1031 d2str = str(d2)
1032 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1033 'd2: %(d2str)s' %
1034 {"msg": msg, "d1str": d1str, "d2str": d2str})
1035 raise AssertionError(base_msg)
1036
1037 d1keys = set(d1.keys())
1038 d2keys = set(d2.keys())
1039 if d1keys != d2keys:
1040 d1only = d1keys - d2keys
1041 d2only = d2keys - d1keys
1042 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1043 'Keys in d2 and not d1: %(d2only)s' %
1044 {"d1only": d1only, "d2only": d2only})
1045
1046 for key in d1keys:
1047 d1value = d1[key]
1048 d2value = d2[key]
1049 try:
1050 error = abs(float(d1value) - float(d2value))
1051 within_tolerance = error <= tolerance
1052 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001053 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001054 # ValueError if arg is a str, TypeError if it's something else
1055 # (like None)
1056 within_tolerance = False
1057
1058 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1059 self.assertDictMatch(d1value, d2value)
1060 elif 'DONTCARE' in (d1value, d2value):
1061 continue
1062 elif approx_equal and within_tolerance:
1063 continue
1064 elif d1value != d2value:
1065 raise_assertion("d1['%(key)s']=%(d1value)s != "
1066 "d2['%(key)s']=%(d2value)s" %
1067 {
1068 "key": key,
1069 "d1value": d1value,
1070 "d2value": d2value
1071 })
1072
Alex Meadeba8a1602016-05-06 09:33:09 -04001073 def create_user_message(self):
1074 """Trigger a 'no valid host' situation to generate a message."""
1075 extra_specs = {
1076 'vendor_name': 'foobar',
1077 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1078 }
1079 share_type_name = data_utils.rand_name("share-type")
1080
1081 bogus_type = self.create_share_type(
1082 name=share_type_name,
1083 extra_specs=extra_specs)['share_type']
1084
1085 params = {'share_type_id': bogus_type['id'],
1086 'share_network_id': self.shares_v2_client.share_network_id}
1087 share = self.shares_v2_client.create_share(**params)
1088 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1089 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1090 return self.shares_v2_client.wait_for_message(share['id'])
1091
Marc Koderer0abc93b2015-07-15 09:18:35 +02001092
1093class BaseSharesAltTest(BaseSharesTest):
1094 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001095 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001096
1097
1098class BaseSharesAdminTest(BaseSharesTest):
1099 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001100 credentials = ('admin', )
1101
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001102 @classmethod
1103 def setup_clients(cls):
1104 super(BaseSharesAdminTest, cls).setup_clients()
1105 # Initialise share clients
1106 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1107
1108 @classmethod
1109 def _create_share_type(cls, specs=None):
1110 name = data_utils.rand_name("unique_st_name")
1111 extra_specs = cls.add_extra_specs_to_dict(specs)
1112 return cls.create_share_type(
1113 name, extra_specs=extra_specs,
1114 client=cls.admin_shares_v2_client)['share_type']
1115
1116 @classmethod
1117 def _create_share_group_type(cls):
1118 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1119 return cls.create_share_group_type(
1120 name=share_group_type_name, share_types=[cls.share_type_id],
1121 client=cls.admin_shares_v2_client)
1122
Lucio Seki37056942019-01-24 15:40:20 -02001123 def _create_share_for_manage(self):
1124 creation_data = {
1125 'share_type_id': self.st['share_type']['id'],
1126 'share_protocol': self.protocol,
1127 }
1128
1129 share = self.create_share(**creation_data)
1130 share = self.shares_v2_client.get_share(share['id'])
1131
1132 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1133 el = self.shares_v2_client.list_share_export_locations(share["id"])
1134 share["export_locations"] = el
1135
1136 return share
1137
1138 def _unmanage_share_and_wait(self, share):
1139 self.shares_v2_client.unmanage_share(share['id'])
1140 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1141
1142 def _reset_state_and_delete_share(self, share):
1143 self.shares_v2_client.reset_state(share['id'])
1144 self._delete_share_and_wait(share)
1145
1146 def _delete_snapshot_and_wait(self, snap):
1147 self.shares_v2_client.delete_snapshot(snap['id'])
1148 self.shares_v2_client.wait_for_resource_deletion(
1149 snapshot_id=snap['id']
1150 )
1151 self.assertRaises(exceptions.NotFound,
1152 self.shares_v2_client.get_snapshot,
1153 snap['id'])
1154
1155 def _delete_share_and_wait(self, share):
1156 self.shares_v2_client.delete_share(share['id'])
1157 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1158 self.assertRaises(exceptions.NotFound,
1159 self.shares_v2_client.get_share,
1160 share['id'])
1161
1162 def _manage_share(self, share, name, description, share_server_id):
1163 managed_share = self.shares_v2_client.manage_share(
1164 service_host=share['host'],
1165 export_path=share['export_locations'][0],
1166 protocol=share['share_proto'],
1167 share_type_id=self.share_type['share_type']['id'],
1168 name=name,
1169 description=description,
1170 share_server_id=share_server_id
1171 )
1172 self.shares_v2_client.wait_for_share_status(
1173 managed_share['id'], constants.STATUS_AVAILABLE
1174 )
1175
1176 return managed_share
1177
1178 def _unmanage_share_server_and_wait(self, server):
1179 self.shares_v2_client.unmanage_share_server(server['id'])
1180 self.shares_v2_client.wait_for_resource_deletion(
1181 server_id=server['id']
1182 )
1183
1184 def _manage_share_server(self, share_server, fields=None):
1185 params = fields or {}
1186 managed_share_server = self.shares_v2_client.manage_share_server(
1187 params.get('host', share_server['host']),
1188 params.get('share_network_id', share_server['share_network_id']),
1189 params.get('identifier', share_server['identifier']),
1190 )
1191 self.shares_v2_client.wait_for_share_server_status(
1192 managed_share_server['id'],
1193 constants.SERVER_STATE_ACTIVE,
1194 )
1195
1196 return managed_share_server
1197
1198 def _delete_share_server_and_wait(self, share_server_id):
1199 self.shares_v2_client.delete_share_server(
1200 share_server_id
1201 )
1202 self.shares_v2_client.wait_for_resource_deletion(
1203 server_id=share_server_id)
1204
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001205
1206class BaseSharesMixedTest(BaseSharesTest):
1207 """Base test case class for all Shares API tests with all user roles."""
1208 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001209
1210 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001211 def setup_clients(cls):
1212 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001213 # Initialise share clients
1214 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1215 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1216 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1217 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1218 # Initialise network clients
1219 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1220 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001221
1222 if CONF.share.multitenancy_enabled:
1223 admin_share_network_id = cls.provide_share_network(
1224 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1225 cls.admin_shares_client.share_network_id = admin_share_network_id
1226 cls.admin_shares_v2_client.share_network_id = (
1227 admin_share_network_id)
1228
1229 alt_share_network_id = cls.provide_share_network(
1230 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1231 cls.alt_shares_client.share_network_id = alt_share_network_id
1232 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001233 resource = {
1234 "type": "share_network",
1235 "id": alt_share_network_id,
1236 "client": cls.alt_shares_v2_client,
1237 }
1238 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001239
1240 @classmethod
1241 def _create_share_type(cls, specs=None):
1242 name = data_utils.rand_name("unique_st_name")
1243 extra_specs = cls.add_extra_specs_to_dict(specs)
1244 return cls.create_share_type(
1245 name, extra_specs=extra_specs,
1246 client=cls.admin_shares_v2_client)['share_type']
1247
1248 @classmethod
1249 def _create_share_group_type(cls):
1250 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1251 return cls.create_share_group_type(
1252 name=share_group_type_name, share_types=[cls.share_type_id],
1253 client=cls.admin_shares_v2_client)