Support accessing all clusters/templates across projects

As an admin user, I'd like to access all clusters or templates across
all projects for operation purpose. Similar function is supported by
most of the other services, like Nova, Neutron, Cinder, Heat, etc.

Related-Bug: #1740982

Change-Id: Icaba09de79a3452286fb60fee80a53430317cba0
This commit is contained in:
Feilong Wang 2018-01-04 10:33:57 +13:00 committed by Spyros Trigazis (strigazi)
parent e644a20e81
commit 198fce72e5
7 changed files with 193 additions and 0 deletions

View File

@ -147,6 +147,12 @@ class Cluster(base.APIBase):
container_version = wsme.wsattr(wtypes.text, readonly=True)
"""Version of the container software. Example: docker version."""
project_id = wsme.wsattr(wtypes.text, readonly=True)
"""Project id of the cluster belongs to"""
user_id = wsme.wsattr(wtypes.text, readonly=True)
"""User id of the cluster belongs to"""
node_addresses = wsme.wsattr([wtypes.text], readonly=True)
"""IP addresses of cluster slave nodes"""
@ -272,6 +278,23 @@ class ClustersController(base.Controller):
sort_key, sort_dir, expand=False,
resource_url=None):
context = pecan.request.context
if context.is_admin:
if expand:
policy.enforce(context, "cluster:detail_all_projects",
action="cluster:detail_all_projects")
else:
policy.enforce(context, "cluster:get_all_all_projects",
action="cluster:get_all_all_projects")
# TODO(flwang): Instead of asking an extra 'all_project's
# parameter, currently the design is allowing admin user to list
# all clusters from all projects. But the all_tenants is one of
# the condition to do project filter in DB API. And it's also used
# by periodic tasks. So the could be removed in the future and
# a new parameter 'project_id' would be added so that admin user
# can list clusters for a particular project.
context.all_tenants = True
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
@ -362,6 +385,18 @@ class ClustersController(base.Controller):
:param cluster_ident: UUID or logical name of the Cluster.
"""
context = pecan.request.context
if context.is_admin:
policy.enforce(context, "cluster:get_one_all_projects",
action="cluster:get_one_all_projects")
# TODO(flwang): Instead of asking an extra 'all_project's
# parameter, currently the design is allowing admin user to list
# all clusters from all projects. But the all_tenants is one of
# the condition to do project filter in DB API. And it's also used
# by periodic tasks. So the could be removed in the future and
# a new parameter 'project_id' would be added so that admin user
# can list clusters for a particular project.
context.all_tenants = True
cluster = api_utils.get_resource('Cluster', cluster_ident)
policy.enforce(context, 'cluster:get', cluster.as_dict(),
action='cluster:get')

View File

@ -139,6 +139,12 @@ class ClusterTemplate(base.APIBase):
floating_ip_enabled = wsme.wsattr(types.boolean, default=True)
"""Indicates whether created clusters should have a floating ip or not."""
project_id = wsme.wsattr(wtypes.text, readonly=True)
"""Project id of the cluster belongs to"""
user_id = wsme.wsattr(wtypes.text, readonly=True)
"""User id of the cluster belongs to"""
def __init__(self, **kwargs):
self.fields = []
for field in objects.ClusterTemplate.fields:
@ -249,6 +255,23 @@ class ClusterTemplatesController(base.Controller):
sort_key, sort_dir,
resource_url=None):
context = pecan.request.context
if context.is_admin:
if resource_url == '/'.join(['clustertemplates', 'detail']):
policy.enforce(context, "clustertemplate:detail_all_projects",
action="clustertemplate:detail_all_projects")
else:
policy.enforce(context, "clustertemplate:get_all_all_projects",
action="clustertemplate:get_all_all_projects")
# TODO(flwang): Instead of asking an extra 'all_project's
# parameter, currently the design is allowing admin user to list
# all clusters from all projects. But the all_tenants is one of
# the condition to do project filter in DB API. And it's also used
# by periodic tasks. So the could be removed in the future and
# a new parameter 'project_id' would be added so that admin user
# can list clusters for a particular project.
context.all_tenants = True
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
@ -317,8 +340,22 @@ class ClusterTemplatesController(base.Controller):
ClusterTemplate.
"""
context = pecan.request.context
if context.is_admin:
policy.enforce(context, "clustertemplate:get_one_all_projects",
action="clustertemplate:get_one_all_projects")
# TODO(flwang): Instead of asking an extra 'all_project's
# parameter, currently the design is allowing admin user to list
# all clusters from all projects. But the all_tenants is one of
# the condition to do project filter in DB API. And it's also used
# by periodic tasks. So the could be removed in the future and
# a new parameter 'project_id' would be added so that admin user
# can list clusters for a particular project.
context.all_tenants = True
cluster_template = api_utils.get_resource('ClusterTemplate',
cluster_template_ident)
if not cluster_template.public:
policy.enforce(context, 'clustertemplate:get',
cluster_template.as_dict(),

View File

@ -51,6 +51,17 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER % 'detail_all_projects',
check_str=base.RULE_ADMIN_API,
description='Retrieve a list of clusters with detail across projects.',
operations=[
{
'path': '/v1/clusters',
'method': 'GET'
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER % 'get',
check_str=base.RULE_DENY_CLUSTER_USER,
@ -62,6 +73,18 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER % 'get_one_all_projects',
check_str=base.RULE_ADMIN_API,
description=('Retrieve information about the given cluster across '
'projects.'),
operations=[
{
'path': '/v1/clusters/{cluster_ident}',
'method': 'GET'
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER % 'get_all',
check_str=base.RULE_DENY_CLUSTER_USER,
@ -73,6 +96,17 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER % 'get_all_all_projects',
check_str=base.RULE_ADMIN_API,
description='Retrieve a list of all clusters across projects.',
operations=[
{
'path': '/v1/clusters/',
'method': 'GET'
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER % 'update',
check_str=base.RULE_DENY_CLUSTER_USER,

View File

@ -40,6 +40,18 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER_TEMPLATE % 'detail_all_projects',
check_str=base.RULE_ADMIN_API,
description=('Retrieve a list of cluster templates with detail across '
'projects.'),
operations=[
{
'path': '/v1/clustertemplates',
'method': 'GET'
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER_TEMPLATE % 'detail',
check_str=base.RULE_DENY_CLUSTER_USER,
@ -62,6 +74,18 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER_TEMPLATE % 'get_one_all_projects',
check_str=base.RULE_ADMIN_API,
description=('Retrieve information about the given cluster template '
'across project.'),
operations=[
{
'path': '/v1/clustertemplate/{clustertemplate_ident}',
'method': 'GET'
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER_TEMPLATE % 'get_all',
check_str=base.RULE_DENY_CLUSTER_USER,
@ -73,6 +97,17 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER_TEMPLATE % 'get_all_all_projects',
check_str=base.RULE_ADMIN_API,
description='Retrieve a list of cluster templates across projects.',
operations=[
{
'path': '/v1/clustertemplates',
'method': 'GET'
}
]
),
policy.DocumentedRuleDefault(
name=CLUSTER_TEMPLATE % 'update',
check_str=base.RULE_DENY_CLUSTER_USER,

View File

@ -144,6 +144,17 @@ class TestListCluster(api_base.FunctionalTest):
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['errors'])
@mock.patch("magnum.common.policy.enforce")
@mock.patch("magnum.common.context.make_context")
def test_get_one_by_uuid_admin(self, mock_context, mock_policy):
temp_uuid = uuidutils.generate_uuid()
obj_utils.create_test_cluster(self.context, uuid=temp_uuid,
project_id=temp_uuid)
self.context.is_admin = True
response = self.get_json(
'/clusters/%s' % temp_uuid)
self.assertEqual(temp_uuid, response['uuid'])
def test_get_one_by_name_multiple_cluster(self):
obj_utils.create_test_cluster(self.context, name='test_cluster',
uuid=uuidutils.generate_uuid())
@ -169,6 +180,19 @@ class TestListCluster(api_base.FunctionalTest):
self.assertEqual(cluster_list[-1].uuid,
response['clusters'][0]['uuid'])
@mock.patch("magnum.common.policy.enforce")
@mock.patch("magnum.common.context.make_context")
def test_get_all_with_all_projects(self, mock_context, mock_policy):
for id_ in range(4):
temp_uuid = uuidutils.generate_uuid()
obj_utils.create_test_cluster(self.context, id=id_,
uuid=temp_uuid,
project_id=id_)
self.context.is_admin = True
response = self.get_json('/clusters')
self.assertEqual(4, len(response['clusters']))
def test_detail(self):
cluster = obj_utils.create_test_cluster(self.context)
response = self.get_json('/clusters/detail')

View File

@ -114,6 +114,17 @@ class TestListClusterTemplate(api_base.FunctionalTest):
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['errors'])
@mock.patch("magnum.common.policy.enforce")
@mock.patch("magnum.common.context.make_context")
def test_get_one_by_uuid_admin(self, mock_context, mock_policy):
temp_uuid = uuidutils.generate_uuid()
obj_utils.create_test_cluster_template(self.context, uuid=temp_uuid,
project_id=temp_uuid)
self.context.is_admin = True
response = self.get_json(
'/clustertemplates/%s' % temp_uuid)
self.assertEqual(temp_uuid, response['uuid'])
def test_get_one_by_name_multiple_cluster_template(self):
obj_utils.create_test_cluster_template(
self.context, name='test_clustertemplate',
@ -142,6 +153,18 @@ class TestListClusterTemplate(api_base.FunctionalTest):
self.assertEqual(bm_list[-1].uuid,
response['clustertemplates'][0]['uuid'])
@mock.patch("magnum.common.policy.enforce")
@mock.patch("magnum.common.context.make_context")
def test_get_all_with_all_projects(self, mock_context, mock_policy):
for id_ in range(4):
obj_utils.create_test_cluster_template(
self.context, id=id_, project_id=id_,
uuid=uuidutils.generate_uuid())
self.context.is_admin = True
response = self.get_json('/clustertemplates')
self.assertEqual(4, len(response['clustertemplates']))
def test_detail(self):
cluster_template = obj_utils.create_test_cluster_template(self.context)
response = self.get_json('/clustertemplates/detail')

View File

@ -0,0 +1,5 @@
---
features:
- |
Now admin user can access all clusters across projects.