Commit bfd76bee by Bach Dániel

dashboard: add non-live migration support

Closes #324
parent 709eec33
...@@ -752,6 +752,26 @@ class VmRenewForm(forms.Form): ...@@ -752,6 +752,26 @@ class VmRenewForm(forms.Form):
return helper return helper
class VmMigrateForm(forms.Form):
live_migration = forms.BooleanField(
required=False, initial=True, label=_("live migration"))
def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices')
default = kwargs.pop('default')
super(VmMigrateForm, self).__init__(*args, **kwargs)
self.fields.insert(0, 'to_node', forms.ModelChoiceField(
queryset=choices, initial=default, required=False,
widget=forms.RadioSelect(), label=_("Node")))
@property
def helper(self):
helper = FormHelper(self)
helper.form_tag = False
return helper
class VmStateChangeForm(forms.Form): class VmStateChangeForm(forms.Form):
interrupt = forms.BooleanField(required=False, label=_( interrupt = forms.BooleanField(required=False, label=_(
......
{% extends "dashboard/operate.html" %} {% extends "dashboard/operate.html" %}
{% load i18n %} {% load i18n %}
{% load sizefieldtags %} {% load sizefieldtags %}
{% load crispy_forms_tags %}
{% block question %} {% block question %}
<p> <p>
...@@ -13,8 +14,8 @@ Choose a compute node to migrate {{obj}} to. ...@@ -13,8 +14,8 @@ Choose a compute node to migrate {{obj}} to.
{% block formfields %} {% block formfields %}
<ul id="vm-migrate-node-list" class="list-unstyled"> <ul id="vm-migrate-node-list" class="list-unstyled">
{% with current=object.node.pk %} {% with current=object.node.pk recommended=form.fields.to_node.initial %}
{% for n in nodes %} {% for n in form.fields.to_node.queryset.all %}
<li class="panel panel-default"><div class="panel-body"> <li class="panel panel-default"><div class="panel-body">
<label for="migrate-to-{{n.pk}}"> <label for="migrate-to-{{n.pk}}">
<strong>{{ n }}</strong> <strong>{{ n }}</strong>
...@@ -23,7 +24,7 @@ Choose a compute node to migrate {{obj}} to. ...@@ -23,7 +24,7 @@ Choose a compute node to migrate {{obj}} to.
{% if current == n.pk %}<div class="label label-info">{% trans "current" %}</div>{% endif %} {% if current == n.pk %}<div class="label label-info">{% trans "current" %}</div>{% endif %}
{% if recommended == n.pk %}<div class="label label-success">{% trans "recommended" %}</div>{% endif %} {% if recommended == n.pk %}<div class="label label-success">{% trans "recommended" %}</div>{% endif %}
</label> </label>
<input id="migrate-to-{{n.pk}}" type="radio" name="node" value="{{ n.pk }}" style="float: right;" <input id="migrate-to-{{n.pk}}" type="radio" name="to_node" value="{{ n.pk }}" style="float: right;"
{% if current == n.pk %}disabled="disabled"{% endif %} {% if current == n.pk %}disabled="disabled"{% endif %}
{% if recommended == n.pk %}checked="checked"{% endif %} /> {% if recommended == n.pk %}checked="checked"{% endif %} />
<span class="vm-migrate-node-property">{% trans "CPU load" %}: {{ n.cpu_usage }}</span> <span class="vm-migrate-node-property">{% trans "CPU load" %}: {{ n.cpu_usage }}</span>
...@@ -33,4 +34,5 @@ Choose a compute node to migrate {{obj}} to. ...@@ -33,4 +34,5 @@ Choose a compute node to migrate {{obj}} to.
{% endfor %} {% endfor %}
{% endwith %} {% endwith %}
</ul> </ul>
{{ form.live_migration|as_crispy_field }}
{% endblock %} {% endblock %}
...@@ -60,6 +60,7 @@ from ..forms import ( ...@@ -60,6 +60,7 @@ from ..forms import (
VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm, VmSaveForm, VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm, VmSaveForm,
VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm, VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm,
TransferOwnershipForm, VmDiskResizeForm, RedeployForm, VmDiskRemoveForm, TransferOwnershipForm, VmDiskResizeForm, RedeployForm, VmDiskRemoveForm,
VmMigrateForm,
) )
from ..models import Favourite, Profile from ..models import Favourite, Profile
...@@ -421,36 +422,28 @@ class VmDownloadDiskView(FormOperationMixin, VmOperationView): ...@@ -421,36 +422,28 @@ class VmDownloadDiskView(FormOperationMixin, VmOperationView):
with_reload = True with_reload = True
class VmMigrateView(VmOperationView): class VmMigrateView(FormOperationMixin, VmOperationView):
op = 'migrate' op = 'migrate'
icon = 'truck' icon = 'truck'
effect = 'info' effect = 'info'
template_name = 'dashboard/_vm-migrate.html' template_name = 'dashboard/_vm-migrate.html'
form_class = VmMigrateForm
def get_context_data(self, **kwargs): def get_form_kwargs(self):
ctx = super(VmMigrateView, self).get_context_data(**kwargs) online = (n.pk for n in Node.objects.filter(enabled=True) if n.online)
ctx['nodes'] = [n for n in Node.objects.filter(enabled=True) choices = Node.objects.filter(pk__in=online)
if n.online] default = None
inst = self.get_object() inst = self.get_object()
ctx["recommended"] = None
try: try:
if isinstance(inst, Instance): if isinstance(inst, Instance):
ctx["recommended"] = inst.select_node().pk default = inst.select_node().pk
except SchedulerError: except SchedulerError:
logger.exception("scheduler error:") logger.exception("scheduler error:")
return ctx val = super(VmMigrateView, self).get_form_kwargs()
val.update({'choices': choices, 'default': default})
def post(self, request, extra=None, *args, **kwargs): return val
if extra is None:
extra = {}
node = self.request.POST.get("node")
if node:
node = get_object_or_404(Node, pk=node)
extra["to_node"] = node
return super(VmMigrateView, self).post(request, extra, *args, **kwargs)
class VmSaveView(FormOperationMixin, VmOperationView): class VmSaveView(FormOperationMixin, VmOperationView):
......
...@@ -472,10 +472,9 @@ class MigrateOperation(RemoteInstanceOperation): ...@@ -472,10 +472,9 @@ class MigrateOperation(RemoteInstanceOperation):
remote_queue = ("vm", "slow") remote_queue = ("vm", "slow")
timeout = 600 timeout = 600
def _get_remote_args(self, to_node, **kwargs): def _get_remote_args(self, to_node, live_migration, **kwargs):
return (super(MigrateOperation, self)._get_remote_args(**kwargs) return (super(MigrateOperation, self)._get_remote_args(**kwargs)
+ [to_node.host.hostname, True]) + [to_node.host.hostname, live_migration])
# TODO handle non-live migration
def rollback(self, activity): def rollback(self, activity):
with activity.sub_activity( with activity.sub_activity(
...@@ -483,7 +482,7 @@ class MigrateOperation(RemoteInstanceOperation): ...@@ -483,7 +482,7 @@ class MigrateOperation(RemoteInstanceOperation):
"redeploy network (rollback)")): "redeploy network (rollback)")):
self.instance.deploy_net() self.instance.deploy_net()
def _operation(self, activity, to_node=None): def _operation(self, activity, to_node=None, live_migration=True):
if not to_node: if not to_node:
with activity.sub_activity('scheduling', with activity.sub_activity('scheduling',
readable_name=ugettext_noop( readable_name=ugettext_noop(
...@@ -495,7 +494,8 @@ class MigrateOperation(RemoteInstanceOperation): ...@@ -495,7 +494,8 @@ class MigrateOperation(RemoteInstanceOperation):
with activity.sub_activity( with activity.sub_activity(
'migrate_vm', readable_name=create_readable( 'migrate_vm', readable_name=create_readable(
ugettext_noop("migrate to %(node)s"), node=to_node)): ugettext_noop("migrate to %(node)s"), node=to_node)):
super(MigrateOperation, self)._operation(to_node=to_node) super(MigrateOperation, self)._operation(
to_node=to_node, live_migration=live_migration)
except Exception as e: except Exception as e:
if hasattr(e, 'libvirtError'): if hasattr(e, 'libvirtError'):
self.rollback(activity) self.rollback(activity)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment