Merge "Convert limits and registered limits to flask dispatching"

This commit is contained in:
Zuul 2018-08-08 21:56:48 +00:00 committed by Gerrit Code Review
commit b625ccc42c
8 changed files with 233 additions and 219 deletions

View File

@ -12,9 +12,13 @@
from keystone.api import credentials
from keystone.api import discovery
from keystone.api import limits
from keystone.api import os_oauth1
from keystone.api import os_revoke
from keystone.api import registered_limits
from keystone.api import trusts
__all__ = ('discovery', 'credentials', 'os_oauth1', 'os_revoke', 'trusts')
__apis__ = (discovery, credentials, os_oauth1, os_revoke, trusts)
__all__ = ('discovery', 'credentials', 'limits', 'os_oauth1', 'os_revoke',
'registered_limits', 'trusts')
__apis__ = (discovery, credentials, limits, os_oauth1, os_revoke,
registered_limits, trusts)

121
keystone/api/limits.py Normal file
View File

@ -0,0 +1,121 @@
# 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/limits
import flask
import flask_restful
from six.moves import http_client
from keystone.common import json_home
from keystone.common import provider_api
from keystone.common import rbac_enforcer
from keystone.common import validation
from keystone import exception
from keystone.i18n import _
from keystone.limit import schema
from keystone.server import flask as ks_flask
PROVIDERS = provider_api.ProviderAPIs
ENFORCER = rbac_enforcer.RBACEnforcer
class LimitsResource(ks_flask.ResourceBase):
collection_key = 'limits'
member_key = 'limit'
json_home_resource_status = json_home.Status.EXPERIMENTAL
def _list_limits(self):
filters = ['service_id', 'region_id', 'resource_name', 'project_id']
ENFORCER.enforce_call(action='identity:list_limits', filters=filters)
hints = self.build_driver_hints(filters)
project_id_filter = hints.get_exact_filter_by_name('project_id')
if project_id_filter:
if self.oslo_context.system_scope:
refs = PROVIDERS.unified_limit_api.list_limits(hints)
else:
refs = []
else:
project_id = self.oslo_context.project_id
if project_id:
hints.add_filter('project_id', project_id)
refs = PROVIDERS.unified_limit_api.list_limits(hints)
return self.wrap_collection(refs, hints=hints)
def _get_limit(self, limit_id):
ENFORCER.enforce_call(action='identity:get_limit')
ref = PROVIDERS.unified_limit_api.get_limit(limit_id)
if (not self.oslo_context.is_admin and
not ('admin' in self.oslo_context.roles)):
project_id = self.oslo_context.project_id
if project_id and project_id != ref['project_id']:
action = _('The authenticated project should match the '
'project_id')
raise exception.Forbidden(action=action)
return self.wrap_member(ref)
def get(self, limit_id=None):
if limit_id is not None:
return self._get_limit(limit_id)
return self._list_limits()
def post(self):
ENFORCER.enforce_call(action='identity:create_limits')
limits_b = (flask.request.get_json(silent=True, force=True) or {}).get(
'limits', {})
validation.lazy_validate(schema.limit_create, limits_b)
limits = [self._assign_unique_id(self._normalize_dict(limit))
for limit in limits_b]
refs = PROVIDERS.unified_limit_api.create_limits(limits)
refs = self.wrap_collection(refs)
refs.pop('links')
return refs, http_client.CREATED
def patch(self, limit_id):
ENFORCER.enforce_call(action='identity:update_limit')
limit = (flask.request.get_json(silent=True, force=True) or {}).get(
'limit', {})
validation.lazy_validate(schema.limit_update, limit)
self._require_matching_id(limit)
ref = PROVIDERS.unified_limit_api.update_limit(limit_id, limit)
return self.wrap_member(ref)
def delete(self, limit_id):
ENFORCER.enforce_call(action='identity:delete_limit')
return (PROVIDERS.unified_limit_api.delete_limit(limit_id),
http_client.NO_CONTENT)
class LimitModelResource(flask_restful.Resource):
def get(self):
ENFORCER.enforce_call(action='identity:get_limit_model')
model = PROVIDERS.unified_limit_api.get_model()
return {'model': model}
class LimitsAPI(ks_flask.APIBase):
_name = 'limits'
_import_name = __name__
resources = [LimitsResource]
resource_mapping = [
ks_flask.construct_resource_map(
resource=LimitModelResource,
resource_kwargs={},
url='/limits/model',
rel='limit_model',
status=json_home.Status.EXPERIMENTAL
)
]
APIs = (LimitsAPI,)

