Fix version discovery and auth_plugins

V3Client support added version discovery and session supports. Most
of the external auth system doesn't support this. This fix bypasses
version discovery if the idenity service doesn't support that. Session
is used only if  no external auth plugin is used

Change-Id: Ia84a2ad45940d35c5ef740727f5f7477c72ea9d4
Closes-Bug: #1333961
This commit is contained in:
Haneef Ali 2014-06-25 10:04:57 -07:00
parent 67a5f72378
commit b4906c855f
4 changed files with 106 additions and 36 deletions

View File

@ -506,7 +506,9 @@ def _construct_http_client(username=None, password=None, project_id=None,
cacert=None, tenant_id=None, cacert=None, tenant_id=None,
session=None, session=None,
auth=None): auth=None):
if session:
# Don't use sessions if third party plugin is used
if session and not auth_plugin:
# If auth pluggin is specified use that pluggin # If auth pluggin is specified use that pluggin
session.auth = auth or session.auth session.auth = auth or session.auth
@ -525,29 +527,29 @@ def _construct_http_client(username=None, password=None, project_id=None,
service_name=service_name, service_name=service_name,
region_name=region_name, region_name=region_name,
http_log_debug=http_log_debug) http_log_debug=http_log_debug)
else:
# FIXME(jamielennox): username and password are now optional. Need # FIXME(jamielennox): username and password are now optional. Need
# to test that they were provided in this mode. # to test that they were provided in this mode.
return HTTPClient(username, return HTTPClient(username,
password, password,
projectid=project_id, projectid=project_id,
auth_url=auth_url, auth_url=auth_url,
insecure=insecure, insecure=insecure,
timeout=timeout, timeout=timeout,
tenant_id=tenant_id, tenant_id=tenant_id,
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,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
service_type=service_type, service_type=service_type,
service_name=service_name, service_name=service_name,
volume_service_name=volume_service_name, volume_service_name=volume_service_name,
retries=retries, retries=retries,
http_log_debug=http_log_debug, http_log_debug=http_log_debug,
cacert=cacert, cacert=cacert,
auth_system=auth_system, auth_system=auth_system,
auth_plugin=auth_plugin, auth_plugin=auth_plugin,
) )
def get_client_class(version): def get_client_class(version):

View File

@ -43,6 +43,8 @@ from keystoneclient import discover
from keystoneclient import session from keystoneclient import session
from keystoneclient.auth.identity import v2 as v2_auth from keystoneclient.auth.identity import v2 as v2_auth
from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient.auth.identity import v3 as v3_auth
from keystoneclient.exceptions import DiscoveryFailure
import six.moves.urllib.parse as urlparse
DEFAULT_OS_VOLUME_API_VERSION = "1" DEFAULT_OS_VOLUME_API_VERSION = "1"
@ -157,8 +159,7 @@ class OpenStackCinderShell(object):
parser.add_argument('--endpoint-type', parser.add_argument('--endpoint-type',
metavar='<endpoint-type>', metavar='<endpoint-type>',
default=utils.env('CINDER_ENDPOINT_TYPE', default=utils.env('CINDER_ENDPOINT_TYPE',
default= default=DEFAULT_CINDER_ENDPOINT_TYPE),
DEFAULT_CINDER_ENDPOINT_TYPE),
help='Endpoint type, which is publicURL or ' help='Endpoint type, which is publicURL or '
'internalURL. ' 'internalURL. '
'Default=nova env[CINDER_ENDPOINT_TYPE] or ' 'Default=nova env[CINDER_ENDPOINT_TYPE] or '
@ -737,6 +738,36 @@ class OpenStackCinderShell(object):
project_domain_id=project_domain_id, project_domain_id=project_domain_id,
) )
def _discover_auth_versions(self, session, auth_url):
# discover the API versions the server is supporting based on the
# given URL
v2_auth_url = None
v3_auth_url = None
try:
ks_discover = discover.Discover(session=session, auth_url=auth_url)
v2_auth_url = ks_discover.url_for('2.0')
v3_auth_url = ks_discover.url_for('3.0')
except DiscoveryFailure:
# Discovery response mismatch. Raise the error
raise
except Exception:
# Some public clouds throw some other exception or doesn't support
# discovery. In that case try to determine version from auth_url
# API version from the original URL
url_parts = urlparse.urlparse(auth_url)
(scheme, netloc, path, params, query, fragment) = url_parts
path = path.lower()
if path.startswith('/v3'):
v3_auth_url = auth_url
elif path.startswith('/v2'):
v2_auth_url = auth_url
else:
raise exc.CommandError('Unable to determine the Keystone'
' version to authenticate with '
'using the given auth_url.')
return (v2_auth_url, v3_auth_url)
def _get_keystone_session(self, **kwargs): def _get_keystone_session(self, **kwargs):
# first create a Keystone session # first create a Keystone session
cacert = self.options.os_cacert or None cacert = self.options.os_cacert or None
@ -747,16 +778,12 @@ class OpenStackCinderShell(object):
verify = False verify = False
else: else:
verify = cacert or True verify = cacert or True
ks_session = session.Session(verify=verify, cert=cert) ks_session = session.Session(verify=verify, cert=cert)
# discover the supported keystone versions using the given url # discover the supported keystone versions using the given url
ks_discover = discover.Discover(session=ks_session, (v2_auth_url, v3_auth_url) = self._discover_auth_versions(
auth_url=self.options.os_auth_url) session=ks_session,
auth_url=self.options.os_auth_url)
# Determine which authentication plugin to use. First inspect the
# auth_url to see the supported version. If both v3 and v2 are
# supported, then use the highest version if possible.
v2_auth_url = ks_discover.url_for('v2.0')
v3_auth_url = ks_discover.url_for('v3.0')
username = self.options.os_username or None username = self.options.os_username or None
user_domain_name = self.options.os_user_domain_name or None user_domain_name = self.options.os_user_domain_name or None

