keystone.catalog.core

Source code for keystone.catalog.core

# Copyright 2012 OpenStack Foundation
# Copyright 2012 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""Main entry point into the Catalog service."""

from keystone.common import cache
from keystone.common import driver_hints
from keystone.common import manager
from keystone.common import provider_api
import keystone.conf
from keystone import exception
from keystone.i18n import _
from keystone import notifications


CONF = keystone.conf.CONF
PROVIDERS = provider_api.ProviderAPIs


# 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 = cache.create_region(name='computed catalog region')
MEMOIZE_COMPUTED_CATALOG = cache.get_memoization_decorator(
    group='catalog',
    region=COMPUTED_CATALOG_REGION)


[docs]class Manager(manager.Manager): """Default pivot point for the Catalog backend. See :mod:`keystone.common.manager.Manager` for more details on how this dynamically calls the backend. """ driver_namespace = 'keystone.catalog' _provides_api = 'catalog_api' _ENDPOINT = 'endpoint' _SERVICE = 'service' _REGION = 'region' def __init__(self): super(Manager, self).__init__(CONF.catalog.driver) notifications.register_event_callback( notifications.ACTIONS.deleted, 'project', self._on_project_or_endpoint_delete) notifications.register_event_callback( notifications.ACTIONS.deleted, 'endpoint', self._on_project_or_endpoint_delete) def _on_project_or_endpoint_delete(self, service, resource_type, operation, payload): project_or_endpoint_id = payload['resource_info'] if resource_type == 'project': PROVIDERS.catalog_api.delete_association_by_project( project_or_endpoint_id) PROVIDERS.catalog_api.delete_endpoint_group_association_by_project( project_or_endpoint_id) else: PROVIDERS.catalog_api.delete_association_by_endpoint( project_or_endpoint_id)
[docs] def create_region(self, region_ref, initiator=None): # Check duplicate ID try: self.get_region(region_ref['id']) except exception.RegionNotFound: # nosec # A region with the same id doesn't exist already, good. pass else: msg = _('Duplicate ID, %s.') % region_ref['id'] raise exception.Conflict(type='region', details=msg) # NOTE(lbragstad,dstanek): The description column of the region # database cannot be null. So if the user doesn't pass in a # description or passes in a null description then set it to an # empty string. if region_ref.get('description') is None: region_ref['description'] = '' try: ret = self.driver.create_region(region_ref) except exception.NotFound: parent_region_id = region_ref.get('parent_region_id') raise exception.RegionNotFound(region_id=parent_region_id) notifications.Audit.created(self._REGION, ret['id'], initiator) COMPUTED_CATALOG_REGION.invalidate() return ret
[docs] @MEMOIZE def get_region(self, region_id): try: return self.driver.get_region(region_id) except exception.NotFound: raise exception.RegionNotFound(region_id=region_id)
[docs] def update_region(self, region_id, region_ref, initiator=None): # NOTE(lbragstad,dstanek): The description column of the region # database cannot be null. So if the user passes in a null # description set it to an empty string. if 'description' in region_ref and region_ref['description'] is None: region_ref['description'] = '' 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
[docs] def delete_region(self, region_id, initiator=None): try: 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)
[docs] @manager.response_truncated def list_regions(self, hints=None): return self.driver.list_regions(hints or driver_hints.Hints())
[docs] def create_service(self, service_id, service_ref, initiator=None): service_ref.setdefault('enabled', True) 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
[docs] @MEMOIZE def get_service(self, service_id): try: return self.driver.get_service(service_id) except exception.NotFound: raise exception.ServiceNotFound(service_id=service_id)
[docs] def update_service(self, service_id, service_ref, initiator=None): 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
[docs] def delete_service(self, service_id, initiator=None): try: endpoints = self.list_endpoints() ret = self.driver.delete_service(service_id) notifications.Audit.deleted(self._SERVICE, service_id, initiator) self.get_service.invalidate(self, service_id) 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)
[docs] @manager.response_truncated def list_services(self, hints=None): return self.driver.list_services(hints or driver_hints.Hints())
def _assert_region_exists(self, region_id): try: if region_id is not None: self.get_region(region_id) except exception.RegionNotFound: raise exception.ValidationError(attribute='endpoint region_id', target='region table') def _assert_service_exists(self, service_id): try: if service_id is not None: self.get_service(service_id) except exception.ServiceNotFound: raise exception.ValidationError(attribute='endpoint service_id', target='service table')
[docs] def create_endpoint(self, endpoint_id, endpoint_ref, initiator=None): self._assert_region_exists(endpoint_ref.get('region_id')) self._assert_service_exists(endpoint_ref['service_id']) ref = self.driver.create_endpoint(endpoint_id, endpoint_ref) notifications.Audit.created(self._ENDPOINT, endpoint_id, initiator) COMPUTED_CATALOG_REGION.invalidate() return ref
[docs] def update_endpoint(self, endpoint_id, endpoint_ref, initiator=None): self._assert_region_exists(endpoint_ref.get('region_id')) self._assert_service_exists(endpoint_ref.get('service_id')) 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
[docs] def delete_endpoint(self, endpoint_id, initiator=None): try: 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)
[docs] @MEMOIZE def get_endpoint(self, endpoint_id): try: return self.driver.get_endpoint(endpoint_id) except exception.NotFound: raise exception.EndpointNotFound(endpoint_id=endpoint_id)
[docs] @manager.response_truncated def list_endpoints(self, hints=None): return self.driver.list_endpoints(hints or driver_hints.Hints())
[docs] @MEMOIZE_COMPUTED_CATALOG def get_v3_catalog(self, user_id, project_id): return self.driver.get_v3_catalog(user_id, project_id)
[docs] def add_endpoint_to_project(self, endpoint_id, project_id): self.driver.add_endpoint_to_project(endpoint_id, project_id) COMPUTED_CATALOG_REGION.invalidate()
[docs] def remove_endpoint_from_project(self, endpoint_id, project_id): self.driver.remove_endpoint_from_project(endpoint_id, project_id) COMPUTED_CATALOG_REGION.invalidate()
[docs] 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()
[docs] 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()
[docs] def delete_endpoint_group_association_by_project(self, project_id): try: self.driver.delete_endpoint_group_association_by_project( project_id) except exception.NotImplemented: # Some catalog drivers don't support this pass
[docs] def get_endpoint_groups_for_project(self, project_id): # recover the project endpoint group memberships and for each # membership recover the endpoint group PROVIDERS.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 []
[docs] 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
[docs] 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
[docs] def delete_association_by_endpoint(self, endpoint_id): try: self.driver.delete_association_by_endpoint(endpoint_id) except exception.NotImplemented: # Some catalog drivers don't support this pass
[docs] def delete_association_by_project(self, project_id): try: self.driver.delete_association_by_project(project_id) except exception.NotImplemented: # Some catalog drivers don't support this pass
Creative Commons Attribution 3.0 License

Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.