Merge "User messages panel"

This commit is contained in:
Zuul 2020-09-22 19:19:20 +00:00 committed by Gerrit Code Review
commit e36f87fc61
30 changed files with 941 additions and 0 deletions

View File

@ -639,3 +639,18 @@ def share_group_type_unset_specs(request, share_group_type, keys):
def share_group_type_get_specs(request, share_group_type):
return manilaclient(request).share_group_types.get(
share_group_type).get_keys()
# ####### User Messages # ########
def messages_get(request, message_id):
return manilaclient(request).messages.get(message_id)
def messages_list(request, search_opts=None, sort_key=None, sort_dir=None):
return manilaclient(request).messages.list(search_opts=search_opts,
sort_key=sort_key,
sort_dir=sort_dir)
def messages_delete(request, message_id):
return manilaclient(request).messages.delete(message_id)

View File

@ -0,0 +1,28 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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 _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class UserMessages(horizon.Panel):
name = _("User Messages")
slug = 'user_messages'
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(UserMessages)

View File

@ -0,0 +1,46 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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 tables
from manila_ui.dashboards.project.user_messages import tables as project_tables
class DeleteUserMessage(project_tables.DeleteUserMessage):
pass
class UserMessageIDColumn(project_tables.UserMessageIDColumn):
pass
class UserMessagesAdminTable(project_tables.UserMessagesTable):
project_name = tables.Column(
"project_name",
verbose_name=_("Project"),
)
class Meta(object):
name = "user_messages"
verbose_name = _("User Messages")
table_actions = (
tables.NameFilterAction,
DeleteUserMessage,
)
row_actions = (
DeleteUserMessage,
)
columns = ('project_name', 'message_id', 'resource_type',
'resource_id', 'user_message', 'created_at',)

View File

@ -0,0 +1,23 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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 manila_ui.dashboards.project.user_messages import tabs as project_tabs
class UserMessagesOverviewTab(project_tabs.UserMessagesOverviewTab):
template_name = "admin/user_messages/_detail.html"
class UserMessagesDetailTabs(project_tabs.UserMessagesDetailTabs):
pass

View File

@ -0,0 +1,26 @@
{% load i18n sizeformat parse_date %}
<h3>{% trans "User Messages Overview" %}</h3>
<div class="detail">
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "ID" %}</dt>
<dd>{{ message.id }}</dd>
<dt>{% trans "Action" %}</dt>
<dd>{{ message.action_id }}</dd>
<dt>{% trans "Message" %}</dt>
<dd>{{ message.user_message }}</dd>
<dt>{% trans "Message Level" %}</dt>
<dd>{{ message.message_level }}</dd>
<dt>{% trans "Resource Type" %}</dt>
<dd>{{ message.resource_type }}</dd>
<dt>{% trans "Resource ID" %}</dt>
<dd>{{ message.resource_id }}</dd>
<dt>{% trans "Created at" %}</dt>
<dd>{{ message.created_at|parse_isotime }}</dd>
<dt>{% trans "Expires at" %}</dt>
<dd>{{ message.expires_at|parse_isotime }}</dd>
<dt>{% trans "Request ID" %}</dt>
<dd>{{ message.request_id }}</dd>
</dl>
</div>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "User Messages Detail" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "User Messages" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ user_messages_table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,29 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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.conf import urls
from manila_ui.dashboards.admin.user_messages import views
urlpatterns = [
urls.url(
r'^$',
views.UserMessagesAdminView.as_view(),
name='index'),
urls.url(
r'^(?P<message_id>[^/]+)$',
views.UserMessagesAdminDetailView.as_view(),
name='user_messages_detail'),
]

View File

@ -0,0 +1,48 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon.utils import memoized
from manila_ui.api import manila
from manila_ui.dashboards.admin.user_messages import tables as admin_tables
from manila_ui.dashboards.admin.user_messages import tabs as admin_tabs
from manila_ui.dashboards.admin import utils
from manila_ui.dashboards.project.user_messages import views as project_views
class UserMessagesAdminView(project_views.UserMessagesView):
table_classes = (
admin_tables.UserMessagesAdminTable,
)
template_name = "admin/user_messages/index.html"
@memoized.memoized_method
def get_user_messages_data(self):
try:
messages = manila.messages_list(self.request)
utils.set_project_name_to_objects(self.request, messages)
except Exception:
msg = _("Unable to retrieve messages list.")
exceptions.handle(self.request, msg)
return []
return messages
class UserMessagesAdminDetailView(project_views.UserMessagesDetailView):
tab_group_class = admin_tabs.UserMessagesDetailTabs
template_name = 'admin/user_messages/detail.html'
redirect_url = reverse_lazy('horizon:admin:user_messages:index')

