Commit c5f2291b by Kálmán Viktor

Merge branch 'master' into feature-jshint

Conflicts:
	circle/dashboard/static/dashboard/group-details.js
parents f47965c1 bd898707
......@@ -41,6 +41,7 @@ from django.forms.widgets import TextInput, HiddenInput
from django.template import Context
from django.template.loader import render_to_string
from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from sizefield.widgets import FileSizeWidget
from django.core.urlresolvers import reverse_lazy
......@@ -951,18 +952,45 @@ class VmAddInterfaceForm(OperationForm):
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):
def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices', None)
instance = kwargs.pop("instance")
super(VmDeployForm, self).__init__(*args, **kwargs)
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=_(
"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):
......
......@@ -188,11 +188,15 @@ html {
display: none;
}
.vm-details-home-name {
#group-details-rename-form {
display: inline-block;
}
.vm-details-home-name, #group-details-rename-form .input-group {
max-width: 401px;
}
#node-details-rename-name, #group-details-rename-name {
#node-details-rename-name {
max-width: 160px;
}
......
$(function() {
/* rename */
$("#group-details-h1-name, .group-details-rename-button").click(function() {
$("#group-details-h1-name").hide();
$("#group-details-rename").css('display', 'inline');
$("#group-details-rename-name").focus();
$("#group-details-h1-name span").hide();
$("#group-details-rename-form").show().css('display', 'inline-block');
$("#group-details-rename-name").select();
});
/* rename ajax */
$('#group-details-rename-submit').click(function() {
if(!$("#group-details-rename-name")[0].checkValidity()) {
return true;
}
var name = $('#group-details-rename-name').val();
$.ajax({
method: 'POST',
url: location.href,
data: {'new_name': name},
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(data, textStatus, xhr) {
$("#group-details-h1-name").text(data.new_name).show();
$('#group-details-rename').hide();
// addMessage(data['message'], "success");
$("#group-details-h1-name span").text(data.new_name).show();
$('#group-details-rename-form').hide();
},
error: function(xhr, textStatus, error) {
addMessage("Error during renaming!", "danger");
addMessage("Error during renaming.", "danger");
}
});
return false;
});
$(".group-details-help-button").click(function() {
$(".group-details-help").stop().slideToggle();
});
});
......@@ -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>
{% 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 n.pk not in nodes_w_traits %}
<div class="label label-warning">
<i class="fa fa-warning"></i>
{% trans "missing traits" %}</div>
{% endif %}
</label>
<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 recommended == n.pk %}checked="checked"{% endif %}
{% if recommended == n.pk and n.pk != current %}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 "RAM usage" %}: {{ n.byte_ram_usage|filesize }}/{{ n.ram_size|filesize }}</span>
......
......@@ -9,43 +9,33 @@
<div class="body-content">
<div class="page-header">
<div class="pull-right" style="padding-top: 15px;">
<a title="{% trans "Rename" %}" href="#" class="btn btn-default btn-xs group-details-rename-button">
<a title="{% trans "Rename" %}" class="btn btn-default btn-xs group-details-rename-button">
<i class="fa fa-pencil"></i>
</a>
<a title="{% trans "Delete" %}" data-group-pk="{{ group.pk }}" class="btn btn-default btn-xs real-link group-delete" href="{% url "dashboard.views.delete-group" pk=group.pk %}">
<i class="fa fa-trash-o"></i>
</a>
<a title="{% trans "Help" %}" href="#" class="btn btn-default btn-xs group-details-help-button">
<i class="fa fa-question"></i>
</a>
</div>
<h1>
<div id="group-details-rename">
<form action="" method="POST" id="group-details-rename-form">
<form action="" method="POST" id="group-details-rename-form" class="js-hidden">
{% csrf_token %}
<input id="group-details-rename-name" class="form-control" name="new_name" type="text" value="{{ group.name }}"/>
<button type="submit" id="group-details-rename-submit" class="btn">{% trans "Rename" %}</button>
</form>
<div class="input-group">
<input id="group-details-rename-name" class="form-control" name="new_name"
type="text" value="{{ group.name }}" required />
<span class="input-group-btn">
<button type="submit" id="group-details-rename-submit" class="btn">
{% trans "Rename" %}
</button>
</span>
</div>
</form>
<div id="group-details-h1-name">
{{ group.name }}
<span class="no-js-hidden">{{ group.name }}</span>
{% if group.groupprofile.org_id %}
<small>{{group.groupprofile.org_id}}</small>
{% endif %}
</div>
</h1>
<div class="group-details-help js-hidden">
<ul style="list-style: none;">
<li>
<strong>{% trans "Rename" %}:</strong>
{% trans "Change the name of the group." %}
</li>
<li>
<strong>{% trans "Delete" %}:</strong>
{% trans "Delete group." %}
</li>
</ul>
</div>
</div><!-- .page-header -->
<div class="row">
<div class="col-md-12" id="group-detail-pane">
......
......@@ -73,7 +73,7 @@
</a>
</li>
<li>
<a href="{% url "dashboard.views.vm-list" %}?s=node:{{ node.name }}"
<a href="{% url "dashboard.views.vm-list" %}?s=node_exact:{{ node.name }}"
target="blank" class="text-center">
<i class="fa fa-desktop fa-2x"></i><br>
{% trans "Virtual Machines" %}
......
......@@ -143,8 +143,13 @@ class NodeDetailView(LoginRequiredMixin,
def __remove_trait(self, request):
try:
to_remove = request.POST.get('to_remove')
self.object = self.get_object()
self.object.traits.remove(to_remove)
trait = Trait.objects.get(pk=to_remove)
node = self.get_object()
node.traits.remove(to_remove)
if not trait.in_use:
trait.delete()
message = u"Success"
except: # note this won't really happen
message = u"Not success"
......@@ -155,7 +160,7 @@ class NodeDetailView(LoginRequiredMixin,
content_type="application/json"
)
else:
return redirect(self.object.get_absolute_url())
return redirect(node.get_absolute_url())
class NodeList(LoginRequiredMixin, GraphMixin, SingleTableView):
......
......@@ -67,6 +67,7 @@ from ..forms import (
VmRemoveInterfaceForm,
)
from ..models import Favourite
from manager.scheduler import has_traits
logger = logging.getLogger(__name__)
......@@ -444,6 +445,20 @@ class VmMigrateView(FormOperationMixin, VmOperationView):
val.update({'choices': choices, 'default': default})
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):
......@@ -698,6 +713,7 @@ class VmDeployView(FormOperationMixin, VmOperationView):
online = (n.pk for n in
Node.objects.filter(enabled=True) if n.online)
kwargs['choices'] = Node.objects.filter(pk__in=online)
kwargs['instance'] = self.get_object()
return kwargs
......
......@@ -170,3 +170,10 @@ class Trait(Model):
def __unicode__(self):
return self.name
@property
def in_use(self):
return (
self.instance_set.exists() or self.node_set.exists()
or self.instancetemplate_set.exists()
)
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