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:
parent
25eacc26bc
commit
6025e5a96c
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 = (
|
||||
|
@ -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 %}
|
@ -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 %}
|
@ -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 %}
|
@ -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 %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Cancel Migration" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/shares/_migration_cancel.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Complete Migration" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/shares/_migration_complete.html' %}
|
||||
{% endblock %}
|
@ -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 %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Migrate Share" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'admin/shares/_migration_start.html' %}
|
||||
{% endblock %}
|
@ -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'),
|
||||
])
|
||||
|
@ -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'
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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'))
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user