diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py index 2032114441..2ec98853aa 100644 --- a/openstack_dashboard/api/cinder.py +++ b/openstack_dashboard/api/cinder.py @@ -187,6 +187,10 @@ class VolumePool(base.APIResourceWrapper): 'storage_protocol', 'extra_specs'] +class Message(base.APIResourceWrapper): + _attrs = ['id', 'event_id', 'created_at', 'resource_type', 'user_message'] + + class Group(base.APIResourceWrapper): _attrs = ['id', 'status', 'availability_zone', 'created_at', 'name', 'description', 'group_type', 'volume_types', diff --git a/openstack_dashboard/dashboards/admin/snapshots/tabs.py b/openstack_dashboard/dashboards/admin/snapshots/tabs.py index 29b35e4f21..bea8398b53 100644 --- a/openstack_dashboard/dashboards/admin/snapshots/tabs.py +++ b/openstack_dashboard/dashboards/admin/snapshots/tabs.py @@ -16,10 +16,12 @@ from django.utils.translation import ugettext_lazy as _ from horizon import tabs from openstack_dashboard.dashboards.project.snapshots \ - import tabs as overview_tab + import tables as snap_messages_tables +from openstack_dashboard.dashboards.project.snapshots \ + import tabs as project_tab -class OverviewTab(overview_tab.OverviewTab): +class OverviewTab(project_tab.OverviewTab): name = _("Overview") slug = "overview" template_name = ("project/snapshots/_detail_overview.html") @@ -28,6 +30,10 @@ class OverviewTab(overview_tab.OverviewTab): return reverse('horizon:admin:snapshots:index') +class SnapshotMessagesTab(project_tab.SnapshotMessagesTab): + table_classes = (snap_messages_tables.SnapshotMessagesTable,) + + class SnapshotDetailsTabs(tabs.TabGroup): slug = "snapshot_details" - tabs = (OverviewTab,) + tabs = (OverviewTab, SnapshotMessagesTab) diff --git a/openstack_dashboard/dashboards/admin/volumes/tabs.py b/openstack_dashboard/dashboards/admin/volumes/tabs.py index 7535dbd615..3571475be2 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tabs.py +++ b/openstack_dashboard/dashboards/admin/volumes/tabs.py @@ -11,6 +11,8 @@ # under the License. from openstack_dashboard.dashboards.admin.snapshots import tables +from openstack_dashboard.dashboards.project.volumes \ + import tables as vol_messages_tables from openstack_dashboard.dashboards.project.volumes import tabs as project_tabs @@ -34,5 +36,9 @@ class SnapshotTab(project_tabs.SnapshotTab): table_classes = (tables.VolumeDetailsSnapshotsTable,) +class VolumeMessagesTab(project_tabs.VolumeMessagesTab): + table_classes = (vol_messages_tables.VolumeMessagesTable,) + + class VolumeDetailTabs(project_tabs.VolumeDetailTabs): - tabs = (OverviewTab, SnapshotTab) + tabs = (OverviewTab, SnapshotTab, VolumeMessagesTab) diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py index f7bf53f6c5..a633c8e2ac 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/tests.py @@ -380,7 +380,7 @@ class VolumeTests(test.BaseAdminViewTests): @test.create_mocks({ api.nova: ['server_get'], api.cinder: ['tenant_absolute_limits', 'volume_get', - 'volume_snapshot_list', 'message_list']}) + 'volume_snapshot_list']}) def test_detail_view_snapshot_tab(self): volume = self.cinder_volumes.first() server = self.servers.first() @@ -394,7 +394,6 @@ class VolumeTests(test.BaseAdminViewTests): self.mock_tenant_absolute_limits.return_value = volume_limits self.mock_volume_get.return_value = volume self.mock_volume_snapshot_list.return_value = this_volume_snapshots - self.mock_message_list.return_value = [] url = (reverse(DETAIL_URL, args=[volume.id]) + '?' + '='.join(['tab', 'volume_details__snapshots_tab'])) @@ -414,9 +413,3 @@ class VolumeTests(test.BaseAdminViewTests): self.mock_volume_snapshot_list.assert_called_once_with( test.IsHttpRequest(), search_opts={'volume_id': volume.id, 'all_tenants': True}) - self.mock_message_list.assert_called_once_with( - test.IsHttpRequest(), - { - 'resource_uuid': volume.id, - 'resource_type': 'volume' - }) diff --git a/openstack_dashboard/dashboards/project/snapshots/tables.py b/openstack_dashboard/dashboards/project/snapshots/tables.py index 5db1b52b68..c43c7a740d 100644 --- a/openstack_dashboard/dashboards/project/snapshots/tables.py +++ b/openstack_dashboard/dashboards/project/snapshots/tables.py @@ -263,3 +263,21 @@ class VolumeSnapshotsTable(VolumeDetailsSnapshotsTable): class Meta(VolumeDetailsSnapshotsTable.Meta): pass + + +class SnapshotMessagesTable(tables.DataTable): + message_id = tables.Column("id", verbose_name=_("ID")) + message_level = tables.Column("message_level", + verbose_name=_("Message Level")) + event_id = tables.Column("event_id", + verbose_name=_("Event Id")) + user_message = tables.Column("user_message", + verbose_name=_("User Message")) + created_at = tables.Column("created_at", + verbose_name=_("Created At")) + guaranteed_until = tables.Column("guaranteed_until", + verbose_name=_("Guaranteed Until")) + + class Meta(object): + name = "snapshot_messages" + verbose_name = _("Messages") diff --git a/openstack_dashboard/dashboards/project/snapshots/tabs.py b/openstack_dashboard/dashboards/project/snapshots/tabs.py index 548fd4044c..2c29e99ccd 100644 --- a/openstack_dashboard/dashboards/project/snapshots/tabs.py +++ b/openstack_dashboard/dashboards/project/snapshots/tabs.py @@ -19,6 +19,8 @@ from horizon import exceptions from horizon import tabs from openstack_dashboard.api import cinder +from openstack_dashboard.dashboards.project.snapshots \ + import tables as snap_messages_tables class OverviewTab(tabs.Tab): @@ -43,6 +45,29 @@ class OverviewTab(tabs.Tab): return reverse('horizon:project:snapshots:index') +class SnapshotMessagesTab(tabs.TableTab): + table_classes = (snap_messages_tables.SnapshotMessagesTable,) + name = _("Messages") + slug = "messages_tab" + template_name = ("horizon/common/_detail_table.html") + preload = False + + def get_snapshot_messages_data(self): + messages = [] + snapshot = self.tab_group.kwargs['snapshot'] + snap_id = snapshot.id + try: + snap_msgs = cinder.message_list(self.request, search_opts={ + 'resource_type': 'volume_snapshot', 'resource_uuid': snap_id}) + for snap_msg in snap_msgs: + messages.append(snap_msg) + + except Exception: + exceptions.handle(self.request, _("Unable to retrieve " + "snapshot messages.")) + return messages + + class SnapshotDetailTabs(tabs.TabGroup): slug = "snapshot_details" - tabs = (OverviewTab,) + tabs = (OverviewTab, SnapshotMessagesTab) diff --git a/openstack_dashboard/dashboards/project/volumes/tables.py b/openstack_dashboard/dashboards/project/volumes/tables.py index ecb8e4ce20..418514314a 100644 --- a/openstack_dashboard/dashboards/project/volumes/tables.py +++ b/openstack_dashboard/dashboards/project/volumes/tables.py @@ -656,3 +656,21 @@ class AttachmentsTable(tables.DataTable): verbose_name = _("Attachments") table_actions = (DetachVolume,) row_actions = (DetachVolume,) + + +class VolumeMessagesTable(tables.DataTable): + message_id = tables.Column("id", verbose_name=_("ID")) + message_level = tables.Column("message_level", + verbose_name=_("Message Level")) + event_id = tables.Column("event_id", + verbose_name=_("Event Id")) + user_message = tables.Column("user_message", + verbose_name=_("User Message")) + created_at = tables.Column("created_at", + verbose_name=_("Created At")) + guaranteed_until = tables.Column("guaranteed_until", + verbose_name=_("Guaranteed Until")) + + class Meta(object): + name = "volume_messages" + verbose_name = _("Messages") diff --git a/openstack_dashboard/dashboards/project/volumes/tabs.py b/openstack_dashboard/dashboards/project/volumes/tabs.py index 22322e520d..13f7031494 100644 --- a/openstack_dashboard/dashboards/project/volumes/tabs.py +++ b/openstack_dashboard/dashboards/project/volumes/tabs.py @@ -19,6 +19,8 @@ from horizon import tabs from openstack_dashboard.api import cinder from openstack_dashboard.dashboards.project.snapshots import tables +from openstack_dashboard.dashboards.project.volumes \ + import tables as vol_messages_tables class OverviewTab(tabs.Tab): @@ -77,6 +79,28 @@ class SnapshotTab(tabs.TableTab): return snapshots +class VolumeMessagesTab(tabs.TableTab): + table_classes = (vol_messages_tables.VolumeMessagesTable,) + name = _("Messages") + slug = "messages_tab" + template_name = ("horizon/common/_detail_table.html") + preload = False + + def get_volume_messages_data(self): + messages = [] + volume = self.tab_group.kwargs['volume'].id + try: + vol_msgs = cinder.message_list(self.request, search_opts={ + 'resource_type': 'volume', 'resource_uuid': volume}) + for vol_msg in vol_msgs: + messages.append(vol_msg) + + except Exception: + exceptions.handle(self.request, _("Unable to retrieve " + "volume messages.")) + return messages + + class VolumeDetailTabs(tabs.DetailTabsGroup): slug = "volume_details" - tabs = (OverviewTab, SnapshotTab) + tabs = (OverviewTab, SnapshotTab, VolumeMessagesTab) diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_detail_overview.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_detail_overview.html index 96e9e638eb..e3d8204117 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_detail_overview.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_detail_overview.html @@ -105,22 +105,4 @@
{{ volume.transfer.created_at|parse_date }}
{% endif %} - -{% if volume.messages %} -

{% trans "Messages" %}

-
-
- {% for m in volume.messages %} -
- {{ m.user_message }} -
- {% endfor %} -
-{% endif %} diff --git a/openstack_dashboard/dashboards/project/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/tests.py index 6d3e158a45..65b0ba0aac 100644 --- a/openstack_dashboard/dashboards/project/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/tests.py @@ -1404,8 +1404,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): @test.create_mocks({ api.nova: ['server_get'], - cinder: ['message_list', - 'volume_snapshot_list', + cinder: ['volume_snapshot_list', 'volume_get', 'tenant_absolute_limits'], }) @@ -1422,7 +1421,6 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.mock_server_get.return_value = server self.mock_tenant_absolute_limits.return_value = \ self.cinder_limits['absolute'] - self.mock_message_list.return_value = [] url = reverse('horizon:project:volumes:detail', args=[volume.id]) @@ -1438,12 +1436,6 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.mock_server_get.assert_called_once_with(test.IsHttpRequest(), server.id) self.mock_tenant_absolute_limits.assert_called_once() - self.mock_message_list.assert_called_once_with( - test.IsHttpRequest(), - { - 'resource_uuid': '11023e92-8008-4c8b-8059-7f2293ff3887', - 'resource_type': 'volume', - }) @mock.patch.object(cinder, 'volume_get_encryption_metadata') @mock.patch.object(cinder, 'volume_get') @@ -1530,8 +1522,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): api.nova: ['server_get'], cinder: ['tenant_absolute_limits', 'volume_get', - 'volume_snapshot_list', - 'message_list'], + 'volume_snapshot_list'], }) def test_detail_view_snapshot_tab(self): volume = self.cinder_volumes.first() @@ -1546,7 +1537,6 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.mock_server_get.return_value = server self.mock_tenant_absolute_limits.return_value = \ self.cinder_limits['absolute'] - self.mock_message_list.return_value = [] self.mock_volume_snapshot_list.return_value = this_volume_snapshots url = '?'.join([reverse(DETAIL_URL, args=[volume.id]), @@ -1564,12 +1554,6 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.mock_volume_snapshot_list.assert_called_once_with( test.IsHttpRequest(), search_opts={'volume_id': volume.id}) self.mock_tenant_absolute_limits.assert_called_once() - self.mock_message_list.assert_called_once_with( - test.IsHttpRequest(), - { - 'resource_uuid': volume.id, - 'resource_type': 'volume' - }) @mock.patch.object(cinder, 'volume_get') def test_detail_view_with_exception(self, mock_get): diff --git a/openstack_dashboard/dashboards/project/volumes/views.py b/openstack_dashboard/dashboards/project/volumes/views.py index 496cc8b348..76d93c5f35 100644 --- a/openstack_dashboard/dashboards/project/volumes/views.py +++ b/openstack_dashboard/dashboards/project/volumes/views.py @@ -223,18 +223,6 @@ class DetailView(tabs.TabbedTableView): exceptions.handle(self.request, _('Unable to retrieve volume details.'), redirect=redirect) - try: - volume.messages = cinder.message_list( - self.request, - {'resource_type': 'volume', 'resource_uuid': volume.id}, - ) - except Exception: - volume.messages = [] - exceptions.handle( - self.request, - _('Unable to retrieve volume messages.'), - ignore=True, - ) return volume, snapshots def get_redirect_url(self): diff --git a/openstack_dashboard/test/test_data/cinder_data.py b/openstack_dashboard/test/test_data/cinder_data.py index 2dc59a39a5..7f67a160f1 100644 --- a/openstack_dashboard/test/test_data/cinder_data.py +++ b/openstack_dashboard/test/test_data/cinder_data.py @@ -28,6 +28,7 @@ from cinderclient.v2 import volumes from cinderclient.v3 import group_snapshots from cinderclient.v3 import group_types from cinderclient.v3 import groups +from cinderclient.v3 import messages from openstack_dashboard import api from openstack_dashboard.api import cinder as cinder_api @@ -36,6 +37,7 @@ from openstack_dashboard.usage import quotas as usage_quotas def data(TEST): + TEST.cinder_messages = utils.TestDataContainer() TEST.cinder_services = utils.TestDataContainer() TEST.cinder_volumes = utils.TestDataContainer() TEST.cinder_volume_backups = utils.TestDataContainer() @@ -547,3 +549,27 @@ def data(TEST): TEST.cinder_volume_snapshots_with_groups.add( api.cinder.VolumeSnapshot(snapshot5)) + + # Cinder Messages + messages_1 = messages.Message( + messages.MessageManager(None), + {'created_at': '2020-07-08T17:12:06.000000', + 'event_id': 'VOLUME_VOLUME_001_001', + 'guaranteed_until': '2020-08-07T17:12:06.000000', + 'id': '2d2bb0d7-af28-4566-9a65-6d987c19093c', + 'resource_type': 'VOLUME', + 'resource_uuid': '6d53d143-e10f-440a-a65f-16a6b6d068f7', + 'user_message': 'schedule allocate volume:An unknown error occurred.' + }) + + messages_2 = messages.Message( + messages.MessageManager(None), + {'created_at': '2020-07-12T12:56:43.000000', + 'event_id': 'VOLUME_VOLUME_SNAPSHOT_009_015', + 'guaranteed_until': '2020-08-11T12:56:43.000000', + 'id': 'd360b4e2-bda5-4289-b673-714a90cde80b', + 'resource_type': 'VOLUME_SNAPSHOT', + 'resource_uuid': '761634b0-fa1c-4e59-b8ad-d720807cb355', + 'user_message': 'create snapshot:Snapshot is busy.'}) + TEST.cinder_messages.add(api.cinder.Message(messages_1)) + TEST.cinder_messages.add(api.cinder.Message(messages_2)) diff --git a/openstack_dashboard/test/unit/api/test_cinder.py b/openstack_dashboard/test/unit/api/test_cinder.py index 769f29bed6..026b7a6275 100644 --- a/openstack_dashboard/test/unit/api/test_cinder.py +++ b/openstack_dashboard/test/unit/api/test_cinder.py @@ -447,6 +447,19 @@ class CinderApiTests(test.APIMockTestCase): self.assertEqual(default_volume_type, volume_type) cinderclient.volume_types.default.assert_called_once() + @test.create_mocks({ + api.cinder: [('_cinderclient_with_features', 'cinderclient'), ]}) + def test_cinder_message_list(self): + search_opts = {'resource_type': 'VOLUME', + 'resource_uuid': '6d53d143-e10f-440a-a65f-16a6b6d068f7'} + messages = self.cinder_messages.list() + cinderclient = self.mock_cinderclient.return_value + messages_mock = cinderclient.messages.list + messages_mock.return_value = messages + + api.cinder.message_list(self.request, search_opts=search_opts) + messages_mock.assert_called_once_with(search_opts) + class CinderApiVersionTests(test.TestCase): diff --git a/releasenotes/notes/cinder-user-facing-messages-92cfd25492616d6d.yaml b/releasenotes/notes/cinder-user-facing-messages-92cfd25492616d6d.yaml new file mode 100644 index 0000000000..4d51056e12 --- /dev/null +++ b/releasenotes/notes/cinder-user-facing-messages-92cfd25492616d6d.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + [`cinder-user-facing messages `_] + This bp add a new tab "Messages" in volume/snapshot detail pages where User can see failure summary messages + for corresponding volume and snapshot resources. Before this bp it only shows user messages and message-level + info. for a specific volume in the detail page but now more parameters will be displayed for that volume.