Remove support for non-keystone auth systems

The support for non-keystone auth systems and plugins
was deprecated with 1f11840dd8
back in the 3.1.0 release in mitaka.

Now that we're working on shoring up the support for
keystone auth sessions in the client and eventually moving
to remove support for the non-session HTTPClient, we should
also remove the support to load non-keystone auth plugins.

The whole concept of non-keystone auth systems/plugins is
a legacy artifact and is a barrier to interoperability which
is a goal of nova and OpenStack in general.

Change-Id: Ia649db257c416ca054977812ecb3f1a8044fa584
This commit is contained in:
Matt Riedemann 2016-10-19 16:01:42 -04:00
parent f5647e35c3
commit daa9bdc823
8 changed files with 29 additions and 622 deletions

View File

@ -1,150 +0,0 @@
# Copyright 2013 OpenStack Foundation
# Copyright 2013 Spanish National Research Council.
# All Rights Reserved.
#
# 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 logging
import pkg_resources
import six
from novaclient import exceptions
from novaclient import utils
logger = logging.getLogger(__name__)
_discovered_plugins = {}
def discover_auth_systems():
"""Discover the available auth-systems.
This won't take into account the old style auth-systems.
"""
ep_name = 'openstack.client.auth_plugin'
for ep in pkg_resources.iter_entry_points(ep_name):
try:
# FIXME(dhellmann): It would be better to use stevedore
# here, since it abstracts this difference in behavior
# between versions of setuptools, but this seemed like a
# more expedient fix.
if hasattr(ep, 'resolve') and hasattr(ep, 'require'):
auth_plugin = ep.resolve()
else:
auth_plugin = ep.load(require=False)
except (ImportError, pkg_resources.UnknownExtra, AttributeError) as e:
logger.debug("ERROR: Cannot load auth plugin %s" % ep.name)
logger.debug(e, exc_info=1)
else:
_discovered_plugins[ep.name] = auth_plugin
def load_auth_system_opts(parser):
"""Load options needed by the available auth-systems into a parser.
This function will try to populate the parser with options from the
available plugins.
"""
for name, auth_plugin in six.iteritems(_discovered_plugins):
add_opts_fn = getattr(auth_plugin, "add_opts", None)
if add_opts_fn:
group = parser.add_argument_group("Auth-system '%s' options" %
name)
add_opts_fn(group)
def load_plugin(auth_system):
if auth_system in _discovered_plugins:
return _discovered_plugins[auth_system]()
# NOTE(aloga): If we arrive here, the plugin will be an old-style one,
# so we have to create a fake AuthPlugin for it.
return DeprecatedAuthPlugin(auth_system)
class BaseAuthPlugin(object):
"""Base class for authentication plugins.
An authentication plugin needs to override at least the authenticate
method to be a valid plugin.
"""
def __init__(self):
self.opts = {}
def get_auth_url(self):
"""Return the auth url for the plugin (if any)."""
return None
@staticmethod
def add_opts(parser):
"""Populate and return the parser with the options for this plugin.
If the plugin does not need any options, it should return the same
parser untouched.
"""
return parser
def parse_opts(self, args):
"""Parse the actual auth-system options if any.
This method is expected to populate the attribute self.opts with a
dict containing the options and values needed to make authentication.
If the dict is empty, the client should assume that it needs the same
options as the 'keystone' auth system (i.e. os_username and
os_password).
Returns the self.opts dict.
"""
return self.opts
def authenticate(self, cls, auth_url):
"""Authenticate using plugin defined method."""
raise exceptions.AuthSystemNotFound(self.auth_system)
class DeprecatedAuthPlugin(object):
"""Class to mimic the AuthPlugin class for deprecated auth systems.
Old auth systems only define two entry points: openstack.client.auth_url
and openstack.client.authenticate. This class will load those entry points
into a class similar to a valid AuthPlugin.
"""
def __init__(self, auth_system):
self.auth_system = auth_system
def authenticate(cls, auth_url):
raise exceptions.AuthSystemNotFound(self.auth_system)
self.opts = {}
self.get_auth_url = lambda: None
self.authenticate = authenticate
self._load_endpoints()
def _load_endpoints(self):
ep_name = 'openstack.client.auth_url'
fn = utils.load_entry_point(ep_name, name=self.auth_system)
if fn:
self.get_auth_url = fn
ep_name = 'openstack.client.authenticate'
fn = utils.load_entry_point(ep_name, name=self.auth_system)
if fn:
self.authenticate = fn
def parse_opts(self, args):
return self.opts

