Add Share Migration support

Add share migration support to manila-ui.

Share migration row buttons are present in Shares table from
Admin section.

Share's task_state field is added to the Shares detail view.

Change-Id: I2af0f56c17b8ca949b068a60314349686b81b34d
Implements: blueprint share-migration
This commit is contained in:
Rodrigo Barbieri 2016-07-19 11:15:17 -03:00
parent 25eacc26bc
commit 6025e5a96c
19 changed files with 700 additions and 1 deletions

View File

@ -34,7 +34,7 @@ from openstack_dashboard.api import nova
LOG = logging.getLogger(__name__)
MANILA_UI_USER_AGENT_REPR = "manila_ui_plugin_for_horizon"
MANILA_VERSION = "2.15" # requires manilaclient 1.8.0 or newer
MANILA_VERSION = "2.22" # requires manilaclient 1.10.0 or newer
MANILA_SERVICE_TYPE = "sharev2"
# API static values
@ -139,6 +139,32 @@ def share_manage(request, service_host, protocol, export_path,
)
def migration_start(request, share, dest_host, force_host_assisted_migration,
writable, preserve_metadata, nondisruptive,
new_share_network_id):
return manilaclient(request).shares.migration_start(
share,
host=dest_host,
force_host_assisted_migration=force_host_assisted_migration,
writable=writable,
preserve_metadata=preserve_metadata,
nondisruptive=nondisruptive,
new_share_network_id=new_share_network_id,
)
def migration_complete(request, share):
return manilaclient(request).shares.migration_complete(share)
def migration_get_progress(request, share):
return manilaclient(request).shares.migration_get_progress(share)
def migration_cancel(request, share):
return manilaclient(request).shares.migration_cancel(share)
def share_unmanage(request, share):
# Param 'share' can be either string with ID or object with attr 'id'.
return manilaclient(request).shares.unmanage(share)
@ -416,3 +442,9 @@ def share_instance_get(request, share_instance_id):
def is_replication_enabled():
manila_config = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
return manila_config.get('enable_replication', True)
@memoized
def is_migration_enabled():
manila_config = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
return manila_config.get('enable_migration', True)

View File

