blob: b9763fb9808946cf79ba0b3afcb66c46512bf2b2 [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
26from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020027from tempest_lib.common.utils import data_utils
28from tempest_lib import exceptions
29
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
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +020082
83
Marc Koderer0abc93b2015-07-15 09:18:35 +020084class BaseSharesTest(test.BaseTestCase):
85 """Base test case class for all Manila API tests."""
86
87 force_tenant_isolation = False
88 protocols = ["nfs", "cifs", "glusterfs", "hdfs"]
89
90 # Will be cleaned up in resource_cleanup
91 class_resources = []
92
93 # Will be cleaned up in tearDown method
94 method_resources = []
95
96 # Will be cleaned up in resource_cleanup
97 class_isolated_creds = []
98
99 # Will be cleaned up in tearDown method
100 method_isolated_creds = []
101
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200102 def skip_if_microversion_not_supported(self, microversion):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200103 if not utils.is_microversion_supported(microversion):
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200104 raise self.skipException(
105 "Microversion '%s' is not supported." % microversion)
106
Marc Koderer0abc93b2015-07-15 09:18:35 +0200107 @classmethod
108 def get_client_with_isolated_creds(cls,
109 name=None,
110 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400111 cleanup_in_class=False,
112 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200113 """Creates isolated creds.
114
115 :param name: name, will be used for naming ic and related stuff
116 :param type_of_creds: admin, alt or primary
117 :param cleanup_in_class: defines place where to delete
118 :returns: SharesClient -- shares client with isolated creds.
119 :returns: To client added dict attr 'creds' with
120 :returns: key elements 'tenant' and 'user'.
121 """
122 if name is None:
123 # Get name of test method
124 name = inspect.stack()[1][3]
125 if len(name) > 32:
126 name = name[0:32]
127
128 # Choose type of isolated creds
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200129 ic = dynamic_creds.DynamicCredentialProvider(
130 identity_version=CONF.identity.auth_version,
131 name=name,
Sam Wanc7b7f1f2015-11-25 00:22:28 -0500132 admin_role=CONF.identity.admin_role,
133 admin_creds=common_creds.get_configured_credentials(
134 'identity_admin'))
Marc Koderer0abc93b2015-07-15 09:18:35 +0200135 if "admin" in type_of_creds:
136 creds = ic.get_admin_creds()
137 elif "alt" in type_of_creds:
138 creds = ic.get_alt_creds()
139 else:
140 creds = ic.self.get_credentials(type_of_creds)
141 ic.type_of_creds = type_of_creds
142
143 # create client with isolated creds
144 os = clients.Manager(credentials=creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400145 if client_version == '1':
146 client = os.shares_client
147 elif client_version == '2':
148 client = os.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200149
150 # Set place where will be deleted isolated creds
151 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200152 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200153 "deleted": False,
154 }
155 if cleanup_in_class:
156 cls.class_isolated_creds.insert(0, ic_res)
157 else:
158 cls.method_isolated_creds.insert(0, ic_res)
159
160 # Provide share network
161 if CONF.share.multitenancy_enabled:
162 if not CONF.service_available.neutron:
163 raise cls.skipException("Neutron support is required")
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200164 nc = os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200165 share_network_id = cls.provide_share_network(client, nc, ic)
166 client.share_network_id = share_network_id
167 resource = {
168 "type": "share_network",
169 "id": client.share_network_id,
170 "client": client,
171 }
172 if cleanup_in_class:
173 cls.class_resources.insert(0, resource)
174 else:
175 cls.method_resources.insert(0, resource)
176 return client
177
178 @classmethod
179 def verify_nonempty(cls, *args):
180 if not all(args):
181 msg = "Missing API credentials in configuration."
182 raise cls.skipException(msg)
183
184 @classmethod
185 def resource_setup(cls):
186 if not (any(p in CONF.share.enable_protocols
187 for p in cls.protocols) and
188 CONF.service_available.manila):
189 skip_msg = "Manila is disabled"
190 raise cls.skipException(skip_msg)
191 super(BaseSharesTest, cls).resource_setup()
192 if not hasattr(cls, "os"):
193 cls.username = CONF.identity.username
194 cls.password = CONF.identity.password
195 cls.tenant_name = CONF.identity.tenant_name
196 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
197 cls.os = clients.Manager()
198 if CONF.share.multitenancy_enabled:
199 if not CONF.service_available.neutron:
200 raise cls.skipException("Neutron support is required")
201 sc = cls.os.shares_client
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200202 nc = cls.os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200203 share_network_id = cls.provide_share_network(sc, nc)
204 cls.os.shares_client.share_network_id = share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400205 cls.os.shares_v2_client.share_network_id = share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200206 cls.shares_client = cls.os.shares_client
Clinton Knighte5c8f092015-08-27 15:00:23 -0400207 cls.shares_v2_client = cls.os.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200208
209 def setUp(self):
210 super(BaseSharesTest, self).setUp()
211 self.addCleanup(self.clear_resources)
212 self.addCleanup(self.clear_isolated_creds)
213
214 @classmethod
215 def resource_cleanup(cls):
216 super(BaseSharesTest, cls).resource_cleanup()
217 cls.clear_resources(cls.class_resources)
218 cls.clear_isolated_creds(cls.class_isolated_creds)
219
220 @classmethod
221 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200222 def provide_share_network(cls, shares_client, networks_client,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200223 isolated_creds_client=None):
224 """Used for finding/creating share network for multitenant driver.
225
226 This method creates/gets entity share-network for one tenant. This
227 share-network will be used for creation of service vm.
228
229 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200230 :param networks_client: network client from same tenant as shares
231 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200232 If provided, then its networking will be used if needed.
233 If not provided, then common network will be used if needed.
234 :returns: str -- share network id for shares_client tenant
235 :returns: None -- if single-tenant driver used
236 """
237
238 sc = shares_client
239
240 if not CONF.share.multitenancy_enabled:
241 # Assumed usage of a single-tenant driver
242 share_network_id = None
243 elif sc.share_network_id:
244 # Share-network already exists, use it
245 share_network_id = sc.share_network_id
246 else:
247 net_id = subnet_id = share_network_id = None
248
249 if not isolated_creds_client:
250 # Search for networks, created in previous runs
251 search_word = "reusable"
252 sn_name = "autogenerated_by_tempest_%s" % search_word
253 service_net_name = "share-service"
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200254 networks = networks_client.list_networks()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200255 if "networks" in networks.keys():
256 networks = networks["networks"]
257 for network in networks:
258 if (service_net_name in network["name"] and
259 sc.tenant_id == network['tenant_id']):
260 net_id = network["id"]
261 if len(network["subnets"]) > 0:
262 subnet_id = network["subnets"][0]
263 break
264
265 # Create suitable network
266 if (net_id is None or subnet_id is None):
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200267 ic = dynamic_creds.DynamicCredentialProvider(
268 identity_version=CONF.identity.auth_version,
269 name=service_net_name,
270 admin_role=CONF.identity.admin_role,
Sam Wanc7b7f1f2015-11-25 00:22:28 -0500271 admin_creds=common_creds.get_configured_credentials(
272 'identity_admin'))
Marc Koderer0abc93b2015-07-15 09:18:35 +0200273 net_data = ic._create_network_resources(sc.tenant_id)
274 network, subnet, router = net_data
275 net_id = network["id"]
276 subnet_id = subnet["id"]
277
278 # Try get suitable share-network
279 share_networks = sc.list_share_networks_with_detail()
280 for sn in share_networks:
281 if (net_id == sn["neutron_net_id"] and
282 subnet_id == sn["neutron_subnet_id"] and
283 sn["name"] and search_word in sn["name"]):
284 share_network_id = sn["id"]
285 break
286 else:
287 sn_name = "autogenerated_by_tempest_for_isolated_creds"
288 # Use precreated network and subnet from isolated creds
289 net_id = isolated_creds_client.get_credentials(
290 isolated_creds_client.type_of_creds).network['id']
291 subnet_id = isolated_creds_client.get_credentials(
292 isolated_creds_client.type_of_creds).subnet['id']
293
294 # Create suitable share-network
295 if share_network_id is None:
296 sn_desc = "This share-network was created by tempest"
297 sn = sc.create_share_network(name=sn_name,
298 description=sn_desc,
299 neutron_net_id=net_id,
300 neutron_subnet_id=subnet_id)
301 share_network_id = sn["id"]
302
303 return share_network_id
304
305 @classmethod
306 def _create_share(cls, share_protocol=None, size=1, name=None,
307 snapshot_id=None, description=None, metadata=None,
308 share_network_id=None, share_type_id=None,
Andrew Kerrbf31e912015-07-29 10:39:38 -0400309 consistency_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400310 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300311 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200312 description = description or "Tempest's share"
313 share_network_id = share_network_id or client.share_network_id or None
314 metadata = metadata or {}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400315 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200316 'share_protocol': share_protocol,
317 'size': size,
318 'name': name,
319 'snapshot_id': snapshot_id,
320 'description': description,
321 'metadata': metadata,
322 'share_network_id': share_network_id,
323 'share_type_id': share_type_id,
324 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400325 })
Andrew Kerrbf31e912015-07-29 10:39:38 -0400326 if consistency_group_id:
327 kwargs['consistency_group_id'] = consistency_group_id
328
Marc Koderer0abc93b2015-07-15 09:18:35 +0200329 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400330 resource = {"type": "share", "id": share["id"], "client": client,
331 "consistency_group_id": consistency_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200332 cleanup_list = (cls.class_resources if cleanup_in_class else
333 cls.method_resources)
334 cleanup_list.insert(0, resource)
335 return share
336
337 @classmethod
Clinton Knighte5c8f092015-08-27 15:00:23 -0400338 def migrate_share(cls, share_id, dest_host, client=None, **kwargs):
339 client = client or cls.shares_v2_client
340 client.migrate_share(share_id, dest_host, **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300341 share = client.wait_for_migration_completed(share_id, dest_host)
342 return share
343
344 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200345 def create_share(cls, *args, **kwargs):
346 """Create one share and wait for available state. Retry if allowed."""
347 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
348 return result[0]
349
350 @classmethod
351 def create_shares(cls, share_data_list):
352 """Creates several shares in parallel with retries.
353
354 Use this method when you want to create more than one share at same
355 time. Especially if config option 'share.share_creation_retry_number'
356 has value more than zero (0).
357 All shares will be expected to have 'available' status with or without
358 recreation else error will be raised.
359
360 :param share_data_list: list -- list of dictionaries with 'args' and
361 'kwargs' for '_create_share' method of this base class.
362 example of data:
363 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
364 :returns: list -- list of shares created using provided data.
365 """
366
367 data = [copy.deepcopy(d) for d in share_data_list]
368 for d in data:
369 if not isinstance(d, dict):
370 raise exceptions.TempestException(
371 "Expected 'dict', got '%s'" % type(d))
372 if "args" not in d:
373 d["args"] = []
374 if "kwargs" not in d:
375 d["kwargs"] = {}
376 if len(d) > 2:
377 raise exceptions.TempestException(
378 "Expected only 'args' and 'kwargs' keys. "
379 "Provided %s" % list(d))
380 d["kwargs"]["client"] = d["kwargs"].get(
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300381 "client", cls.shares_v2_client)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200382 d["share"] = cls._create_share(*d["args"], **d["kwargs"])
383 d["cnt"] = 0
384 d["available"] = False
385
386 while not all(d["available"] for d in data):
387 for d in data:
388 if d["available"]:
389 continue
390 try:
391 d["kwargs"]["client"].wait_for_share_status(
392 d["share"]["id"], "available")
393 d["available"] = True
394 except (share_exceptions.ShareBuildErrorException,
395 exceptions.TimeoutException) as e:
396 if CONF.share.share_creation_retry_number > d["cnt"]:
397 d["cnt"] += 1
398 msg = ("Share '%s' failed to be built. "
399 "Trying create another." % d["share"]["id"])
400 LOG.error(msg)
401 LOG.error(e)
402 d["share"] = cls._create_share(
403 *d["args"], **d["kwargs"])
404 else:
405 raise e
406
407 return [d["share"] for d in data]
408
409 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400410 def create_consistency_group(cls, client=None, cleanup_in_class=True,
411 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400412 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400413 kwargs['share_network_id'] = (share_network_id or
414 client.share_network_id or None)
415 consistency_group = client.create_consistency_group(**kwargs)
416 resource = {
417 "type": "consistency_group",
418 "id": consistency_group["id"],
419 "client": client}
420 if cleanup_in_class:
421 cls.class_resources.insert(0, resource)
422 else:
423 cls.method_resources.insert(0, resource)
424
425 if kwargs.get('source_cgsnapshot_id'):
426 new_cg_shares = client.list_shares(
427 detailed=True,
428 params={'consistency_group_id': consistency_group['id']})
429
430 for share in new_cg_shares:
431 resource = {"type": "share",
432 "id": share["id"],
433 "client": client,
434 "consistency_group_id": share.get(
435 'consistency_group_id')}
436 if cleanup_in_class:
437 cls.class_resources.insert(0, resource)
438 else:
439 cls.method_resources.insert(0, resource)
440
441 client.wait_for_consistency_group_status(consistency_group['id'],
442 'available')
443 return consistency_group
444
445 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200446 def create_snapshot_wait_for_active(cls, share_id, name=None,
447 description=None, force=False,
448 client=None, cleanup_in_class=True):
449 if client is None:
450 client = cls.shares_client
451 if description is None:
452 description = "Tempest's snapshot"
453 snapshot = client.create_snapshot(share_id, name, description, force)
454 resource = {
455 "type": "snapshot",
456 "id": snapshot["id"],
457 "client": client,
458 }
459 if cleanup_in_class:
460 cls.class_resources.insert(0, resource)
461 else:
462 cls.method_resources.insert(0, resource)
463 client.wait_for_snapshot_status(snapshot["id"], "available")
464 return snapshot
465
466 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400467 def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
468 name=None, description=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400469 client=None, cleanup_in_class=True,
470 **kwargs):
471 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400472 if description is None:
473 description = "Tempest's cgsnapshot"
Clinton Knighte5c8f092015-08-27 15:00:23 -0400474 cgsnapshot = client.create_cgsnapshot(consistency_group_id,
475 name=name,
476 description=description,
477 **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400478 resource = {
479 "type": "cgsnapshot",
480 "id": cgsnapshot["id"],
481 "client": client,
482 }
483 if cleanup_in_class:
484 cls.class_resources.insert(0, resource)
485 else:
486 cls.method_resources.insert(0, resource)
487 client.wait_for_cgsnapshot_status(cgsnapshot["id"], "available")
488 return cgsnapshot
489
490 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200491 def create_share_network(cls, client=None,
492 cleanup_in_class=False, **kwargs):
493 if client is None:
494 client = cls.shares_client
495 share_network = client.create_share_network(**kwargs)
496 resource = {
497 "type": "share_network",
498 "id": share_network["id"],
499 "client": client,
500 }
501 if cleanup_in_class:
502 cls.class_resources.insert(0, resource)
503 else:
504 cls.method_resources.insert(0, resource)
505 return share_network
506
507 @classmethod
508 def create_security_service(cls, ss_type="ldap", client=None,
509 cleanup_in_class=False, **kwargs):
510 if client is None:
511 client = cls.shares_client
512 security_service = client.create_security_service(ss_type, **kwargs)
513 resource = {
514 "type": "security_service",
515 "id": security_service["id"],
516 "client": client,
517 }
518 if cleanup_in_class:
519 cls.class_resources.insert(0, resource)
520 else:
521 cls.method_resources.insert(0, resource)
522 return security_service
523
524 @classmethod
525 def create_share_type(cls, name, is_public=True, client=None,
526 cleanup_in_class=True, **kwargs):
527 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200528 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200529 share_type = client.create_share_type(name, is_public, **kwargs)
530 resource = {
531 "type": "share_type",
532 "id": share_type["share_type"]["id"],
533 "client": client,
534 }
535 if cleanup_in_class:
536 cls.class_resources.insert(0, resource)
537 else:
538 cls.method_resources.insert(0, resource)
539 return share_type
540
541 @staticmethod
542 def add_required_extra_specs_to_dict(extra_specs=None):
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300543 dhss = six.text_type(CONF.share.multitenancy_enabled)
544 snapshot_support = six.text_type(
545 CONF.share.capability_snapshot_support)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200546 required = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300547 "driver_handles_share_servers": dhss,
548 "snapshot_support": snapshot_support,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200549 }
550 if extra_specs:
551 required.update(extra_specs)
552 return required
553
554 @classmethod
555 def clear_isolated_creds(cls, creds=None):
556 if creds is None:
557 creds = cls.method_isolated_creds
558 for ic in creds:
559 if "deleted" not in ic.keys():
560 ic["deleted"] = False
561 if not ic["deleted"]:
562 with handle_cleanup_exceptions():
563 ic["method"]()
564 ic["deleted"] = True
565
566 @classmethod
567 def clear_resources(cls, resources=None):
568 """Deletes resources, that were created in test suites.
569
570 This method tries to remove resources from resource list,
571 if it is not found, assumed it was deleted in test itself.
572 It is expected, that all resources were added as LIFO
573 due to restriction of deletion resources, that is in the chain.
574
575 :param resources: dict with keys 'type','id','client' and 'deleted'
576 """
577
578 if resources is None:
579 resources = cls.method_resources
580 for res in resources:
581 if "deleted" not in res.keys():
582 res["deleted"] = False
583 if "client" not in res.keys():
584 res["client"] = cls.shares_client
585 if not(res["deleted"]):
586 res_id = res['id']
587 client = res["client"]
588 with handle_cleanup_exceptions():
589 if res["type"] is "share":
Andrew Kerrbf31e912015-07-29 10:39:38 -0400590 cg_id = res.get('consistency_group_id')
591 if cg_id:
592 params = {'consistency_group_id': cg_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400593 client.delete_share(res_id, params=params)
594 else:
595 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200596 client.wait_for_resource_deletion(share_id=res_id)
597 elif res["type"] is "snapshot":
598 client.delete_snapshot(res_id)
599 client.wait_for_resource_deletion(snapshot_id=res_id)
600 elif res["type"] is "share_network":
601 client.delete_share_network(res_id)
602 client.wait_for_resource_deletion(sn_id=res_id)
603 elif res["type"] is "security_service":
604 client.delete_security_service(res_id)
605 client.wait_for_resource_deletion(ss_id=res_id)
606 elif res["type"] is "share_type":
607 client.delete_share_type(res_id)
608 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400609 elif res["type"] is "consistency_group":
610 client.delete_consistency_group(res_id)
611 client.wait_for_resource_deletion(cg_id=res_id)
612 elif res["type"] is "cgsnapshot":
613 client.delete_cgsnapshot(res_id)
614 client.wait_for_resource_deletion(cgsnapshot_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200615 else:
huayue97bacbf2016-01-04 09:57:39 +0800616 LOG.warning("Provided unsupported resource type for "
617 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200618 res["deleted"] = True
619
620 @classmethod
621 def generate_share_network_data(self):
622 data = {
623 "name": data_utils.rand_name("sn-name"),
624 "description": data_utils.rand_name("sn-desc"),
625 "neutron_net_id": data_utils.rand_name("net-id"),
626 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
627 }
628 return data
629
630 @classmethod
631 def generate_security_service_data(self):
632 data = {
633 "name": data_utils.rand_name("ss-name"),
634 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200635 "dns_ip": utils.rand_ip(),
636 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200637 "domain": data_utils.rand_name("ss-domain"),
638 "user": data_utils.rand_name("ss-user"),
639 "password": data_utils.rand_name("ss-password"),
640 }
641 return data
642
643 # Useful assertions
644 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
645 """Assert two dicts are equivalent.
646
647 This is a 'deep' match in the sense that it handles nested
648 dictionaries appropriately.
649
650 NOTE:
651
652 If you don't care (or don't know) a given value, you can specify
653 the string DONTCARE as the value. This will cause that dict-item
654 to be skipped.
655
656 """
657 def raise_assertion(msg):
658 d1str = str(d1)
659 d2str = str(d2)
660 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
661 'd2: %(d2str)s' %
662 {"msg": msg, "d1str": d1str, "d2str": d2str})
663 raise AssertionError(base_msg)
664
665 d1keys = set(d1.keys())
666 d2keys = set(d2.keys())
667 if d1keys != d2keys:
668 d1only = d1keys - d2keys
669 d2only = d2keys - d1keys
670 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
671 'Keys in d2 and not d1: %(d2only)s' %
672 {"d1only": d1only, "d2only": d2only})
673
674 for key in d1keys:
675 d1value = d1[key]
676 d2value = d2[key]
677 try:
678 error = abs(float(d1value) - float(d2value))
679 within_tolerance = error <= tolerance
680 except (ValueError, TypeError):
681 # If both values aren't convertable to float, just ignore
682 # ValueError if arg is a str, TypeError if it's something else
683 # (like None)
684 within_tolerance = False
685
686 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
687 self.assertDictMatch(d1value, d2value)
688 elif 'DONTCARE' in (d1value, d2value):
689 continue
690 elif approx_equal and within_tolerance:
691 continue
692 elif d1value != d2value:
693 raise_assertion("d1['%(key)s']=%(d1value)s != "
694 "d2['%(key)s']=%(d2value)s" %
695 {
696 "key": key,
697 "d1value": d1value,
698 "d2value": d2value
699 })
700
701
702class BaseSharesAltTest(BaseSharesTest):
703 """Base test case class for all Shares Alt API tests."""
704
705 @classmethod
706 def resource_setup(cls):
707 cls.username = CONF.identity.alt_username
708 cls.password = CONF.identity.alt_password
709 cls.tenant_name = CONF.identity.alt_tenant_name
710 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
711 cls.os = clients.AltManager()
712 alt_share_network_id = CONF.share.alt_share_network_id
713 cls.os.shares_client.share_network_id = alt_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400714 cls.os.shares_v2_client.share_network_id = alt_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200715 super(BaseSharesAltTest, cls).resource_setup()
716
717
718class BaseSharesAdminTest(BaseSharesTest):
719 """Base test case class for all Shares Admin API tests."""
720
721 @classmethod
722 def resource_setup(cls):
Sam Wanb5047aa2015-10-08 05:37:43 -0400723 if hasattr(CONF.identity, 'admin_username'):
724 cls.username = CONF.identity.admin_username
725 cls.password = CONF.identity.admin_password
726 cls.tenant_name = CONF.identity.admin_tenant_name
727 else:
728 cls.username = CONF.auth.admin_username
729 cls.password = CONF.auth.admin_password
730 cls.tenant_name = CONF.auth.admin_tenant_name
Marc Koderer0abc93b2015-07-15 09:18:35 +0200731 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
732 cls.os = clients.AdminManager()
733 admin_share_network_id = CONF.share.admin_share_network_id
734 cls.os.shares_client.share_network_id = admin_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400735 cls.os.shares_v2_client.share_network_id = admin_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200736 super(BaseSharesAdminTest, cls).resource_setup()