View File

@ -156,8 +156,7 @@ class HTTPClient(object):
service_name=None, volume_service_name=None, service_name=None, volume_service_name=None,
timings=False, bypass_url=None, timings=False, bypass_url=None,
os_cache=False, no_cache=True, os_cache=False, no_cache=True,
http_log_debug=False, auth_system='keystone', http_log_debug=False, auth_token=None,
auth_plugin=None, auth_token=None,
cacert=None, tenant_id=None, user_id=None, cacert=None, tenant_id=None, user_id=None,
connection_pool=False, api_version=None, connection_pool=False, api_version=None,
logger=None): logger=None):
@ -177,13 +176,6 @@ class HTTPClient(object):
# been proven invalid # been proven invalid
self.password_func = None self.password_func = None
if auth_system and auth_system != 'keystone' and not auth_plugin:
raise exceptions.AuthSystemNotFound(auth_system)
if not auth_url and auth_system and auth_system != 'keystone':
auth_url = auth_plugin.get_auth_url()
if not auth_url:
raise exceptions.EndpointNotFound()
self.auth_url = auth_url.rstrip('/') if auth_url else auth_url self.auth_url = auth_url.rstrip('/') if auth_url else auth_url
self.version = 'v1.1' self.version = 'v1.1'
self.region_name = region_name self.region_name = region_name
@ -217,8 +209,6 @@ class HTTPClient(object):
else: else:
self.verify_cert = True self.verify_cert = True
self.auth_system = auth_system
self.auth_plugin = auth_plugin
self._session = None self._session = None
self._current_url = None self._current_url = None
self._logger = logger or logging.getLogger(__name__) self._logger = logger or logging.getLogger(__name__)
@ -589,10 +579,7 @@ class HTTPClient(object):
auth_url = self.auth_url auth_url = self.auth_url
if self.version == "v2.0": # FIXME(chris): This should be better. if self.version == "v2.0": # FIXME(chris): This should be better.
while auth_url: while auth_url:
if not self.auth_system or self.auth_system == 'keystone': auth_url = self._v2_auth(auth_url)
auth_url = self._v2_auth(auth_url)
else:
auth_url = self._plugin_auth(auth_url)
# Are we acting on behalf of another user via an # Are we acting on behalf of another user via an
# existing token? If so, our actual endpoints may # existing token? If so, our actual endpoints may
@ -659,9 +646,6 @@ class HTTPClient(object):
else: else:
raise exceptions.from_response(resp, body, url) raise exceptions.from_response(resp, body, url)
def _plugin_auth(self, auth_url):
return self.auth_plugin.authenticate(self, auth_url)
def _v2_auth(self, url): def _v2_auth(self, url):
"""Authenticate against a v2.0 auth service.""" """Authenticate against a v2.0 auth service."""
if self.auth_token: if self.auth_token:
@ -707,7 +691,6 @@ def _construct_http_client(username=None, password=None, project_id=None,
service_name=None, volume_service_name=None, service_name=None, volume_service_name=None,
timings=False, bypass_url=None, os_cache=False, timings=False, bypass_url=None, os_cache=False,
no_cache=True, http_log_debug=False, no_cache=True, http_log_debug=False,
auth_system='keystone', auth_plugin=None,
auth_token=None, cacert=None, tenant_id=None, auth_token=None, cacert=None, tenant_id=None,
user_id=None, connection_pool=False, session=None, user_id=None, connection_pool=False, session=None,
auth=None, user_agent='python-novaclient', auth=None, user_agent='python-novaclient',
@ -738,8 +721,6 @@ def _construct_http_client(username=None, password=None, project_id=None,
auth_token=auth_token, auth_token=auth_token,
insecure=insecure, insecure=insecure,
timeout=timeout, timeout=timeout,
auth_system=auth_system,
auth_plugin=auth_plugin,
proxy_token=proxy_token, proxy_token=proxy_token,
proxy_tenant_id=proxy_tenant_id, proxy_tenant_id=proxy_tenant_id,
region_name=region_name, region_name=region_name,

View File

@ -54,15 +54,6 @@ class NoUniqueMatch(Exception):
pass pass
class AuthSystemNotFound(Exception):
"""When the user specify a AuthSystem but not installed."""
def __init__(self, auth_system):
self.auth_system = auth_system
def __str__(self):
return "AuthSystemNotFound: %s" % repr(self.auth_system)
class NoTokenLookupException(Exception): class NoTokenLookupException(Exception):
"""This form of authentication does not support looking up """This form of authentication does not support looking up
endpoints from an existing token. endpoints from an existing token.

View File

@ -23,7 +23,6 @@ import argparse
import getpass import getpass
import logging import logging
import sys import sys
import warnings
from keystoneauth1 import loading from keystoneauth1 import loading
from oslo_utils import encodeutils from oslo_utils import encodeutils
@ -41,7 +40,6 @@ except ImportError:
import novaclient import novaclient
from novaclient import api_versions from novaclient import api_versions
import novaclient.auth_plugin
from novaclient import client from novaclient import client
from novaclient import exceptions as exc from novaclient import exceptions as exc
import novaclient.extension import novaclient.extension
@ -455,12 +453,6 @@ class OpenStackComputeShell(object):
default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'), default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'),
help=_('Defaults to env[OS_REGION_NAME].')) help=_('Defaults to env[OS_REGION_NAME].'))
parser.add_argument(
'--os-auth-system',
metavar='<auth-system>',
default=utils.env('OS_AUTH_SYSTEM'),
help=argparse.SUPPRESS)
parser.add_argument( parser.add_argument(
'--service-type', '--service-type',
metavar='<service-type>', metavar='<service-type>',
@ -509,9 +501,6 @@ class OpenStackComputeShell(object):
help=_("Use this API endpoint instead of the Service Catalog. " help=_("Use this API endpoint instead of the Service Catalog. "
"Defaults to env[NOVACLIENT_BYPASS_URL].")) "Defaults to env[NOVACLIENT_BYPASS_URL]."))
# The auth-system-plugins might require some extra options
novaclient.auth_plugin.load_auth_system_opts(parser)
self._append_global_identity_args(parser, argv) self._append_global_identity_args(parser, argv)
return parser return parser
@ -639,9 +628,6 @@ class OpenStackComputeShell(object):
skip_auth = do_help or ( skip_auth = do_help or (
'bash-completion' in argv) 'bash-completion' in argv)
# Discover available auth plugins
novaclient.auth_plugin.discover_auth_systems()
if not args.os_compute_api_version: if not args.os_compute_api_version:
api_version = api_versions.get_api_version( api_version = api_versions.get_api_version(
DEFAULT_MAJOR_OS_COMPUTE_API_VERSION) DEFAULT_MAJOR_OS_COMPUTE_API_VERSION)
@ -658,7 +644,6 @@ class OpenStackComputeShell(object):
args, 'os_project_id', getattr(args, 'os_tenant_id', None)) args, 'os_project_id', getattr(args, 'os_tenant_id', None))
os_auth_url = args.os_auth_url os_auth_url = args.os_auth_url
os_region_name = args.os_region_name os_region_name = args.os_region_name
os_auth_system = args.os_auth_system
if "v2.0" not in os_auth_url: if "v2.0" not in os_auth_url:
# NOTE(andreykurilin): assume that keystone V3 is used and try to # NOTE(andreykurilin): assume that keystone V3 is used and try to
@ -691,15 +676,6 @@ class OpenStackComputeShell(object):
auth_token = getattr(args, 'os_token', None) auth_token = getattr(args, 'os_token', None)
management_url = bypass_url if bypass_url else None management_url = bypass_url if bypass_url else None
if os_auth_system and os_auth_system != "keystone":
warnings.warn(_(
'novaclient auth plugins that are not keystone are deprecated.'
' Auth plugins should now be done as plugins to keystoneauth'
' and selected with --os-auth-type or OS_AUTH_TYPE'))
auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system)
else:
auth_plugin = None
if not endpoint_type: if not endpoint_type:
endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE
@ -717,25 +693,20 @@ class OpenStackComputeShell(object):
# Expired tokens are handled by client.py:_cs_request # Expired tokens are handled by client.py:_cs_request
must_auth = not (auth_token and management_url) must_auth = not (auth_token and management_url)
# Do not use Keystone session for cases with no session support. The # Do not use Keystone session for cases with no session support.
# presence of auth_plugin means os_auth_system is present and is not
# keystone.
use_session = True use_session = True
if auth_plugin or bypass_url or os_cache or volume_service_name: if bypass_url or os_cache or volume_service_name:
use_session = False use_session = False
# FIXME(usrleon): Here should be restrict for project id same as # FIXME(usrleon): Here should be restrict for project id same as
# for os_username or os_password but for compatibility it is not. # for os_username or os_password but for compatibility it is not.
if must_auth and not skip_auth: if must_auth and not skip_auth:
if auth_plugin:
auth_plugin.parse_opts(args)
if not auth_plugin or not auth_plugin.opts: if not os_username and not os_user_id:
if not os_username and not os_user_id: raise exc.CommandError(
raise exc.CommandError( _("You must provide a username "
_("You must provide a username " "or user ID via --os-username, --os-user-id, "
"or user ID via --os-username, --os-user-id, " "env[OS_USERNAME] or env[OS_USER_ID]"))
"env[OS_USERNAME] or env[OS_USER_ID]"))
if not any([os_project_name, os_project_id]): if not any([os_project_name, os_project_id]):
raise exc.CommandError(_("You must provide a project name or" raise exc.CommandError(_("You must provide a project name or"
@ -746,16 +717,9 @@ class OpenStackComputeShell(object):
" interchangeably.")) " interchangeably."))
if not os_auth_url: if not os_auth_url:
if os_auth_system and os_auth_system != 'keystone': raise exc.CommandError(
os_auth_url = auth_plugin.get_auth_url() _("You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL]."))
if not os_auth_url:
raise exc.CommandError(
_("You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL] "
"or specify an auth_system which defines a "
"default url with --os-auth-system "
"or env[OS_AUTH_SYSTEM]"))
if use_session: if use_session:
# Not using Nova auth plugin, so use keystone # Not using Nova auth plugin, so use keystone
@ -792,8 +756,7 @@ class OpenStackComputeShell(object):
auth_url=os_auth_url, insecure=insecure, auth_url=os_auth_url, insecure=insecure,
region_name=os_region_name, endpoint_type=endpoint_type, region_name=os_region_name, endpoint_type=endpoint_type,
extensions=self.extensions, service_type=service_type, extensions=self.extensions, service_type=service_type,
service_name=service_name, auth_system=os_auth_system, service_name=service_name, auth_token=auth_token,
auth_plugin=auth_plugin, auth_token=auth_token,
volume_service_name=volume_service_name, volume_service_name=volume_service_name,
timings=args.timings, bypass_url=bypass_url, timings=args.timings, bypass_url=bypass_url,
os_cache=os_cache, http_log_debug=args.debug, os_cache=os_cache, http_log_debug=args.debug,
@ -857,8 +820,7 @@ class OpenStackComputeShell(object):
auth_url=os_auth_url, insecure=insecure, auth_url=os_auth_url, insecure=insecure,
region_name=os_region_name, endpoint_type=endpoint_type, region_name=os_region_name, endpoint_type=endpoint_type,
extensions=self.extensions, service_type=service_type, extensions=self.extensions, service_type=service_type,
service_name=service_name, auth_system=os_auth_system, service_name=service_name, auth_token=auth_token,
auth_plugin=auth_plugin, auth_token=auth_token,
volume_service_name=volume_service_name, volume_service_name=volume_service_name,
timings=args.timings, bypass_url=bypass_url, timings=args.timings, bypass_url=bypass_url,
os_cache=os_cache, http_log_debug=args.debug, os_cache=os_cache, http_log_debug=args.debug,
@ -870,11 +832,6 @@ class OpenStackComputeShell(object):
if must_auth: if must_auth:
helper = SecretsHelper(args, self.cs.client) helper = SecretsHelper(args, self.cs.client)
self.cs.client.keyring_saver = helper self.cs.client.keyring_saver = helper
if (auth_plugin and auth_plugin.opts and
"os_password" not in auth_plugin.opts):
use_pw = False
else:
use_pw = True
tenant_id = helper.tenant_id tenant_id = helper.tenant_id
# Allow commandline to override cache # Allow commandline to override cache
@ -887,7 +844,7 @@ class OpenStackComputeShell(object):
self.cs.client.auth_token = auth_token self.cs.client.auth_token = auth_token
self.cs.client.management_url = management_url self.cs.client.management_url = management_url
self.cs.client.password_func = lambda: helper.password self.cs.client.password_func = lambda: helper.password
elif use_pw: else:
# We're missing something, so auth with user/pass and save # We're missing something, so auth with user/pass and save
# the result in our helper. # the result in our helper.
self.cs.client.password = helper.password self.cs.client.password = helper.password

