From c6ca687354ac5b537d4d6d037e7757600f82e972 Mon Sep 17 00:00:00 2001 From: Samuel Matzek Date: Thu, 27 Jul 2017 09:41:23 -0500 Subject: [PATCH] Fix nova proxy admin login The Nova Python client changed how it handles Keystone authentication during the Ocata release. In particular it treats context.auth_token as an authentication token. This broke the Nova proxy admin login that Trove uses because it was passing the user password on the context.auth_token property. User id and password authentication is still possible through Nova client but the password must be explicitly passed on its own keyword arg. Additionally, Keystone user/password authentication has also changed in the meantime and requires the passing of the user domain name. Additional changes were made in the code path to move away from old deprecated Oslo context property names to the new names. Closes-Bug: #1700586 Change-Id: Ic410692e07194bae64d9a3e49816bc24958969d4 --- trove/common/cfg.py | 2 ++ trove/common/remote.py | 10 +++++--- trove/taskmanager/manager.py | 4 +-- trove/tests/unittests/common/test_remote.py | 27 ++++++++++++++++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/trove/common/cfg.py b/trove/common/cfg.py index c76cb9be97..0f495da8ec 100644 --- a/trove/common/cfg.py +++ b/trove/common/cfg.py @@ -362,6 +362,8 @@ common_opts = [ help="Admin tenant ID used to connect to Nova.", secret=True), cfg.StrOpt('nova_proxy_admin_tenant_name', default='', help="Admin tenant name used to connect to Nova.", secret=True), + cfg.StrOpt('nova_proxy_admin_user_domain_name', default='Default', + help="User domain of the admin user used to connect to Nova."), cfg.StrOpt('network_label_regex', default='^private$', help='Regular expression to match Trove network labels.'), cfg.StrOpt('ip_regex', default=None, diff --git a/trove/common/remote.py b/trove/common/remote.py index 4809f99d58..4809d9007c 100644 --- a/trove/common/remote.py +++ b/trove/common/remote.py @@ -84,11 +84,11 @@ def guest_client(context, id, manager=None): return clazz(context, id) -def nova_client(context, region_name=None): +def nova_client(context, region_name=None, password=None): if CONF.nova_compute_url: url = '%(nova_url)s%(tenant)s' % { 'nova_url': normalize_url(CONF.nova_compute_url), - 'tenant': context.tenant} + 'tenant': context.project_id} else: url = get_endpoint(context.service_catalog, service_type=CONF.nova_compute_service_type, @@ -97,9 +97,11 @@ def nova_client(context, region_name=None): client = Client(CONF.nova_client_version, username=context.user, + password=password, endpoint_override=url, - project_id=context.tenant, + project_id=context.project_id, project_domain_name=context.project_domain_name, + user_domain_name=context.user_domain_name, auth_url=CONF.trove_auth_url, auth_token=context.auth_token, insecure=CONF.nova_api_insecure) @@ -113,7 +115,7 @@ def create_admin_nova_client(context): Creates client that uses trove admin credentials :return: a client for nova for the trove admin """ - client = create_nova_client(context) + client = create_nova_client(context, password=CONF.nova_proxy_admin_pass) return client diff --git a/trove/taskmanager/manager.py b/trove/taskmanager/manager.py index ad5bfd9eb6..4846bb2621 100644 --- a/trove/taskmanager/manager.py +++ b/trove/taskmanager/manager.py @@ -45,8 +45,8 @@ class Manager(periodic_task.PeriodicTasks): super(Manager, self).__init__(CONF) self.admin_context = TroveContext( user=CONF.nova_proxy_admin_user, - auth_token=CONF.nova_proxy_admin_pass, - tenant=CONF.nova_proxy_admin_tenant_id) + tenant=CONF.nova_proxy_admin_tenant_id, + user_domain_name=CONF.nova_proxy_admin_user_domain_name) if CONF.exists_notification_transformer: self.exists_transformer = importutils.import_object( CONF.exists_notification_transformer, diff --git a/trove/tests/unittests/common/test_remote.py b/trove/tests/unittests/common/test_remote.py index 17fbad459d..1468951763 100644 --- a/trove/tests/unittests/common/test_remote.py +++ b/trove/tests/unittests/common/test_remote.py @@ -17,7 +17,7 @@ # import uuid -from mock import patch, MagicMock +from mock import ANY, patch, MagicMock import swiftclient.client from testtools import ExpectedException, matchers @@ -363,6 +363,7 @@ class TestCreateNovaClient(trove_testtools.TestCase): cfg.CONF.clear_override('nova_compute_url') cfg.CONF.clear_override('nova_compute_service_type') cfg.CONF.clear_override('os_region_name') + cfg.CONF.clear_override('nova_proxy_admin_pass') def test_create_with_no_conf_no_catalog(self): self.assertRaises(exception.EmptyCatalog, @@ -417,6 +418,30 @@ class TestCreateNovaClient(trove_testtools.TestCase): self.assertEqual('%s%s' % (nova_url_from_conf, admin_tenant_id), admin_client.client.endpoint_override) + @patch('trove.common.remote.Client', autospec=True) + def test_nova_client_password_passthrough(self, nova_mock): + test_domain = 'test_domain_name' + ctx = TroveContext(user='admin1', + project_id='project_id', + user_domain_name=test_domain, + service_catalog=self.service_catalog) + remote.nova_client(ctx, password='adminpass') + nova_mock.assert_called_with(ANY, username='admin1', + password='adminpass', + user_domain_name=test_domain, + project_id='project_id', + auth_token=None, + auth_url=ANY, + endpoint_override=ANY, + project_domain_name=ANY, + insecure=False) + + @patch('trove.common.remote.create_nova_client', autospec=True) + def test_admin_client_password(self, nc_mock): + cfg.CONF.set_override('nova_proxy_admin_pass', 's3cr3t3') + remote.create_admin_nova_client('mycontext') + nc_mock.assert_called_with('mycontext', password='s3cr3t3') + class TestCreateSwiftClient(trove_testtools.TestCase): def setUp(self):