Properly return 501 for unsupported Catalog calls

Similar to the other APIs, this creates a Driver class that describes
expected functionality of the catalog driver and raises NotImplemented
accordingly. NotImplementedError()'s are caught and returned as proper
501s instead of AttributeErrors.

Also fixes some inconsistent paramters names in the sql backend.

Fixes bug 954087

Update: Convert usage of NotImplementedError() to new
        keystone.exception.NotImplemented() for all
        unimplemented driver actions.

Change-Id: I69d8e21a6f651e69b724ec5ed5784645bad80c00
This commit is contained in:
Adam Gandelman 2012-03-13 16:23:45 -07:00
parent acc9f892b3
commit 9363d5fea6
9 changed files with 127 additions and 39 deletions

View File

@ -207,7 +207,7 @@ variety of CRUD operations are provided for the sake of development and testing.
CRUD is treated as an extension or additional feature to the core feature set
in that it is not required that a backend support it. It is expected that
backends for services that don't support the CRUD operations will raise a
:mod:`NotImplementedError`.
:mod:`keystone.exception.NotImplemented`.
----------------------------------

View File

@ -15,10 +15,11 @@
# under the License.
from keystone import catalog
from keystone.common import kvs
class Catalog(kvs.Base):
class Catalog(kvs.Base, catalog.Driver):
# Public interface
def get_catalog(self, user_id, tenant_id, metadata=None):
return self.db.get('catalog-%s-%s' % (tenant_id, user_id))

View File

@ -77,7 +77,7 @@ class Endpoint(sql.ModelBase, sql.DictBase):
return extra_copy
class Catalog(sql.Base):
class Catalog(sql.Base, catalog.Driver):
def db_sync(self):
migration.db_sync()
@ -99,7 +99,7 @@ class Catalog(sql.Base):
session.delete(service_ref)
session.flush()
def create_service(self, context, service_ref):
def create_service(self, service_id, service_ref):
session = self.get_session()
with session.begin():
service = Service.from_dict(service_ref)
@ -114,7 +114,7 @@ class Catalog(sql.Base):
return True
# Endpoints
def create_endpoint(self, context, endpoint_ref):
def create_endpoint(self, endpoint_id, endpoint_ref):
session = self.get_session()
new_endpoint = Endpoint.from_dict(endpoint_ref)
with session.begin():

View File

@ -22,6 +22,7 @@ import uuid
import webob.exc
from keystone import config
from keystone import exception
from keystone import identity
from keystone import policy
from keystone import token
@ -44,6 +45,84 @@ class Manager(manager.Manager):
super(Manager, self).__init__(CONF.catalog.driver)
class Driver(object):
"""Interface description for an Catalog driver."""
def list_services(self):
"""List all service ids in catalog.
Returns: list of service_ids or an empty list.
"""
raise exception.NotImplemented()
def get_service(self, service_id):
"""Get service by id.
Returns: service_ref dict or None.
"""
raise exception.NotImplemented()
def delete_service(self, service_id):
raise exception.NotImplemented()
def create_service(self, service_id, service_ref):
raise exception.NotImplemented()
def service_exists(self, service_id):
"""Query existence of a service by id.
Returns: True if the service exists or False.
"""
raise exception.NotImplemented()
def create_endpoint(self, endpoint_id, endpoint_ref):
raise exception.NotImplemented()
def delete_endpoint(self, endpoint_id):
raise exception.NotImplemented()
def get_endpoint(self, endpoint_id):
"""Get endpoint by id.
Returns: endpoint_ref dict or None.
"""
raise exception.NotImplemented()
def list_endpoints(self):
"""List all endpoint ids in catalog.
Returns: list of endpoint_ids or an empty list.
"""
raise exception.NotImplemented()
def get_catalog(self, user_id, tenant_id, metadata=None):
"""Retreive and format the current service catalog.
Returns: A nested dict representing the service catalog or an
empty dict.
Example:
{ 'RegionOne':
{'compute': {
'adminURL': u'http://host:8774/v1.1/tenantid',
'internalURL': u'http://host:8774/v1.1/tenant_id',
'name': 'Compute Service',
'publicURL': u'http://host:8774/v1.1/tenantid'},
'ec2': {
'adminURL': 'http://host:8773/services/Admin',
'internalURL': 'http://host:8773/services/Cloud',
'name': 'EC2 Service',
'publicURL': 'http://host:8773/services/Cloud'}}
"""
raise exception.NotImplemented()
class ServiceController(wsgi.Application):
def __init__(self):
self.catalog_api = Manager()

View File

@ -69,5 +69,11 @@ class NotFound(Error):
title = 'Not Found'
class NotImplemented(Error):
"""The action you have requested has not been implemented."""
code = 501
action = 'Not Implemented'
class TokenNotFound(NotFound):
"""Could not find token: %(token_id)s"""

View File

