From 2e7b4f2027a1147ca28301e4f88adf8274b39a1f Mon Sep 17 00:00:00 2001 From: DUVAL Thomas Date: Thu, 9 Jun 2016 09:11:50 +0200 Subject: Update Keystone core to Mitaka. Change-Id: Ia10d6add16f4a9d25d1f42d420661c46332e69db --- keystone-moon/keystone/catalog/core.py | 388 +++++++++++++++++++++++++++++++-- 1 file changed, 367 insertions(+), 21 deletions(-) (limited to 'keystone-moon/keystone/catalog/core.py') 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) -- cgit 1.2.3-korg