diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py index 565da19221..894b02deef 100644 --- a/openstack_dashboard/api/cinder.py +++ b/openstack_dashboard/api/cinder.py @@ -175,7 +175,8 @@ class VolumePool(base.APIResourceWrapper): class Group(base.APIResourceWrapper): _attrs = ['id', 'status', 'availability_zone', 'created_at', 'name', 'description', 'group_type', 'volume_types', - 'group_snapshot_id', 'source_group_id', 'replication_status'] + 'group_snapshot_id', 'source_group_id', 'replication_status', + 'project_id'] class GroupSnapshot(base.APIResourceWrapper): diff --git a/openstack_dashboard/api/microversions.py b/openstack_dashboard/api/microversions.py index e28bbe03dd..071a1b2b12 100644 --- a/openstack_dashboard/api/microversions.py +++ b/openstack_dashboard/api/microversions.py @@ -37,7 +37,7 @@ MICROVERSION_FEATURES = { "auto_allocated_network": ["2.37", "2.42"], }, "cinder": { - "groups": ["3.27", "3.43", "3.48"], + "groups": ["3.27", "3.43", "3.48", "3.58"], "consistency_groups": ["2.0", "3.10"], "message_list": ["3.5", "3.29"], "limits_project_id_query": ["3.43", "3.50", "3.55"], diff --git a/openstack_dashboard/dashboards/admin/volume_groups/tables.py b/openstack_dashboard/dashboards/admin/volume_groups/tables.py index bab271b85a..075ba7beb9 100644 --- a/openstack_dashboard/dashboards/admin/volume_groups/tables.py +++ b/openstack_dashboard/dashboards/admin/volume_groups/tables.py @@ -14,7 +14,11 @@ from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions from horizon import tables + +from openstack_dashboard.api import cinder +from openstack_dashboard.api import keystone from openstack_dashboard.dashboards.project.volume_groups \ import tables as project_tables @@ -27,15 +31,31 @@ class RemoveAllVolumes(project_tables.RemoveAllVolumes): url = "horizon:admin:volume_groups:remove_volumes" +class UpdateRow(tables.Row): + ajax = True + + def get_data(self, request, group_id): + groups = cinder.group_list_with_vol_type_names(request, group_id) + tenant_id = getattr(groups, 'project_id') + try: + tenant = keystone.tenant_get(request, tenant_id) + groups.tenant_name = getattr(tenant, "name") + except Exception: + msg = _('Unable to retrieve volume group project information.') + exceptions.handle(request, msg) + + return groups + + class ManageVolumes(project_tables.ManageVolumes): url = "horizon:admin:volume_groups:manage" class GroupsTable(project_tables.GroupsTable): - # TODO(vishalmanchanda): Add Project Info.column in table name = tables.WrappingColumn("name_or_id", verbose_name=_("Name"), link="horizon:admin:volume_groups:detail") + project = tables.Column("tenant_name", verbose_name=_("Project")) class Meta(object): name = "volume_groups" @@ -48,5 +68,7 @@ class GroupsTable(project_tables.GroupsTable): RemoveAllVolumes, DeleteGroup, ) - row_class = project_tables.UpdateRow + row_class = UpdateRow status_columns = ("status",) + columns = ('project', 'name', 'description', 'status', + 'availability_zone', 'volume_type', 'has_snapshots',) diff --git a/openstack_dashboard/dashboards/admin/volume_groups/templates/volume_groups/_detail_overview.html b/openstack_dashboard/dashboards/admin/volume_groups/templates/volume_groups/_detail_overview.html index 50d2b886a3..1f27c13c34 100644 --- a/openstack_dashboard/dashboards/admin/volume_groups/templates/volume_groups/_detail_overview.html +++ b/openstack_dashboard/dashboards/admin/volume_groups/templates/volume_groups/_detail_overview.html @@ -3,11 +3,13 @@
{% trans "Name" %}
-
{{ group.name|default:_("-") }}
+
{{ group.name|default:_("-") }}
{% trans "ID" %}
{{ group.id }}
{% trans "Description" %}
{{ group.description|default:_("-") }}
+
{% trans "Project ID" %}
+
{{ group.project_id|default:_("-") }}
{% trans "Status" %}
{{ group.status|capfirst }}
{% trans "Availability Zone" %}
diff --git a/openstack_dashboard/dashboards/admin/volume_groups/tests.py b/openstack_dashboard/dashboards/admin/volume_groups/tests.py index 951b21fb39..b0f9ffe159 100644 --- a/openstack_dashboard/dashboards/admin/volume_groups/tests.py +++ b/openstack_dashboard/dashboards/admin/volume_groups/tests.py @@ -17,7 +17,6 @@ from django.urls import reverse import mock from openstack_dashboard import api -from openstack_dashboard.api import cinder from openstack_dashboard.test import helpers as test @@ -26,13 +25,17 @@ INDEX_TEMPLATE = 'horizon/common/_data_table_view.html' class AdminVolumeGroupTests(test.BaseAdminViewTests): - @test.create_mocks({api.cinder: ['group_list_with_vol_type_names', - 'group_snapshot_list']}) + @test.create_mocks({ + api.keystone: ['tenant_list'], + api.cinder: ['group_list_with_vol_type_names', + 'group_snapshot_list']}) def test_index(self): group = self.cinder_groups.list() vg_snapshot = self.cinder_group_snapshots.list() + tenants = self.tenants.list() self.mock_group_list_with_vol_type_names.return_value = group self.mock_group_snapshot_list.return_value = vg_snapshot + self.mock_tenant_list.return_value = [tenants, False] res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, INDEX_TEMPLATE) @@ -41,12 +44,13 @@ class AdminVolumeGroupTests(test.BaseAdminViewTests): volume_groups = volume_groups_table.data self.assertEqual(len(volume_groups), 1) + self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest()) self.mock_group_list_with_vol_type_names.assert_called_once_with( test.IsHttpRequest(), {'all_tenants': 1}) self.mock_group_snapshot_list.assert_called_once_with( test.IsHttpRequest()) - @test.create_mocks({cinder: ['group_get', 'group_delete']}) + @test.create_mocks({api.cinder: ['group_get', 'group_delete']}) def test_delete_group(self): group = self.cinder_groups.first() @@ -65,7 +69,7 @@ class AdminVolumeGroupTests(test.BaseAdminViewTests): group.id, delete_volumes=False) - @test.create_mocks({cinder: ['group_get', 'group_delete']}) + @test.create_mocks({api.cinder: ['group_get', 'group_delete']}) def test_delete_group_delete_volumes_flag(self): group = self.cinder_consistencygroups.first() formData = {'delete_volumes': True} @@ -85,7 +89,7 @@ class AdminVolumeGroupTests(test.BaseAdminViewTests): group.id, delete_volumes=True) - @test.create_mocks({cinder: ['group_get', 'group_delete']}) + @test.create_mocks({api.cinder: ['group_get', 'group_delete']}) def test_delete_group_exception(self): group = self.cinder_groups.first() formData = {'delete_volumes': False} @@ -111,10 +115,10 @@ class AdminVolumeGroupTests(test.BaseAdminViewTests): def test_update_group_remove_vol(self): self._test_update_group_add_remove_vol(add=False) - @test.create_mocks({cinder: ['volume_list', - 'volume_type_list', - 'group_get', - 'group_update']}) + @test.create_mocks({api.cinder: ['volume_list', + 'volume_type_list', + 'group_get', + 'group_update']}) def _test_update_group_add_remove_vol(self, add=True): group = self.cinder_groups.first() volume_types = self.cinder_volume_types.list() @@ -166,9 +170,9 @@ class AdminVolumeGroupTests(test.BaseAdminViewTests): add_volumes=[], remove_volumes=assigned_volume_ids) - @test.create_mocks({cinder: ['group_get_with_vol_type_names', - 'volume_list', - 'group_snapshot_list']}) + @test.create_mocks({api.cinder: ['group_get_with_vol_type_names', + 'volume_list', + 'group_snapshot_list']}) def test_detail_view(self): group = self.cinder_groups.first() volumes = self.cinder_volumes.list() @@ -193,7 +197,7 @@ class AdminVolumeGroupTests(test.BaseAdminViewTests): self.mock_group_snapshot_list.assert_called_once_with( test.IsHttpRequest(), search_opts=search_opts) - @test.create_mocks({cinder: ['group_get']}) + @test.create_mocks({api.cinder: ['group_get']}) def test_detail_view_with_exception(self): group = self.cinder_groups.first() diff --git a/openstack_dashboard/dashboards/admin/volume_groups/views.py b/openstack_dashboard/dashboards/admin/volume_groups/views.py index d2bfa53709..1cba1ca11e 100644 --- a/openstack_dashboard/dashboards/admin/volume_groups/views.py +++ b/openstack_dashboard/dashboards/admin/volume_groups/views.py @@ -49,8 +49,25 @@ class IndexView(tables.DataTableView): return groups group_snapshots = api.cinder.group_snapshot_list(self.request) snapshot_groups = {gs.group_id for gs in group_snapshots} + + # Gather our tenants to correlate against Group IDs + try: + tenants, has_more = api.keystone.tenant_list(self.request) + except Exception: + tenants = [] + msg = _('Unable to retrieve volume group project information.') + exceptions.handle(self.request, msg) + + tenant_dict = dict((t.id, t) for t in tenants) for g in groups: g.has_snapshots = g.id in snapshot_groups + tenant_id = getattr(g, "project_id", None) + tenant = tenant_dict.get(tenant_id) + + # NOTE: If horizon is using cinder API microversion below '3.58', + # it doesn't include any 'project id' information in group's + # object. + g.tenant_name = getattr(tenant, "name", None) return groups