@ -37,6 +37,133 @@ ST_EXTRA_SPECS_FORM_ATTRS = {
}
class MigrationStart(forms.SelfHandlingForm):
name = forms.CharField(
label=_("Share Name"), required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
share_id = forms.CharField(
label=_("ID"), required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
host = forms.CharField(
max_length=255, label=_("Host to migrate share"),
help_text=_("Destination host where share will be migrated to. Use the"
" format 'host@backend#pool'."))
force_host_assisted_migration = forms.BooleanField(
label=_("Force Host Assisted Migration"),
required=False, initial=False,
help_text=("Defines whether the migration of this share should skip "
"the attempt to be assisted by a storage vendor backend. "
"This will force the use of the Data Service to perform "
"migration."))
nondisruptive = forms.BooleanField(
label=_("Non-disruptive"),
required=False, initial=False,
help_text=("Defines whether the migration of this share should be "
"performed only if it is non-disruptive If set so, this "
"will prevent the use of the Data Service for migration."))
writable = forms.BooleanField(
label=_("Writable"), required=False, initial=True,
help_text=("Defines whether this share should remain writable during "
"migration. If set so, this will prevent the use of the "
"Data Service for migration."))
preserve_metadata = forms.BooleanField(
label=_("Preserve Metadata"), required=False, initial=True,
help_text=("Defines whether this share should have all its file "
"metadata preserved during migration. If set so, this will "
"prevent the use of the Data Service for migration."))
new_share_network_id = forms.CharField(
max_length=255, label=_("ID of share network to be set in migrated "
"share"), required=False,
help_text=_("Input the ID of the share network where the share should"
" be migrated to."))
def handle(self, request, data):
share_name = _get_id_if_name_empty(data)
try:
manila.migration_start(
request, self.initial['share_id'],
force_host_assisted_migration=(
data['force_host_assisted_migration']),
writable=data['writable'],
preserve_metadata=data['preserve_metadata'],
nondisruptive=data['nondisruptive'],
dest_host=data['host'],
new_share_network_id=data['new_share_network_id'])
messages.success(
request,
_('Successfully sent the request to migrate share: %s.')
% share_name)
return True
except Exception:
exceptions.handle(request, _("Unable to migrate share %s.")
% share_name)
return False
class MigrationForms(forms.SelfHandlingForm):
name = forms.CharField(
label=_("Share Name"), required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
share_id = forms.CharField(
label=_("ID"), required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
class MigrationComplete(MigrationForms):
def handle(self, request, data):
share_name = _get_id_if_name_empty(data)
try:
manila.migration_complete(request, self.initial['share_id'])
messages.success(
request,
_('Successfully sent the request to complete migration of '
' share: %s.') % share_name)
return True
except Exception:
exceptions.handle(request, _("Unable to complete migration "
"of share %s.") % share_name)
return False
class MigrationGetProgress(MigrationForms):
def handle(self, request, data):
share_name = _get_id_if_name_empty(data)
try:
result = manila.migration_get_progress(request,
self.initial['share_id'])
progress = result[1]
messages.success(
request,
_('Migration of share %(name)s is at %(progress)s percent.') %
{'name': share_name, 'progress': progress['total_progress']})
return True
except Exception:
exceptions.handle(request, _("Unable to obtain progress of "
"migration of share %s at this "
"moment.") % share_name)
return False
class MigrationCancel(MigrationForms):
def handle(self, request, data):
share_name = _get_id_if_name_empty(data)
try:
manila.migration_cancel(request, self.initial['share_id'])
messages.success(
request,
_('Successfully sent the request to cancel migration of '
' share: %s.') % share_name)
return True
except Exception:
exceptions.handle(request, _("Unable to cancel migration of share"
" %s at this moment.") % share_name)
return False
class ManageShare(forms.SelfHandlingForm):
name = forms.CharField(
max_length=255, label=_("Share Name"), required=False,
@ -323,3 +450,12 @@ class CreateShareNetworkForm(forms.SelfHandlingForm):
exceptions.handle(request,
_('Unable to create share network.'))
return False
def _get_id_if_name_empty(data):
result = data.get('name', None)
if not result:
result = data.get('id')
if not result:
result = ''
return result

View File

@ -68,6 +68,67 @@ class ManageShareTypeAccess(tables.LinkAction):
return {"project_id": project_id}
class MigrationStartAction(tables.LinkAction):
name = "migration_start"
verbose_name = _("Migrate Share")
url = "horizon:admin:shares:migration_start"
classes = ("ajax-modal",)
policy_rules = (("share", "migration_start"),)
ajax = True
def allowed(self, request, share=None):
if share:
return (share.status.upper() == "AVAILABLE" and
not getattr(share, 'has_snapshot', False) and
manila.is_migration_enabled())
return False
class MigrationCompleteAction(tables.LinkAction):
name = "migration_complete"
verbose_name = _("Complete migration")
url = "horizon:admin:shares:migration_complete"
classes = ("ajax-modal",)
policy_rules = (("share", "migration_complete"),)
ajax = True
def allowed(self, request, share=None):
if (share and share.status.upper() == "MIGRATING" and
manila.is_migration_enabled()):
return True
return False
class MigrationCancelAction(tables.LinkAction):
name = "migration_cancel"
verbose_name = _("Cancel migration")
url = "horizon:admin:shares:migration_cancel"
classes = ("ajax-modal",)
policy_rules = (("share", "migration_cancel"),)
ajax = True
def allowed(self, request, share=None):
if (share and share.status.upper() == "MIGRATING" and
manila.is_migration_enabled()):
return True
return False
class MigrationGetProgressAction(tables.LinkAction):
name = "migration_get_progress"
verbose_name = _("Get migration progress")
url = "horizon:admin:shares:migration_get_progress"
classes = ("ajax-modal",)
policy_rules = (("share", "migration_get_progress"),)
ajax = True
def allowed(self, request, share=None):
if (share and share.status.upper() == "MIGRATING" and
manila.is_migration_enabled()):
return True
return False
class ManageShareAction(tables.LinkAction):
name = "manage"
verbose_name = _("Manage Share")
@ -175,6 +236,10 @@ class SharesTable(shares_tables.SharesTable):
shares_tables.DeleteShare)
row_actions = (
ManageReplicas,
MigrationStartAction,
MigrationCompleteAction,
MigrationGetProgressAction,
MigrationCancelAction,
UnmanageShareAction,
shares_tables.DeleteShare)
columns = (

View File

@ -0,0 +1,13 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% blocktrans %}
Cancel migration of a migrating share.
<br>
<br>
This is equivalent to the <tt>'manila migration-cancel'</tt> command.
{% endblocktrans %}
</p>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% blocktrans %}
Complete migration of a migrating share to another Manila host.
This operation is expected to be disruptive.
<br>
<br>
This is equivalent to the <tt>'manila migration-complete'</tt> command.
{% endblocktrans %}
</p>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% blocktrans %}
Obtains migration progress of a migrating share.
<br>
<br>
This is equivalent to the <tt>'manila migration-get-progress'</tt> command.
{% endblocktrans %}
</p>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% blocktrans %}
Migrate an existing share to another Manila host.
This will move all your share data from one host to another.
<br>
<br>
This is equivalent to the <tt>'manila migration-start'</tt> command.
{% endblocktrans %}
</p>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Cancel Migration" %}{% endblock %}
{% block main %}
{% include 'admin/shares/_migration_cancel.html' %}
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Complete Migration" %}{% endblock %}
{% block main %}
{% include 'admin/shares/_migration_complete.html' %}
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Get Migration Progress" %}{% endblock %}
{% block main %}
{% include 'admin/shares/_migration_get_progress.html' %}
{% endblock %}

View File

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

View File

@ -69,3 +69,16 @@ if manila.is_replication_enabled():
replica_views.ResetReplicaStateView.as_view(),
name='reset_replica_state'),
])
if manila.is_migration_enabled():
urlpatterns.extend([
url(r'^migration_start/(?P<share_id>[^/]+)$',
views.MigrationStartView.as_view(), name='migration_start'),
url(r'^migration_complete/(?P<share_id>[^/]+)$',
views.MigrationCompleteView.as_view(), name='migration_complete'),
url(r'^migration_cancel/(?P<share_id>[^/]+)$',
views.MigrationCancelView.as_view(), name='migration_cancel'),
url(r'^migration_get_progress/(?P<share_id>[^/]+)$',
views.MigrationGetProgressView.as_view(),
name='migration_get_progress'),
])

