From 5dc3211b71199a8b769db635b797f3acd191c22c Mon Sep 17 00:00:00 2001 From: dimtruck Date: Mon, 4 Jan 2016 23:00:53 -0600 Subject: [PATCH] Swithc to using dynamic credentials in tempest tests Switch to using DynamicCredentialProvider, which provides a unique non-admin user per test case, making it possible to functional test in parallel. Change-Id: I6bb5d493bb01d0dd2b4cd092f87f111b13b1fe02 --- magnum/tests/contrib/post_test_hook.sh | 8 -- .../api/v1/clients/baymodel_client.py | 12 +-- .../api/v1/clients/magnum_service_client.py | 5 +- .../tests/functional/api/v1/test_baymodel.py | 92 +++++++++---------- .../functional/api/v1/test_magnum_service.py | 18 +++- magnum/tests/functional/common/base.py | 60 ++++++++++++ magnum/tests/functional/common/client.py | 67 +------------- magnum/tests/functional/common/config.py | 9 ++ magnum/tests/functional/common/manager.py | 69 ++++++-------- 9 files changed, 167 insertions(+), 173 deletions(-) diff --git a/magnum/tests/contrib/post_test_hook.sh b/magnum/tests/contrib/post_test_hook.sh index 8da033b8d6..e6c6b73a5d 100755 --- a/magnum/tests/contrib/post_test_hook.sh +++ b/magnum/tests/contrib/post_test_hook.sh @@ -142,14 +142,6 @@ if [[ "api" == "$coe" ]]; then iniset $BASE/new/tempest/etc/tempest.conf magnum keypair_id default iniset $BASE/new/tempest/etc/tempest.conf magnum flavor_id m1.magnum - # This is a quick fix in case the other patch fails. Just to unblock gate - iniset $BASE/new/tempest/etc/tempest.conf identity username ${TEMPEST_USERNAME:-demo} - iniset $BASE/new/tempest/etc/tempest.conf identity password "${ADMIN_PASSWORD:-secrete}" - iniset $BASE/new/tempest/etc/tempest.conf identity tenant_name ${TEMPEST_TENANT_NAME:-demo} - iniset $BASE/new/tempest/etc/tempest.conf identity alt_username ${ALT_USERNAME:-alt_demo} - iniset $BASE/new/tempest/etc/tempest.conf identity alt_password "${ADMIN_PASSWORD:-secrete}" - iniset $BASE/new/tempest/etc/tempest.conf identity alt_tenant_name ${ALT_TENANT_NAME:-alt_demo} - # show tempest config with magnum cat etc/tempest.conf diff --git a/magnum/tests/functional/api/v1/clients/baymodel_client.py b/magnum/tests/functional/api/v1/clients/baymodel_client.py index 747e8a51bd..10d6a8b4a4 100644 --- a/magnum/tests/functional/api/v1/clients/baymodel_client.py +++ b/magnum/tests/functional/api/v1/clients/baymodel_client.py @@ -14,7 +14,7 @@ from magnum.tests.functional.api.v1.models import baymodel_model from magnum.tests.functional.common import client -class BayModelClient(client.ClientMixin): +class BayModelClient(client.MagnumClient): """Encapsulates REST calls and maps JSON to/from models""" @classmethod @@ -49,7 +49,7 @@ class BayModelClient(client.ClientMixin): :returns: response object and BayModelCollection object """ - resp, body = self.client.get(self.baymodels_uri(filters), **kwargs) + resp, body = self.get(self.baymodels_uri(filters), **kwargs) return self.deserialize(resp, body, baymodel_model.BayModelCollection) def get_baymodel(self, baymodel_id, **kwargs): @@ -61,7 +61,7 @@ class BayModelClient(client.ClientMixin): :returns: response object and BayModelCollection object """ - resp, body = self.client.get(self.baymodel_uri(baymodel_id)) + resp, body = self.get(self.baymodel_uri(baymodel_id)) return self.deserialize(resp, body, baymodel_model.BayModelEntity) def post_baymodel(self, model, **kwargs): @@ -73,7 +73,7 @@ class BayModelClient(client.ClientMixin): :returns: response object and BayModelEntity object """ - resp, body = self.client.post( + resp, body = self.post( self.baymodels_uri(), body=model.to_json(), **kwargs) return self.deserialize(resp, body, baymodel_model.BayModelEntity) @@ -88,7 +88,7 @@ class BayModelClient(client.ClientMixin): :returns: response object and BayModelEntity object """ - resp, body = self.client.patch( + resp, body = self.patch( self.baymodel_uri(baymodel_id), body=baymodelpatch_listmodel.to_json(), **kwargs) return self.deserialize(resp, body, baymodel_model.BayModelEntity) @@ -102,4 +102,4 @@ class BayModelClient(client.ClientMixin): :returns: response object """ - return self.client.delete(self.baymodel_uri(baymodel_id), **kwargs) + return self.delete(self.baymodel_uri(baymodel_id), **kwargs) diff --git a/magnum/tests/functional/api/v1/clients/magnum_service_client.py b/magnum/tests/functional/api/v1/clients/magnum_service_client.py index 05645601be..0559c91342 100644 --- a/magnum/tests/functional/api/v1/clients/magnum_service_client.py +++ b/magnum/tests/functional/api/v1/clients/magnum_service_client.py @@ -14,7 +14,7 @@ from magnum.tests.functional.api.v1.models import magnum_service_model from magnum.tests.functional.common import client -class MagnumServiceClient(client.ClientMixin): +class MagnumServiceClient(client.MagnumClient): """Encapsulates REST calls and maps JSON to/from models""" @classmethod @@ -39,7 +39,6 @@ class MagnumServiceClient(client.ClientMixin): :returns: response object and MagnumServiceCollection object """ - resp, body = self.client.get(self.magnum_service_uri(filters), - **kwargs) + resp, body = self.get(self.magnum_service_uri(filters), **kwargs) return self.deserialize(resp, body, magnum_service_model.MagnumServiceCollection) diff --git a/magnum/tests/functional/api/v1/test_baymodel.py b/magnum/tests/functional/api/v1/test_baymodel.py index 361bfb4490..0113293787 100644 --- a/magnum/tests/functional/api/v1/test_baymodel.py +++ b/magnum/tests/functional/api/v1/test_baymodel.py @@ -14,7 +14,6 @@ from tempest_lib import exceptions import testtools -from magnum.tests.functional.api.v1.clients import baymodel_client as cli from magnum.tests.functional.common import base from magnum.tests.functional.common import datagen @@ -26,34 +25,39 @@ class BayModelTest(base.BaseMagnumTest): def __init__(self, *args, **kwargs): super(BayModelTest, self).__init__(*args, **kwargs) self.baymodels = [] + self.baymodel_client = None + self.keypairs_client = None def setUp(self): super(BayModelTest, self).setUp() + (self.baymodel_client, + self.keypairs_client) = self.get_clients_with_isolated_creds( + type_of_creds='default', + request_type='baymodel') def tearDown(self): super(BayModelTest, self).tearDown() - for (baymodel_id, user) in self.baymodels: - self._delete_baymodel(baymodel_id, user) - self.baymodels.remove((baymodel_id, user)) + for baymodel_id in self.baymodels: + self._delete_baymodel(baymodel_id) + self.baymodels.remove(baymodel_id) - def _create_baymodel(self, baymodel_model, user='default'): - resp, model = cli.BayModelClient.as_user(user).post_baymodel( - baymodel_model) + def _create_baymodel(self, baymodel_model): + resp, model = self.baymodel_client.post_baymodel(baymodel_model) self.assertEqual(201, resp.status) - self.baymodels.append((model.uuid, user)) + self.baymodels.append(model.uuid) return resp, model - def _delete_baymodel(self, baymodel_id, user='default'): - resp, model = cli.BayModelClient.as_user(user).delete_baymodel( - baymodel_id) + def _delete_baymodel(self, baymodel_id): + resp, model = self.baymodel_client.delete_baymodel(baymodel_id) self.assertEqual(204, resp.status) return resp, model @testtools.testcase.attr('positive') def test_list_baymodels(self): + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id() - _, temp_model = self._create_baymodel(gen_model, user='default') - resp, model = cli.BayModelClient.as_user('default').list_baymodels() + _, temp_model = self._create_baymodel(gen_model) + resp, model = self.baymodel_client.list_baymodels() self.assertEqual(200, resp.status) self.assertGreater(len(model.baymodels), 0) self.assertIn( @@ -61,123 +65,117 @@ class BayModelTest(base.BaseMagnumTest): @testtools.testcase.attr('positive') def test_create_baymodel(self): + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id() - resp, model = self._create_baymodel(gen_model, user='default') + resp, model = self._create_baymodel(gen_model) @testtools.testcase.attr('positive') def test_update_baymodel_by_uuid(self): + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id() - resp, old_model = self._create_baymodel(gen_model, user='default') + resp, old_model = self._create_baymodel(gen_model) patch_model = datagen.random_baymodel_name_patch_data() - bay_model_client = cli.BayModelClient.as_user('default') - resp, new_model = bay_model_client.patch_baymodel( + resp, new_model = self.baymodel_client.patch_baymodel( old_model.uuid, patch_model) self.assertEqual(200, resp.status) - resp, model = cli.BayModelClient.as_user('default').get_baymodel( - new_model.uuid) + resp, model = self.baymodel_client.get_baymodel(new_model.uuid) self.assertEqual(200, resp.status) self.assertEqual(old_model.uuid, new_model.uuid) self.assertEqual(model.name, new_model.name) @testtools.testcase.attr('positive') def test_delete_baymodel_by_uuid(self): + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id() - resp, model = self._create_baymodel(gen_model, user='default') - resp, _ = cli.BayModelClient.as_user('default').delete_baymodel( - model.uuid) + resp, model = self._create_baymodel(gen_model) + resp, _ = self.baymodel_client.delete_baymodel(model.uuid) self.assertEqual(204, resp.status) - self.baymodels.remove((model.uuid, 'default')) + self.baymodels.remove(model.uuid) @testtools.testcase.attr('positive') def test_delete_baymodel_by_name(self): + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id() - resp, model = self._create_baymodel(gen_model, user='default') - resp, _ = cli.BayModelClient.as_user('default').delete_baymodel( - model.name) + resp, model = self._create_baymodel(gen_model) + resp, _ = self.baymodel_client.delete_baymodel(model.name) self.assertEqual(204, resp.status) - self.baymodels.remove((model.uuid, 'default')) + self.baymodels.remove(model.uuid) @testtools.testcase.attr('negative') def test_get_baymodel_by_uuid_404(self): - bay_model_client = cli.BayModelClient.as_user('default') self.assertRaises( exceptions.NotFound, - bay_model_client.get_baymodel, datagen.random_uuid()) + self.baymodel_client.get_baymodel, datagen.random_uuid()) @testtools.testcase.attr('negative') def test_update_baymodel_404(self): patch_model = datagen.random_baymodel_name_patch_data() - bay_model_client = cli.BayModelClient.as_user('default') self.assertRaises( exceptions.NotFound, - bay_model_client.patch_baymodel, + self.baymodel_client.patch_baymodel, datagen.random_uuid(), patch_model) @testtools.testcase.attr('negative') def test_delete_baymodel_404(self): - bay_model_client = cli.BayModelClient.as_user('default') self.assertRaises( exceptions.NotFound, - bay_model_client.delete_baymodel, datagen.random_uuid()) + self.baymodel_client.delete_baymodel, datagen.random_uuid()) @testtools.testcase.attr('negative') def test_get_baymodel_by_name_404(self): - bay_model_client = cli.BayModelClient.as_user('default') self.assertRaises( exceptions.NotFound, - bay_model_client.get_baymodel, 'fooo') + self.baymodel_client.get_baymodel, 'fooo') @testtools.testcase.attr('negative') def test_update_baymodel_name_not_found(self): patch_model = datagen.random_baymodel_name_patch_data() - bay_model_client = cli.BayModelClient.as_user('default') self.assertRaises( exceptions.NotFound, - bay_model_client.patch_baymodel, 'fooo', patch_model) + self.baymodel_client.patch_baymodel, 'fooo', patch_model) @testtools.testcase.attr('negative') def test_delete_baymodel_by_name_404(self): - bay_model_client = cli.BayModelClient.as_user('default') self.assertRaises( exceptions.NotFound, - bay_model_client.get_baymodel, 'fooo') + self.baymodel_client.get_baymodel, 'fooo') @testtools.testcase.attr('negative') def test_create_baymodel_missing_image(self): - bay_model_client = cli.BayModelClient.as_user('default') + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair() self.assertRaises( exceptions.NotFound, - bay_model_client.post_baymodel, gen_model) + self.baymodel_client.post_baymodel, gen_model) @testtools.testcase.attr('negative') def test_create_baymodel_missing_keypair(self): - bay_model_client = cli.BayModelClient.as_user('default') gen_model = datagen.random_baymodel_data_w_valid_image_id() self.assertRaises( exceptions.NotFound, - bay_model_client.post_baymodel, gen_model) + self.baymodel_client.post_baymodel, gen_model) @testtools.testcase.attr('negative') def test_update_baymodel_invalid_patch(self): # get json object + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id() resp, old_model = self._create_baymodel(gen_model) - bay_model_client = cli.BayModelClient.as_user('default') self.assertRaises( exceptions.BadRequest, - bay_model_client.patch_baymodel, datagen.random_uuid(), gen_model) + self.baymodel_client.patch_baymodel, datagen.random_uuid(), + gen_model) @testtools.testcase.attr('negative') def test_create_baymodel_invalid_network_driver(self): - bay_model_client = cli.BayModelClient.as_user('default') + self.keypairs_client.create_keypair(name='default') gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id() gen_model.network_driver = 'invalid_network_driver' self.assertRaises( exceptions.BadRequest, - bay_model_client.post_baymodel, gen_model) + self.baymodel_client.post_baymodel, gen_model) diff --git a/magnum/tests/functional/api/v1/test_magnum_service.py b/magnum/tests/functional/api/v1/test_magnum_service.py index 9f2c44bcdb..56f8bf0035 100644 --- a/magnum/tests/functional/api/v1/test_magnum_service.py +++ b/magnum/tests/functional/api/v1/test_magnum_service.py @@ -15,7 +15,6 @@ from tempest_lib import exceptions import testtools import unittest -from magnum.tests.functional.api.v1.clients import magnum_service_client as cli from magnum.tests.functional.common import base @@ -23,6 +22,10 @@ class MagnumServiceTest(base.BaseMagnumTest): """Tests for magnum-service .""" + def __init__(self, *args, **kwargs): + super(MagnumServiceTest, self).__init__(*args, **kwargs) + self.service_client = None + # NOTE(suro-patz): The following test fails in lieu of non-admin user # available for functional-test-gate. # For now, I will keep the test, but skipped. @@ -30,14 +33,19 @@ class MagnumServiceTest(base.BaseMagnumTest): @testtools.testcase.attr('negative') def test_magnum_service_list_needs_admin(self): # Ensure that policy enforcement does not allow 'default' user - client = cli.MagnumServiceClient.as_user('default') - self.assertRaises(exceptions.ServerFault, client.magnum_service_list) + (self.service_client, _) = self.get_clients_with_isolated_creds( + type_of_creds='default', + request_type='service') + self.assertRaises(exceptions.ServerFault, + self.service_client.magnum_service_list) @testtools.testcase.attr('positive') def test_magnum_service_list(self): # get json object - client = cli.MagnumServiceClient.as_user('admin') - resp, msvcs = client.magnum_service_list() + (self.service_client, _) = self.get_clients_with_isolated_creds( + type_of_creds='admin', + request_type='service') + resp, msvcs = self.service_client.magnum_service_list() self.assertEqual(200, resp.status) # Note(suro-patz): Following code assumes that we have only # one service, magnum-conductor enabled, as of now. diff --git a/magnum/tests/functional/common/base.py b/magnum/tests/functional/common/base.py index 3f4e3b1b9b..c2216b3ce7 100644 --- a/magnum/tests/functional/common/base.py +++ b/magnum/tests/functional/common/base.py @@ -10,9 +10,14 @@ # License for the specific language governing permissions and limitations # under the License. +import inspect + +from tempest.common import credentials_factory as common_creds +from tempest.common import dynamic_creds from tempest_lib import base from magnum.tests.functional.common import config +from magnum.tests.functional.common import manager class BaseMagnumTest(base.BaseTestCase): @@ -20,8 +25,63 @@ class BaseMagnumTest(base.BaseTestCase): def __init__(self, *args, **kwargs): super(BaseMagnumTest, self).__init__(*args, **kwargs) + self.ic = None @classmethod def setUpClass(cls): super(BaseMagnumTest, cls).setUpClass() config.Config.setUp() + + @classmethod + def tearDownClass(cls): + super(BaseMagnumTest, cls).tearDownClass() + + def tearDown(self): + super(BaseMagnumTest, self).tearDown() + if self.ic is not None: + self.ic.clear_creds() + + def get_clients_with_isolated_creds(self, + name=None, + type_of_creds="default", + request_type=None): + """Creates isolated creds. + + :param name: name, will be used for dynamic creds + :param type_of_creds: admin, alt or default + :param request_type: baymodel or service + :returns: MagnumClient -- client with isolated creds. + :returns: KeypairClient -- allows for creating of keypairs + """ + if name is None: + # Get name of test method + name = inspect.stack()[1][3] + if len(name) > 32: + name = name[0:32] + + # Choose type of isolated creds + self.ic = dynamic_creds.DynamicCredentialProvider( + identity_version=config.Config.auth_version, + name=name, + admin_role=config.Config.admin_role, + admin_creds=common_creds.get_configured_credentials( + 'identity_admin')) + if "admin" == type_of_creds: + creds = self.ic.get_admin_creds() + manager_inst = manager.AdminManager(credentials=creds, + request_type=request_type) + elif "alt" == type_of_creds: + creds = self.ic.get_alt_creds() + manager_inst = manager.AltManager(credentials=creds, + request_type=request_type) + elif "default" == type_of_creds: + creds = self.ic.get_primary_creds() + manager_inst = manager.DefaultManager(credentials=creds, + request_type=request_type) + else: + creds = self.ic.self.get_credentials(type_of_creds) + manager_inst = manager.DefaultManager(credentials=creds, + request_type=request_type) + + # create client with isolated creds + return (manager_inst.client, manager_inst.keypairs_client) diff --git a/magnum/tests/functional/common/client.py b/magnum/tests/functional/common/client.py index 23bbac9760..0e640a4bfe 100644 --- a/magnum/tests/functional/common/client.py +++ b/magnum/tests/functional/common/client.py @@ -17,82 +17,23 @@ from six.moves.urllib import parse from tempest_lib.common import rest_client from magnum.tests.functional.common import config -from magnum.tests.functional.common import manager -from magnum.tests.functional.common.utils import memoized @six.add_metaclass(abc.ABCMeta) -class BaseMagnumClient(rest_client.RestClient): +class MagnumClient(rest_client.RestClient): """Abstract class responsible for setting up auth provider""" - def __init__(self): - super(BaseMagnumClient, self).__init__( - auth_provider=self.get_auth_provider(), + def __init__(self, auth_provider): + super(MagnumClient, self).__init__( + auth_provider=auth_provider, service='container', region=config.Config.region ) - @abc.abstractmethod - def get_auth_provider(self): - pass - - -class MagnumClient(BaseMagnumClient): - """Responsible for setting up auth provider for default user""" - - def get_auth_provider(self): - mgr = manager.Manager() - return mgr.get_auth_provider( - username=config.Config.user, - password=config.Config.passwd, - tenant_name=config.Config.tenant - ) - - -class AdminMagnumClient(BaseMagnumClient): - """Responsible for setting up auth provider for admin user""" - - def get_auth_provider(self): - mgr = manager.Manager() - return mgr.get_auth_provider( - username=config.Config.admin_user, - password=config.Config.admin_passwd, - tenant_name=config.Config.admin_tenant - ) - - -class ClientMixin(object): - """Responsible for mapping setting up common client use cases: - - - deserializing response data to a model - - mapping user requests to a specific client for authentication - - generating request URLs - """ - - @classmethod - @memoized - def get_clients(cls): - return { - 'default': MagnumClient(), - 'admin': AdminMagnumClient(), - } - - def __init__(self, client): - self.client = client - @classmethod def deserialize(cls, resp, body, model_type): return resp, model_type.from_json(body) - @classmethod - def as_user(cls, user): - """Retrieves Magnum client based on user parameter - - :param user: type of user ('default') - :returns: a class that maps to user type in get_clients dict - """ - return cls(cls.get_clients()[user]) - @property def tenant_id(self): return self.client.tenant_id diff --git a/magnum/tests/functional/common/config.py b/magnum/tests/functional/common/config.py index 22b36a9738..1568716444 100644 --- a/magnum/tests/functional/common/config.py +++ b/magnum/tests/functional/common/config.py @@ -48,6 +48,14 @@ class Config(object): raise Exception('config missing auth_url key') cls.auth_url = CONF.identity.uri + @classmethod + def set_admin_role(cls, config): + # admin_role for client authentication + if cls.auth_version == 'v3': + cls.admin_role = CONF.identity.admin_role + else: + cls.admin_role = 'admin' + @classmethod def set_region(cls, config): if 'region' in CONF.identity: @@ -79,6 +87,7 @@ class Config(object): cls.set_user_creds(config) cls.set_auth_version(config) cls.set_auth_url(config) + cls.set_admin_role(config) cls.set_region(config) cls.set_image_id(config) diff --git a/magnum/tests/functional/common/manager.py b/magnum/tests/functional/common/manager.py index 2f77d9507d..d46bcffdc1 100644 --- a/magnum/tests/functional/common/manager.py +++ b/magnum/tests/functional/common/manager.py @@ -10,53 +10,40 @@ # License for the specific language governing permissions and limitations # under the License. -from tempest_lib import auth -from tempest_lib import exceptions +from tempest import clients +from tempest.common import credentials_factory as common_creds -import magnum.tests.functional.common.config as config +from magnum.tests.functional.api.v1.clients import baymodel_client +from magnum.tests.functional.api.v1.clients import magnum_service_client +from magnum.tests.functional.common import client -class Manager(object): - - """Responsible for providing an auth provider object""" - - def _get_auth_credentials(self, auth_version, **credentials): - """Retrieves auth credentials based on passed in creds and version - - :param auth_version: auth version ('v2' or 'v3') - :param credentials: credentials dict to validate against - :returns: credentials object - """ - - if credentials is None: - raise exceptions.InvalidCredentials( - 'Credentials must be specified') - if auth_version == 'v3': - return auth.KeystoneV3Credentials(**credentials) - elif auth_version == 'v2': - return auth.KeystoneV2Credentials(**credentials) +class Manager(clients.Manager): + def __init__( + self, + credentials=common_creds.get_configured_credentials( + 'identity_admin'), + request_type=None): + super(Manager, self).__init__(credentials, 'container') + if request_type == 'baymodel': + self.client = baymodel_client.BayModelClient(self.auth_provider) + elif request_type == 'service': + self.client = magnum_service_client.MagnumServiceClient( + self.auth_provider) else: - raise exceptions.InvalidCredentials('Specify identity version') + self.client = client.MagnumClient(self.auth_provider) - def get_auth_provider(self, **credentials): - """Validates credentials and returns auth provider - Auth provider will contain required security context to pass to magnum +class DefaultManager(Manager): + def __init__(self, credentials, request_type=None): + super(DefaultManager, self).__init__(credentials, request_type) - :param credentials: credentials dict to validate against - :returns: auth provider object - """ - auth_version = config.Config.auth_version - creds = self._get_auth_credentials(auth_version, **credentials) - if auth_version == 'v3': - auth_provider = auth.KeystoneV3AuthProvider( - creds, config.Config.auth_url) - elif auth_version == 'v2': - auth_provider = auth.KeystoneV2AuthProvider( - creds, config.Config.auth_url) - else: - raise exceptions.InvalidCredentials('Specify identity version') +class AltManager(Manager): + def __init__(self, credentials, request_type=None): + super(AltManager, self).__init__(credentials, request_type) - auth_provider.fill_credentials() - return auth_provider + +class AdminManager(Manager): + def __init__(self, credentials, request_type=None): + super(AdminManager, self).__init__(credentials, request_type)