blob: be56719b62124692c096d5ddbdb3d5eeb7381f56 [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
Andrew Kerr40df1d72015-09-28 13:22:33 -040018import random
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
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +020025from tempest.common import dynamic_creds
26from tempest import config
27from tempest import test
Marc Koderer0abc93b2015-07-15 09:18:35 +020028from tempest_lib.common.utils import data_utils
29from tempest_lib import exceptions
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +020030import testtools
Marc Koderer0abc93b2015-07-15 09:18:35 +020031
32from manila_tempest_tests import clients_share as clients
33from manila_tempest_tests import share_exceptions
34
35CONF = config.CONF
36LOG = log.getLogger(__name__)
37
38
Andrew Kerr40df1d72015-09-28 13:22:33 -040039def rand_ip():
40 """This uses the TEST-NET-3 range of reserved IP addresses.
41
42 Using this range, which are reserved solely for use in
43 documentation and example source code, should avoid any potential
44 conflicts in real-world testing.
45 """
46 TEST_NET_3 = '203.0.113.'
47 final_octet = six.text_type(random.randint(0, 255))
48 return TEST_NET_3 + final_octet
49
50
Marc Koderer0abc93b2015-07-15 09:18:35 +020051class handle_cleanup_exceptions(object):
52 """Handle exceptions raised with cleanup operations.
53
54 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
55 are raised.
56 Suppress all other exceptions only in case config opt
57 'suppress_errors_in_cleanup' in config group 'share' is True.
58 """
59
60 def __enter__(self):
61 return self
62
63 def __exit__(self, exc_type, exc_value, exc_traceback):
64 if not (isinstance(exc_value,
65 (exceptions.NotFound, exceptions.Forbidden)) or
66 CONF.share.suppress_errors_in_cleanup):
67 return False # Do not suppress error if any
68 if exc_traceback:
69 LOG.error("Suppressed cleanup error in Manila: "
70 "\n%s" % traceback.format_exc())
71 return True # Suppress error if any
72
73
74def network_synchronized(f):
75
76 def wrapped_func(self, *args, **kwargs):
77 with_isolated_creds = True if len(args) > 2 else False
78 no_lock_required = kwargs.get(
79 "isolated_creds_client", with_isolated_creds)
80 if no_lock_required:
81 # Usage of not reusable network. No need in lock.
82 return f(self, *args, **kwargs)
83
84 # Use lock assuming reusage of common network.
85 @lockutils.synchronized("manila_network_lock", external=True)
86 def source_func(self, *args, **kwargs):
87 return f(self, *args, **kwargs)
88
89 return source_func(self, *args, **kwargs)
90
91 return wrapped_func
92
93
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +020094def is_microversion_supported(microversion):
95 if (float(microversion) > float(CONF.share.max_api_microversion) or
96 float(microversion) < float(CONF.share.min_api_microversion)):
97 return False
98 return True
99
100
101def skip_if_microversion_not_supported(microversion):
102 """Decorator for tests that are microversion-specific."""
103 if not is_microversion_supported(microversion):
104 reason = ("Skipped. Test requires microversion '%s'." % microversion)
105 return testtools.skip(reason)
106 return lambda f: f
107
108
Marc Koderer0abc93b2015-07-15 09:18:35 +0200109class BaseSharesTest(test.BaseTestCase):
110 """Base test case class for all Manila API tests."""
111
112 force_tenant_isolation = False
113 protocols = ["nfs", "cifs", "glusterfs", "hdfs"]
114
115 # Will be cleaned up in resource_cleanup
116 class_resources = []
117
118 # Will be cleaned up in tearDown method
119 method_resources = []
120
121 # Will be cleaned up in resource_cleanup
122 class_isolated_creds = []
123
124 # Will be cleaned up in tearDown method
125 method_isolated_creds = []
126
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200127 def skip_if_microversion_not_supported(self, microversion):
128 if not is_microversion_supported(microversion):
129 raise self.skipException(
130 "Microversion '%s' is not supported." % microversion)
131
Marc Koderer0abc93b2015-07-15 09:18:35 +0200132 @classmethod
133 def get_client_with_isolated_creds(cls,
134 name=None,
135 type_of_creds="admin",
Clinton Knighte5c8f092015-08-27 15:00:23 -0400136 cleanup_in_class=False,
137 client_version='1'):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200138 """Creates isolated creds.
139
140 :param name: name, will be used for naming ic and related stuff
141 :param type_of_creds: admin, alt or primary
142 :param cleanup_in_class: defines place where to delete
143 :returns: SharesClient -- shares client with isolated creds.
144 :returns: To client added dict attr 'creds' with
145 :returns: key elements 'tenant' and 'user'.
146 """
147 if name is None:
148 # Get name of test method
149 name = inspect.stack()[1][3]
150 if len(name) > 32:
151 name = name[0:32]
152
153 # Choose type of isolated creds
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200154 ic = dynamic_creds.DynamicCredentialProvider(
155 identity_version=CONF.identity.auth_version,
156 name=name,
Sam Wanc7b7f1f2015-11-25 00:22:28 -0500157 admin_role=CONF.identity.admin_role,
158 admin_creds=common_creds.get_configured_credentials(
159 'identity_admin'))
Marc Koderer0abc93b2015-07-15 09:18:35 +0200160 if "admin" in type_of_creds:
161 creds = ic.get_admin_creds()
162 elif "alt" in type_of_creds:
163 creds = ic.get_alt_creds()
164 else:
165 creds = ic.self.get_credentials(type_of_creds)
166 ic.type_of_creds = type_of_creds
167
168 # create client with isolated creds
169 os = clients.Manager(credentials=creds)
Clinton Knighte5c8f092015-08-27 15:00:23 -0400170 if client_version == '1':
171 client = os.shares_client
172 elif client_version == '2':
173 client = os.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200174
175 # Set place where will be deleted isolated creds
176 ic_res = {
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200177 "method": ic.clear_creds,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200178 "deleted": False,
179 }
180 if cleanup_in_class:
181 cls.class_isolated_creds.insert(0, ic_res)
182 else:
183 cls.method_isolated_creds.insert(0, ic_res)
184
185 # Provide share network
186 if CONF.share.multitenancy_enabled:
187 if not CONF.service_available.neutron:
188 raise cls.skipException("Neutron support is required")
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200189 nc = os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200190 share_network_id = cls.provide_share_network(client, nc, ic)
191 client.share_network_id = share_network_id
192 resource = {
193 "type": "share_network",
194 "id": client.share_network_id,
195 "client": client,
196 }
197 if cleanup_in_class:
198 cls.class_resources.insert(0, resource)
199 else:
200 cls.method_resources.insert(0, resource)
201 return client
202
203 @classmethod
204 def verify_nonempty(cls, *args):
205 if not all(args):
206 msg = "Missing API credentials in configuration."
207 raise cls.skipException(msg)
208
209 @classmethod
210 def resource_setup(cls):
211 if not (any(p in CONF.share.enable_protocols
212 for p in cls.protocols) and
213 CONF.service_available.manila):
214 skip_msg = "Manila is disabled"
215 raise cls.skipException(skip_msg)
216 super(BaseSharesTest, cls).resource_setup()
217 if not hasattr(cls, "os"):
218 cls.username = CONF.identity.username
219 cls.password = CONF.identity.password
220 cls.tenant_name = CONF.identity.tenant_name
221 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
222 cls.os = clients.Manager()
223 if CONF.share.multitenancy_enabled:
224 if not CONF.service_available.neutron:
225 raise cls.skipException("Neutron support is required")
226 sc = cls.os.shares_client
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200227 nc = cls.os.networks_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200228 share_network_id = cls.provide_share_network(sc, nc)
229 cls.os.shares_client.share_network_id = share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400230 cls.os.shares_v2_client.share_network_id = share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200231 cls.shares_client = cls.os.shares_client
Clinton Knighte5c8f092015-08-27 15:00:23 -0400232 cls.shares_v2_client = cls.os.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200233
234 def setUp(self):
235 super(BaseSharesTest, self).setUp()
236 self.addCleanup(self.clear_resources)
237 self.addCleanup(self.clear_isolated_creds)
238
239 @classmethod
240 def resource_cleanup(cls):
241 super(BaseSharesTest, cls).resource_cleanup()
242 cls.clear_resources(cls.class_resources)
243 cls.clear_isolated_creds(cls.class_isolated_creds)
244
245 @classmethod
246 @network_synchronized
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200247 def provide_share_network(cls, shares_client, networks_client,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200248 isolated_creds_client=None):
249 """Used for finding/creating share network for multitenant driver.
250
251 This method creates/gets entity share-network for one tenant. This
252 share-network will be used for creation of service vm.
253
254 :param shares_client: shares client, which requires share-network
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200255 :param networks_client: network client from same tenant as shares
256 :param isolated_creds_client: DynamicCredentialProvider instance
Marc Koderer0abc93b2015-07-15 09:18:35 +0200257 If provided, then its networking will be used if needed.
258 If not provided, then common network will be used if needed.
259 :returns: str -- share network id for shares_client tenant
260 :returns: None -- if single-tenant driver used
261 """
262
263 sc = shares_client
264
265 if not CONF.share.multitenancy_enabled:
266 # Assumed usage of a single-tenant driver
267 share_network_id = None
268 elif sc.share_network_id:
269 # Share-network already exists, use it
270 share_network_id = sc.share_network_id
271 else:
272 net_id = subnet_id = share_network_id = None
273
274 if not isolated_creds_client:
275 # Search for networks, created in previous runs
276 search_word = "reusable"
277 sn_name = "autogenerated_by_tempest_%s" % search_word
278 service_net_name = "share-service"
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200279 networks = networks_client.list_networks()
Marc Koderer0abc93b2015-07-15 09:18:35 +0200280 if "networks" in networks.keys():
281 networks = networks["networks"]
282 for network in networks:
283 if (service_net_name in network["name"] and
284 sc.tenant_id == network['tenant_id']):
285 net_id = network["id"]
286 if len(network["subnets"]) > 0:
287 subnet_id = network["subnets"][0]
288 break
289
290 # Create suitable network
291 if (net_id is None or subnet_id is None):
Valeriy Ponomaryov48a2bd72015-11-05 13:22:44 +0200292 ic = dynamic_creds.DynamicCredentialProvider(
293 identity_version=CONF.identity.auth_version,
294 name=service_net_name,
295 admin_role=CONF.identity.admin_role,
Sam Wanc7b7f1f2015-11-25 00:22:28 -0500296 admin_creds=common_creds.get_configured_credentials(
297 'identity_admin'))
Marc Koderer0abc93b2015-07-15 09:18:35 +0200298 net_data = ic._create_network_resources(sc.tenant_id)
299 network, subnet, router = net_data
300 net_id = network["id"]
301 subnet_id = subnet["id"]
302
303 # Try get suitable share-network
304 share_networks = sc.list_share_networks_with_detail()
305 for sn in share_networks:
306 if (net_id == sn["neutron_net_id"] and
307 subnet_id == sn["neutron_subnet_id"] and
308 sn["name"] and search_word in sn["name"]):
309 share_network_id = sn["id"]
310 break
311 else:
312 sn_name = "autogenerated_by_tempest_for_isolated_creds"
313 # Use precreated network and subnet from isolated creds
314 net_id = isolated_creds_client.get_credentials(
315 isolated_creds_client.type_of_creds).network['id']
316 subnet_id = isolated_creds_client.get_credentials(
317 isolated_creds_client.type_of_creds).subnet['id']
318
319 # Create suitable share-network
320 if share_network_id is None:
321 sn_desc = "This share-network was created by tempest"
322 sn = sc.create_share_network(name=sn_name,
323 description=sn_desc,
324 neutron_net_id=net_id,
325 neutron_subnet_id=subnet_id)
326 share_network_id = sn["id"]
327
328 return share_network_id
329
330 @classmethod
331 def _create_share(cls, share_protocol=None, size=1, name=None,
332 snapshot_id=None, description=None, metadata=None,
333 share_network_id=None, share_type_id=None,
Andrew Kerrbf31e912015-07-29 10:39:38 -0400334 consistency_group_id=None, client=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400335 cleanup_in_class=True, is_public=False, **kwargs):
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300336 client = client or cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200337 description = description or "Tempest's share"
338 share_network_id = share_network_id or client.share_network_id or None
339 metadata = metadata or {}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400340 kwargs.update({
Marc Koderer0abc93b2015-07-15 09:18:35 +0200341 'share_protocol': share_protocol,
342 'size': size,
343 'name': name,
344 'snapshot_id': snapshot_id,
345 'description': description,
346 'metadata': metadata,
347 'share_network_id': share_network_id,
348 'share_type_id': share_type_id,
349 'is_public': is_public,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400350 })
Andrew Kerrbf31e912015-07-29 10:39:38 -0400351 if consistency_group_id:
352 kwargs['consistency_group_id'] = consistency_group_id
353
Marc Koderer0abc93b2015-07-15 09:18:35 +0200354 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400355 resource = {"type": "share", "id": share["id"], "client": client,
356 "consistency_group_id": consistency_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200357 cleanup_list = (cls.class_resources if cleanup_in_class else
358 cls.method_resources)
359 cleanup_list.insert(0, resource)
360 return share
361
362 @classmethod
Clinton Knighte5c8f092015-08-27 15:00:23 -0400363 def migrate_share(cls, share_id, dest_host, client=None, **kwargs):
364 client = client or cls.shares_v2_client
365 client.migrate_share(share_id, dest_host, **kwargs)
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300366 share = client.wait_for_migration_completed(share_id, dest_host)
367 return share
368
369 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200370 def create_share(cls, *args, **kwargs):
371 """Create one share and wait for available state. Retry if allowed."""
372 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
373 return result[0]
374
375 @classmethod
376 def create_shares(cls, share_data_list):
377 """Creates several shares in parallel with retries.
378
379 Use this method when you want to create more than one share at same
380 time. Especially if config option 'share.share_creation_retry_number'
381 has value more than zero (0).
382 All shares will be expected to have 'available' status with or without
383 recreation else error will be raised.
384
385 :param share_data_list: list -- list of dictionaries with 'args' and
386 'kwargs' for '_create_share' method of this base class.
387 example of data:
388 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
389 :returns: list -- list of shares created using provided data.
390 """
391
392 data = [copy.deepcopy(d) for d in share_data_list]
393 for d in data:
394 if not isinstance(d, dict):
395 raise exceptions.TempestException(
396 "Expected 'dict', got '%s'" % type(d))
397 if "args" not in d:
398 d["args"] = []
399 if "kwargs" not in d:
400 d["kwargs"] = {}
401 if len(d) > 2:
402 raise exceptions.TempestException(
403 "Expected only 'args' and 'kwargs' keys. "
404 "Provided %s" % list(d))
405 d["kwargs"]["client"] = d["kwargs"].get(
Valeriy Ponomaryov1aaa72d2015-09-08 12:59:41 +0300406 "client", cls.shares_v2_client)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200407 d["share"] = cls._create_share(*d["args"], **d["kwargs"])
408 d["cnt"] = 0
409 d["available"] = False
410
411 while not all(d["available"] for d in data):
412 for d in data:
413 if d["available"]:
414 continue
415 try:
416 d["kwargs"]["client"].wait_for_share_status(
417 d["share"]["id"], "available")
418 d["available"] = True
419 except (share_exceptions.ShareBuildErrorException,
420 exceptions.TimeoutException) as e:
421 if CONF.share.share_creation_retry_number > d["cnt"]:
422 d["cnt"] += 1
423 msg = ("Share '%s' failed to be built. "
424 "Trying create another." % d["share"]["id"])
425 LOG.error(msg)
426 LOG.error(e)
427 d["share"] = cls._create_share(
428 *d["args"], **d["kwargs"])
429 else:
430 raise e
431
432 return [d["share"] for d in data]
433
434 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400435 def create_consistency_group(cls, client=None, cleanup_in_class=True,
436 share_network_id=None, **kwargs):
Clinton Knighte5c8f092015-08-27 15:00:23 -0400437 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400438 kwargs['share_network_id'] = (share_network_id or
439 client.share_network_id or None)
440 consistency_group = client.create_consistency_group(**kwargs)
441 resource = {
442 "type": "consistency_group",
443 "id": consistency_group["id"],
444 "client": client}
445 if cleanup_in_class:
446 cls.class_resources.insert(0, resource)
447 else:
448 cls.method_resources.insert(0, resource)
449
450 if kwargs.get('source_cgsnapshot_id'):
451 new_cg_shares = client.list_shares(
452 detailed=True,
453 params={'consistency_group_id': consistency_group['id']})
454
455 for share in new_cg_shares:
456 resource = {"type": "share",
457 "id": share["id"],
458 "client": client,
459 "consistency_group_id": share.get(
460 'consistency_group_id')}
461 if cleanup_in_class:
462 cls.class_resources.insert(0, resource)
463 else:
464 cls.method_resources.insert(0, resource)
465
466 client.wait_for_consistency_group_status(consistency_group['id'],
467 'available')
468 return consistency_group
469
470 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200471 def create_snapshot_wait_for_active(cls, share_id, name=None,
472 description=None, force=False,
473 client=None, cleanup_in_class=True):
474 if client is None:
475 client = cls.shares_client
476 if description is None:
477 description = "Tempest's snapshot"
478 snapshot = client.create_snapshot(share_id, name, description, force)
479 resource = {
480 "type": "snapshot",
481 "id": snapshot["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_snapshot_status(snapshot["id"], "available")
489 return snapshot
490
491 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400492 def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
493 name=None, description=None,
Clinton Knighte5c8f092015-08-27 15:00:23 -0400494 client=None, cleanup_in_class=True,
495 **kwargs):
496 client = client or cls.shares_v2_client
Andrew Kerrbf31e912015-07-29 10:39:38 -0400497 if description is None:
498 description = "Tempest's cgsnapshot"
Clinton Knighte5c8f092015-08-27 15:00:23 -0400499 cgsnapshot = client.create_cgsnapshot(consistency_group_id,
500 name=name,
501 description=description,
502 **kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400503 resource = {
504 "type": "cgsnapshot",
505 "id": cgsnapshot["id"],
506 "client": client,
507 }
508 if cleanup_in_class:
509 cls.class_resources.insert(0, resource)
510 else:
511 cls.method_resources.insert(0, resource)
512 client.wait_for_cgsnapshot_status(cgsnapshot["id"], "available")
513 return cgsnapshot
514
515 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200516 def create_share_network(cls, client=None,
517 cleanup_in_class=False, **kwargs):
518 if client is None:
519 client = cls.shares_client
520 share_network = client.create_share_network(**kwargs)
521 resource = {
522 "type": "share_network",
523 "id": share_network["id"],
524 "client": client,
525 }
526 if cleanup_in_class:
527 cls.class_resources.insert(0, resource)
528 else:
529 cls.method_resources.insert(0, resource)
530 return share_network
531
532 @classmethod
533 def create_security_service(cls, ss_type="ldap", client=None,
534 cleanup_in_class=False, **kwargs):
535 if client is None:
536 client = cls.shares_client
537 security_service = client.create_security_service(ss_type, **kwargs)
538 resource = {
539 "type": "security_service",
540 "id": security_service["id"],
541 "client": client,
542 }
543 if cleanup_in_class:
544 cls.class_resources.insert(0, resource)
545 else:
546 cls.method_resources.insert(0, resource)
547 return security_service
548
549 @classmethod
550 def create_share_type(cls, name, is_public=True, client=None,
551 cleanup_in_class=True, **kwargs):
552 if client is None:
Valeriy Ponomaryova14c2252015-10-29 13:34:32 +0200553 client = cls.shares_v2_client
Marc Koderer0abc93b2015-07-15 09:18:35 +0200554 share_type = client.create_share_type(name, is_public, **kwargs)
555 resource = {
556 "type": "share_type",
557 "id": share_type["share_type"]["id"],
558 "client": client,
559 }
560 if cleanup_in_class:
561 cls.class_resources.insert(0, resource)
562 else:
563 cls.method_resources.insert(0, resource)
564 return share_type
565
566 @staticmethod
567 def add_required_extra_specs_to_dict(extra_specs=None):
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300568 dhss = six.text_type(CONF.share.multitenancy_enabled)
569 snapshot_support = six.text_type(
570 CONF.share.capability_snapshot_support)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200571 required = {
Valeriy Ponomaryovad55dc52015-09-23 13:54:00 +0300572 "driver_handles_share_servers": dhss,
573 "snapshot_support": snapshot_support,
Marc Koderer0abc93b2015-07-15 09:18:35 +0200574 }
575 if extra_specs:
576 required.update(extra_specs)
577 return required
578
579 @classmethod
580 def clear_isolated_creds(cls, creds=None):
581 if creds is None:
582 creds = cls.method_isolated_creds
583 for ic in creds:
584 if "deleted" not in ic.keys():
585 ic["deleted"] = False
586 if not ic["deleted"]:
587 with handle_cleanup_exceptions():
588 ic["method"]()
589 ic["deleted"] = True
590
591 @classmethod
592 def clear_resources(cls, resources=None):
593 """Deletes resources, that were created in test suites.
594
595 This method tries to remove resources from resource list,
596 if it is not found, assumed it was deleted in test itself.
597 It is expected, that all resources were added as LIFO
598 due to restriction of deletion resources, that is in the chain.
599
600 :param resources: dict with keys 'type','id','client' and 'deleted'
601 """
602
603 if resources is None:
604 resources = cls.method_resources
605 for res in resources:
606 if "deleted" not in res.keys():
607 res["deleted"] = False
608 if "client" not in res.keys():
609 res["client"] = cls.shares_client
610 if not(res["deleted"]):
611 res_id = res['id']
612 client = res["client"]
613 with handle_cleanup_exceptions():
614 if res["type"] is "share":
Andrew Kerrbf31e912015-07-29 10:39:38 -0400615 cg_id = res.get('consistency_group_id')
616 if cg_id:
617 params = {'consistency_group_id': cg_id}
Clinton Knighte5c8f092015-08-27 15:00:23 -0400618 client.delete_share(res_id, params=params)
619 else:
620 client.delete_share(res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200621 client.wait_for_resource_deletion(share_id=res_id)
622 elif res["type"] is "snapshot":
623 client.delete_snapshot(res_id)
624 client.wait_for_resource_deletion(snapshot_id=res_id)
625 elif res["type"] is "share_network":
626 client.delete_share_network(res_id)
627 client.wait_for_resource_deletion(sn_id=res_id)
628 elif res["type"] is "security_service":
629 client.delete_security_service(res_id)
630 client.wait_for_resource_deletion(ss_id=res_id)
631 elif res["type"] is "share_type":
632 client.delete_share_type(res_id)
633 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400634 elif res["type"] is "consistency_group":
635 client.delete_consistency_group(res_id)
636 client.wait_for_resource_deletion(cg_id=res_id)
637 elif res["type"] is "cgsnapshot":
638 client.delete_cgsnapshot(res_id)
639 client.wait_for_resource_deletion(cgsnapshot_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200640 else:
641 LOG.warn("Provided unsupported resource type for "
642 "cleanup '%s'. Skipping." % res["type"])
643 res["deleted"] = True
644
645 @classmethod
646 def generate_share_network_data(self):
647 data = {
648 "name": data_utils.rand_name("sn-name"),
649 "description": data_utils.rand_name("sn-desc"),
650 "neutron_net_id": data_utils.rand_name("net-id"),
651 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
652 }
653 return data
654
655 @classmethod
656 def generate_security_service_data(self):
657 data = {
658 "name": data_utils.rand_name("ss-name"),
659 "description": data_utils.rand_name("ss-desc"),
Andrew Kerr40df1d72015-09-28 13:22:33 -0400660 "dns_ip": rand_ip(),
661 "server": rand_ip(),
Marc Koderer0abc93b2015-07-15 09:18:35 +0200662 "domain": data_utils.rand_name("ss-domain"),
663 "user": data_utils.rand_name("ss-user"),
664 "password": data_utils.rand_name("ss-password"),
665 }
666 return data
667
668 # Useful assertions
669 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
670 """Assert two dicts are equivalent.
671
672 This is a 'deep' match in the sense that it handles nested
673 dictionaries appropriately.
674
675 NOTE:
676
677 If you don't care (or don't know) a given value, you can specify
678 the string DONTCARE as the value. This will cause that dict-item
679 to be skipped.
680
681 """
682 def raise_assertion(msg):
683 d1str = str(d1)
684 d2str = str(d2)
685 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
686 'd2: %(d2str)s' %
687 {"msg": msg, "d1str": d1str, "d2str": d2str})
688 raise AssertionError(base_msg)
689
690 d1keys = set(d1.keys())
691 d2keys = set(d2.keys())
692 if d1keys != d2keys:
693 d1only = d1keys - d2keys
694 d2only = d2keys - d1keys
695 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
696 'Keys in d2 and not d1: %(d2only)s' %
697 {"d1only": d1only, "d2only": d2only})
698
699 for key in d1keys:
700 d1value = d1[key]
701 d2value = d2[key]
702 try:
703 error = abs(float(d1value) - float(d2value))
704 within_tolerance = error <= tolerance
705 except (ValueError, TypeError):
706 # If both values aren't convertable to float, just ignore
707 # ValueError if arg is a str, TypeError if it's something else
708 # (like None)
709 within_tolerance = False
710
711 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
712 self.assertDictMatch(d1value, d2value)
713 elif 'DONTCARE' in (d1value, d2value):
714 continue
715 elif approx_equal and within_tolerance:
716 continue
717 elif d1value != d2value:
718 raise_assertion("d1['%(key)s']=%(d1value)s != "
719 "d2['%(key)s']=%(d2value)s" %
720 {
721 "key": key,
722 "d1value": d1value,
723 "d2value": d2value
724 })
725
726
727class BaseSharesAltTest(BaseSharesTest):
728 """Base test case class for all Shares Alt API tests."""
729
730 @classmethod
731 def resource_setup(cls):
732 cls.username = CONF.identity.alt_username
733 cls.password = CONF.identity.alt_password
734 cls.tenant_name = CONF.identity.alt_tenant_name
735 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
736 cls.os = clients.AltManager()
737 alt_share_network_id = CONF.share.alt_share_network_id
738 cls.os.shares_client.share_network_id = alt_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400739 cls.os.shares_v2_client.share_network_id = alt_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200740 super(BaseSharesAltTest, cls).resource_setup()
741
742
743class BaseSharesAdminTest(BaseSharesTest):
744 """Base test case class for all Shares Admin API tests."""
745
746 @classmethod
747 def resource_setup(cls):
Sam Wanb5047aa2015-10-08 05:37:43 -0400748 if hasattr(CONF.identity, 'admin_username'):
749 cls.username = CONF.identity.admin_username
750 cls.password = CONF.identity.admin_password
751 cls.tenant_name = CONF.identity.admin_tenant_name
752 else:
753 cls.username = CONF.auth.admin_username
754 cls.password = CONF.auth.admin_password
755 cls.tenant_name = CONF.auth.admin_tenant_name
Marc Koderer0abc93b2015-07-15 09:18:35 +0200756 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
757 cls.os = clients.AdminManager()
758 admin_share_network_id = CONF.share.admin_share_network_id
759 cls.os.shares_client.share_network_id = admin_share_network_id
Clinton Knighte5c8f092015-08-27 15:00:23 -0400760 cls.os.shares_v2_client.share_network_id = admin_share_network_id
Marc Koderer0abc93b2015-07-15 09:18:35 +0200761 super(BaseSharesAdminTest, cls).resource_setup()