Commit f4df866a by Kálmán Viktor

dashboard: warn user about missing traits on migrate/deploy

parent e70e03b7
...@@ -41,6 +41,7 @@ from django.forms.widgets import TextInput, HiddenInput ...@@ -41,6 +41,7 @@ from django.forms.widgets import TextInput, HiddenInput
from django.template import Context from django.template import Context
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import escape, format_html from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from sizefield.widgets import FileSizeWidget from sizefield.widgets import FileSizeWidget
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
...@@ -951,18 +952,45 @@ class VmAddInterfaceForm(OperationForm): ...@@ -951,18 +952,45 @@ class VmAddInterfaceForm(OperationForm):
self.fields['vlan'] = field self.fields['vlan'] = field
class DeployChoiceField(forms.ModelChoiceField):
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop("instance")
super(DeployChoiceField, self).__init__(*args, **kwargs)
def label_from_instance(self, obj):
traits = set(obj.traits.all())
req_traits = set(self.instance.req_traits.all())
# if the subset is empty the node satisfies the required traits
subset = req_traits - traits
label = "%s %s" % (
"&#xf071" if subset else "", escape(obj.name),
)
if subset:
missing_traits = ", ".join(map(lambda x: escape(x.name), subset))
label += _(" (missing_traits: %s)") % missing_traits
return mark_safe(label)
class VmDeployForm(OperationForm): class VmDeployForm(OperationForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices', None) choices = kwargs.pop('choices', None)
instance = kwargs.pop("instance")
super(VmDeployForm, self).__init__(*args, **kwargs) super(VmDeployForm, self).__init__(*args, **kwargs)
if choices is not None: if choices is not None:
self.fields.insert(0, 'node', forms.ModelChoiceField( self.fields.insert(0, 'node', DeployChoiceField(
queryset=choices, required=False, label=_('Node'), help_text=_( queryset=choices, required=False, label=_('Node'), help_text=_(
"Deploy virtual machine to this node " "Deploy virtual machine to this node "
"(blank allows scheduling automatically)."))) "(blank allows scheduling automatically)."),
widget=forms.Select(attrs={
'class': "font-awesome-font",
}), instance=instance
))
class VmPortRemoveForm(OperationForm): class VmPortRemoveForm(OperationForm):
......
...@@ -23,11 +23,35 @@ Choose a compute node to migrate {{obj}} to. ...@@ -23,11 +23,35 @@ Choose a compute node to migrate {{obj}} to.
<i class="fa {{n.get_status_icon}}"></i> {{n.get_status_display}}</div> <i class="fa {{n.get_status_icon}}"></i> {{n.get_status_display}}</div>
{% 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 %}
{% if n.pk not in nodes_w_traits %}
<div class="label label-warning">
<i class="fa fa-warning"></i>
{% trans "missing traits" %}</div>
{% endif %}
</label> </label>
<input id="migrate-to-{{n.pk}}" type="radio" name="to_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 %}
/> />
{% if n.pk not in nodes_w_traits %}
<span class="vm-migrate-node-property">
{% trans "Node traits" %}:
{% if n.traits.all %}
{{ n.traits.all|join:", " }}
{% else %}
-
{% endif %}
</span>
<span class="vm-migrate-node-property">
{% trans "Required traits" %}:
{% if object.req_traits.all %}
{{ object.req_traits.all|join:", " }}
{% else %}
-
{% endif %}
</span>
<hr />
{% 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>
<span class="vm-migrate-node-property"> <span class="vm-migrate-node-property">
{% trans "RAM usage" %}: {{ n.byte_ram_usage|filesize }}/{{ n.ram_size|filesize }}</span> {% trans "RAM usage" %}: {{ n.byte_ram_usage|filesize }}/{{ n.ram_size|filesize }}</span>
......
...@@ -67,6 +67,7 @@ from ..forms import ( ...@@ -67,6 +67,7 @@ from ..forms import (
VmRemoveInterfaceForm, VmRemoveInterfaceForm,
) )
from ..models import Favourite from ..models import Favourite
from manager.scheduler import has_traits
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -444,6 +445,20 @@ class VmMigrateView(FormOperationMixin, VmOperationView): ...@@ -444,6 +445,20 @@ class VmMigrateView(FormOperationMixin, VmOperationView):
val.update({'choices': choices, 'default': default}) val.update({'choices': choices, 'default': default})
return val return val
def get_context_data(self, *args, **kwargs):
ctx = super(VmMigrateView, self).get_context_data(*args, **kwargs)
inst = self.get_object()
if isinstance(inst, Instance):
nodes_w_traits = [
n.pk for n in Node.objects.filter(enabled=True)
if n.online and
has_traits(inst.req_traits.all(), n)
]
ctx['nodes_w_traits'] = nodes_w_traits
return ctx
class VmPortRemoveView(FormOperationMixin, VmOperationView): class VmPortRemoveView(FormOperationMixin, VmOperationView):
...@@ -698,6 +713,7 @@ class VmDeployView(FormOperationMixin, VmOperationView): ...@@ -698,6 +713,7 @@ class VmDeployView(FormOperationMixin, VmOperationView):
online = (n.pk for n in online = (n.pk for n in
Node.objects.filter(enabled=True) if n.online) Node.objects.filter(enabled=True) if n.online)
kwargs['choices'] = Node.objects.filter(pk__in=online) kwargs['choices'] = Node.objects.filter(pk__in=online)
kwargs['instance'] = self.get_object()
return kwargs return kwargs
......
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