blob: 6236cad6747cb50bcac03646e98182cdfac84db9 [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
18import traceback
19
20from oslo_concurrency import lockutils
21from oslo_log import log
22import six
Sam Wanc7b7f1f2015-11-25 00:22:28 -050023from tempest.common import credentials_factory as common_creds
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020024from tempest.common import dynamic_creds
25from tempest import config
Ben Swartzlander1c4ff522016-03-02 22:16:23 -050026from tempest.lib.common.utils import data_utils
27from tempest.lib import exceptions
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020028from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020029
30from manila_tempest_tests import clients_share as clients
31from manila_tempest_tests import share_exceptions
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020032from manila_tempest_tests import utils
Marc Koderer0abc93b2015-07-15 09:18:35 +020033
34CONF = config.CONF
35LOG = log.getLogger(__name__)
36
37
38class handle_cleanup_exceptions(object):
39 """Handle exceptions raised with cleanup operations.
40
41 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
42 are raised.
43 Suppress all other exceptions only in case config opt
44 'suppress_errors_in_cleanup' in config group 'share' is True.
45 """
46
47 def __enter__(self):
48 return self
49
50 def __exit__(self, exc_type, exc_value, exc_traceback):
51 if not (isinstance(exc_value,
52 (exceptions.NotFound, exceptions.Forbidden)) or
53 CONF.share.suppress_errors_in_cleanup):
54 return False # Do not suppress error if any
55 if exc_traceback:
56 LOG.error("Suppressed cleanup error in Manila: "
57 "\n%s" % traceback.format_exc())
58 return True # Suppress error if any
59
60
61def network_synchronized(f):
62
63 def wrapped_func(self, *args, **kwargs):
64 with_isolated_creds = True if len(args) > 2 else False
65 no_lock_required = kwargs.get(
66 "isolated_creds_client", with_isolated_creds)
67 if no_lock_required:
68 # Usage of not reusable network. No need in lock.
69 return f(self, *args, **kwargs)
70
71 # Use lock assuming reusage of common network.
72 @lockutils.synchronized("manila_network_lock", external=True)
73 def source_func(self, *args, **kwargs):
74 return f(self, *args, **kwargs)
75
76 return source_func(self, *args, **kwargs)
77
78 return wrapped_func
79
80
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020081skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
Xing Yang69b00b52015-11-22 16:10:44 -050082skip_if_microversion_lt = utils.skip_if_microversion_lt
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +020083
84
Marc Koderer0abc93b2015-07-15 09:18:35 +020085class BaseSharesTest(test.BaseTestCase):
86 """Base test case class for all Manila API tests."""
87
88 force_tenant_isolation = False
John Spray061b1452015-11-18 13:15:32 +000089 protocols = ["nfs", "cifs", "glusterfs", "hdfs", "cephfs"]
Marc Koderer0abc93b2015-07-15 09:18:35 +020090
91 # Will be cleaned up in resource_cleanup
92 class_resources = []
93
94 # Will be cleaned up in tearDown method
95 method_resources = []
96
97 # Will be cleaned up in resource_cleanup
98 class_isolated_creds = []
99
100 # Will be cleaned up in tearDown method
101 method_isolated_creds = []
102
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200103 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200104 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200105 raise self.skipException(
106 "Microversion '%s' is not supported." % microversion)
107
Xing Yang69b00b52015-11-22 16:10:44 -0500108 def skip_if_microversion_lt(self, microversion):
109 if utils.is_microversion_lt(CONF.share.max_api_microversion,
110 microversion):
111 raise self.skipException(
112 "Microversion must be greater than or equal to '%s'." %
113 microversion)
114
Marc Koderer0abc93b2015-07-15 09:18:35 +0200115 @classmethod
116 def get_client_with_isolated_creds(cls,
117 name=None,
118 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400119 cleanup_in_class=False,
120 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200121 """Creates isolated creds.
122
123 :param name: name, will be used for naming ic and related stuff
124 :param type_of_creds: admin, alt or primary
125 :param cleanup_in_class: defines place where to delete
126 :returns: SharesClient -- shares client with isolated creds.
127 :returns: To client added dict attr 'creds' with
128 :returns: key elements 'tenant' and 'user'.
129 """
130 if name is None:
131 # Get name of test method
132 name = inspect.stack()[1][3]
133 if len(name) > 32:
134 name = name[0:32]
135
136 # Choose type of isolated creds
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200137 ic = dynamic_creds.DynamicCredentialProvider(
138 identity_version=CONF.identity.auth_version,
139 name=name,
Sam Wanc7b7f1f2015-11-25 00:22:28 -0500140 admin_role=CONF.identity.admin_role,
141 admin_creds=common_creds.get_configured_credentials(
142 'identity_admin'))
Marc Koderer0abc93b2015-07-15 09:18:35 +0200143 if "admin" in type_of_creds:
144 creds = ic.get_admin_creds()
145 elif "alt" in type_of_creds:
146 creds = ic.get_alt_creds()
147 else:
148 creds = ic.self.get_credentials(type_of_creds)
149 ic.type_of_creds = type_of_creds
150
151 # create client with isolated creds
152 os = clients.Manager(credentials=creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400153 if client_version == '1':
154 client = os.shares_client
155 elif client_version == '2':
156 client = os.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200157
158 # Set place where will be deleted isolated creds
159 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200160 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200161 "deleted": False,
162 }
163 if cleanup_in_class:
164 cls.class_isolated_creds.insert(0, ic_res)
165 else:
166 cls.method_isolated_creds.insert(0, ic_res)
167
168 # Provide share network
169 if CONF.share.multitenancy_enabled:
170 if not CONF.service_available.neutron:
171 raise cls.skipException("Neutron support is required")
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200172 nc = os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200173 share_network_id = cls.provide_share_network(client, nc, ic)
174 client.share_network_id = share_network_id
175 resource = {
176 "type": "share_network",
177 "id": client.share_network_id,
178 "client": client,
179 }
180 if cleanup_in_class:
181 cls.class_resources.insert(0, resource)
182 else:
183 cls.method_resources.insert(0, resource)
184 return client
185
186 @classmethod
187 def verify_nonempty(cls, *args):
188 if not all(args):
189 msg = "Missing API credentials in configuration."
190 raise cls.skipException(msg)
191
192 @classmethod
193 def resource_setup(cls):
194 if not (any(p in CONF.share.enable_protocols
195 for p in cls.protocols) and
196 CONF.service_available.manila):
197 skip_msg = "Manila is disabled"
198 raise cls.skipException(skip_msg)
199 super(BaseSharesTest, cls).resource_setup()
200 if not hasattr(cls, "os"):
201 cls.username = CONF.identity.username
202 cls.password = CONF.identity.password
203 cls.tenant_name = CONF.identity.tenant_name
204 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
205 cls.os = clients.Manager()
206 if CONF.share.multitenancy_enabled:
207 if not CONF.service_available.neutron:
208 raise cls.skipException("Neutron support is required")
209 sc = cls.os.shares_client
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200210 nc = cls.os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200211 share_network_id = cls.provide_share_network(sc, nc)
212 cls.os.shares_client.share_network_id = share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400213 cls.os.shares_v2_client.share_network_id = share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200214 cls.shares_client = cls.os.shares_client
Clinton Knighte5c8f092015-08-27 15:00:23 -0400215 cls.shares_v2_client = cls.os.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200216
217 def setUp(self):
218 super(BaseSharesTest, self).setUp()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200219 self.addCleanup(self.clear_isolated_creds)
Valeriy Ponomaryovdd162cb2016-01-20 19:09:49 +0200220 self.addCleanup(self.clear_resources)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200221
222 @classmethod
223 def resource_cleanup(cls):
224 super(BaseSharesTest, cls).resource_cleanup()
225 cls.clear_resources(cls.class_resources)
226 cls.clear_isolated_creds(cls.class_isolated_creds)
227
228 @classmethod
229 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200230 def provide_share_network(cls, shares_client, networks_client,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200231 isolated_creds_client=None):
232 """Used for finding/creating share network for multitenant driver.
233
234 This method creates/gets entity share-network for one tenant. This
235 share-network will be used for creation of service vm.
236
237 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200238 :param networks_client: network client from same tenant as shares
239 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200240 If provided, then its networking will be used if needed.
241 If not provided, then common network will be used if needed.
242 :returns: str -- share network id for shares_client tenant
243 :returns: None -- if single-tenant driver used
244 """
245
246 sc = shares_client
247
248 if not CONF.share.multitenancy_enabled:
249 # Assumed usage of a single-tenant driver
250 share_network_id = None
251 elif sc.share_network_id:
252 # Share-network already exists, use it
253 share_network_id = sc.share_network_id
254 else:
255 net_id = subnet_id = share_network_id = None
256
257 if not isolated_creds_client:
258 # Search for networks, created in previous runs
259 search_word = "reusable"
260 sn_name = "autogenerated_by_tempest_%s" % search_word
261 service_net_name = "share-service"
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200262 networks = networks_client.list_networks()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200263 if "networks" in networks.keys():
264 networks = networks["networks"]
265 for network in networks:
266 if (service_net_name in network["name"] and
267 sc.tenant_id == network['tenant_id']):
268 net_id = network["id"]
269 if len(network["subnets"]) > 0:
270 subnet_id = network["subnets"][0]
271 break
272
273 # Create suitable network
274 if (net_id is None or subnet_id is None):
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200275 ic = dynamic_creds.DynamicCredentialProvider(
276 identity_version=CONF.identity.auth_version,
277 name=service_net_name,
278 admin_role=CONF.identity.admin_role,
Sam Wanc7b7f1f2015-11-25 00:22:28 -0500279 admin_creds=common_creds.get_configured_credentials(
280 'identity_admin'))
Marc Koderer0abc93b2015-07-15 09:18:35 +0200281 net_data = ic._create_network_resources(sc.tenant_id)
282 network, subnet, router = net_data
283 net_id = network["id"]
284 subnet_id = subnet["id"]
285
286 # Try get suitable share-network
287 share_networks = sc.list_share_networks_with_detail()
288 for sn in share_networks:
289 if (net_id == sn["neutron_net_id"] and
290 subnet_id == sn["neutron_subnet_id"] and
291 sn["name"] and search_word in sn["name"]):
292 share_network_id = sn["id"]
293 break
294 else:
295 sn_name = "autogenerated_by_tempest_for_isolated_creds"
296 # Use precreated network and subnet from isolated creds
297 net_id = isolated_creds_client.get_credentials(
298 isolated_creds_client.type_of_creds).network['id']
299 subnet_id = isolated_creds_client.get_credentials(
300 isolated_creds_client.type_of_creds).subnet['id']
301
302 # Create suitable share-network
303 if share_network_id is None:
304 sn_desc = "This share-network was created by tempest"
305 sn = sc.create_share_network(name=sn_name,
306 description=sn_desc,
307 neutron_net_id=net_id,
308 neutron_subnet_id=subnet_id)
309 share_network_id = sn["id"]
310
311 return share_network_id
312
313 @classmethod
314 def _create_share(cls, share_protocol=None, size=1, name=None,
315 snapshot_id=None, description=None, metadata=None,
316 share_network_id=None, share_type_id=None,
Andrew Kerrbf31e912015-07-29 10:39:38 -0400317 consistency_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400318 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300319 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200320 description = description or "Tempest's share"
321 share_network_id = share_network_id or client.share_network_id or None
322 metadata = metadata or {}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400323 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200324 'share_protocol': share_protocol,
325 'size': size,
326 'name': name,
327 'snapshot_id': snapshot_id,
328 'description': description,
329 'metadata': metadata,
330 'share_network_id': share_network_id,
331 'share_type_id': share_type_id,
332 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400333 })
Andrew Kerrbf31e912015-07-29 10:39:38 -0400334 if consistency_group_id:
335 kwargs['consistency_group_id'] = consistency_group_id
336
Marc Koderer0abc93b2015-07-15 09:18:35 +0200337 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400338 resource = {"type": "share", "id": share["id"], "client": client,
339 "consistency_group_id": consistency_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200340 cleanup_list = (cls.class_resources if cleanup_in_class else
341 cls.method_resources)
342 cleanup_list.insert(0, resource)
343 return share
344
345 @classmethod
Clinton Knighte5c8f092015-08-27 15:00:23 -0400346 def migrate_share(cls, share_id, dest_host, client=None, **kwargs):
347 client = client or cls.shares_v2_client
348 client.migrate_share(share_id, dest_host, **kwargs)
Valeriy Ponomaryov1801c0c2015-12-01 17:30:16 +0200349 share = client.wait_for_migration_completed(
350 share_id, dest_host, version=kwargs.get('version'))
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300351 return share
352
353 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200354 def create_share(cls, *args, **kwargs):
355 """Create one share and wait for available state. Retry if allowed."""
356 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
357 return result[0]
358
359 @classmethod
360 def create_shares(cls, share_data_list):
361 """Creates several shares in parallel with retries.
362
363 Use this method when you want to create more than one share at same
364 time. Especially if config option 'share.share_creation_retry_number'
365 has value more than zero (0).
366 All shares will be expected to have 'available' status with or without
367 recreation else error will be raised.
368
369 :param share_data_list: list -- list of dictionaries with 'args' and
370 'kwargs' for '_create_share' method of this base class.
371 example of data:
372 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
373 :returns: list -- list of shares created using provided data.
374 """
375
376 data = [copy.deepcopy(d) for d in share_data_list]
377 for d in data:
378 if not isinstance(d, dict):
379 raise exceptions.TempestException(
380 "Expected 'dict', got '%s'" % type(d))
381 if "args" not in d:
382 d["args"] = []
383 if "kwargs" not in d:
384 d["kwargs"] = {}
385 if len(d) > 2:
386 raise exceptions.TempestException(
387 "Expected only 'args' and 'kwargs' keys. "
388 "Provided %s" % list(d))
389 d["kwargs"]["client"] = d["kwargs"].get(
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300390 "client", cls.shares_v2_client)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200391 d["share"] = cls._create_share(*d["args"], **d["kwargs"])
392 d["cnt"] = 0
393 d["available"] = False
394
395 while not all(d["available"] for d in data):
396 for d in data:
397 if d["available"]:
398 continue
399 try:
400 d["kwargs"]["client"].wait_for_share_status(
401 d["share"]["id"], "available")
402 d["available"] = True
403 except (share_exceptions.ShareBuildErrorException,
404 exceptions.TimeoutException) as e:
405 if CONF.share.share_creation_retry_number > d["cnt"]:
406 d["cnt"] += 1
407 msg = ("Share '%s' failed to be built. "
408 "Trying create another." % d["share"]["id"])
409 LOG.error(msg)
410 LOG.error(e)
411 d["share"] = cls._create_share(
412 *d["args"], **d["kwargs"])
413 else:
414 raise e
415
416 return [d["share"] for d in data]
417
418 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400419 def create_consistency_group(cls, client=None, cleanup_in_class=True,
420 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400421 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400422 kwargs['share_network_id'] = (share_network_id or
423 client.share_network_id or None)
424 consistency_group = client.create_consistency_group(**kwargs)
425 resource = {
426 "type": "consistency_group",
427 "id": consistency_group["id"],
428 "client": client}
429 if cleanup_in_class:
430 cls.class_resources.insert(0, resource)
431 else:
432 cls.method_resources.insert(0, resource)
433
434 if kwargs.get('source_cgsnapshot_id'):
435 new_cg_shares = client.list_shares(
436 detailed=True,
437 params={'consistency_group_id': consistency_group['id']})
438
439 for share in new_cg_shares:
440 resource = {"type": "share",
441 "id": share["id"],
442 "client": client,
443 "consistency_group_id": share.get(
444 'consistency_group_id')}
445 if cleanup_in_class:
446 cls.class_resources.insert(0, resource)
447 else:
448 cls.method_resources.insert(0, resource)
449
450 client.wait_for_consistency_group_status(consistency_group['id'],
451 'available')
452 return consistency_group
453
454 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200455 def create_snapshot_wait_for_active(cls, share_id, name=None,
456 description=None, force=False,
457 client=None, cleanup_in_class=True):
458 if client is None:
459 client = cls.shares_client
460 if description is None:
461 description = "Tempest's snapshot"
462 snapshot = client.create_snapshot(share_id, name, description, force)
463 resource = {
464 "type": "snapshot",
465 "id": snapshot["id"],
466 "client": client,
467 }
468 if cleanup_in_class:
469 cls.class_resources.insert(0, resource)
470 else:
471 cls.method_resources.insert(0, resource)
472 client.wait_for_snapshot_status(snapshot["id"], "available")
473 return snapshot
474
475 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400476 def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
477 name=None, description=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400478 client=None, cleanup_in_class=True,
479 **kwargs):
480 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400481 if description is None:
482 description = "Tempest's cgsnapshot"
Clinton Knighte5c8f092015-08-27 15:00:23 -0400483 cgsnapshot = client.create_cgsnapshot(consistency_group_id,
484 name=name,
485 description=description,
486 **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400487 resource = {
488 "type": "cgsnapshot",
489 "id": cgsnapshot["id"],
490 "client": client,
491 }
492 if cleanup_in_class:
493 cls.class_resources.insert(0, resource)
494 else:
495 cls.method_resources.insert(0, resource)
496 client.wait_for_cgsnapshot_status(cgsnapshot["id"], "available")
497 return cgsnapshot
498
499 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200500 def create_share_network(cls, client=None,
501 cleanup_in_class=False, **kwargs):
502 if client is None:
503 client = cls.shares_client
504 share_network = client.create_share_network(**kwargs)
505 resource = {
506 "type": "share_network",
507 "id": share_network["id"],
508 "client": client,
509 }
510 if cleanup_in_class:
511 cls.class_resources.insert(0, resource)
512 else:
513 cls.method_resources.insert(0, resource)
514 return share_network
515
516 @classmethod
517 def create_security_service(cls, ss_type="ldap", client=None,
518 cleanup_in_class=False, **kwargs):
519 if client is None:
520 client = cls.shares_client
521 security_service = client.create_security_service(ss_type, **kwargs)
522 resource = {
523 "type": "security_service",
524 "id": security_service["id"],
525 "client": client,
526 }
527 if cleanup_in_class:
528 cls.class_resources.insert(0, resource)
529 else:
530 cls.method_resources.insert(0, resource)
531 return security_service
532
533 @classmethod
534 def create_share_type(cls, name, is_public=True, client=None,
535 cleanup_in_class=True, **kwargs):
536 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200537 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200538 share_type = client.create_share_type(name, is_public, **kwargs)
539 resource = {
540 "type": "share_type",
541 "id": share_type["share_type"]["id"],
542 "client": client,
543 }
544 if cleanup_in_class:
545 cls.class_resources.insert(0, resource)
546 else:
547 cls.method_resources.insert(0, resource)
548 return share_type
549
550 @staticmethod
551 def add_required_extra_specs_to_dict(extra_specs=None):
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300552 dhss = six.text_type(CONF.share.multitenancy_enabled)
553 snapshot_support = six.text_type(
554 CONF.share.capability_snapshot_support)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200555 required = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300556 "driver_handles_share_servers": dhss,
557 "snapshot_support": snapshot_support,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200558 }
559 if extra_specs:
560 required.update(extra_specs)
561 return required
562
563 @classmethod
564 def clear_isolated_creds(cls, creds=None):
565 if creds is None:
566 creds = cls.method_isolated_creds
567 for ic in creds:
568 if "deleted" not in ic.keys():
569 ic["deleted"] = False
570 if not ic["deleted"]:
571 with handle_cleanup_exceptions():
572 ic["method"]()
573 ic["deleted"] = True
574
575 @classmethod
576 def clear_resources(cls, resources=None):
577 """Deletes resources, that were created in test suites.
578
579 This method tries to remove resources from resource list,
580 if it is not found, assumed it was deleted in test itself.
581 It is expected, that all resources were added as LIFO
582 due to restriction of deletion resources, that is in the chain.
583
584 :param resources: dict with keys 'type','id','client' and 'deleted'
585 """
586
587 if resources is None:
588 resources = cls.method_resources
589 for res in resources:
590 if "deleted" not in res.keys():
591 res["deleted"] = False
592 if "client" not in res.keys():
593 res["client"] = cls.shares_client
594 if not(res["deleted"]):
595 res_id = res['id']
596 client = res["client"]
597 with handle_cleanup_exceptions():
598 if res["type"] is "share":
Andrew Kerrbf31e912015-07-29 10:39:38 -0400599 cg_id = res.get('consistency_group_id')
600 if cg_id:
601 params = {'consistency_group_id': cg_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400602 client.delete_share(res_id, params=params)
603 else:
604 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200605 client.wait_for_resource_deletion(share_id=res_id)
606 elif res["type"] is "snapshot":
607 client.delete_snapshot(res_id)
608 client.wait_for_resource_deletion(snapshot_id=res_id)
609 elif res["type"] is "share_network":
610 client.delete_share_network(res_id)
611 client.wait_for_resource_deletion(sn_id=res_id)
612 elif res["type"] is "security_service":
613 client.delete_security_service(res_id)
614 client.wait_for_resource_deletion(ss_id=res_id)
615 elif res["type"] is "share_type":
616 client.delete_share_type(res_id)
617 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400618 elif res["type"] is "consistency_group":
619 client.delete_consistency_group(res_id)
620 client.wait_for_resource_deletion(cg_id=res_id)
621 elif res["type"] is "cgsnapshot":
622 client.delete_cgsnapshot(res_id)
623 client.wait_for_resource_deletion(cgsnapshot_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200624 else:
huayue97bacbf2016-01-04 09:57:39 +0800625 LOG.warning("Provided unsupported resource type for "
626 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200627 res["deleted"] = True
628
629 @classmethod
630 def generate_share_network_data(self):
631 data = {
632 "name": data_utils.rand_name("sn-name"),
633 "description": data_utils.rand_name("sn-desc"),
634 "neutron_net_id": data_utils.rand_name("net-id"),
635 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
636 }
637 return data
638
639 @classmethod
640 def generate_security_service_data(self):
641 data = {
642 "name": data_utils.rand_name("ss-name"),
643 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200644 "dns_ip": utils.rand_ip(),
645 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200646 "domain": data_utils.rand_name("ss-domain"),
647 "user": data_utils.rand_name("ss-user"),
648 "password": data_utils.rand_name("ss-password"),
649 }
650 return data
651
652 # Useful assertions
653 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
654 """Assert two dicts are equivalent.
655
656 This is a 'deep' match in the sense that it handles nested
657 dictionaries appropriately.
658
659 NOTE:
660
661 If you don't care (or don't know) a given value, you can specify
662 the string DONTCARE as the value. This will cause that dict-item
663 to be skipped.
664
665 """
666 def raise_assertion(msg):
667 d1str = str(d1)
668 d2str = str(d2)
669 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
670 'd2: %(d2str)s' %
671 {"msg": msg, "d1str": d1str, "d2str": d2str})
672 raise AssertionError(base_msg)
673
674 d1keys = set(d1.keys())
675 d2keys = set(d2.keys())
676 if d1keys != d2keys:
677 d1only = d1keys - d2keys
678 d2only = d2keys - d1keys
679 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
680 'Keys in d2 and not d1: %(d2only)s' %
681 {"d1only": d1only, "d2only": d2only})
682
683 for key in d1keys:
684 d1value = d1[key]
685 d2value = d2[key]
686 try:
687 error = abs(float(d1value) - float(d2value))
688 within_tolerance = error <= tolerance
689 except (ValueError, TypeError):
690 # If both values aren't convertable to float, just ignore
691 # ValueError if arg is a str, TypeError if it's something else
692 # (like None)
693 within_tolerance = False
694
695 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
696 self.assertDictMatch(d1value, d2value)
697 elif 'DONTCARE' in (d1value, d2value):
698 continue
699 elif approx_equal and within_tolerance:
700 continue
701 elif d1value != d2value:
702 raise_assertion("d1['%(key)s']=%(d1value)s != "
703 "d2['%(key)s']=%(d2value)s" %
704 {
705 "key": key,
706 "d1value": d1value,
707 "d2value": d2value
708 })
709
710
711class BaseSharesAltTest(BaseSharesTest):
712 """Base test case class for all Shares Alt API tests."""
713
714 @classmethod
715 def resource_setup(cls):
716 cls.username = CONF.identity.alt_username
717 cls.password = CONF.identity.alt_password
718 cls.tenant_name = CONF.identity.alt_tenant_name
719 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
720 cls.os = clients.AltManager()
721 alt_share_network_id = CONF.share.alt_share_network_id
722 cls.os.shares_client.share_network_id = alt_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400723 cls.os.shares_v2_client.share_network_id = alt_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200724 super(BaseSharesAltTest, cls).resource_setup()
725
726
727class BaseSharesAdminTest(BaseSharesTest):
728 """Base test case class for all Shares Admin API tests."""
729
730 @classmethod
731 def resource_setup(cls):
Sam Wanb5047aa2015-10-08 05:37:43 -0400732 if hasattr(CONF.identity, 'admin_username'):
733 cls.username = CONF.identity.admin_username
734 cls.password = CONF.identity.admin_password
735 cls.tenant_name = CONF.identity.admin_tenant_name
736 else:
737 cls.username = CONF.auth.admin_username
738 cls.password = CONF.auth.admin_password
739 cls.tenant_name = CONF.auth.admin_tenant_name
Marc Koderer0abc93b2015-07-15 09:18:35 +0200740 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
741 cls.os = clients.AdminManager()
742 admin_share_network_id = CONF.share.admin_share_network_id
743 cls.os.shares_client.share_network_id = admin_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400744 cls.os.shares_v2_client.share_network_id = admin_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200745 super(BaseSharesAdminTest, cls).resource_setup()