Move endpoint filter into keystone core

Remove endpoint_filter as an extension and move it to a core resource,
i.e. consolidate endpoint_filter extension into catalog.
For now we leave the database migrations in the extension directory,
this will address in a separate patch.

DocImpact: Remove the endpoint_filter extension from keystone's
paste pipeline.
Co-Authored-By: Jamie Lennox <jamielennox@redhat.com>
Partially implements: bp move-extensions

Change-Id: Ib8969d584f6be053aaad6e6051fde5e3b7ef7485
This commit is contained in:
Dave Chen 2015-05-15 10:04:18 +08:00
parent ea776641f3
commit dafbf5b8b2
18 changed files with 1166 additions and 736 deletions

View File

@ -33,9 +33,6 @@ use = egg:keystone#ec2_extension_v3
[filter:s3_extension]
use = egg:keystone#s3_extension
[filter:endpoint_filter_extension]
use = egg:keystone#endpoint_filter_extension
[filter:simple_cert_extension]
use = egg:keystone#simple_cert_extension
@ -67,7 +64,7 @@ pipeline = sizelimit url_normalize request_id build_auth_context token_auth admi
[pipeline:api_v3]
# The last item in this pipeline must be service_v3 or an equivalent
# application. It cannot be a filter.
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension_v3 s3_extension simple_cert_extension endpoint_filter_extension service_v3
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension_v3 s3_extension simple_cert_extension service_v3
[app:public_version_service]
use = egg:keystone#public_version_service

View File