View File

@ -1,358 +0,0 @@
# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# 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 argparse
from keystoneauth1 import fixture
import mock
import pkg_resources
import requests
try:
import json
except ImportError:
import simplejson as json
from novaclient import auth_plugin
from novaclient import client
from novaclient import exceptions
from novaclient.tests.unit import utils
def mock_http_request(resp=None):
"""Mock an HTTP Request."""
if not resp:
resp = fixture.V2Token()
resp.set_scope()
s = resp.add_service('compute')
s.add_endpoint("http://localhost:8774/v1.1", region='RegionOne')
auth_response = utils.TestResponse({
"status_code": 200,
"text": json.dumps(resp),
})
return mock.Mock(return_value=(auth_response))
def requested_headers(cs):
"""Return requested passed headers."""
return {
'User-Agent': cs.client.USER_AGENT,
'Content-Type': 'application/json',
'Accept': 'application/json',
}
class DeprecatedAuthPluginTest(utils.TestCase):
def test_auth_system_success(self):
class MockEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return self.authenticate
def resolve(self):
return self.authenticate
def authenticate(self, cls, auth_url):
cls._authenticate(auth_url, {"fake": "me"})
def mock_iter_entry_points(_type, name):
if _type == 'openstack.client.authenticate':
return [MockEntrypoint("fake", "fake", ["fake"])]
else:
return []
mock_request = mock_http_request()
@mock.patch.object(pkg_resources, "iter_entry_points",
mock_iter_entry_points)
@mock.patch.object(requests, "request", mock_request)
def test_auth_call():
plugin = auth_plugin.DeprecatedAuthPlugin("fake")
cs = client.Client("2", "username", "password", "project_id",
utils.AUTH_URL_V2, auth_system="fake",
auth_plugin=plugin)
cs.client.authenticate()
headers = requested_headers(cs)
token_url = cs.client.auth_url + "/tokens"
mock_request.assert_called_with(
"POST",
token_url,
headers=headers,
data='{"fake": "me"}',
allow_redirects=True,
**self.TEST_REQUEST_BASE)
test_auth_call()
def test_auth_system_not_exists(self):
def mock_iter_entry_points(_t, name=None):
return [pkg_resources.EntryPoint("fake", "fake", ["fake"])]
mock_request = mock_http_request()
@mock.patch.object(pkg_resources, "iter_entry_points",
mock_iter_entry_points)
@mock.patch.object(requests, "request", mock_request)
def test_auth_call():
auth_plugin.discover_auth_systems()
plugin = auth_plugin.DeprecatedAuthPlugin("notexists")
cs = client.Client("2", "username", "password", "project_id",
utils.AUTH_URL_V2, auth_system="notexists",
auth_plugin=plugin)
self.assertRaises(exceptions.AuthSystemNotFound,
cs.client.authenticate)
test_auth_call()
def test_auth_system_defining_auth_url(self):
class MockAuthUrlEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return self.auth_url
def resolve(self):
return self.auth_url
def auth_url(self):
return "http://faked/v2.0"
class MockAuthenticateEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return self.authenticate
def resolve(self):
return self.authenticate
def authenticate(self, cls, auth_url):
cls._authenticate(auth_url, {"fake": "me"})
def mock_iter_entry_points(_type, name):
if _type == 'openstack.client.auth_url':
return [MockAuthUrlEntrypoint("fakewithauthurl",
"fakewithauthurl",
["auth_url"])]
elif _type == 'openstack.client.authenticate':
return [MockAuthenticateEntrypoint("fakewithauthurl",
"fakewithauthurl",
["authenticate"])]
else:
return []
mock_request = mock_http_request()
@mock.patch.object(pkg_resources, "iter_entry_points",
mock_iter_entry_points)
@mock.patch.object(requests, "request", mock_request)
def test_auth_call():
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
cs = client.Client("2", "username", "password", "project_id",
auth_system="fakewithauthurl",
auth_plugin=plugin)
cs.client.authenticate()
self.assertEqual("http://faked/v2.0", cs.client.auth_url)
test_auth_call()
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_client_raises_exc_without_auth_url(self, mock_iter_entry_points):
class MockAuthUrlEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return self.auth_url
def resolve(self):
return self.auth_url
def auth_url(self):
return None
mock_iter_entry_points.side_effect = lambda _t, name: [
MockAuthUrlEntrypoint("fakewithauthurl",
"fakewithauthurl",
["auth_url"])]
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
self.assertRaises(
exceptions.EndpointNotFound,
client.Client, "2", "username", "password", "project_id",
auth_system="fakewithauthurl", auth_plugin=plugin)
class AuthPluginTest(utils.TestCase):
@mock.patch.object(requests, "request")
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_auth_system_success(self, mock_iter_entry_points, mock_request):
"""Test that we can authenticate using the auth system."""
class MockEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return FakePlugin
def resolve(self):
return FakePlugin
class FakePlugin(auth_plugin.BaseAuthPlugin):
def authenticate(self, cls, auth_url):
cls._authenticate(auth_url, {"fake": "me"})
mock_iter_entry_points.side_effect = lambda _t, name=None: [
MockEntrypoint("fake", "fake", ["FakePlugin"])]
mock_request.side_effect = mock_http_request()
auth_plugin.discover_auth_systems()
plugin = auth_plugin.load_plugin("fake")
cs = client.Client("2", "username", "password", "project_id",
utils.AUTH_URL_V2, auth_system="fake",
auth_plugin=plugin)
cs.client.authenticate()
headers = requested_headers(cs)
token_url = cs.client.auth_url + "/tokens"
mock_request.assert_called_with(
"POST",
token_url,
headers=headers,
data='{"fake": "me"}',
allow_redirects=True,
**self.TEST_REQUEST_BASE)
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_discover_auth_system_options(self, mock_iter_entry_points):
"""Test that we can load the auth system options."""
class FakePlugin(auth_plugin.BaseAuthPlugin):
@staticmethod
def add_opts(parser):
parser.add_argument('--auth_system_opt',
default=False,
action='store_true',
help="Fake option")
return parser
class MockEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return FakePlugin
def resolve(self):
return FakePlugin
mock_iter_entry_points.side_effect = lambda _t, name=None: [
MockEntrypoint("fake", "fake", ["FakePlugin"])]
parser = argparse.ArgumentParser()
auth_plugin.discover_auth_systems()
auth_plugin.load_auth_system_opts(parser)
opts, args = parser.parse_known_args(['--auth_system_opt'])
self.assertTrue(opts.auth_system_opt)
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_parse_auth_system_options(self, mock_iter_entry_points):
"""Test that we can parse the auth system options."""
class MockEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return FakePlugin
def resolve(self):
return FakePlugin
class FakePlugin(auth_plugin.BaseAuthPlugin):
def __init__(self):
self.opts = {"fake_argument": True}
def parse_opts(self, args):
return self.opts
mock_iter_entry_points.side_effect = lambda _t, name=None: [
MockEntrypoint("fake", "fake", ["FakePlugin"])]
auth_plugin.discover_auth_systems()
plugin = auth_plugin.load_plugin("fake")
plugin.parse_opts([])
self.assertIn("fake_argument", plugin.opts)
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_auth_system_defining_url(self, mock_iter_entry_points):
"""Test the auth_system defining an url."""
class MockEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return FakePlugin
def resolve(self):
return FakePlugin
class FakePlugin(auth_plugin.BaseAuthPlugin):
def get_auth_url(self):
return "http://faked/v2.0"
mock_iter_entry_points.side_effect = lambda _t, name=None: [
MockEntrypoint("fake", "fake", ["FakePlugin"])]
auth_plugin.discover_auth_systems()
plugin = auth_plugin.load_plugin("fake")
cs = client.Client("2", "username", "password", "project_id",
auth_system="fakewithauthurl",
auth_plugin=plugin)
self.assertEqual("http://faked/v2.0", cs.client.auth_url)
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_exception_if_no_authenticate(self, mock_iter_entry_points):
"""Test that no authenticate raises a proper exception."""
class MockEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return FakePlugin
def resolve(self):
return FakePlugin
class FakePlugin(auth_plugin.BaseAuthPlugin):
pass
mock_iter_entry_points.side_effect = lambda _t, name=None: [
MockEntrypoint("fake", "fake", ["FakePlugin"])]
auth_plugin.discover_auth_systems()
plugin = auth_plugin.load_plugin("fake")
self.assertRaises(
exceptions.EndpointNotFound,
client.Client, "2", "username", "password", "project_id",
auth_system="fake", auth_plugin=plugin)
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_exception_if_no_url(self, mock_iter_entry_points):
"""Test that no auth_url at all raises exception."""
class MockEntrypoint(pkg_resources.EntryPoint):
def load(self, require=False):
return FakePlugin
def resolve(self):
return FakePlugin
class FakePlugin(auth_plugin.BaseAuthPlugin):
pass
mock_iter_entry_points.side_effect = lambda _t, name=None: [
MockEntrypoint("fake", "fake", ["FakePlugin"])]
auth_plugin.discover_auth_systems()
plugin = auth_plugin.load_plugin("fake")
self.assertRaises(
exceptions.EndpointNotFound,
client.Client, "2", "username", "password", "project_id",
auth_system="fake", auth_plugin=plugin)