View File

@ -0,0 +1,91 @@
# 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/registered_limits
import flask
from six.moves import http_client
from keystone.common import json_home
from keystone.common import provider_api
from keystone.common import rbac_enforcer
from keystone.common import validation
from keystone.limit import schema
from keystone.server import flask as ks_flask
PROVIDERS = provider_api.ProviderAPIs
ENFORCER = rbac_enforcer.RBACEnforcer
class RegisteredLimitResource(ks_flask.ResourceBase):
collection_key = 'registered_limits'
member_key = 'registered_limit'
json_home_resource_status = json_home.Status.EXPERIMENTAL
def _get_registered_limit(self, registered_limit_id):
ENFORCER.enforce_call(action='identity:get_registered_limit')
ref = PROVIDERS.unified_limit_api.get_registered_limit(
registered_limit_id)
return self.wrap_member(ref)
def _list_registered_limits(self):
filters = ['service_id', 'region_id', 'resource_name']
ENFORCER.enforce_call(action='identity:list_registered_limits',
filters=filters)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.unified_limit_api.list_registered_limits(hints)
return self.wrap_collection(refs)
def get(self, registered_limit_id=None):
if registered_limit_id is not None:
return self._get_registered_limit(registered_limit_id)
return self._list_registered_limits()
def post(self):
ENFORCER.enforce_call(action='identity:create_registered_limits')
reg_limits = (flask.request.get_json(
silent=True, force=True) or {}).get('registered_limits', {})
validation.lazy_validate(schema.registered_limit_create, reg_limits)
registered_limits = [self._assign_unique_id(self._normalize_dict(r))
for r in reg_limits]
refs = PROVIDERS.unified_limit_api.create_registered_limits(
registered_limits)
refs = self.wrap_collection(refs)
refs.pop('links')
return refs, http_client.CREATED
def patch(self, registered_limit_id):
ENFORCER.enforce_call(action='identity:update_registered_limit')
registered_limit = (flask.request.get_json(
silent=True, force=True) or {}).get('registered_limit', {})
validation.lazy_validate(schema.registered_limit_update,
registered_limit)
self._require_matching_id(registered_limit)
ref = PROVIDERS.unified_limit_api.update_registered_limit(
registered_limit_id, registered_limit)
return self.wrap_member(ref)
def delete(self, registered_limit_id):
ENFORCER.enforce_call(action='identity:delete_registered_limit')
return (PROVIDERS.unified_limit_api.delete_registered_limit(
registered_limit_id), http_client.NO_CONTENT)
class RegisteredLimitsAPI(ks_flask.APIBase):
_name = 'registered_limit'
_import_name = __name__
resources = [RegisteredLimitResource]
resource_mapping = []
APIs = (RegisteredLimitsAPI,)

View File