View File

@ -86,6 +86,155 @@ class ManageShareView(forms.ModalFormView):
return context
class MigrationStartView(forms.ModalFormView):
form_class = project_forms.MigrationStart
template_name = 'admin/shares/migration_start.html'
modal_header = _("Migrate Share")
form_id = "migration_start_share"
modal_id = "migration_start_share_modal"
submit_label = _("Start migration")
success_url = reverse_lazy('horizon:admin:shares:index')
submit_url = 'horizon:admin:shares:migration_start'
cancel_url = reverse_lazy('horizon:admin:shares:index')
page_title = _("Migrate a Share")
def get_context_data(self, **kwargs):
context = super(MigrationStartView, self).get_context_data(**kwargs)
args = (self.kwargs['share_id'],)
context['submit_url'] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def get_data(self):
try:
share_id = self.kwargs['share_id']
share = manila.share_get(self.request, share_id)
except Exception:
exceptions.handle(
self.request, _('Unable to retrieve share details.'),
redirect=self.success_url)
return share
def get_initial(self):
share = self.get_data()
return {
'share_id': self.kwargs["share_id"],
'name': share.name,
}
class MigrationCompleteView(forms.ModalFormView):
form_class = project_forms.MigrationComplete
template_name = 'admin/shares/migration_complete.html'
modal_header = _("Confirm Migration Completion of Share")
form_id = "migration_complete_share"
modal_id = "migration_complete_share_modal"
submit_label = _("Complete Migration")
success_url = reverse_lazy('horizon:admin:shares:index')
submit_url = 'horizon:admin:shares:migration_complete'
cancel_url = reverse_lazy('horizon:admin:shares:index')
page_title = _("Complete migration of a Share")
def get_context_data(self, **kwargs):
context = super(MigrationCompleteView, self).get_context_data(**kwargs)
args = (self.kwargs['share_id'],)
context['submit_url'] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def get_data(self):
try:
share_id = self.kwargs['share_id']
share = manila.share_get(self.request, share_id)
except Exception:
exceptions.handle(
self.request, _('Unable to retrieve share details.'),
redirect=self.success_url)
return share
def get_initial(self):
share = self.get_data()
return {
'share_id': self.kwargs["share_id"],
'name': share.name,
}
class MigrationCancelView(forms.ModalFormView):
form_class = project_forms.MigrationCancel
template_name = 'admin/shares/migration_cancel.html'
modal_header = _("Confirm Migration Cancelling of Share")
form_id = "migration_cancel_share"
modal_id = "migration_cancel_share_modal"
submit_label = _("Cancel Migration")
success_url = reverse_lazy('horizon:admin:shares:index')
submit_url = 'horizon:admin:shares:migration_cancel'
cancel_url = reverse_lazy('horizon:admin:shares:index')
page_title = _("Cancel migration of a Share")
def get_context_data(self, **kwargs):
context = super(MigrationCancelView, self).get_context_data(**kwargs)
args = (self.kwargs['share_id'],)
context['submit_url'] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def get_data(self):
try:
share_id = self.kwargs['share_id']
share = manila.share_get(self.request, share_id)
except Exception:
exceptions.handle(
self.request, _('Unable to retrieve share details.'),
redirect=self.success_url)
return share
def get_initial(self):
share = self.get_data()
return {
'share_id': self.kwargs["share_id"],
'name': share.name,
}
class MigrationGetProgressView(forms.ModalFormView):
form_class = project_forms.MigrationGetProgress
template_name = 'admin/shares/migration_get_progress.html'
modal_header = _("Confirm Obtaining migration progress of Share")
form_id = "migration_get_progress_share"
modal_id = "migration_get_progress_share_modal"
submit_label = _("Obtain Progress")
success_url = reverse_lazy('horizon:admin:shares:index')
submit_url = 'horizon:admin:shares:migration_get_progress'
cancel_url = reverse_lazy('horizon:admin:shares:index')
page_title = _("Obtain migration progress of a Share")
def get_context_data(self, **kwargs):
context = super(MigrationGetProgressView,
self).get_context_data(**kwargs)
args = (self.kwargs['share_id'],)
context['submit_url'] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def get_data(self):
try:
share_id = self.kwargs['share_id']
share = manila.share_get(self.request, share_id)
except Exception:
exceptions.handle(
self.request, _('Unable to retrieve share details.'),
redirect=self.success_url)
return share
def get_initial(self):
share = self.get_data()
return {
'share_id': self.kwargs["share_id"],
'name': share.name,
}
class UnmanageShareView(forms.ModalFormView):
form_class = project_forms.UnmanageShare
template_name = 'admin/shares/unmanage_share.html'