View File

@ -62,15 +62,7 @@ FAKE_ENV4 = {'OS_USER_ID': 'user_id',
FAKE_ENV5 = {'OS_USERNAME': 'username', FAKE_ENV5 = {'OS_USERNAME': 'username',
'OS_PASSWORD': 'password', 'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name', 'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where/v2.0', 'OS_AUTH_URL': 'http://no.where/v2.0'}
'OS_COMPUTE_API_VERSION': '2',
'OS_AUTH_SYSTEM': 'rackspace'}
FAKE_ENV6 = {'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where/v2.0',
'OS_AUTH_SYSTEM': 'rackspace'}
def _create_ver_list(versions): def _create_ver_list(versions):
@ -519,9 +511,7 @@ class ShellTest(utils.TestCase):
def test_no_auth_url(self): def test_no_auth_url(self):
required = ('You must provide an auth url' required = ('You must provide an auth url'
' via either --os-auth-url or env[OS_AUTH_URL] or' ' via either --os-auth-url or env[OS_AUTH_URL].',)
' specify an auth_system which defines a default url'
' with --os-auth-system or env[OS_AUTH_SYSTEM]',)
self.make_env(exclude='OS_AUTH_URL') self.make_env(exclude='OS_AUTH_URL')
try: try:
self.shell('list') self.shell('list')
@ -687,7 +677,7 @@ class ShellTest(utils.TestCase):
@mock.patch('novaclient.client.Client') @mock.patch('novaclient.client.Client')
def test_microversion_with_default_behaviour(self, mock_client): def test_microversion_with_default_behaviour(self, mock_client):
self.make_env(fake_env=FAKE_ENV6) self.make_env(fake_env=FAKE_ENV5)
self.mock_server_version_range.return_value = ( self.mock_server_version_range.return_value = (
api_versions.APIVersion("2.1"), api_versions.APIVersion("2.3")) api_versions.APIVersion("2.1"), api_versions.APIVersion("2.3"))
self.shell('list') self.shell('list')
@ -697,7 +687,7 @@ class ShellTest(utils.TestCase):
@mock.patch('novaclient.client.Client') @mock.patch('novaclient.client.Client')
def test_microversion_with_default_behaviour_with_legacy_server( def test_microversion_with_default_behaviour_with_legacy_server(
self, mock_client): self, mock_client):
self.make_env(fake_env=FAKE_ENV6) self.make_env(fake_env=FAKE_ENV5)
self.mock_server_version_range.return_value = ( self.mock_server_version_range.return_value = (
api_versions.APIVersion(), api_versions.APIVersion()) api_versions.APIVersion(), api_versions.APIVersion())
self.shell('list') self.shell('list')
@ -777,15 +767,6 @@ class ShellTest(utils.TestCase):
self.shell, self.shell,
'--os-compute-api-version 2.3 list') '--os-compute-api-version 2.3 list')
@mock.patch('novaclient.client.Client')
def test_custom_auth_plugin(self, mock_client):
self.make_env(fake_env=FAKE_ENV5)
self.shell('list')
password = mock_client.call_args_list[0][0][2]
client_kwargs = mock_client.call_args_list[0][1]
self.assertEqual(password, 'password')
self.assertIs(client_kwargs['session'], None)
@mock.patch.object(novaclient.shell.OpenStackComputeShell, 'main') @mock.patch.object(novaclient.shell.OpenStackComputeShell, 'main')
def test_main_error_handling(self, mock_compute_shell): def test_main_error_handling(self, mock_compute_shell):
class MyException(Exception): class MyException(Exception):