@ -1,139 +0,0 @@
# Copyright 2018 Huawei
# 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 controller
from keystone.common import provider_api
from keystone.common import validation
from keystone import exception
from keystone.i18n import _
from keystone.limit import schema
PROVIDERS = provider_api.ProviderAPIs
class RegisteredLimitV3(controller.V3Controller):
collection_name = 'registered_limits'
member_name = 'registered_limit'
def __init__(self):
super(RegisteredLimitV3, self).__init__()
self.get_member_from_driver = (
self.unified_limit_api.get_registered_limit
)
@controller.protected()
def create_registered_limits(self, request, registered_limits):
validation.lazy_validate(schema.registered_limit_create,
registered_limits)
registered_limits = [self._assign_unique_id(self._normalize_dict(
registered_limit)) for registered_limit in registered_limits]
refs = PROVIDERS.unified_limit_api.create_registered_limits(
registered_limits)
refs = RegisteredLimitV3.wrap_collection(request.context_dict, refs)
refs.pop("links")
return refs
@controller.protected()
def update_registered_limit(self, request, registered_limit_id,
registered_limit):
validation.lazy_validate(schema.registered_limit_update,
registered_limit)
ref = PROVIDERS.unified_limit_api.update_registered_limit(
registered_limit_id, registered_limit)
return RegisteredLimitV3.wrap_member(request.context_dict, ref)
@controller.filterprotected('service_id', 'region_id', 'resource_name')
def list_registered_limits(self, request, filters):
hints = RegisteredLimitV3.build_driver_hints(request, filters)
refs = PROVIDERS.unified_limit_api.list_registered_limits(hints)
return RegisteredLimitV3.wrap_collection(request.context_dict, refs,
hints=hints)
@controller.protected()
def get_registered_limit(self, request, registered_limit_id):
ref = PROVIDERS.unified_limit_api.get_registered_limit(
registered_limit_id)
return RegisteredLimitV3.wrap_member(request.context_dict, ref)
@controller.protected()
def delete_registered_limit(self, request, registered_limit_id):
return PROVIDERS.unified_limit_api.delete_registered_limit(
registered_limit_id)
class LimitV3(controller.V3Controller):
collection_name = 'limits'
member_name = 'limit'
def __init__(self):
super(LimitV3, self).__init__()
self.get_member_from_driver = self.unified_limit_api.get_limit
@controller.protected()
def get_limit_model(self, request):
model = PROVIDERS.unified_limit_api.get_model()
return {'model': model}
@controller.protected()
def create_limits(self, request, limits):
validation.lazy_validate(schema.limit_create, limits)
limits = [self._assign_unique_id(self._normalize_dict(limit))
for limit in limits]
refs = PROVIDERS.unified_limit_api.create_limits(limits)
refs = LimitV3.wrap_collection(request.context_dict, refs)
refs.pop("links")
return refs
@controller.protected()
def update_limit(self, request, limit_id, limit):
validation.lazy_validate(schema.limit_update, limit)
ref = PROVIDERS.unified_limit_api.update_limit(
limit_id, limit)
return LimitV3.wrap_member(request.context_dict, ref)
@controller.filterprotected('service_id', 'region_id', 'resource_name',
'project_id')
def list_limits(self, request, filters):
hints = LimitV3.build_driver_hints(request, filters)
context = request.context
project_id_filter = hints.get_exact_filter_by_name('project_id')
if project_id_filter:
if context.system_scope:
refs = PROVIDERS.unified_limit_api.list_limits(hints)
else:
refs = []
else:
project_id = context.project_id
if project_id:
hints.add_filter('project_id', project_id)
refs = PROVIDERS.unified_limit_api.list_limits(hints)
return LimitV3.wrap_collection(request.context_dict, refs, hints=hints)
@controller.protected()
def get_limit(self, request, limit_id):
ref = PROVIDERS.unified_limit_api.get_limit(limit_id)
# TODO(wxy): Add system-scope check. If the request is system-scoped,
# it can get any limits.
context = request.context
if not context.is_admin and not ('admin' in context.roles):
project_id = context.project_id
if project_id and project_id != ref['project_id']:
action = _("The authenticated project should match the "
"project_id")
raise exception.Forbidden(action=action)
return LimitV3.wrap_member(request.context_dict, ref)
@controller.protected()
def delete_limit(self, request, limit_id):
return PROVIDERS.unified_limit_api.delete_limit(limit_id)

View File