View File

@ -164,6 +164,7 @@ class SharesTableBase(tables.DataTable):
("available", True), ("AVAILABLE", True),
("creating", None), ("CREATING", None),
("deleting", None), ("DELETING", None),
("migrating", None), ("migrating_to", None),
("error", False), ("ERROR", False),
("error_deleting", False), ("ERROR_DELETING", False),
("MANAGE_ERROR", False),
@ -177,6 +178,9 @@ class SharesTableBase(tables.DataTable):
("CREATING", pgettext_lazy("Current status of share", u"Creating")),
("deleting", pgettext_lazy("Current status of share", u"Deleting")),
("DELETING", pgettext_lazy("Current status of share", u"Deleting")),
("migrating", pgettext_lazy("Current status of share", u"Migrating")),
("migrating_to", pgettext_lazy("Current status of share",
u"Migrating to")),
("error", pgettext_lazy("Current status of share", u"Error")),
("ERROR", pgettext_lazy("Current status of share", u"Error")),
("error_deleting", pgettext_lazy("Current status of share",

View File

@ -65,6 +65,8 @@
<dd>{{ share.created_at|parse_date }}</dd>
<dt>{% trans "Host" %}</dt>
<dd>{{ share.host }}</dd>
<dt>{% trans "Task state" %}</dt>
<dd>{{ share.task_state }}</dd>
</dl>
</div>

View File

@ -21,4 +21,5 @@ ADD_PANEL = 'manila_ui.dashboards.admin.shares.panel.Shares'
# various services provided by manila.
OPENSTACK_MANILA_FEATURES = {
'enable_replication': True,
'enable_migration': True,
}

View File

@ -220,3 +220,35 @@ class ManilaApiTests(base.APITestCase):
self.assertEqual(expected, result)
self.manilaclient.share_replicas.list.assert_called_once_with(share)
api.nova.availability_zone_list.assert_called_once_with(self.request)
def test_migration_start(self):
api.migration_start(self.request, 'fake_share', 'fake_host', False,
True, True, False, 'fake_net_id')
self.manilaclient.shares.migration_start.assert_called_once_with(
'fake_share',
host='fake_host',
force_host_assisted_migration=False,
nondisruptive=False,
writable=True,
preserve_metadata=True,
new_share_network_id='fake_net_id'
)
def test_migration_complete(self):
api.migration_complete(self.request, 'fake_share')
self.manilaclient.shares.migration_complete.assert_called_once_with(
'fake_share')
def test_migration_cancel(self):
api.migration_cancel(self.request, 'fake_share')
self.manilaclient.shares.migration_cancel.assert_called_once_with(
'fake_share')
def test_migration_get_progress(self):
api.migration_get_progress(self.request, 'fake_share')
(self.manilaclient.shares.migration_get_progress.
assert_called_once_with('fake_share'))

View File

@ -19,6 +19,7 @@ from django import forms as django_forms
from horizon import forms as horizon_forms
import mock
from manila_ui.api import manila as api
from manila_ui.dashboards.admin.shares import forms
from manila_ui.tests import helpers as base
@ -252,3 +253,67 @@ class ManilaDashboardsAdminSharesCreateShareTypeFormTests(base.APITestCase):
is_public=enable_public_share_type_creation)
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
class ManilaDashboardsAdminMigrationFormTests(base.APITestCase):
def setUp(self):
super(self.__class__, self).setUp()
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
self.request = wsgi.WSGIRequest(FAKE_ENVIRON)
def _get_initial(self):
initial = {'name': 'fake_name', 'share_id': 'fake_id'}
kwargs = {
'prefix': None,
'initial': initial,
}
return kwargs
@mock.patch('horizon.messages.success')
def test_migration_start(self, mock_horizon_messages_success):
form = forms.MigrationStart(self.request, **self._get_initial())
data = {
'force_host_assisted_migration': False,
'writable': True,
'preserve_metadata': True,
'nondisruptive': False,
'new_share_network_id': 'fake_net_id',
'host': 'fake_host',
}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
@mock.patch('horizon.messages.success')
def test_migration_complete(self, mock_horizon_messages_success):
form = forms.MigrationComplete(self.request, **self._get_initial())
result = form.handle(self.request, {})
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
@mock.patch('horizon.messages.success')
def test_migration_cancel(self, mock_horizon_messages_success):
form = forms.MigrationCancel(self.request, **self._get_initial())
result = form.handle(self.request, {})
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
@mock.patch('horizon.messages.success')
def test_migration_get_progress(self, mock_horizon_messages_success):
result = ({'Response': 200}, {'total_progress': 25})
self.mock_object(api, 'migration_get_progress',
mock.Mock(return_value=result))
form = forms.MigrationGetProgress(self.request, **self._get_initial())
result = form.handle(self.request, {})
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)

