blob: 3723d361376ea2e36f40c9ed972bbd91cce01052 [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)
Valeriy Ponomaryov1801c0c2015-12-01 17:30:16 +0200341 share = client.wait_for_migration_completed(
342 share_id, dest_host, version=kwargs.get('version'))
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300343 return share
344
345 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200346 def create_share(cls, *args, **kwargs):
347 """Create one share and wait for available state. Retry if allowed."""
348 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
349 return result[0]
350
351 @classmethod
352 def create_shares(cls, share_data_list):
353 """Creates several shares in parallel with retries.
354
355 Use this method when you want to create more than one share at same
356 time. Especially if config option 'share.share_creation_retry_number'
357 has value more than zero (0).
358 All shares will be expected to have 'available' status with or without
359 recreation else error will be raised.
360
361 :param share_data_list: list -- list of dictionaries with 'args' and
362 'kwargs' for '_create_share' method of this base class.
363 example of data:
364 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
365 :returns: list -- list of shares created using provided data.
366 """
367
368 data = [copy.deepcopy(d) for d in share_data_list]
369 for d in data:
370 if not isinstance(d, dict):
371 raise exceptions.TempestException(
372 "Expected 'dict', got '%s'" % type(d))
373 if "args" not in d:
374 d["args"] = []
375 if "kwargs" not in d:
376 d["kwargs"] = {}
377 if len(d) > 2:
378 raise exceptions.TempestException(
379 "Expected only 'args' and 'kwargs' keys. "
380 "Provided %s" % list(d))
381 d["kwargs"]["client"] = d["kwargs"].get(
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300382 "client", cls.shares_v2_client)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200383 d["share"] = cls._create_share(*d["args"], **d["kwargs"])
384 d["cnt"] = 0
385 d["available"] = False
386
387 while not all(d["available"] for d in data):
388 for d in data:
389 if d["available"]:
390 continue
391 try:
392 d["kwargs"]["client"].wait_for_share_status(
393 d["share"]["id"], "available")
394 d["available"] = True
395 except (share_exceptions.ShareBuildErrorException,
396 exceptions.TimeoutException) as e:
397 if CONF.share.share_creation_retry_number > d["cnt"]:
398 d["cnt"] += 1
399 msg = ("Share '%s' failed to be built. "
400 "Trying create another." % d["share"]["id"])
401 LOG.error(msg)
402 LOG.error(e)
403 d["share"] = cls._create_share(
404 *d["args"], **d["kwargs"])
405 else:
406 raise e
407
408 return [d["share"] for d in data]
409
410 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400411 def create_consistency_group(cls, client=None, cleanup_in_class=True,
412 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400413 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400414 kwargs['share_network_id'] = (share_network_id or
415 client.share_network_id or None)
416 consistency_group = client.create_consistency_group(**kwargs)
417 resource = {
418 "type": "consistency_group",
419 "id": consistency_group["id"],
420 "client": client}
421 if cleanup_in_class:
422 cls.class_resources.insert(0, resource)
423 else:
424 cls.method_resources.insert(0, resource)
425
426 if kwargs.get('source_cgsnapshot_id'):
427 new_cg_shares = client.list_shares(
428 detailed=True,
429 params={'consistency_group_id': consistency_group['id']})
430
431 for share in new_cg_shares:
432 resource = {"type": "share",
433 "id": share["id"],
434 "client": client,
435 "consistency_group_id": share.get(
436 'consistency_group_id')}
437 if cleanup_in_class:
438 cls.class_resources.insert(0, resource)
439 else:
440 cls.method_resources.insert(0, resource)
441
442 client.wait_for_consistency_group_status(consistency_group['id'],
443 'available')
444 return consistency_group
445
446 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200447 def create_snapshot_wait_for_active(cls, share_id, name=None,
448 description=None, force=False,
449 client=None, cleanup_in_class=True):
450 if client is None:
451 client = cls.shares_client
452 if description is None:
453 description = "Tempest's snapshot"
454 snapshot = client.create_snapshot(share_id, name, description, force)
455 resource = {
456 "type": "snapshot",
457 "id": snapshot["id"],
458 "client": client,
459 }
460 if cleanup_in_class:
461 cls.class_resources.insert(0, resource)
462 else:
463 cls.method_resources.insert(0, resource)
464 client.wait_for_snapshot_status(snapshot["id"], "available")
465 return snapshot
466
467 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400468 def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
469 name=None, description=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400470 client=None, cleanup_in_class=True,
471 **kwargs):
472 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400473 if description is None:
474 description = "Tempest's cgsnapshot"
Clinton Knighte5c8f092015-08-27 15:00:23 -0400475 cgsnapshot = client.create_cgsnapshot(consistency_group_id,
476 name=name,
477 description=description,
478 **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400479 resource = {
480 "type": "cgsnapshot",
481 "id": cgsnapshot["id"],
482 "client": client,
483 }
484 if cleanup_in_class:
485 cls.class_resources.insert(0, resource)
486 else:
487 cls.method_resources.insert(0, resource)
488 client.wait_for_cgsnapshot_status(cgsnapshot["id"], "available")
489 return cgsnapshot
490
491 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200492 def create_share_network(cls, client=None,
493 cleanup_in_class=False, **kwargs):
494 if client is None:
495 client = cls.shares_client
496 share_network = client.create_share_network(**kwargs)
497 resource = {
498 "type": "share_network",
499 "id": share_network["id"],
500 "client": client,
501 }
502 if cleanup_in_class:
503 cls.class_resources.insert(0, resource)
504 else:
505 cls.method_resources.insert(0, resource)
506 return share_network
507
508 @classmethod
509 def create_security_service(cls, ss_type="ldap", client=None,
510 cleanup_in_class=False, **kwargs):
511 if client is None:
512 client = cls.shares_client
513 security_service = client.create_security_service(ss_type, **kwargs)
514 resource = {
515 "type": "security_service",
516 "id": security_service["id"],
517 "client": client,
518 }
519 if cleanup_in_class:
520 cls.class_resources.insert(0, resource)
521 else:
522 cls.method_resources.insert(0, resource)
523 return security_service
524
525 @classmethod
526 def create_share_type(cls, name, is_public=True, client=None,
527 cleanup_in_class=True, **kwargs):
528 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200529 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200530 share_type = client.create_share_type(name, is_public, **kwargs)
531 resource = {
532 "type": "share_type",
533 "id": share_type["share_type"]["id"],
534 "client": client,
535 }
536 if cleanup_in_class:
537 cls.class_resources.insert(0, resource)
538 else:
539 cls.method_resources.insert(0, resource)
540 return share_type
541
542 @staticmethod
543 def add_required_extra_specs_to_dict(extra_specs=None):
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300544 dhss = six.text_type(CONF.share.multitenancy_enabled)
545 snapshot_support = six.text_type(
546 CONF.share.capability_snapshot_support)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200547 required = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300548 "driver_handles_share_servers": dhss,
549 "snapshot_support": snapshot_support,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200550 }
551 if extra_specs:
552 required.update(extra_specs)
553 return required
554
555 @classmethod
556 def clear_isolated_creds(cls, creds=None):
557 if creds is None:
558 creds = cls.method_isolated_creds
559 for ic in creds:
560 if "deleted" not in ic.keys():
561 ic["deleted"] = False
562 if not ic["deleted"]:
563 with handle_cleanup_exceptions():
564 ic["method"]()
565 ic["deleted"] = True
566
567 @classmethod
568 def clear_resources(cls, resources=None):
569 """Deletes resources, that were created in test suites.
570
571 This method tries to remove resources from resource list,
572 if it is not found, assumed it was deleted in test itself.
573 It is expected, that all resources were added as LIFO
574 due to restriction of deletion resources, that is in the chain.
575
576 :param resources: dict with keys 'type','id','client' and 'deleted'
577 """
578
579 if resources is None:
580 resources = cls.method_resources
581 for res in resources:
582 if "deleted" not in res.keys():
583 res["deleted"] = False
584 if "client" not in res.keys():
585 res["client"] = cls.shares_client
586 if not(res["deleted"]):
587 res_id = res['id']
588 client = res["client"]
589 with handle_cleanup_exceptions():
590 if res["type"] is "share":
Andrew Kerrbf31e912015-07-29 10:39:38 -0400591 cg_id = res.get('consistency_group_id')
592 if cg_id:
593 params = {'consistency_group_id': cg_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400594 client.delete_share(res_id, params=params)
595 else:
596 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200597 client.wait_for_resource_deletion(share_id=res_id)
598 elif res["type"] is "snapshot":
599 client.delete_snapshot(res_id)
600 client.wait_for_resource_deletion(snapshot_id=res_id)
601 elif res["type"] is "share_network":
602 client.delete_share_network(res_id)
603 client.wait_for_resource_deletion(sn_id=res_id)
604 elif res["type"] is "security_service":
605 client.delete_security_service(res_id)
606 client.wait_for_resource_deletion(ss_id=res_id)
607 elif res["type"] is "share_type":
608 client.delete_share_type(res_id)
609 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400610 elif res["type"] is "consistency_group":
611 client.delete_consistency_group(res_id)
612 client.wait_for_resource_deletion(cg_id=res_id)
613 elif res["type"] is "cgsnapshot":
614 client.delete_cgsnapshot(res_id)
615 client.wait_for_resource_deletion(cgsnapshot_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200616 else:
huayue97bacbf2016-01-04 09:57:39 +0800617 LOG.warning("Provided unsupported resource type for "
618 "cleanup '%s'. Skipping." % res["type"])
Marc Koderer0abc93b2015-07-15 09:18:35 +0200619 res["deleted"] = True
620
621 @classmethod
622 def generate_share_network_data(self):
623 data = {
624 "name": data_utils.rand_name("sn-name"),
625 "description": data_utils.rand_name("sn-desc"),
626 "neutron_net_id": data_utils.rand_name("net-id"),
627 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
628 }
629 return data
630
631 @classmethod
632 def generate_security_service_data(self):
633 data = {
634 "name": data_utils.rand_name("ss-name"),
635 "description": data_utils.rand_name("ss-desc"),
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200636 "dns_ip": utils.rand_ip(),
637 "server": utils.rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200638 "domain": data_utils.rand_name("ss-domain"),
639 "user": data_utils.rand_name("ss-user"),
640 "password": data_utils.rand_name("ss-password"),
641 }
642 return data
643
644 # Useful assertions
645 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
646 """Assert two dicts are equivalent.
647
648 This is a 'deep' match in the sense that it handles nested
649 dictionaries appropriately.
650
651 NOTE:
652
653 If you don't care (or don't know) a given value, you can specify
654 the string DONTCARE as the value. This will cause that dict-item
655 to be skipped.
656
657 """
658 def raise_assertion(msg):
659 d1str = str(d1)
660 d2str = str(d2)
661 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
662 'd2: %(d2str)s' %
663 {"msg": msg, "d1str": d1str, "d2str": d2str})
664 raise AssertionError(base_msg)
665
666 d1keys = set(d1.keys())
667 d2keys = set(d2.keys())
668 if d1keys != d2keys:
669 d1only = d1keys - d2keys
670 d2only = d2keys - d1keys
671 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
672 'Keys in d2 and not d1: %(d2only)s' %
673 {"d1only": d1only, "d2only": d2only})
674
675 for key in d1keys:
676 d1value = d1[key]
677 d2value = d2[key]
678 try:
679 error = abs(float(d1value) - float(d2value))
680 within_tolerance = error <= tolerance
681 except (ValueError, TypeError):
682 # If both values aren't convertable to float, just ignore
683 # ValueError if arg is a str, TypeError if it's something else
684 # (like None)
685 within_tolerance = False
686
687 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
688 self.assertDictMatch(d1value, d2value)
689 elif 'DONTCARE' in (d1value, d2value):
690 continue
691 elif approx_equal and within_tolerance:
692 continue
693 elif d1value != d2value:
694 raise_assertion("d1['%(key)s']=%(d1value)s != "
695 "d2['%(key)s']=%(d2value)s" %
696 {
697 "key": key,
698 "d1value": d1value,
699 "d2value": d2value
700 })
701
702
703class BaseSharesAltTest(BaseSharesTest):
704 """Base test case class for all Shares Alt API tests."""
705
706 @classmethod
707 def resource_setup(cls):
708 cls.username = CONF.identity.alt_username
709 cls.password = CONF.identity.alt_password
710 cls.tenant_name = CONF.identity.alt_tenant_name
711 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
712 cls.os = clients.AltManager()
713 alt_share_network_id = CONF.share.alt_share_network_id
714 cls.os.shares_client.share_network_id = alt_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400715 cls.os.shares_v2_client.share_network_id = alt_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200716 super(BaseSharesAltTest, cls).resource_setup()
717
718
719class BaseSharesAdminTest(BaseSharesTest):
720 """Base test case class for all Shares Admin API tests."""
721
722 @classmethod
723 def resource_setup(cls):
Sam Wanb5047aa2015-10-08 05:37:43 -0400724 if hasattr(CONF.identity, 'admin_username'):
725 cls.username = CONF.identity.admin_username
726 cls.password = CONF.identity.admin_password
727 cls.tenant_name = CONF.identity.admin_tenant_name
728 else:
729 cls.username = CONF.auth.admin_username
730 cls.password = CONF.auth.admin_password
731 cls.tenant_name = CONF.auth.admin_tenant_name
Marc Koderer0abc93b2015-07-15 09:18:35 +0200732 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
733 cls.os = clients.AdminManager()
734 admin_share_network_id = CONF.share.admin_share_network_id
735 cls.os.shares_client.share_network_id = admin_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400736 cls.os.shares_v2_client.share_network_id = admin_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200737 super(BaseSharesAdminTest, cls).resource_setup()