Add include_limits filter

Add include_limits filter for get project to support
fetching project hierarchy limits.

This filter should be used together with "subtree_as_list"
or "parents_as_list" filter

bp: strict-two-level-model
Change-Id: Ib602887c92b89be0ffec1394a3076f5dd5671511
This commit is contained in:
wangxiyuan 2018-06-29 10:54:19 +08:00
parent 059fa7eb83
commit 8038c70abf
4 changed files with 95 additions and 4 deletions

View File

@ -213,6 +213,8 @@ class ProjectV3(controller.V3Controller):
self.query_filter_is_true(params['subtree_as_list']))
subtree_as_ids = 'subtree_as_ids' in params and (
self.query_filter_is_true(params['subtree_as_ids']))
include_limits = 'include_limits' in params and (
self.query_filter_is_true(params['include_limits']))
# parents_as_list and parents_as_ids are mutually exclusive
if parents_as_list and parents_as_ids:
@ -228,7 +230,7 @@ class ProjectV3(controller.V3Controller):
if parents_as_list:
parents = PROVIDERS.resource_api.list_project_parents(
ref['id'], request.context.user_id)
ref['id'], request.context.user_id, include_limits)
ref['parents'] = [ProjectV3.wrap_member(context, p)
for p in parents]
elif parents_as_ids:
@ -238,7 +240,7 @@ class ProjectV3(controller.V3Controller):
if subtree_as_list:
subtree = PROVIDERS.resource_api.list_projects_in_subtree(
ref['id'], request.context.user_id)
ref['id'], request.context.user_id, include_limits)
ref['subtree'] = [ProjectV3.wrap_member(context, p)
for p in subtree]
elif subtree_as_ids:

View File

@ -526,13 +526,28 @@ class Manager(manager.Manager):
# Check if project_id exists
self.get_project(project_id)
def list_project_parents(self, project_id, user_id=None):
def _include_limits(self, projects):
"""Modify a list of projects to include limit information.
:param projects: a list of project references including an `id`
:type projects: list of dictionaries
"""
for project in projects:
hints = driver_hints.Hints()
hints.add_filter('project_id', project['id'])
limits = PROVIDERS.unified_limit_api.list_limits(hints)
project['limits'] = limits
def list_project_parents(self, project_id, user_id=None,
include_limits=False):
self._assert_valid_project_id(project_id)
parents = self.driver.list_project_parents(project_id)
# If a user_id was provided, the returned list should be filtered
# against the projects this user has access to.
if user_id:
parents = self._filter_projects_list(parents, user_id)
if include_limits:
self._include_limits(parents)
return parents
def _build_parents_as_ids_dict(self, project, parents_by_id):
@ -578,13 +593,16 @@ class Manager(manager.Manager):
project, {proj['id']: proj for proj in parents_list})
return parents_as_ids
def list_projects_in_subtree(self, project_id, user_id=None):
def list_projects_in_subtree(self, project_id, user_id=None,
include_limits=False):
self._assert_valid_project_id(project_id)
subtree = self.driver.list_projects_in_subtree(project_id)
# If a user_id was provided, the returned list should be filtered
# against the projects this user has access to.
if user_id:
subtree = self._filter_projects_list(subtree, user_id)
if include_limits:
self._include_limits(subtree)
return subtree
def _build_subtree_as_ids_dict(self, project_id, subtree_by_parent):

View File

@ -1090,6 +1090,70 @@ class ResourceTestCase(test_v3.RestfulTestCase,
'project_id': projects[1]['project']['id']},
expected_status=http_client.BAD_REQUEST)
def test_get_project_with_include_limits(self):
parent, project, subproject = self._create_projects_hierarchy(2)
# Assign a role for the user on all the created projects
for proj in (parent, project, subproject):
self.put(self.build_role_assignment_link(
role_id=self.role_id, user_id=self.user_id,
project_id=proj['project']['id']))
# create a registered limit and three limits for each project.
reg_limit = unit.new_registered_limit_ref(service_id=self.service_id,
region_id=self.region_id,
resource_name='volume')
self.post(
'/registered_limits',
body={'registered_limits': [reg_limit]},
expected_status=http_client.CREATED)
limit1 = unit.new_limit_ref(project_id=parent['project']['id'],
service_id=self.service_id,
region_id=self.region_id,
resource_name='volume')
limit2 = unit.new_limit_ref(project_id=project['project']['id'],
service_id=self.service_id,
region_id=self.region_id,
resource_name='volume')
limit3 = unit.new_limit_ref(project_id=subproject['project']['id'],
service_id=self.service_id,
region_id=self.region_id,
resource_name='volume')
self.post(
'/limits',
body={'limits': [limit1, limit2, limit3]},
expected_status=http_client.CREATED)
# "include_limits" should work together with "parents_as_list" or
# "subtree_as_list". Only using "include_limits" really does nothing.
r = self.get('/projects/%(project_id)s?include_limits' %
{'project_id': subproject['project']['id']})
self.assertIsNone(r.result['project'].get('parents'))
self.assertIsNone(r.result['project'].get('subtree'))
self.assertIsNone(r.result['project'].get('limits'))
# using "include_limits" with "parents_as_list"
r = self.get('/projects/%(project_id)s?include_limits&parents_as_list'
% {'project_id': subproject['project']['id']})
self.assertEqual(2, len(r.result['project']['parents']))
for parent in r.result['project']['parents']:
self.assertEqual(1, len(parent['project']['limits']))
self.assertEqual(parent['project']['id'],
parent['project']['limits'][0]['project_id'])
self.assertEqual(10,
parent['project']['limits'][0]['resource_limit'])
# using "include_limits" with "subtree_as_list"
r = self.get('/projects/%(project_id)s?include_limits&subtree_as_list'
% {'project_id': parent['project']['id']})
self.assertEqual(2, len(r.result['project']['subtree']))
for child in r.result['project']['subtree']:
self.assertEqual(1, len(child['project']['limits']))
self.assertEqual(child['project']['id'],
child['project']['limits'][0]['project_id'])
self.assertEqual(10,
child['project']['limits'][0]['resource_limit'])
def test_list_project_is_domain_filter(self):
"""Call ``GET /projects?is_domain=True/False``."""
# Get the initial number of projects, both acting as a domain as well

View File

@ -19,3 +19,10 @@ features:
The `project_id` filter is added for listing limits. This filter is used
for system-scoped request only to fetch the specified project limits. Non
system-scoped request will get empty response body instead.
- >
[`blueprint strict-two-level-model <https://blueprints.launchpad.net/keystone/+spec/strict-two-level-model>`_]
The `include_limits` filter is added to `GET /v3/projects/{project_id}` API.
This filter should be used together with `parents_as_list` or
`subtree_as_list` filter to add parent/sub project's limit information the
response body.