Merge "Reorganize Service Catalog"
This commit is contained in:
@@ -16,10 +16,14 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import abc
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from keystoneclient import exceptions
|
from keystoneclient import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class ServiceCatalog(object):
|
class ServiceCatalog(object):
|
||||||
"""Helper methods for dealing with a Keystone Service Catalog."""
|
"""Helper methods for dealing with a Keystone Service Catalog."""
|
||||||
|
|
||||||
@@ -33,6 +37,7 @@ class ServiceCatalog(object):
|
|||||||
else:
|
else:
|
||||||
raise NotImplementedError('Unrecognized auth response')
|
raise NotImplementedError('Unrecognized auth response')
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
def get_token(self):
|
def get_token(self):
|
||||||
"""Fetch token details from service catalog.
|
"""Fetch token details from service catalog.
|
||||||
|
|
||||||
@@ -47,14 +52,82 @@ class ServiceCatalog(object):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def _is_endpoint_type_match(self, endpoint, endpoint_type):
|
||||||
|
"""Helper function to normalize endpoint matching across v2 and v3.
|
||||||
|
|
||||||
|
:returns: True if the provided endpoint matches the required
|
||||||
|
endpoint_type otherwise False.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def _normalize_endpoint_type(self, endpoint_type):
|
||||||
|
"""Handle differences in the way v2 and v3 catalogs specify endpoint.
|
||||||
|
|
||||||
|
Both v2 and v3 must be able to handle the endpoint style of the other.
|
||||||
|
For example v2 must be able to handle a 'public' endpoint_type and
|
||||||
|
v3 must be able to handle a 'publicURL' endpoint_type.
|
||||||
|
|
||||||
|
:returns: the endpoint string in the format appropriate for this
|
||||||
|
service catalog.
|
||||||
|
"""
|
||||||
|
|
||||||
def get_endpoints(self, service_type=None, endpoint_type=None):
|
def get_endpoints(self, service_type=None, endpoint_type=None):
|
||||||
"""Fetch and filter endpoints for the specified service(s).
|
"""Fetch and filter endpoints for the specified service(s).
|
||||||
|
|
||||||
Returns endpoints for the specified service (or all) and
|
Returns endpoints for the specified service (or all) and
|
||||||
that contain the specified type (or all).
|
that contain the specified type (or all).
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
endpoint_type = self._normalize_endpoint_type(endpoint_type)
|
||||||
|
|
||||||
|
sc = {}
|
||||||
|
|
||||||
|
for service in (self.get_data() or []):
|
||||||
|
try:
|
||||||
|
st = service['type']
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if service_type and service_type != st:
|
||||||
|
continue
|
||||||
|
|
||||||
|
sc[st] = []
|
||||||
|
|
||||||
|
for endpoint in service.get('endpoints', []):
|
||||||
|
if (endpoint_type and not
|
||||||
|
self._is_endpoint_type_match(endpoint, endpoint_type)):
|
||||||
|
continue
|
||||||
|
if (self.region_name and
|
||||||
|
self.region_name != endpoint.get('region')):
|
||||||
|
continue
|
||||||
|
sc[st].append(endpoint)
|
||||||
|
|
||||||
|
return sc
|
||||||
|
|
||||||
|
def _get_service_endpoints(self, attr, filter_value, service_type,
|
||||||
|
endpoint_type):
|
||||||
|
"""Fetch the endpoints of a particular service_type and handle
|
||||||
|
the filtering.
|
||||||
|
"""
|
||||||
|
sc_endpoints = self.get_endpoints(service_type=service_type,
|
||||||
|
endpoint_type=endpoint_type)
|
||||||
|
|
||||||
|
try:
|
||||||
|
endpoints = sc_endpoints[service_type]
|
||||||
|
except KeyError:
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO(jamielennox): at least swiftclient is known to set attr and not
|
||||||
|
# filter_value and expects that to mean that filtering is ignored, so
|
||||||
|
# we can't check for the presence of attr. This behaviour should be
|
||||||
|
# deprecated and an appropriate warning provided.
|
||||||
|
if filter_value:
|
||||||
|
return [endpoint for endpoint in endpoints
|
||||||
|
if endpoint.get(attr) == filter_value]
|
||||||
|
|
||||||
|
return endpoints
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
def get_urls(self, attr=None, filter_value=None,
|
def get_urls(self, attr=None, filter_value=None,
|
||||||
service_type='identity', endpoint_type='publicURL'):
|
service_type='identity', endpoint_type='publicURL'):
|
||||||
"""Fetch endpoint urls from the service catalog.
|
"""Fetch endpoint urls from the service catalog.
|
||||||
@@ -88,15 +161,33 @@ class ServiceCatalog(object):
|
|||||||
`internal` or `internalURL`,
|
`internal` or `internalURL`,
|
||||||
`admin` or 'adminURL`
|
`admin` or 'adminURL`
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
if not self.get_data():
|
||||||
|
raise exceptions.EmptyCatalog('The service catalog is empty.')
|
||||||
|
|
||||||
|
urls = self.get_urls(attr=attr,
|
||||||
|
filter_value=filter_value,
|
||||||
|
service_type=service_type,
|
||||||
|
endpoint_type=endpoint_type)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return urls[0]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
msg = '%(endpoint)s endpoint for %(service)s not found'
|
||||||
|
msg = msg % {'endpoint': endpoint_type,
|
||||||
|
'service': service_type}
|
||||||
|
|
||||||
|
raise exceptions.EndpointNotFound(msg)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
"""Get the raw catalog structure.
|
"""Get the raw catalog structure.
|
||||||
|
|
||||||
Get the version dependant catalog structure as it is presented within
|
Get the version dependant catalog structure as it is presented within
|
||||||
the resource.
|
the resource.
|
||||||
|
|
||||||
:returns: dict containing raw catalog data or None
|
:returns: list containing raw catalog data entries or None
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@@ -117,6 +208,15 @@ class ServiceCatalogV2(ServiceCatalog):
|
|||||||
# will not work. Use 'token' attribute instead.
|
# will not work. Use 'token' attribute instead.
|
||||||
return 'token' in resource_dict
|
return 'token' in resource_dict
|
||||||
|
|
||||||
|
def _normalize_endpoint_type(self, endpoint_type):
|
||||||
|
if endpoint_type and 'URL' not in endpoint_type:
|
||||||
|
endpoint_type = endpoint_type + 'URL'
|
||||||
|
|
||||||
|
return endpoint_type
|
||||||
|
|
||||||
|
def _is_endpoint_type_match(self, endpoint, endpoint_type):
|
||||||
|
return endpoint_type in endpoint
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return self.catalog.get('serviceCatalog')
|
return self.catalog.get('serviceCatalog')
|
||||||
|
|
||||||
@@ -131,64 +231,18 @@ class ServiceCatalogV2(ServiceCatalog):
|
|||||||
pass
|
pass
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def get_endpoints(self, service_type=None, endpoint_type=None):
|
|
||||||
if endpoint_type and 'URL' not in endpoint_type:
|
|
||||||
endpoint_type = endpoint_type + 'URL'
|
|
||||||
|
|
||||||
sc = {}
|
|
||||||
for service in (self.get_data() or []):
|
|
||||||
if service_type and service_type != service['type']:
|
|
||||||
continue
|
|
||||||
sc[service['type']] = []
|
|
||||||
for endpoint in service['endpoints']:
|
|
||||||
if endpoint_type and endpoint_type not in endpoint:
|
|
||||||
continue
|
|
||||||
sc[service['type']].append(endpoint)
|
|
||||||
return sc
|
|
||||||
|
|
||||||
def get_urls(self, attr=None, filter_value=None,
|
def get_urls(self, attr=None, filter_value=None,
|
||||||
service_type='identity', endpoint_type='publicURL'):
|
service_type='identity', endpoint_type='publicURL'):
|
||||||
sc_endpoints = self.get_endpoints(service_type=service_type,
|
endpoint_type = self._normalize_endpoint_type(endpoint_type)
|
||||||
endpoint_type=endpoint_type)
|
endpoints = self._get_service_endpoints(attr=attr,
|
||||||
endpoints = sc_endpoints.get(service_type)
|
filter_value=filter_value,
|
||||||
if not endpoints:
|
service_type=service_type,
|
||||||
return
|
endpoint_type=endpoint_type)
|
||||||
|
|
||||||
if endpoint_type and 'URL' not in endpoint_type:
|
if endpoints:
|
||||||
endpoint_type = endpoint_type + 'URL'
|
return tuple([endpoint[endpoint_type] for endpoint in endpoints])
|
||||||
|
else:
|
||||||
return tuple(endpoint[endpoint_type]
|
return None
|
||||||
for endpoint in endpoints
|
|
||||||
if (endpoint_type in endpoint
|
|
||||||
and (not self.region_name
|
|
||||||
or endpoint.get('region') == self.region_name)
|
|
||||||
and (not filter_value
|
|
||||||
or endpoint.get(attr) == filter_value)))
|
|
||||||
|
|
||||||
def url_for(self, attr=None, filter_value=None,
|
|
||||||
service_type='identity', endpoint_type='publicURL'):
|
|
||||||
catalog = self.get_data()
|
|
||||||
|
|
||||||
if not catalog:
|
|
||||||
raise exceptions.EmptyCatalog('The service catalog is empty.')
|
|
||||||
|
|
||||||
if 'URL' not in endpoint_type:
|
|
||||||
endpoint_type = endpoint_type + 'URL'
|
|
||||||
|
|
||||||
for service in catalog:
|
|
||||||
if service['type'] != service_type:
|
|
||||||
continue
|
|
||||||
|
|
||||||
endpoints = service['endpoints']
|
|
||||||
for endpoint in endpoints:
|
|
||||||
if (self.region_name and
|
|
||||||
endpoint.get('region') != self.region_name):
|
|
||||||
continue
|
|
||||||
if not filter_value or endpoint.get(attr) == filter_value:
|
|
||||||
return endpoint[endpoint_type]
|
|
||||||
|
|
||||||
raise exceptions.EndpointNotFound('%s endpoint for %s not found.' %
|
|
||||||
(endpoint_type, service_type))
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceCatalogV3(ServiceCatalog):
|
class ServiceCatalogV3(ServiceCatalog):
|
||||||
@@ -208,6 +262,18 @@ class ServiceCatalogV3(ServiceCatalog):
|
|||||||
# will not work. Use 'methods' attribute instead.
|
# will not work. Use 'methods' attribute instead.
|
||||||
return 'methods' in resource_dict
|
return 'methods' in resource_dict
|
||||||
|
|
||||||
|
def _normalize_endpoint_type(self, endpoint_type):
|
||||||
|
if endpoint_type:
|
||||||
|
endpoint_type = endpoint_type.rstrip('URL')
|
||||||
|
|
||||||
|
return endpoint_type
|
||||||
|
|
||||||
|
def _is_endpoint_type_match(self, endpoint, endpoint_type):
|
||||||
|
try:
|
||||||
|
return endpoint_type == endpoint['interface']
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return self.catalog.get('catalog')
|
return self.catalog.get('catalog')
|
||||||
|
|
||||||
@@ -227,63 +293,14 @@ class ServiceCatalogV3(ServiceCatalog):
|
|||||||
pass
|
pass
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def get_endpoints(self, service_type=None, endpoint_type=None):
|
|
||||||
if endpoint_type:
|
|
||||||
endpoint_type = endpoint_type.rstrip('URL')
|
|
||||||
sc = {}
|
|
||||||
for service in (self.get_data() or []):
|
|
||||||
if service_type and service_type != service['type']:
|
|
||||||
continue
|
|
||||||
sc[service['type']] = []
|
|
||||||
for endpoint in service['endpoints']:
|
|
||||||
if endpoint_type and endpoint_type != endpoint['interface']:
|
|
||||||
continue
|
|
||||||
sc[service['type']].append(endpoint)
|
|
||||||
return sc
|
|
||||||
|
|
||||||
def get_urls(self, attr=None, filter_value=None,
|
def get_urls(self, attr=None, filter_value=None,
|
||||||
service_type='identity', endpoint_type='public'):
|
service_type='identity', endpoint_type='public'):
|
||||||
if endpoint_type:
|
endpoints = self._get_service_endpoints(attr=attr,
|
||||||
endpoint_type = endpoint_type.rstrip('URL')
|
filter_value=filter_value,
|
||||||
sc_endpoints = self.get_endpoints(service_type=service_type,
|
service_type=service_type,
|
||||||
endpoint_type=endpoint_type)
|
endpoint_type=endpoint_type)
|
||||||
endpoints = sc_endpoints.get(service_type)
|
|
||||||
if not endpoints:
|
if endpoints:
|
||||||
|
return tuple([endpoint['url'] for endpoint in endpoints])
|
||||||
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
urls = list()
|
|
||||||
for endpoint in endpoints:
|
|
||||||
if (endpoint['interface'] == endpoint_type
|
|
||||||
and (not self.region_name
|
|
||||||
or endpoint.get('region') == self.region_name)
|
|
||||||
and (not filter_value
|
|
||||||
or endpoint.get(attr) == filter_value)):
|
|
||||||
urls.append(endpoint['url'])
|
|
||||||
return tuple(urls)
|
|
||||||
|
|
||||||
def url_for(self, attr=None, filter_value=None,
|
|
||||||
service_type='identity', endpoint_type='public'):
|
|
||||||
catalog = self.get_data()
|
|
||||||
|
|
||||||
if not catalog:
|
|
||||||
raise exceptions.EmptyCatalog('The service catalog is empty.')
|
|
||||||
|
|
||||||
if endpoint_type:
|
|
||||||
endpoint_type = endpoint_type.rstrip('URL')
|
|
||||||
|
|
||||||
for service in catalog:
|
|
||||||
if service['type'] != service_type:
|
|
||||||
continue
|
|
||||||
|
|
||||||
endpoints = service['endpoints']
|
|
||||||
for endpoint in endpoints:
|
|
||||||
if endpoint.get('interface') != endpoint_type:
|
|
||||||
continue
|
|
||||||
if (self.region_name and
|
|
||||||
endpoint.get('region') != self.region_name):
|
|
||||||
continue
|
|
||||||
if not filter_value or endpoint.get(attr) == filter_value:
|
|
||||||
return endpoint['url']
|
|
||||||
|
|
||||||
raise exceptions.EndpointNotFound('%s endpoint for %s not found.' %
|
|
||||||
(endpoint_type, service_type))
|
|
||||||
|
Reference in New Issue
Block a user