Add service name to catalog

The service catalog can contain names for the services. These names can
be filtered on by other clients and are going to be required if we
expect to use the keystoneclient service catalog in other clients.

Change-Id: Iab69d67427ed40ce2f556f9a6348f4f1d9c26b5b
This commit is contained in:
Jamie Lennox
2014-03-05 15:19:57 +10:00
parent ebfaf10357
commit 00c05a18fa
4 changed files with 126 additions and 20 deletions

View File

@@ -84,11 +84,15 @@ class ServiceCatalog(object):
""" """
def get_endpoints(self, service_type=None, endpoint_type=None, def get_endpoints(self, service_type=None, endpoint_type=None,
region_name=None): region_name=None, service_name=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) containing Returns endpoints for the specified service (or all) containing
the specified type (or all) and region (or all). the specified type (or all) and region (or all) and service name.
If there is no name in the service catalog the service_name check will
be skipped. This allows compatibility with services that existed
before the name was available in the catalog.
""" """
endpoint_type = self._normalize_endpoint_type(endpoint_type) endpoint_type = self._normalize_endpoint_type(endpoint_type)
region_name = region_name or self._region_name region_name = region_name or self._region_name
@@ -104,6 +108,21 @@ class ServiceCatalog(object):
if service_type and service_type != st: if service_type and service_type != st:
continue continue
# NOTE(jamielennox): service_name is different. It is not available
# in API < v3.3. If it is in the catalog then we enforce it, if it
# is not then we don't because the name could be correct we just
# don't have that information to check against.
if service_name:
try:
sn = service['name']
except KeyError:
# assume that we're in v3.0-v3.2 and don't have the name in
# the catalog. Skip the check.
pass
else:
if service_name != sn:
continue
sc[st] = [] sc[st] = []
for endpoint in service.get('endpoints', []): for endpoint in service.get('endpoints', []):
@@ -117,13 +136,14 @@ class ServiceCatalog(object):
return sc return sc
def _get_service_endpoints(self, attr, filter_value, service_type, def _get_service_endpoints(self, attr, filter_value, service_type,
endpoint_type, region_name): endpoint_type, region_name, service_name):
"""Fetch the endpoints of a particular service_type and handle """Fetch the endpoints of a particular service_type and handle
the filtering. the filtering.
""" """
sc_endpoints = self.get_endpoints(service_type=service_type, sc_endpoints = self.get_endpoints(service_type=service_type,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
region_name=region_name) region_name=region_name,
service_name=service_name)
try: try:
endpoints = sc_endpoints[service_type] endpoints = sc_endpoints[service_type]
@@ -144,7 +164,7 @@ class ServiceCatalog(object):
@utils.positional(enforcement=utils.positional.WARN) @utils.positional(enforcement=utils.positional.WARN)
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',
region_name=None): region_name=None, service_name=None):
"""Fetch endpoint urls from the service catalog. """Fetch endpoint urls from the service catalog.
Fetch the endpoints from the service catalog for a particular Fetch the endpoints from the service catalog for a particular
@@ -159,6 +179,7 @@ class ServiceCatalog(object):
internal or internalURL, admin or internal or internalURL, admin or
adminURL adminURL
:param string region_name: Region of the endpoint. :param string region_name: Region of the endpoint.
:param string service_name: The assigned name of the service.
:returns: tuple of urls or None (if no match found) :returns: tuple of urls or None (if no match found)
""" """
@@ -167,7 +188,7 @@ class ServiceCatalog(object):
@utils.positional(3, enforcement=utils.positional.WARN) @utils.positional(3, enforcement=utils.positional.WARN)
def url_for(self, attr=None, filter_value=None, def url_for(self, attr=None, filter_value=None,
service_type='identity', endpoint_type='publicURL', service_type='identity', endpoint_type='publicURL',
region_name=None): region_name=None, service_name=None):
"""Fetch an endpoint from the service catalog. """Fetch an endpoint from the service catalog.
Fetch the specified endpoint from the service catalog for Fetch the specified endpoint from the service catalog for
@@ -177,6 +198,14 @@ class ServiceCatalog(object):
Valid endpoint types: `public` or `publicURL`, Valid endpoint types: `public` or `publicURL`,
`internal` or `internalURL`, `internal` or `internalURL`,
`admin` or 'adminURL` `admin` or 'adminURL`
:param string attr: Endpoint attribute name.
:param string filter_value: Endpoint attribute value.
:param string service_type: Service type of the endpoint.
:param string endpoint_type: Type of endpoint.
:param string region_name: Region of the endpoint.
:param string service_name: The assigned name of the service.
:
""" """
if not self.get_data(): if not self.get_data():
raise exceptions.EmptyCatalog('The service catalog is empty.') raise exceptions.EmptyCatalog('The service catalog is empty.')
@@ -185,19 +214,20 @@ class ServiceCatalog(object):
filter_value=filter_value, filter_value=filter_value,
service_type=service_type, service_type=service_type,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
region_name=region_name) region_name=region_name,
service_name=service_name)
try: try:
return urls[0] return urls[0]
except Exception: except Exception:
pass pass
msg = '%(endpoint)s endpoint for %(service)s%(region)s not found' msg = '%s endpoint for %s service' % (endpoint_type, service_type)
region = ' in %s region' % region_name if region_name else '' if service_name:
msg = msg % {'endpoint': endpoint_type, msg += ' named %s' % service_name
'service': service_type, if region_name:
'region': region} msg += ' in %s region' % region_name
msg += ' not found'
raise exceptions.EndpointNotFound(msg) raise exceptions.EndpointNotFound(msg)
@abc.abstractmethod @abc.abstractmethod
@@ -254,13 +284,14 @@ class ServiceCatalogV2(ServiceCatalog):
@utils.positional(enforcement=utils.positional.WARN) @utils.positional(enforcement=utils.positional.WARN)
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',
region_name=None): region_name=None, service_name=None):
endpoint_type = self._normalize_endpoint_type(endpoint_type) endpoint_type = self._normalize_endpoint_type(endpoint_type)
endpoints = self._get_service_endpoints(attr=attr, endpoints = self._get_service_endpoints(attr=attr,
filter_value=filter_value, filter_value=filter_value,
service_type=service_type, service_type=service_type,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
region_name=region_name) region_name=region_name,
service_name=service_name)
if endpoints: if endpoints:
return tuple([endpoint[endpoint_type] for endpoint in endpoints]) return tuple([endpoint[endpoint_type] for endpoint in endpoints])
@@ -319,12 +350,13 @@ class ServiceCatalogV3(ServiceCatalog):
@utils.positional(enforcement=utils.positional.WARN) @utils.positional(enforcement=utils.positional.WARN)
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',
region_name=None): region_name=None, service_name=None):
endpoints = self._get_service_endpoints(attr=attr, endpoints = self._get_service_endpoints(attr=attr,
filter_value=filter_value, filter_value=filter_value,
service_type=service_type, service_type=service_type,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
region_name=region_name) region_name=region_name,
service_name=service_name)
if endpoints: if endpoints:
return tuple([endpoint['url'] for endpoint in endpoints]) return tuple([endpoint['url'] for endpoint in endpoints])

View File

@@ -150,3 +150,26 @@ class ServiceCatalogTest(utils.TestCase):
self.assertEqual(len(endpoints['image']), 1) self.assertEqual(len(endpoints['image']), 1)
self.assertEqual(endpoints['image'][0]['publicURL'], self.assertEqual(endpoints['image'][0]['publicURL'],
'https://image.south.host/v1/') 'https://image.south.host/v1/')
def test_service_catalog_service_name(self):
auth_ref = access.AccessInfo.factory(resp=None,
body=self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
url = sc.url_for(service_name='Image Servers', endpoint_type='public',
service_type='image', region_name='North')
self.assertEqual('https://image.north.host/v1/', url)
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
service_name='Image Servers', service_type='compute')
urls = sc.get_urls(service_type='image', service_name='Image Servers',
endpoint_type='public')
self.assertIn('https://image.north.host/v1/', urls)
self.assertIn('https://image.south.host/v1/', urls)
urls = sc.get_urls(service_type='image', service_name='Servers',
endpoint_type='public')
self.assertIsNone(urls)

View File

@@ -332,7 +332,8 @@ AUTH_RESPONSE_BODY = {
'region': 'North', 'region': 'North',
'interface': 'admin' 'interface': 'admin'
}], }],
'type': 'compute' 'type': 'compute',
'name': 'nova',
}, { }, {
'endpoints': [{ 'endpoints': [{
'url': 'http://swift.north.host/swiftapi/public', 'url': 'http://swift.north.host/swiftapi/public',
@@ -347,7 +348,8 @@ AUTH_RESPONSE_BODY = {
'region': 'South', 'region': 'South',
'interface': 'admin' 'interface': 'admin'
}], }],
'type': 'object-store' 'type': 'object-store',
'name': 'swift',
}, { }, {
'endpoints': [{ 'endpoints': [{
'url': 'http://glance.north.host/glanceapi/public', 'url': 'http://glance.north.host/glanceapi/public',
@@ -374,7 +376,8 @@ AUTH_RESPONSE_BODY = {
'region': 'South', 'region': 'South',
'interface': 'admin' 'interface': 'admin'
}], }],
'type': 'image' 'type': 'image',
'name': 'glance',
}] }]
} }
} }

View File

@@ -171,3 +171,51 @@ class ServiceCatalogTest(utils.TestCase):
for endpoint in endpoints['image']: for endpoint in endpoints['image']:
self.assertEqual(endpoint['url'], self.assertEqual(endpoint['url'],
self.south_endpoints[endpoint['interface']]) self.south_endpoints[endpoint['interface']])
def test_service_catalog_service_name(self):
auth_ref = access.AccessInfo.factory(resp=None,
body=self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
url = sc.url_for(service_name='glance', endpoint_type='public',
service_type='image', region_name='North')
self.assertEqual('http://glance.north.host/glanceapi/public', url)
url = sc.url_for(service_name='glance', endpoint_type='public',
service_type='image', region_name='South')
self.assertEqual('http://glance.south.host/glanceapi/public', url)
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
service_name='glance', service_type='compute')
urls = sc.get_urls(service_type='image', service_name='glance',
endpoint_type='public')
self.assertIn('http://glance.north.host/glanceapi/public', urls)
self.assertIn('http://glance.south.host/glanceapi/public', urls)
urls = sc.get_urls(service_type='image', service_name='Servers',
endpoint_type='public')
self.assertIsNone(urls)
def test_service_catalog_without_name(self):
pr_auth_ref = access.AccessInfo.factory(
resp=None,
body=client_fixtures.PROJECT_SCOPED_TOKEN)
pr_sc = pr_auth_ref.service_catalog
# this will work because there are no service names on that token
url_ref = 'http://public.com:8774/v2/225da22d3ce34b15877ea70b2a575f58'
url = pr_sc.url_for(service_type='compute', service_name='NotExist',
endpoint_type='public')
self.assertEqual(url_ref, url)
ab_auth_ref = access.AccessInfo.factory(resp=None,
body=self.AUTH_RESPONSE_BODY)
ab_sc = ab_auth_ref.service_catalog
# this won't work because there is a name and it's not this one
self.assertRaises(exceptions.EndpointNotFound, ab_sc.url_for,
service_type='compute', service_name='NotExist',
endpoint_type='public')