@ -1,75 +0,0 @@
# Copyright 2018 Huawei
#
# 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 json_home
from keystone.common import wsgi
from keystone.limit import controllers
class Routers(wsgi.RoutersBase):
_path_prefixes = ('registered_limits', 'limits')
def append_v3_routers(self, mapper, routers):
self._add_resource(
mapper, controllers.RegisteredLimitV3(),
path='/registered_limits',
post_action='create_registered_limits',
get_head_action='list_registered_limits',
status=json_home.Status.EXPERIMENTAL,
rel=json_home.build_v3_resource_relation('registered_limits')
)
self._add_resource(
mapper, controllers.RegisteredLimitV3(),
path='/registered_limits/{registered_limit_id}',
get_head_action='get_registered_limit',
patch_action='update_registered_limit',
delete_action='delete_registered_limit',
status=json_home.Status.EXPERIMENTAL,
rel=json_home.build_v3_resource_relation('registered_limits'),
path_vars={
'registered_limit_id':
json_home.Parameters.REGISTERED_LIMIT_ID}
)
self._add_resource(
mapper, controllers.LimitV3(),
path='/limits',
post_action='create_limits',
get_head_action='list_limits',
status=json_home.Status.EXPERIMENTAL,
rel=json_home.build_v3_resource_relation('limits')
)
self._add_resource(
mapper, controllers.LimitV3(),
path='/limits/model',
get_head_action='get_limit_model',
status=json_home.Status.EXPERIMENTAL,
rel=json_home.build_v3_resource_relation('limit_model')
)
self._add_resource(
mapper, controllers.LimitV3(),
path='/limits/{limit_id}',
get_head_action='get_limit',
patch_action='update_limit',
delete_action='delete_limit',
status=json_home.Status.EXPERIMENTAL,
rel=json_home.build_v3_resource_relation('limits'),
path_vars={
'limit_id':
json_home.Parameters.LIMIT_ID}
)

View File

@ -34,7 +34,6 @@ from keystone.contrib.s3 import routers as s3_routers
from keystone.endpoint_policy import routers as endpoint_policy_routers
from keystone.federation import routers as federation_routers
from keystone.identity import routers as identity_routers
from keystone.limit import routers as limit_routers
from keystone.oauth1 import routers as oauth1_routers
from keystone.policy import routers as policy_routers
from keystone.resource import routers as resource_routers
@ -43,7 +42,7 @@ from keystone.token import _simple_cert as simple_cert_ext
# TODO(morgan): _MOVED_API_PREFIXES to be removed when the legacy dispatch
# support is removed.
_MOVED_API_PREFIXES = frozenset(['credentials', 'OS-OAUTH1', 'OS-REVOKE',
'OS-TRUST'])
'OS-TRUST', 'limits', 'registered_limits'])
LOG = log.getLogger(__name__)
@ -52,7 +51,6 @@ ALL_API_ROUTERS = [auth_routers,
catalog_routers,
identity_routers,
app_cred_routers,
limit_routers,
policy_routers,
resource_routers,
federation_routers,

View File

@ -358,6 +358,8 @@ class APIBase(object):
resource_rel_func = getattr(
r, 'json_home_resource_rel_func',
json_home.build_v3_resource_relation)
resource_rel_status = getattr(
r, 'json_home_resource_status', None)
collection_rel = resource_rel_func(resource_name=c_key)
# NOTE(morgan): Add the prefix explicitly for JSON Home documents
# to the collection path.
@ -376,6 +378,12 @@ class APIBase(object):
entity_rel_data = {'href-template': jh_e_path,
'href-vars': {id_str: id_param_rel}}
if resource_rel_status is not None:
json_home.Status.update_resource_data(
rel_data, resource_rel_status)
json_home.Status.update_resource_data(
entity_rel_data, resource_rel_status)
json_home.JsonHomeResources.append_resource(
collection_rel, rel_data)
json_home.JsonHomeResources.append_resource(

View File

@ -609,6 +609,9 @@ V3_JSON_HOME_RESOURCES = {
'group': json_home.build_v3_parameter_relation('config_group'),
'option': json_home.build_v3_parameter_relation('config_option')}},
json_home.build_v3_resource_relation('registered_limits'): {
'hints': {'status': 'experimental'},
'href': '/registered_limits'},
json_home.build_v3_resource_relation('registered_limit'): {
'href-template': '/registered_limits/{registered_limit_id}',
'href-vars': {
'registered_limit_id': json_home.build_v3_parameter_relation(
@ -617,6 +620,9 @@ V3_JSON_HOME_RESOURCES = {
'hints': {'status': 'experimental'}
},
json_home.build_v3_resource_relation('limits'): {
'hints': {'status': 'experimental'},
'href': '/limits'},
json_home.build_v3_resource_relation('limit'): {
'href-template': '/limits/{limit_id}',
'href-vars': {
'limit_id': json_home.build_v3_parameter_relation('limit_id')