Add groups tab in project details view.

Add an extra tab "Groups" in the project details view which
displays the groups which have a role on the project (the
groups members of the project).

The groups are displayed in a table which is an extension
(inheritance) of the group table used in the Groups panel.

An extra column is added to this table, displaying the
roles of each group on project

Change-Id: Ie6338c7e10b67900d5a553d0328b29bfd4a28212
Closes-Bug: #1785263
This commit is contained in:
David Gutman 2018-08-03 17:01:45 +02:00
parent 6e754e5dad
commit 7c80aba5ef
4 changed files with 162 additions and 1 deletions

View File

@ -0,0 +1,33 @@
# 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 django.utils.translation import ugettext_lazy as _
from horizon import forms
from horizon import tables
from openstack_dashboard.dashboards.identity.groups \
import tables as groups_tables
class GroupsTable(groups_tables.GroupsTable):
"""Display groups of the project."""
roles = tables.Column(
lambda obj: ", ".join(getattr(obj, 'roles', [])),
verbose_name=_('Roles'),
form_field=forms.CharField(
widget=forms.Textarea(attrs={'rows': 4}),
required=False))
class Meta(object):
name = "groupstable"
verbose_name = _("Groups")

View File

@ -18,6 +18,8 @@ from horizon import tabs
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.dashboards.identity.projects.groups \
import tables as groups_tables
from openstack_dashboard.dashboards.identity.projects.users \ from openstack_dashboard.dashboards.identity.projects.users \
import tables as users_tables import tables as users_tables
@ -169,6 +171,44 @@ class UsersTab(tabs.TableTab):
return project_users.values() return project_users.values()
class GroupsTab(tabs.TableTab):
"""Display groups member of the project. """
table_classes = (groups_tables.GroupsTable,)
name = _("Groups")
slug = "groups"
template_name = "horizon/common/_detail_table.html"
preload = False
def get_groupstable_data(self):
groups_in_project = []
project = self.tab_group.kwargs['project']
try:
# Get project_groups_roles: {group_id: [role_id_1, role_id_2]}
project_groups_roles = api.keystone.get_project_groups_roles(
self.request,
project=project.id)
# Get global roles and groups
roles = api.keystone.role_list(self.request)
# For keystone.group_list, we do not give the project_id because it
# is ignored when called with admin creds.
groups = api.keystone.group_list(self.request)
groups = {group.id: group for group in groups}
except Exception:
exceptions.handle(self.request,
_("Unable to display the groups of this"
" project."))
else:
# Construct Groups list, adding the role attribute
for group_id in project_groups_roles:
group = groups[group_id]
group.roles = [role.name for role in roles
if role.id in project_groups_roles[group_id]]
groups_in_project.append(group)
return groups_in_project
class ProjectDetailTabs(tabs.DetailTabsGroup): class ProjectDetailTabs(tabs.DetailTabsGroup):
slug = "project_details" slug = "project_details"
tabs = (OverviewTab, UsersTab,) tabs = (OverviewTab, UsersTab, GroupsTab,)

View File

@ -1486,6 +1486,94 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
self.mock_enabled_quotas.assert_called_once_with(test.IsHttpRequest()) self.mock_enabled_quotas.assert_called_once_with(test.IsHttpRequest())
self.mock_role_list.assert_called_once_with(test.IsHttpRequest()) self.mock_role_list.assert_called_once_with(test.IsHttpRequest())
@test.create_mocks({api.keystone: ("tenant_get",
"get_project_groups_roles",
"role_list",
"group_list"),
quotas: ('enabled_quotas',)})
def test_detail_view_groups_tab(self):
project = self.tenants.first()
groups = self.groups.filter(domain_id=project.domain_id)
role_assignments = self.role_assignments.filter(
scope={'project': {'id': project.id}})
# {group_id: [role_id1, role_id2]} as returned by the api
project_groups_roles = self._project_group_roles(role_assignments)
# Prepare mocks
self.mock_tenant_get.return_value = project
self.mock_enabled_quotas.return_value = ('instances',)
self.mock_get_project_groups_roles.return_value = project_groups_roles
self.mock_group_list.return_value = groups
self.mock_role_list.return_value = self.roles.list()
# Get project details view on group tab
url = reverse('horizon:identity:projects:detail', args=[project.id])
detail_view = tabs.ProjectDetailTabs(self.request, group=project)
groups_tab_link = "?%s=%s" % (
detail_view.param_name,
detail_view.get_tab("groups").get_id()
)
url += groups_tab_link
res = self.client.get(url)
# Check the template expected has been used
self.assertTemplateUsed(res,
"horizon/common/_detail_table.html")
# Check the table content
groups_expected = {'1': ["_member_"], }
groups_id_observed = [group.id for group in
res.context["groupstable_table"].data]
# Check the group is displayed
self.assertItemsEqual(groups_id_observed, groups_expected.keys())
# Check the groups roles
for group in res.context["groupstable_table"].data:
self.assertEqual(groups_expected[group.id], group.roles)
self.mock_tenant_get.assert_called_once_with(test.IsHttpRequest(),
self.tenant.id)
self.mock_enabled_quotas.assert_called_once_with(test.IsHttpRequest())
self.mock_role_list.assert_called_once_with(test.IsHttpRequest())
self.mock_group_list.assert_called_once_with(test.IsHttpRequest())
self.mock_get_project_groups_roles.assert_called_once_with(
test.IsHttpRequest(), project=project.id)
@test.create_mocks({api.keystone: ("tenant_get",
"get_project_groups_roles"),
quotas: ('enabled_quotas',)})
def test_detail_view_groups_tab_exception(self):
project = self.tenants.first()
# Prepare mocks
self.mock_tenant_get.return_value = project
self.mock_enabled_quotas.return_value = ('instances',)
self.mock_get_project_groups_roles.side_effect = \
self.exceptions.keystone
# Get project details view on group tab
url = reverse('horizon:identity:projects:detail', args=[project.id])
detail_view = tabs.ProjectDetailTabs(self.request, group=project)
groups_tab_link = "?%s=%s" % (
detail_view.param_name,
detail_view.get_tab("groups").get_id()
)
url += groups_tab_link
res = self.client.get(url)
# Check the projects table is empty
self.assertFalse(res.context["groupstable_table"].data)
# Check one error message is displayed
self.assertMessageCount(res, error=1)
self.mock_tenant_get.assert_called_once_with(test.IsHttpRequest(),
self.tenant.id)
self.mock_enabled_quotas.assert_called_once_with(test.IsHttpRequest())
self.mock_get_project_groups_roles.assert_called_once_with(
test.IsHttpRequest(), project=project.id)
@tag('selenium') @tag('selenium')
class SeleniumTests(test.SeleniumAdminTestCase, test.TestCase): class SeleniumTests(test.SeleniumAdminTestCase, test.TestCase):