blob: 65e299da25c11cef4596260327720617e6febfe6 [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
23from tempest.common import isolated_creds # noqa
24from tempest import config # noqa
25from tempest import test # noqa
26from tempest_lib.common.utils import data_utils
27from tempest_lib import exceptions
28
29from manila_tempest_tests import clients_share as clients
30from manila_tempest_tests import share_exceptions
31
32CONF = config.CONF
33LOG = log.getLogger(__name__)
34
35
36class handle_cleanup_exceptions(object):
37 """Handle exceptions raised with cleanup operations.
38
39 Always suppress errors when exceptions.NotFound or exceptions.Forbidden
40 are raised.
41 Suppress all other exceptions only in case config opt
42 'suppress_errors_in_cleanup' in config group 'share' is True.
43 """
44
45 def __enter__(self):
46 return self
47
48 def __exit__(self, exc_type, exc_value, exc_traceback):
49 if not (isinstance(exc_value,
50 (exceptions.NotFound, exceptions.Forbidden)) or
51 CONF.share.suppress_errors_in_cleanup):
52 return False # Do not suppress error if any
53 if exc_traceback:
54 LOG.error("Suppressed cleanup error in Manila: "
55 "\n%s" % traceback.format_exc())
56 return True # Suppress error if any
57
58
59def network_synchronized(f):
60
61 def wrapped_func(self, *args, **kwargs):
62 with_isolated_creds = True if len(args) > 2 else False
63 no_lock_required = kwargs.get(
64 "isolated_creds_client", with_isolated_creds)
65 if no_lock_required:
66 # Usage of not reusable network. No need in lock.
67 return f(self, *args, **kwargs)
68
69 # Use lock assuming reusage of common network.
70 @lockutils.synchronized("manila_network_lock", external=True)
71 def source_func(self, *args, **kwargs):
72 return f(self, *args, **kwargs)
73
74 return source_func(self, *args, **kwargs)
75
76 return wrapped_func
77
78
79class BaseSharesTest(test.BaseTestCase):
80 """Base test case class for all Manila API tests."""
81
82 force_tenant_isolation = False
83 protocols = ["nfs", "cifs", "glusterfs", "hdfs"]
84
85 # Will be cleaned up in resource_cleanup
86 class_resources = []
87
88 # Will be cleaned up in tearDown method
89 method_resources = []
90
91 # Will be cleaned up in resource_cleanup
92 class_isolated_creds = []
93
94 # Will be cleaned up in tearDown method
95 method_isolated_creds = []
96
97 @classmethod
98 def get_client_with_isolated_creds(cls,
99 name=None,
100 type_of_creds="admin",
101 cleanup_in_class=False):
102 """Creates isolated creds.
103
104 :param name: name, will be used for naming ic and related stuff
105 :param type_of_creds: admin, alt or primary
106 :param cleanup_in_class: defines place where to delete
107 :returns: SharesClient -- shares client with isolated creds.
108 :returns: To client added dict attr 'creds' with
109 :returns: key elements 'tenant' and 'user'.
110 """
111 if name is None:
112 # Get name of test method
113 name = inspect.stack()[1][3]
114 if len(name) > 32:
115 name = name[0:32]
116
117 # Choose type of isolated creds
118 ic = isolated_creds.IsolatedCreds(name=name)
119 if "admin" in type_of_creds:
120 creds = ic.get_admin_creds()
121 elif "alt" in type_of_creds:
122 creds = ic.get_alt_creds()
123 else:
124 creds = ic.self.get_credentials(type_of_creds)
125 ic.type_of_creds = type_of_creds
126
127 # create client with isolated creds
128 os = clients.Manager(credentials=creds)
129 client = os.shares_client
130
131 # Set place where will be deleted isolated creds
132 ic_res = {
133 "method": ic.clear_isolated_creds,
134 "deleted": False,
135 }
136 if cleanup_in_class:
137 cls.class_isolated_creds.insert(0, ic_res)
138 else:
139 cls.method_isolated_creds.insert(0, ic_res)
140
141 # Provide share network
142 if CONF.share.multitenancy_enabled:
143 if not CONF.service_available.neutron:
144 raise cls.skipException("Neutron support is required")
145 nc = os.network_client
146 share_network_id = cls.provide_share_network(client, nc, ic)
147 client.share_network_id = share_network_id
148 resource = {
149 "type": "share_network",
150 "id": client.share_network_id,
151 "client": client,
152 }
153 if cleanup_in_class:
154 cls.class_resources.insert(0, resource)
155 else:
156 cls.method_resources.insert(0, resource)
157 return client
158
159 @classmethod
160 def verify_nonempty(cls, *args):
161 if not all(args):
162 msg = "Missing API credentials in configuration."
163 raise cls.skipException(msg)
164
165 @classmethod
166 def resource_setup(cls):
167 if not (any(p in CONF.share.enable_protocols
168 for p in cls.protocols) and
169 CONF.service_available.manila):
170 skip_msg = "Manila is disabled"
171 raise cls.skipException(skip_msg)
172 super(BaseSharesTest, cls).resource_setup()
173 if not hasattr(cls, "os"):
174 cls.username = CONF.identity.username
175 cls.password = CONF.identity.password
176 cls.tenant_name = CONF.identity.tenant_name
177 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
178 cls.os = clients.Manager()
179 if CONF.share.multitenancy_enabled:
180 if not CONF.service_available.neutron:
181 raise cls.skipException("Neutron support is required")
182 sc = cls.os.shares_client
183 nc = cls.os.network_client
184 share_network_id = cls.provide_share_network(sc, nc)
185 cls.os.shares_client.share_network_id = share_network_id
186 cls.shares_client = cls.os.shares_client
187
188 def setUp(self):
189 super(BaseSharesTest, self).setUp()
190 self.addCleanup(self.clear_resources)
191 self.addCleanup(self.clear_isolated_creds)
192
193 @classmethod
194 def resource_cleanup(cls):
195 super(BaseSharesTest, cls).resource_cleanup()
196 cls.clear_resources(cls.class_resources)
197 cls.clear_isolated_creds(cls.class_isolated_creds)
198
199 @classmethod
200 @network_synchronized
201 def provide_share_network(cls, shares_client, network_client,
202 isolated_creds_client=None):
203 """Used for finding/creating share network for multitenant driver.
204
205 This method creates/gets entity share-network for one tenant. This
206 share-network will be used for creation of service vm.
207
208 :param shares_client: shares client, which requires share-network
209 :param network_client: network client from same tenant as shares
210 :param isolated_creds_client: IsolatedCreds instance
211 If provided, then its networking will be used if needed.
212 If not provided, then common network will be used if needed.
213 :returns: str -- share network id for shares_client tenant
214 :returns: None -- if single-tenant driver used
215 """
216
217 sc = shares_client
218
219 if not CONF.share.multitenancy_enabled:
220 # Assumed usage of a single-tenant driver
221 share_network_id = None
222 elif sc.share_network_id:
223 # Share-network already exists, use it
224 share_network_id = sc.share_network_id
225 else:
226 net_id = subnet_id = share_network_id = None
227
228 if not isolated_creds_client:
229 # Search for networks, created in previous runs
230 search_word = "reusable"
231 sn_name = "autogenerated_by_tempest_%s" % search_word
232 service_net_name = "share-service"
233 networks = network_client.list_networks()
234 if "networks" in networks.keys():
235 networks = networks["networks"]
236 for network in networks:
237 if (service_net_name in network["name"] and
238 sc.tenant_id == network['tenant_id']):
239 net_id = network["id"]
240 if len(network["subnets"]) > 0:
241 subnet_id = network["subnets"][0]
242 break
243
244 # Create suitable network
245 if (net_id is None or subnet_id is None):
246 ic = isolated_creds.IsolatedCreds(name=service_net_name)
247 net_data = ic._create_network_resources(sc.tenant_id)
248 network, subnet, router = net_data
249 net_id = network["id"]
250 subnet_id = subnet["id"]
251
252 # Try get suitable share-network
253 share_networks = sc.list_share_networks_with_detail()
254 for sn in share_networks:
255 if (net_id == sn["neutron_net_id"] and
256 subnet_id == sn["neutron_subnet_id"] and
257 sn["name"] and search_word in sn["name"]):
258 share_network_id = sn["id"]
259 break
260 else:
261 sn_name = "autogenerated_by_tempest_for_isolated_creds"
262 # Use precreated network and subnet from isolated creds
263 net_id = isolated_creds_client.get_credentials(
264 isolated_creds_client.type_of_creds).network['id']
265 subnet_id = isolated_creds_client.get_credentials(
266 isolated_creds_client.type_of_creds).subnet['id']
267
268 # Create suitable share-network
269 if share_network_id is None:
270 sn_desc = "This share-network was created by tempest"
271 sn = sc.create_share_network(name=sn_name,
272 description=sn_desc,
273 neutron_net_id=net_id,
274 neutron_subnet_id=subnet_id)
275 share_network_id = sn["id"]
276
277 return share_network_id
278
279 @classmethod
280 def _create_share(cls, share_protocol=None, size=1, name=None,
281 snapshot_id=None, description=None, metadata=None,
282 share_network_id=None, share_type_id=None,
Andrew Kerrbf31e912015-07-29 10:39:38 -0400283 consistency_group_id=None, client=None,
284 cleanup_in_class=True, is_public=False):
Marc Koderer0abc93b2015-07-15 09:18:35 +0200285 client = client or cls.shares_client
286 description = description or "Tempest's share"
287 share_network_id = share_network_id or client.share_network_id or None
288 metadata = metadata or {}
289 kwargs = {
290 'share_protocol': share_protocol,
291 'size': size,
292 'name': name,
293 'snapshot_id': snapshot_id,
294 'description': description,
295 'metadata': metadata,
296 'share_network_id': share_network_id,
297 'share_type_id': share_type_id,
298 'is_public': is_public,
299 }
Andrew Kerrbf31e912015-07-29 10:39:38 -0400300 if consistency_group_id:
301 kwargs['consistency_group_id'] = consistency_group_id
302
Marc Koderer0abc93b2015-07-15 09:18:35 +0200303 share = client.create_share(**kwargs)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400304 resource = {"type": "share", "id": share["id"], "client": client,
305 "consistency_group_id": consistency_group_id}
Marc Koderer0abc93b2015-07-15 09:18:35 +0200306 cleanup_list = (cls.class_resources if cleanup_in_class else
307 cls.method_resources)
308 cleanup_list.insert(0, resource)
309 return share
310
311 @classmethod
Rodrigo Barbierib7137ad2015-09-06 22:53:16 -0300312 def migrate_share(cls, share_id, dest_host, client=None):
313 client = client or cls.shares_client
314 client.migrate_share(share_id, dest_host)
315 share = client.wait_for_migration_completed(share_id, dest_host)
316 return share
317
318 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200319 def create_share(cls, *args, **kwargs):
320 """Create one share and wait for available state. Retry if allowed."""
321 result = cls.create_shares([{"args": args, "kwargs": kwargs}])
322 return result[0]
323
324 @classmethod
325 def create_shares(cls, share_data_list):
326 """Creates several shares in parallel with retries.
327
328 Use this method when you want to create more than one share at same
329 time. Especially if config option 'share.share_creation_retry_number'
330 has value more than zero (0).
331 All shares will be expected to have 'available' status with or without
332 recreation else error will be raised.
333
334 :param share_data_list: list -- list of dictionaries with 'args' and
335 'kwargs' for '_create_share' method of this base class.
336 example of data:
337 share_data_list=[{'args': ['quuz'], 'kwargs': {'foo': 'bar'}}}]
338 :returns: list -- list of shares created using provided data.
339 """
340
341 data = [copy.deepcopy(d) for d in share_data_list]
342 for d in data:
343 if not isinstance(d, dict):
344 raise exceptions.TempestException(
345 "Expected 'dict', got '%s'" % type(d))
346 if "args" not in d:
347 d["args"] = []
348 if "kwargs" not in d:
349 d["kwargs"] = {}
350 if len(d) > 2:
351 raise exceptions.TempestException(
352 "Expected only 'args' and 'kwargs' keys. "
353 "Provided %s" % list(d))
354 d["kwargs"]["client"] = d["kwargs"].get(
355 "client", cls.shares_client)
356 d["share"] = cls._create_share(*d["args"], **d["kwargs"])
357 d["cnt"] = 0
358 d["available"] = False
359
360 while not all(d["available"] for d in data):
361 for d in data:
362 if d["available"]:
363 continue
364 try:
365 d["kwargs"]["client"].wait_for_share_status(
366 d["share"]["id"], "available")
367 d["available"] = True
368 except (share_exceptions.ShareBuildErrorException,
369 exceptions.TimeoutException) as e:
370 if CONF.share.share_creation_retry_number > d["cnt"]:
371 d["cnt"] += 1
372 msg = ("Share '%s' failed to be built. "
373 "Trying create another." % d["share"]["id"])
374 LOG.error(msg)
375 LOG.error(e)
376 d["share"] = cls._create_share(
377 *d["args"], **d["kwargs"])
378 else:
379 raise e
380
381 return [d["share"] for d in data]
382
383 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400384 def create_consistency_group(cls, client=None, cleanup_in_class=True,
385 share_network_id=None, **kwargs):
386 client = client or cls.shares_client
387 kwargs['share_network_id'] = (share_network_id or
388 client.share_network_id or None)
389 consistency_group = client.create_consistency_group(**kwargs)
390 resource = {
391 "type": "consistency_group",
392 "id": consistency_group["id"],
393 "client": client}
394 if cleanup_in_class:
395 cls.class_resources.insert(0, resource)
396 else:
397 cls.method_resources.insert(0, resource)
398
399 if kwargs.get('source_cgsnapshot_id'):
400 new_cg_shares = client.list_shares(
401 detailed=True,
402 params={'consistency_group_id': consistency_group['id']})
403
404 for share in new_cg_shares:
405 resource = {"type": "share",
406 "id": share["id"],
407 "client": client,
408 "consistency_group_id": share.get(
409 'consistency_group_id')}
410 if cleanup_in_class:
411 cls.class_resources.insert(0, resource)
412 else:
413 cls.method_resources.insert(0, resource)
414
415 client.wait_for_consistency_group_status(consistency_group['id'],
416 'available')
417 return consistency_group
418
419 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200420 def create_snapshot_wait_for_active(cls, share_id, name=None,
421 description=None, force=False,
422 client=None, cleanup_in_class=True):
423 if client is None:
424 client = cls.shares_client
425 if description is None:
426 description = "Tempest's snapshot"
427 snapshot = client.create_snapshot(share_id, name, description, force)
428 resource = {
429 "type": "snapshot",
430 "id": snapshot["id"],
431 "client": client,
432 }
433 if cleanup_in_class:
434 cls.class_resources.insert(0, resource)
435 else:
436 cls.method_resources.insert(0, resource)
437 client.wait_for_snapshot_status(snapshot["id"], "available")
438 return snapshot
439
440 @classmethod
Andrew Kerrbf31e912015-07-29 10:39:38 -0400441 def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
442 name=None, description=None,
443 client=None, cleanup_in_class=True):
444 client = client or cls.shares_client
445 if description is None:
446 description = "Tempest's cgsnapshot"
447 cgsnapshot = client.create_cgsnapshot(consistency_group_id, name=name,
448 description=description)
449 resource = {
450 "type": "cgsnapshot",
451 "id": cgsnapshot["id"],
452 "client": client,
453 }
454 if cleanup_in_class:
455 cls.class_resources.insert(0, resource)
456 else:
457 cls.method_resources.insert(0, resource)
458 client.wait_for_cgsnapshot_status(cgsnapshot["id"], "available")
459 return cgsnapshot
460
461 @classmethod
Marc Koderer0abc93b2015-07-15 09:18:35 +0200462 def create_share_network(cls, client=None,
463 cleanup_in_class=False, **kwargs):
464 if client is None:
465 client = cls.shares_client
466 share_network = client.create_share_network(**kwargs)
467 resource = {
468 "type": "share_network",
469 "id": share_network["id"],
470 "client": client,
471 }
472 if cleanup_in_class:
473 cls.class_resources.insert(0, resource)
474 else:
475 cls.method_resources.insert(0, resource)
476 return share_network
477
478 @classmethod
479 def create_security_service(cls, ss_type="ldap", client=None,
480 cleanup_in_class=False, **kwargs):
481 if client is None:
482 client = cls.shares_client
483 security_service = client.create_security_service(ss_type, **kwargs)
484 resource = {
485 "type": "security_service",
486 "id": security_service["id"],
487 "client": client,
488 }
489 if cleanup_in_class:
490 cls.class_resources.insert(0, resource)
491 else:
492 cls.method_resources.insert(0, resource)
493 return security_service
494
495 @classmethod
496 def create_share_type(cls, name, is_public=True, client=None,
497 cleanup_in_class=True, **kwargs):
498 if client is None:
499 client = cls.shares_client
500 share_type = client.create_share_type(name, is_public, **kwargs)
501 resource = {
502 "type": "share_type",
503 "id": share_type["share_type"]["id"],
504 "client": client,
505 }
506 if cleanup_in_class:
507 cls.class_resources.insert(0, resource)
508 else:
509 cls.method_resources.insert(0, resource)
510 return share_type
511
512 @staticmethod
513 def add_required_extra_specs_to_dict(extra_specs=None):
514 value = six.text_type(CONF.share.multitenancy_enabled)
515 required = {
516 "driver_handles_share_servers": value,
517 "snapshot_support": 'True',
518 }
519 if extra_specs:
520 required.update(extra_specs)
521 return required
522
523 @classmethod
524 def clear_isolated_creds(cls, creds=None):
525 if creds is None:
526 creds = cls.method_isolated_creds
527 for ic in creds:
528 if "deleted" not in ic.keys():
529 ic["deleted"] = False
530 if not ic["deleted"]:
531 with handle_cleanup_exceptions():
532 ic["method"]()
533 ic["deleted"] = True
534
535 @classmethod
536 def clear_resources(cls, resources=None):
537 """Deletes resources, that were created in test suites.
538
539 This method tries to remove resources from resource list,
540 if it is not found, assumed it was deleted in test itself.
541 It is expected, that all resources were added as LIFO
542 due to restriction of deletion resources, that is in the chain.
543
544 :param resources: dict with keys 'type','id','client' and 'deleted'
545 """
546
547 if resources is None:
548 resources = cls.method_resources
549 for res in resources:
550 if "deleted" not in res.keys():
551 res["deleted"] = False
552 if "client" not in res.keys():
553 res["client"] = cls.shares_client
554 if not(res["deleted"]):
555 res_id = res['id']
556 client = res["client"]
557 with handle_cleanup_exceptions():
558 if res["type"] is "share":
Andrew Kerrbf31e912015-07-29 10:39:38 -0400559 params = None
560 cg_id = res.get('consistency_group_id')
561 if cg_id:
562 params = {'consistency_group_id': cg_id}
563 client.delete_share(res_id, params=params)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200564 client.wait_for_resource_deletion(share_id=res_id)
565 elif res["type"] is "snapshot":
566 client.delete_snapshot(res_id)
567 client.wait_for_resource_deletion(snapshot_id=res_id)
568 elif res["type"] is "share_network":
569 client.delete_share_network(res_id)
570 client.wait_for_resource_deletion(sn_id=res_id)
571 elif res["type"] is "security_service":
572 client.delete_security_service(res_id)
573 client.wait_for_resource_deletion(ss_id=res_id)
574 elif res["type"] is "share_type":
575 client.delete_share_type(res_id)
576 client.wait_for_resource_deletion(st_id=res_id)
Andrew Kerrbf31e912015-07-29 10:39:38 -0400577 elif res["type"] is "consistency_group":
578 client.delete_consistency_group(res_id)
579 client.wait_for_resource_deletion(cg_id=res_id)
580 elif res["type"] is "cgsnapshot":
581 client.delete_cgsnapshot(res_id)
582 client.wait_for_resource_deletion(cgsnapshot_id=res_id)
Marc Koderer0abc93b2015-07-15 09:18:35 +0200583 else:
584 LOG.warn("Provided unsupported resource type for "
585 "cleanup '%s'. Skipping." % res["type"])
586 res["deleted"] = True
587
588 @classmethod
589 def generate_share_network_data(self):
590 data = {
591 "name": data_utils.rand_name("sn-name"),
592 "description": data_utils.rand_name("sn-desc"),
593 "neutron_net_id": data_utils.rand_name("net-id"),
594 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
595 }
596 return data
597
598 @classmethod
599 def generate_security_service_data(self):
600 data = {
601 "name": data_utils.rand_name("ss-name"),
602 "description": data_utils.rand_name("ss-desc"),
603 "dns_ip": data_utils.rand_name("ss-dns_ip"),
604 "server": data_utils.rand_name("ss-server"),
605 "domain": data_utils.rand_name("ss-domain"),
606 "user": data_utils.rand_name("ss-user"),
607 "password": data_utils.rand_name("ss-password"),
608 }
609 return data
610
611 # Useful assertions
612 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
613 """Assert two dicts are equivalent.
614
615 This is a 'deep' match in the sense that it handles nested
616 dictionaries appropriately.
617
618 NOTE:
619
620 If you don't care (or don't know) a given value, you can specify
621 the string DONTCARE as the value. This will cause that dict-item
622 to be skipped.
623
624 """
625 def raise_assertion(msg):
626 d1str = str(d1)
627 d2str = str(d2)
628 base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
629 'd2: %(d2str)s' %
630 {"msg": msg, "d1str": d1str, "d2str": d2str})
631 raise AssertionError(base_msg)
632
633 d1keys = set(d1.keys())
634 d2keys = set(d2.keys())
635 if d1keys != d2keys:
636 d1only = d1keys - d2keys
637 d2only = d2keys - d1keys
638 raise_assertion('Keys in d1 and not d2: %(d1only)s. '
639 'Keys in d2 and not d1: %(d2only)s' %
640 {"d1only": d1only, "d2only": d2only})
641
642 for key in d1keys:
643 d1value = d1[key]
644 d2value = d2[key]
645 try:
646 error = abs(float(d1value) - float(d2value))
647 within_tolerance = error <= tolerance
648 except (ValueError, TypeError):
649 # If both values aren't convertable to float, just ignore
650 # ValueError if arg is a str, TypeError if it's something else
651 # (like None)
652 within_tolerance = False
653
654 if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
655 self.assertDictMatch(d1value, d2value)
656 elif 'DONTCARE' in (d1value, d2value):
657 continue
658 elif approx_equal and within_tolerance:
659 continue
660 elif d1value != d2value:
661 raise_assertion("d1['%(key)s']=%(d1value)s != "
662 "d2['%(key)s']=%(d2value)s" %
663 {
664 "key": key,
665 "d1value": d1value,
666 "d2value": d2value
667 })
668
669
670class BaseSharesAltTest(BaseSharesTest):
671 """Base test case class for all Shares Alt API tests."""
672
673 @classmethod
674 def resource_setup(cls):
675 cls.username = CONF.identity.alt_username
676 cls.password = CONF.identity.alt_password
677 cls.tenant_name = CONF.identity.alt_tenant_name
678 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
679 cls.os = clients.AltManager()
680 alt_share_network_id = CONF.share.alt_share_network_id
681 cls.os.shares_client.share_network_id = alt_share_network_id
682 super(BaseSharesAltTest, cls).resource_setup()
683
684
685class BaseSharesAdminTest(BaseSharesTest):
686 """Base test case class for all Shares Admin API tests."""
687
688 @classmethod
689 def resource_setup(cls):
690 cls.username = CONF.identity.admin_username
691 cls.password = CONF.identity.admin_password
692 cls.tenant_name = CONF.identity.admin_tenant_name
693 cls.verify_nonempty(cls.username, cls.password, cls.tenant_name)
694 cls.os = clients.AdminManager()
695 admin_share_network_id = CONF.share.admin_share_network_id
696 cls.os.shares_client.share_network_id = admin_share_network_id
697 super(BaseSharesAdminTest, cls).resource_setup()