blob: 9ba720a4d3f1e4ecc6adda2222c7cc67a3cacc9b [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
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300770 replica = client.create_share_replica(
771 share_id, availability_zone=availability_zone)
Yogeshbdb88102015-09-29 23:41:02 -0400772 resource = {
773 "type": "share_replica",
774 "id": replica["id"],
775 "client": client,
776 "share_id": share_id,
777 }
778 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
779 if cleanup:
780 if cleanup_in_class:
781 cls.class_resources.insert(0, resource)
782 else:
783 cls.method_resources.insert(0, resource)
784 client.wait_for_share_replica_status(
785 replica["id"], constants.STATUS_AVAILABLE)
786 return replica
787
788 @classmethod
789 def delete_share_replica(cls, replica_id, client=None):
790 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400791 try:
792 client.delete_share_replica(replica_id)
793 client.wait_for_resource_deletion(replica_id=replica_id)
794 except exceptions.NotFound:
795 pass
Yogeshbdb88102015-09-29 23:41:02 -0400796
797 @classmethod
798 def promote_share_replica(cls, replica_id, client=None):
799 client = client or cls.shares_v2_client
800 replica = client.promote_share_replica(replica_id)
801 client.wait_for_share_replica_status(
802 replica["id"],
803 constants.REPLICATION_STATE_ACTIVE,
804 status_attr="replica_state")
805 return replica
806
yogeshdb32f462016-09-28 15:09:50 -0400807 def _get_access_rule_data_from_config(self):
808 """Get the first available access type/to combination from config.
809
810 This method opportunistically picks the first configured protocol
811 to create the share. Do not use this method in tests where you need
812 to test depth and breadth in the access types and access recipients.
813 """
814 protocol = self.shares_v2_client.share_protocol
815
816 if protocol in CONF.share.enable_ip_rules_for_protocols:
817 access_type = "ip"
818 access_to = utils.rand_ip()
819 elif protocol in CONF.share.enable_user_rules_for_protocols:
820 access_type = "user"
821 access_to = CONF.share.username_for_user_rules
822 elif protocol in CONF.share.enable_cert_rules_for_protocols:
823 access_type = "cert"
824 access_to = "client3.com"
825 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
826 access_type = "cephx"
827 access_to = "eve"
828 else:
829 message = "Unrecognized protocol and access rules configuration."
830 raise self.skipException(message)
831
832 return access_type, access_to
833
Yogeshbdb88102015-09-29 23:41:02 -0400834 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200835 def create_share_network(cls, client=None,
836 cleanup_in_class=False, **kwargs):
837 if client is None:
838 client = cls.shares_client
839 share_network = client.create_share_network(**kwargs)
840 resource = {
841 "type": "share_network",
842 "id": share_network["id"],
843 "client": client,
844 }
845 if cleanup_in_class:
846 cls.class_resources.insert(0, resource)
847 else:
848 cls.method_resources.insert(0, resource)
849 return share_network
850
851 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300852 def create_share_network_subnet(cls, client=None,
853 cleanup_in_class=False, **kwargs):
854 if client is None:
855 client = cls.shares_v2_client
856 share_network_subnet = client.create_subnet(**kwargs)
857 resource = {
858 "type": "share-network-subnet",
859 "id": share_network_subnet["id"],
860 "extra_params": {
861 "share_network_id": share_network_subnet["share_network_id"]
862 },
863 "client": client,
864 }
865 if cleanup_in_class:
866 cls.class_resources.insert(0, resource)
867 else:
868 cls.method_resources.insert(0, resource)
869 return share_network_subnet
870
871 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200872 def create_security_service(cls, ss_type="ldap", client=None,
873 cleanup_in_class=False, **kwargs):
874 if client is None:
875 client = cls.shares_client
876 security_service = client.create_security_service(ss_type, **kwargs)
877 resource = {
878 "type": "security_service",
879 "id": security_service["id"],
880 "client": client,
881 }
882 if cleanup_in_class:
883 cls.class_resources.insert(0, resource)
884 else:
885 cls.method_resources.insert(0, resource)
886 return security_service
887
888 @classmethod
889 def create_share_type(cls, name, is_public=True, client=None,
890 cleanup_in_class=True, **kwargs):
891 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200892 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200893 share_type = client.create_share_type(name, is_public, **kwargs)
894 resource = {
895 "type": "share_type",
896 "id": share_type["share_type"]["id"],
897 "client": client,
898 }
899 if cleanup_in_class:
900 cls.class_resources.insert(0, resource)
901 else:
902 cls.method_resources.insert(0, resource)
903 return share_type
904
haixin0d1d29f2019-08-02 16:50:45 +0800905 @classmethod
906 def update_share_type(cls, share_type_id, name=None,
907 is_public=None, description=None,
908 client=None):
909 if client is None:
910 client = cls.shares_v2_client
911 share_type = client.update_share_type(share_type_id, name,
912 is_public, description)
913 return share_type
914
Marc Koderer0abc93b2015-07-15 09:18:35 +0200915 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400916 def add_extra_specs_to_dict(extra_specs=None):
917 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300918 dhss = six.text_type(CONF.share.multitenancy_enabled)
919 snapshot_support = six.text_type(
920 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400921 create_from_snapshot_support = six.text_type(
922 CONF.share.capability_create_share_from_snapshot_support)
923
924 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300925 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200926 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400927
928 optional = {
929 "snapshot_support": snapshot_support,
930 "create_share_from_snapshot_support": create_from_snapshot_support,
931 }
932 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
933 # required extra-spec
934 extra_specs_dict.update(optional)
935
Marc Koderer0abc93b2015-07-15 09:18:35 +0200936 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400937 extra_specs_dict.update(extra_specs)
938
939 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200940
941 @classmethod
942 def clear_isolated_creds(cls, creds=None):
943 if creds is None:
944 creds = cls.method_isolated_creds
945 for ic in creds:
946 if "deleted" not in ic.keys():
947 ic["deleted"] = False
948 if not ic["deleted"]:
949 with handle_cleanup_exceptions():
950 ic["method"]()
951 ic["deleted"] = True
952
953 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400954 def clear_share_replicas(cls, share_id, client=None):
955 client = client or cls.shares_v2_client
956 share_replicas = client.list_share_replicas(
957 share_id=share_id)
958
959 for replica in share_replicas:
960 try:
961 cls.delete_share_replica(replica['id'])
962 except exceptions.BadRequest:
963 # Ignore the exception due to deletion of last active replica
964 pass
965
966 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200967 def clear_resources(cls, resources=None):
968 """Deletes resources, that were created in test suites.
969
970 This method tries to remove resources from resource list,
971 if it is not found, assumed it was deleted in test itself.
972 It is expected, that all resources were added as LIFO
973 due to restriction of deletion resources, that is in the chain.
974
975 :param resources: dict with keys 'type','id','client' and 'deleted'
976 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200977 if resources is None:
978 resources = cls.method_resources
979 for res in resources:
980 if "deleted" not in res.keys():
981 res["deleted"] = False
982 if "client" not in res.keys():
983 res["client"] = cls.shares_client
984 if not(res["deleted"]):
985 res_id = res['id']
986 client = res["client"]
987 with handle_cleanup_exceptions():
988 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400989 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400990 share_group_id = res.get('share_group_id')
991 if share_group_id:
992 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400993 client.delete_share(res_id, params=params)
994 else:
995 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200996 client.wait_for_resource_deletion(share_id=res_id)
997 elif res["type"] is "snapshot":
998 client.delete_snapshot(res_id)
999 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -04001000 elif (res["type"] is "share_network" and
1001 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -04001002 client.delete_share_network(res_id)
1003 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +02001004 elif res["type"] is "security_service":
1005 client.delete_security_service(res_id)
1006 client.wait_for_resource_deletion(ss_id=res_id)
1007 elif res["type"] is "share_type":
1008 client.delete_share_type(res_id)
1009 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -04001010 elif res["type"] is "share_group":
1011 client.delete_share_group(res_id)
1012 client.wait_for_resource_deletion(
1013 share_group_id=res_id)
1014 elif res["type"] is "share_group_type":
1015 client.delete_share_group_type(res_id)
1016 client.wait_for_resource_deletion(
1017 share_group_type_id=res_id)
1018 elif res["type"] is "share_group_snapshot":
1019 client.delete_share_group_snapshot(res_id)
1020 client.wait_for_resource_deletion(
1021 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -04001022 elif res["type"] is "share_replica":
1023 client.delete_share_replica(res_id)
1024 client.wait_for_resource_deletion(replica_id=res_id)
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001025 elif res["type"] is "share_network_subnet":
1026 sn_id = res["extra_params"]["share_network_id"]
1027 client.delete_subnet(sn_id, res_id)
1028 client.wait_for_resource_deletion(
1029 share_network_subnet_id=res_id,
1030 sn_id=sn_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +02001031 else:
huayue97bacbf2016-01-04 09:57:39 +08001032 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +08001033 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +02001034 res["deleted"] = True
1035
1036 @classmethod
1037 def generate_share_network_data(self):
1038 data = {
1039 "name": data_utils.rand_name("sn-name"),
1040 "description": data_utils.rand_name("sn-desc"),
1041 "neutron_net_id": data_utils.rand_name("net-id"),
1042 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1043 }
1044 return data
1045
1046 @classmethod
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001047 def generate_subnet_data(self):
1048 data = {
1049 "neutron_net_id": data_utils.rand_name("net-id"),
1050 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1051 }
1052 return data
1053
1054 @classmethod
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001055 def generate_security_service_data(self, set_ou=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +02001056 data = {
1057 "name": data_utils.rand_name("ss-name"),
1058 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001059 "dns_ip": utils.rand_ip(),
1060 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001061 "domain": data_utils.rand_name("ss-domain"),
1062 "user": data_utils.rand_name("ss-user"),
1063 "password": data_utils.rand_name("ss-password"),
1064 }
Maurice Schreiber5ac37172018-02-01 15:17:31 +01001065 if set_ou:
1066 data["ou"] = data_utils.rand_name("ss-ou")
1067
Marc Koderer0abc93b2015-07-15 09:18:35 +02001068 return data
1069
1070 # Useful assertions
1071 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1072 """Assert two dicts are equivalent.
1073
1074 This is a 'deep' match in the sense that it handles nested
1075 dictionaries appropriately.
1076
1077 NOTE:
1078
1079 If you don't care (or don't know) a given value, you can specify
1080 the string DONTCARE as the value. This will cause that dict-item
1081 to be skipped.
1082
1083 """
1084 def raise_assertion(msg):
1085 d1str = str(d1)
1086 d2str = str(d2)
1087 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1088 'd2: %(d2str)s' %
1089 {"msg": msg, "d1str": d1str, "d2str": d2str})
1090 raise AssertionError(base_msg)
1091
1092 d1keys = set(d1.keys())
1093 d2keys = set(d2.keys())
1094 if d1keys != d2keys:
1095 d1only = d1keys - d2keys
1096 d2only = d2keys - d1keys
1097 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1098 'Keys in d2 and not d1: %(d2only)s' %
1099 {"d1only": d1only, "d2only": d2only})
1100
1101 for key in d1keys:
1102 d1value = d1[key]
1103 d2value = d2[key]
1104 try:
1105 error = abs(float(d1value) - float(d2value))
1106 within_tolerance = error <= tolerance
1107 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001108 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001109 # ValueError if arg is a str, TypeError if it's something else
1110 # (like None)
1111 within_tolerance = False
1112
1113 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1114 self.assertDictMatch(d1value, d2value)
1115 elif 'DONTCARE' in (d1value, d2value):
1116 continue
1117 elif approx_equal and within_tolerance:
1118 continue
1119 elif d1value != d2value:
1120 raise_assertion("d1['%(key)s']=%(d1value)s != "
1121 "d2['%(key)s']=%(d2value)s" %
1122 {
1123 "key": key,
1124 "d1value": d1value,
1125 "d2value": d2value
1126 })
1127
Alex Meadeba8a1602016-05-06 09:33:09 -04001128 def create_user_message(self):
1129 """Trigger a 'no valid host' situation to generate a message."""
1130 extra_specs = {
1131 'vendor_name': 'foobar',
1132 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1133 }
1134 share_type_name = data_utils.rand_name("share-type")
1135
1136 bogus_type = self.create_share_type(
1137 name=share_type_name,
1138 extra_specs=extra_specs)['share_type']
1139
1140 params = {'share_type_id': bogus_type['id'],
1141 'share_network_id': self.shares_v2_client.share_network_id}
1142 share = self.shares_v2_client.create_share(**params)
1143 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1144 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1145 return self.shares_v2_client.wait_for_message(share['id'])
1146
Marc Koderer0abc93b2015-07-15 09:18:35 +02001147
1148class BaseSharesAltTest(BaseSharesTest):
1149 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001150 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001151
1152
1153class BaseSharesAdminTest(BaseSharesTest):
1154 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001155 credentials = ('admin', )
1156
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001157 @classmethod
1158 def setup_clients(cls):
1159 super(BaseSharesAdminTest, cls).setup_clients()
1160 # Initialise share clients
1161 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1162
1163 @classmethod
1164 def _create_share_type(cls, specs=None):
1165 name = data_utils.rand_name("unique_st_name")
1166 extra_specs = cls.add_extra_specs_to_dict(specs)
1167 return cls.create_share_type(
1168 name, extra_specs=extra_specs,
1169 client=cls.admin_shares_v2_client)['share_type']
1170
1171 @classmethod
1172 def _create_share_group_type(cls):
1173 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1174 return cls.create_share_group_type(
1175 name=share_group_type_name, share_types=[cls.share_type_id],
1176 client=cls.admin_shares_v2_client)
1177
Lucio Seki37056942019-01-24 15:40:20 -02001178 def _create_share_for_manage(self):
1179 creation_data = {
1180 'share_type_id': self.st['share_type']['id'],
1181 'share_protocol': self.protocol,
1182 }
1183
1184 share = self.create_share(**creation_data)
1185 share = self.shares_v2_client.get_share(share['id'])
1186
1187 if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.9"):
1188 el = self.shares_v2_client.list_share_export_locations(share["id"])
1189 share["export_locations"] = el
1190
1191 return share
1192
1193 def _unmanage_share_and_wait(self, share):
1194 self.shares_v2_client.unmanage_share(share['id'])
1195 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1196
1197 def _reset_state_and_delete_share(self, share):
1198 self.shares_v2_client.reset_state(share['id'])
1199 self._delete_share_and_wait(share)
1200
1201 def _delete_snapshot_and_wait(self, snap):
1202 self.shares_v2_client.delete_snapshot(snap['id'])
1203 self.shares_v2_client.wait_for_resource_deletion(
1204 snapshot_id=snap['id']
1205 )
1206 self.assertRaises(exceptions.NotFound,
1207 self.shares_v2_client.get_snapshot,
1208 snap['id'])
1209
1210 def _delete_share_and_wait(self, share):
1211 self.shares_v2_client.delete_share(share['id'])
1212 self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
1213 self.assertRaises(exceptions.NotFound,
1214 self.shares_v2_client.get_share,
1215 share['id'])
1216
1217 def _manage_share(self, share, name, description, share_server_id):
1218 managed_share = self.shares_v2_client.manage_share(
1219 service_host=share['host'],
1220 export_path=share['export_locations'][0],
1221 protocol=share['share_proto'],
1222 share_type_id=self.share_type['share_type']['id'],
1223 name=name,
1224 description=description,
1225 share_server_id=share_server_id
1226 )
1227 self.shares_v2_client.wait_for_share_status(
1228 managed_share['id'], constants.STATUS_AVAILABLE
1229 )
1230
1231 return managed_share
1232
1233 def _unmanage_share_server_and_wait(self, server):
1234 self.shares_v2_client.unmanage_share_server(server['id'])
1235 self.shares_v2_client.wait_for_resource_deletion(
1236 server_id=server['id']
1237 )
1238
1239 def _manage_share_server(self, share_server, fields=None):
1240 params = fields or {}
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001241 subnet_id = params.get('share_network_subnet_id', None)
Lucio Seki37056942019-01-24 15:40:20 -02001242 managed_share_server = self.shares_v2_client.manage_share_server(
1243 params.get('host', share_server['host']),
1244 params.get('share_network_id', share_server['share_network_id']),
1245 params.get('identifier', share_server['identifier']),
Douglas Viroelb7e27e72019-08-06 19:40:37 -03001246 share_network_subnet_id=subnet_id,
Lucio Seki37056942019-01-24 15:40:20 -02001247 )
1248 self.shares_v2_client.wait_for_share_server_status(
1249 managed_share_server['id'],
1250 constants.SERVER_STATE_ACTIVE,
1251 )
1252
1253 return managed_share_server
1254
1255 def _delete_share_server_and_wait(self, share_server_id):
1256 self.shares_v2_client.delete_share_server(
1257 share_server_id
1258 )
1259 self.shares_v2_client.wait_for_resource_deletion(
1260 server_id=share_server_id)
1261
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001262
1263class BaseSharesMixedTest(BaseSharesTest):
1264 """Base test case class for all Shares API tests with all user roles."""
1265 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001266
1267 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001268 def setup_clients(cls):
1269 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001270 # Initialise share clients
1271 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1272 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1273 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1274 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1275 # Initialise network clients
1276 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1277 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001278
1279 if CONF.share.multitenancy_enabled:
1280 admin_share_network_id = cls.provide_share_network(
1281 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1282 cls.admin_shares_client.share_network_id = admin_share_network_id
1283 cls.admin_shares_v2_client.share_network_id = (
1284 admin_share_network_id)
1285
1286 alt_share_network_id = cls.provide_share_network(
1287 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1288 cls.alt_shares_client.share_network_id = alt_share_network_id
1289 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001290 resource = {
1291 "type": "share_network",
1292 "id": alt_share_network_id,
1293 "client": cls.alt_shares_v2_client,
1294 }
1295 cls.class_resources.insert(0, resource)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -05001296
1297 @classmethod
1298 def _create_share_type(cls, specs=None):
1299 name = data_utils.rand_name("unique_st_name")
1300 extra_specs = cls.add_extra_specs_to_dict(specs)
1301 return cls.create_share_type(
1302 name, extra_specs=extra_specs,
1303 client=cls.admin_shares_v2_client)['share_type']
1304
1305 @classmethod
1306 def _create_share_group_type(cls):
1307 share_group_type_name = data_utils.rand_name("unique_sgtype_name")
1308 return cls.create_share_group_type(
1309 name=share_group_type_name, share_types=[cls.share_type_id],
1310 client=cls.admin_shares_v2_client)