View File

@ -0,0 +1,28 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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 _
import horizon
from openstack_dashboard.dashboards.project import dashboard
class UserMessages(horizon.Panel):
name = _("User Messages")
slug = 'user_messages'
permissions = (
'openstack.services.share',
)
dashboard.Project.register(UserMessages)

View File

@ -0,0 +1,101 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import tables
from horizon.utils.filters import parse_isotime
from manila_ui.api import manila
def get_date(message):
return parse_isotime(message.created_at)
class DeleteUserMessage(tables.DeleteAction):
policy_rules = (("share", "share:delete_message"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete User Message",
u"Delete User Messages",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted User Message",
u"Deleted User Messages",
count
)
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum, "project_id", None)
return {"project_id": project_id}
def delete(self, request, obj_id):
obj = self.table.get_object_by_id(obj_id)
message_id = self.table.get_object_display(obj)
try:
manila.messages_delete(request, message_id)
except Exception:
msg = _('Unable to delete message "%s"')
exceptions.handle(self.request, msg % message_id)
raise
class UserMessageIDColumn(tables.Column):
def get_link_url(self, message):
return reverse(self.link, args=(message.message_id,))
class UserMessagesTable(tables.DataTable):
message_id = tables.Column(
"id",
verbose_name=_("ID"),
link="horizon:project:user_messages:user_messages_detail")
resource_type = tables.Column(
"resource_type",
verbose_name=_("Resource Type"))
resource_id = tables.Column(
"resource_id",
verbose_name=_("Resource ID"))
user_message = tables.Column(
"user_message",
verbose_name=_("User Message"))
created_at = tables.Column(
get_date,
verbose_name=_("Created At"))
def get_object_display(self, obj):
return obj.id
class Meta(object):
name = "user_messages"
verbose_name = _("User Messages")
table_actions = (
tables.NameFilterAction,
DeleteUserMessage,
)
row_actions = (
DeleteUserMessage,
)

View File

@ -0,0 +1,32 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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 tabs
class UserMessagesOverviewTab(tabs.Tab):
name = _("User Messages Overview")
slug = "user_messages_overview_tab"
template_name = "project/user_messages/_detail.html"
def get_context_data(self, request):
return {"message": self.tab_group.kwargs['message']}
class UserMessagesDetailTabs(tabs.TabGroup):
slug = "user_messages_detail"
tabs = (
UserMessagesOverviewTab,
)

View File

@ -0,0 +1,26 @@
{% load i18n sizeformat parse_date %}
<h3>{% trans "User Messages Overview" %}</h3>
<div class="detail">
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "ID" %}</dt>
<dd>{{ message.id }}</dd>
<dt>{% trans "Action" %}</dt>
<dd>{{ message.action_id }}</dd>
<dt>{% trans "Message" %}</dt>
<dd>{{ message.user_message }}</dd>
<dt>{% trans "Message Level" %}</dt>
<dd>{{ message.message_level }}</dd>
<dt>{% trans "Resource Type" %}</dt>
<dd>{{ message.resource_type }}</dd>
<dt>{% trans "Resource ID" %}</dt>
<dd>{{ message.resource_id }}</dd>
<dt>{% trans "Created at" %}</dt>
<dd>{{ message.created_at|parse_isotime }}</dd>
<dt>{% trans "Expires at" %}</dt>
<dd>{{ message.expires_at|parse_isotime }}</dd>
<dt>{% trans "Request ID" %}</dt>
<dd>{{ message.request_id }}</dd>
</dl>
</div>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "User Messages Details" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "User Messages" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ user_messages_table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,29 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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.conf import urls
from manila_ui.dashboards.project.user_messages import views
urlpatterns = [
urls.url(
r'^$',
views.UserMessagesView.as_view(),
name='index'),
urls.url(
r'^(?P<message_id>[^/]+)$',
views.UserMessagesDetailView.as_view(),
name='user_messages_detail'),
]

View File

