diff --git a/rally/exceptions.py b/rally/exceptions.py index 8fcb2acbbd..df739466ed 100644 --- a/rally/exceptions.py +++ b/rally/exceptions.py @@ -158,7 +158,7 @@ class ChecksumMismatch(RallyException): class InvalidAdminException(InvalidArgumentsException): - msg_fmt = _("user %(username)s doesn't have 'admin' role") + msg_fmt = _("user '%(username)s' doesn't have 'admin' role") class InvalidEndpointsException(InvalidArgumentsException): diff --git a/rally/osclients.py b/rally/osclients.py index 367f0c1732..6a7803610a 100644 --- a/rally/osclients.py +++ b/rally/osclients.py @@ -142,59 +142,25 @@ class OSClient(plugin.Plugin): raise exceptions.RallyException(_( "Setting service type is not supported.")) - def keystone(self, *args, **kwargs): - """Make a call to keystone client.""" - keystone = OSClient.get("keystone")(self.credential, self.api_info, - self.cache) - return keystone(*args, **kwargs) + @property + def keystone(self): + return OSClient.get("keystone")(self.credential, self.api_info, + self.cache) def _get_session(self, auth_url=None, version=None): - from keystoneauth1 import discover - from keystoneauth1 import session - from keystoneclient.auth import identity - - password_args = { - "auth_url": auth_url or self.credential.auth_url, - "username": self.credential.username, - "password": self.credential.password, - "tenant_name": self.credential.tenant_name - } - - version = OSClient.get("keystone")( - self.credential, self.api_info, self.cache).choose_version(version) - if version is None: - # NOTE(rvasilets): If version not specified than we discover - # available version with the smallest number. To be able to - # discover versions we need session - temp_session = session.Session( - verify=( - self.credential.cacert or not self.credential.insecure), - timeout=CONF.openstack_client_http_timeout) - version = str(discover.Discover( - temp_session, - password_args["auth_url"]).version_data()[0]["version"][0]) - - if "v2.0" not in password_args["auth_url"] and ( - version != "2"): - password_args.update({ - "user_domain_name": self.credential.user_domain_name, - "domain_name": self.credential.domain_name, - "project_domain_name": self.credential.project_domain_name, - }) - identity_plugin = identity.Password(**password_args) - sess = session.Session( - auth=identity_plugin, verify=( - self.credential.cacert or not self.credential.insecure), - timeout=CONF.openstack_client_http_timeout) - return sess, identity_plugin + import warnings + warnings.warn( + "Method `rally.osclient.OSClient._get_session` is deprecated since" + " Rally 0.6.0. Use " + "`rally.osclient.OSClient.keystone.get_session` instead.") + return self.keystone.get_session(version) def _get_endpoint(self, service_type=None): - kc = self.keystone() kw = {"service_type": self.choose_service_type(service_type), "region_name": self.credential.region_name} if self.credential.endpoint_type: - kw["endpoint_type"] = self.credential.endpoint_type - api_url = kc.service_catalog.url_for(**kw) + kw["interface"] = self.credential.endpoint_type + api_url = self.keystone.service_catalog.url_for(**kw) return api_url def _get_auth_info(self, user_key="username", @@ -250,10 +216,68 @@ class OSClient(plugin.Plugin): @configure("keystone", supported_versions=("2", "3")) class Keystone(OSClient): - def keystone(self, *args, **kwargs): + @property + def keystone(self): raise exceptions.RallyException(_("Method 'keystone' is restricted " "for keystoneclient. :)")) + @property + def service_catalog(self): + return self.auth_ref.service_catalog + + @property + def auth_ref(self): + if "keystone_auth_ref" not in self.cache: + sess, plugin = self.get_session() + self.cache["keystone_auth_ref"] = plugin.get_access(sess) + return self.cache["keystone_auth_ref"] + + def get_session(self, version=None): + key = "keystone_session_and_plugin_%s" % version + if key not in self.cache: + from keystoneauth1 import discover + from keystoneauth1 import identity + from keystoneauth1 import session + + version = self.choose_version(version) + auth_url = self.credential.auth_url + if version is not None: + auth_url = self._remove_url_version() + + password_args = { + "auth_url": auth_url, + "username": self.credential.username, + "password": self.credential.password, + "tenant_name": self.credential.tenant_name + } + + if version is None: + # NOTE(rvasilets): If version not specified than we discover + # available version with the smallest number. To be able to + # discover versions we need session + temp_session = session.Session( + verify=(self.credential.cacert or + not self.credential.insecure), + timeout=CONF.openstack_client_http_timeout) + version = str(discover.Discover( + temp_session, + password_args["auth_url"]).version_data()[0]["version"][0]) + + if "v2.0" not in password_args["auth_url"] and ( + version != "2"): + password_args.update({ + "user_domain_name": self.credential.user_domain_name, + "domain_name": self.credential.domain_name, + "project_domain_name": self.credential.project_domain_name, + }) + identity_plugin = identity.Password(**password_args) + sess = session.Session( + auth=identity_plugin, verify=( + self.credential.cacert or not self.credential.insecure), + timeout=CONF.openstack_client_http_timeout) + self.cache[key] = (sess, identity_plugin) + return self.cache[key] + def _remove_url_version(self): """Remove any version from the auth_url. @@ -282,36 +306,21 @@ class Keystone(OSClient): # Use the version in the api_info if provided, otherwise fall # back to the passed version (which may be None, in which case # keystoneclient chooses). - version = self.choose_version(version) - auth_url = self.credential.auth_url - if version is not None: - auth_url = self._remove_url_version() - sess, plugin = self._get_session(auth_url=auth_url, version=version) - # NOTE(bigjools): When using sessions, keystoneclient no longer - # does any pre-auth and calling client.authenticate() with - # sessions is deprecated (it's still possible to call it but if - # endpoint is defined it'll crash). We're forcing that pre-auth - # here because the use of the service_catalog depends on doing - # this. Also note that while the API has got the - # endpoints.list() equivalent, there is no service_type in that - # list which is why we need to ensure service_catalog is still - # present. - auth_ref = plugin.get_access(sess) + sess = self.get_session(version=version)[0] + kw = {"version": version, "session": sess, "timeout": CONF.openstack_client_http_timeout} if keystoneclient.__version__[0] == "1": # NOTE(andreykurilin): let's leave this hack for envs which uses # old(<2.0.0) keystoneclient version. Upstream fix: # https://github.com/openstack/python-keystoneclient/commit/d9031c252848d89270a543b67109a46f9c505c86 - from keystoneclient import base - kw["auth_url"] = sess.get_endpoint(interface=base.AUTH_INTERFACE) + from keystoneauth1 import plugin + kw["auth_url"] = sess.get_endpoint(interface=plugin.AUTH_INTERFACE) if self.credential.endpoint_type: kw["endpoint_type"] = self.credential.endpoint_type - ks = client.Client(**kw) - ks.auth_ref = auth_ref - return ks + return client.Client(**kw) @configure("nova", default_version="2", default_service_type="compute") @@ -331,9 +340,8 @@ class Nova(OSClient): """Return nova client.""" from novaclient import client as nova - kc = self.keystone() client = nova.Client(self.choose_version(version), - auth_token=kc.auth_token, + auth_token=self.keystone.auth_ref.auth_token, http_log_debug=logging.is_debug(), timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, @@ -349,9 +357,8 @@ class Neutron(OSClient): """Return neutron client.""" from neutronclient.neutron import client as neutron - kc = self.keystone() client = neutron.Client(self.choose_version(version), - token=kc.auth_token, + token=self.keystone.auth_ref.auth_token, endpoint_url=self._get_endpoint(service_type), timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, @@ -368,10 +375,9 @@ class Glance(OSClient): """Return glance client.""" import glanceclient as glance - kc = self.keystone() client = glance.Client(self.choose_version(version), endpoint=self._get_endpoint(service_type), - token=kc.auth_token, + token=self.keystone.auth_ref.auth_token, timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, cacert=self.credential.cacert) @@ -385,10 +391,9 @@ class Heat(OSClient): """Return heat client.""" from heatclient import client as heat - kc = self.keystone() client = heat.Client(self.choose_version(version), endpoint=self._get_endpoint(service_type), - token=kc.auth_token, + token=self.keystone.auth_ref.auth_token, timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, **self._get_auth_info(project_name_key=None, @@ -408,9 +413,8 @@ class Cinder(OSClient): timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, **self._get_auth_info(password_key="api_key")) - kc = self.keystone() client.client.management_url = self._get_endpoint(service_type) - client.client.auth_token = kc.auth_token + client.client.auth_token = self.keystone.auth_ref.auth_token return client @@ -428,9 +432,8 @@ class Manila(OSClient): insecure=self.credential.insecure, **self._get_auth_info(password_key="api_key", project_name_key="project_name")) - kc = self.keystone() manila_client.client.management_url = self._get_endpoint(service_type) - manila_client.client.auth_token = kc.auth_token + manila_client.client.auth_token = self.keystone.auth_ref.auth_token return manila_client @@ -441,16 +444,10 @@ class Ceilometer(OSClient): """Return ceilometer client.""" from ceilometerclient import client as ceilometer - kc = self.keystone() - auth_token = kc.auth_token - if not hasattr(auth_token, "__call__"): - # python-ceilometerclient requires auth_token to be a callable - auth_token = lambda: kc.auth_token - client = ceilometer.get_client( self.choose_version(version), os_endpoint=self._get_endpoint(service_type), - token=auth_token, + token=self.keystone.auth_ref.auth_token, timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, **self._get_auth_info(project_name_key="tenant_name", @@ -469,7 +466,7 @@ class Gnocchi(OSClient): from gnocchiclient import client as gnocchi service_type = self.choose_service_type(service_type) - sess = self._get_session()[0] + sess = self.keystone.get_session()[0] gclient = gnocchi.Client(version=self.choose_version( version), session=sess, service_type=service_type) return gclient @@ -483,9 +480,9 @@ class Ironic(OSClient): """Return Ironic client.""" from ironicclient import client as ironic - kc = self.keystone() + auth_token = self.keystone.auth_ref.auth_token client = ironic.get_client(self.choose_version(version), - os_auth_token=kc.auth_token, + os_auth_token=auth_token, ironic_url=self._get_endpoint(service_type), timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, @@ -534,12 +531,12 @@ class Zaqar(OSClient): def create_client(self, version=None, service_type=None): """Return Zaqar client.""" from zaqarclient.queues import client as zaqar - kc = self.keystone() + tenant_id = self.keystone.auth_ref.get("token").get("tenant").get("id") conf = {"auth_opts": {"backend": "keystone", "options": { "os_username": self.credential.username, "os_password": self.credential.password, "os_project_name": self.credential.tenant_name, - "os_project_id": kc.auth_ref.get("token").get("tenant").get("id"), + "os_project_id": tenant_id, "os_auth_url": self.credential.auth_url, "insecure": self.credential.insecure, }}} @@ -557,10 +554,9 @@ class Murano(OSClient): """Return Murano client.""" from muranoclient import client as murano - kc = self.keystone() client = murano.Client(self.choose_version(version), endpoint=self._get_endpoint(service_type), - token=kc.auth_token) + token=self.keystone.auth_ref.auth_token) return client @@ -577,7 +573,7 @@ class Designate(OSClient): api_url = self._get_endpoint(service_type) api_url += "/v%s" % version - session = self._get_session()[0] + session = self.keystone.get_session()[0] if version == "2": return client.Client(version, session=session, endpoint_override=api_url) @@ -606,11 +602,10 @@ class Mistral(OSClient): """Return Mistral client.""" from mistralclient.api import client as mistral - kc = self.keystone() client = mistral.client( mistral_url=self._get_endpoint(service_type), service_type=self.choose_service_type(service_type), - auth_token=kc.auth_token) + auth_token=self.keystone.auth_ref.auth_token) return client @@ -620,10 +615,10 @@ class Swift(OSClient): """Return swift client.""" from swiftclient import client as swift - kc = self.keystone() + auth_token = self.keystone.auth_ref.auth_token client = swift.Connection(retries=1, preauthurl=self._get_endpoint(service_type), - preauthtoken=kc.auth_token, + preauthtoken=auth_token, insecure=self.credential.insecure, cacert=self.credential.cacert, user=self.credential.username, @@ -639,6 +634,7 @@ class EC2(OSClient): import boto kc = self.keystone() + if kc.version != "v2.0": raise exceptions.RallyException( _("Rally EC2 benchmark currently supports only" @@ -660,12 +656,10 @@ class Monasca(OSClient): """Return monasca client.""" from monascaclient import client as monasca - kc = self.keystone() - auth_token = kc.auth_token client = monasca.Client( self.choose_version(version), self._get_endpoint(service_type), - token=auth_token, + token=self.keystone.auth_ref.auth_token, timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, **self._get_auth_info(project_name_key="tenant_name")) @@ -682,7 +676,7 @@ class Cue(OSClient): api_url = self._get_endpoint(service_type) api_url += "v%s" % version - session = self._get_session(auth_url=api_url)[0] + session = self.keystone.get_session()[0] endpoint_type = self.credential.endpoint_type return cue.Client(session=session, interface=endpoint_type) @@ -710,7 +704,7 @@ class Magnum(OSClient): from magnumclient import client as magnum api_url = self._get_endpoint(service_type) - session = self._get_session()[0] + session = self.keystone.get_session()[0] return magnum.Client( session=session, @@ -724,13 +718,12 @@ class Watcher(OSClient): def create_client(self, version=None, service_type=None): """Return watcher client.""" from watcherclient import client as watcher_client - kc = self.keystone() watcher_api_url = self._get_endpoint( self.choose_service_type(service_type)) client = watcher_client.Client( self.choose_version(version), watcher_api_url, - token=kc.auth_token, + token=self.keystone.auth_ref.auth_token, timeout=CONF.openstack_client_http_timeout, insecure=self.credential.insecure, ca_file=self.credential.cacert, @@ -781,9 +774,8 @@ class Clients(object): from keystoneclient import exceptions as keystone_exceptions try: # Ensure that user is admin - client = self.keystone() if "admin" not in [role.lower() for role in - client.auth_ref.role_names]: + self.keystone.auth_ref.role_names]: raise exceptions.InvalidAdminException( username=self.credential.username) except keystone_exceptions.Unauthorized: @@ -791,7 +783,7 @@ class Clients(object): except keystone_exceptions.AuthorizationFailure: raise exceptions.HostUnreachableException( url=self.credential.auth_url) - return client + return self.keystone() def services(self): """Return available services names and types. @@ -800,8 +792,7 @@ class Clients(object): """ if "services_data" not in self.cache: services_data = {} - ks = self.keystone() - available_services = ks.service_catalog.get_endpoints() + available_services = self.keystone.service_catalog.get_endpoints() for stype in available_services.keys(): if stype in consts.ServiceType: services_data[stype] = consts.ServiceType[stype] diff --git a/rally/plugins/openstack/context/api_versions.py b/rally/plugins/openstack/context/api_versions.py index e680682e0a..623a66553d 100644 --- a/rally/plugins/openstack/context/api_versions.py +++ b/rally/plugins/openstack/context/api_versions.py @@ -191,7 +191,7 @@ class OpenStackAPIVersions(context.Context): self.context.get("admin", {}).get("credential")) clients = osclients.Clients(random.choice( self.context["users"])["credential"]) - services = clients.keystone().service_catalog.get_endpoints() + services = clients.keystone.service_catalog.get_endpoints() services_from_admin = None for client_name, conf in six.iteritems(self.config): if "service_type" in conf and conf["service_type"] not in services: diff --git a/rally/plugins/openstack/context/keystone/existing_users.py b/rally/plugins/openstack/context/keystone/existing_users.py index f83c7464cb..100add281d 100644 --- a/rally/plugins/openstack/context/keystone/existing_users.py +++ b/rally/plugins/openstack/context/keystone/existing_users.py @@ -50,17 +50,15 @@ class ExistingUsers(users.UserContextMixin, context.Context): for user in self.config: user_credential = objects.Credential(**user) - user_kclient = osclients.Clients(user_credential).keystone() + user_clients = osclients.Clients(user_credential) - user_name = user_kclient.username - tenant_name = user_kclient.project_name - user_id = user_kclient.get_user_id(user_name) - tenant_id = user_kclient.get_project_id(tenant_name) + user_id = user_clients.keystone.auth_ref.user_id + tenant_id = user_clients.keystone.auth_ref.project_id if tenant_id not in self.context["tenants"]: self.context["tenants"][tenant_id] = { "id": tenant_id, - "name": tenant_name + "name": user_credential.tenant_name } self.context["users"].append({ diff --git a/tests/unit/plugins/openstack/context/keystone/test_existing_users.py b/tests/unit/plugins/openstack/context/keystone/test_existing_users.py index d97655e971..4d19b97c0b 100644 --- a/tests/unit/plugins/openstack/context/keystone/test_existing_users.py +++ b/tests/unit/plugins/openstack/context/keystone/test_existing_users.py @@ -26,18 +26,30 @@ class ExistingUserTestCase(test.TestCase): @mock.patch("%s.keystone.existing_users.objects.Credential" % CTX) def test_setup(self, mock_credential, mock_clients): user1 = mock.MagicMock(tenant_id="1", user_id="1", - project_name="proj", username="usr") + tenant_name="proj", username="usr") user2 = mock.MagicMock(tenant_id="1", user_id="2", - project_name="proj", username="usr") + tenant_name="proj", username="usr") user3 = mock.MagicMock(tenant_id="2", user_id="3", - project_name="proj", username="usr") + tenant_name="proj", username="usr") user_list = [user1, user2, user3] - for u in user_list: - u.get_user_id.return_value = u.user_id - u.get_project_id.return_value = u.tenant_id - mock_clients.return_value.keystone.side_effect = user_list + class AuthRef(object): + USER_ID_COUNT = 0 + PROJECT_ID_COUNT = 0 + + @property + def user_id(self): + self.USER_ID_COUNT += 1 + return user_list[self.USER_ID_COUNT - 1].user_id + + @property + def project_id(self): + self.PROJECT_ID_COUNT += 1 + return user_list[self.PROJECT_ID_COUNT - 1].tenant_id + + mock_clients.return_value.keystone.auth_ref = AuthRef() + mock_credential.side_effect = user_list context = { "task": mock.MagicMock(), @@ -53,15 +65,15 @@ class ExistingUserTestCase(test.TestCase): self.assertEqual( { "id": user1.user_id, - "credential": mock_credential.return_value, + "credential": user1, "tenant_id": user1.tenant_id }, context["users"][0] ) self.assertEqual(["1", "2"], sorted(context["tenants"].keys())) - self.assertEqual({"id": "1", "name": user1.project_name}, + self.assertEqual({"id": "1", "name": user1.tenant_name}, context["tenants"]["1"]) - self.assertEqual({"id": "2", "name": user3.project_name}, + self.assertEqual({"id": "2", "name": user3.tenant_name}, context["tenants"]["2"]) def test_cleanup(self): diff --git a/tests/unit/plugins/openstack/context/test_api_versions.py b/tests/unit/plugins/openstack/context/test_api_versions.py index 3441d2b7d0..e935392d09 100644 --- a/tests/unit/plugins/openstack/context/test_api_versions.py +++ b/tests/unit/plugins/openstack/context/test_api_versions.py @@ -24,8 +24,10 @@ class OpenStackServicesTestCase(test.TestCase): def setUp(self): super(OpenStackServicesTestCase, self).setUp() self.mock_clients = mock.patch("rally.osclients.Clients").start() - self.mock_kc = self.mock_clients.return_value.keystone.return_value - self.mock_kc.service_catalog.get_endpoints.return_value = [] + osclient_kc = self.mock_clients.return_value.keystone + self.mock_kc = osclient_kc.return_value + self.service_catalog = osclient_kc.service_catalog + self.service_catalog.get_endpoints.return_value = [] self.mock_kc.services.list.return_value = [] def test_validate_correct_config(self): @@ -85,7 +87,7 @@ class OpenStackServicesTestCase(test.TestCase): "users": [{"credential": mock.MagicMock()}]} ctx = api_versions.OpenStackAPIVersions(context) self.assertRaises(exceptions.ValidationError, ctx.setup) - self.mock_kc.service_catalog.get_endpoints.assert_called_once_with() + self.service_catalog.get_endpoints.assert_called_once_with() self.mock_kc.services.list.assert_called_once_with() def test_setup_with_wrong_service_name_and_without_admin(self): @@ -95,7 +97,7 @@ class OpenStackServicesTestCase(test.TestCase): "users": [{"credential": mock.MagicMock()}]} ctx = api_versions.OpenStackAPIVersions(context) self.assertRaises(exceptions.BenchmarkSetupFailure, ctx.setup) - self.mock_kc.service_catalog.get_endpoints.assert_called_once_with() + self.service_catalog.get_endpoints.assert_called_once_with() self.assertFalse(self.mock_kc.services.list.called) def test_setup_with_wrong_service_type(self): @@ -105,7 +107,7 @@ class OpenStackServicesTestCase(test.TestCase): "users": [{"credential": mock.MagicMock()}]} ctx = api_versions.OpenStackAPIVersions(context) self.assertRaises(exceptions.ValidationError, ctx.setup) - self.mock_kc.service_catalog.get_endpoints.assert_called_once_with() + self.service_catalog.get_endpoints.assert_called_once_with() def test_setup_with_service_name(self): self.mock_kc.services.list.return_value = [ @@ -118,7 +120,7 @@ class OpenStackServicesTestCase(test.TestCase): ctx = api_versions.OpenStackAPIVersions(context) ctx.setup() - self.mock_kc.service_catalog.get_endpoints.assert_called_once_with() + self.service_catalog.get_endpoints.assert_called_once_with() self.mock_kc.services.list.assert_called_once_with() self.assertEqual( diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 3f7ea36f5a..e7c45ec0dd 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -377,8 +377,10 @@ class DeploymentAPITestCase(BaseDeploymentTestCase): for key in self.deployment: self.assertEqual(ret[key], self.deployment[key]) + @mock.patch("rally.osclients.Clients.services") @mock.patch("rally.osclients.Keystone.create_client") - def test_deployment_check(self, mock_keystone_create_client): + def test_deployment_check(self, mock_keystone_create_client, + mock_clients_services): sample_credential = objects.Credential("http://192.168.1.1:5000/v2.0/", "admin", "adminpass").to_dict() @@ -386,6 +388,7 @@ class DeploymentAPITestCase(BaseDeploymentTestCase): "users": [sample_credential]} api.Deployment.check(deployment) mock_keystone_create_client.assert_called_with() + mock_clients_services.assert_called_once_with() def test_deployment_check_raise(self): sample_credential = objects.Credential("http://192.168.1.1:5000/v2.0/", diff --git a/tests/unit/test_osclients.py b/tests/unit/test_osclients.py index dc099abbf9..d2b576ba84 100644 --- a/tests/unit/test_osclients.py +++ b/tests/unit/test_osclients.py @@ -37,23 +37,20 @@ class OSClientTestCaseUtils(object): def set_up_keystone_mocks(self): self.ksc_module = mock.MagicMock(__version__="2.0.0") self.ksc_client = mock.MagicMock() - self.ksc_identity = mock.MagicMock() - self.ksc_identity_plugin = mock.MagicMock() - self.ksc_password = mock.MagicMock( - return_value=self.ksc_identity_plugin) - self.ksc_auth = mock.MagicMock() + self.ksa_identity_plugin = mock.MagicMock() + self.ksa_password = mock.MagicMock( + return_value=self.ksa_identity_plugin) + self.ksa_identity = mock.MagicMock(Password=self.ksa_password) self.ksa_auth = mock.MagicMock() self.ksa_session = mock.MagicMock() self.patcher = mock.patch.dict("sys.modules", {"keystoneclient": self.ksc_module, - "keystoneclient.auth": self.ksc_auth, "keystoneauth1": self.ksa_auth}) self.patcher.start() self.addCleanup(self.patcher.stop) self.ksc_module.client = self.ksc_client - self.ksc_auth.identity = self.ksc_identity - self.ksc_auth.identity.Password = self.ksc_password + self.ksa_auth.identity = self.ksa_identity self.ksa_auth.session = self.ksa_session def make_auth_args(self): @@ -86,70 +83,43 @@ class OSClientTestCase(test.TestCase, OSClientTestCaseUtils): self.assertEqual("foo", fake_client.choose_service_type("foo")) - @ddt.data( - {"auth_url": "http://auth_url/v2.0"}, - {"auth_url": "http://auth_url/v3"}, - {"auth_url": "http://auth_url/"}, - {"auth_url": None}, - ) - @ddt.unpack - def test__get_session(self, auth_url): - credential = objects.Credential("http://auth_url/v2.0", "user", - "pass", "tenant") - mock_keystoneauth1 = mock.MagicMock() - self.set_up_keystone_mocks() - osclient = osclients.OSClient(credential, {}, mock.MagicMock()) - with mock.patch.dict( - "sys.modules", { - "keystoneauth1": mock_keystoneauth1}): - mock_keystoneauth1.discover.Discover.return_value = ( - mock.Mock(version_data=mock.Mock(return_value=[ - {"version": (1, 0)}])) - ) - osclient._get_session(auth_url=auth_url) - if auth_url == "http://auth_url/v2.0": - self.ksc_password.assert_called_once_with( - auth_url=auth_url, password="pass", - tenant_name="tenant", username="user") - elif auth_url is None: - self.ksc_password.assert_called_once_with( - auth_url="http://auth_url/v2.0", password="pass", - tenant_name="tenant", username="user") - else: - self.ksc_password.assert_called_once_with( - auth_url=auth_url, password="pass", - tenant_name="tenant", username="user", - domain_name=None, project_domain_name=None, - user_domain_name=None) - mock_keystoneauth1.session.Session.assert_has_calls( - [mock.call(timeout=180.0, verify=True), - mock.call(auth=self.ksc_identity_plugin, timeout=180.0, - verify=True)]) - + @mock.patch("rally.osclients.Keystone.service_catalog") @ddt.data( {"endpoint_type": None, "service_type": None, "region_name": None}, {"endpoint_type": "et", "service_type": "st", "region_name": "rn"} ) @ddt.unpack - def test__get_endpoint(self, endpoint_type, service_type, region_name): + def test__get_endpoint(self, mock_keystone_service_catalog, endpoint_type, + service_type, region_name): credential = objects.Credential("http://auth_url/v2.0", "user", "pass", endpoint_type=endpoint_type, region_name=region_name) mock_choose_service_type = mock.MagicMock() osclient = osclients.OSClient(credential, {}, mock.MagicMock()) osclient.choose_service_type = mock_choose_service_type - with mock.patch.object(osclient, "keystone") as ks: - mock_url_for = ks.return_value.service_catalog.url_for - self.assertEqual(mock_url_for.return_value, - osclient._get_endpoint(service_type)) - call_args = { - "service_type": mock_choose_service_type.return_value, - "region_name": region_name} - if endpoint_type: - call_args["endpoint_type"] = endpoint_type - mock_url_for.assert_called_once_with(**call_args) + mock_url_for = mock_keystone_service_catalog.url_for + self.assertEqual(mock_url_for.return_value, + osclient._get_endpoint(service_type)) + call_args = { + "service_type": mock_choose_service_type.return_value, + "region_name": region_name} + if endpoint_type: + call_args["interface"] = endpoint_type + mock_url_for.assert_called_once_with(**call_args) mock_choose_service_type.assert_called_once_with(service_type) + @mock.patch("rally.osclients.Keystone.get_session") + def test__get_session(self, mock_keystone_get_session): + osclient = osclients.OSClient(None, None, None) + auth_url = "auth_url" + version = "version" + import warnings + with mock.patch.object(warnings, "warn") as mock_warn: + self.assertEqual(mock_keystone_get_session.return_value, + osclient._get_session(auth_url, version)) + self.assertTrue(mock_warn.called) + mock_keystone_get_session.assert_called_once_with(version) + class CachedTestCase(test.TestCase): @@ -178,6 +148,7 @@ class CachedTestCase(test.TestCase): self.assertEqual({}, clients.cache) +@ddt.ddt class TestCreateKeystoneClient(test.TestCase, OSClientTestCaseUtils): def setUp(self): @@ -195,10 +166,8 @@ class TestCreateKeystoneClient(test.TestCase, OSClientTestCaseUtils): # keystone guys. self.set_up_keystone_mocks() keystone = osclients.Keystone(self.credential, {}, mock.MagicMock()) - keystone._get_session = mock.Mock( - return_value=(self.ksa_session, self.ksc_identity_plugin,)) - self.ksc_identity_plugin.get_access = mock.Mock( - return_value="fake_auth_ref") + keystone.get_session = mock.Mock( + return_value=(self.ksa_session, self.ksa_identity_plugin,)) client = keystone.create_client(version=3) kwargs_session = self.credential.to_dict() @@ -206,19 +175,10 @@ class TestCreateKeystoneClient(test.TestCase, OSClientTestCaseUtils): "auth_url": "http://auth_url/", "session": self.ksa_session, "timeout": 180.0}) - keystone._get_session.assert_called_once_with( - auth_url="http://auth_url/", version="3") - self.ksc_identity_plugin.get_access.assert_called_once_with( - self.ksa_session) + keystone.get_session.assert_called_once_with(version="3") self.ksc_client.Client.assert_called_once_with( session=self.ksa_session, timeout=180.0, version="3") self.assertIs(client, self.ksc_client.Client()) - self.assertEqual("fake_auth_ref", self.ksc_client.Client().auth_ref) - # The client needs to be pre-authed so that service_catalog - # works. This is because when using sessions, lazy auth is done - # in keystoneclient. - auth_ref = getattr(client, "auth_ref", None) - self.assertIsNot(auth_ref, None) def test_create_client_removes_url_path_if_version_specified(self): # If specifying a version on the client creation call, ensure @@ -228,8 +188,8 @@ class TestCreateKeystoneClient(test.TestCase, OSClientTestCaseUtils): auth_kwargs, all_kwargs = self.make_auth_args() keystone = osclients.Keystone( self.credential, {}, mock.MagicMock()) - keystone._get_session = mock.Mock( - return_value=(self.ksa_session, self.ksc_identity_plugin,)) + keystone.get_session = mock.Mock( + return_value=(self.ksa_session, self.ksa_identity_plugin,)) client = keystone.create_client(version="3") self.assertIs(client, self.ksc_client.Client()) @@ -238,6 +198,57 @@ class TestCreateKeystoneClient(test.TestCase, OSClientTestCaseUtils): {"session": self.ksa_session, "timeout": 180.0, "version": "3"}, called_with) + @ddt.data("http://auth_url/v2.0", "http://auth_url/v3", + "http://auth_url/", "auth_url") + def test_keystone_get_session(self, auth_url): + credential = objects.Credential(auth_url, "user", + "pass", "tenant") + self.set_up_keystone_mocks() + keystone = osclients.Keystone(credential, {}, {}) + + version_data = mock.Mock(return_value=[{"version": (1, 0)}]) + self.ksa_auth.discover.Discover.return_value = ( + mock.Mock(version_data=version_data)) + + self.assertEqual((self.ksa_session.Session.return_value, + self.ksa_identity_plugin), + keystone.get_session()) + if auth_url.endswith("v2.0"): + self.ksa_password.assert_called_once_with( + auth_url=auth_url, password="pass", + tenant_name="tenant", username="user") + else: + self.ksa_password.assert_called_once_with( + auth_url=auth_url, password="pass", + tenant_name="tenant", username="user", + domain_name=None, project_domain_name=None, + user_domain_name=None) + self.ksa_session.Session.assert_has_calls( + [mock.call(timeout=180.0, verify=True), + mock.call(auth=self.ksa_identity_plugin, timeout=180.0, + verify=True)]) + + def test_keystone_property(self): + keystone = osclients.Keystone(None, None, None) + self.assertRaises(exceptions.RallyException, lambda: keystone.keystone) + + @mock.patch("rally.osclients.Keystone.get_session") + def test_auth_ref(self, mock_keystone_get_session): + session = mock.MagicMock() + auth_plugin = mock.MagicMock() + mock_keystone_get_session.return_value = (session, auth_plugin) + cache = {} + keystone = osclients.Keystone(None, None, cache) + + self.assertEqual(auth_plugin.get_access.return_value, + keystone.auth_ref) + self.assertEqual(auth_plugin.get_access.return_value, + cache["keystone_auth_ref"]) + + # check that auth_ref was cached. + keystone.auth_ref + mock_keystone_get_session.assert_called_once_with() + @ddt.ddt class OSClientsTestCase(test.TestCase): @@ -249,18 +260,17 @@ class OSClientsTestCase(test.TestCase): self.clients = osclients.Clients(self.credential, {}) self.fake_keystone = fakes.FakeKeystoneClient() - self.fake_keystone.auth_token = mock.MagicMock() - self.service_catalog = self.fake_keystone.service_catalog - self.service_catalog.url_for = mock.MagicMock() keystone_patcher = mock.patch( - "rally.osclients.Keystone.create_client") + "rally.osclients.Keystone.create_client", + return_value=self.fake_keystone) self.mock_create_keystone_client = keystone_patcher.start() - self.addCleanup(keystone_patcher.stop) - self.mock_create_keystone_client.return_value = self.fake_keystone - def tearDown(self): - super(OSClientsTestCase, self).tearDown() + self.auth_ref_patcher = mock.patch("rally.osclients.Keystone.auth_ref") + self.auth_ref = self.auth_ref_patcher.start() + + self.service_catalog = self.auth_ref.service_catalog + self.service_catalog.url_for = mock.MagicMock() def test_create_from_env(self): with mock.patch.dict("os.environ", @@ -288,28 +298,29 @@ class OSClientsTestCase(test.TestCase): self.mock_create_keystone_client.assert_called_once_with() self.assertEqual(self.fake_keystone, self.clients.cache["keystone"]) - @mock.patch("rally.osclients.Keystone.create_client") - def test_verified_keystone_user_not_admin(self, - mock_keystone_create_client): - # naming rule for mocks sucks - mock_keystone = mock_keystone_create_client - mock_keystone.return_value = fakes.FakeKeystoneClient() - mock_keystone.return_value.auth_ref.role_names = ["notadmin"] + def test_verified_keystone(self): + self.auth_ref.role_names = ["admin"] + self.assertEqual(self.mock_create_keystone_client.return_value, + self.clients.verified_keystone()) + + def test_verified_keystone_user_not_admin(self): + self.auth_ref.role_names = ["notadmin"] self.assertRaises(exceptions.InvalidAdminException, self.clients.verified_keystone) - @mock.patch("rally.osclients.Keystone.create_client") - def test_verified_keystone_unauthorized(self, mock_keystone_create_client): - mock_keystone_create_client.return_value = fakes.FakeKeystoneClient() - mock_keystone_create_client.side_effect = ( - keystone_exceptions.Unauthorized) + @mock.patch("rally.osclients.Keystone.get_session") + def test_verified_keystone_unauthorized(self, mock_keystone_get_session): + self.auth_ref_patcher.stop() + mock_keystone_get_session.side_effect = ( + keystone_exceptions.Unauthorized + ) self.assertRaises(exceptions.InvalidEndpointsException, self.clients.verified_keystone) - @mock.patch("rally.osclients.Keystone.create_client") - def test_verified_keystone_unreachable(self, mock_keystone_create_client): - mock_keystone_create_client.return_value = fakes.FakeKeystoneClient() - mock_keystone_create_client.side_effect = ( + @mock.patch("rally.osclients.Keystone.get_session") + def test_verified_keystone_unreachable(self, mock_keystone_get_session): + self.auth_ref_patcher.stop() + mock_keystone_get_session.side_effect = ( keystone_exceptions.AuthorizationFailure ) self.assertRaises(exceptions.HostUnreachableException, @@ -328,7 +339,7 @@ class OSClientsTestCase(test.TestCase): region_name=self.credential.region_name) mock_nova.client.Client.assert_called_once_with( "2", - auth_token=self.fake_keystone.auth_token, + auth_token=self.auth_ref.auth_token, http_log_debug=False, timeout=cfg.CONF.openstack_client_http_timeout, insecure=False, cacert=None, @@ -350,7 +361,7 @@ class OSClientsTestCase(test.TestCase): client = self.clients.neutron() self.assertEqual(fake_neutron, client) kw = { - "token": self.fake_keystone.auth_token, + "token": self.auth_ref.auth_token, "endpoint_url": self.service_catalog.url_for.return_value, "timeout": cfg.CONF.openstack_client_http_timeout, "insecure": self.credential.insecure, @@ -375,7 +386,7 @@ class OSClientsTestCase(test.TestCase): client = self.clients.glance() self.assertEqual(fake_glance, client) kw = {"endpoint": self.service_catalog.url_for.return_value, - "token": self.fake_keystone.auth_token, + "token": self.auth_ref.auth_token, "timeout": cfg.CONF.openstack_client_http_timeout, "insecure": False, "cacert": None} self.service_catalog.url_for.assert_called_once_with( @@ -407,7 +418,7 @@ class OSClientsTestCase(test.TestCase): self.assertEqual(fake_cinder.client.management_url, self.service_catalog.url_for.return_value) self.assertEqual(fake_cinder.client.auth_token, - self.fake_keystone.auth_token) + self.auth_ref.auth_token) self.assertEqual(fake_cinder, self.clients.cache["cinder"]) def test_manila(self): @@ -434,7 +445,7 @@ class OSClientsTestCase(test.TestCase): self.service_catalog.url_for.return_value) self.assertEqual( mock_manila.client.Client.return_value.client.auth_token, - self.fake_keystone.auth_token) + self.auth_ref.auth_token) self.assertEqual( mock_manila.client.Client.return_value, self.clients.cache["manila"]) @@ -453,7 +464,7 @@ class OSClientsTestCase(test.TestCase): service_type="metering", region_name=self.credential.region_name) kw = {"os_endpoint": self.service_catalog.url_for.return_value, - "token": self.fake_keystone.auth_token, + "token": self.auth_ref.auth_token, "timeout": cfg.CONF.openstack_client_http_timeout, "insecure": False, "cacert": None, "username": self.credential.username, @@ -501,7 +512,7 @@ class OSClientsTestCase(test.TestCase): service_type="monitoring", region_name=self.credential.region_name) os_endpoint = self.service_catalog.url_for.return_value - kw = {"token": self.fake_keystone.auth_token, + kw = {"token": self.auth_ref.auth_token, "timeout": cfg.CONF.openstack_client_http_timeout, "insecure": False, "cacert": None, "username": self.credential.username, @@ -528,7 +539,7 @@ class OSClientsTestCase(test.TestCase): service_type="baremetal", region_name=self.credential.region_name) kw = { - "os_auth_token": self.fake_keystone.auth_token, + "os_auth_token": self.auth_ref.auth_token, "ironic_url": self.service_catalog.url_for.return_value, "timeout": cfg.CONF.openstack_client_http_timeout, "insecure": self.credential.insecure, @@ -563,7 +574,7 @@ class OSClientsTestCase(test.TestCase): mock_zaqar = mock.MagicMock() mock_zaqar.client.Client = mock.MagicMock(return_value=fake_zaqar) self.assertNotIn("zaqar", self.clients.cache) - p_id = self.fake_keystone.auth_ref.get("token").get("tenant").get("id") + p_id = self.auth_ref.get("token").get("tenant").get("id") with mock.patch.dict("sys.modules", {"zaqarclient.queues": mock_zaqar}): client = self.clients.zaqar() @@ -624,7 +635,7 @@ class OSClientsTestCase(test.TestCase): mock_mistral.client.client.assert_called_once_with( mistral_url=fake_mistral_url, service_type="workflowv2", - auth_token=self.fake_keystone.auth_token + auth_token=self.auth_ref.auth_token ) self.assertEqual(fake_mistral, self.clients.cache["mistral"]) @@ -641,7 +652,7 @@ class OSClientsTestCase(test.TestCase): region_name=self.credential.region_name) kw = {"retries": 1, "preauthurl": self.service_catalog.url_for.return_value, - "preauthtoken": self.fake_keystone.auth_token, + "preauthtoken": self.auth_ref.auth_token, "insecure": False, "cacert": None, "user": self.credential.username, @@ -674,14 +685,13 @@ class OSClientsTestCase(test.TestCase): mock_boto.connect_ec2_endpoint.assert_called_once_with(**kw) self.assertEqual(fake_ec2, self.clients.cache["ec2"]) - @mock.patch("rally.osclients.Keystone.create_client") - def test_services(self, mock_keystone_create_client): + @mock.patch("rally.osclients.Keystone.service_catalog") + def test_services(self, mock_keystone_service_catalog): available_services = {consts.ServiceType.IDENTITY: {}, consts.ServiceType.COMPUTE: {}, "some_service": {}} - mock_keystone_create_client.return_value = mock.Mock( - service_catalog=mock.Mock( - get_endpoints=lambda: available_services)) + mock_get_endpoints = mock_keystone_service_catalog.get_endpoints + mock_get_endpoints.return_value = available_services clients = osclients.Clients(self.credential) self.assertEqual( @@ -703,11 +713,11 @@ class OSClientsTestCase(test.TestCase): region_name=self.credential.region_name ) kw = {"endpoint": self.service_catalog.url_for.return_value, - "token": self.fake_keystone.auth_token} + "token": self.auth_ref.auth_token} mock_murano.client.Client.assert_called_once_with("1", **kw) self.assertEqual(fake_murano, self.clients.cache["murano"]) - @mock.patch("rally.osclients.Designate._get_session") + @mock.patch("rally.osclients.Keystone.get_session") @ddt.data( {}, {"version": "2"}, @@ -715,13 +725,13 @@ class OSClientsTestCase(test.TestCase): {"version": None} ) @ddt.unpack - def test_designate(self, mock_designate__get_session, version=None): + def test_designate(self, mock_keystone_get_session, version=None): fake_designate = fakes.FakeDesignateClient() mock_designate = mock.Mock() mock_designate.client.Client.return_value = fake_designate - mock_designate__get_session.return_value = ("fake_session", - "fake_auth_plugin") + mock_keystone_get_session.return_value = ("fake_session", + "fake_auth_plugin") self.assertNotIn("designate", self.clients.cache) with mock.patch.dict("sys.modules", @@ -742,7 +752,7 @@ class OSClientsTestCase(test.TestCase): url = self.service_catalog.url_for.return_value url.__iadd__.assert_called_once_with("/v%s" % default) - mock_designate__get_session.assert_called_once_with() + mock_keystone_get_session.assert_called_once_with() if version == "2": mock_designate.client.Client.assert_called_once_with( @@ -760,13 +770,13 @@ class OSClientsTestCase(test.TestCase): key += "%s" % {"version": version} self.assertEqual(fake_designate, self.clients.cache[key]) - @mock.patch("rally.osclients.Cue._get_session") - def test_cue(self, mock_cue__get_session): + @mock.patch("rally.osclients.Keystone.get_session") + def test_cue(self, mock_keystone_get_session): fake_cue = fakes.FakeCueClient() mock_cue = mock.MagicMock() mock_cue.client.Client = mock.MagicMock(return_value=fake_cue) - mock_cue__get_session.return_value = ("fake_session", - "fake_auth_plugin") + mock_keystone_get_session.return_value = ("fake_session", + "fake_auth_plugin") self.assertNotIn("cue", self.clients.cache) with mock.patch.dict("sys.modules", {"cueclient": mock_cue, "cueclient.v1": mock_cue}): @@ -794,14 +804,14 @@ class OSClientsTestCase(test.TestCase): mock_senlin.client.Client.return_value, self.clients.cache["senlin"]) - @mock.patch("rally.osclients.Magnum._get_session") - def test_magnum(self, mock_magnum__get_session): + @mock.patch("rally.osclients.Keystone.get_session") + def test_magnum(self, mock_keystone_get_session): fake_magnum = fakes.FakeMagnumClient() mock_magnum = mock.MagicMock() mock_magnum.client.Client.return_value = fake_magnum - mock_magnum__get_session.return_value = (self.fake_keystone.session, - "fake_auth_plugin") + mock_keystone_get_session.return_value = (self.fake_keystone.session, + "fake_auth_plugin") self.service_catalog.url_for.return_value = "http://fakeurl/" self.assertNotIn("magnum", self.clients.cache) @@ -838,7 +848,7 @@ class OSClientsTestCase(test.TestCase): mock_watcher.client.Client.assert_called_once_with( "1", self.service_catalog.url_for.return_value, - token=self.fake_keystone.auth_token, + token=self.auth_ref.auth_token, ca_file=None, insecure=False, timeout=180.0,