@ -54,7 +54,7 @@ class Driver(object):
Returns: (user, tenant, metadata).
"""
raise NotImplementedError()
raise exception.NotImplemented()
def get_tenant(self, tenant_id):
"""Get a tenant by id.
@ -62,7 +62,7 @@ class Driver(object):
Returns: tenant_ref or None.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def get_tenant_by_name(self, tenant_name):
"""Get a tenant by name.
@ -70,7 +70,7 @@ class Driver(object):
Returns: tenant_ref or None.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def get_user(self, user_id):
"""Get a user by id.
@ -78,7 +78,7 @@ class Driver(object):
Returns: user_ref or None.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def get_user_by_name(self, user_name):
"""Get a user by name.
@ -86,7 +86,7 @@ class Driver(object):
Returns: user_ref or None.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def get_role(self, role_id):
"""Get a role by id.
@ -94,7 +94,7 @@ class Driver(object):
Returns: role_ref or None.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def list_users(self):
"""List all users in the system.
@ -105,7 +105,7 @@ class Driver(object):
Returns: a list of user_refs or an empty list.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def list_roles(self):
"""List all roles in the system.
@ -113,18 +113,18 @@ class Driver(object):
Returns: a list of role_refs or an empty list.
"""
raise NotImplementedError()
raise exception.NotImplemented()
# NOTE(termie): seven calls below should probably be exposed by the api
# more clearly when the api redesign happens
def add_user_to_tenant(self, tenant_id, user_id):
raise NotImplementedError()
raise exception.NotImplemented()
def remove_user_from_tenant(self, tenant_id, user_id):
raise NotImplementedError()
raise exception.NotImplemented()
def get_all_tenants(self):
raise NotImplementedError()
raise exception.NotImplemented()
def get_tenants_for_user(self, user_id):
"""Get the tenants associated with a given user.
@ -132,7 +132,7 @@ class Driver(object):
Returns: a list of tenant ids.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def get_roles_for_user_and_tenant(self, user_id, tenant_id):
"""Get the roles associated with a user within given tenant.
@ -140,59 +140,59 @@ class Driver(object):
Returns: a list of role ids.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id):
"""Add a role to a user within given tenant."""
raise NotImplementedError()
raise exception.NotImplemented()
def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id):
"""Remove a role from a user within given tenant."""
raise NotImplementedError()
raise exception.NotImplemented()
# user crud
def create_user(self, user_id, user):
raise NotImplementedError()
raise exception.NotImplemented()
def update_user(self, user_id, user):
raise NotImplementedError()
raise exception.NotImplemented()
def delete_user(self, user_id):
raise NotImplementedError()
raise exception.NotImplemented()
# tenant crud
def create_tenant(self, tenant_id, tenant):
raise NotImplementedError()
raise exception.NotImplemented()
def update_tenant(self, tenant_id, tenant):
raise NotImplementedError()
raise exception.NotImplemented()
def delete_tenant(self, tenant_id, tenant):
raise NotImplementedError()
raise exception.NotImplemented()
# metadata crud
def get_metadata(self, user_id, tenant_id):
raise NotImplementedError()
raise exception.NotImplemented()
def create_metadata(self, user_id, tenant_id, metadata):
raise NotImplementedError()
raise exception.NotImplemented()
def update_metadata(self, user_id, tenant_id, metadata):
raise NotImplementedError()
raise exception.NotImplemented()
def delete_metadata(self, user_id, tenant_id, metadata):
raise NotImplementedError()
raise exception.NotImplemented()
# role crud
def create_role(self, role_id, role):
raise NotImplementedError()
raise exception.NotImplemented()
def update_role(self, role_id, role):
raise NotImplementedError()
raise exception.NotImplemented()
def delete_role(self, role_id):
raise NotImplementedError()
raise exception.NotImplemented()
class PublicRouter(wsgi.ComposableRouter):

View File

@ -17,6 +17,7 @@
"""Main entry point into the Policy service."""
from keystone import config
from keystone import exception
from keystone.common import manager
@ -42,4 +43,4 @@ class Driver(object):
For more information on a full implementation of this see:
`keystone.common.policy.enforce`.
"""
raise NotImplementedError()
raise exception.NotImplemented()

View File

@ -149,7 +149,7 @@ class VersionController(wsgi.Application):
if service == 'identity':
return service_ref[self.url_key]
raise NotImplementedError()
raise exception.NotImplemented()
def _get_versions_list(self, context):
"""The list of versions is dependent on the context."""
@ -421,7 +421,7 @@ class TokenController(wsgi.Application):
def endpoints(self, context, token_id):
"""Return a list of endpoints available to the token."""
raise NotImplementedError()
raise exception.NotImplemented()
def _format_authenticate(self, token_ref, roles_ref, catalog_ref):
o = self._format_token(token_ref, roles_ref)

View File

@ -19,6 +19,7 @@
import datetime
from keystone import config
from keystone import exception
from keystone.common import manager
@ -50,7 +51,7 @@ class Driver(object):
:raises: keystone.exception.TokenNotFound
"""
raise NotImplementedError()
raise exception.NotImplemented()
def create_token(self, token_id, data):
"""Create a token by id and data.
@ -73,7 +74,7 @@ class Driver(object):
:returns: token_ref or None.
"""
raise NotImplementedError()
raise exception.NotImplemented()
def delete_token(self, token_id):
"""Deletes a token by id.
@ -84,7 +85,7 @@ class Driver(object):
:raises: keystone.exception.TokenNotFound
"""
raise NotImplementedError()
raise exception.NotImplemented()
def _get_default_expire_time(self):
"""Determine when a token should expire based on the config.