View File

@ -132,6 +132,124 @@ class SharesTests(test.BaseAdminViewTests):
mock.ANY, detailed=True, search_opts={'all_tenants': True})
self.assertRedirectsNoFollow(res, INDEX_URL)
@ddt.data(None, Exception('fake'))
def test_migration_start_post(self, exc):
share = test_data.share
url = reverse('horizon:admin:shares:migration_start',
args=[share.id])
formData = {
'share_id': share.id,
'name': share.name,
'host': 'fake_host',
'writable': True,
'preserve_metadata': True,
'force_host_assisted_migration': True,
'nondisruptive': True,
'new_share_network_id': 'fake_net_id',
}
self.mock_object(
api_manila, "share_get", mock.Mock(return_value=share))
self.mock_object(api_manila, "migration_start", mock.Mock(
side_effect=exc))
res = self.client.post(url, formData)
api_manila.share_get.assert_called_once_with(mock.ANY, share.id)
api_manila.migration_start.assert_called_once_with(
mock.ANY, formData['share_id'],
dest_host=formData['host'],
force_host_assisted_migration=(
formData['force_host_assisted_migration']),
writable=formData['writable'],
preserve_metadata=formData['preserve_metadata'],
nondisruptive=formData['nondisruptive'],
new_share_network_id=formData['new_share_network_id'])
status_code = 200 if exc else 302
self.assertEqual(res.status_code, status_code)
if not exc:
self.assertTemplateNotUsed(
res, 'admin/shares/migration_start.html')
self.assertRedirectsNoFollow(res, INDEX_URL)
else:
self.assertTemplateUsed(res, 'admin/shares/migration_start.html')
@ddt.data('migration_start', 'migration_cancel', 'migration_complete',
'migration_get_progress')
def test_migration_forms_open_form_successfully(self, method):
share = test_data.share
url = reverse('horizon:admin:shares:' + method, args=[share.id])
self.mock_object(
api_manila, "share_get", mock.Mock(return_value=share))
self.mock_object(api_manila, method)
res = self.client.get(url)
api_manila.share_get.assert_called_once_with(mock.ANY, share.id)
self.assertFalse(getattr(api_manila, method).called)
self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'admin/shares/' + method + '.html')
@ddt.data('migration_start', 'migration_cancel', 'migration_complete',
'migration_get_progress')
def test_migration_start_get_share_exception(self, method):
share = test_data.share
url = reverse('horizon:admin:shares:' + method, args=[share.id])
self.mock_object(
api_manila, "share_get", mock.Mock(side_effect=Exception('fake')))
self.mock_object(api_manila, method)
res = self.client.get(url)
api_manila.share_get.assert_called_once_with(mock.ANY, share.id)
self.assertFalse(getattr(api_manila, method).called)
self.assertEqual(res.status_code, 302)
self.assertTemplateNotUsed(res, 'admin/shares/' + method + '.html')
@ddt.data({'method': 'migration_complete', 'exc': None},
{'method': 'migration_complete', 'exc': Exception('fake')},
{'method': 'migration_cancel', 'exc': None},
{'method': 'migration_cancel', 'exc': Exception('fake')},
{'method': 'migration_get_progress',
'exc': {'response': 200, 'total_progress': 25}},
{'method': 'migration_get_progress', 'exc': Exception('fake')})
@ddt.unpack
def test_migration_forms_post(self, exc, method):
share = test_data.share
url = reverse('horizon:admin:shares:' + method,
args=[share.id])
formData = {
'share_id': share.id,
'name': share.name,
}
self.mock_object(
api_manila, "share_get", mock.Mock(return_value=share))
self.mock_object(api_manila, method, mock.Mock(
side_effect=exc))
res = self.client.post(url, formData)
api_manila.share_get.assert_called_once_with(mock.ANY, share.id)
getattr(api_manila, method).assert_called_once_with(
mock.ANY, formData['share_id'])
status_code = 200 if exc else 302
self.assertEqual(res.status_code, status_code)
if not exc:
self.assertTemplateNotUsed(
res, 'admin/shares/' + method + '.html')
self.assertRedirectsNoFollow(res, INDEX_URL)
else:
self.assertTemplateUsed(
res, 'admin/shares/' + method + '.html')
class ShareInstanceTests(test.BaseAdminViewTests):