@ -152,3 +152,213 @@ class Catalog(kvs.Base, catalog.CatalogDriverV8):
def _create_catalog(self, user_id, tenant_id, data):
self.db.set('catalog-%s-%s' % (tenant_id, user_id), data)
return data
# TODO(davechen): Apparently, these methods are not implemented but
# we cannot raise exception.NotImplemented() just because the notification
# to those resource will break some testcases, will look into CADF to
# see if there is any better way to do this.
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,
:returns: None.
"""
pass
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: exception.NotFound
:returns: None.
"""
pass
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: exception.NotFound
:returns: None.
"""
pass
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.
"""
pass
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.
"""
pass
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
"""
pass
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
"""
pass
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,
:returns: an endpoint group representation.
"""
pass
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: exception.NotFound
:returns: an endpoint group representation.
"""
pass
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: exception.NotFound
:returns: an endpoint group representation.
"""
pass
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: exception.NotFound
:returns: None.
"""
pass
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,
:returns: None.
"""
pass
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: exception.NotFound
:returns: a project endpoint group representation.
"""
pass
def list_endpoint_groups(self):
"""List all endpoint groups.
:raises: exception.NotFound
:returns: None.
"""
pass
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
:raises: exception.NotFound
:returns: None.
"""
pass
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
:raises: exception.NotFound
:returns: None.
"""
pass
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: exception.NotFound
:returns: None.
"""
pass
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
"""
pass

View File

@ -23,6 +23,7 @@ from keystone import catalog
from keystone.catalog import core
from keystone.common import sql
from keystone import exception
from keystone.i18n import _
CONF = cfg.CONF
@ -381,3 +382,210 @@ class Catalog(catalog.CatalogDriverV8):
return service
return [make_v3_service(svc) for svc in services]
@sql.handle_conflicts(conflict_type='project_endpoint')
def add_endpoint_to_project(self, endpoint_id, project_id):
session = sql.get_session()
with session.begin():
endpoint_filter_ref = ProjectEndpoint(endpoint_id=endpoint_id,
project_id=project_id)
session.add(endpoint_filter_ref)
def _get_project_endpoint_ref(self, session, endpoint_id, project_id):
endpoint_filter_ref = session.query(ProjectEndpoint).get(
(endpoint_id, project_id))
if endpoint_filter_ref is None:
msg = _('Endpoint %(endpoint_id)s not found in project '
'%(project_id)s') % {'endpoint_id': endpoint_id,
'project_id': project_id}
raise exception.NotFound(msg)
return endpoint_filter_ref
def check_endpoint_in_project(self, endpoint_id, project_id):
session = sql.get_session()
self._get_project_endpoint_ref(session, endpoint_id, project_id)
def remove_endpoint_from_project(self, endpoint_id, project_id):
session = sql.get_session()
endpoint_filter_ref = self._get_project_endpoint_ref(
session, endpoint_id, project_id)
with session.begin():
session.delete(endpoint_filter_ref)
def list_endpoints_for_project(self, project_id):
session = sql.get_session()
query = session.query(ProjectEndpoint)
query = query.filter_by(project_id=project_id)
endpoint_filter_refs = query.all()
return [ref.to_dict() for ref in endpoint_filter_refs]
def list_projects_for_endpoint(self, endpoint_id):
session = sql.get_session()
query = session.query(ProjectEndpoint)
query = query.filter_by(endpoint_id=endpoint_id)
endpoint_filter_refs = query.all()
return [ref.to_dict() for ref in endpoint_filter_refs]
def delete_association_by_endpoint(self, endpoint_id):
session = sql.get_session()
with session.begin():
query = session.query(ProjectEndpoint)
query = query.filter_by(endpoint_id=endpoint_id)
query.delete(synchronize_session=False)
def delete_association_by_project(self, project_id):
session = sql.get_session()
with session.begin():
query = session.query(ProjectEndpoint)
query = query.filter_by(project_id=project_id)
query.delete(synchronize_session=False)
def create_endpoint_group(self, endpoint_group_id, endpoint_group):
session = sql.get_session()
with session.begin():
endpoint_group_ref = EndpointGroup.from_dict(endpoint_group)
session.add(endpoint_group_ref)
return endpoint_group_ref.to_dict()
def _get_endpoint_group(self, session, endpoint_group_id):
endpoint_group_ref = session.query(EndpointGroup).get(
endpoint_group_id)
if endpoint_group_ref is None:
raise exception.EndpointGroupNotFound(
endpoint_group_id=endpoint_group_id)
return endpoint_group_ref
def get_endpoint_group(self, endpoint_group_id):
session = sql.get_session()
endpoint_group_ref = self._get_endpoint_group(session,
endpoint_group_id)
return endpoint_group_ref.to_dict()
def update_endpoint_group(self, endpoint_group_id, endpoint_group):
session = sql.get_session()
with session.begin():
endpoint_group_ref = self._get_endpoint_group(session,
endpoint_group_id)
old_endpoint_group = endpoint_group_ref.to_dict()
old_endpoint_group.update(endpoint_group)
new_endpoint_group = EndpointGroup.from_dict(old_endpoint_group)
for attr in EndpointGroup.mutable_attributes:
setattr(endpoint_group_ref, attr,
getattr(new_endpoint_group, attr))
return endpoint_group_ref.to_dict()
def delete_endpoint_group(self, endpoint_group_id):
session = sql.get_session()
endpoint_group_ref = self._get_endpoint_group(session,
endpoint_group_id)
with session.begin():
self._delete_endpoint_group_association_by_endpoint_group(
session, endpoint_group_id)
session.delete(endpoint_group_ref)
def get_endpoint_group_in_project(self, endpoint_group_id, project_id):
session = sql.get_session()
ref = self._get_endpoint_group_in_project(session,
endpoint_group_id,
project_id)
return ref.to_dict()
@sql.handle_conflicts(conflict_type='project_endpoint_group')
def add_endpoint_group_to_project(self, endpoint_group_id, project_id):
session = sql.get_session()
with session.begin():
# Create a new Project Endpoint group entity
endpoint_group_project_ref = ProjectEndpointGroupMembership(
endpoint_group_id=endpoint_group_id, project_id=project_id)
session.add(endpoint_group_project_ref)
def _get_endpoint_group_in_project(self, session,
endpoint_group_id, project_id):
endpoint_group_project_ref = session.query(
ProjectEndpointGroupMembership).get((endpoint_group_id,
project_id))
if endpoint_group_project_ref is None:
msg = _('Endpoint Group Project Association not found')
raise exception.NotFound(msg)
else:
return endpoint_group_project_ref
def list_endpoint_groups(self):
session = sql.get_session()
query = session.query(EndpointGroup)
endpoint_group_refs = query.all()
return [e.to_dict() for e in endpoint_group_refs]
def list_endpoint_groups_for_project(self, project_id):
session = sql.get_session()
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(project_id=project_id)
endpoint_group_refs = query.all()
return [ref.to_dict() for ref in endpoint_group_refs]
def remove_endpoint_group_from_project(self, endpoint_group_id,
project_id):
session = sql.get_session()
endpoint_group_project_ref = self._get_endpoint_group_in_project(
session, endpoint_group_id, project_id)
with session.begin():
session.delete(endpoint_group_project_ref)
def list_projects_associated_with_endpoint_group(self, endpoint_group_id):
session = sql.get_session()
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(endpoint_group_id=endpoint_group_id)
endpoint_group_refs = query.all()
return [ref.to_dict() for ref in endpoint_group_refs]
def _delete_endpoint_group_association_by_endpoint_group(
self, session, endpoint_group_id):
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(endpoint_group_id=endpoint_group_id)
query.delete()
def delete_endpoint_group_association_by_project(self, project_id):
session = sql.get_session()
with session.begin():
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(project_id=project_id)
query.delete()
class ProjectEndpoint(sql.ModelBase, sql.ModelDictMixin):
"""project-endpoint relationship table."""
__tablename__ = 'project_endpoint'
attributes = ['endpoint_id', 'project_id']
endpoint_id = sql.Column(sql.String(64),
primary_key=True,
nullable=False)
project_id = sql.Column(sql.String(64),
primary_key=True,
nullable=False)
class EndpointGroup(sql.ModelBase, sql.ModelDictMixin):
"""Endpoint Groups table."""
__tablename__ = 'endpoint_group'
attributes = ['id', 'name', 'description', 'filters']
mutable_attributes = frozenset(['name', 'description', 'filters'])
id = sql.Column(sql.String(64), primary_key=True)
name = sql.Column(sql.String(255), nullable=False)
description = sql.Column(sql.Text, nullable=True)
filters = sql.Column(sql.JsonBlob(), nullable=False)
class ProjectEndpointGroupMembership(sql.ModelBase, sql.ModelDictMixin):
"""Project to Endpoint group relationship table."""
__tablename__ = 'project_endpoint_group'
attributes = ['endpoint_group_id', 'project_id']
endpoint_group_id = sql.Column(sql.String(64),
sql.ForeignKey('endpoint_group.id'),
nullable=False)
project_id = sql.Column(sql.String(64), nullable=False)
__table_args__ = (sql.PrimaryKeyConstraint('endpoint_group_id',
'project_id'), {})

View File

@ -15,6 +15,8 @@
import uuid
import six
from keystone.catalog import core
from keystone.catalog import schema
from keystone.common import controller
@ -24,6 +26,7 @@ from keystone.common import wsgi
from keystone import exception
from keystone.i18n import _
from keystone import notifications
from keystone import resource
INTERFACES = ['public', 'internal', 'admin']
@ -379,3 +382,277 @@ class EndpointV3(controller.V3Controller):
def delete_endpoint(self, context, endpoint_id):
initiator = notifications._get_request_audit_info(context)
return self.catalog_api.delete_endpoint(endpoint_id, initiator)
@dependency.requires('catalog_api', 'resource_api')
class _ControllerBase(controller.V3Controller):
"""Base behaviors for endpoint filter controllers."""
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.catalog_api.list_endpoint_groups_for_project(
project_id)
endpoint_groups = [self.catalog_api.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.catalog_api.list_endpoints()
filters = self.catalog_api.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
class EndpointFilterV3Controller(_ControllerBase):
def __init__(self):
super(EndpointFilterV3Controller, self).__init__()
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':
self.catalog_api.delete_association_by_project(
project_or_endpoint_id)
else:
self.catalog_api.delete_association_by_endpoint(
project_or_endpoint_id)
@controller.protected()
def add_endpoint_to_project(self, context, project_id, endpoint_id):
"""Establishes an association between an endpoint and a project."""
# NOTE(gyee): we just need to make sure endpoint and project exist
# first. We don't really care whether if project is disabled.
# The relationship can still be established even with a disabled
# project as there are no security implications.
self.catalog_api.get_endpoint(endpoint_id)
self.resource_api.get_project(project_id)
self.catalog_api.add_endpoint_to_project(endpoint_id,
project_id)
@controller.protected()
def check_endpoint_in_project(self, context, project_id, endpoint_id):
"""Verifies endpoint is currently associated with given project."""
self.catalog_api.get_endpoint(endpoint_id)
self.resource_api.get_project(project_id)
self.catalog_api.check_endpoint_in_project(endpoint_id,
project_id)
@controller.protected()
def list_endpoints_for_project(self, context, project_id):
"""List all endpoints currently associated with a given project."""
self.resource_api.get_project(project_id)
refs = self.catalog_api.list_endpoints_for_project(project_id)
filtered_endpoints = {ref['endpoint_id']:
self.catalog_api.get_endpoint(ref['endpoint_id'])
for ref in refs}
# 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 EndpointV3.wrap_collection(
context, [v for v in six.itervalues(filtered_endpoints)])
@controller.protected()
def remove_endpoint_from_project(self, context, project_id, endpoint_id):
"""Remove the endpoint from the association with given project."""
self.catalog_api.remove_endpoint_from_project(endpoint_id,
project_id)
@controller.protected()
def list_projects_for_endpoint(self, context, endpoint_id):
"""Return a list of projects associated with the endpoint."""
self.catalog_api.get_endpoint(endpoint_id)
refs = self.catalog_api.list_projects_for_endpoint(endpoint_id)
projects = [self.resource_api.get_project(
ref['project_id']) for ref in refs]
return resource.controllers.ProjectV3.wrap_collection(context,
projects)
class EndpointGroupV3Controller(_ControllerBase):
collection_name = 'endpoint_groups'
member_name = 'endpoint_group'
VALID_FILTER_KEYS = ['service_id', 'region_id', 'interface']
def __init__(self):
super(EndpointGroupV3Controller, self).__init__()
@classmethod
def base_url(cls, context, path=None):
"""Construct a path and pass it to V3Controller.base_url method."""
path = '/OS-EP-FILTER/' + cls.collection_name
return super(EndpointGroupV3Controller, cls).base_url(context,
path=path)
@controller.protected()
@validation.validated(schema.endpoint_group_create, 'endpoint_group')
def create_endpoint_group(self, context, endpoint_group):
"""Creates an Endpoint Group with the associated filters."""
ref = self._assign_unique_id(self._normalize_dict(endpoint_group))
self._require_attribute(ref, 'filters')
self._require_valid_filter(ref)
ref = self.catalog_api.create_endpoint_group(ref['id'], ref)
return EndpointGroupV3Controller.wrap_member(context, ref)
def _require_valid_filter(self, endpoint_group):
filters = endpoint_group.get('filters')
for key in six.iterkeys(filters):
if key not in self.VALID_FILTER_KEYS:
raise exception.ValidationError(
attribute=self._valid_filter_keys(),
target='endpoint_group')
def _valid_filter_keys(self):
return ' or '.join(self.VALID_FILTER_KEYS)
@controller.protected()
def get_endpoint_group(self, context, endpoint_group_id):
"""Retrieve the endpoint group associated with the id if exists."""
ref = self.catalog_api.get_endpoint_group(endpoint_group_id)
return EndpointGroupV3Controller.wrap_member(
context, ref)
@controller.protected()
@validation.validated(schema.endpoint_group_update, 'endpoint_group')
def update_endpoint_group(self, context, endpoint_group_id,
endpoint_group):
"""Update fixed values and/or extend the filters."""
if 'filters' in endpoint_group:
self._require_valid_filter(endpoint_group)
ref = self.catalog_api.update_endpoint_group(endpoint_group_id,
endpoint_group)
return EndpointGroupV3Controller.wrap_member(
context, ref)
@controller.protected()
def delete_endpoint_group(self, context, endpoint_group_id):
"""Delete endpoint_group."""
self.catalog_api.delete_endpoint_group(endpoint_group_id)
@controller.protected()
def list_endpoint_groups(self, context):
"""List all endpoint groups."""
refs = self.catalog_api.list_endpoint_groups()
return EndpointGroupV3Controller.wrap_collection(
context, refs)
@controller.protected()
def list_endpoint_groups_for_project(self, context, project_id):
"""List all endpoint groups associated with a given project."""
return EndpointGroupV3Controller.wrap_collection(
context, self._get_endpoint_groups_for_project(project_id))
@controller.protected()
def list_projects_associated_with_endpoint_group(self,
context,
endpoint_group_id):
"""List all projects associated with endpoint group."""
endpoint_group_refs = (self.catalog_api.
list_projects_associated_with_endpoint_group(
endpoint_group_id))
projects = []
for endpoint_group_ref in endpoint_group_refs:
project = self.resource_api.get_project(
endpoint_group_ref['project_id'])
if project:
projects.append(project)
return resource.controllers.ProjectV3.wrap_collection(context,
projects)
@controller.protected()
def list_endpoints_associated_with_endpoint_group(self,
context,
endpoint_group_id):
"""List all the endpoints filtered by a specific endpoint group."""
filtered_endpoints = self._get_endpoints_filtered_by_endpoint_group(
endpoint_group_id)
return EndpointV3.wrap_collection(context, filtered_endpoints)
class ProjectEndpointGroupV3Controller(_ControllerBase):
collection_name = 'project_endpoint_groups'
member_name = 'project_endpoint_group'
def __init__(self):
super(ProjectEndpointGroupV3Controller, self).__init__()
notifications.register_event_callback(
notifications.ACTIONS.deleted, 'project',
self._on_project_delete)
def _on_project_delete(self, service, resource_type,
operation, payload):
project_id = payload['resource_info']
(self.catalog_api.
delete_endpoint_group_association_by_project(
project_id))
@controller.protected()
def get_endpoint_group_in_project(self, context, endpoint_group_id,
project_id):
"""Retrieve the endpoint group associated with the id if exists."""
self.resource_api.get_project(project_id)
self.catalog_api.get_endpoint_group(endpoint_group_id)
ref = self.catalog_api.get_endpoint_group_in_project(
endpoint_group_id, project_id)
return ProjectEndpointGroupV3Controller.wrap_member(
context, ref)
@controller.protected()
def add_endpoint_group_to_project(self, context, endpoint_group_id,
project_id):
"""Creates an association between an endpoint group and project."""
self.resource_api.get_project(project_id)
self.catalog_api.get_endpoint_group(endpoint_group_id)
self.catalog_api.add_endpoint_group_to_project(
endpoint_group_id, project_id)
@controller.protected()
def remove_endpoint_group_from_project(self, context, endpoint_group_id,
project_id):
"""Remove the endpoint group from associated project."""
self.resource_api.get_project(project_id)
self.catalog_api.get_endpoint_group(endpoint_group_id)
self.catalog_api.remove_endpoint_group_from_project(
endpoint_group_id, project_id)
@classmethod
def _add_self_referential_link(cls, context, ref):
url = ('/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s'
'/projects/%(project_id)s' % {
'endpoint_group_id': ref['endpoint_group_id'],
'project_id': ref['project_id']})
ref.setdefault('links', {})
ref['links']['self'] = url

View File

@ -302,6 +302,25 @@ class Manager(manager.Manager):
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()
@six.add_metaclass(abc.ABCMeta)
class CatalogDriverV8(object):
@ -570,5 +589,228 @@ 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,
: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: exception.NotFound
: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: exception.NotFound
: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,
: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: exception.NotFound
: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: exception.NotFound
: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: exception.NotFound
: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,
: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: exception.NotFound
:returns: a project endpoint group representation.
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def list_endpoint_groups(self):
"""List all endpoint groups.
:raises: exception.NotFound
: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
:raises: exception.NotFound
: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
:raises: exception.NotFound
: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: exception.NotFound
: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)