@ -0,0 +1,76 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon import tabs
from horizon.utils import memoized
from manila_ui.api import manila
from manila_ui.dashboards.project.user_messages import tables as um_tables
from manila_ui.dashboards.project.user_messages import tabs as um_tabs
class UserMessagesView(tables.MultiTableView):
table_classes = (
um_tables.UserMessagesTable,
)
template_name = "project/user_messages/index.html"
page_title = _("User Messages")
@memoized.memoized_method
def get_user_messages_data(self):
try:
messages = manila.messages_list(self.request)
except Exception:
msg = _("Unable to retrieve messages list.")
exceptions.handle(self.request, msg)
return []
return messages
class UserMessagesDetailView(tabs.TabView):
tab_group_class = um_tabs.UserMessagesDetailTabs
template_name = 'project/user_messages/detail.html'
redirect_url = reverse_lazy('horizon:project:user_messages:index')
def get_context_data(self, **kwargs):
context = super(UserMessagesDetailView, self).get_context_data(
**kwargs)
message = self.get_data()
message_id = message.id
context["message"] = message
context["message_id"] = message_id
context["page_title"] = _("User Message Details: "
"%(message_id)s") % (
{'message_id': message_id})
return context
@memoized.memoized_method
def get_data(self):
try:
message_id = self.kwargs['message_id']
message = manila.messages_get(self.request, message_id)
except Exception:
exceptions.handle(
self.request,
_('Unable to retrieve user message detail.'),
redirect=self.redirect_url)
return message
def get_tabs(self, request, *args, **kwargs):
message = self.get_data()
return self.tab_group_class(request, message=message, **kwargs)

View File

@ -0,0 +1,19 @@
# Copyright 2020 Red Hat Inc.
#
# 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.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'share'
PANEL = 'user_messages'
ADD_PANEL = ('manila_ui.dashboards.admin.user_messages.panel.UserMessages')

View File

@ -0,0 +1,19 @@
# Copyright 2020 Red Hat Inc.
#
# 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.
PANEL_DASHBOARD = 'project'
PANEL_GROUP = 'share'
PANEL = 'user_messages'
ADD_PANEL = ('manila_ui.dashboards.project.user_messages.panel.UserMessages')

View File

@ -690,3 +690,39 @@ class ManilaApiTests(base.APITestCase):
get_method.return_value.get_keys.return_value, result)
get_method.assert_called_once_with(sgt)
get_method.return_value.get_keys.assert_called_once_with()
@ddt.data(None, "some_fake_message_id")
def test_share_messages_get(self, message_id):
result = api.messages_get(self.request, message_id)
self.assertIsNotNone(result)
self.assertEqual(
self.manilaclient.messages.get.return_value,
result)
self.manilaclient.messages.get.assert_called_once_with(message_id)
@ddt.data(
{},
{'search_opts': 'foo', 'sort_key': 'k', 'sort_dir': 'v'},
)
def test_messages_list(self, kwargs):
result = api.messages_list(self.request, **kwargs)
self.assertIsNotNone(result)
self.assertEqual(
self.manilaclient.messages.list.return_value,
result)
self.manilaclient.messages.list.assert_called_once_with(
search_opts=kwargs.get('search_opts'),
sort_key=kwargs.get('sort_key'),
sort_dir=kwargs.get('sort_dir'))
@ddt.data(None, "some_fake_message_id")
def test_messages_delete(self, message_id):
result = api.messages_delete(self.request, message_id)
self.assertIsNotNone(result)
self.assertEqual(
self.manilaclient.messages.delete.return_value,
result)
self.manilaclient.messages.delete.assert_called_once_with(message_id)

View File

