Enable Keystone v3 support for compat client
Enable Keystone v3 auth support in the compat client. This is needed for the Trove integration and scenario tests which use the compat client. The Keystone v2 APIs have been removed in the Queens release and this stop-gap fix of putting Keystone v3 support in the compat client is needed to fix the Trove gate. Change-Id: I60ea2188443d8905c03c24607703e69c6490420b
This commit is contained in:
parent
a397f72da4
commit
565a6e0a5b
|
@ -23,7 +23,7 @@ def get_authenticator_cls(cls_or_name):
|
|||
return cls_or_name
|
||||
elif isinstance(cls_or_name, six.string_types):
|
||||
if cls_or_name == "keystone":
|
||||
return KeyStoneV2Authenticator
|
||||
return KeyStoneV3Authenticator
|
||||
elif cls_or_name == "auth1.1":
|
||||
return Auth1_1
|
||||
elif cls_or_name == "fake":
|
||||
|
@ -68,7 +68,24 @@ class Authenticator(object):
|
|||
finally:
|
||||
self.client.follow_all_redirects = tmp_follow_all_redirects
|
||||
|
||||
if resp.status == 200: # content must always present
|
||||
if resp.status == 201: # Keystone v3
|
||||
try:
|
||||
token = resp.get('x-subject-token')
|
||||
return ServiceCatalog3(body, region=self.region,
|
||||
service_type=self.service_type,
|
||||
service_name=self.service_name,
|
||||
service_url=self.service_url,
|
||||
token=token)
|
||||
except exceptions.AmbiguousEndpoints:
|
||||
print("Found more than one valid endpoint. Use a more "
|
||||
"restrictive filter")
|
||||
raise
|
||||
except KeyError:
|
||||
raise exceptions.AuthorizationFailure()
|
||||
except exceptions.EndpointNotFound:
|
||||
print("Could not find any suitable endpoint. Correct region?")
|
||||
raise
|
||||
elif resp.status == 200: # Keystone pre-v3
|
||||
try:
|
||||
return ServiceCatalog(body, region=self.region,
|
||||
service_type=self.service_type,
|
||||
|
@ -94,6 +111,61 @@ class Authenticator(object):
|
|||
raise NotImplementedError("Missing authenticate method.")
|
||||
|
||||
|
||||
class KeyStoneV3Authenticator(Authenticator):
|
||||
def __init__(self, client, type, url, username, password, tenant,
|
||||
region=None, service_type=None, service_name=None,
|
||||
service_url=None):
|
||||
super(KeyStoneV3Authenticator, self).\
|
||||
__init__(client, type, url, username, password, tenant,
|
||||
region=region, service_type=service_type,
|
||||
service_name=service_name, service_url=service_url)
|
||||
|
||||
# The Auth obect is needed because
|
||||
# troveclient.v1.instances.Instances._get_swift_client assumes
|
||||
# its v3 auth path is using a client which inherits from
|
||||
# keystone1.adapter.Adapter and then further goes after that
|
||||
# adapter's internal attributes to fetch them and send them
|
||||
# to the Swift client it creates.
|
||||
class Auth(object):
|
||||
def __init__(self, auth_url, username, password, project_name):
|
||||
token_str = "/auth/tokens"
|
||||
if auth_url.endswith(token_str):
|
||||
auth_url = auth_url[:-len(token_str)]
|
||||
|
||||
self.auth_url = auth_url
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._project_name = project_name
|
||||
|
||||
self.auth = Auth(url, username, password, tenant)
|
||||
|
||||
def authenticate(self):
|
||||
if self.url is None:
|
||||
raise exceptions.AuthUrlNotGiven()
|
||||
return self._v3_auth(self.url)
|
||||
|
||||
def _v3_auth(self, url):
|
||||
"""Authenticate against a v3.0 auth service."""
|
||||
body = {'auth': {
|
||||
'identity': {
|
||||
'methods': ['password'],
|
||||
'password': {
|
||||
'user': {
|
||||
'domain': {'name': 'Default'},
|
||||
'name': self.username,
|
||||
'password': self.password
|
||||
}
|
||||
}
|
||||
}}}
|
||||
|
||||
if self.tenant:
|
||||
body['auth']['scope'] = {'project': {
|
||||
'domain': {'name': 'Default'},
|
||||
'name': self.tenant}}
|
||||
|
||||
return self._authenticate(url, body)
|
||||
|
||||
|
||||
class KeyStoneV2Authenticator(Authenticator):
|
||||
def authenticate(self):
|
||||
if self.url is None:
|
||||
|
@ -231,3 +303,73 @@ class ServiceCatalog(object):
|
|||
raise exceptions.AmbiguousEndpoints(endpoints=matching_endpoints)
|
||||
else:
|
||||
return matching_endpoints[0].get(endpoint_type, None)
|
||||
|
||||
|
||||
class ServiceCatalog3(object):
|
||||
"""Represents a Keystone Service Catalog which describes a service.
|
||||
|
||||
This class has methods to obtain a valid token as well as a public service
|
||||
url and a management url.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, resource_dict, region=None, service_type=None,
|
||||
service_name=None, service_url=None, token=None):
|
||||
self.body = resource_dict
|
||||
self.region = region
|
||||
self.service_type = service_type
|
||||
self.service_name = service_name
|
||||
self.service_url = service_url
|
||||
self.management_url = None
|
||||
self.public_url = None
|
||||
self.token = token
|
||||
self._load()
|
||||
|
||||
def _load(self):
|
||||
if not self.service_url:
|
||||
self.public_url = self._url_for(attr='region',
|
||||
filter_value=self.region,
|
||||
endpoint_type="public")
|
||||
self.management_url = self._url_for(attr='region',
|
||||
filter_value=self.region,
|
||||
endpoint_type="admin")
|
||||
else:
|
||||
self.public_url = self.service_url
|
||||
self.management_url = self.service_url
|
||||
|
||||
def get_token(self):
|
||||
return self.token
|
||||
|
||||
def get_management_url(self):
|
||||
return self.management_url
|
||||
|
||||
def get_public_url(self):
|
||||
return self.public_url
|
||||
|
||||
def _url_for(self, attr=None, filter_value=None,
|
||||
endpoint_type='public'):
|
||||
"""Fetch requested URL.
|
||||
|
||||
Fetch the public URL from the Trove service for a particular
|
||||
endpoint attribute. If none given, return the first.
|
||||
"""
|
||||
"""Fetch the requested end point URL.
|
||||
"""
|
||||
matching_endpoints = []
|
||||
catalog = self.body['token']['catalog']
|
||||
for service in catalog:
|
||||
if service.get("type") != self.service_type:
|
||||
continue
|
||||
if (self.service_name and self.service_type == 'database' and
|
||||
service.get('name') != self.service_name):
|
||||
continue
|
||||
endpoints = service['endpoints']
|
||||
for endpoint in endpoints:
|
||||
if endpoint.get('interface') == endpoint_type and \
|
||||
(not filter_value or
|
||||
endpoint.get(attr) == filter_value):
|
||||
matching_endpoints.append(endpoint)
|
||||
if not matching_endpoints:
|
||||
raise exceptions.EndpointNotFound()
|
||||
else:
|
||||
return matching_endpoints[0].get('url')
|
||||
|
|
|
@ -94,6 +94,8 @@ class TroveHTTPClient(httplib2.Http):
|
|||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
service_url=service_url)
|
||||
if hasattr(self.authenticator, 'auth'):
|
||||
self.auth = self.authenticator.auth
|
||||
|
||||
def get_timings(self):
|
||||
return self.times
|
||||
|
|
|
@ -57,7 +57,7 @@ class AuthenticatorTest(testtools.TestCase):
|
|||
for c in class_list:
|
||||
self.assertEqual(c, auth.get_authenticator_cls(c))
|
||||
|
||||
class_names = {"keystone": auth.KeyStoneV2Authenticator,
|
||||
class_names = {"keystone": auth.KeyStoneV3Authenticator,
|
||||
"auth1.1": auth.Auth1_1,
|
||||
"fake": auth.FakeAuth}
|
||||
|
||||
|
|
Loading…
Reference in New Issue