Merge "Addition of share shrinking feature to Manila-UI"

This commit is contained in:
Zuul 2021-03-12 09:07:06 +00:00 committed by Gerrit Code Review
commit 0c39bd8436
13 changed files with 148 additions and 93 deletions

View File

@ -112,17 +112,22 @@ Edit share
A message indicates whether the action was successful.
Extend share
Resize share
------------
#. Log in to the dashboard, choose a project, and click :guilabel:`Shares`.
#. Go to the share that you want to edit and choose :guilabel:`Extend Share`
#. Go to the share that you want to edit and choose :guilabel:`Resize Share`
from Actions.
#. :guilabel:`New Size (GB)`: Enter new size.
#. :guilabel:`New Size (GB)`: Enter new size. It can be increased or decreased
from the original size. The size of the share cannot be lower than the size
of the data stored in the share.
If increased, the size of the share will be extended.
If decreased, the size of the share will be shrinked.
#. Click :guilabel:`Extend Share`.
#. Click :guilabel:`Resize Share`.
A message indicates whether the action was successful.

View File

@ -194,8 +194,11 @@ def share_unmanage(request, share):
return manilaclient(request).shares.unmanage(share)
def share_extend(request, share_id, new_size):
return manilaclient(request).shares.extend(share_id, new_size)
def share_resize(request, share_id, new_size, orig_size):
if orig_size > new_size:
return manilaclient(request).shares.shrink(share_id, new_size)
else:
return manilaclient(request).shares.extend(share_id, new_size)
def share_revert(request, share, snapshot):

View File

@ -26,6 +26,7 @@ from manila_ui.api import manila
from manila_ui.dashboards import utils
from manila_ui import features
from manilaclient.common.apiclient import exceptions as m_exceptions
import time
class CreateForm(forms.SelfHandlingForm):
@ -346,7 +347,7 @@ class AddRule(forms.SelfHandlingForm):
request, _('Unable to add rule.'), redirect=redirect)
class ExtendForm(forms.SelfHandlingForm):
class ResizeForm(forms.SelfHandlingForm):
name = forms.CharField(
max_length="255", label=_("Share Name"),
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
@ -360,16 +361,21 @@ class ExtendForm(forms.SelfHandlingForm):
)
new_size = forms.IntegerField(
label=_("New Size (GiB)"),
label=_("New Size (GiB)")
)
def clean(self):
cleaned_data = super(ExtendForm, self).clean()
cleaned_data = super(ResizeForm, self).clean()
new_size = cleaned_data.get('new_size')
orig_size = self.initial['orig_size']
if new_size <= orig_size:
message = _("New size must be greater than current size.")
if new_size == orig_size:
message = _("New size must be different than the existing size")
self._errors["new_size"] = self.error_class([message])
return cleaned_data
if new_size <= 0:
message = _("New size should not be less than or equal to zero")
self._errors["new_size"] = self.error_class([message])
return cleaned_data
@ -386,16 +392,33 @@ class ExtendForm(forms.SelfHandlingForm):
def handle(self, request, data):
share_id = self.initial['share_id']
new_size = data['new_size']
orig_size = data['orig_size']
try:
manila.share_extend(request, share_id, data['new_size'])
message = _('Extend share "%s"') % data['name']
messages.success(request, message)
return True
manila.share_resize(request, share_id, new_size, orig_size)
return self.check_size(request, share_id, new_size)
except Exception:
redirect = reverse("horizon:project:shares:index")
exceptions.handle(request,
_('Unable to extend share.'),
redirect=redirect)
exceptions.handle(request, _(
'Unable to resize share.'), redirect=redirect)
def check_size(self, request, share_id, new_size):
share = manila.share_get(request, share_id)
timeout = 30
interval = 0.35
time_elapsed = 0
while share.status != 'available':
time.sleep(interval)
time_elapsed += interval
share = manila.share_get(request, share_id)
if time_elapsed > timeout:
break
if share.size == new_size:
message = _('Resized share "%s"') % share.name
messages.success(request, message)
return True
raise Exception
class RevertForm(forms.SelfHandlingForm):

View File