View File

@ -12,15 +12,72 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
from keystone.catalog import controllers
from keystone.common import json_home
from keystone.common import router
from keystone.common import wsgi
build_resource_relation = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-EP-FILTER', extension_version='1.0')
build_parameter_relation = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-EP-FILTER', extension_version='1.0')
ENDPOINT_GROUP_PARAMETER_RELATION = build_parameter_relation(
parameter_name='endpoint_group_id')
class Routers(wsgi.RoutersBase):
"""API for the keystone catalog.
The API Endpoint Filter looks like::
PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
GET /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
GET /OS-EP-FILTER/endpoints/{endpoint_id}/projects
GET /OS-EP-FILTER/projects/{project_id}/endpoints
GET /OS-EP-FILTER/projects/{project_id}/endpoint_groups
GET /OS-EP-FILTER/endpoint_groups
POST /OS-EP-FILTER/endpoint_groups
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
DELETE /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}/projects
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}/endpoints
PUT /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
DELETE /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
"""
PATH_PREFIX = '/OS-EP-FILTER'
PATH_PROJECT_ENDPOINT = '/projects/{project_id}/endpoints/{endpoint_id}'
PATH_ENDPOINT_GROUPS = '/endpoint_groups/{endpoint_group_id}'
PATH_ENDPOINT_GROUP_PROJECTS = PATH_ENDPOINT_GROUPS + (
'/projects/{project_id}')
def append_v3_routers(self, mapper, routers):
regions_controller = controllers.RegionV3()
endpoint_filter_controller = controllers.EndpointFilterV3Controller()
endpoint_group_controller = controllers.EndpointGroupV3Controller()
project_endpoint_group_controller = (
controllers.ProjectEndpointGroupV3Controller())
routers.append(router.Router(regions_controller,
'regions', 'region',
resource_descriptions=self.v3_resources))
@ -38,3 +95,88 @@ class Routers(wsgi.RoutersBase):
routers.append(router.Router(controllers.EndpointV3(),
'endpoints', 'endpoint',
resource_descriptions=self.v3_resources))
self._add_resource(
mapper, endpoint_filter_controller,
path=self.PATH_PREFIX + '/endpoints/{endpoint_id}/projects',
get_action='list_projects_for_endpoint',
rel=build_resource_relation(resource_name='endpoint_projects'),
path_vars={
'endpoint_id': json_home.Parameters.ENDPOINT_ID,
})
self._add_resource(
mapper, endpoint_filter_controller,
path=self.PATH_PREFIX + self.PATH_PROJECT_ENDPOINT,
get_head_action='check_endpoint_in_project',
put_action='add_endpoint_to_project',
delete_action='remove_endpoint_from_project',
rel=build_resource_relation(resource_name='project_endpoint'),
path_vars={
'endpoint_id': json_home.Parameters.ENDPOINT_ID,
'project_id': json_home.Parameters.PROJECT_ID,
})
self._add_resource(
mapper, endpoint_filter_controller,
path=self.PATH_PREFIX + '/projects/{project_id}/endpoints',
get_action='list_endpoints_for_project',
rel=build_resource_relation(resource_name='project_endpoints'),
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + '/projects/{project_id}/endpoint_groups',
get_action='list_endpoint_groups_for_project',
rel=build_resource_relation(
resource_name='project_endpoint_groups'),
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + '/endpoint_groups',
get_action='list_endpoint_groups',
post_action='create_endpoint_group',
rel=build_resource_relation(resource_name='endpoint_groups'))
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUPS,
get_head_action='get_endpoint_group',
patch_action='update_endpoint_group',
delete_action='delete_endpoint_group',
rel=build_resource_relation(resource_name='endpoint_group'),
path_vars={
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})
self._add_resource(
mapper, project_endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUP_PROJECTS,
get_head_action='get_endpoint_group_in_project',
put_action='add_endpoint_group_to_project',
delete_action='remove_endpoint_group_from_project',
rel=build_resource_relation(
resource_name='endpoint_group_to_project_association'),
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUPS + (
'/projects'),
get_action='list_projects_associated_with_endpoint_group',
rel=build_resource_relation(
resource_name='projects_associated_with_endpoint_group'),
path_vars={
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUPS + (
'/endpoints'),
get_action='list_endpoints_associated_with_endpoint_group',
rel=build_resource_relation(
resource_name='endpoints_in_endpoint_group'),
path_vars={
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystone.common import validation
from keystone.common.validation import parameter_types
@ -96,3 +97,23 @@ endpoint_update = {
'minProperties': 1,
'additionalProperties': True
}
_endpoint_group_properties = {
'description': validation.nullable(parameter_types.description),
'filters': {
'type': 'object'
},
'name': parameter_types.name
}
endpoint_group_create = {
'type': 'object',
'properties': _endpoint_group_properties,
'required': ['name', 'filters']
}
endpoint_group_update = {
'type': 'object',
'properties': _endpoint_group_properties,
'minProperties': 1
}

View File

@ -472,6 +472,9 @@ FILE_OPTIONS = {
'in a policy collection.'),
],
'endpoint_filter': [
cfg.BoolOpt('enabled',
default=True,
help='Enable endpoint filtering functionality.'),
cfg.StrOpt('driver',
default='sql',
help='Entrypoint for the endpoint filter backend driver in '

View File

@ -1,15 +0,0 @@
# Copyright 2013 OpenStack Foundation
#
# 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.
from keystone.contrib.endpoint_filter.core import * # noqa

View File

@ -23,7 +23,7 @@ from keystone import exception
CONF = cfg.CONF
@dependency.requires('endpoint_filter_api')
@dependency.requires('catalog_api')
class EndpointFilterCatalog(sql.Catalog):
def get_v3_catalog(self, user_id, project_id):
substitutions = dict(CONF.items())
@ -31,7 +31,7 @@ class EndpointFilterCatalog(sql.Catalog):
services = {}
refs = self.endpoint_filter_api.list_endpoints_for_project(project_id)
refs = self.catalog_api.list_endpoints_for_project(project_id)
if (not refs and
CONF.endpoint_filter.return_all_endpoints_if_no_filter):
@ -61,7 +61,7 @@ class EndpointFilterCatalog(sql.Catalog):
service['endpoints'] = [endpoint]
except exception.EndpointNotFound:
# remove bad reference from association
self.endpoint_filter_api.remove_endpoint_from_project(
self.catalog_api.remove_endpoint_from_project(
entry['endpoint_id'], project_id)
# format catalog

View File

@ -12,217 +12,19 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystone.common import sql
from keystone.contrib import endpoint_filter
from keystone import exception
from keystone.i18n import _
from oslo_log import versionutils
from keystone.catalog.backends import sql
_OLD = 'keystone.contrib.endpoint_filter.backends.sql.EndpointFilter'
_NEW = 'sql'
class ProjectEndpoint(sql.ModelBase, sql.ModelDictMixin):
"""project-endpoint relationship table."""
__tablename__ = 'project_endpoint'
attributes = ['endpoint_id', 'project_id']
endpoint_id = sql.Column(sql.String(64),
primary_key=True,
nullable=False)
project_id = sql.Column(sql.String(64),
primary_key=True,
nullable=False)
class EndpointGroup(sql.ModelBase, sql.ModelDictMixin):
"""Endpoint Groups table."""
__tablename__ = 'endpoint_group'
attributes = ['id', 'name', 'description', 'filters']
mutable_attributes = frozenset(['name', 'description', 'filters'])
id = sql.Column(sql.String(64), primary_key=True)
name = sql.Column(sql.String(255), nullable=False)
description = sql.Column(sql.Text, nullable=True)
filters = sql.Column(sql.JsonBlob(), nullable=False)
class ProjectEndpointGroupMembership(sql.ModelBase, sql.ModelDictMixin):
"""Project to Endpoint group relationship table."""
__tablename__ = 'project_endpoint_group'
attributes = ['endpoint_group_id', 'project_id']
endpoint_group_id = sql.Column(sql.String(64),
sql.ForeignKey('endpoint_group.id'),
nullable=False)
project_id = sql.Column(sql.String(64), nullable=False)
__table_args__ = (sql.PrimaryKeyConstraint('endpoint_group_id',
'project_id'), {})
class EndpointFilter(endpoint_filter.EndpointFilterDriverV8):
@sql.handle_conflicts(conflict_type='project_endpoint')
def add_endpoint_to_project(self, endpoint_id, project_id):
session = sql.get_session()
with session.begin():
endpoint_filter_ref = ProjectEndpoint(endpoint_id=endpoint_id,
project_id=project_id)
session.add(endpoint_filter_ref)
def _get_project_endpoint_ref(self, session, endpoint_id, project_id):
endpoint_filter_ref = session.query(ProjectEndpoint).get(
(endpoint_id, project_id))
if endpoint_filter_ref is None:
msg = _('Endpoint %(endpoint_id)s not found in project '
'%(project_id)s') % {'endpoint_id': endpoint_id,
'project_id': project_id}
raise exception.NotFound(msg)
return endpoint_filter_ref
def check_endpoint_in_project(self, endpoint_id, project_id):
session = sql.get_session()
self._get_project_endpoint_ref(session, endpoint_id, project_id)
def remove_endpoint_from_project(self, endpoint_id, project_id):
session = sql.get_session()
endpoint_filter_ref = self._get_project_endpoint_ref(
session, endpoint_id, project_id)
with session.begin():
session.delete(endpoint_filter_ref)
def list_endpoints_for_project(self, project_id):
session = sql.get_session()
query = session.query(ProjectEndpoint)
query = query.filter_by(project_id=project_id)
endpoint_filter_refs = query.all()
return [ref.to_dict() for ref in endpoint_filter_refs]
def list_projects_for_endpoint(self, endpoint_id):
session = sql.get_session()
query = session.query(ProjectEndpoint)
query = query.filter_by(endpoint_id=endpoint_id)
endpoint_filter_refs = query.all()
return [ref.to_dict() for ref in endpoint_filter_refs]
def delete_association_by_endpoint(self, endpoint_id):
session = sql.get_session()
with session.begin():
query = session.query(ProjectEndpoint)
query = query.filter_by(endpoint_id=endpoint_id)
query.delete(synchronize_session=False)
def delete_association_by_project(self, project_id):
session = sql.get_session()
with session.begin():
query = session.query(ProjectEndpoint)
query = query.filter_by(project_id=project_id)
query.delete(synchronize_session=False)
def create_endpoint_group(self, endpoint_group_id, endpoint_group):
session = sql.get_session()
with session.begin():
endpoint_group_ref = EndpointGroup.from_dict(endpoint_group)
session.add(endpoint_group_ref)
return endpoint_group_ref.to_dict()
def _get_endpoint_group(self, session, endpoint_group_id):
endpoint_group_ref = session.query(EndpointGroup).get(
endpoint_group_id)
if endpoint_group_ref is None:
raise exception.EndpointGroupNotFound(
endpoint_group_id=endpoint_group_id)
return endpoint_group_ref
def get_endpoint_group(self, endpoint_group_id):
session = sql.get_session()
endpoint_group_ref = self._get_endpoint_group(session,
endpoint_group_id)
return endpoint_group_ref.to_dict()
def update_endpoint_group(self, endpoint_group_id, endpoint_group):
session = sql.get_session()
with session.begin():
endpoint_group_ref = self._get_endpoint_group(session,
endpoint_group_id)
old_endpoint_group = endpoint_group_ref.to_dict()
old_endpoint_group.update(endpoint_group)
new_endpoint_group = EndpointGroup.from_dict(old_endpoint_group)
for attr in EndpointGroup.mutable_attributes:
setattr(endpoint_group_ref, attr,
getattr(new_endpoint_group, attr))
return endpoint_group_ref.to_dict()
def delete_endpoint_group(self, endpoint_group_id):
session = sql.get_session()
endpoint_group_ref = self._get_endpoint_group(session,
endpoint_group_id)
with session.begin():
self._delete_endpoint_group_association_by_endpoint_group(
session, endpoint_group_id)
session.delete(endpoint_group_ref)
def get_endpoint_group_in_project(self, endpoint_group_id, project_id):
session = sql.get_session()
ref = self._get_endpoint_group_in_project(session,
endpoint_group_id,
project_id)
return ref.to_dict()
@sql.handle_conflicts(conflict_type='project_endpoint_group')
def add_endpoint_group_to_project(self, endpoint_group_id, project_id):
session = sql.get_session()
with session.begin():
# Create a new Project Endpoint group entity
endpoint_group_project_ref = ProjectEndpointGroupMembership(
endpoint_group_id=endpoint_group_id, project_id=project_id)
session.add(endpoint_group_project_ref)
def _get_endpoint_group_in_project(self, session,
endpoint_group_id, project_id):
endpoint_group_project_ref = session.query(
ProjectEndpointGroupMembership).get((endpoint_group_id,
project_id))
if endpoint_group_project_ref is None:
msg = _('Endpoint Group Project Association not found')
raise exception.NotFound(msg)
else:
return endpoint_group_project_ref
def list_endpoint_groups(self):
session = sql.get_session()
query = session.query(EndpointGroup)
endpoint_group_refs = query.all()
return [e.to_dict() for e in endpoint_group_refs]
def list_endpoint_groups_for_project(self, project_id):
session = sql.get_session()
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(project_id=project_id)
endpoint_group_refs = query.all()
return [ref.to_dict() for ref in endpoint_group_refs]
def remove_endpoint_group_from_project(self, endpoint_group_id,
project_id):
session = sql.get_session()
endpoint_group_project_ref = self._get_endpoint_group_in_project(
session, endpoint_group_id, project_id)
with session.begin():
session.delete(endpoint_group_project_ref)
def list_projects_associated_with_endpoint_group(self, endpoint_group_id):
session = sql.get_session()
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(endpoint_group_id=endpoint_group_id)
endpoint_group_refs = query.all()
return [ref.to_dict() for ref in endpoint_group_refs]
def _delete_endpoint_group_association_by_endpoint_group(
self, session, endpoint_group_id):
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(endpoint_group_id=endpoint_group_id)
query.delete()
def delete_endpoint_group_association_by_project(self, project_id):
session = sql.get_session()
with session.begin():
query = session.query(ProjectEndpointGroupMembership)
query = query.filter_by(project_id=project_id)
query.delete()
class EndpointFilter(sql.Catalog):
@versionutils.deprecated(
as_of=versionutils.deprecated.MITAKA,
in_favor_of=_NEW,
what=_OLD,
remove_in=2)
def __init__(self, *args, **kwargs):
super(EndpointFilter, self).__init__(*args, **kwargs)

View File

@ -1,299 +0,0 @@
# Copyright 2013 OpenStack Foundation
#
# 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.
import six
from keystone.catalog import controllers as catalog_controllers
from keystone.common import controller
from keystone.common import dependency
from keystone.common import validation
from keystone.contrib.endpoint_filter import schema
from keystone import exception
from keystone import notifications
from keystone import resource
@dependency.requires('catalog_api', 'endpoint_filter_api', 'resource_api')
class _ControllerBase(controller.V3Controller):
"""Base behaviors for endpoint filter controllers."""
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.endpoint_filter_api.list_endpoint_groups_for_project(
project_id)
endpoint_groups = [self.endpoint_filter_api.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.catalog_api.list_endpoints()
filters = self.endpoint_filter_api.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
class EndpointFilterV3Controller(_ControllerBase):
def __init__(self):
super(EndpointFilterV3Controller, self).__init__()
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':
self.endpoint_filter_api.delete_association_by_project(
project_or_endpoint_id)
else:
self.endpoint_filter_api.delete_association_by_endpoint(
project_or_endpoint_id)
@controller.protected()
def add_endpoint_to_project(self, context, project_id, endpoint_id):
"""Establishes an association between an endpoint and a project."""
# NOTE(gyee): we just need to make sure endpoint and project exist
# first. We don't really care whether if project is disabled.
# The relationship can still be established even with a disabled
# project as there are no security implications.
self.catalog_api.get_endpoint(endpoint_id)
self.resource_api.get_project(project_id)
self.endpoint_filter_api.add_endpoint_to_project(endpoint_id,
project_id)
@controller.protected()
def check_endpoint_in_project(self, context, project_id, endpoint_id):
"""Verifies endpoint is currently associated with given project."""
self.catalog_api.get_endpoint(endpoint_id)
self.resource_api.get_project(project_id)
self.endpoint_filter_api.check_endpoint_in_project(endpoint_id,
project_id)
@controller.protected()
def list_endpoints_for_project(self, context, project_id):
"""List all endpoints currently associated with a given project."""
self.resource_api.get_project(project_id)
refs = self.endpoint_filter_api.list_endpoints_for_project(project_id)
filtered_endpoints = {ref['endpoint_id']:
self.catalog_api.get_endpoint(ref['endpoint_id'])
for ref in refs}
# 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 catalog_controllers.EndpointV3.wrap_collection(
context, [v for v in six.itervalues(filtered_endpoints)])
@controller.protected()
def remove_endpoint_from_project(self, context, project_id, endpoint_id):
"""Remove the endpoint from the association with given project."""
self.endpoint_filter_api.remove_endpoint_from_project(endpoint_id,
project_id)
@controller.protected()
def list_projects_for_endpoint(self, context, endpoint_id):
"""Return a list of projects associated with the endpoint."""
self.catalog_api.get_endpoint(endpoint_id)
refs = self.endpoint_filter_api.list_projects_for_endpoint(endpoint_id)
projects = [self.resource_api.get_project(
ref['project_id']) for ref in refs]
return resource.controllers.ProjectV3.wrap_collection(context,
projects)
class EndpointGroupV3Controller(_ControllerBase):
collection_name = 'endpoint_groups'
member_name = 'endpoint_group'
VALID_FILTER_KEYS = ['service_id', 'region_id', 'interface']
def __init__(self):
super(EndpointGroupV3Controller, self).__init__()
@classmethod
def base_url(cls, context, path=None):
"""Construct a path and pass it to V3Controller.base_url method."""
path = '/OS-EP-FILTER/' + cls.collection_name
return super(EndpointGroupV3Controller, cls).base_url(context,
path=path)
@controller.protected()
@validation.validated(schema.endpoint_group_create, 'endpoint_group')
def create_endpoint_group(self, context, endpoint_group):
"""Creates an Endpoint Group with the associated filters."""
ref = self._assign_unique_id(self._normalize_dict(endpoint_group))
self._require_attribute(ref, 'filters')
self._require_valid_filter(ref)
ref = self.endpoint_filter_api.create_endpoint_group(ref['id'], ref)
return EndpointGroupV3Controller.wrap_member(context, ref)
def _require_valid_filter(self, endpoint_group):
filters = endpoint_group.get('filters')
for key in six.iterkeys(filters):
if key not in self.VALID_FILTER_KEYS:
raise exception.ValidationError(
attribute=self._valid_filter_keys(),
target='endpoint_group')
def _valid_filter_keys(self):
return ' or '.join(self.VALID_FILTER_KEYS)
@controller.protected()
def get_endpoint_group(self, context, endpoint_group_id):
"""Retrieve the endpoint group associated with the id if exists."""
ref = self.endpoint_filter_api.get_endpoint_group(endpoint_group_id)
return EndpointGroupV3Controller.wrap_member(
context, ref)
@controller.protected()
@validation.validated(schema.endpoint_group_update, 'endpoint_group')
def update_endpoint_group(self, context, endpoint_group_id,
endpoint_group):
"""Update fixed values and/or extend the filters."""
if 'filters' in endpoint_group:
self._require_valid_filter(endpoint_group)
ref = self.endpoint_filter_api.update_endpoint_group(endpoint_group_id,
endpoint_group)
return EndpointGroupV3Controller.wrap_member(
context, ref)
@controller.protected()
def delete_endpoint_group(self, context, endpoint_group_id):
"""Delete endpoint_group."""
self.endpoint_filter_api.delete_endpoint_group(endpoint_group_id)
@controller.protected()
def list_endpoint_groups(self, context):
"""List all endpoint groups."""
refs = self.endpoint_filter_api.list_endpoint_groups()
return EndpointGroupV3Controller.wrap_collection(
context, refs)
@controller.protected()
def list_endpoint_groups_for_project(self, context, project_id):
"""List all endpoint groups associated with a given project."""
return EndpointGroupV3Controller.wrap_collection(
context, self._get_endpoint_groups_for_project(project_id))
@controller.protected()
def list_projects_associated_with_endpoint_group(self,
context,
endpoint_group_id):
"""List all projects associated with endpoint group."""
endpoint_group_refs = (self.endpoint_filter_api.
list_projects_associated_with_endpoint_group(
endpoint_group_id))
projects = []
for endpoint_group_ref in endpoint_group_refs:
project = self.resource_api.get_project(
endpoint_group_ref['project_id'])
if project:
projects.append(project)
return resource.controllers.ProjectV3.wrap_collection(context,
projects)
@controller.protected()
def list_endpoints_associated_with_endpoint_group(self,
context,
endpoint_group_id):
"""List all the endpoints filtered by a specific endpoint group."""
filtered_endpoints = self._get_endpoints_filtered_by_endpoint_group(
endpoint_group_id)
return catalog_controllers.EndpointV3.wrap_collection(
context, filtered_endpoints)
class ProjectEndpointGroupV3Controller(_ControllerBase):
collection_name = 'project_endpoint_groups'
member_name = 'project_endpoint_group'
def __init__(self):
super(ProjectEndpointGroupV3Controller, self).__init__()
notifications.register_event_callback(
notifications.ACTIONS.deleted, 'project',
self._on_project_delete)
def _on_project_delete(self, service, resource_type,
operation, payload):
project_id = payload['resource_info']
(self.endpoint_filter_api.
delete_endpoint_group_association_by_project(
project_id))
@controller.protected()
def get_endpoint_group_in_project(self, context, endpoint_group_id,
project_id):
"""Retrieve the endpoint group associated with the id if exists."""
self.resource_api.get_project(project_id)
self.endpoint_filter_api.get_endpoint_group(endpoint_group_id)
ref = self.endpoint_filter_api.get_endpoint_group_in_project(
endpoint_group_id, project_id)
return ProjectEndpointGroupV3Controller.wrap_member(
context, ref)
@controller.protected()
def add_endpoint_group_to_project(self, context, endpoint_group_id,
project_id):
"""Creates an association between an endpoint group and project."""
self.resource_api.get_project(project_id)
self.endpoint_filter_api.get_endpoint_group(endpoint_group_id)
self.endpoint_filter_api.add_endpoint_group_to_project(
endpoint_group_id, project_id)
@controller.protected()
def remove_endpoint_group_from_project(self, context, endpoint_group_id,
project_id):
"""Remove the endpoint group from associated project."""
self.resource_api.get_project(project_id)
self.endpoint_filter_api.get_endpoint_group(endpoint_group_id)
self.endpoint_filter_api.remove_endpoint_group_from_project(
endpoint_group_id, project_id)
@classmethod
def _add_self_referential_link(cls, context, ref):
url = ('/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s'
'/projects/%(project_id)s' % {
'endpoint_group_id': ref['endpoint_group_id'],
'project_id': ref['project_id']})
ref.setdefault('links', {})
ref['links']['self'] = url

View File

@ -12,152 +12,22 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
from oslo_log import log
from oslo_log import versionutils
from keystone.common import json_home
from keystone.common import wsgi
from keystone.contrib.endpoint_filter import controllers
from keystone.i18n import _
build_resource_relation = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-EP-FILTER', extension_version='1.0')
build_parameter_relation = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-EP-FILTER', extension_version='1.0')
ENDPOINT_GROUP_PARAMETER_RELATION = build_parameter_relation(
parameter_name='endpoint_group_id')
LOG = log.getLogger(__name__)
class EndpointFilterExtension(wsgi.V3ExtensionRouter):
"""API Endpoints for the Endpoint Filter extension.
class EndpointFilterExtension(wsgi.Middleware):
The API looks like::
PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
GET /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
GET /OS-EP-FILTER/endpoints/{endpoint_id}/projects
GET /OS-EP-FILTER/projects/{project_id}/endpoints
GET /OS-EP-FILTER/projects/{project_id}/endpoint_groups
GET /OS-EP-FILTER/endpoint_groups
POST /OS-EP-FILTER/endpoint_groups
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
DELETE /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}/projects
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}/endpoints
PUT /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
GET /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
DELETE /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
{project_id}
"""
PATH_PREFIX = '/OS-EP-FILTER'
PATH_PROJECT_ENDPOINT = '/projects/{project_id}/endpoints/{endpoint_id}'
PATH_ENDPOINT_GROUPS = '/endpoint_groups/{endpoint_group_id}'
PATH_ENDPOINT_GROUP_PROJECTS = PATH_ENDPOINT_GROUPS + (
'/projects/{project_id}')
def add_routes(self, mapper):
endpoint_filter_controller = controllers.EndpointFilterV3Controller()
endpoint_group_controller = controllers.EndpointGroupV3Controller()
project_endpoint_group_controller = (
controllers.ProjectEndpointGroupV3Controller())
self._add_resource(
mapper, endpoint_filter_controller,
path=self.PATH_PREFIX + '/endpoints/{endpoint_id}/projects',
get_action='list_projects_for_endpoint',
rel=build_resource_relation(resource_name='endpoint_projects'),
path_vars={
'endpoint_id': json_home.Parameters.ENDPOINT_ID,
})
self._add_resource(
mapper, endpoint_filter_controller,
path=self.PATH_PREFIX + self.PATH_PROJECT_ENDPOINT,
get_head_action='check_endpoint_in_project',
put_action='add_endpoint_to_project',
delete_action='remove_endpoint_from_project',
rel=build_resource_relation(resource_name='project_endpoint'),
path_vars={
'endpoint_id': json_home.Parameters.ENDPOINT_ID,
'project_id': json_home.Parameters.PROJECT_ID,
})
self._add_resource(
mapper, endpoint_filter_controller,
path=self.PATH_PREFIX + '/projects/{project_id}/endpoints',
get_action='list_endpoints_for_project',
rel=build_resource_relation(resource_name='project_endpoints'),
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + '/projects/{project_id}/endpoint_groups',
get_action='list_endpoint_groups_for_project',
rel=build_resource_relation(
resource_name='project_endpoint_groups'),
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + '/endpoint_groups',
get_action='list_endpoint_groups',
post_action='create_endpoint_group',
rel=build_resource_relation(resource_name='endpoint_groups'))
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUPS,
get_head_action='get_endpoint_group',
patch_action='update_endpoint_group',
delete_action='delete_endpoint_group',
rel=build_resource_relation(resource_name='endpoint_group'),
path_vars={
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})
self._add_resource(
mapper, project_endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUP_PROJECTS,
get_head_action='get_endpoint_group_in_project',
put_action='add_endpoint_group_to_project',
delete_action='remove_endpoint_group_from_project',
rel=build_resource_relation(
resource_name='endpoint_group_to_project_association'),
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUPS + (
'/projects'),
get_action='list_projects_associated_with_endpoint_group',
rel=build_resource_relation(
resource_name='projects_associated_with_endpoint_group'),
path_vars={
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})
self._add_resource(
mapper, endpoint_group_controller,
path=self.PATH_PREFIX + self.PATH_ENDPOINT_GROUPS + (
'/endpoints'),
get_action='list_endpoints_associated_with_endpoint_group',
rel=build_resource_relation(
resource_name='endpoints_in_endpoint_group'),
path_vars={
'endpoint_group_id': ENDPOINT_GROUP_PARAMETER_RELATION
})
def __init__(self, *args, **kwargs):
super(EndpointFilterExtension, self).__init__(*args, **kwargs)
msg = _("Remove endpoint_filter_extension from the paste pipeline, "
"the endpoint filter extension is now always available. "
"Update the [pipeline:api_v3] section in keystone-paste.ini "
"accordingly as it will be removed in the O release.")
versionutils.report_deprecated_feature(LOG, msg)

View File

@ -1,35 +0,0 @@
# 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.
from keystone.common import validation
from keystone.common.validation import parameter_types
_endpoint_group_properties = {
'description': validation.nullable(parameter_types.description),
'filters': {
'type': 'object'
},
'name': parameter_types.name
}
endpoint_group_create = {
'type': 'object',
'properties': _endpoint_group_properties,
'required': ['name', 'filters']
}
endpoint_group_update = {
'type': 'object',
'properties': _endpoint_group_properties,
'minProperties': 1
}

View File

@ -14,7 +14,6 @@ from keystone import assignment
from keystone import auth
from keystone import catalog
from keystone.common import cache
from keystone.contrib import endpoint_filter
from keystone import credential
from keystone import endpoint_policy
from keystone import federation
@ -45,7 +44,6 @@ def load_backends():
catalog_api=catalog.Manager(),
credential_api=credential.Manager(),
domain_config_api=resource.DomainConfigManager(),
endpoint_filter_api=endpoint_filter.Manager(),
endpoint_policy_api=endpoint_policy.Manager(),
federation_api=federation.Manager(),
id_generator_api=identity.generator.Manager(),

View File

@ -15,25 +15,25 @@
import copy
import uuid
import mock
from oslo_log import versionutils
from six.moves import http_client
from testtools import matchers
from keystone.contrib.endpoint_filter import routers
from keystone.tests import unit
from keystone.tests.unit import test_v3
class TestExtensionCase(test_v3.RestfulTestCase):
EXTENSION_NAME = 'endpoint_filter'
EXTENSION_TO_ADD = 'endpoint_filter_extension'
class EndpointFilterTestCase(test_v3.RestfulTestCase):
def config_overrides(self):
super(TestExtensionCase, self).config_overrides()
super(EndpointFilterTestCase, self).config_overrides()
self.config_fixture.config(
group='catalog', driver='endpoint_filter.sql')
def setUp(self):
super(TestExtensionCase, self).setUp()
super(EndpointFilterTestCase, self).setUp()
self.default_request_url = (
'/OS-EP-FILTER/projects/%(project_id)s'
'/endpoints/%(endpoint_id)s' % {
@ -41,7 +41,17 @@ class TestExtensionCase(test_v3.RestfulTestCase):
'endpoint_id': self.endpoint_id})
class EndpointFilterCRUDTestCase(TestExtensionCase):
class EndpointFilterDeprecateTestCase(test_v3.RestfulTestCase):
@mock.patch.object(versionutils, 'report_deprecated_feature')
def test_exception_happens(self, mock_deprecator):
routers.EndpointFilterExtension(mock.ANY)
mock_deprecator.assert_called_once_with(mock.ANY, mock.ANY)
args, _kwargs = mock_deprecator.call_args
self.assertIn("Remove endpoint_filter_extension from", args[1])
class EndpointFilterCRUDTestCase(EndpointFilterTestCase):
def test_create_endpoint_project_association(self):
"""PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
@ -269,8 +279,8 @@ class EndpointFilterCRUDTestCase(TestExtensionCase):
self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id'])
# add the second endpoint to default project, bypassing
# endpoint_filter_api API manager.
self.endpoint_filter_api.driver.add_endpoint_to_project(
# catalog_api API manager.
self.catalog_api.driver.add_endpoint_to_project(
endpoint_id2,
self.default_domain_project_id)
@ -283,13 +293,13 @@ class EndpointFilterCRUDTestCase(TestExtensionCase):
self.assertEqual(1, len(catalog[0]['endpoints']))
# remove the endpoint2 from the default project, and add it again via
# endpoint_filter_api API manager.
self.endpoint_filter_api.driver.remove_endpoint_from_project(
# catalog_api API manager.
self.catalog_api.driver.remove_endpoint_from_project(
endpoint_id2,
self.default_domain_project_id)
# add second endpoint to default project, this can be done by calling
# the endpoint_filter_api API manager directly but call the REST API
# the catalog_api API manager directly but call the REST API
# instead for consistency.
self.put('/OS-EP-FILTER/projects/%(project_id)s'
'/endpoints/%(endpoint_id)s' % {
@ -340,8 +350,8 @@ class EndpointFilterCRUDTestCase(TestExtensionCase):
self.assertListEqual([self.endpoint_id, endpoint_id2], ep_id_list)
# remove the endpoint2 from the default project, bypassing
# endpoint_filter_api API manager.
self.endpoint_filter_api.driver.remove_endpoint_from_project(
# catalog_api API manager.
self.catalog_api.driver.remove_endpoint_from_project(
endpoint_id2,
self.default_domain_project_id)
@ -355,13 +365,13 @@ class EndpointFilterCRUDTestCase(TestExtensionCase):
self.assertEqual(2, len(catalog[0]['endpoints']))
# add back the endpoint2 to the default project, and remove it by
# endpoint_filter_api API manage.
self.endpoint_filter_api.driver.add_endpoint_to_project(
# catalog_api API manage.
self.catalog_api.driver.add_endpoint_to_project(
endpoint_id2,
self.default_domain_project_id)
# remove the endpoint2 from the default project, this can be done
# by calling the endpoint_filter_api API manager directly but call
# by calling the catalog_api API manager directly but call
# the REST API instead for consistency.
self.delete('/OS-EP-FILTER/projects/%(project_id)s'
'/endpoints/%(endpoint_id)s' % {
@ -378,7 +388,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase):
self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id'])
class EndpointFilterTokenRequestTestCase(TestExtensionCase):
class EndpointFilterTokenRequestTestCase(EndpointFilterTestCase):
def test_project_scoped_token_using_endpoint_filter(self):
"""Verify endpoints from project scoped token filtered."""
@ -595,7 +605,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase):
auth_catalog.result['catalog'])
class JsonHomeTests(TestExtensionCase, test_v3.JsonHomeTestMixin):
class JsonHomeTests(EndpointFilterTestCase, test_v3.JsonHomeTestMixin):
JSON_HOME_DATA = {
'http://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
'1.0/rel/endpoint_projects': {
@ -666,7 +676,7 @@ class JsonHomeTests(TestExtensionCase, test_v3.JsonHomeTestMixin):
}
class EndpointGroupCRUDTestCase(TestExtensionCase):
class EndpointGroupCRUDTestCase(EndpointFilterTestCase):
DEFAULT_ENDPOINT_GROUP_BODY = {
'endpoint_group': {

View File

@ -21,7 +21,6 @@ from keystone.catalog import schema as catalog_schema
from keystone.common import validation
from keystone.common.validation import parameter_types
from keystone.common.validation import validators
from keystone.contrib.endpoint_filter import schema as endpoint_filter_schema
from keystone.credential import schema as credential_schema
from keystone import exception
from keystone.federation import schema as federation_schema
@ -1298,8 +1297,8 @@ class EndpointGroupValidationTestCase(unit.BaseTestCase):
def setUp(self):
super(EndpointGroupValidationTestCase, self).setUp()
create = endpoint_filter_schema.endpoint_group_create
update = endpoint_filter_schema.endpoint_group_update
create = catalog_schema.endpoint_group_create
update = catalog_schema.endpoint_group_update
self.create_endpoint_grp_validator = validators.SchemaValidator(create)
self.update_endpoint_grp_validator = validators.SchemaValidator(update)

View File

@ -156,7 +156,7 @@ keystone.trust =
sql = keystone.trust.backends.sql:Trust
keystone.endpoint_filter =
sql = keystone.contrib.endpoint_filter.backends.sql:EndpointFilter
sql = keystone.catalog.backends.sql:Catalog
keystone.endpoint_policy =
sql = keystone.endpoint_policy.backends.sql:EndpointPolicy