blob: d3ebbb8e6ab987b364da6bb648586526709480c7 [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:
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300364 net_info = (
365 utils.share_network_get_default_subnet(sn)
366 if utils.share_network_subnets_are_supported() else sn)
367 if net_info is None:
368 continue
369 if(net_info["neutron_net_id"] is None and
370 net_info["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200371 sn["name"] and search_word in sn["name"]):
372 share_network_id = sn["id"]
373 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200374
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300375 # Create new share-network if one was not found
376 if share_network_id is None:
377 sn_desc = "This share-network was created by tempest"
378 sn = sc.create_share_network(name=sn_name,
379 description=sn_desc)
380 share_network_id = sn["id"]
381 else:
382 net_id = subnet_id = share_network_id = None
383
384 if not isolated_creds_client:
385 # Search for networks, created in previous runs
386 service_net_name = "share-service"
387 networks = networks_client.list_networks()
388 if "networks" in networks.keys():
389 networks = networks["networks"]
390 for network in networks:
391 if (service_net_name in network["name"] and
392 sc.tenant_id == network['tenant_id']):
393 net_id = network["id"]
394 if len(network["subnets"]) > 0:
395 subnet_id = network["subnets"][0]
396 break
397
398 # Create suitable network
399 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500400 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300401 net_data = ic._create_network_resources(sc.tenant_id)
402 network, subnet, router = net_data
403 net_id = network["id"]
404 subnet_id = subnet["id"]
405
406 # Try get suitable share-network
407 share_networks = sc.list_share_networks_with_detail()
408 for sn in share_networks:
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300409 net_info = (
410 utils.share_network_get_default_subnet(sn)
411 if utils.share_network_subnets_are_supported()
412 else sn)
413 if net_info is None:
414 continue
415 if (net_id == net_info["neutron_net_id"] and
416 subnet_id == net_info["neutron_subnet_id"] and
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300417 sn["name"] and search_word in sn["name"]):
418 share_network_id = sn["id"]
419 break
420 else:
421 sn_name = "autogenerated_by_tempest_for_isolated_creds"
422 # Use precreated network and subnet from isolated creds
423 net_id = isolated_creds_client.get_credentials(
424 isolated_creds_client.type_of_creds).network['id']
425 subnet_id = isolated_creds_client.get_credentials(
426 isolated_creds_client.type_of_creds).subnet['id']
427
428 # Create suitable share-network
429 if share_network_id is None:
430 sn_desc = "This share-network was created by tempest"
431 sn = sc.create_share_network(name=sn_name,
432 description=sn_desc,
433 neutron_net_id=net_id,
434 neutron_subnet_id=subnet_id)
435 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200436
437 return share_network_id
438
439 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300440 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200441 snapshot_id=None, description=None, metadata=None,
442 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400443 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400444 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300445 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200446 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400447 share_network_id = (share_network_id or
448 CONF.share.share_network_id or
449 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200450 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300451 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400452 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200453 'share_protocol': share_protocol,
454 'size': size,
455 'name': name,
456 'snapshot_id': snapshot_id,
457 'description': description,
458 'metadata': metadata,
459 'share_network_id': share_network_id,
460 'share_type_id': share_type_id,
461 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400462 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400463 if share_group_id:
464 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400465
Marc Koderer0abc93b2015-07-15 09:18:35 +0200466 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400467 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400468 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200469 cleanup_list = (cls.class_resources if cleanup_in_class else
470 cls.method_resources)
471 cleanup_list.insert(0, resource)
472 return share
473
474 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300475 def migrate_share(
476 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200477 force_host_assisted_migration=False, writable=False,
478 nondisruptive=False, preserve_metadata=False,
479 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300480 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400481 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300482 client.migrate_share(
483 share_id, dest_host,
484 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200485 writable=writable, preserve_metadata=preserve_metadata,
486 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300487 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300488 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200489 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300490 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200491 return share
492
493 @classmethod
494 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
495 client = client or cls.shares_v2_client
496 client.migration_complete(share_id, **kwargs)
497 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300498 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300499 return share
500
501 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300502 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
503 client = client or cls.shares_v2_client
504 client.migration_cancel(share_id, **kwargs)
505 share = client.wait_for_migration_status(
506 share_id, dest_host, 'migration_cancelled', **kwargs)
507 return share
508
509 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200510 def create_share(cls, *args, **kwargs):
511 """Create one share and wait for available state. Retry if allowed."""
512 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
513 return result[0]
514
515 @classmethod
516 def create_shares(cls, share_data_list):
517 """Creates several shares in parallel with retries.
518
519 Use this method when you want to create more than one share at same
520 time. Especially if config option 'share.share_creation_retry_number'
521 has value more than zero (0).
522 All shares will be expected to have 'available' status with or without
523 recreation else error will be raised.
524
525 :param share_data_list: list -- list of dictionaries with 'args' and
526 'kwargs' for '_create_share' method of this base class.
527 example of data:
528 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
529 :returns: list -- list of shares created using provided data.
530 """
531
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300532 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200533 if not isinstance(d, dict):
534 raise exceptions.TempestException(
535 "Expected 'dict', got '%s'" % type(d))
536 if "args" not in d:
537 d["args"] = []
538 if "kwargs" not in d:
539 d["kwargs"] = {}
540 if len(d) > 2:
541 raise exceptions.TempestException(
542 "Expected only 'args' and 'kwargs' keys. "
543 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300544
545 data = []
546 for d in share_data_list:
547 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400548 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300549 local_d = {
550 "args": d["args"],
551 "kwargs": copy.deepcopy(d["kwargs"]),
552 }
553 local_d["kwargs"]["client"] = client
554 local_d["share"] = cls._create_share(
555 *local_d["args"], **local_d["kwargs"])
556 local_d["cnt"] = 0
557 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400558 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300559 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200560
561 while not all(d["available"] for d in data):
562 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400563 if not d["wait_for_status"]:
564 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200565 if d["available"]:
566 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300567 client = d["kwargs"]["client"]
568 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200569 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300570 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200571 d["available"] = True
572 except (share_exceptions.ShareBuildErrorException,
573 exceptions.TimeoutException) as e:
574 if CONF.share.share_creation_retry_number > d["cnt"]:
575 d["cnt"] += 1
576 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300577 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200578 LOG.error(msg)
579 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300580 cg_id = d["kwargs"].get("consistency_group_id")
581 if cg_id:
582 # NOTE(vponomaryov): delete errored share
583 # immediately in case share is part of CG.
584 client.delete_share(
585 share_id,
586 params={"consistency_group_id": cg_id})
587 client.wait_for_resource_deletion(
588 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200589 d["share"] = cls._create_share(
590 *d["args"], **d["kwargs"])
591 else:
gecong197358663802016-08-25 11:08:45 +0800592 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200593
594 return [d["share"] for d in data]
595
596 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400597 def create_share_group(cls, client=None, cleanup_in_class=True,
598 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400599 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400600 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400601 kwargs['share_network_id'] = (share_network_id or
602 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400603 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400604 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400605 "type": "share_group",
606 "id": share_group["id"],
607 "client": client,
608 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400609 if cleanup_in_class:
610 cls.class_resources.insert(0, resource)
611 else:
612 cls.method_resources.insert(0, resource)
613
Andrew Kerrb8436922016-06-01 15:32:43 -0400614 if kwargs.get('source_share_group_snapshot_id'):
615 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400616 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400617 params={'share_group_id': share_group['id']},
618 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400619
Andrew Kerrb8436922016-06-01 15:32:43 -0400620 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400621 resource = {"type": "share",
622 "id": share["id"],
623 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400624 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400625 if cleanup_in_class:
626 cls.class_resources.insert(0, resource)
627 else:
628 cls.method_resources.insert(0, resource)
629
Andrew Kerrb8436922016-06-01 15:32:43 -0400630 client.wait_for_share_group_status(share_group['id'], 'available')
631 return share_group
632
633 @classmethod
634 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
635 group_specs=None, client=None,
636 cleanup_in_class=True, **kwargs):
637 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300638 if (group_specs is None and
639 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300640 group_specs = {
641 'consistent_snapshot_support': (
642 CONF.share.capability_sg_consistent_snapshot_support),
643 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400644 share_group_type = client.create_share_group_type(
645 name=name,
646 share_types=share_types,
647 is_public=is_public,
648 group_specs=group_specs,
649 **kwargs)
650 resource = {
651 "type": "share_group_type",
652 "id": share_group_type["id"],
653 "client": client,
654 }
655 if cleanup_in_class:
656 cls.class_resources.insert(0, resource)
657 else:
658 cls.method_resources.insert(0, resource)
659 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400660
661 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200662 def create_snapshot_wait_for_active(cls, share_id, name=None,
663 description=None, force=False,
664 client=None, cleanup_in_class=True):
665 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400666 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200667 if description is None:
668 description = "Tempest's snapshot"
669 snapshot = client.create_snapshot(share_id, name, description, force)
670 resource = {
671 "type": "snapshot",
672 "id": snapshot["id"],
673 "client": client,
674 }
675 if cleanup_in_class:
676 cls.class_resources.insert(0, resource)
677 else:
678 cls.method_resources.insert(0, resource)
679 client.wait_for_snapshot_status(snapshot["id"], "available")
680 return snapshot
681
682 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400683 def create_share_group_snapshot_wait_for_active(
684 cls, share_group_id, name=None, description=None, client=None,
685 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400686 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400687 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400688 description = "Tempest's share group snapshot"
689 sg_snapshot = client.create_share_group_snapshot(
690 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400691 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400692 "type": "share_group_snapshot",
693 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400694 "client": client,
695 }
696 if cleanup_in_class:
697 cls.class_resources.insert(0, resource)
698 else:
699 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400700 client.wait_for_share_group_snapshot_status(
701 sg_snapshot["id"], "available")
702 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400703
704 @classmethod
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800705 def get_availability_zones(cls, client=None, backends=None):
Yogeshbdb88102015-09-29 23:41:02 -0400706 """List the availability zones for "manila-share" services
707
708 that are currently in "up" state.
709 """
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800710 client = client or cls.admin_shares_v2_client
711 backends = (
712 '|'.join(['^%s$' % backend for backend in backends])
713 if backends else '.*'
714 )
Yogeshbdb88102015-09-29 23:41:02 -0400715 cls.services = client.list_services()
716 zones = [service['zone'] for service in cls.services if
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800717 service['binary'] == 'manila-share' and
718 service['state'] == 'up' and
719 re.search(backends, service['host'])]
Yogeshbdb88102015-09-29 23:41:02 -0400720 return zones
721
Goutham Pacha Ravi839c98b2019-01-14 23:16:23 -0800722 @classmethod
723 def get_pools_matching_share_type(cls, share_type, client=None):
724 client = client or cls.admin_shares_v2_client
725 if utils.is_microversion_supported('2.23'):
726 return client.list_pools(
727 search_opts={'share_type': share_type['id']})['pools']
728
729 pools = client.list_pools(detail=True)['pools']
730 share_type = client.get_share_type(share_type['id'])['share_type']
731 extra_specs = {}
732 for k, v in share_type['extra_specs'].items():
733 extra_specs[k] = (
734 True if six.text_type(v).lower() == 'true'
735 else False if six.text_type(v).lower() == 'false' else v
736 )
737 return [
738 pool for pool in pools if all(y in pool['capabilities'].items()
739 for y in extra_specs.items())
740 ]
741
742 @classmethod
743 def get_availability_zones_matching_share_type(cls, share_type,
744 client=None):
745
746 client = client or cls.admin_shares_v2_client
747 pools_matching_share_type = cls.get_pools_matching_share_type(
748 share_type, client=client)
749 backends_matching_share_type = set(
750 [pool['name'].split("#")[0] for pool in pools_matching_share_type]
751 )
752 azs = cls.get_availability_zones(backends=backends_matching_share_type)
753 return azs
754
Yogesh1f931ff2015-09-29 23:41:02 -0400755 def get_pools_for_replication_domain(self):
756 # Get the list of pools for the replication domain
757 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500758 instance_host = self.admin_client.get_share(
759 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400760 host_pool = [p for p in pools if p['name'] == instance_host][0]
761 rep_domain = host_pool['capabilities']['replication_domain']
762 pools_in_rep_domain = [p for p in pools if p['capabilities'][
763 'replication_domain'] == rep_domain]
764 return rep_domain, pools_in_rep_domain
765
Yogeshbdb88102015-09-29 23:41:02 -0400766 @classmethod
767 def create_share_replica(cls, share_id, availability_zone, client=None,
768 cleanup_in_class=False, cleanup=True):
769 client = client or cls.shares_v2_client
770 replica = client.create_share_replica(share_id, availability_zone)
771 resource = {
772 "type": "share_replica",
773 "id": replica["id"],
774 "client": client,
775 "share_id": share_id,
776 }
777 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
778 if cleanup:
779 if cleanup_in_class:
780 cls.class_resources.insert(0, resource)
781 else:
782 cls.method_resources.insert(0, resource)
783 client.wait_for_share_replica_status(
784 replica["id"], constants.STATUS_AVAILABLE)
785 return replica
786
787 @classmethod
788 def delete_share_replica(cls, replica_id, client=None):
789 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400790 try:
791 client.delete_share_replica(replica_id)
792 client.wait_for_resource_deletion(replica_id=replica_id)
793 except exceptions.NotFound:
794 pass
Yogeshbdb88102015-09-29 23:41:02 -0400795
796 @classmethod
797 def promote_share_replica(cls, replica_id, client=None):
798 client = client or cls.shares_v2_client
799 replica = client.promote_share_replica(replica_id)
800 client.wait_for_share_replica_status(
801 replica["id"],
802 constants.REPLICATION_STATE_ACTIVE,
803 status_attr="replica_state")
804 return replica
805
yogeshdb32f462016-09-28 15:09:50 -0400806 def _get_access_rule_data_from_config(self):
807 """Get the first available access type/to combination from config.
808
809 This method opportunistically picks the first configured protocol
810 to create the share. Do not use this method in tests where you need
811 to test depth and breadth in the access types and access recipients.
812 """
813 protocol = self.shares_v2_client.share_protocol
814
815 if protocol in CONF.share.enable_ip_rules_for_protocols:
816 access_type = "ip"
817 access_to = utils.rand_ip()
818 elif protocol in CONF.share.enable_user_rules_for_protocols:
819 access_type = "user"
820 access_to = CONF.share.username_for_user_rules
821 elif protocol in CONF.share.enable_cert_rules_for_protocols:
822 access_type = "cert"
823 access_to = "client3.com"
824 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
825 access_type = "cephx"
826 access_to = "eve"
827 else:
828 message = "Unrecognized protocol and access rules configuration."
829 raise self.skipException(message)
830
831 return access_type, access_to
832
Yogeshbdb88102015-09-29 23:41:02 -0400833 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200834 def create_share_network(cls, client=None,
835 cleanup_in_class=False, **kwargs):
836 if client is None:
837 client = cls.shares_client
838 share_network = client.create_share_network(**kwargs)
839 resource = {
840 "type": "share_network",
841 "id": share_network["id"],
842 "client": client,
843 }
844 if cleanup_in_class:
845 cls.class_resources.insert(0, resource)
846 else:
847 cls.method_resources.insert(0, resource)
848 return share_network
849
850 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300851 def create_share_network_subnet(cls, client=None,
852 cleanup_in_class=False, **kwargs):
853 if client is None:
854 client = cls.shares_v2_client
855 share_network_subnet = client.create_subnet(**kwargs)
856 resource = {
857 "type": "share-network-subnet",
858 "id": share_network_subnet["id"],
859 "extra_params": {
860 "share_network_id": share_network_subnet["share_network_id"]
861 },
862 "client": client,
863 }
864 if cleanup_in_class:
865 cls.class_resources.insert(0, resource)
866 else:
867 cls.method_resources.insert(0, resource)
868 return share_network_subnet
869
870 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200871 def create_security_service(cls, ss_type="ldap", client=None,
872 cleanup_in_class=False, **kwargs):
873 if client is None:
874 client = cls.shares_client
875 security_service = client.create_security_service(ss_type, **kwargs)
876 resource = {
877 "type": "security_service",
878 "id": security_service["id"],
879 "client": client,
880 }
881 if cleanup_in_class:
882 cls.class_resources.insert(0, resource)
883 else:
884 cls.method_resources.insert(0, resource)
885 return security_service
886
887 @classmethod
888 def create_share_type(cls, name, is_public=True, client=None,
889 cleanup_in_class=True, **kwargs):
890 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200891 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200892 share_type = client.create_share_type(name, is_public, **kwargs)
893 resource = {
894 "type": "share_type",
895 "id": share_type["share_type"]["id"],
896 "client": client,
897 }
898 if cleanup_in_class:
899 cls.class_resources.insert(0, resource)
900 else:
901 cls.method_resources.insert(0, resource)
902 return share_type
903
haixin0d1d29f2019-08-02 16:50:45 +0800904 @classmethod
905 def update_share_type(cls, share_type_id, name=None,
906 is_public=None, description=None,
907 client=None):
908 if client is None:
909 client = cls.shares_v2_client
910 share_type = client.update_share_type(share_type_id, name,
911 is_public, description)
912 return share_type
913
Marc Koderer0abc93b2015-07-15 09:18:35 +0200914 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400915 def add_extra_specs_to_dict(extra_specs=None):
916 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300917 dhss = six.text_type(CONF.share.multitenancy_enabled)
918 snapshot_support = six.text_type(
919 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400920 create_from_snapshot_support = six.text_type(
921 CONF.share.capability_create_share_from_snapshot_support)
922
923 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300924 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200925 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400926
927 optional = {
928 "snapshot_support": snapshot_support,
929 "create_share_from_snapshot_support": create_from_snapshot_support,
930 }
931 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
932 # required extra-spec
933 extra_specs_dict.update(optional)
934
Marc Koderer0abc93b2015-07-15 09:18:35 +0200935 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400936 extra_specs_dict.update(extra_specs)
937
938 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200939
940 @classmethod
941 def clear_isolated_creds(cls, creds=None):
942 if creds is None:
943 creds = cls.method_isolated_creds
944 for ic in creds:
945 if "deleted" not in ic.keys():
946 ic["deleted"] = False
947 if not ic["deleted"]:
948 with handle_cleanup_exceptions():
949 ic["method"]()
950 ic["deleted"] = True
951
952 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400953 def clear_share_replicas(cls, share_id, client=None):
954 client = client or cls.shares_v2_client
955 share_replicas = client.list_share_replicas(
956 share_id=share_id)
957
958 for replica in share_replicas:
959 try:
960 cls.delete_share_replica(replica['id'])
961 except exceptions.BadRequest:
962 # Ignore the exception due to deletion of last active replica
963 pass
964
965 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200966 def clear_resources(cls, resources=None):
967 """Deletes resources, that were created in test suites.
968
969 This method tries to remove resources from resource list,
970 if it is not found, assumed it was deleted in test itself.
971 It is expected, that all resources were added as LIFO
972 due to restriction of deletion resources, that is in the chain.
973
974 :param resources: dict with keys 'type','id','client' and 'deleted'
975 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200976 if resources is None:
977 resources = cls.method_resources
978 for res in resources:
979 if "deleted" not in res.keys():
980 res["deleted"] = False
981 if "client" not in res.keys():
982 res["client"] = cls.shares_client
983 if not(res["deleted"]):
984 res_id = res['id']
985 client = res["client"]
986 with handle_cleanup_exceptions():
987 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400988 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400989 share_group_id = res.get('share_group_id')
990 if share_group_id:
991 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400992 client.delete_share(res_id, params=params)
993 else:
994 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200995 client.wait_for_resource_deletion(share_id=res_id)
996 elif res["type"] is "snapshot":
997 client.delete_snapshot(res_id)
998 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400999 elif (res["type"] is "share_network" and
1000 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -04001001 client.delete_share_network(res_id)
1002 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +02001003 elif res["type"] is "security_service":
1004 client.delete_security_service(res_id)
1005 client.wait_for_resource_deletion(ss_id=res_id)
1006 elif res["type"] is "share_type":
1007 client.delete_share_type(res_id)
1008 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -04001009 elif res["type"] is "share_group":
1010 client.delete_share_group(res_id)
1011 client.wait_for_resource_deletion(
1012 share_group_id=res_id)
1013 elif res["type"] is "share_group_type":
1014 client.delete_share_group_type(res_id)
1015 client.wait_for_resource_deletion(
1016 share_group_type_id=res_id)
1017 elif res["type"] is "share_group_snapshot":
1018 client.delete_share_group_snapshot(res_id)
1019 client.wait_for_resource_deletion(
1020 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -04001021 elif res["type"] is "share_replica":
1022 client.delete_share_replica(res_id)
1023 client.wait_for_resource_deletion(replica_id=res_id)
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001024 elif res["type"] is "share_network_subnet":
1025 sn_id = res["extra_params"]["share_network_id"]
1026 client.delete_subnet(sn_id, res_id)
1027 client.wait_for_resource_deletion(
1028 share_network_subnet_id=res_id,
1029 sn_id=sn_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +02001030 else:
huayue97bacbf2016-01-04 09:57:39 +08001031 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +08001032 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +02001033 res["deleted"] = True
1034
1035 @classmethod
1036 def generate_share_network_data(self):
1037 data = {
1038 "name": data_utils.rand_name("sn-name"),
1039 "description": data_utils.rand_name("sn-desc"),
1040 "neutron_net_id": data_utils.rand_name("net-id"),
1041 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1042 }
1043 return data
1044
1045 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001046 def generate_subnet_data(self):
1047 data = {
1048 "neutron_net_id": data_utils.rand_name("net-id"),
1049 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1050 }
1051 return data
1052
1053 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001054 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001055 data = {
1056 "name": data_utils.rand_name("ss-name"),
1057 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001058 "dns_ip": utils.rand_ip(),
1059 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001060 "domain": data_utils.rand_name("ss-domain"),
1061 "user": data_utils.rand_name("ss-user"),
1062 "password": data_utils.rand_name("ss-password"),
1063 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001064 if set_ou:
1065 data["ou"] = data_utils.rand_name("ss-ou")
1066
Marc Koderer0abc93b2015-07-15 09:18:35 +02001067 return data
1068
1069 # Useful assertions
1070 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1071 """Assert two dicts are equivalent.
1072
1073 This is a 'deep' match in the sense that it handles nested
1074 dictionaries appropriately.
1075
1076 NOTE:
1077
1078 If you don't care (or don't know) a given value, you can specify
1079 the string DONTCARE as the value. This will cause that dict-item
1080 to be skipped.
1081
1082 """
1083 def raise_assertion(msg):
1084 d1str = str(d1)
1085 d2str = str(d2)
1086 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1087 'd2: %(d2str)s' %
1088 {"msg": msg, "d1str": d1str, "d2str": d2str})
1089 raise AssertionError(base_msg)
1090
1091 d1keys = set(d1.keys())
1092 d2keys = set(d2.keys())
1093 if d1keys != d2keys:
1094 d1only = d1keys - d2keys
1095 d2only = d2keys - d1keys
1096 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1097 'Keys in d2 and not d1: %(d2only)s' %
1098 {"d1only": d1only, "d2only": d2only})
1099
1100 for key in d1keys:
1101 d1value = d1[key]
1102 d2value = d2[key]
1103 try:
1104 error = abs(float(d1value) - float(d2value))
1105 within_tolerance = error <= tolerance
1106 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001107 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001108 # ValueError if arg is a str, TypeError if it's something else
1109 # (like None)
1110 within_tolerance = False
1111
1112 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1113 self.assertDictMatch(d1value, d2value)
1114 elif 'DONTCARE' in (d1value, d2value):
1115 continue
1116 elif approx_equal and within_tolerance:
1117 continue
1118 elif d1value != d2value:
1119 raise_assertion("d1['%(key)s']=%(d1value)s != "
1120 "d2['%(key)s']=%(d2value)s" %
1121 {
1122 "key": key,
1123 "d1value": d1value,
1124 "d2value": d2value
1125 })
1126
Alex Meadeba8a1602016-05-06 09:33:09 -04001127 def create_user_message(self):
1128 """Trigger a 'no valid host' situation to generate a message."""
1129 extra_specs = {
1130 'vendor_name': 'foobar',
1131 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1132 }
1133 share_type_name = data_utils.rand_name("share-type")
1134
1135 bogus_type = self.create_share_type(
1136 name=share_type_name,
1137 extra_specs=extra_specs)['share_type']
1138
1139 params = {'share_type_id': bogus_type['id'],
1140 'share_network_id': self.shares_v2_client.share_network_id}
1141 share = self.shares_v2_client.create_share(**params)
1142 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1143 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1144 return self.shares_v2_client.wait_for_message(share['id'])
1145
Marc Koderer0abc93b2015-07-15 09:18:35 +02001146
1147class BaseSharesAltTest(BaseSharesTest):
1148 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001149 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001150
1151
1152class BaseSharesAdminTest(BaseSharesTest):
1153 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001154 credentials = ('admin', )
1155
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001156 @classmethod
1157 def setup_clients(cls):
1158 super(BaseSharesAdminTest, cls).setup_clients()
1159 # Initialise share clients
1160 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1161
1162 @classmethod
1163 def _create_share_type(cls, specs=None):
1164 name = data_utils.rand_name("unique_st_name")
1165 extra_specs = cls.add_extra_specs_to_dict(specs)
1166 return cls.create_share_type(
1167 name, extra_specs=extra_specs,
1168 client=cls.admin_shares_v2_client)['share_type']
1169
1170 @classmethod
1171 def _create_share_group_type(cls):
1172 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1173 return cls.create_share_group_type(
1174 name=share_group_type_name, share_types=[cls.share_type_id],
1175 client=cls.admin_shares_v2_client)
1176
Lucio Seki37056942019-01-24 15:40:20 -02001177 def _create_share_for_manage(self):
1178 creation_data = {
1179 'share_type_id': self.st['share_type']['id'],
1180 'share_protocol': self.protocol,
1181 }
1182
1183 share = self.create_share(**creation_data)
1184 share = self.shares_v2_client.get_share(share['id'])
1185
1186 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1187 el = self.shares_v2_client.list_share_export_locations(share["id"])
1188 share["export_locations"] = el
1189
1190 return share
1191
1192 def _unmanage_share_and_wait(self, share):
1193 self.shares_v2_client.unmanage_share(share['id'])
1194 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1195
1196 def _reset_state_and_delete_share(self, share):
1197 self.shares_v2_client.reset_state(share['id'])
1198 self._delete_share_and_wait(share)
1199
1200 def _delete_snapshot_and_wait(self, snap):
1201 self.shares_v2_client.delete_snapshot(snap['id'])
1202 self.shares_v2_client.wait_for_resource_deletion(
1203 snapshot_id=snap['id']
1204 )
1205 self.assertRaises(exceptions.NotFound,
1206 self.shares_v2_client.get_snapshot,
1207 snap['id'])
1208
1209 def _delete_share_and_wait(self, share):
1210 self.shares_v2_client.delete_share(share['id'])
1211 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1212 self.assertRaises(exceptions.NotFound,
1213 self.shares_v2_client.get_share,
1214 share['id'])
1215
1216 def _manage_share(self, share, name, description, share_server_id):
1217 managed_share = self.shares_v2_client.manage_share(
1218 service_host=share['host'],
1219 export_path=share['export_locations'][0],
1220 protocol=share['share_proto'],
1221 share_type_id=self.share_type['share_type']['id'],
1222 name=name,
1223 description=description,
1224 share_server_id=share_server_id
1225 )
1226 self.shares_v2_client.wait_for_share_status(
1227 managed_share['id'], constants.STATUS_AVAILABLE
1228 )
1229
1230 return managed_share
1231
1232 def _unmanage_share_server_and_wait(self, server):
1233 self.shares_v2_client.unmanage_share_server(server['id'])
1234 self.shares_v2_client.wait_for_resource_deletion(
1235 server_id=server['id']
1236 )
1237
1238 def _manage_share_server(self, share_server, fields=None):
1239 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001240 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001241 managed_share_server = self.shares_v2_client.manage_share_server(
1242 params.get('host', share_server['host']),
1243 params.get('share_network_id', share_server['share_network_id']),
1244 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001245 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001246 )
1247 self.shares_v2_client.wait_for_share_server_status(
1248 managed_share_server['id'],
1249 constants.SERVER_STATE_ACTIVE,
1250 )
1251
1252 return managed_share_server
1253
1254 def _delete_share_server_and_wait(self, share_server_id):
1255 self.shares_v2_client.delete_share_server(
1256 share_server_id
1257 )
1258 self.shares_v2_client.wait_for_resource_deletion(
1259 server_id=share_server_id)
1260
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001261
1262class BaseSharesMixedTest(BaseSharesTest):
1263 """Base test case class for all Shares API tests with all user roles."""
1264 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001265
1266 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001267 def setup_clients(cls):
1268 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001269 # Initialise share clients
1270 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1271 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1272 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1273 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1274 # Initialise network clients
1275 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1276 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001277
1278 if CONF.share.multitenancy_enabled:
1279 admin_share_network_id = cls.provide_share_network(
1280 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1281 cls.admin_shares_client.share_network_id = admin_share_network_id
1282 cls.admin_shares_v2_client.share_network_id = (
1283 admin_share_network_id)
1284
1285 alt_share_network_id = cls.provide_share_network(
1286 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1287 cls.alt_shares_client.share_network_id = alt_share_network_id
1288 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001289 resource = {
1290 "type": "share_network",
1291 "id": alt_share_network_id,
1292 "client": cls.alt_shares_v2_client,
1293 }
1294 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001295
1296 @classmethod
1297 def _create_share_type(cls, specs=None):
1298 name = data_utils.rand_name("unique_st_name")
1299 extra_specs = cls.add_extra_specs_to_dict(specs)
1300 return cls.create_share_type(
1301 name, extra_specs=extra_specs,
1302 client=cls.admin_shares_v2_client)['share_type']
1303
1304 @classmethod
1305 def _create_share_group_type(cls):
1306 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1307 return cls.create_share_group_type(
1308 name=share_group_type_name, share_types=[cls.share_type_id],
1309 client=cls.admin_shares_v2_client)