aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/catalog/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/catalog/core.py')
-rw-r--r--keystone-moon/keystone/catalog/core.py388
1 files changed, 367 insertions, 21 deletions
diff --git a/keystone-moon/keystone/catalog/core.py b/keystone-moon/keystone/catalog/core.py
index 8bb72619..384a9b2b 100644
--- a/keystone-moon/keystone/catalog/core.py
+++ b/keystone-moon/keystone/catalog/core.py
@@ -18,6 +18,7 @@
import abc
import itertools
+from oslo_cache import core as oslo_cache
from oslo_config import cfg
from oslo_log import log
import six
@@ -35,12 +36,24 @@ from keystone import notifications
CONF = cfg.CONF
LOG = log.getLogger(__name__)
-MEMOIZE = cache.get_memoization_decorator(section='catalog')
WHITELISTED_PROPERTIES = [
- 'tenant_id', 'user_id', 'public_bind_host', 'admin_bind_host',
+ 'tenant_id', 'project_id', 'user_id',
+ 'public_bind_host', 'admin_bind_host',
'compute_host', 'admin_port', 'public_port',
'public_endpoint', 'admin_endpoint', ]
+# This is a general cache region for catalog administration (CRUD operations).
+MEMOIZE = cache.get_memoization_decorator(group='catalog')
+
+# This builds a discrete cache region dedicated to complete service catalogs
+# computed for a given user + project pair. Any write operation to create,
+# modify or delete elements of the service catalog should invalidate this
+# entire cache region.
+COMPUTED_CATALOG_REGION = oslo_cache.create_region()
+MEMOIZE_COMPUTED_CATALOG = cache.get_memoization_decorator(
+ group='catalog',
+ region=COMPUTED_CATALOG_REGION)
+
def format_url(url, substitutions, silent_keyerror_failures=None):
"""Formats a user-defined URL with the given substitutions.
@@ -52,7 +65,6 @@ def format_url(url, substitutions, silent_keyerror_failures=None):
:returns: a formatted URL
"""
-
substitutions = utils.WhiteListedItemFilter(
WHITELISTED_PROPERTIES,
substitutions)
@@ -108,6 +120,7 @@ def check_endpoint_url(url):
@dependency.provider('catalog_api')
+@dependency.requires('resource_api')
class Manager(manager.Manager):
"""Default pivot point for the Catalog backend.
@@ -129,7 +142,8 @@ class Manager(manager.Manager):
# Check duplicate ID
try:
self.get_region(region_ref['id'])
- except exception.RegionNotFound:
+ except exception.RegionNotFound: # nosec
+ # A region with the same id doesn't exist already, good.
pass
else:
msg = _('Duplicate ID, %s.') % region_ref['id']
@@ -148,6 +162,7 @@ class Manager(manager.Manager):
raise exception.RegionNotFound(region_id=parent_region_id)
notifications.Audit.created(self._REGION, ret['id'], initiator)
+ COMPUTED_CATALOG_REGION.invalidate()
return ret
@MEMOIZE
@@ -166,6 +181,7 @@ class Manager(manager.Manager):
ref = self.driver.update_region(region_id, region_ref)
notifications.Audit.updated(self._REGION, region_id, initiator)
self.get_region.invalidate(self, region_id)
+ COMPUTED_CATALOG_REGION.invalidate()
return ref
def delete_region(self, region_id, initiator=None):
@@ -173,6 +189,7 @@ class Manager(manager.Manager):
ret = self.driver.delete_region(region_id)
notifications.Audit.deleted(self._REGION, region_id, initiator)
self.get_region.invalidate(self, region_id)
+ COMPUTED_CATALOG_REGION.invalidate()
return ret
except exception.NotFound:
raise exception.RegionNotFound(region_id=region_id)
@@ -186,6 +203,7 @@ class Manager(manager.Manager):
service_ref.setdefault('name', '')
ref = self.driver.create_service(service_id, service_ref)
notifications.Audit.created(self._SERVICE, service_id, initiator)
+ COMPUTED_CATALOG_REGION.invalidate()
return ref
@MEMOIZE
@@ -199,6 +217,7 @@ class Manager(manager.Manager):
ref = self.driver.update_service(service_id, service_ref)
notifications.Audit.updated(self._SERVICE, service_id, initiator)
self.get_service.invalidate(self, service_id)
+ COMPUTED_CATALOG_REGION.invalidate()
return ref
def delete_service(self, service_id, initiator=None):
@@ -210,6 +229,7 @@ class Manager(manager.Manager):
for endpoint in endpoints:
if endpoint['service_id'] == service_id:
self.get_endpoint.invalidate(self, endpoint['id'])
+ COMPUTED_CATALOG_REGION.invalidate()
return ret
except exception.NotFound:
raise exception.ServiceNotFound(service_id=service_id)
@@ -240,6 +260,7 @@ class Manager(manager.Manager):
ref = self.driver.create_endpoint(endpoint_id, endpoint_ref)
notifications.Audit.created(self._ENDPOINT, endpoint_id, initiator)
+ COMPUTED_CATALOG_REGION.invalidate()
return ref
def update_endpoint(self, endpoint_id, endpoint_ref, initiator=None):
@@ -248,6 +269,7 @@ class Manager(manager.Manager):
ref = self.driver.update_endpoint(endpoint_id, endpoint_ref)
notifications.Audit.updated(self._ENDPOINT, endpoint_id, initiator)
self.get_endpoint.invalidate(self, endpoint_id)
+ COMPUTED_CATALOG_REGION.invalidate()
return ref
def delete_endpoint(self, endpoint_id, initiator=None):
@@ -255,6 +277,7 @@ class Manager(manager.Manager):
ret = self.driver.delete_endpoint(endpoint_id)
notifications.Audit.deleted(self._ENDPOINT, endpoint_id, initiator)
self.get_endpoint.invalidate(self, endpoint_id)
+ COMPUTED_CATALOG_REGION.invalidate()
return ret
except exception.NotFound:
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
@@ -270,12 +293,96 @@ class Manager(manager.Manager):
def list_endpoints(self, hints=None):
return self.driver.list_endpoints(hints or driver_hints.Hints())
+ @MEMOIZE_COMPUTED_CATALOG
def get_catalog(self, user_id, tenant_id):
try:
return self.driver.get_catalog(user_id, tenant_id)
except exception.NotFound:
raise exception.NotFound('Catalog not found for user and tenant')
+ @MEMOIZE_COMPUTED_CATALOG
+ def get_v3_catalog(self, user_id, tenant_id):
+ return self.driver.get_v3_catalog(user_id, tenant_id)
+
+ def add_endpoint_to_project(self, endpoint_id, project_id):
+ self.driver.add_endpoint_to_project(endpoint_id, project_id)
+ COMPUTED_CATALOG_REGION.invalidate()
+
+ def remove_endpoint_from_project(self, endpoint_id, project_id):
+ self.driver.remove_endpoint_from_project(endpoint_id, project_id)
+ COMPUTED_CATALOG_REGION.invalidate()
+
+ def add_endpoint_group_to_project(self, endpoint_group_id, project_id):
+ self.driver.add_endpoint_group_to_project(
+ endpoint_group_id, project_id)
+ COMPUTED_CATALOG_REGION.invalidate()
+
+ def remove_endpoint_group_from_project(self, endpoint_group_id,
+ project_id):
+ self.driver.remove_endpoint_group_from_project(
+ endpoint_group_id, project_id)
+ COMPUTED_CATALOG_REGION.invalidate()
+
+ def get_endpoint_groups_for_project(self, project_id):
+ # recover the project endpoint group memberships and for each
+ # membership recover the endpoint group
+ self.resource_api.get_project(project_id)
+ try:
+ refs = self.list_endpoint_groups_for_project(project_id)
+ endpoint_groups = [self.get_endpoint_group(
+ ref['endpoint_group_id']) for ref in refs]
+ return endpoint_groups
+ except exception.EndpointGroupNotFound:
+ return []
+
+ def get_endpoints_filtered_by_endpoint_group(self, endpoint_group_id):
+ endpoints = self.list_endpoints()
+ filters = self.get_endpoint_group(endpoint_group_id)['filters']
+ filtered_endpoints = []
+
+ for endpoint in endpoints:
+ is_candidate = True
+ for key, value in filters.items():
+ if endpoint[key] != value:
+ is_candidate = False
+ break
+ if is_candidate:
+ filtered_endpoints.append(endpoint)
+ return filtered_endpoints
+
+ def list_endpoints_for_project(self, project_id):
+ """List all endpoints associated with a project.
+
+ :param project_id: project identifier to check
+ :type project_id: string
+ :returns: a list of endpoint ids or an empty list.
+
+ """
+ refs = self.driver.list_endpoints_for_project(project_id)
+ filtered_endpoints = {}
+ for ref in refs:
+ try:
+ endpoint = self.get_endpoint(ref['endpoint_id'])
+ filtered_endpoints.update({ref['endpoint_id']: endpoint})
+ except exception.EndpointNotFound:
+ # remove bad reference from association
+ self.remove_endpoint_from_project(ref['endpoint_id'],
+ project_id)
+
+ # need to recover endpoint_groups associated with project
+ # then for each endpoint group return the endpoints.
+ endpoint_groups = self.get_endpoint_groups_for_project(project_id)
+ for endpoint_group in endpoint_groups:
+ endpoint_refs = self.get_endpoints_filtered_by_endpoint_group(
+ endpoint_group['id'])
+ # now check if any endpoints for current endpoint group are not
+ # contained in the list of filtered endpoints
+ for endpoint_ref in endpoint_refs:
+ if endpoint_ref['id'] not in filtered_endpoints:
+ filtered_endpoints[endpoint_ref['id']] = endpoint_ref
+
+ return filtered_endpoints
+
@six.add_metaclass(abc.ABCMeta)
class CatalogDriverV8(object):
@@ -304,8 +411,9 @@ class CatalogDriverV8(object):
def create_region(self, region_ref):
"""Creates a new region.
- :raises: keystone.exception.Conflict
- :raises: keystone.exception.RegionNotFound (if parent region invalid)
+ :raises keystone.exception.Conflict: If the region already exists.
+ :raises keystone.exception.RegionNotFound: If the parent region
+ is invalid.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -328,7 +436,7 @@ class CatalogDriverV8(object):
"""Get region by id.
:returns: region_ref dict
- :raises: keystone.exception.RegionNotFound
+ :raises keystone.exception.RegionNotFound: If the region doesn't exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -338,7 +446,7 @@ class CatalogDriverV8(object):
"""Update region by id.
:returns: region_ref dict
- :raises: keystone.exception.RegionNotFound
+ :raises keystone.exception.RegionNotFound: If the region doesn't exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -347,7 +455,7 @@ class CatalogDriverV8(object):
def delete_region(self, region_id):
"""Deletes an existing region.
- :raises: keystone.exception.RegionNotFound
+ :raises keystone.exception.RegionNotFound: If the region doesn't exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -356,7 +464,7 @@ class CatalogDriverV8(object):
def create_service(self, service_id, service_ref):
"""Creates a new service.
- :raises: keystone.exception.Conflict
+ :raises keystone.exception.Conflict: If a duplicate service exists.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -379,7 +487,8 @@ class CatalogDriverV8(object):
"""Get service by id.
:returns: service_ref dict
- :raises: keystone.exception.ServiceNotFound
+ :raises keystone.exception.ServiceNotFound: If the service doesn't
+ exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -389,7 +498,8 @@ class CatalogDriverV8(object):
"""Update service by id.
:returns: service_ref dict
- :raises: keystone.exception.ServiceNotFound
+ :raises keystone.exception.ServiceNotFound: If the service doesn't
+ exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -398,7 +508,8 @@ class CatalogDriverV8(object):
def delete_service(self, service_id):
"""Deletes an existing service.
- :raises: keystone.exception.ServiceNotFound
+ :raises keystone.exception.ServiceNotFound: If the service doesn't
+ exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -407,8 +518,9 @@ class CatalogDriverV8(object):
def create_endpoint(self, endpoint_id, endpoint_ref):
"""Creates a new endpoint for a service.
- :raises: keystone.exception.Conflict,
- keystone.exception.ServiceNotFound
+ :raises keystone.exception.Conflict: If a duplicate endpoint exists.
+ :raises keystone.exception.ServiceNotFound: If the service doesn't
+ exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -418,7 +530,8 @@ class CatalogDriverV8(object):
"""Get endpoint by id.
:returns: endpoint_ref dict
- :raises: keystone.exception.EndpointNotFound
+ :raises keystone.exception.EndpointNotFound: If the endpoint doesn't
+ exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -441,8 +554,10 @@ class CatalogDriverV8(object):
"""Get endpoint by id.
:returns: endpoint_ref dict
- :raises: keystone.exception.EndpointNotFound
- keystone.exception.ServiceNotFound
+ :raises keystone.exception.EndpointNotFound: If the endpoint doesn't
+ exist.
+ :raises keystone.exception.ServiceNotFound: If the service doesn't
+ exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -451,7 +566,8 @@ class CatalogDriverV8(object):
def delete_endpoint(self, endpoint_id):
"""Deletes an endpoint for a service.
- :raises: keystone.exception.EndpointNotFound
+ :raises keystone.exception.EndpointNotFound: If the endpoint doesn't
+ exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -476,7 +592,7 @@ class CatalogDriverV8(object):
:returns: A nested dict representing the service catalog or an
empty dict.
- :raises: keystone.exception.NotFound
+ :raises keystone.exception.NotFound: If the endpoint doesn't exist.
"""
raise exception.NotImplemented() # pragma: no cover
@@ -508,7 +624,7 @@ class CatalogDriverV8(object):
}]
:returns: A list representing the service catalog or an empty list
- :raises: keystone.exception.NotFound
+ :raises keystone.exception.NotFound: If the endpoint doesn't exist.
"""
v2_catalog = self.get_catalog(user_id, tenant_id)
@@ -544,5 +660,235 @@ class CatalogDriverV8(object):
return v3_catalog
+ @abc.abstractmethod
+ def add_endpoint_to_project(self, endpoint_id, project_id):
+ """Create an endpoint to project association.
+
+ :param endpoint_id: identity of endpoint to associate
+ :type endpoint_id: string
+ :param project_id: identity of the project to be associated with
+ :type project_id: string
+ :raises: keystone.exception.Conflict: If the endpoint was already
+ added to project.
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def remove_endpoint_from_project(self, endpoint_id, project_id):
+ """Removes an endpoint to project association.
+
+ :param endpoint_id: identity of endpoint to remove
+ :type endpoint_id: string
+ :param project_id: identity of the project associated with
+ :type project_id: string
+ :raises keystone.exception.NotFound: If the endpoint was not found
+ in the project.
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def check_endpoint_in_project(self, endpoint_id, project_id):
+ """Checks if an endpoint is associated with a project.
+
+ :param endpoint_id: identity of endpoint to check
+ :type endpoint_id: string
+ :param project_id: identity of the project associated with
+ :type project_id: string
+ :raises keystone.exception.NotFound: If the endpoint was not found
+ in the project.
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def list_endpoints_for_project(self, project_id):
+ """List all endpoints associated with a project.
+
+ :param project_id: identity of the project to check
+ :type project_id: string
+ :returns: a list of identity endpoint ids or an empty list.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def list_projects_for_endpoint(self, endpoint_id):
+ """List all projects associated with an endpoint.
+
+ :param endpoint_id: identity of endpoint to check
+ :type endpoint_id: string
+ :returns: a list of projects or an empty list.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def delete_association_by_endpoint(self, endpoint_id):
+ """Removes all the endpoints to project association with endpoint.
+
+ :param endpoint_id: identity of endpoint to check
+ :type endpoint_id: string
+ :returns: None
+
+ """
+ raise exception.NotImplemented()
+
+ @abc.abstractmethod
+ def delete_association_by_project(self, project_id):
+ """Removes all the endpoints to project association with project.
+
+ :param project_id: identity of the project to check
+ :type project_id: string
+ :returns: None
+
+ """
+ raise exception.NotImplemented()
+
+ @abc.abstractmethod
+ def create_endpoint_group(self, endpoint_group):
+ """Create an endpoint group.
+
+ :param endpoint_group: endpoint group to create
+ :type endpoint_group: dictionary
+ :raises: keystone.exception.Conflict: If a duplicate endpoint group
+ already exists.
+ :returns: an endpoint group representation.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def get_endpoint_group(self, endpoint_group_id):
+ """Get an endpoint group.
+
+ :param endpoint_group_id: identity of endpoint group to retrieve
+ :type endpoint_group_id: string
+ :raises keystone.exception.NotFound: If the endpoint group was not
+ found.
+ :returns: an endpoint group representation.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def update_endpoint_group(self, endpoint_group_id, endpoint_group):
+ """Update an endpoint group.
+
+ :param endpoint_group_id: identity of endpoint group to retrieve
+ :type endpoint_group_id: string
+ :param endpoint_group: A full or partial endpoint_group
+ :type endpoint_group: dictionary
+ :raises keystone.exception.NotFound: If the endpoint group was not
+ found.
+ :returns: an endpoint group representation.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def delete_endpoint_group(self, endpoint_group_id):
+ """Delete an endpoint group.
+
+ :param endpoint_group_id: identity of endpoint group to delete
+ :type endpoint_group_id: string
+ :raises keystone.exception.NotFound: If the endpoint group was not
+ found.
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def add_endpoint_group_to_project(self, endpoint_group_id, project_id):
+ """Adds an endpoint group to project association.
+
+ :param endpoint_group_id: identity of endpoint to associate
+ :type endpoint_group_id: string
+ :param project_id: identity of project to associate
+ :type project_id: string
+ :raises keystone.exception.Conflict: If the endpoint group was already
+ added to the project.
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def get_endpoint_group_in_project(self, endpoint_group_id, project_id):
+ """Get endpoint group to project association.
+
+ :param endpoint_group_id: identity of endpoint group to retrieve
+ :type endpoint_group_id: string
+ :param project_id: identity of project to associate
+ :type project_id: string
+ :raises keystone.exception.NotFound: If the endpoint group to the
+ project association was not found.
+ :returns: a project endpoint group representation.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def list_endpoint_groups(self):
+ """List all endpoint groups.
+
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def list_endpoint_groups_for_project(self, project_id):
+ """List all endpoint group to project associations for a project.
+
+ :param project_id: identity of project to associate
+ :type project_id: string
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def list_projects_associated_with_endpoint_group(self, endpoint_group_id):
+ """List all projects associated with endpoint group.
+
+ :param endpoint_group_id: identity of endpoint to associate
+ :type endpoint_group_id: string
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def remove_endpoint_group_from_project(self, endpoint_group_id,
+ project_id):
+ """Remove an endpoint to project association.
+
+ :param endpoint_group_id: identity of endpoint to associate
+ :type endpoint_group_id: string
+ :param project_id: identity of project to associate
+ :type project_id: string
+ :raises keystone.exception.NotFound: If endpoint group project
+ association was not found.
+ :returns: None.
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
+
+ @abc.abstractmethod
+ def delete_endpoint_group_association_by_project(self, project_id):
+ """Remove endpoint group to project associations.
+
+ :param project_id: identity of the project to check
+ :type project_id: string
+ :returns: None
+
+ """
+ raise exception.NotImplemented() # pragma: no cover
Driver = manager.create_legacy_driver(CatalogDriverV8)