Commit d3945c0d by Gelencsér Szabolcs

New dashboard concept. #TODO: implement generateVmHTML in dashboard.js.…

New dashboard concept. #TODO: implement generateVmHTML in dashboard.js. (searching for a vm ruins the vm list currently)
parent dad93220
...@@ -30,6 +30,7 @@ $(function() { ...@@ -30,6 +30,7 @@ $(function() {
/* operations */ /* operations */
$('#ops, #vm-details-resources-disk, #vm-details-renew-op, #vm-details-pw-reset, #vm-details-add-interface, .operation-wrapper').on('click', '.operation', function(e) { $('#ops, #vm-details-resources-disk, #vm-details-renew-op, #vm-details-pw-reset, #vm-details-add-interface, .operation-wrapper').on('click', '.operation', function(e) {
var icon = $(this).children("i").addClass('fa-spinner fa-spin'); var icon = $(this).children("i").addClass('fa-spinner fa-spin');
var redirect_on_success = $(this).data("redirect-on-success");
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
...@@ -42,6 +43,10 @@ $(function() { ...@@ -42,6 +43,10 @@ $(function() {
$('#confirmation-modal').remove(); $('#confirmation-modal').remove();
}); });
$('#vm-migrate-node-list li input:checked').closest('li').addClass('panel-primary'); $('#vm-migrate-node-list li input:checked').closest('li').addClass('panel-primary');
if(redirect_on_success) {
var submit_button = $('#confirmation-modal button[type=submit]')
submit_button.data("redirect-on-success", redirect_on_success);
}
} }
}); });
e.preventDefault(); e.preventDefault();
...@@ -50,6 +55,7 @@ $(function() { ...@@ -50,6 +55,7 @@ $(function() {
/* if the operation fails show the modal again */ /* if the operation fails show the modal again */
$("body").on("click", "#confirmation-modal #op-form-send", function() { $("body").on("click", "#confirmation-modal #op-form-send", function() {
var url = $(this).closest("form").prop("action"); var url = $(this).closest("form").prop("action");
var redirect_on_success = $(this).data("redirect-on-success");
$.ajax({ $.ajax({
url: url, url: url,
...@@ -75,6 +81,10 @@ $(function() { ...@@ -75,6 +81,10 @@ $(function() {
if(data.messages && data.messages.length > 0) { if(data.messages && data.messages.length > 0) {
addMessage(data.messages.join("<br />"), data.success ? "success" : "danger"); addMessage(data.messages.join("<br />"), data.success ? "success" : "danger");
} }
if(redirect_on_success) {
window.location.href = redirect_on_success;
}
} }
else { else {
/* if the post was not successful wait for the modal to disappear /* if the post was not successful wait for the modal to disappear
......
...@@ -98,6 +98,7 @@ $(function () { ...@@ -98,6 +98,7 @@ $(function () {
return false; return false;
}); });
$('body .dashboard-vm-collapse-caret').tooltip({'placement': 'right'});
$('body .title-favourite').tooltip({'placement': 'right'}); $('body .title-favourite').tooltip({'placement': 'right'});
$('body :input[title]').tooltip({trigger: 'focus', placement: 'auto right'}); $('body :input[title]').tooltip({trigger: 'focus', placement: 'auto right'});
$('body [title]').tooltip(); $('body [title]').tooltip();
...@@ -121,6 +122,54 @@ $(function () { ...@@ -121,6 +122,54 @@ $(function () {
$('.no-js-hidden').show(); $('.no-js-hidden').show();
$('.js-hidden').hide(); $('.js-hidden').hide();
/* dropdown caret */
$("#dashboard-vm-list").on('click', '.dashboard-vm-collapse-caret', function(e) {
var isCollapsed = $(this).hasClass("fa-caret-down");
var pk = $(this).data("pk");
var collapseBody = $("#dashboard-vm-collapse-body-" + pk);
if(isCollapsed)
{
$(this).removeClass("fa-caret-down").addClass("fa-caret-up");
collapseBody.removeClass("dashboard-vm-collapse-body-collapsed");
}
else
{
$(this).removeClass("fa-caret-up").addClass("fa-caret-down");
collapseBody.addClass("dashboard-vm-collapse-body-collapsed");
}
return false;
});
/* select input's text on focus for easy copy */
$("#dashboard-vm-list").on('focus', 'input', function(e) {
$(this).select();
return false;
});
/* show password button */
$("#dashboard-vm-list").on('click', '.show-password', function(e) {
var passwordInput = $(this).siblings(".password-input");
var inputType = passwordInput.prop("type");
var icon = $(this).children('i');
if(inputType == "password")
{
passwordInput.prop("type", "text").focus();
icon.removeClass("fa-eye").addClass("fa-eye-slash");
}
else
{
passwordInput.prop("type", "password");
icon.removeClass("fa-eye-slash").addClass("fa-eye");
}
return false;
});
/* favourite star */ /* favourite star */
$("#dashboard-vm-list, .page-header").on('click', '.dashboard-vm-favourite', function(e) { $("#dashboard-vm-list, .page-header").on('click', '.dashboard-vm-favourite', function(e) {
var star = $(this).children("i"); var star = $(this).children("i");
......
...@@ -197,10 +197,6 @@ html { ...@@ -197,10 +197,6 @@ html {
text-align: center; text-align: center;
} }
.dashboard-index .panel {
height: 294px;
}
#vm-details-rename, #vm-details-h1-name, #vm-details-rename , #vm-details-rename, #vm-details-h1-name, #vm-details-rename ,
#node-details-rename, #node-details-rename *, #node-details-h1-name, #node-list-rename, #node-list-rename *#group-details-rename, #group-details-rename *, #group-details-h1-name, #group-list-rename, #group-list-rename * { #node-details-rename, #node-details-rename *, #node-details-h1-name, #node-list-rename, #node-list-rename *#group-details-rename, #group-details-rename *, #group-details-h1-name, #group-list-rename, #group-list-rename * {
...@@ -584,8 +580,12 @@ footer a, footer a:hover, footer a:visited { ...@@ -584,8 +580,12 @@ footer a, footer a:hover, footer a:visited {
margin: 0; margin: 0;
} }
#dashboard-vm-list, #dashboard-node-list, #dashboard-group-list, #dashboard-vm-list, #dashboard-group-list {
#dashboard-template-list, #dashboard-files-toplist, #dashboard-user-list { min-height: 254px;
}
#dashboard-node-list, #dashboard-template-list,
#dashboard-files-toplist, #dashboard-user-list {
min-height: 200px; min-height: 200px;
} }
...@@ -1175,6 +1175,40 @@ textarea[name="new_members"] { ...@@ -1175,6 +1175,40 @@ textarea[name="new_members"] {
#dashboard-vm-list { #dashboard-vm-list {
.list-group-item { .list-group-item {
display: flex; display: flex;
margin-bottom: -7px;
}
.dashboard-vm-collapse-body {
padding: 10px 15px;
.list-group-item {
display: block;
}
.form-group {
margin: 0;
}
}
.dashboard-vm-collapse-caret {
cursor: pointer;
font-size: large;
position: relative;
top: 2px;
}
.dashboard-vm-collapse-body-collapsed {
height: 0;
padding: 0;
visibility: hidden;
margin: 0;
* {
height: 0;
visibility: hidden;
padding: 0;
margin: 0;
}
} }
.index-vm-list-name, .index-vm-list-host { .index-vm-list-name, .index-vm-list-host {
...@@ -1191,8 +1225,32 @@ textarea[name="new_members"] { ...@@ -1191,8 +1225,32 @@ textarea[name="new_members"] {
padding-top: 3px; padding-top: 3px;
flex: 1; flex: 1;
} }
}
.input-xs {
height: 22px;
padding: 2px 8px;
font-size: 11px;
}
.label-xs {
font-size: 11px;
position: relative;
top: -5px;
}
.multiline-list-group-item-fix {
display: block;
}
.vm-multiline-info {
font-size: 11.9px;
margin-left: 20px;
}
.pull-right-center-fix {
margin-top: 4px;
}
}
#dashboard-user-list { #dashboard-user-list {
.list-group-item { .list-group-item {
......
...@@ -2,14 +2,6 @@ ...@@ -2,14 +2,6 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<div class="pull-right toolbar"> <div class="pull-right toolbar">
<div class="btn-group">
<a href="#index-graph-view" data-index-box="vm" class="btn btn-default btn-xs"
data-container="body"
title="{% trans "summary view" %}"><i class="fa fa-dashboard"></i></a>
<a href="#index-list-view" data-index-box="vm" class="btn btn-default btn-xs disabled"
data-container="body"
title="{% trans "list view" %}"><i class="fa fa-list"></i></a>
</div>
<span class="btn btn-default btn-xs infobtn" data-container="body" title="{% trans "List of your current virtual machines. Favourited ones are ahead of others." %}"><i class="fa fa-info-circle"></i></span> <span class="btn btn-default btn-xs infobtn" data-container="body" title="{% trans "List of your current virtual machines. Favourited ones are ahead of others." %}"><i class="fa fa-info-circle"></i></span>
</div> </div>
<h3 class="no-margin"> <h3 class="no-margin">
...@@ -27,24 +19,101 @@ ...@@ -27,24 +19,101 @@
<div id="dashboard-vm-list"> <div id="dashboard-vm-list">
{% for i in instances %} {% for i in instances %}
<a href="{{ i.get_absolute_url }}" class="list-group-item <a href="{{ i.get_absolute_url }}" class="list-group-item
{% if forloop.last and instances|length < 5 %} list-group-item-last{% endif %}"> {% if forloop.last and instances|length < 5 %} list-group-item-last{% endif %} multiline-list-group-item-fix">
<span class="index-vm-list-name"> <div>
<i class="fa {{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i> <span class="index-vm-list-name">
{{ i.name }} <i class="fa {{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i>
</span> {{ i.name }}
<small class="text-muted index-vm-list-host"> </span>
{% if i.owner == request.user %}{{ i.short_hostname }} <small class="text-muted index-vm-list-host">
{% else %}{{i.owner.profile.get_display_name}}{% endif %} {% if i.owner == request.user %}{{ i.short_hostname }}
</small> {% else %}{{i.owner.profile.get_display_name}}{% endif %}
<div class="pull-right dashboard-vm-favourite" data-vm="{{ i.pk }}"> </small>
{% if i.fav %} <div class="pull-right operation-wrapper pull-right-center-fix">
<i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i> <!-- Preferred operation -->
{% else %} <span
<i class="fa fa-star-o text-primary title-favourite" title="{% trans "Mark as favorite" %}"></i> href="{{i.pref_operation.get_url}}"
{% endif %} data-redirect-on-success="vm/{{ i.pk }}"
class="operation operation-{{i.pref_operation.op}} btn btn-{{i.pref_operation.effect}} btn-xs"
title="{{i.pref_operation.name|capfirst}}: {{i.pref_operation.description|truncatewords:15}}">
<i class="fa fa-{{i.pref_operation.icon}}"></i>
<span>{{i.pref_operation.name}}</span>
</span>
<!-- (Un)Favourite -->
<span class="dashboard-vm-favourite" data-vm="{{ i.pk }}">
{% if i.fav %}
<i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i>
{% else %}
<i class="fa fa-star-o text-primary title-favourite" title="{% trans "Mark as favorite" %}"></i>
{% endif %}
</span>
<!-- Collapse's caret -->
<i class="fa fa-caret-down dashboard-vm-collapse-caret" data-pk="{{ i.pk }}" title="{% trans "IndexVmDropDown" %}"></i>
</div>
</div> </div>
<div style="clear: both;"></div> <div class="text-muted vm-multiline-info">
RAM: {{ i.ram_size }}, CPU: {{ i.num_cores }}, IP: {{ i.get_connect_host }}
</div>
<div style="clear: both;"></div>
</a> </a>
<div id="dashboard-vm-collapse-body-{{ i.pk }}" class="dashboard-vm-collapse-body dashboard-vm-collapse-body-collapsed">
<form class="form-horizontal">
<!-- IPv4 -->
<div class="form-group">
<label for="connectionInfoIpv4-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "IPv4" %}</label>
<div class="col-sm-9">
{% if i.get_connect_port %}
<input type="text" class="form-control input-xs" id="connectionInfoIpv4-{{ i.pk }}"
value="cloud@{{ i.get_connect_host }}:{{ i.get_connect_port }}">
{% elif i.interface_set.count < 1%}
<input type="text" class="form-control input-xs" disabled id="connectionInfoIpv4-{{ i.pk }}"
value="{% trans "The VM doesn't have any network interface." %}">
{% else %}
<input type="text" class="form-control input-xs" disabled id="connectionInfoIpv4-{{ i.pk }}"
value="{% trans "The required port for this protocol is not forwarded." %}">
{% endif %}
</div>
</div>
<!-- IPv6 -->
{% if i.ipv6 and i.get_connect_port %}
<div class="form-group">
<label for="connectionInfoIpv6-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "IPv6" %}</label>
<div class="col-sm-9">
<input type="text" class="form-control input-xs" id="connectionInfoIpv6-{{ i.pk }}"
value="cloud@{{ i.ipv6 }}:{{ i.get_connect_port }}">
</div>
</div>
{% endif %}
<!-- Password -->
<div class="form-group">
<label for="password-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "Password" %}</label>
<div class=" col-sm-9">
<div class="input-group" id="password-{{ i.pk }}">
<input type="password" class="password-input form-control input-xs"
value="{{ i.pw }}" spellcheck="false"/>
<span class="show-password input-group-addon btn input-xs"
title="{% trans "Show password" %}">
<i class="fa fa-eye"></i>
</span>
</div>
</div>
</div>
<!-- Command -->
{% for command in i.connect_commands %}
<div class="form-group">
<label for="command-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "Command" %}</label>
<div class="col-sm-9">
<input type="text" class="form-control input-xs" id="command-{{ i.pk }}"
value="{{ command }}">
</div>
</div>
{% endfor %}
</form>
</div>
{% empty %} {% empty %}
<div class="list-group-item list-group-item-last"> <div class="list-group-item list-group-item-last">
{% trans "You have no virtual machines." %} {% trans "You have no virtual machines." %}
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<div class="row"> <div class="row">
{% if perms.vm %} {% if perms.vm %}
<div class="col-lg-4 col-sm-6"> <div class="col-lg-6 col-sm-6">
{% include "dashboard/index-vm.html" %} {% include "dashboard/index-vm.html" %}
</div> </div>
{% else %} {% else %}
...@@ -25,18 +25,22 @@ ...@@ -25,18 +25,22 @@
</div> </div>
{% endif %} {% endif %}
{% if perms.auth %} {% if perms.auth %}
<div class="col-lg-4 col-sm-6"> <div class="col-lg-6 col-sm-6">
{% include "dashboard/index-groups.html" %} {% include "dashboard/index-groups.html" %}
</div> </div>
{% endif %} {% endif %}
</div>
<div class="row">
{% if not no_store %} {% if not no_store %}
<div class="col-lg-4 col-sm-6"> <div class="col-lg-4 col-sm-6">
{% include "dashboard/store/index-files.html" %} {% include "dashboard/store/index-files.html" %}
</div> </div>
{% endif %} {% endif %}
{% if perms.vm.create_template %} {% if perms.vm.create_template %}
<div class="col-lg-4 col-sm-6"> <div class="col-lg-4 col-sm-6">
{% include "dashboard/index-templates.html" %} {% include "dashboard/index-templates.html" %}
......
...@@ -20,6 +20,7 @@ import logging ...@@ -20,6 +20,7 @@ import logging
from django.core.cache import get_cache from django.core.cache import get_cache
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.views.generic import TemplateView from django.views.generic import TemplateView
...@@ -34,6 +35,21 @@ from ..store_api import Store ...@@ -34,6 +35,21 @@ from ..store_api import Store
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_operations(instance, user):
ops = []
for k, v in vm_ops.iteritems():
try:
op = v.get_op_by_object(instance)
op.check_auth(user)
op.check_precond()
except PermissionDenied as e:
logger.debug('Not showing operation %s for %s: %s',
k, instance, unicode(e))
except Exception:
ops.append(v.bind_to_object(instance, disabled=True))
else:
ops.append(v.bind_to_object(instance))
return ops
class IndexView(LoginRequiredMixin, TemplateView): class IndexView(LoginRequiredMixin, TemplateView):
template_name = "dashboard/index.html" template_name = "dashboard/index.html"
...@@ -49,9 +65,16 @@ class IndexView(LoginRequiredMixin, TemplateView): ...@@ -49,9 +65,16 @@ class IndexView(LoginRequiredMixin, TemplateView):
display = list(favs) + list(set(instances) - set(favs)) display = list(favs) + list(set(instances) - set(favs))
for d in display: for d in display:
d.fav = True if d in favs else False d.fav = True if d in favs else False
for i_pos in range(0, min(instances.count(), 5)):
instances[i_pos].connect_commands = user.profile.get_connect_commands(instances[i_pos])
operations = get_operations(instances[i_pos], user)
instances[i_pos].pref_operation = next(oper for oper in operations \
if oper.is_preferred and not hasattr(oper, 'disabled') )
context.update({ context.update({
'instances': display[:5], 'instances': display[:5],
'more_instances': instances.count() - len(instances[:5]) 'more_instances': instances.count() - len(instances[:5]),
}) })
running = instances.filter(status='RUNNING') running = instances.filter(status='RUNNING')
......
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