Merge "Convert endpoints api to flask native dispatching"
This commit is contained in:
commit
604141a230
|
@ -12,6 +12,7 @@
|
|||
|
||||
from keystone.api import credentials
|
||||
from keystone.api import discovery
|
||||
from keystone.api import endpoints
|
||||
from keystone.api import limits
|
||||
from keystone.api import os_ep_filter
|
||||
from keystone.api import os_oauth1
|
||||
|
@ -25,6 +26,7 @@ from keystone.api import trusts
|
|||
__all__ = (
|
||||
'discovery',
|
||||
'credentials',
|
||||
'endpoints',
|
||||
'limits',
|
||||
'os_ep_filter',
|
||||
'os_oauth1',
|
||||
|
@ -39,6 +41,7 @@ __all__ = (
|
|||
__apis__ = (
|
||||
discovery,
|
||||
credentials,
|
||||
endpoints,
|
||||
limits,
|
||||
os_ep_filter,
|
||||
os_oauth1,
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
# 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.
|
||||
|
||||
# This file handles all flask-restful resources for /v3/services
|
||||
|
||||
import flask_restful
|
||||
import functools
|
||||
from six.moves import http_client
|
||||
|
||||
from keystone.catalog import schema
|
||||
from keystone.common import json_home
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import rbac_enforcer
|
||||
from keystone.common import utils
|
||||
from keystone.common import validation
|
||||
from keystone import exception
|
||||
from keystone.server import flask as ks_flask
|
||||
|
||||
|
||||
ENFORCER = rbac_enforcer.RBACEnforcer
|
||||
PROVIDERS = provider_api.ProviderAPIs
|
||||
|
||||
# TODO(morgan) move the partial functions to a common place so that they
|
||||
# can be referenced by multiple APIs, such as endpoints and endpoint_policy.
|
||||
_build_resource_relation = functools.partial(
|
||||
json_home.build_v3_extension_resource_relation,
|
||||
extension_name='OS-ENDPOINT-POLICY', extension_version='1.0')
|
||||
|
||||
|
||||
def _filter_endpoint(ref):
|
||||
ref.pop('legacy_endpoint_id', None)
|
||||
ref['region'] = ref['region_id']
|
||||
return ref
|
||||
|
||||
|
||||
class EndpointResource(ks_flask.ResourceBase):
|
||||
collection_key = 'endpoints'
|
||||
member_key = 'endpoint'
|
||||
|
||||
def __init__(self):
|
||||
super(EndpointResource, self).__init__()
|
||||
self.get_member_from_driver = PROVIDERS.catalog_api.get_endpoint
|
||||
|
||||
@staticmethod
|
||||
def _validate_endpoint_region(endpoint):
|
||||
"""Ensure the region for the endpoint exists.
|
||||
|
||||
If 'region_id' is used to specify the region, then we will let the
|
||||
manager/driver take care of this. If, however, 'region' is used,
|
||||
then for backward compatibility, we will auto-create the region.
|
||||
|
||||
"""
|
||||
if (endpoint.get('region_id') is None and
|
||||
endpoint.get('region') is not None):
|
||||
# To maintain backward compatibility with clients that are
|
||||
# using the v3 API in the same way as they used the v2 API,
|
||||
# create the endpoint region, if that region does not exist
|
||||
# in keystone.
|
||||
endpoint['region_id'] = endpoint.pop('region')
|
||||
try:
|
||||
PROVIDERS.catalog_api.get_region(endpoint['region_id'])
|
||||
except exception.RegionNotFound:
|
||||
region = dict(id=endpoint['region_id'])
|
||||
PROVIDERS.catalog_api.create_region(
|
||||
region, initiator=ks_flask.build_audit_initiator())
|
||||
return endpoint
|
||||
|
||||
def _get_endpoint(self, endpoint_id):
|
||||
ENFORCER.enforce_call(action='identity:get_endpoint')
|
||||
return self.wrap_member(_filter_endpoint(
|
||||
PROVIDERS.catalog_api.get_endpoint(endpoint_id)))
|
||||
|
||||
def _list_endpoints(self):
|
||||
filters = ['interface', 'service_id', 'region_id']
|
||||
ENFORCER.enforce_call(action='identity:list_endpoints',
|
||||
filters=filters)
|
||||
hints = self.build_driver_hints(filters)
|
||||
refs = PROVIDERS.catalog_api.list_endpoints(hints=hints)
|
||||
return self.wrap_collection([_filter_endpoint(r) for r in refs],
|
||||
hints=hints)
|
||||
|
||||
def get(self, endpoint_id=None):
|
||||
if endpoint_id is not None:
|
||||
return self._get_endpoint(endpoint_id)
|
||||
return self._list_endpoints()
|
||||
|
||||
def post(self):
|
||||
ENFORCER.enforce_call(action='identity:create_endpoint')
|
||||
endpoint = self.request_body_json.get('endpoint')
|
||||
validation.lazy_validate(schema.endpoint_create, endpoint)
|
||||
utils.check_endpoint_url(endpoint['url'])
|
||||
endpoint = self._assign_unique_id(self._normalize_dict(endpoint))
|
||||
endpoint = self._validate_endpoint_region(endpoint)
|
||||
ref = PROVIDERS.catalog_api.create_endpoint(
|
||||
endpoint['id'], endpoint, initiator=self.audit_initiator)
|
||||
return self.wrap_member(_filter_endpoint(ref)), http_client.CREATED
|
||||
|
||||
def patch(self, endpoint_id):
|
||||
ENFORCER.enforce_call(action='identity:update_endpoint')
|
||||
endpoint = self.request_body_json.get('endpoint')
|
||||
validation.lazy_validate(schema.endpoint_update, endpoint)
|
||||
self._require_matching_id(endpoint)
|
||||
endpoint = self._validate_endpoint_region(endpoint)
|
||||
ref = PROVIDERS.catalog_api.update_endpoint(
|
||||
endpoint_id, endpoint, initiator=self.audit_initiator)
|
||||
return self.wrap_member(_filter_endpoint(ref))
|
||||
|
||||
def delete(self, endpoint_id):
|
||||
ENFORCER.enforce_call(action='identity:delete_endpoint')
|
||||
PROVIDERS.catalog_api.delete_endpoint(endpoint_id,
|
||||
initiator=self.audit_initiator)
|
||||
return None, http_client.NO_CONTENT
|
||||
|
||||
|
||||
class EndpointPolicyEndpointResource(flask_restful.Resource):
|
||||
def get(self, endpoint_id):
|
||||
ENFORCER.enforce_call(action='identity:get_policy_for_endpoint')
|
||||
PROVIDERS.catalog_api.get_endpoint(endpoint_id)
|
||||
ref = PROVIDERS.endpoint_policy_api.get_policy_for_endpoint(
|
||||
endpoint_id)
|
||||
return ks_flask.ResourceBase.wrap_member(
|
||||
ref, collection_name='endpoints', member_name='policy')
|
||||
|
||||
|
||||
class EndpointAPI(ks_flask.APIBase):
|
||||
_name = 'endpoints'
|
||||
_import_name = __name__
|
||||
resources = [EndpointResource]
|
||||
resource_mapping = [
|
||||
ks_flask.construct_resource_map(
|
||||
resource=EndpointPolicyEndpointResource,
|
||||
url='/endpoints/<string:endpoint_id>/OS-ENDPOINT-POLICY/policy',
|
||||
resource_kwargs={},
|
||||
rel='endpoint_policy',
|
||||
resource_relation_func=_build_resource_relation,
|
||||
path_vars={'endpoint_id': json_home.Parameters.ENDPOINT_ID})
|
||||
]
|
||||
|
||||
|
||||
APIs = (EndpointAPI,)
|
|
@ -16,6 +16,7 @@ import flask_restful
|
|||
import functools
|
||||
from six.moves import http_client
|
||||
|
||||
from keystone.api import endpoints as _endpoints_api
|
||||
from keystone.catalog import schema
|
||||
from keystone.common import json_home
|
||||
from keystone.common import provider_api
|
||||
|
@ -41,12 +42,9 @@ _ENDPOINT_GROUP_PARAMETER_RELATION = _build_parameter_relation(
|
|||
parameter_name='endpoint_group_id')
|
||||
|
||||
|
||||
# TODO(morgan): move this to a common location once catalog endpoint API is
|
||||
# converted to flask
|
||||
def _filter_endpoint(ref):
|
||||
ref.pop('legacy_endpoint_id', None)
|
||||
ref['region'] = ref['region_id']
|
||||
return ref
|
||||
# NOTE(morgan): This is shared from keystone.api.endpoint, this is a special
|
||||
# case where cross-api code is used. This pattern should not be replicated.
|
||||
_filter_endpoint = _endpoints_api._filter_endpoint
|
||||
|
||||
|
||||
class EndpointGroupsResource(ks_flask.ResourceBase):
|
||||
|
|
|
@ -12,5 +12,4 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone.catalog import controllers # noqa
|
||||
from keystone.catalog.core import * # noqa
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from keystone.catalog import schema
|
||||
from keystone.common import controller
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import utils
|
||||
from keystone.common import validation
|
||||
from keystone import exception
|
||||
|
||||
|
||||
INTERFACES = ['public', 'internal', 'admin']
|
||||
PROVIDERS = provider_api.ProviderAPIs
|
||||
|
||||
|
||||
class EndpointV3(controller.V3Controller):
|
||||
collection_name = 'endpoints'
|
||||
member_name = 'endpoint'
|
||||
|
||||
def __init__(self):
|
||||
super(EndpointV3, self).__init__()
|
||||
self.get_member_from_driver = PROVIDERS.catalog_api.get_endpoint
|
||||
|
||||
@classmethod
|
||||
def filter_endpoint(cls, ref):
|
||||
if 'legacy_endpoint_id' in ref:
|
||||
ref.pop('legacy_endpoint_id')
|
||||
ref['region'] = ref['region_id']
|
||||
return ref
|
||||
|
||||
@classmethod
|
||||
def wrap_member(cls, context, ref):
|
||||
ref = cls.filter_endpoint(ref)
|
||||
return super(EndpointV3, cls).wrap_member(context, ref)
|
||||
|
||||
def _validate_endpoint_region(self, endpoint, request):
|
||||
"""Ensure the region for the endpoint exists.
|
||||
|
||||
If 'region_id' is used to specify the region, then we will let the
|
||||
manager/driver take care of this. If, however, 'region' is used,
|
||||
then for backward compatibility, we will auto-create the region.
|
||||
|
||||
"""
|
||||
if (endpoint.get('region_id') is None and
|
||||
endpoint.get('region') is not None):
|
||||
# To maintain backward compatibility with clients that are
|
||||
# using the v3 API in the same way as they used the v2 API,
|
||||
# create the endpoint region, if that region does not exist
|
||||
# in keystone.
|
||||
endpoint['region_id'] = endpoint.pop('region')
|
||||
try:
|
||||
PROVIDERS.catalog_api.get_region(endpoint['region_id'])
|
||||
except exception.RegionNotFound:
|
||||
region = dict(id=endpoint['region_id'])
|
||||
PROVIDERS.catalog_api.create_region(
|
||||
region, initiator=request.audit_initiator
|
||||
)
|
||||
|
||||
return endpoint
|
||||
|
||||
@controller.protected()
|
||||
def create_endpoint(self, request, endpoint):
|
||||
validation.lazy_validate(schema.endpoint_create, endpoint)
|
||||
utils.check_endpoint_url(endpoint['url'])
|
||||
ref = self._assign_unique_id(self._normalize_dict(endpoint))
|
||||
ref = self._validate_endpoint_region(ref, request)
|
||||
ref = PROVIDERS.catalog_api.create_endpoint(
|
||||
ref['id'], ref, initiator=request.audit_initiator
|
||||
)
|
||||
return EndpointV3.wrap_member(request.context_dict, ref)
|
||||
|
||||
@controller.filterprotected('interface', 'service_id', 'region_id')
|
||||
def list_endpoints(self, request, filters):
|
||||
hints = EndpointV3.build_driver_hints(request, filters)
|
||||
refs = PROVIDERS.catalog_api.list_endpoints(hints=hints)
|
||||
return EndpointV3.wrap_collection(request.context_dict,
|
||||
refs,
|
||||
hints=hints)
|
||||
|
||||
@controller.protected()
|
||||
def get_endpoint(self, request, endpoint_id):
|
||||
ref = PROVIDERS.catalog_api.get_endpoint(endpoint_id)
|
||||
return EndpointV3.wrap_member(request.context_dict, ref)
|
||||
|
||||
@controller.protected()
|
||||
def update_endpoint(self, request, endpoint_id, endpoint):
|
||||
validation.lazy_validate(schema.endpoint_update, endpoint)
|
||||
self._require_matching_id(endpoint_id, endpoint)
|
||||
|
||||
endpoint = self._validate_endpoint_region(endpoint.copy(),
|
||||
request)
|
||||
|
||||
ref = PROVIDERS.catalog_api.update_endpoint(
|
||||
endpoint_id, endpoint, initiator=request.audit_initiator
|
||||
)
|
||||
return EndpointV3.wrap_member(request.context_dict, ref)
|
||||
|
||||
@controller.protected()
|
||||
def delete_endpoint(self, request, endpoint_id):
|
||||
return PROVIDERS.catalog_api.delete_endpoint(
|
||||
endpoint_id, initiator=request.audit_initiator
|
||||
)
|
|
@ -1,28 +0,0 @@
|
|||
# Copyright 2012 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.catalog import controllers
|
||||
from keystone.common import router
|
||||
from keystone.common import wsgi
|
||||
|
||||
|
||||
class Routers(wsgi.RoutersBase):
|
||||
"""API for the keystone catalog."""
|
||||
|
||||
_path_prefixes = ('endpoints',)
|
||||
|
||||
def append_v3_routers(self, mapper, routers):
|
||||
routers.append(router.Router(controllers.EndpointV3(),
|
||||
'endpoints', 'endpoint',
|
||||
resource_descriptions=self.v3_resources))
|
|
@ -135,19 +135,6 @@ class EndpointPolicyV3Controller(controller.V3Controller):
|
|||
PROVIDERS.endpoint_policy_api.delete_policy_association(
|
||||
policy_id, service_id=service_id, region_id=region_id)
|
||||
|
||||
@controller.protected()
|
||||
def get_policy_for_endpoint(self, request, endpoint_id):
|
||||
"""Get the effective policy for an endpoint."""
|
||||
PROVIDERS.catalog_api.get_endpoint(endpoint_id)
|
||||
ref = PROVIDERS.endpoint_policy_api.get_policy_for_endpoint(
|
||||
endpoint_id
|
||||
)
|
||||
# NOTE(henry-nash): since the collection and member for this class is
|
||||
# set to endpoints, we have to handle wrapping this policy entity
|
||||
# ourselves.
|
||||
self._add_self_referential_link(request.context_dict, ref)
|
||||
return {'policy': ref}
|
||||
|
||||
# NOTE(henry-nash): As in the catalog controller, we must ensure that the
|
||||
# legacy_endpoint_id does not escape.
|
||||
|
||||
|
|
|
@ -33,12 +33,6 @@ class Routers(wsgi.RoutersBase):
|
|||
def append_v3_routers(self, mapper, routers):
|
||||
endpoint_policy_controller = controllers.EndpointPolicyV3Controller()
|
||||
|
||||
self._add_resource(
|
||||
mapper, endpoint_policy_controller,
|
||||
path='/endpoints/{endpoint_id}' + self.PATH_PREFIX + '/policy',
|
||||
get_head_action='get_policy_for_endpoint',
|
||||
rel=build_resource_relation(resource_name='endpoint_policy'),
|
||||
path_vars={'endpoint_id': json_home.Parameters.ENDPOINT_ID})
|
||||
self._add_resource(
|
||||
mapper, endpoint_policy_controller,
|
||||
path='/policies/{policy_id}' + self.PATH_PREFIX + '/endpoints',
|
||||
|
|
|
@ -27,7 +27,6 @@ import keystone.api
|
|||
from keystone.application_credential import routers as app_cred_routers
|
||||
from keystone.assignment import routers as assignment_routers
|
||||
from keystone.auth import routers as auth_routers
|
||||
from keystone.catalog import routers as catalog_routers
|
||||
from keystone.common import wsgi as keystone_wsgi
|
||||
from keystone.contrib.ec2 import routers as ec2_routers
|
||||
from keystone.contrib.s3 import routers as s3_routers
|
||||
|
@ -42,6 +41,7 @@ from keystone.resource import routers as resource_routers
|
|||
# support is removed.
|
||||
_MOVED_API_PREFIXES = frozenset(
|
||||
['credentials',
|
||||
'endpoints',
|
||||
'OS-OAUTH1',
|
||||
'OS-EP-FILTER',
|
||||
'OS-REVOKE',
|
||||
|
@ -59,7 +59,6 @@ LOG = log.getLogger(__name__)
|
|||
|
||||
ALL_API_ROUTERS = [auth_routers,
|
||||
assignment_routers,
|
||||
catalog_routers,
|
||||
identity_routers,
|
||||
app_cred_routers,
|
||||
policy_routers,
|
||||
|
|
Loading…
Reference in New Issue