View File

@ -49,6 +49,11 @@ V3_VERSION = {'id': 'v3.0',
'status': 'stable', 'status': 'stable',
'updated': UPDATED} 'updated': UPDATED}
WRONG_VERSION_RESPONSE = {'id': 'v2.0',
'links': [V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF],
'status': 'stable',
'updated': UPDATED}
def _create_version_list(versions): def _create_version_list(versions):
return json.dumps({'versions': {'values': versions}}) return json.dumps({'versions': {'values': versions}})
@ -206,7 +211,8 @@ def generate_v3_project_scoped_token(**kwargs):
def keystone_request_callback(request, uri, headers): def keystone_request_callback(request, uri, headers):
response_headers = {"content-type": "application/json"} response_headers = {"content-type": "application/json",
'server': 'Python/HTTPretty', }
if uri == BASE_URL: if uri == BASE_URL:
return (200, headers, V3_VERSION_LIST) return (200, headers, V3_VERSION_LIST)
elif uri == BASE_URL + "/v2.0": elif uri == BASE_URL + "/v2.0":
@ -216,3 +222,7 @@ def keystone_request_callback(request, uri, headers):
token_id, token_data = generate_v3_project_scoped_token() token_id, token_data = generate_v3_project_scoped_token()
response_headers["X-Subject-Token"] = token_id response_headers["X-Subject-Token"] = token_id
return (201, response_headers, token_data) return (201, response_headers, token_data)
elif "WrongDiscoveryResponse.discovery.com" in uri:
return (200, response_headers, str(WRONG_VERSION_RESPONSE))
else:
return (500, response_headers, str(WRONG_VERSION_RESPONSE))

View File

@ -22,6 +22,9 @@ from testtools import matchers
from cinderclient import exceptions from cinderclient import exceptions
from cinderclient import shell from cinderclient import shell
from cinderclient.tests import utils from cinderclient.tests import utils
from cinderclient.tests.fixture_data import keystone_client
from keystoneclient.exceptions import DiscoveryFailure
import httpretty
class ShellTest(utils.TestCase): class ShellTest(utils.TestCase):
@ -80,8 +83,36 @@ class ShellTest(utils.TestCase):
self.assertThat(help_text, self.assertThat(help_text,
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def register_keystone_auth_fixture(self, url):
httpretty.register_uri(httpretty.GET, url,
body=keystone_client.keystone_request_callback)
@httpretty.activate
def test_version_discovery(self):
_shell = shell.OpenStackCinderShell()
os_auth_url = "https://WrongDiscoveryResponse.discovery.com:35357/v2.0"
self.register_keystone_auth_fixture(os_auth_url)
self.assertRaises(DiscoveryFailure, _shell._discover_auth_versions,
None, auth_url=os_auth_url)
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
self.register_keystone_auth_fixture(os_auth_url)
v2_url, v3_url = _shell._discover_auth_versions(
None, auth_url=os_auth_url)
self.assertEqual(v2_url, os_auth_url, "Expected v2 url")
self.assertEqual(v3_url, None, "Expected no v3 url")
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
self.register_keystone_auth_fixture(os_auth_url)
v2_url, v3_url = _shell._discover_auth_versions(
None, auth_url=os_auth_url)
self.assertEqual(v3_url, os_auth_url, "Expected v3 url")
self.assertEqual(v2_url, None, "Expected no v2 url")
class CinderClientArgumentParserTest(utils.TestCase): class CinderClientArgumentParserTest(utils.TestCase):
def test_ambiguity_solved_for_one_visible_argument(self): def test_ambiguity_solved_for_one_visible_argument(self):
parser = shell.CinderClientArgumentParser(add_help=False) parser = shell.CinderClientArgumentParser(add_help=False)
parser.add_argument('--test-parameter', parser.add_argument('--test-parameter',
@ -94,7 +125,7 @@ class CinderClientArgumentParserTest(utils.TestCase):
opts = parser.parse_args(['--test']) opts = parser.parse_args(['--test'])
#visible argument must be set # visible argument must be set
self.assertTrue(opts.visible_param) self.assertTrue(opts.visible_param)
self.assertFalse(opts.hidden_param) self.assertFalse(opts.hidden_param)