@ -142,12 +142,12 @@ class EditShareMetadata(tables.LinkAction):
return share.status in ("available", "in-use")
class ExtendShare(tables.LinkAction):
name = "extend_share"
verbose_name = _("Extend Share")
url = "horizon:project:shares:extend"
class ResizeShare(tables.LinkAction):
name = "resize_share"
verbose_name = _("Resize Share")
url = "horizon:project:shares:resize"
classes = ("ajax-modal", "btn-create")
policy_rules = (("share", "share:extend"),)
policy_rules = (("share", "share:resize"),)
def get_policy_target(self, request, datum=None):
project_id = None
@ -213,6 +213,8 @@ class SharesTableBase(tables.DataTable):
("MANAGE_ERROR", False),
("UNMANAGE_ERROR", False),
("extending_error", False),
("shrinking_error", False),
("shrinking_possible_data_loss_error", False),
("reverting_error", False),
)
STATUS_DISPLAY_CHOICES = (
@ -237,6 +239,10 @@ class SharesTableBase(tables.DataTable):
u"Unmanage Error")),
("extending_error", pgettext_lazy("Current status of share",
u"Extending Error")),
("shrinking_error", pgettext_lazy("Current status of share",
u"Shrinking Error")),
("shrinking_possible_data_loss_error", pgettext_lazy(
"Current status of share", u"Shrinking Error")),
("reverting_error", pgettext_lazy("Current status of share",
u"Reverting Error")),
)
@ -411,7 +417,7 @@ class SharesTable(SharesTableBase):
DeleteShare)
row_actions = (
EditShare,
ExtendShare,
ResizeShare,
RevertShare,
ss_tables.CreateShareSnapshot,
ManageRules,

View File

@ -2,6 +2,6 @@
{% load i18n %}
{% block modal-body-right %}
<div class="quota-dynamic">
{% include "project/shares/_extend_limits.html" with usages=usages %}
{% include "project/shares/_resize_limits.html" with usages=usages %}
</div>
{% endblock %}

View File

@ -2,7 +2,7 @@
<h3>{% trans "Description" %}:</h3>
<p>{% trans "Extend the size of a share. " %}</p>
<p>{% trans "Resize the size of a share. " %}</p>
<h3>{% trans "Share Limits" %}</h3>

View File

@ -1,7 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Extend Share" %}{% endblock %}
{% block main %}
{% include 'project/shares/_extend.html' %}
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Resize Share" %}{% endblock %}
{% block main %}
{% include 'project/shares/_resize.html' %}
{% endblock %}

View File

@ -49,9 +49,9 @@ urlpatterns = [
shares_views.UpdateMetadataView.as_view(),
name='update_metadata'),
urls.url(
r'^(?P<share_id>[^/]+)/extend/$',
shares_views.ExtendView.as_view(),
name='extend'),
r'^(?P<share_id>[^/]+)/resize/$',
shares_views.ResizeView.as_view(),
name='resize'),
urls.url(
r'^(?P<share_id>[^/]+)/revert/$',
shares_views.RevertView.as_view(),

View File

@ -276,16 +276,16 @@ class ManageRulesView(tables.DataTableView):
return rules
class ExtendView(forms.ModalFormView):
form_class = share_form.ExtendForm
form_id = "extend_share"
template_name = 'project/shares/extend.html'
modal_header = _("Extend Share")
modal_id = "extend_share_modal"
submit_label = _("Extend")
submit_url = "horizon:project:shares:extend"
class ResizeView(forms.ModalFormView):
form_class = share_form.ResizeForm
form_id = "resize_share"
template_name = 'project/shares/resize.html'
modal_header = _("Resize Share")
modal_id = "resize_share_modal"
submit_label = _("Resize")
submit_url = "horizon:project:shares:resize"
success_url = reverse_lazy("horizon:project:shares:index")
page_title = _('Extend Share')
page_title = _('Resize Share')
@memoized.memoized_method
def get_object(self):
@ -295,7 +295,7 @@ class ExtendView(forms.ModalFormView):
exceptions.handle(self.request, _('Unable to retrieve share.'))
def get_context_data(self, **kwargs):
context = super(ExtendView, self).get_context_data(**kwargs)
context = super(ResizeView, self).get_context_data(**kwargs)
args = (self.get_object().id,)
context['submit_url'] = reverse(self.submit_url, args=args)
try:
@ -315,7 +315,7 @@ class ExtendView(forms.ModalFormView):
'share_id': self.kwargs["share_id"],
'name': share.name or share.id,
'orig_size': share.size,
'new_size': int(share.size) + 1,
'new_size': int(share.size),
}