View File

@ -68,7 +68,7 @@ class Client(object):
service_type='compute', service_name=None, service_type='compute', service_name=None,
volume_service_name=None, timings=False, bypass_url=None, volume_service_name=None, timings=False, bypass_url=None,
os_cache=False, no_cache=True, http_log_debug=False, os_cache=False, no_cache=True, http_log_debug=False,
auth_system='keystone', auth_plugin=None, auth_token=None, auth_token=None,
cacert=None, tenant_id=None, user_id=None, cacert=None, tenant_id=None, user_id=None,
connection_pool=False, session=None, auth=None, connection_pool=False, session=None, auth=None,
api_version=None, direct_use=True, logger=None, **kwargs): api_version=None, direct_use=True, logger=None, **kwargs):
@ -93,8 +93,6 @@ class Client(object):
:param bool os_cache: OS cache :param bool os_cache: OS cache
:param bool no_cache: No cache :param bool no_cache: No cache
:param bool http_log_debug: Enable debugging for HTTP connections :param bool http_log_debug: Enable debugging for HTTP connections
:param str auth_system: Auth system
:param str auth_plugin: Auth plugin
:param str auth_token: Auth token :param str auth_token: Auth token
:param str cacert: cacert :param str cacert: cacert
:param str tenant_id: Tenant ID :param str tenant_id: Tenant ID
@ -194,8 +192,6 @@ class Client(object):
auth_token=auth_token, auth_token=auth_token,
insecure=insecure, insecure=insecure,
timeout=timeout, timeout=timeout,
auth_system=auth_system,
auth_plugin=auth_plugin,
proxy_token=proxy_token, proxy_token=proxy_token,
proxy_tenant_id=proxy_tenant_id, proxy_tenant_id=proxy_tenant_id,
region_name=region_name, region_name=region_name,

View File

@ -0,0 +1,9 @@
---
prelude: >
The ability to use non-Keystone authentication systems has been removed.
upgrade:
- The ``--os-auth-system`` CLI option and ``OS_AUTH_SYSTEM`` environment
variable usage was deprecated in the 3.1.0 release during the Mitaka
development series. This release drops the support for using those options
to load non-Keystone authentication systems via the
``openstack.client.auth_plugin`` extension point.