blob: 2f896ec123da3b1206d0e83e8f22ceffd283acd3 [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
haobing1264a6022018-03-13 20:39:10 +080029from tempest.lib.common.utils import test_utils
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050030from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020031from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020032
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +010033from manila_tempest_tests import clients
Yogeshbdb88102015-09-29 23:41:02 -040034from manila_tempest_tests.common import constants
Marc Koderer0abc93b2015-07-15 09:18:35 +020035from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020036from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020037
38CONF = config.CONF
39LOG = log.getLogger(__name__)
40
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +030041# Test tags related to test direction
42TAG_POSITIVE = "positive"
43TAG_NEGATIVE = "negative"
44
45# Test tags related to service involvement
46TAG_API = "api"
47TAG_BACKEND = "backend"
48TAG_API_WITH_BACKEND = "api_with_backend"
49
50TAGS_MAPPER = {
51 "p": TAG_POSITIVE,
52 "n": TAG_NEGATIVE,
53 "a": TAG_API,
54 "b": TAG_BACKEND,
55 "ab": TAG_API_WITH_BACKEND,
56}
57TAGS_PATTERN = re.compile(
58 r"(?=.*\[.*\b(%(p)s|%(n)s)\b.*\])(?=.*\[.*\b(%(a)s|%(b)s|%(ab)s)\b.*\])" %
59 TAGS_MAPPER)
60
61
62def verify_test_has_appropriate_tags(self):
63 if not TAGS_PATTERN.match(self.id()):
64 msg = (
65 "Required attributes either not set or set improperly. "
66 "Two test attributes are expected:\n"
67 " - one of '%(p)s' or '%(n)s' and \n"
68 " - one of '%(a)s', '%(b)s' or '%(ab)s'."
69 ) % TAGS_MAPPER
70 raise self.failureException(msg)
71
Marc Koderer0abc93b2015-07-15 09:18:35 +020072
73class handle_cleanup_exceptions(object):
74 """Handle exceptions raised with cleanup operations.
75
76 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
77 are raised.
78 Suppress all other exceptions only in case config opt
79 'suppress_errors_in_cleanup' in config group 'share' is True.
80 """
81
82 def __enter__(self):
83 return self
84
85 def __exit__(self, exc_type, exc_value, exc_traceback):
86 if not (isinstance(exc_value,
87 (exceptions.NotFound, exceptions.Forbidden)) or
88 CONF.share.suppress_errors_in_cleanup):
89 return False # Do not suppress error if any
90 if exc_traceback:
91 LOG.error("Suppressed cleanup error in Manila: "
junbolib236c242017-07-18 18:12:37 +080092 "\n%s", traceback.format_exc())
Marc Koderer0abc93b2015-07-15 09:18:35 +020093 return True # Suppress error if any
94
95
96def network_synchronized(f):
97
98 def wrapped_func(self, *args, **kwargs):
99 with_isolated_creds = True if len(args) > 2 else False
100 no_lock_required = kwargs.get(
101 "isolated_creds_client", with_isolated_creds)
102 if no_lock_required:
103 # Usage of not reusable network. No need in lock.
104 return f(self, *args, **kwargs)
105
106 # Use lock assuming reusage of common network.
107 @lockutils.synchronized("manila_network_lock", external=True)
108 def source_func(self, *args, **kwargs):
109 return f(self, *args, **kwargs)
110
111 return source_func(self, *args, **kwargs)
112
113 return wrapped_func
114
115
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200116skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -0500117skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200118
119
Marc Koderer0abc93b2015-07-15 09:18:35 +0200120class BaseSharesTest(test.BaseTestCase):
121 """Base test case class for all Manila API tests."""
122
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300123 credentials = ('primary', )
Marc Koderer0abc93b2015-07-15 09:18:35 +0200124 force_tenant_isolation = False
Vitaliy Levitksicfebfff2016-12-15 16:16:35 +0200125 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs", "maprfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200126
127 # Will be cleaned up in resource_cleanup
128 class_resources = []
129
haobing1264a6022018-03-13 20:39:10 +0800130 # Will be cleaned up in clear_net_resources
131 class_net_resources = []
132
Marc Koderer0abc93b2015-07-15 09:18:35 +0200133 # 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)
haobing1264a6022018-03-13 20:39:10 +0800317 self.addCleanup(self.clear_net_resources)
Valeriy Ponomaryov2abf5d72016-06-01 18:30:12 +0300318 verify_test_has_appropriate_tags(self)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200319
320 @classmethod
321 def resource_cleanup(cls):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200322 cls.clear_resources(cls.class_resources)
323 cls.clear_isolated_creds(cls.class_isolated_creds)
haobing1264a6022018-03-13 20:39:10 +0800324 cls.clear_net_resources(cls.class_net_resources)
Sam Wan241029c2016-07-26 03:37:42 -0400325 super(BaseSharesTest, cls).resource_cleanup()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200326
327 @classmethod
328 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200329 def provide_share_network(cls, shares_client, networks_client,
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300330 isolated_creds_client=None,
331 ignore_multitenancy_config=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200332 """Used for finding/creating share network for multitenant driver.
333
334 This method creates/gets entity share-network for one tenant. This
335 share-network will be used for creation of service vm.
336
337 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200338 :param networks_client: network client from same tenant as shares
339 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200340 If provided, then its networking will be used if needed.
341 If not provided, then common network will be used if needed.
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300342 :param ignore_multitenancy_config: provide a share network regardless
343 of 'multitenancy_enabled' configuration value.
Marc Koderer0abc93b2015-07-15 09:18:35 +0200344 :returns: str -- share network id for shares_client tenant
345 :returns: None -- if single-tenant driver used
346 """
347
348 sc = shares_client
Valeriy Ponomaryovc5dae272016-06-10 18:29:24 +0300349 search_word = "reusable"
350 sn_name = "autogenerated_by_tempest_%s" % search_word
Marc Koderer0abc93b2015-07-15 09:18:35 +0200351
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300352 if (not ignore_multitenancy_config and
353 not CONF.share.multitenancy_enabled):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200354 # Assumed usage of a single-tenant driver
355 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200356 else:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300357 if sc.share_network_id:
358 # Share-network already exists, use it
359 share_network_id = sc.share_network_id
360 elif not CONF.share.create_networks_when_multitenancy_enabled:
361 share_network_id = None
Marc Koderer0abc93b2015-07-15 09:18:35 +0200362
363 # Try get suitable share-network
364 share_networks = sc.list_share_networks_with_detail()
365 for sn in share_networks:
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300366 if (sn["neutron_net_id"] is None and
367 sn["neutron_subnet_id"] is None and
Marc Koderer0abc93b2015-07-15 09:18:35 +0200368 sn["name"] and search_word in sn["name"]):
369 share_network_id = sn["id"]
370 break
Marc Koderer0abc93b2015-07-15 09:18:35 +0200371
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300372 # Create new share-network if one was not found
373 if share_network_id is None:
374 sn_desc = "This share-network was created by tempest"
375 sn = sc.create_share_network(name=sn_name,
376 description=sn_desc)
377 share_network_id = sn["id"]
378 else:
379 net_id = subnet_id = share_network_id = None
380
381 if not isolated_creds_client:
382 # Search for networks, created in previous runs
383 service_net_name = "share-service"
384 networks = networks_client.list_networks()
385 if "networks" in networks.keys():
386 networks = networks["networks"]
387 for network in networks:
388 if (service_net_name in network["name"] and
389 sc.tenant_id == network['tenant_id']):
390 net_id = network["id"]
391 if len(network["subnets"]) > 0:
392 subnet_id = network["subnets"][0]
393 break
394
395 # Create suitable network
396 if net_id is None or subnet_id is None:
Tom Barron4b8834a2017-02-02 11:02:20 -0500397 ic = cls._get_dynamic_creds(service_net_name)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300398 net_data = ic._create_network_resources(sc.tenant_id)
399 network, subnet, router = net_data
400 net_id = network["id"]
401 subnet_id = subnet["id"]
haobing1264a6022018-03-13 20:39:10 +0800402 network_res = {
403 "type": "network",
404 "resource": network,
405 "client": ic,
406 }
407 subnet_res = {
408 "type": "subnet",
409 "resource": subnet,
410 "client": ic,
411 }
412 router_res = {
413 "type": "router",
414 "resource": router,
415 "client": ic,
416 }
417 cls.class_net_resources.insert(0, network_res)
418 cls.class_net_resources.insert(0, subnet_res)
419 cls.class_net_resources.insert(0, router_res)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300420
421 # Try get suitable share-network
422 share_networks = sc.list_share_networks_with_detail()
423 for sn in share_networks:
424 if (net_id == sn["neutron_net_id"] and
425 subnet_id == sn["neutron_subnet_id"] and
426 sn["name"] and search_word in sn["name"]):
427 share_network_id = sn["id"]
428 break
429 else:
430 sn_name = "autogenerated_by_tempest_for_isolated_creds"
431 # Use precreated network and subnet from isolated creds
432 net_id = isolated_creds_client.get_credentials(
433 isolated_creds_client.type_of_creds).network['id']
434 subnet_id = isolated_creds_client.get_credentials(
435 isolated_creds_client.type_of_creds).subnet['id']
436
437 # Create suitable share-network
438 if share_network_id is None:
439 sn_desc = "This share-network was created by tempest"
440 sn = sc.create_share_network(name=sn_name,
441 description=sn_desc,
442 neutron_net_id=net_id,
443 neutron_subnet_id=subnet_id)
444 share_network_id = sn["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200445
446 return share_network_id
447
448 @classmethod
haobing1264a6022018-03-13 20:39:10 +0800449 def clear_net_resources(cls, resources=None):
450 if resources is None:
451 resources = cls.class_net_resources
452 for res in resources:
453 if "deleted" not in res.keys():
454 res["deleted"] = False
455 if not (res["deleted"]):
456 if res["type"] is "router":
457 cls.clear_router(res['client'], res['resource'])
458 elif res["type"] is "subnet":
459 cls.clear_subnet(res['client'], res['resource'])
460 elif res["type"] is "network":
461 cls.clear_network(res['client'], res['resource'])
462 else:
463 LOG.warning("Provided unsupported resource type for "
464 "cleanup '%s'. Skipping." % res["type"])
465 res["deleted"] = True
466
467 @classmethod
468 def clear_router(cls, ic, router):
469 body = ic.ports_admin_client.list_ports(device_id=router['id'])
470 interfaces = body['ports']
471 for i in interfaces:
472 test_utils.call_and_ignore_notfound_exc(
473 ic.routers_admin_client.remove_router_interface, router['id'],
474 subnet_id=i['fixed_ips'][0]['subnet_id'])
475
476 try:
477 ic.routers_admin_client.delete_router(router['id'])
478 except exceptions.NotFound:
479 LOG.warning('router with name: %s not found for delete' %
480 router['name'])
481
482 @classmethod
483 def clear_subnet(cls, ic, subnet):
484 client = ic.subnets_admin_client
485 try:
486 client.delete_subnet(subnet['id'])
487 except exceptions.NotFound:
488 LOG.warning('subnet with name: %s not found for delete' %
489 subnet['name'])
490
491 @classmethod
492 def clear_network(cls, ic, network):
493 net_client = ic.networks_admin_client
494 try:
495 net_client.delete_network(network['id'])
496 except exceptions.NotFound:
497 LOG.warning('network with name: %s not found for delete' %
498 network['name'])
499
500 @classmethod
marcusvrne0d7cfd2016-06-24 12:27:55 -0300501 def _create_share(cls, share_protocol=None, size=None, name=None,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200502 snapshot_id=None, description=None, metadata=None,
503 share_network_id=None, share_type_id=None,
Andrew Kerrb8436922016-06-01 15:32:43 -0400504 share_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400505 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300506 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200507 description = description or "Tempest's share"
yogesh06f519f2017-06-26 13:16:12 -0400508 share_network_id = (share_network_id or
509 CONF.share.share_network_id or
510 client.share_network_id or None)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200511 metadata = metadata or {}
marcusvrne0d7cfd2016-06-24 12:27:55 -0300512 size = size or CONF.share.share_size
Clinton Knighte5c8f092015-08-27 15:00:23 -0400513 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200514 'share_protocol': share_protocol,
515 'size': size,
516 'name': name,
517 'snapshot_id': snapshot_id,
518 'description': description,
519 'metadata': metadata,
520 'share_network_id': share_network_id,
521 'share_type_id': share_type_id,
522 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400523 })
Andrew Kerrb8436922016-06-01 15:32:43 -0400524 if share_group_id:
525 kwargs['share_group_id'] = share_group_id
Andrew Kerrbf31e912015-07-29 10:39:38 -0400526
Marc Koderer0abc93b2015-07-15 09:18:35 +0200527 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400528 resource = {"type": "share", "id": share["id"], "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400529 "share_group_id": share_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200530 cleanup_list = (cls.class_resources if cleanup_in_class else
531 cls.method_resources)
532 cleanup_list.insert(0, resource)
533 return share
534
535 @classmethod
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300536 def migrate_share(
537 cls, share_id, dest_host, wait_for_status, client=None,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200538 force_host_assisted_migration=False, writable=False,
539 nondisruptive=False, preserve_metadata=False,
540 preserve_snapshots=False, new_share_network_id=None,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300541 new_share_type_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400542 client = client or cls.shares_v2_client
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300543 client.migrate_share(
544 share_id, dest_host,
545 force_host_assisted_migration=force_host_assisted_migration,
Rodrigo Barbieri027df982016-11-24 15:52:03 -0200546 writable=writable, preserve_metadata=preserve_metadata,
547 nondisruptive=nondisruptive, preserve_snapshots=preserve_snapshots,
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300548 new_share_network_id=new_share_network_id,
Rodrigo Barbierid38d2f52016-07-19 22:24:56 -0300549 new_share_type_id=new_share_type_id, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200550 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300551 share_id, dest_host, wait_for_status, **kwargs)
Rodrigo Barbierie3305122016-02-03 14:32:24 -0200552 return share
553
554 @classmethod
555 def migration_complete(cls, share_id, dest_host, client=None, **kwargs):
556 client = client or cls.shares_v2_client
557 client.migration_complete(share_id, **kwargs)
558 share = client.wait_for_migration_status(
Rodrigo Barbieri427bc052016-06-06 17:10:06 -0300559 share_id, dest_host, 'migration_success', **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300560 return share
561
562 @classmethod
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300563 def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
564 client = client or cls.shares_v2_client
565 client.migration_cancel(share_id, **kwargs)
566 share = client.wait_for_migration_status(
567 share_id, dest_host, 'migration_cancelled', **kwargs)
568 return share
569
570 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200571 def create_share(cls, *args, **kwargs):
572 """Create one share and wait for available state. Retry if allowed."""
573 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
574 return result[0]
575
576 @classmethod
577 def create_shares(cls, share_data_list):
578 """Creates several shares in parallel with retries.
579
580 Use this method when you want to create more than one share at same
581 time. Especially if config option 'share.share_creation_retry_number'
582 has value more than zero (0).
583 All shares will be expected to have 'available' status with or without
584 recreation else error will be raised.
585
586 :param share_data_list: list -- list of dictionaries with 'args' and
587 'kwargs' for '_create_share' method of this base class.
588 example of data:
589 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
590 :returns: list -- list of shares created using provided data.
591 """
592
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300593 for d in share_data_list:
Marc Koderer0abc93b2015-07-15 09:18:35 +0200594 if not isinstance(d, dict):
595 raise exceptions.TempestException(
596 "Expected 'dict', got '%s'" % type(d))
597 if "args" not in d:
598 d["args"] = []
599 if "kwargs" not in d:
600 d["kwargs"] = {}
601 if len(d) > 2:
602 raise exceptions.TempestException(
603 "Expected only 'args' and 'kwargs' keys. "
604 "Provided %s" % list(d))
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300605
606 data = []
607 for d in share_data_list:
608 client = d["kwargs"].pop("client", cls.shares_v2_client)
yogeshdb32f462016-09-28 15:09:50 -0400609 wait_for_status = d["kwargs"].pop("wait_for_status", True)
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300610 local_d = {
611 "args": d["args"],
612 "kwargs": copy.deepcopy(d["kwargs"]),
613 }
614 local_d["kwargs"]["client"] = client
615 local_d["share"] = cls._create_share(
616 *local_d["args"], **local_d["kwargs"])
617 local_d["cnt"] = 0
618 local_d["available"] = False
yogeshdb32f462016-09-28 15:09:50 -0400619 local_d["wait_for_status"] = wait_for_status
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +0300620 data.append(local_d)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200621
622 while not all(d["available"] for d in data):
623 for d in data:
yogeshdb32f462016-09-28 15:09:50 -0400624 if not d["wait_for_status"]:
625 d["available"] = True
Marc Koderer0abc93b2015-07-15 09:18:35 +0200626 if d["available"]:
627 continue
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300628 client = d["kwargs"]["client"]
629 share_id = d["share"]["id"]
Marc Koderer0abc93b2015-07-15 09:18:35 +0200630 try:
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300631 client.wait_for_share_status(share_id, "available")
Marc Koderer0abc93b2015-07-15 09:18:35 +0200632 d["available"] = True
633 except (share_exceptions.ShareBuildErrorException,
634 exceptions.TimeoutException) as e:
635 if CONF.share.share_creation_retry_number > d["cnt"]:
636 d["cnt"] += 1
637 msg = ("Share '%s' failed to be built. "
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300638 "Trying create another." % share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200639 LOG.error(msg)
640 LOG.error(e)
Valeriy Ponomaryov1a3e3382016-06-08 15:17:16 +0300641 cg_id = d["kwargs"].get("consistency_group_id")
642 if cg_id:
643 # NOTE(vponomaryov): delete errored share
644 # immediately in case share is part of CG.
645 client.delete_share(
646 share_id,
647 params={"consistency_group_id": cg_id})
648 client.wait_for_resource_deletion(
649 share_id=share_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200650 d["share"] = cls._create_share(
651 *d["args"], **d["kwargs"])
652 else:
gecong197358663802016-08-25 11:08:45 +0800653 raise
Marc Koderer0abc93b2015-07-15 09:18:35 +0200654
655 return [d["share"] for d in data]
656
657 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400658 def create_share_group(cls, client=None, cleanup_in_class=True,
659 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400660 client = client or cls.shares_v2_client
Andrew Kerrb8436922016-06-01 15:32:43 -0400661 if kwargs.get('source_share_group_snapshot_id') is None:
Goutham Pacha Ravi9221f5e2016-04-21 13:17:49 -0400662 kwargs['share_network_id'] = (share_network_id or
663 client.share_network_id or None)
Andrew Kerrb8436922016-06-01 15:32:43 -0400664 share_group = client.create_share_group(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400665 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400666 "type": "share_group",
667 "id": share_group["id"],
668 "client": client,
669 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400670 if cleanup_in_class:
671 cls.class_resources.insert(0, resource)
672 else:
673 cls.method_resources.insert(0, resource)
674
Andrew Kerrb8436922016-06-01 15:32:43 -0400675 if kwargs.get('source_share_group_snapshot_id'):
676 new_share_group_shares = client.list_shares(
Andrew Kerrbf31e912015-07-29 10:39:38 -0400677 detailed=True,
Andrew Kerrb8436922016-06-01 15:32:43 -0400678 params={'share_group_id': share_group['id']},
679 experimental=True)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400680
Andrew Kerrb8436922016-06-01 15:32:43 -0400681 for share in new_share_group_shares:
Andrew Kerrbf31e912015-07-29 10:39:38 -0400682 resource = {"type": "share",
683 "id": share["id"],
684 "client": client,
Andrew Kerrb8436922016-06-01 15:32:43 -0400685 "share_group_id": share.get("share_group_id")}
Andrew Kerrbf31e912015-07-29 10:39:38 -0400686 if cleanup_in_class:
687 cls.class_resources.insert(0, resource)
688 else:
689 cls.method_resources.insert(0, resource)
690
Andrew Kerrb8436922016-06-01 15:32:43 -0400691 client.wait_for_share_group_status(share_group['id'], 'available')
692 return share_group
693
694 @classmethod
695 def create_share_group_type(cls, name=None, share_types=(), is_public=None,
696 group_specs=None, client=None,
697 cleanup_in_class=True, **kwargs):
698 client = client or cls.shares_v2_client
Valeriy Ponomaryove92f09f2017-03-16 17:25:47 +0300699 if (group_specs is None and
700 CONF.share.capability_sg_consistent_snapshot_support):
Valeriy Ponomaryov3c188932017-03-15 19:06:23 +0300701 group_specs = {
702 'consistent_snapshot_support': (
703 CONF.share.capability_sg_consistent_snapshot_support),
704 }
Andrew Kerrb8436922016-06-01 15:32:43 -0400705 share_group_type = client.create_share_group_type(
706 name=name,
707 share_types=share_types,
708 is_public=is_public,
709 group_specs=group_specs,
710 **kwargs)
711 resource = {
712 "type": "share_group_type",
713 "id": share_group_type["id"],
714 "client": client,
715 }
716 if cleanup_in_class:
717 cls.class_resources.insert(0, resource)
718 else:
719 cls.method_resources.insert(0, resource)
720 return share_group_type
Andrew Kerrbf31e912015-07-29 10:39:38 -0400721
722 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200723 def create_snapshot_wait_for_active(cls, share_id, name=None,
724 description=None, force=False,
725 client=None, cleanup_in_class=True):
726 if client is None:
Yogesh1f931ff2015-09-29 23:41:02 -0400727 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200728 if description is None:
729 description = "Tempest's snapshot"
730 snapshot = client.create_snapshot(share_id, name, description, force)
731 resource = {
732 "type": "snapshot",
733 "id": snapshot["id"],
734 "client": client,
735 }
736 if cleanup_in_class:
737 cls.class_resources.insert(0, resource)
738 else:
739 cls.method_resources.insert(0, resource)
740 client.wait_for_snapshot_status(snapshot["id"], "available")
741 return snapshot
742
743 @classmethod
Andrew Kerrb8436922016-06-01 15:32:43 -0400744 def create_share_group_snapshot_wait_for_active(
745 cls, share_group_id, name=None, description=None, client=None,
746 cleanup_in_class=True, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400747 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400748 if description is None:
Andrew Kerrb8436922016-06-01 15:32:43 -0400749 description = "Tempest's share group snapshot"
750 sg_snapshot = client.create_share_group_snapshot(
751 share_group_id, name=name, description=description, **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400752 resource = {
Andrew Kerrb8436922016-06-01 15:32:43 -0400753 "type": "share_group_snapshot",
754 "id": sg_snapshot["id"],
Andrew Kerrbf31e912015-07-29 10:39:38 -0400755 "client": client,
756 }
757 if cleanup_in_class:
758 cls.class_resources.insert(0, resource)
759 else:
760 cls.method_resources.insert(0, resource)
Andrew Kerrb8436922016-06-01 15:32:43 -0400761 client.wait_for_share_group_snapshot_status(
762 sg_snapshot["id"], "available")
763 return sg_snapshot
Andrew Kerrbf31e912015-07-29 10:39:38 -0400764
765 @classmethod
Yogeshbdb88102015-09-29 23:41:02 -0400766 def get_availability_zones(cls, client=None):
767 """List the availability zones for "manila-share" services
768
769 that are currently in "up" state.
770 """
771 client = client or cls.shares_v2_client
772 cls.services = client.list_services()
773 zones = [service['zone'] for service in cls.services if
774 service['binary'] == "manila-share" and
775 service['state'] == 'up']
776 return zones
777
Yogesh1f931ff2015-09-29 23:41:02 -0400778 def get_pools_for_replication_domain(self):
779 # Get the list of pools for the replication domain
780 pools = self.admin_client.list_pools(detail=True)['pools']
Ben Swartzlander7150c652017-02-13 22:31:18 -0500781 instance_host = self.admin_client.get_share(
782 self.shares[0]['id'])['host']
Yogesh1f931ff2015-09-29 23:41:02 -0400783 host_pool = [p for p in pools if p['name'] == instance_host][0]
784 rep_domain = host_pool['capabilities']['replication_domain']
785 pools_in_rep_domain = [p for p in pools if p['capabilities'][
786 'replication_domain'] == rep_domain]
787 return rep_domain, pools_in_rep_domain
788
Yogeshbdb88102015-09-29 23:41:02 -0400789 @classmethod
790 def create_share_replica(cls, share_id, availability_zone, client=None,
791 cleanup_in_class=False, cleanup=True):
792 client = client or cls.shares_v2_client
793 replica = client.create_share_replica(share_id, availability_zone)
794 resource = {
795 "type": "share_replica",
796 "id": replica["id"],
797 "client": client,
798 "share_id": share_id,
799 }
800 # NOTE(Yogi1): Cleanup needs to be disabled during promotion tests.
801 if cleanup:
802 if cleanup_in_class:
803 cls.class_resources.insert(0, resource)
804 else:
805 cls.method_resources.insert(0, resource)
806 client.wait_for_share_replica_status(
807 replica["id"], constants.STATUS_AVAILABLE)
808 return replica
809
810 @classmethod
811 def delete_share_replica(cls, replica_id, client=None):
812 client = client or cls.shares_v2_client
Yogesh1f931ff2015-09-29 23:41:02 -0400813 try:
814 client.delete_share_replica(replica_id)
815 client.wait_for_resource_deletion(replica_id=replica_id)
816 except exceptions.NotFound:
817 pass
Yogeshbdb88102015-09-29 23:41:02 -0400818
819 @classmethod
820 def promote_share_replica(cls, replica_id, client=None):
821 client = client or cls.shares_v2_client
822 replica = client.promote_share_replica(replica_id)
823 client.wait_for_share_replica_status(
824 replica["id"],
825 constants.REPLICATION_STATE_ACTIVE,
826 status_attr="replica_state")
827 return replica
828
yogeshdb32f462016-09-28 15:09:50 -0400829 def _get_access_rule_data_from_config(self):
830 """Get the first available access type/to combination from config.
831
832 This method opportunistically picks the first configured protocol
833 to create the share. Do not use this method in tests where you need
834 to test depth and breadth in the access types and access recipients.
835 """
836 protocol = self.shares_v2_client.share_protocol
837
838 if protocol in CONF.share.enable_ip_rules_for_protocols:
839 access_type = "ip"
840 access_to = utils.rand_ip()
841 elif protocol in CONF.share.enable_user_rules_for_protocols:
842 access_type = "user"
843 access_to = CONF.share.username_for_user_rules
844 elif protocol in CONF.share.enable_cert_rules_for_protocols:
845 access_type = "cert"
846 access_to = "client3.com"
847 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
848 access_type = "cephx"
849 access_to = "eve"
850 else:
851 message = "Unrecognized protocol and access rules configuration."
852 raise self.skipException(message)
853
854 return access_type, access_to
855
Yogeshbdb88102015-09-29 23:41:02 -0400856 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200857 def create_share_network(cls, client=None,
858 cleanup_in_class=False, **kwargs):
859 if client is None:
860 client = cls.shares_client
861 share_network = client.create_share_network(**kwargs)
862 resource = {
863 "type": "share_network",
864 "id": share_network["id"],
865 "client": client,
866 }
867 if cleanup_in_class:
868 cls.class_resources.insert(0, resource)
869 else:
870 cls.method_resources.insert(0, resource)
871 return share_network
872
873 @classmethod
874 def create_security_service(cls, ss_type="ldap", client=None,
875 cleanup_in_class=False, **kwargs):
876 if client is None:
877 client = cls.shares_client
878 security_service = client.create_security_service(ss_type, **kwargs)
879 resource = {
880 "type": "security_service",
881 "id": security_service["id"],
882 "client": client,
883 }
884 if cleanup_in_class:
885 cls.class_resources.insert(0, resource)
886 else:
887 cls.method_resources.insert(0, resource)
888 return security_service
889
890 @classmethod
891 def create_share_type(cls, name, is_public=True, client=None,
892 cleanup_in_class=True, **kwargs):
893 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200894 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200895 share_type = client.create_share_type(name, is_public, **kwargs)
896 resource = {
897 "type": "share_type",
898 "id": share_type["share_type"]["id"],
899 "client": client,
900 }
901 if cleanup_in_class:
902 cls.class_resources.insert(0, resource)
903 else:
904 cls.method_resources.insert(0, resource)
905 return share_type
906
907 @staticmethod
Clinton Knight4699a8c2016-08-16 22:36:13 -0400908 def add_extra_specs_to_dict(extra_specs=None):
909 """Add any required extra-specs to share type dictionary"""
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300910 dhss = six.text_type(CONF.share.multitenancy_enabled)
911 snapshot_support = six.text_type(
912 CONF.share.capability_snapshot_support)
Clinton Knight4699a8c2016-08-16 22:36:13 -0400913 create_from_snapshot_support = six.text_type(
914 CONF.share.capability_create_share_from_snapshot_support)
915
916 extra_specs_dict = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300917 "driver_handles_share_servers": dhss,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200918 }
Clinton Knight4699a8c2016-08-16 22:36:13 -0400919
920 optional = {
921 "snapshot_support": snapshot_support,
922 "create_share_from_snapshot_support": create_from_snapshot_support,
923 }
924 # NOTE(gouthamr): In micro-versions < 2.24, snapshot_support is a
925 # required extra-spec
926 extra_specs_dict.update(optional)
927
Marc Koderer0abc93b2015-07-15 09:18:35 +0200928 if extra_specs:
Clinton Knight4699a8c2016-08-16 22:36:13 -0400929 extra_specs_dict.update(extra_specs)
930
931 return extra_specs_dict
Marc Koderer0abc93b2015-07-15 09:18:35 +0200932
933 @classmethod
934 def clear_isolated_creds(cls, creds=None):
935 if creds is None:
936 creds = cls.method_isolated_creds
937 for ic in creds:
938 if "deleted" not in ic.keys():
939 ic["deleted"] = False
940 if not ic["deleted"]:
941 with handle_cleanup_exceptions():
942 ic["method"]()
943 ic["deleted"] = True
944
945 @classmethod
Yogesh1f931ff2015-09-29 23:41:02 -0400946 def clear_share_replicas(cls, share_id, client=None):
947 client = client or cls.shares_v2_client
948 share_replicas = client.list_share_replicas(
949 share_id=share_id)
950
951 for replica in share_replicas:
952 try:
953 cls.delete_share_replica(replica['id'])
954 except exceptions.BadRequest:
955 # Ignore the exception due to deletion of last active replica
956 pass
957
958 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200959 def clear_resources(cls, resources=None):
960 """Deletes resources, that were created in test suites.
961
962 This method tries to remove resources from resource list,
963 if it is not found, assumed it was deleted in test itself.
964 It is expected, that all resources were added as LIFO
965 due to restriction of deletion resources, that is in the chain.
966
967 :param resources: dict with keys 'type','id','client' and 'deleted'
968 """
Marc Koderer0abc93b2015-07-15 09:18:35 +0200969 if resources is None:
970 resources = cls.method_resources
971 for res in resources:
972 if "deleted" not in res.keys():
973 res["deleted"] = False
974 if "client" not in res.keys():
975 res["client"] = cls.shares_client
976 if not(res["deleted"]):
977 res_id = res['id']
978 client = res["client"]
979 with handle_cleanup_exceptions():
980 if res["type"] is "share":
Yogesh1f931ff2015-09-29 23:41:02 -0400981 cls.clear_share_replicas(res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -0400982 share_group_id = res.get('share_group_id')
983 if share_group_id:
984 params = {'share_group_id': share_group_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400985 client.delete_share(res_id, params=params)
986 else:
987 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200988 client.wait_for_resource_deletion(share_id=res_id)
989 elif res["type"] is "snapshot":
990 client.delete_snapshot(res_id)
991 client.wait_for_resource_deletion(snapshot_id=res_id)
yogesh06f519f2017-06-26 13:16:12 -0400992 elif (res["type"] is "share_network" and
993 res_id != CONF.share.share_network_id):
Victoria Martinez de la Cruzcad92012018-06-08 14:46:35 -0400994 client.delete_share_network(res_id)
995 client.wait_for_resource_deletion(sn_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200996 elif res["type"] is "security_service":
997 client.delete_security_service(res_id)
998 client.wait_for_resource_deletion(ss_id=res_id)
999 elif res["type"] is "share_type":
1000 client.delete_share_type(res_id)
1001 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrb8436922016-06-01 15:32:43 -04001002 elif res["type"] is "share_group":
1003 client.delete_share_group(res_id)
1004 client.wait_for_resource_deletion(
1005 share_group_id=res_id)
1006 elif res["type"] is "share_group_type":
1007 client.delete_share_group_type(res_id)
1008 client.wait_for_resource_deletion(
1009 share_group_type_id=res_id)
1010 elif res["type"] is "share_group_snapshot":
1011 client.delete_share_group_snapshot(res_id)
1012 client.wait_for_resource_deletion(
1013 share_group_snapshot_id=res_id)
Yogeshbdb88102015-09-29 23:41:02 -04001014 elif res["type"] is "share_replica":
1015 client.delete_share_replica(res_id)
1016 client.wait_for_resource_deletion(replica_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +02001017 else:
huayue97bacbf2016-01-04 09:57:39 +08001018 LOG.warning("Provided unsupported resource type for "
junbolib236c242017-07-18 18:12:37 +08001019 "cleanup '%s'. Skipping.", res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +02001020 res["deleted"] = True
1021
1022 @classmethod
1023 def generate_share_network_data(self):
1024 data = {
1025 "name": data_utils.rand_name("sn-name"),
1026 "description": data_utils.rand_name("sn-desc"),
1027 "neutron_net_id": data_utils.rand_name("net-id"),
1028 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
1029 }
1030 return data
1031
1032 @classmethod
1033 def generate_security_service_data(self):
1034 data = {
1035 "name": data_utils.rand_name("ss-name"),
1036 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001037 "dns_ip": utils.rand_ip(),
1038 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +02001039 "domain": data_utils.rand_name("ss-domain"),
1040 "user": data_utils.rand_name("ss-user"),
1041 "password": data_utils.rand_name("ss-password"),
1042 }
1043 return data
1044
1045 # Useful assertions
1046 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
1047 """Assert two dicts are equivalent.
1048
1049 This is a 'deep' match in the sense that it handles nested
1050 dictionaries appropriately.
1051
1052 NOTE:
1053
1054 If you don't care (or don't know) a given value, you can specify
1055 the string DONTCARE as the value. This will cause that dict-item
1056 to be skipped.
1057
1058 """
1059 def raise_assertion(msg):
1060 d1str = str(d1)
1061 d2str = str(d2)
1062 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
1063 'd2: %(d2str)s' %
1064 {"msg": msg, "d1str": d1str, "d2str": d2str})
1065 raise AssertionError(base_msg)
1066
1067 d1keys = set(d1.keys())
1068 d2keys = set(d2.keys())
1069 if d1keys != d2keys:
1070 d1only = d1keys - d2keys
1071 d2only = d2keys - d1keys
1072 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
1073 'Keys in d2 and not d1: %(d2only)s' %
1074 {"d1only": d1only, "d2only": d2only})
1075
1076 for key in d1keys:
1077 d1value = d1[key]
1078 d2value = d2[key]
1079 try:
1080 error = abs(float(d1value) - float(d2value))
1081 within_tolerance = error <= tolerance
1082 except (ValueError, TypeError):
daiki kato6914b1a2016-03-16 17:16:57 +09001083 # If both values aren't convertible to float, just ignore
Marc Koderer0abc93b2015-07-15 09:18:35 +02001084 # ValueError if arg is a str, TypeError if it's something else
1085 # (like None)
1086 within_tolerance = False
1087
1088 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
1089 self.assertDictMatch(d1value, d2value)
1090 elif 'DONTCARE' in (d1value, d2value):
1091 continue
1092 elif approx_equal and within_tolerance:
1093 continue
1094 elif d1value != d2value:
1095 raise_assertion("d1['%(key)s']=%(d1value)s != "
1096 "d2['%(key)s']=%(d2value)s" %
1097 {
1098 "key": key,
1099 "d1value": d1value,
1100 "d2value": d2value
1101 })
1102
Alex Meadeba8a1602016-05-06 09:33:09 -04001103 def create_user_message(self):
1104 """Trigger a 'no valid host' situation to generate a message."""
1105 extra_specs = {
1106 'vendor_name': 'foobar',
1107 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
1108 }
1109 share_type_name = data_utils.rand_name("share-type")
1110
1111 bogus_type = self.create_share_type(
1112 name=share_type_name,
1113 extra_specs=extra_specs)['share_type']
1114
1115 params = {'share_type_id': bogus_type['id'],
1116 'share_network_id': self.shares_v2_client.share_network_id}
1117 share = self.shares_v2_client.create_share(**params)
1118 self.addCleanup(self.shares_v2_client.delete_share, share['id'])
1119 self.shares_v2_client.wait_for_share_status(share['id'], "error")
1120 return self.shares_v2_client.wait_for_message(share['id'])
1121
Marc Koderer0abc93b2015-07-15 09:18:35 +02001122
1123class BaseSharesAltTest(BaseSharesTest):
1124 """Base test case class for all Shares Alt API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001125 credentials = ('alt', )
Marc Koderer0abc93b2015-07-15 09:18:35 +02001126
1127
1128class BaseSharesAdminTest(BaseSharesTest):
1129 """Base test case class for all Shares Admin API tests."""
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001130 credentials = ('admin', )
1131
1132
1133class BaseSharesMixedTest(BaseSharesTest):
1134 """Base test case class for all Shares API tests with all user roles."""
1135 credentials = ('primary', 'alt', 'admin')
Marc Koderer0abc93b2015-07-15 09:18:35 +02001136
1137 @classmethod
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001138 def setup_clients(cls):
1139 super(BaseSharesMixedTest, cls).setup_clients()
Andrea Frittoli (andreaf)369391a2016-06-27 18:59:13 +01001140 # Initialise share clients
1141 cls.admin_shares_client = cls.os_admin.share_v1.SharesClient()
1142 cls.admin_shares_v2_client = cls.os_admin.share_v2.SharesV2Client()
1143 cls.alt_shares_client = cls.os_alt.share_v1.SharesClient()
1144 cls.alt_shares_v2_client = cls.os_alt.share_v2.SharesV2Client()
1145 # Initialise network clients
1146 cls.os_admin.networks_client = cls.os_admin.network.NetworksClient()
1147 cls.os_alt.networks_client = cls.os_alt.network.NetworksClient()
Valeriy Ponomaryov39cdf722016-05-30 18:16:15 +03001148
1149 if CONF.share.multitenancy_enabled:
1150 admin_share_network_id = cls.provide_share_network(
1151 cls.admin_shares_v2_client, cls.os_admin.networks_client)
1152 cls.admin_shares_client.share_network_id = admin_share_network_id
1153 cls.admin_shares_v2_client.share_network_id = (
1154 admin_share_network_id)
1155
1156 alt_share_network_id = cls.provide_share_network(
1157 cls.alt_shares_v2_client, cls.os_alt.networks_client)
1158 cls.alt_shares_client.share_network_id = alt_share_network_id
1159 cls.alt_shares_v2_client.share_network_id = alt_share_network_id
haobing101c7fee2018-03-09 16:33:00 +08001160 resource = {
1161 "type": "share_network",
1162 "id": alt_share_network_id,
1163 "client": cls.alt_shares_v2_client,
1164 }
1165 cls.class_resources.insert(0, resource)