diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst index d605df1f..16c64480 100644 --- a/doc/source/user/index.rst +++ b/doc/source/user/index.rst @@ -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. diff --git a/manila_ui/api/manila.py b/manila_ui/api/manila.py index f8800ce2..e08d9903 100644 --- a/manila_ui/api/manila.py +++ b/manila_ui/api/manila.py @@ -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): diff --git a/manila_ui/dashboards/project/shares/forms.py b/manila_ui/dashboards/project/shares/forms.py index 1684bc69..33739201 100644 --- a/manila_ui/dashboards/project/shares/forms.py +++ b/manila_ui/dashboards/project/shares/forms.py @@ -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): diff --git a/manila_ui/dashboards/project/shares/tables.py b/manila_ui/dashboards/project/shares/tables.py index 259306c4..ecabf233 100644 --- a/manila_ui/dashboards/project/shares/tables.py +++ b/manila_ui/dashboards/project/shares/tables.py @@ -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, diff --git a/manila_ui/dashboards/project/shares/templates/shares/_extend.html b/manila_ui/dashboards/project/shares/templates/shares/_resize.html similarity index 71% rename from manila_ui/dashboards/project/shares/templates/shares/_extend.html rename to manila_ui/dashboards/project/shares/templates/shares/_resize.html index 8d87895c..b896de8e 100644 --- a/manila_ui/dashboards/project/shares/templates/shares/_extend.html +++ b/manila_ui/dashboards/project/shares/templates/shares/_resize.html @@ -2,6 +2,6 @@ {% load i18n %} {% block modal-body-right %}
- {% include "project/shares/_extend_limits.html" with usages=usages %} + {% include "project/shares/_resize_limits.html" with usages=usages %}
{% endblock %} diff --git a/manila_ui/dashboards/project/shares/templates/shares/_extend_limits.html b/manila_ui/dashboards/project/shares/templates/shares/_resize_limits.html similarity index 95% rename from manila_ui/dashboards/project/shares/templates/shares/_extend_limits.html rename to manila_ui/dashboards/project/shares/templates/shares/_resize_limits.html index 0e37fe27..ba75e500 100644 --- a/manila_ui/dashboards/project/shares/templates/shares/_extend_limits.html +++ b/manila_ui/dashboards/project/shares/templates/shares/_resize_limits.html @@ -2,7 +2,7 @@

{% trans "Description" %}:

-

{% trans "Extend the size of a share. " %}

+

{% trans "Resize the size of a share. " %}

{% trans "Share Limits" %}

diff --git a/manila_ui/dashboards/project/shares/templates/shares/extend.html b/manila_ui/dashboards/project/shares/templates/shares/extend.html deleted file mode 100644 index 9658518f..00000000 --- a/manila_ui/dashboards/project/shares/templates/shares/extend.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Extend Share" %}{% endblock %} - -{% block main %} - {% include 'project/shares/_extend.html' %} -{% endblock %} diff --git a/manila_ui/dashboards/project/shares/templates/shares/resize.html b/manila_ui/dashboards/project/shares/templates/shares/resize.html new file mode 100644 index 00000000..bc6d210a --- /dev/null +++ b/manila_ui/dashboards/project/shares/templates/shares/resize.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Resize Share" %}{% endblock %} + +{% block main %} + {% include 'project/shares/_resize.html' %} +{% endblock %} diff --git a/manila_ui/dashboards/project/shares/urls.py b/manila_ui/dashboards/project/shares/urls.py index 4508e5a3..7d81011b 100644 --- a/manila_ui/dashboards/project/shares/urls.py +++ b/manila_ui/dashboards/project/shares/urls.py @@ -49,9 +49,9 @@ urlpatterns = [ shares_views.UpdateMetadataView.as_view(), name='update_metadata'), urls.url( - r'^(?P[^/]+)/extend/$', - shares_views.ExtendView.as_view(), - name='extend'), + r'^(?P[^/]+)/resize/$', + shares_views.ResizeView.as_view(), + name='resize'), urls.url( r'^(?P[^/]+)/revert/$', shares_views.RevertView.as_view(), diff --git a/manila_ui/dashboards/project/shares/views.py b/manila_ui/dashboards/project/shares/views.py index 288037d3..019adb66 100644 --- a/manila_ui/dashboards/project/shares/views.py +++ b/manila_ui/dashboards/project/shares/views.py @@ -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), } diff --git a/manila_ui/tests/api/test_manila.py b/manila_ui/tests/api/test_manila.py index 02684f2d..fdeaa738 100644 --- a/manila_ui/tests/api/test_manila.py +++ b/manila_ui/tests/api/test_manila.py @@ -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 diff --git a/manila_ui/tests/dashboards/project/shares/tests.py b/manila_ui/tests/dashboards/project/shares/tests.py index 0df98e18..f5c01fc0 100644 --- a/manila_ui/tests/dashboards/project/shares/tests.py +++ b/manila_ui/tests/dashboards/project/shares/tests.py @@ -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) diff --git a/releasenotes/notes/share-shrinking-c1d46fc1c3ce29b7.yaml b/releasenotes/notes/share-shrinking-c1d46fc1c3ce29b7.yaml new file mode 100644 index 00000000..4cd1276b --- /dev/null +++ b/releasenotes/notes/share-shrinking-c1d46fc1c3ce29b7.yaml @@ -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.