@ -0,0 +1,116 @@
# Copyright 2020 Red Hat, Inc.
#
# 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.
import ddt
from django.urls import reverse
from openstack_dashboard.api import keystone as api_keystone
from unittest import mock
from horizon import exceptions as horizon_exceptions
from manila_ui.api import manila as api_manila
from manila_ui.dashboards.admin import utils
from manila_ui.tests.dashboards.project import test_data
from manila_ui.tests import helpers as test
from manila_ui.tests.test_data import keystone_data
INDEX_URL = reverse('horizon:admin:user_messages:index')
@ddt.ddt
class UserMessagesViewTests(test.BaseAdminViewTests):
def setUp(self):
super(self.__class__, self).setUp()
self.mock_object(
api_keystone, "tenant_list",
mock.Mock(return_value=(keystone_data.projects, None)))
# Reset taken list of projects to avoid test interference
utils.PROJECTS = {}
@ddt.data(None, Exception('fake'))
def test_view(self, exc):
message_1 = test_data.fake_message_1
message_2 = test_data.fake_message_2
message_3 = test_data.fake_message_3
fake_message_list = [message_1, message_2, message_3]
url = reverse('horizon:admin:user_messages:index')
self.mock_object(
api_manila, "messages_list",
mock.Mock(side_effect=exc, return_value=fake_message_list))
self.client.get(url)
self.assertNoMessages()
api_manila.messages_list.assert_called_once_with(mock.ANY)
@ddt.data(None, Exception('fake'))
def test_delete_message(self, exc):
message = test_data.fake_message_1
formData = {'action': 'user_messages__delete__%s' % message.id}
self.mock_object(api_manila, "messages_delete",
mock.Mock(side_effect=exc))
self.mock_object(
api_manila, "messages_list",
mock.Mock(return_value=[message]))
res = self.client.post(INDEX_URL, formData)
self.assertEqual(res.status_code, 302)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.messages_list.assert_called_once_with(mock.ANY)
api_manila.messages_delete.assert_called_once_with(
mock.ANY, test_data.fake_message_1.id)
@ddt.ddt
class UserMessagesDetailViewTests(test.BaseAdminViewTests):
def test_detail_view(self):
message = test_data.fake_message_1
url = reverse('horizon:admin:user_messages:user_messages_detail',
args=[message.id])
self.mock_object(
api_manila, "messages_get", mock.Mock(return_value=message))
res = self.client.get(url)
self.assertContains(res, "<h1>User Message Details: %s</h1>"
% message.id,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.action_id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.user_message, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.message_level, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.resource_type, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.resource_id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.request_id, 1, 200)
self.assertNoMessages()
api_manila.messages_get.assert_called_once_with(
mock.ANY, message.id)
def test_detail_view_with_exception(self):
message = test_data.fake_message_1
url = reverse(
'horizon:admin:user_messages:user_messages_detail',
args=[message.id])
self.mock_object(
api_manila, "messages_get",
mock.Mock(side_effect=horizon_exceptions.NotFound(404)))
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.messages_get.assert_called_once_with(mock.ANY, message.id)

View File

@ -13,6 +13,7 @@
# under the License.
import collections
from manilaclient.v2 import messages
from manilaclient.v2 import security_services
from manilaclient.v2 import share_export_locations
from manilaclient.v2 import share_group_snapshots
@ -490,3 +491,50 @@ limits_negative = {"totalSharesUsed": 10,
"maxTotalShareGigabytes": 1000,
"maxTotalSnapshotGigabytes": 1000,
}
# Manila User Messages
fake_message_1 = messages.Message(
messages.MessageManager(FakeAPIClient),
{'resource_id': "351cc796-2d79-4a08-b878-a8ed933b6b68",
'message_level': 'ERROR',
'user_message': 'allocate host: No storage could be allocated for'
' this share request. Trying again with a different'
' size or share type may succeed.',
'expires_at': '2017-07-10T10:27:43.000000',
'id': '4b319d29-d5b7-4b6e-8e7c-8d6e53f3c3d5',
'created_at': '2017-07-10T10:26:43.000000',
'detail_id': '002',
'request_id': 'req-24e7ccb6-a7d5-4ddd-a8e4-d8f72a4509c8',
'project_id': '2e3de76b49b444fd9dc7ca9f7048ce6b',
'resource_type': 'SHARE',
'action_id': '001'})
fake_message_2 = messages.Message(
messages.MessageManager(FakeAPIClient),
{'resource_id': "25b4c0cc-e711-4c6f-b9fd-72d6b5c62bce",
'message_level': 'ERROR',
'user_message': 'Driver does not expect share-network to be provided '
'with current configuration.',
'expires_at': '2018-09-10T09:37:45.000000',
'id': 'd01d03ee-7758-4175-a6b5-853329dd2f4e',
'created_at': '2018-09-10T09:36:45.000000',
'detail_id': '003',
'request_id': 'req-fa568ab0-d6b3-4b32-899d-637ee006fed4',
'project_id': '2e3de76b49b444fd9dc7ca9f7048ce6b',
'resource_type': 'SHARE',
'action_id': '002'})
fake_message_3 = messages.Message(
messages.MessageManager(FakeAPIClient),
{'resource_id': "4befdc84-2796-44e1-8645-14b651bfb787",
'message_level': 'ERROR',
'user_message': 'Share has no replica with "replica_state" '
'set to "active"',
'expires_at': '2020-09-09T22:37:13.000000',
'id': '1e1c493f-d07d-48e9-93a8-ef5ad4b8ca8a',
'created_at': '2020-09-09T22:36:13.000000',
'detail_id': '004',
'request_id': 'req-29449bc8-d0ec-4d6b-b37c-85d0f04251b1',
'project_id': '2e3de76b49b444fd9dc7ca9f7048ce6b',
'resource_type': 'SHARE_REPLICA',
'action_id': '002'})