View File

@ -210,15 +210,18 @@ class ManilaApiTests(base.APITestCase):
assert_called_once_with('fake_share'))
# Share resize tests
def test_share_extend(self):
new_size = "123"
api.share_extend(self.request, self.id, new_size)
self.manilaclient.shares.extend.assert_called_once_with(
self.id, new_size
)
@ddt.data(("123", "78"), ("2", "5"),
("75", "21"), ("0", "2"),
("18", "62"), ("-1", "3"))
@ddt.unpack
def test_share_resize(self, new_size, orig_size):
api.share_resize(self.request, self.id, new_size, orig_size)
if orig_size > new_size:
self.manilaclient.shares.shrink.assert_called_once_with(
self.id, new_size)
else:
self.manilaclient.shares.extend.assert_called_once_with(
self.id, new_size)
# Share snapshots tests

View File

@ -354,9 +354,9 @@ class ShareViewTests(test.APITestCase):
mock.ANY, self.share.id, rule.id)
api_manila.share_rules_list.assert_called_with(mock.ANY, self.share.id)
def test_extend_share_get(self):
def test_resize_share_get(self):
share = test_data.share
url = reverse('horizon:project:shares:extend', args=[share.id])
url = reverse('horizon:project:shares:resize', args=[share.id])
self.mock_object(
neutron, "is_service_enabled", mock.Mock(return_value=[True]))
@ -364,24 +364,24 @@ class ShareViewTests(test.APITestCase):
api_manila.share_get.assert_called_once_with(mock.ANY, share.id)
self.assertNoMessages()
self.assertTemplateUsed(res, 'project/shares/extend.html')
self.assertTemplateUsed(res, 'project/shares/resize.html')
def test_extend_share_open_form_successfully(self):
def test_resize_share_open_form_successfully(self):
self.share.size = 5
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(api_manila, "share_extend")
url = reverse('horizon:project:shares:resize', args=[self.share.id])
self.mock_object(api_manila, "share_resize")
response = self.client.get(url)
self.assertEqual(200, response.status_code)
self.assertTemplateUsed(response, 'project/shares/extend.html')
self.assertTemplateUsed(response, 'project/shares/resize.html')
api_manila.share_get.assert_called_once_with(mock.ANY, self.share.id)
self.assertFalse(api_manila.share_extend.called)
self.assertFalse(api_manila.share_resize.called)
api_manila.tenant_absolute_limits.assert_called_once_with(mock.ANY)
def test_extend_share_get_with_api_exception(self):
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(api_manila, "share_extend")
def test_resize_share_get_with_api_exception(self):
url = reverse('horizon:project:shares:resize', args=[self.share.id])
self.mock_object(api_manila, "share_resize")
self.mock_object(
api_manila, "share_get",
mock.Mock(return_value=Exception('Fake share NotFound exception')))
@ -390,22 +390,22 @@ class ShareViewTests(test.APITestCase):
self.assertEqual(404, response.status_code)
self.assertTemplateNotUsed(
response, 'project/shares/shares/extend.html')
self.assertFalse(api_manila.share_extend.called)
response, 'project/shares/shares/resize.html')
self.assertFalse(api_manila.share_resize.called)
api_manila.share_get.assert_called_once_with(mock.ANY, self.share.id)
self.assertFalse(api_manila.tenant_absolute_limits.called)
@ddt.data(6, 54, 55)
def test_extend_share_post_successfully(self, new_size):
@ddt.data(6, 54, 1, 2, 21)
def test_resize_share_post_successfully(self, new_size):
self.share.size = 5
form_data = {'new_size': new_size}
form_data = {'new_size': new_size, 'orig_size': self.share.size}
usage_limit = {
'maxTotalShareGigabytes': self.share.size + 50,
'totalShareGigabytesUsed': self.share.size,
}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(api_manila, "share_extend")
url = reverse('horizon:project:shares:resize', args=[self.share.id])
self.mock_object(api_manila, "share_resize")
self.mock_object(
api_manila, 'tenant_absolute_limits',
mock.Mock(return_value=usage_limit))
@ -414,23 +414,28 @@ class ShareViewTests(test.APITestCase):
self.assertEqual(302, response.status_code)
self.assertTemplateNotUsed(
response, 'project/shares/extend.html')
api_manila.share_get.assert_called_once_with(mock.ANY, self.share.id)
api_manila.share_extend.assert_called_once_with(
mock.ANY, self.share.id, form_data['new_size'])
response, 'project/shares/resize.html')
calls = [
mock.call(mock.ANY, self.share.id),
mock.call(mock.ANY, self.share.id)]
api_manila.share_get.assert_has_calls(calls)
api_manila.share_resize.assert_called_once_with(
mock.ANY, self.share.id, form_data['new_size'],
form_data['orig_size'])
api_manila.tenant_absolute_limits.assert_called_once_with(mock.ANY)
self.assertRedirectsNoFollow(response, INDEX_URL)
@ddt.data(0, 5, 56)
def test_extend_share_post_with_invalid_value(self, new_size):
@ddt.data(5, 56, 0, -1)
def test_resize_share_post_with_invalid_value(self, new_size):
self.share.size = 5
form_data = {'new_size': new_size}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
form_data = {'new_size': new_size, 'orig_size': self.share.size}
url = reverse('horizon:project:shares:resize', args=[self.share.id])
usage_limit = {
'maxTotalShareGigabytes': self.share.size + 50,
'totalShareGigabytesUsed': self.share.size,
}
self.mock_object(api_manila, "share_extend")
self.mock_object(api_manila, "share_resize")
self.mock_object(
api_manila, 'tenant_absolute_limits',
mock.Mock(return_value=usage_limit))
@ -438,27 +443,31 @@ class ShareViewTests(test.APITestCase):
response = self.client.post(url, form_data)
self.assertEqual(200, response.status_code)
self.assertTemplateUsed(response, 'project/shares/extend.html')
self.assertFalse(api_manila.share_extend.called)
self.assertTemplateUsed(response, 'project/shares/resize.html')
self.assertFalse(api_manila.share_resize.called)
api_manila.share_get.assert_called_once_with(mock.ANY, self.share.id)
api_manila.tenant_absolute_limits.assert_called_with(mock.ANY)
def test_extend_share_post_with_api_exception(self):
def test_resize_share_post_with_api_exception(self):
self.share.size = 5
form_data = {'new_size': 30}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
form_data = {'new_size': 30, 'orig_size': self.share.size}
url = reverse('horizon:project:shares:resize', args=[self.share.id])
self.mock_object(
api_manila, "share_extend",
api_manila, "share_resize",
mock.Mock(return_value=Exception('Fake API exception')))
response = self.client.post(url, form_data)
self.assertEqual(302, response.status_code)
self.assertTemplateNotUsed(
response, 'project/shares/extend.html')
api_manila.share_extend.assert_called_once_with(
mock.ANY, self.share.id, form_data['new_size'])
api_manila.share_get.assert_called_once_with(mock.ANY, self.share.id)
response, 'project/shares/resize.html')
api_manila.share_resize.assert_called_once_with(
mock.ANY, self.share.id, form_data['new_size'],
form_data['orig_size'])
calls = [
mock.call(mock.ANY, self.share.id),
mock.call(mock.ANY, self.share.id)]
api_manila.share_get.assert_has_calls(calls)
api_manila.tenant_absolute_limits.assert_called_once_with(mock.ANY)
self.assertRedirectsNoFollow(response, INDEX_URL)

View File

@ -0,0 +1,6 @@
---
features:
- |
Existing "Extend Share" feature in Manila-UI was renamed
"Resize Share" and share shrinking feature was added. Now
users can both extend and shrink shares.