From f33e5fd8acf4ee840d480e8cb26e3337e1190040 Mon Sep 17 00:00:00 2001 From: Ivan Kolodyazhny Date: Wed, 31 Jul 2019 16:13:01 +0300 Subject: [PATCH] Drop deprecated Keystone V2 API support Keystone V2 API was deprecated in Stein release in Horizon and removed from Keystone in Queens release. Change-Id: I917e273d3174adf0874e516b3d635ccb8ba58a27 --- doc/source/configuration/settings.rst | 4 +- openstack_auth/backend.py | 18 +- openstack_auth/plugin/base.py | 23 +- openstack_auth/plugin/k2k.py | 6 +- openstack_auth/plugin/password.py | 18 +- openstack_auth/plugin/token.py | 17 +- openstack_auth/tests/data_v2.py | 143 -------- openstack_auth/tests/unit/test_auth.py | 330 ------------------ openstack_auth/tests/unit/test_utils.py | 36 -- openstack_auth/utils.py | 47 +-- openstack_dashboard/api/keystone.py | 200 +++-------- openstack_dashboard/api/swift.py | 2 +- .../identity/application_credentials/views.py | 3 - .../dashboards/identity/domains/panel.py | 3 - .../dashboards/identity/projects/tables.py | 18 +- .../dashboards/identity/users/tables.py | 2 - .../dashboards/project/api_access/tables.py | 5 +- .../dashboards/project/api_access/views.py | 16 +- .../tests/test_credentials.py | 22 -- .../test/unit/api/rest/test_keystone.py | 4 +- .../test/unit/api/test_base.py | 2 +- ...-keystone-v2-support-04c7658f3bea67ac.yaml | 7 + 22 files changed, 106 insertions(+), 820 deletions(-) delete mode 100644 openstack_auth/tests/data_v2.py create mode 100644 releasenotes/notes/drop-keystone-v2-support-04c7658f3bea67ac.yaml diff --git a/doc/source/configuration/settings.rst b/doc/source/configuration/settings.rst index d8906e738f..de3f8ea97d 100644 --- a/doc/source/configuration/settings.rst +++ b/doc/source/configuration/settings.rst @@ -492,7 +492,7 @@ OpenStack dashboard to use a specific API version for a given service API. The version should be formatted as it appears in the URL for the service API. For example, the identity service APIs have inconsistent - use of the decimal point, so valid options would be "2.0" or "3". + use of the decimal point, so valid options would be "3". For example: .. code-block:: python @@ -1313,7 +1313,7 @@ Default: ``["admin"]`` The list of roles that have administrator privileges in this OpenStack installation. This check is very basic and essentially only works with -keystone v2.0 and v3 with the default policy file. The setting assumes there +keystone v3 with the default policy file. The setting assumes there is a common ``admin`` like role(s) across services. Example uses of this setting are: diff --git a/openstack_auth/backend.py b/openstack_auth/backend.py index 2599cbd0a7..39030e6f5f 100644 --- a/openstack_auth/backend.py +++ b/openstack_auth/backend.py @@ -149,9 +149,7 @@ class KeystoneBackend(object): scoped_auth = domain_auth scoped_auth_ref = domain_auth_ref elif not scoped_auth_ref and not domain_auth_ref: - msg = _('You are not authorized for any projects.') - if utils.get_keystone_version() >= 3: - msg = _('You are not authorized for any projects or domains.') + msg = _('You are not authorized for any projects or domains.') raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. @@ -170,16 +168,10 @@ class KeystoneBackend(object): interface = settings.OPENSTACK_ENDPOINT_TYPE - endpoint, url_fixed = utils.fix_auth_url_version_prefix( - scoped_auth_ref.service_catalog.url_for( - service_type='identity', - interface=interface, - region_name=region_name)) - if url_fixed: - LOG.warning("The Keystone URL in service catalog points to a v2.0 " - "Keystone endpoint, but v3 is specified as the API " - "version to use by Horizon. Using v3 endpoint for " - "authentication.") + endpoint = scoped_auth_ref.service_catalog.url_for( + service_type='identity', + interface=interface, + region_name=region_name) # If we made it here we succeeded. Create our User! unscoped_token = unscoped_auth_ref.auth_token diff --git a/openstack_auth/plugin/base.py b/openstack_auth/plugin/base.py index 83d9f3aa38..eb0451a1ff 100644 --- a/openstack_auth/plugin/base.py +++ b/openstack_auth/plugin/base.py @@ -15,7 +15,6 @@ import logging from django.utils.translation import ugettext_lazy as _ from keystoneauth1 import exceptions as keystone_exceptions -from keystoneclient.v2_0 import client as v2_client from keystoneclient.v3 import client as v3_client import six @@ -82,16 +81,11 @@ class BasePlugin(object): or v3 keystoneclient projects objects. """ try: - if self.keystone_version >= 3: - client = v3_client.Client(session=session, auth=auth_plugin) - if auth_ref.is_federated: - return client.federation.projects.list() - else: - return client.projects.list(user=auth_ref.user_id) - + client = v3_client.Client(session=session, auth=auth_plugin) + if auth_ref.is_federated: + return client.federation.projects.list() else: - client = v2_client.Client(session=session, auth=auth_plugin) - return client.tenants.list() + return client.projects.list(user=auth_ref.user_id) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): @@ -100,11 +94,8 @@ class BasePlugin(object): def list_domains(self, session, auth_plugin, auth_ref=None): try: - if self.keystone_version >= 3: - client = v3_client.Client(session=session, auth=auth_plugin) - return client.auth.domains() - else: - return [] + client = v3_client.Client(session=session, auth=auth_plugin) + return client.auth.domains() except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): msg = _('Unable to retrieve authorized domains.') @@ -203,8 +194,6 @@ class BasePlugin(object): session = utils.get_session() auth_url = unscoped_auth.auth_url - if utils.get_keystone_version() < 3: - return None, None if domain_name: domains = [domain_name] else: diff --git a/openstack_auth/plugin/k2k.py b/openstack_auth/plugin/k2k.py index 5f6cdc9880..36a291dc61 100644 --- a/openstack_auth/plugin/k2k.py +++ b/openstack_auth/plugin/k2k.py @@ -44,10 +44,8 @@ class K2KAuthPlugin(base.BasePlugin): # Avoid mutable default arg for plugins plugins = plugins or [] - - # service_provider being None prevents infinite recursion - if utils.get_keystone_version() < 3 or not service_provider: - return None + if not service_provider: + return keystone_idp_id = settings.KEYSTONE_PROVIDER_IDP_ID if service_provider == keystone_idp_id: diff --git a/openstack_auth/plugin/password.py b/openstack_auth/plugin/password.py index 0e3100c29e..af18994180 100644 --- a/openstack_auth/plugin/password.py +++ b/openstack_auth/plugin/password.py @@ -12,11 +12,9 @@ import logging -from keystoneauth1.identity import v2 as v2_auth from keystoneauth1.identity import v3 as v3_auth from openstack_auth.plugin import base -from openstack_auth import utils LOG = logging.getLogger(__name__) @@ -38,14 +36,8 @@ class PasswordPlugin(base.BasePlugin): LOG.debug('Attempting to authenticate for %s', username) - if utils.get_keystone_version() >= 3: - return v3_auth.Password(auth_url=auth_url, - username=username, - password=password, - user_domain_name=user_domain_name, - unscoped=True) - - else: - return v2_auth.Password(auth_url=auth_url, - username=username, - password=password) + return v3_auth.Password(auth_url=auth_url, + username=username, + password=password, + user_domain_name=user_domain_name, + unscoped=True) diff --git a/openstack_auth/plugin/token.py b/openstack_auth/plugin/token.py index b812b44fae..5403bcd3b2 100644 --- a/openstack_auth/plugin/token.py +++ b/openstack_auth/plugin/token.py @@ -10,11 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. -from keystoneauth1.identity import v2 as v2_auth from keystoneauth1.identity import v3 as v3_auth from openstack_auth.plugin import base -from openstack_auth import utils __all__ = ['TokenPlugin'] @@ -28,14 +26,7 @@ class TokenPlugin(base.BasePlugin): if not all((auth_url, token)): return None - if utils.get_keystone_version() >= 3: - return v3_auth.Token(auth_url=auth_url, - token=token, - project_id=project_id, - reauthenticate=False) - - else: - return v2_auth.Token(auth_url=auth_url, - token=token, - tenant_id=project_id, - reauthenticate=False) + return v3_auth.Token(auth_url=auth_url, + token=token, + project_id=project_id, + reauthenticate=False) diff --git a/openstack_auth/tests/data_v2.py b/openstack_auth/tests/data_v2.py deleted file mode 100644 index 1f3a914a2b..0000000000 --- a/openstack_auth/tests/data_v2.py +++ /dev/null @@ -1,143 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import datetime -import uuid - -from django.utils import datetime_safe -from keystoneauth1.access import access -from keystoneauth1.access import service_catalog -from keystoneclient.v2_0 import roles -from keystoneclient.v2_0 import tenants -from keystoneclient.v2_0 import users - - -class TestDataContainer(object): - """Arbitrary holder for test data in an object-oriented fashion.""" - pass - - -def generate_test_data(): - '''Builds a set of test_data data as returned by Keystone V2.''' - test_data = TestDataContainer() - - keystone_service = { - 'type': 'identity', - 'name': 'keystone', - 'endpoints_links': [], - 'endpoints': [ - { - 'region': 'RegionOne', - 'adminURL': 'http://admin.localhost:35357/v2.0', - 'internalURL': 'http://internal.localhost:5000/v2.0', - 'publicURL': 'http://public.localhost:5000/v2.0' - } - ] - } - - # Users - user_dict = {'id': uuid.uuid4().hex, - 'name': 'gabriel', - 'email': 'gabriel@example.com', - 'password': 'swordfish', - 'token': '', - 'enabled': True} - test_data.user = users.User(None, user_dict, loaded=True) - - # Tenants - tenant_dict_1 = {'id': uuid.uuid4().hex, - 'name': 'tenant_one', - 'description': '', - 'enabled': True} - tenant_dict_2 = {'id': uuid.uuid4().hex, - 'name': 'tenant_two', - 'description': '', - 'enabled': False} - test_data.tenant_one = tenants.Tenant(None, tenant_dict_1, loaded=True) - test_data.tenant_two = tenants.Tenant(None, tenant_dict_2, loaded=True) - - nova_service = { - 'type': 'compute', - 'name': 'nova', - 'endpoint_links': [], - 'endpoints': [ - { - 'region': 'RegionOne', - 'adminURL': ('http://nova-admin.localhost:8774/v2.0/%s' - % (tenant_dict_1['id'])), - 'internalURL': ('http://nova-internal.localhost:8774/v2.0/%s' - % (tenant_dict_1['id'])), - 'publicURL': ('http://nova-public.localhost:8774/v2.0/%s' - % (tenant_dict_1['id'])) - }, - { - 'region': 'RegionTwo', - 'adminURL': ('http://nova2-admin.localhost:8774/v2.0/%s' - % (tenant_dict_1['id'])), - 'internalURL': ('http://nova2-internal.localhost:8774/v2.0/%s' - % (tenant_dict_1['id'])), - 'publicURL': ('http://nova2-public.localhost:8774/v2.0/%s' - % (tenant_dict_1['id'])) - } - ] - } - - # Roles - role_dict = {'id': uuid.uuid4().hex, - 'name': 'Member'} - test_data.role = roles.Role(roles.RoleManager, role_dict) - - # Tokens - tomorrow = datetime_safe.datetime.now() + datetime.timedelta(days=1) - expiration = datetime_safe.datetime.isoformat(tomorrow) - - scoped_token_dict = { - 'access': { - 'token': { - 'id': uuid.uuid4().hex, - 'expires': expiration, - 'tenant': tenant_dict_1, - 'tenants': [tenant_dict_1, tenant_dict_2]}, - 'user': { - 'id': user_dict['id'], - 'name': user_dict['name'], - 'roles': [role_dict]}, - 'serviceCatalog': [keystone_service, nova_service] - } - } - - test_data.scoped_access_info = access.create( - resp=None, - body=scoped_token_dict) - - unscoped_token_dict = { - 'access': { - 'token': { - 'id': uuid.uuid4().hex, - 'expires': expiration}, - 'user': { - 'id': user_dict['id'], - 'name': user_dict['name'], - 'roles': [role_dict]}, - 'serviceCatalog': [keystone_service] - } - } - test_data.unscoped_access_info = access.create( - resp=None, - body=unscoped_token_dict) - - # Service Catalog - test_data.service_catalog = service_catalog.ServiceCatalogV2( - [keystone_service, nova_service]) - - return test_data diff --git a/openstack_auth/tests/unit/test_auth.py b/openstack_auth/tests/unit/test_auth.py index f5d2ce03be..386f3181bb 100644 --- a/openstack_auth/tests/unit/test_auth.py +++ b/openstack_auth/tests/unit/test_auth.py @@ -19,16 +19,13 @@ from django import test from django.test.utils import override_settings from django.urls import reverse from keystoneauth1 import exceptions as keystone_exceptions -from keystoneauth1.identity import v2 as v2_auth from keystoneauth1.identity import v3 as v3_auth from keystoneauth1 import session from keystoneauth1 import token_endpoint -from keystoneclient.v2_0 import client as client_v2 from keystoneclient.v3 import client as client_v3 from mox3 import mox from testscenarios import load_tests_apply_scenarios -from openstack_auth.tests import data_v2 from openstack_auth.tests import data_v3 from openstack_auth import utils @@ -143,333 +140,6 @@ class OpenStackAuthFederatedTestsMixin(object): self._mock_unscoped_list_domains(client, domains) -class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase): - - def setUp(self): - super(OpenStackAuthTestsV2, self).setUp() - - if getattr(self, 'interface', None): - override = self.settings(OPENSTACK_ENDPOINT_TYPE=self.interface) - override.enable() - self.addCleanup(override.disable) - - self.mox = mox.Mox() - self.addCleanup(self.mox.VerifyAll) - self.addCleanup(self.mox.UnsetStubs) - - self.data = data_v2.generate_test_data() - self.ks_client_module = client_v2 - - settings.OPENSTACK_API_VERSIONS['identity'] = 2.0 - settings.OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v2.0" - - self.mox.StubOutClassWithMocks(token_endpoint, 'Token') - self.mox.StubOutClassWithMocks(v2_auth, 'Token') - self.mox.StubOutClassWithMocks(v2_auth, 'Password') - self.mox.StubOutClassWithMocks(client_v2, 'Client') - - def _mock_unscoped_list_tenants(self, client, tenants): - client.tenants = self.mox.CreateMockAnything() - client.tenants.list().AndReturn(tenants) - - def _mock_unscoped_client_list_tenants(self, user, tenants): - client = self._mock_unscoped_client(user) - self._mock_unscoped_list_tenants(client, tenants) - - def _create_password_auth(self, username=None, password=None, url=None): - if not username: - username = self.data.user.name - - if not password: - password = self.data.user.password - - if not url: - url = settings.OPENSTACK_KEYSTONE_URL - - return v2_auth.Password(auth_url=url, - password=password, - username=username) - - def _create_token_auth(self, project_id, token=None, url=None): - if not token: - token = self.data.unscoped_access_info.auth_token - - if not url: - url = settings.OPENSTACK_KEYSTONE_URL - - return v2_auth.Token(auth_url=url, - token=token, - tenant_id=project_id, - reauthenticate=False) - - def _login(self): - tenants = [self.data.tenant_one, self.data.tenant_two] - user = self.data.user - unscoped = self.data.unscoped_access_info - - form_data = self.get_form_data(user) - self._mock_unscoped_client_list_tenants(user, tenants) - self._mock_scoped_client_for_tenant(unscoped, self.data.tenant_one.id) - - self.mox.ReplayAll() - - url = reverse('login') - - # GET the page to set the test cookie. - response = self.client.get(url, form_data) - self.assertEqual(response.status_code, 200) - - # POST to the page to log in. - response = self.client.post(url, form_data) - self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) - - def test_login(self): - self._login() - - def test_login_with_disabled_tenant(self): - # Test to validate that authentication will not try to get - # scoped token for disabled project. - tenants = [self.data.tenant_two, self.data.tenant_one] - user = self.data.user - unscoped = self.data.unscoped_access_info - - form_data = self.get_form_data(user) - self._mock_unscoped_client_list_tenants(user, tenants) - self._mock_scoped_client_for_tenant(unscoped, self.data.tenant_one.id) - self.mox.ReplayAll() - - url = reverse('login') - - # GET the page to set the test cookie. - response = self.client.get(url, form_data) - self.assertEqual(response.status_code, 200) - - # POST to the page to log in. - response = self.client.post(url, form_data) - self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) - - def test_login_w_bad_region_cookie(self): - self.client.cookies['services_region'] = "bad_region" - self._login() - self.assertNotEqual("bad_region", - self.client.session['services_region']) - self.assertEqual("RegionOne", - self.client.session['services_region']) - - def test_no_enabled_tenants(self): - tenants = [self.data.tenant_two] - user = self.data.user - - form_data = self.get_form_data(user) - self._mock_unscoped_client_list_tenants(user, tenants) - self.mox.ReplayAll() - - url = reverse('login') - - # GET the page to set the test cookie. - response = self.client.get(url, form_data) - self.assertEqual(response.status_code, 200) - - # POST to the page to log in. - response = self.client.post(url, form_data) - self.assertTemplateUsed(response, 'auth/login.html') - self.assertContains(response, - 'You are not authorized for any projects.') - - def test_no_tenants(self): - user = self.data.user - - form_data = self.get_form_data(user) - self._mock_unscoped_client_list_tenants(user, []) - - self.mox.ReplayAll() - - url = reverse('login') - - # GET the page to set the test cookie. - response = self.client.get(url, form_data) - self.assertEqual(response.status_code, 200) - - # POST to the page to log in. - response = self.client.post(url, form_data) - self.assertTemplateUsed(response, 'auth/login.html') - self.assertContains(response, - 'You are not authorized for any projects.') - - def test_invalid_credentials(self): - user = self.data.user - - form_data = self.get_form_data(user) - form_data['password'] = "invalid" - - exc = keystone_exceptions.Unauthorized(401) - self._mock_client_password_auth_failure(user.name, "invalid", exc) - - self.mox.ReplayAll() - - url = reverse('login') - - # GET the page to set the test cookie. - response = self.client.get(url, form_data) - self.assertEqual(response.status_code, 200) - - # POST to the page to log in. - response = self.client.post(url, form_data) - self.assertTemplateUsed(response, 'auth/login.html') - self.assertContains(response, "Invalid credentials.") - - def test_exception(self): - user = self.data.user - - form_data = self.get_form_data(user) - exc = keystone_exceptions.ClientException(500) - self._mock_client_password_auth_failure(user.name, user.password, exc) - self.mox.ReplayAll() - - url = reverse('login') - - # GET the page to set the test cookie. - response = self.client.get(url, form_data) - self.assertEqual(response.status_code, 200) - - # POST to the page to log in. - response = self.client.post(url, form_data) - - self.assertTemplateUsed(response, 'auth/login.html') - self.assertContains(response, - ("An error occurred authenticating. Please try " - "again later.")) - - def test_redirect_when_already_logged_in(self): - self._login() - - response = self.client.get(reverse('login')) - self.assertEqual(response.status_code, 302) - self.assertNotIn(reverse('login'), response['location']) - - def test_dont_redirect_when_already_logged_in_if_next_is_set(self): - self._login() - - expected_url = "%s?%s=/%s/" % (reverse('login'), - auth.REDIRECT_FIELD_NAME, - 'special') - - response = self.client.get(expected_url) - self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'auth/login.html') - - def test_switch(self, next=None): - tenant = self.data.tenant_two - tenants = [self.data.tenant_one, self.data.tenant_two] - user = self.data.user - unscoped = self.data.unscoped_access_info - scoped = self.data.scoped_access_info - sc = self.data.service_catalog - et = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL') - endpoint = sc.url_for(service_type='identity', interface=et) - - form_data = self.get_form_data(user) - - self._mock_unscoped_client_list_tenants(user, tenants) - self._mock_scoped_client_for_tenant(unscoped, self.data.tenant_one.id) - self._mock_scoped_client_for_tenant(scoped, tenant.id, url=endpoint, - client=False) - - self.mox.ReplayAll() - - url = reverse('login') - - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - response = self.client.post(url, form_data) - self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) - - url = reverse('switch_tenants', args=[tenant.id]) - - scoped._token['tenant']['id'] = self.data.tenant_two.id - - if next: - form_data.update({auth.REDIRECT_FIELD_NAME: next}) - - response = self.client.get(url, form_data) - - if next: - expected_url = next - self.assertEqual(response['location'], expected_url) - else: - self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) - - self.assertEqual(self.client.session['token'].tenant['id'], - scoped.tenant_id) - - def test_switch_with_next(self): - self.test_switch(next='/next_url') - - def test_switch_region(self, next=None): - tenants = [self.data.tenant_one, self.data.tenant_two] - user = self.data.user - scoped = self.data.scoped_access_info - sc = self.data.service_catalog - - form_data = self.get_form_data(user) - - self._mock_unscoped_client_list_tenants(user, tenants) - self._mock_scoped_client_for_tenant(scoped, self.data.tenant_one.id) - - self.mox.ReplayAll() - - url = reverse('login') - - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - response = self.client.post(url, form_data) - self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) - - old_region = sc.get_endpoints()['compute'][0]['region'] - self.assertEqual(self.client.session['services_region'], old_region) - - region = sc.get_endpoints()['compute'][1]['region'] - url = reverse('switch_services_region', args=[region]) - - form_data['region_name'] = region - - if next: - form_data.update({auth.REDIRECT_FIELD_NAME: next}) - - response = self.client.get(url, form_data) - - if next: - expected_url = next - self.assertEqual(response['location'], expected_url) - else: - self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) - - self.assertEqual(self.client.session['services_region'], region) - self.assertEqual(self.client.cookies['services_region'].value, region) - - def test_switch_region_with_next(self, next=None): - self.test_switch_region(next='/next_url') - - def test_tenant_sorting(self): - tenants = [self.data.tenant_two, self.data.tenant_one] - expected_tenants = [self.data.tenant_one, self.data.tenant_two] - user = self.data.user - unscoped = self.data.unscoped_access_info - - client = self._mock_unscoped_client_with_token(user, unscoped) - self._mock_unscoped_list_tenants(client, tenants) - - self.mox.ReplayAll() - - tenant_list = utils.get_project_list( - user_id=user.id, - auth_url=settings.OPENSTACK_KEYSTONE_URL, - token=unscoped.auth_token) - self.assertEqual(tenant_list, expected_tenants) - - class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, OpenStackAuthFederatedTestsMixin, test.TestCase): diff --git a/openstack_auth/tests/unit/test_utils.py b/openstack_auth/tests/unit/test_utils.py index 012476344b..ab91fdd012 100644 --- a/openstack_auth/tests/unit/test_utils.py +++ b/openstack_auth/tests/unit/test_utils.py @@ -11,7 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from django.conf import settings from django import http from django import test from django.test import client @@ -55,41 +54,6 @@ class RoleTestCaseAdmin(test.TestCase): class UtilsTestCase(test.TestCase): - def test_fix_auth_url_version_v20(self): - settings.OPENSTACK_API_VERSIONS['identity'] = 2.0 - test_urls = [ - ("http://a/", ("http://a/v2.0", False)), - ("http://a", ("http://a/v2.0", False)), - ("http://a:8080/", ("http://a:8080/v2.0", False)), - ("http://a/v2.0", ("http://a/v2.0", False)), - ("http://a/v2.0/", ("http://a/v2.0/", False)), - ("http://a/identity", ("http://a/identity/v2.0", False)), - ("http://a/identity/", ("http://a/identity/v2.0", False)), - ("http://a:5000/identity/v2.0", - ("http://a:5000/identity/v2.0", False)), - ("http://a/identity/v2.0/", ("http://a/identity/v2.0/", False)) - ] - for src, expected in test_urls: - self.assertEqual(expected, utils.fix_auth_url_version_prefix(src)) - - def test_fix_auth_url_version_v3(self): - settings.OPENSTACK_API_VERSIONS['identity'] = 3 - test_urls = [ - ("http://a/", ("http://a/v3", False)), - ("http://a", ("http://a/v3", False)), - ("http://a:8080/", ("http://a:8080/v3", False)), - ("http://a/v3", ("http://a/v3", False)), - ("http://a/v3/", ("http://a/v3/", False)), - ("http://a/v2.0/", ("http://a/v3/", True)), - ("http://a/v2.0", ("http://a/v3", True)), - ("http://a/identity", ("http://a/identity/v3", False)), - ("http://a:5000/identity/", ("http://a:5000/identity/v3", False)), - ("http://a/identity/v3", ("http://a/identity/v3", False)), - ("http://a/identity/v3/", ("http://a/identity/v3/", False)) - ] - for src, expected in test_urls: - self.assertEqual(expected, utils.fix_auth_url_version_prefix(src)) - @override_settings(DEFAULT_SERVICE_REGIONS={ 'http://example.com': 'RegionThree', '*': 'RegionFour'}) def test_default_services_region_precedence(self): diff --git a/openstack_auth/utils.py b/openstack_auth/utils.py index 4c2d02f129..30a0891cc4 100644 --- a/openstack_auth/utils.py +++ b/openstack_auth/utils.py @@ -19,11 +19,9 @@ from django.conf import settings from django.contrib import auth from django.contrib.auth import models from django.utils import timezone -from keystoneauth1.identity import v2 as v2_auth from keystoneauth1.identity import v3 as v3_auth from keystoneauth1 import session from keystoneauth1 import token_endpoint -from keystoneclient.v2_0 import client as client_v2 from keystoneclient.v3 import client as client_v3 from six.moves.urllib import parse as urlparse @@ -116,17 +114,12 @@ def get_session(): def get_keystone_client(): - if get_keystone_version() < 3: - return client_v2 - else: - return client_v3 + return client_v3 def is_websso_enabled(): """Websso is supported in Keystone version 3.""" - websso_enabled = settings.WEBSSO_ENABLED - keystonev3_plus = (get_keystone_version() >= 3) - return websso_enabled and keystonev3_plus + return settings.WEBSSO_ENABLED def is_websso_default_redirect(): @@ -275,26 +268,24 @@ def _augment_url_with_version(auth_url): the identity URLs returned by Keystone might no longer contain API versions, leaving the version choice up to the user. """ - if has_in_url_path(auth_url, ["/v2.0", "/v3"]): + if has_in_url_path(auth_url, ["/v3"]): return auth_url - if get_keystone_version() >= 3: - return url_path_append(auth_url, "/v3") - else: - return url_path_append(auth_url, "/v2.0") + return url_path_append(auth_url, "/v3") def fix_auth_url_version_prefix(auth_url): """Fix up the auth url if an invalid or no version prefix was given. - People still give a v2 auth_url even when they specify that they want v3 - authentication. Fix the URL to say v3 in this case and add version if it is + Fix the URL to say v3 in this case and add version if it is missing entirely. This should be smarter and use discovery. + Until version discovery is implemented we need this method to get + everything working. """ auth_url = _augment_url_with_version(auth_url) url_fixed = False - if get_keystone_version() >= 3 and has_in_url_path(auth_url, ["/v2.0"]): + if has_in_url_path(auth_url, ["/v2.0"]): url_fixed = True auth_url = url_path_replace(auth_url, "/v2.0", "/v3", 1) @@ -312,21 +303,15 @@ def clean_up_auth_url(auth_url): def get_token_auth_plugin(auth_url, token, project_id=None, domain_name=None): - if get_keystone_version() >= 3: - if domain_name: - return v3_auth.Token(auth_url=auth_url, - token=token, - domain_name=domain_name, - reauthenticate=False) - else: - return v3_auth.Token(auth_url=auth_url, - token=token, - project_id=project_id, - reauthenticate=False) - else: - return v2_auth.Token(auth_url=auth_url, + if domain_name: + return v3_auth.Token(auth_url=auth_url, token=token, - tenant_id=project_id, + domain_name=domain_name, + reauthenticate=False) + else: + return v3_auth.Token(auth_url=auth_url, + token=token, + project_id=project_id, reauthenticate=False) diff --git a/openstack_dashboard/api/keystone.py b/openstack_dashboard/api/keystone.py index 30ff6a6f17..49ba098ef3 100644 --- a/openstack_dashboard/api/keystone.py +++ b/openstack_dashboard/api/keystone.py @@ -33,12 +33,10 @@ from openstack_auth import backend from openstack_auth import utils as auth_utils from horizon import exceptions -from horizon import messages from openstack_dashboard.api import base from openstack_dashboard.contrib.developer.profiler import api as profiler from openstack_dashboard import policy -from openstack_dashboard.utils import settings as utils LOG = logging.getLogger(__name__) @@ -56,25 +54,13 @@ class IdentityAPIVersionManager(base.APIVersionManager): return user def get_project_manager(self, *args, **kwargs): - if VERSIONS.active < 3: - manager = keystoneclient(*args, **kwargs).tenants - else: - manager = keystoneclient(*args, **kwargs).projects - return manager + return keystoneclient(*args, **kwargs).projects VERSIONS = IdentityAPIVersionManager( "identity", preferred_version=auth_utils.get_keystone_version()) -# Import from oldest to newest so that "preferred" takes correct precedence. -try: - # pylint: disable=ungrouped-imports - from keystoneclient.v2_0 import client as keystone_client_v2 - VERSIONS.load_supported_version(2.0, {"client": keystone_client_v2}) -except ImportError: - pass - try: # pylint: disable=ungrouped-imports from keystoneclient.v3 import client as keystone_client_v3 @@ -266,12 +252,9 @@ def tenant_create(request, name, description=None, enabled=None, domain=None, **kwargs): manager = VERSIONS.get_project_manager(request, admin=True) try: - if VERSIONS.active < 3: - return manager.create(name, description, enabled, **kwargs) - else: - return manager.create(name, domain, - description=description, - enabled=enabled, **kwargs) + return manager.create(name, domain, + description=description, + enabled=enabled, **kwargs) except keystone_exceptions.Conflict: raise exceptions.Conflict() @@ -364,25 +347,13 @@ def tenant_delete(request, project): def tenant_list(request, paginate=False, marker=None, domain=None, user=None, admin=True, filters=None): manager = VERSIONS.get_project_manager(request, admin=admin) - page_size = utils.get_page_size(request) tenants = [] - limit = None - if paginate: - limit = page_size + 1 - has_more_data = False # if requesting the projects for the current user, # return the list from the cache if user == request.user.id: tenants = request.user.authorized_tenants - - elif VERSIONS.active < 3: - tenants = manager.list(limit, marker) - if paginate and len(tenants) > page_size: - tenants.pop(-1) - has_more_data = True - # V3 API else: domain_id = get_effective_domain_id(request) kwargs = { @@ -408,12 +379,8 @@ def tenant_update(request, project, name=None, description=None, enabled=None, domain=None, **kwargs): manager = VERSIONS.get_project_manager(request, admin=True) try: - if VERSIONS.active < 3: - return manager.update(project, name, description, enabled, - **kwargs) - else: - return manager.update(project, name=name, description=description, - enabled=enabled, domain=domain, **kwargs) + return manager.update(project, name=name, description=description, + enabled=enabled, domain=domain, **kwargs) except keystone_exceptions.Conflict: raise exceptions.Conflict() @@ -421,16 +388,13 @@ def tenant_update(request, project, name=None, description=None, @profiler.trace def user_list(request, project=None, domain=None, group=None, filters=None): users = [] - if VERSIONS.active < 3: - kwargs = {"tenant_id": project} - else: - kwargs = { - "project": project, - "domain": domain, - "group": group - } - if filters is not None: - kwargs.update(filters) + kwargs = { + "project": project, + "domain": domain, + "group": group + } + if filters is not None: + kwargs.update(filters) if 'id' in kwargs: try: users = [user_get(request, kwargs['id'])] @@ -446,14 +410,10 @@ def user_create(request, name=None, email=None, password=None, project=None, enabled=None, domain=None, description=None, **data): manager = keystoneclient(request, admin=True).users try: - if VERSIONS.active < 3: - user = manager.create(name, password, email, project, enabled) - return VERSIONS.upgrade_v2_user(user) - else: - return manager.create(name, password=password, email=email, - default_project=project, enabled=enabled, - domain=domain, description=description, - **data) + return manager.create(name, password=password, email=email, + default_project=project, enabled=enabled, + domain=domain, description=description, + **data) except keystone_exceptions.Conflict: raise exceptions.Conflict() @@ -472,59 +432,20 @@ def user_get(request, user_id, admin=True): @profiler.trace def user_update(request, user, **data): manager = keystoneclient(request, admin=True).users - error = None if not keystone_can_edit_user(): raise keystone_exceptions.ClientException( 405, _("Identity service does not allow editing user data.")) - - # The v2 API updates user model and default project separately - if VERSIONS.active < 3: - # Update user details - try: - user = manager.update(user, **data) - except keystone_exceptions.Conflict: - raise exceptions.Conflict() - except Exception: - error = exceptions.handle(request, ignore=True) - - if "project" in data: - project = data.pop('project') - - # Update default tenant - try: - user_update_tenant(request, user, project) - user.tenantId = project - except Exception: - error = exceptions.handle(request, ignore=True) - - # Check for existing roles - # Show a warning if no role exists for the project - user_roles = roles_for_user(request, user, project) - if not user_roles: - messages.warning(request, - _('User %s has no role defined for ' - 'that project.') - % data.get('name', None)) - - if error is not None: - raise error - - # v3 API is so much simpler... - else: - try: - user = manager.update(user, **data) - except keystone_exceptions.Conflict: - raise exceptions.Conflict() + try: + user = manager.update(user, **data) + except keystone_exceptions.Conflict: + raise exceptions.Conflict() @profiler.trace def user_update_enabled(request, user, enabled): manager = keystoneclient(request, admin=True).users - if VERSIONS.active < 3: - manager.update_enabled(user, enabled) - else: - manager.update(user, enabled=enabled) + manager.update(user, enabled=enabled) @profiler.trace @@ -535,16 +456,13 @@ def user_update_password(request, user, password, admin=True): 405, _("Identity service does not allow editing user password.")) manager = keystoneclient(request, admin=admin).users - if VERSIONS.active < 3: - manager.update_password(user, password) - else: - manager.update(user, password=password) + manager.update(user, password=password) def user_verify_admin_password(request, admin_password): # attempt to create a new client instance with admin password to # verify if it's correct. - client = keystone_client_v2 if VERSIONS.active < 3 else keystone_client_v3 + client = keystone_client_v3 try: endpoint = _get_endpoint_url(request, 'publicURL') insecure = settings.OPENSTACK_SSL_NO_VERIFY @@ -565,21 +483,14 @@ def user_verify_admin_password(request, admin_password): @profiler.trace def user_update_own_password(request, origpassword, password): client = keystoneclient(request, admin=False) - if VERSIONS.active < 3: - client.user_id = request.user.id - return client.users.update_own_password(origpassword, password) - else: - client.users.client.session.auth.user_id = request.user.id - return client.users.update_password(origpassword, password) + client.users.client.session.auth.user_id = request.user.id + return client.users.update_password(origpassword, password) @profiler.trace def user_update_tenant(request, user, project, admin=True): manager = keystoneclient(request, admin=admin).users - if VERSIONS.active < 3: - return manager.update_tenant(user, project) - else: - return manager.update(user, project=project) + return manager.update(user, project=project) @profiler.trace @@ -689,9 +600,6 @@ def get_project_groups_roles(request, project): def role_assignments_list(request, project=None, user=None, role=None, group=None, domain=None, effective=False, include_subtree=True, include_names=False): - if VERSIONS.active < 3: - raise exceptions.NotAvailable - if include_subtree: domain = None @@ -751,10 +659,7 @@ def role_list(request, filters=None): def roles_for_user(request, user, project=None, domain=None): """Returns a list of user roles scoped to a project or domain.""" manager = keystoneclient(request, admin=True).roles - if VERSIONS.active < 3: - return manager.roles_for_user(user, project) - else: - return manager.list(user=user, domain=domain, project=project) + return manager.list(user=user, domain=domain, project=project) @profiler.trace @@ -793,26 +698,18 @@ def remove_domain_user_role(request, user, role, domain=None): @profiler.trace def get_project_users_roles(request, project): users_roles = collections.defaultdict(list) - if VERSIONS.active < 3: - project_users = user_list(request, project=project) + project_role_assignments = role_assignments_list(request, + project=project) + for role_assignment in project_role_assignments: + if not hasattr(role_assignment, 'user'): + continue + user_id = role_assignment.user['id'] + role_id = role_assignment.role['id'] - for user in project_users: - roles = roles_for_user(request, user.id, project) - roles_ids = [role.id for role in roles] - users_roles[user.id].extend(roles_ids) - else: - project_role_assignments = role_assignments_list(request, - project=project) - for role_assignment in project_role_assignments: - if not hasattr(role_assignment, 'user'): - continue - user_id = role_assignment.user['id'] - role_id = role_assignment.role['id'] - - # filter by project_id - if ('project' in role_assignment.scope and - role_assignment.scope['project']['id'] == project): - users_roles[user_id].append(role_id) + # filter by project_id + if ('project' in role_assignment.scope and + role_assignment.scope['project']['id'] == project): + users_roles[user_id].append(role_id) return users_roles @@ -821,11 +718,8 @@ def add_tenant_user_role(request, project=None, user=None, role=None, group=None, domain=None): """Adds a role for a user on a tenant.""" manager = keystoneclient(request, admin=True).roles - if VERSIONS.active < 3: - manager.add_user_role(user, role, project) - else: - manager.grant(role, user=user, project=project, - group=group, domain=domain) + manager.grant(role, user=user, project=project, + group=group, domain=domain) @profiler.trace @@ -833,11 +727,8 @@ def remove_tenant_user_role(request, project=None, user=None, role=None, group=None, domain=None): """Removes a given single role for a user from a tenant.""" manager = keystoneclient(request, admin=True).roles - if VERSIONS.active < 3: - return manager.remove_user_role(user, role, project) - else: - return manager.revoke(role, user=user, project=project, - group=group, domain=domain) + return manager.revoke(role, user=user, project=project, + group=group, domain=domain) def remove_tenant_user(request, project=None, user=None, domain=None): @@ -907,9 +798,8 @@ def ec2_manager(request): if hasattr(client, 'ec2'): return client.ec2 - # Keystoneclient 4.0 was released without the ec2 creds manager. - from keystoneclient.v2_0 import ec2 - return ec2.CredentialsManager(client) + from keystoneclient.v3 import ec2 + return ec2.EC2Manager(client) @profiler.trace diff --git a/openstack_dashboard/api/swift.py b/openstack_dashboard/api/swift.py index 0947c130f7..5d10fc4456 100644 --- a/openstack_dashboard/api/swift.py +++ b/openstack_dashboard/api/swift.py @@ -128,7 +128,7 @@ def swift_api(request): preauthurl=endpoint, cacert=cacert, insecure=insecure, - auth_version="2.0") + auth_version="3") @profiler.trace diff --git a/openstack_dashboard/dashboards/identity/application_credentials/views.py b/openstack_dashboard/dashboards/identity/application_credentials/views.py index b5e61f7fc8..550286a254 100644 --- a/openstack_dashboard/dashboards/identity/application_credentials/views.py +++ b/openstack_dashboard/dashboards/identity/application_credentials/views.py @@ -19,8 +19,6 @@ from django.urls import reverse from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from openstack_auth import utils - from horizon import exceptions from horizon import forms from horizon import tables @@ -121,7 +119,6 @@ def _get_context(request): auth_url = api.base.url_for(request, 'identity', endpoint_type='publicURL') - auth_url, url_fixed = utils.fix_auth_url_version_prefix(auth_url) interface = 'public' region = getattr(request.user, 'services_region', '') app_cred = request.session['application_credential'] diff --git a/openstack_dashboard/dashboards/identity/domains/panel.py b/openstack_dashboard/dashboards/identity/domains/panel.py index 7974e36e44..434ad8a76c 100644 --- a/openstack_dashboard/dashboards/identity/domains/panel.py +++ b/openstack_dashboard/dashboards/identity/domains/panel.py @@ -30,9 +30,6 @@ class Domains(horizon.Panel): return keystone.VERSIONS.active >= 3 def can_access(self, context): - if keystone.VERSIONS.active < 3: - return super(Domains, self).can_access(context) - request = context['request'] domain_token = request.session.get('domain_token') return super(Domains, self).can_access(context) and domain_token diff --git a/openstack_dashboard/dashboards/identity/projects/tables.py b/openstack_dashboard/dashboards/identity/projects/tables.py index 1f4e9b3c27..079092b7d7 100644 --- a/openstack_dashboard/dashboards/identity/projects/tables.py +++ b/openstack_dashboard/dashboards/identity/projects/tables.py @@ -149,11 +149,8 @@ class ModifyQuotas(tables.LinkAction): policy_rules = (('compute', "os_compute_api:os-quota-sets:update"),) def allowed(self, request, datum): - if api.keystone.VERSIONS.active < 3: - return True - else: - return (api.keystone.is_cloud_admin(request) and - quotas.enabled_quotas(request)) + return (api.keystone.is_cloud_admin(request) and + quotas.enabled_quotas(request)) def get_link_url(self, project): step = 'update_quotas' @@ -198,13 +195,10 @@ class DeleteTenantsAction(policy.PolicyTargetMixin, tables.DeleteAction): class TenantFilterAction(tables.FilterAction): - if api.keystone.VERSIONS.active < 3: - filter_type = "query" - else: - filter_type = "server" - filter_choices = (('name', _("Project Name ="), True), - ('id', _("Project ID ="), True), - ('enabled', _("Enabled ="), True, _('e.g. Yes/No'))) + filter_type = "server" + filter_choices = (('name', _("Project Name ="), True), + ('id', _("Project ID ="), True), + ('enabled', _("Enabled ="), True, _('e.g. Yes/No'))) class UpdateRow(tables.Row): diff --git a/openstack_dashboard/dashboards/identity/users/tables.py b/openstack_dashboard/dashboards/identity/users/tables.py index edb8f7e34e..fcf2cf9d60 100644 --- a/openstack_dashboard/dashboards/identity/users/tables.py +++ b/openstack_dashboard/dashboards/identity/users/tables.py @@ -21,7 +21,6 @@ from openstack_dashboard import policy ENABLE = 0 DISABLE = 1 -KEYSTONE_V2_ENABLED = api.keystone.VERSIONS.active < 3 class CreateUserLink(tables.LinkAction): @@ -186,7 +185,6 @@ class UsersTable(tables.DataTable): form_field=forms.CharField(required=False)) description = tables.Column(lambda obj: getattr(obj, 'description', None), verbose_name=_('Description'), - hidden=KEYSTONE_V2_ENABLED, form_field=forms.CharField( widget=forms.Textarea(attrs={'rows': 4}), required=False)) diff --git a/openstack_dashboard/dashboards/project/api_access/tables.py b/openstack_dashboard/dashboards/project/api_access/tables.py index 03f225bfdf..d84c1a2915 100644 --- a/openstack_dashboard/dashboards/project/api_access/tables.py +++ b/openstack_dashboard/dashboards/project/api_access/tables.py @@ -16,8 +16,6 @@ from django.conf import settings from django.template.defaultfilters import title from django.utils.translation import ugettext_lazy as _ -from openstack_auth import utils - from horizon import tables from openstack_dashboard import api from openstack_dashboard.dashboards.project.api_access import forms @@ -63,8 +61,7 @@ class DownloadOpenRC(tables.LinkAction): url = "horizon:project:api_access:openrc" def allowed(self, request, datum=None): - return (settings.SHOW_OPENRC_FILE and - utils.get_keystone_version() >= 3) + return settings.SHOW_OPENRC_FILE class ViewCredentials(tables.LinkAction): diff --git a/openstack_dashboard/dashboards/project/api_access/views.py b/openstack_dashboard/dashboards/project/api_access/views.py index b53db68244..d2d32f4364 100644 --- a/openstack_dashboard/dashboards/project/api_access/views.py +++ b/openstack_dashboard/dashboards/project/api_access/views.py @@ -24,8 +24,6 @@ from django.template.loader import render_to_string from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from openstack_auth import utils - from horizon import exceptions from horizon import forms from horizon import messages @@ -131,9 +129,6 @@ def download_rc_file(request): except KeyError: project_domain_id = '' context['project_domain_id'] = project_domain_id - # sanity fix for removing v2.0 from the url if present - context['auth_url'], _ = utils.fix_auth_url_version_prefix( - context['auth_url']) context['os_identity_api_version'] = 3 context['os_auth_version'] = 3 return _download_rc_file_for_template(request, context, template) @@ -150,14 +145,9 @@ def download_clouds_yaml_file(request): region_tuple[1] for region_tuple in settings.AVAILABLE_REGIONS ] - if utils.get_keystone_version() >= 3: - # make v3 specific changes - context['user_domain_name'] = request.user.user_domain_name - # sanity fix for removing v2.0 from the url if present - context['auth_url'], _ = utils.fix_auth_url_version_prefix( - context['auth_url']) - context['os_identity_api_version'] = 3 - context['os_auth_version'] = 3 + context['user_domain_name'] = request.user.user_domain_name + context['os_identity_api_version'] = 3 + context['os_auth_version'] = 3 return _download_rc_file_for_template(request, context, template, 'clouds.yaml') diff --git a/openstack_dashboard/test/integration_tests/tests/test_credentials.py b/openstack_dashboard/test/integration_tests/tests/test_credentials.py index 1cdba9a55a..40ba8782fd 100644 --- a/openstack_dashboard/test/integration_tests/tests/test_credentials.py +++ b/openstack_dashboard/test/integration_tests/tests/test_credentials.py @@ -15,7 +15,6 @@ from os.path import join from os import remove from horizon.test import firefox_binary -from openstack_dashboard.test.integration_tests import decorators from openstack_dashboard.test.integration_tests import helpers @@ -41,27 +40,6 @@ class TestDownloadRCFile(helpers.AdminTestCase): self.addCleanup(cleanup) - @decorators.skip_because(bugs=['1792028']) - def test_download_rc_v2_file(self): - """This is a basic scenario test: - - Steps: - 1) Login to Horizon Dashboard as admin user - 2) Navigate to Project > API Access tab - 3) Click on "Download OpenStack RC File" dropdown button - 4) Click on "OpenStack RC File (Identity API v2.0" button - 5) File named by template "-openrc.sh" must be downloaded - 6) Check that username, tenant name and tenant id correspond to current - username, tenant name and tenant id - """ - api_access_page = self.home_pg.\ - go_to_project_apiaccesspage() - api_access_page.download_openstack_rc_file( - 2, self._directory, self._openrc_template) - cred_dict = api_access_page.get_credentials_from_file( - 2, self._directory, self._openrc_template) - self.assertEqual(cred_dict, self.actual_dict) - def test_download_rc_v3_file(self): """This is a basic scenario test: diff --git a/openstack_dashboard/test/unit/api/rest/test_keystone.py b/openstack_dashboard/test/unit/api/rest/test_keystone.py index 95d95d9e90..a462db4948 100644 --- a/openstack_dashboard/test/unit/api/rest/test_keystone.py +++ b/openstack_dashboard/test/unit/api/rest/test_keystone.py @@ -29,10 +29,10 @@ class KeystoneRestTestCase(test.TestCase): @test.create_mocks({api.keystone: ['get_version']}) def test_version_get(self): request = self.mock_rest_request() - self.mock_get_version.return_value = '2.0' + self.mock_get_version.return_value = '3' response = keystone.Version().get(request) self.assertStatusCode(response, 200) - self.assertEqual(response.json, {"version": "2.0"}) + self.assertEqual(response.json, {"version": "3"}) self.mock_get_version.assert_called_once_with() # diff --git a/openstack_dashboard/test/unit/api/test_base.py b/openstack_dashboard/test/unit/api/test_base.py index cee4801222..5c93b0427f 100644 --- a/openstack_dashboard/test/unit/api/test_base.py +++ b/openstack_dashboard/test/unit/api/test_base.py @@ -237,7 +237,7 @@ class ApiVersionTests(test.TestCase): self.previous_settings = settings.OPENSTACK_API_VERSIONS settings.OPENSTACK_API_VERSIONS = { "data-processing": 1.1, - "identity": "2.0", + "identity": "3", "volume": 1 } # Make sure cached data from other tests doesn't interfere diff --git a/releasenotes/notes/drop-keystone-v2-support-04c7658f3bea67ac.yaml b/releasenotes/notes/drop-keystone-v2-support-04c7658f3bea67ac.yaml new file mode 100644 index 0000000000..3b166de563 --- /dev/null +++ b/releasenotes/notes/drop-keystone-v2-support-04c7658f3bea67ac.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + Keystone API V2 support has been dropped in Train release. Keystone V2 API + support was deprecated in Stein release. If you use Keystone V2 before, you + should update the `OPENSTACK_API_VERSIONS` configuration option to use + Keystone V3 API.