View File

@ -0,0 +1,104 @@
# Copyright 2020 Red Hat, Inc.
#
# 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.
import ddt
from django.urls import reverse
from unittest import mock
from horizon import exceptions as horizon_exceptions
from manila_ui.api import manila as api_manila
from manila_ui.tests.dashboards.project import test_data
from manila_ui.tests import helpers as test
INDEX_URL = reverse('horizon:project:user_messages:index')
@ddt.ddt
class UserMessagesViewTests(test.TestCase):
@ddt.data(None, Exception('fake'))
def test_view(self, exc):
message_1 = test_data.fake_message_1
message_2 = test_data.fake_message_2
message_3 = test_data.fake_message_3
fake_message_list = [message_1, message_2, message_3]
url = reverse('horizon:project:user_messages:index')
self.mock_object(
api_manila, "messages_list",
mock.Mock(side_effect=exc, return_value=fake_message_list))
self.client.get(url)
self.assertNoMessages()
api_manila.messages_list.assert_called_once_with(mock.ANY)
@ddt.data(None, Exception('fake'))
def test_delete_message(self, exc):
message = test_data.fake_message_1
formData = {'action': 'user_messages__delete__%s' % message.id}
self.mock_object(api_manila, "messages_delete",
mock.Mock(side_effect=exc))
self.mock_object(
api_manila, "messages_list",
mock.Mock(return_value=[message]))
res = self.client.post(INDEX_URL, formData)
self.assertEqual(res.status_code, 302)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.messages_list.assert_called_once_with(mock.ANY)
api_manila.messages_delete.assert_called_once_with(
mock.ANY, test_data.fake_message_1.id)
@ddt.ddt
class UserMessagesDetailViewTests(test.TestCase):
def test_detail_view(self):
message = test_data.fake_message_1
url = reverse('horizon:project:user_messages:user_messages_detail',
args=[message.id])
self.mock_object(
api_manila, "messages_get", mock.Mock(return_value=message))
res = self.client.get(url)
self.assertContains(res, "<h1>User Message Details: %s</h1>"
% message.id,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.action_id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.user_message, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.message_level, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.resource_type, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.resource_id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % message.request_id, 1, 200)
self.assertNoMessages()
api_manila.messages_get.assert_called_once_with(
mock.ANY, message.id)
def test_detail_view_with_exception(self):
message = test_data.fake_message_1
url = reverse(
'horizon:project:user_messages:user_messages_detail',
args=[message.id])
self.mock_object(
api_manila, "messages_get",
mock.Mock(side_effect=horizon_exceptions.NotFound(404)))
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.messages_get.assert_called_once_with(mock.ANY, message.id)

View File

@ -0,0 +1,20 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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 manila_ui.tests.integration.pages.project.share import messagespage
class MessagesPage(messagespage.MessagesPage):
pass

View File

@ -0,0 +1,22 @@
# Copyright (c) 2020 Red Hat, Inc.
#
# 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 openstack_dashboard.test.integration_tests.pages import basepage
class MessagesPage(basepage.BaseNavigationPage):
def __init__(self, driver, conf):
super(MessagesPage, self).__init__(driver, conf)
self._page_title = "User Messages"

View File

@ -0,0 +1,6 @@
---
features:
- |
Implements the user messages graphical interface for the project and admin
dashboards.
[`blueprint ui-user-messages <https://blueprints.launchpad.net/manila-ui/+spec/ui-user-messages>`_]