Commit ae77d3c8 by Gregory Nagy

Merge

parents b1f4a5e6 e1795208
......@@ -19,7 +19,6 @@ from firewall.models import Vlan, Host
from storage.models import Disk, DataStore
from vm.models import InstanceTemplate, Lease, InterfaceTemplate, Node
VLANS = Vlan.objects.all()
DISKS = Disk.objects.exclude(type="qcow2-snap")
......@@ -554,7 +553,6 @@ class TemplateForm(forms.ModelForm):
Field('description'),
Field("parent", type="hidden"),
Field("system"),
Field("state"),
),
Fieldset(
_("Exeternal"),
......@@ -569,6 +567,7 @@ class TemplateForm(forms.ModelForm):
class Meta:
model = InstanceTemplate
exclude = ('state', )
class LeaseForm(forms.ModelForm):
......
......@@ -114,6 +114,10 @@ body {
min-height: 20em;
}
#group-detail-panel .panel-body {
min-height: 20em;
}
:link i:before:hover {
text-decoration: none !important;
}
......@@ -166,19 +170,20 @@ body {
}
#vm-details-rename, #vm-details-rename *, #vm-details-h1-name, #vm-list-rename, #vm-list-rename *,
#node-details-rename, #node-details-rename *, #node-details-h1-name, #node-list-rename, #node-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 * {
display: inline;
}
#vm-details-rename, #vm-list-rename, #node-details-rename, #node-list-rename {
#vm-details-rename, #vm-list-rename, #node-details-rename, #node-list-rename, #group-details-rename, #group-list-rename {
display: none;
}
#vm-details-rename-name, #node-details-rename-name {
#vm-details-rename-name, #node-details-rename-name, #group-details-rename-name {
max-width: 160px;
}
#vm-list-rename-name, #node-list-rename-name {
#vm-list-rename-name, #node-list-rename-name, #group-list-rename-name {
max-width: 100px;
}
......
......@@ -112,12 +112,12 @@ $(function () {
$('.vm-delete').click(function() {
var vm_pk = $(this).data('vm-pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(deleteVm,
addModalConfirmation(deleteObject,
{ 'url': '/dashboard/vm/delete/' + vm_pk + '/',
'data': [],
'vm_pk': vm_pk,
'redirect': dir});
'pk': vm_pk,
'type': "vm",
'redirect': dir});
return false;
});
......@@ -125,16 +125,44 @@ $(function () {
$('.node-delete').click(function() {
var node_pk = $(this).data('node-pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(deleteNode,
addModalConfirmation(deleteObject,
{ 'url': '/dashboard/node/delete/' + node_pk + '/',
'data': [],
'node_pk': node_pk,
'pk': node_pk,
'type': "node",
'redirect': dir});
return false;
});
/* for Node removes buttons */
$('.group-delete').click(function() {
var group_pk = $(this).data('group-pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(deleteObject,
{ 'url': '/dashboard/group/delete/' + group_pk + '/',
'data': [],
'type': "group",
'pk': group_pk,
'redirect': dir});
return false;
});
/* for Group removes buttons */
$('.group-delete').click(function() {
var group_pk = $(this).data('group-pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(deleteGroup,
{ 'url': '/dashboard/group/delete/' + group_pk + '/',
'data': [],
'group_pk': group_pk,
'redirect': dir});
return false;
});
/* search for vms */
/* search for vms */
var my_vms = []
$("#dashboard-vm-search-input").keyup(function(e) {
// if my_vms is empty get a list of our vms
......@@ -222,7 +250,7 @@ function refreshSliders() {
/* deletes the VM with the pk
* if dir is true, then redirect to the dashboard landing page
* else it adds a success message */
function deleteVm(data) {
function deleteObject(data) {
$.ajax({
type: 'POST',
data: {'redirect': data['redirect']},
......@@ -232,7 +260,7 @@ function deleteVm(data) {
if(!data['redirect']) {
selected = [];
addMessage(re['message'], 'success');
$('a[data-vm-pk="' + data['vm_pk'] + '"]').closest('tr').fadeOut(function() {
$('a[data-'+data['type']+'-pk="' + data['pk'] + '"]').closest('tr').fadeOut(function() {
$(this).remove();
});
} else {
......
var ctrlDown, shiftDown = false;
var ctrlKey = 17;
var shiftKey = 16;
var selected = [];
$(function() {
$(document).keydown(function(e) {
if (e.keyCode == ctrlKey) ctrlDown = true;
if (e.keyCode == shiftKey) shiftDown = true;
}).keyup(function(e) {
if (e.keyCode == ctrlKey) ctrlDown = false;
if (e.keyCode == shiftKey) shiftDown = false;
});
$('.group-list-table tbody').find('tr').mousedown(function() {
var retval = true;
if (ctrlDown) {
setRowColor($(this));
if(!$(this).hasClass('group-list-selected')) {
selected.splice(selected.indexOf($(this).index()), 1);
} else {
selected.push($(this).index());
}
retval = false;
} else if(shiftDown) {
if(selected.length > 0) {
start = selected[selected.length - 1] + 1;
end = $(this).index();
if(start > end) {
var tmp = start - 1; start = end; end = tmp - 1;
}
for(var i = start; i <= end; i++) {
if(selected.indexOf(i) < 0) {
selected.push(i);
setRowColor($('.group-list-table tbody tr').eq(i));
}
}
}
retval = false;
} else {
$('.group-list-selected').removeClass('group-list-selected');
$(this).addClass('group-list-selected');
selected = [$(this).index()];
}
// reset btn disables
$('.group-list-table tbody tr .btn').attr('disabled', false);
// show/hide group controls
if(selected.length > 1) {
$('.group-list-group-control a').attr('disabled', false);
for(var i = 0; i < selected.length; i++) {
$('.group-list-table tbody tr').eq(selected[i]).find('.btn').attr('disabled', true);
}
} else {
$('.group-list-group-control a').attr('disabled', true);
}
return retval;
});
$('#group-list-group-migrate').click(function() {
console.log(collectIds(selected));
});
$('tbody a').mousedown(function(e) {
// parent tr doesn't get selected when clicked
e.stopPropagation();
});
$('tbody a').click(function(e) {
// browser doesn't jump to top when clicked the buttons
if(!$(this).hasClass('real-link')) {
return false;
}
});
/* rename */
$("#group-list-rename-button, .group-details-rename-button").click(function() {
$("#group-list-column-name", $(this).closest("tr")).hide();
$("#group-list-rename", $(this).closest("tr")).css('display', 'inline');
});
/* rename ajax */
$('.group-list-rename-submit').click(function() {
var row = $(this).closest("tr")
var name = $('#group-list-rename-name', row).val();
var url = '/dashboard/group/' + row.children("td:first-child").text().replace(" ", "") + '/';
$.ajax({
method: 'POST',
url: url,
data: {'new_name': name},
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(data, textStatus, xhr) {
$("#group-list-column-name", row).html(
$("<a/>", {
'class': "real-link",
href: "/dashboard/group/" + data['node_pk'] + "/",
text: data['new_name']
})
).show();
$('#group-list-rename', row).hide();
// addMessage(data['message'], "success");
},
error: function(xhr, textStatus, error) {
addMessage("uhoh", "danger");
}
});
return false;
});
/* group actions */
/* select all */
$('#group-list-group-select-all').click(function() {
$('.group-list-table tbody tr').each(function() {
var index = $(this).index();
if(selected.indexOf(index) < 0) {
selected.push(index);
$(this).addClass('group-list-selected');
}
});
if(selected.length > 0)
$('.group-list-group-control a').attr('disabled', false);
return false;
});
/* mass vm delete */
$('#group-list-group-delete').click(function() {
addModalConfirmation(massDeleteVm,
{
'url': '/dashboard/group/mass-delete/',
'data': {
'selected': selected,
'v': collectIds(selected)
}
}
);
return false;
});
});
function collectIds(rows) {
var ids = [];
for(var i = 0; i < rows.length; i++) {
var div = $('td:first-child div', $('.group-list-table tbody tr').eq(rows[i]));
ids.push(div.prop('id').replace('node-', ''));
}
return ids;
}
function setRowColor(row) {
if(!row.hasClass('group-list-selected')) {
row.addClass('group-list-selected');
} else {
row.removeClass('group-list-selected');
}
}
......@@ -162,8 +162,6 @@ $(function() {
// change big status span
$('#node-info-pane').load(location.href+" #node-info-data");
// change resources
$('#resources').load(location.href+" #vm-details-resources-form");
}
$('#table_container').on('click','.node-enable',function() {
......
from django.contrib.auth.models import Group
from django.contrib.auth.models import Group, User
from django_tables2 import Table, A
from django_tables2.columns import (TemplateColumn, Column, BooleanColumn,
LinkColumn)
......@@ -103,11 +103,55 @@ class NodeListTable(Table):
class GroupListTable(Table):
pk = TemplateColumn(
template_name='dashboard/group-list/column-id.html',
verbose_name="ID",
attrs={'th': {'class': 'group-list-table-thin'}},
)
name = TemplateColumn(
template_name="dashboard/group-list/column-name.html"
)
number_of_users = TemplateColumn(
template_name='dashboard/group-list/column-users.html',
attrs={'th': {'class': 'group-list-table-admin'}},
)
admin = TemplateColumn(
template_name='dashboard/group-list/column-admin.html',
attrs={'th': {'class': 'group-list-table-admin'}},
)
actions = TemplateColumn(
attrs={'th': {'class': 'group-list-table-thin'}},
template_code=('{% include "dashboard/group-list/column-'
'actions.html" with btn_size="btn-xs" %}'),
)
class Meta:
model = Group
attrs = {'class': ('table table-bordered table-striped table-hover '
'group-list-table')}
fields = ('id', 'name', )
fields = ('pk', 'name', )
class UserListTable(Table):
pk = TemplateColumn(
template_name='dashboard/vm-list/column-id.html',
verbose_name="ID",
attrs={'th': {'class': 'vm-list-table-thin'}},
)
username = TemplateColumn(
template_name="dashboard/group-list/column-username.html"
)
class Meta:
model = User
attrs = {'class': ('table table-bordered table-striped table-hover '
'vm-list-table')}
fields = ('pk', 'username', )
class NodeVmListTable(Table):
......@@ -147,6 +191,11 @@ class NodeVmListTable(Table):
fields = ('pk', 'name', 'state', 'time_of_suspend', 'time_of_delete', )
class UserListTablex(Table):
class Meta:
model = User
class TemplateListTable(Table):
name = LinkColumn(
'dashboard.views.template-detail',
......
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div class="body-content">
<div class="page-header">
<h1>
<div id="group-details-rename">
<form action="" method="POST" id="group-details-rename-form">
{% 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>
<div id="group-details-h1-name">
{{ group.name }}
</div>
</h1>
</div>
<div class="row">
<div class="col-md-4" id="group-info-pane">
<div class="big">
<div class="btn-group">
<button type="button" class="btn btn-warning dropdown-toggle" data-toggle="dropdown">Action <i class="icon-caret-down"></i></button>
<ul class="dropdown-menu" role="menu">
<li><a href="#" class="group-details-rename-button"><i class="icon-pencil"></i> Rename</a></li>
<li><a data-group-pk="{{ group.pk }}" class="group-delete" href="{% url "dashboard.views.delete-group" pk=group.pk %}"><i class="icon-remove"></i> Discard</a></li>
</ul>
</div>
</div>
</div>
<div class="col-md-8" id="group-detail-pane">
<div class="panel panel-default" id="group-detail-panel">
<div class="tab-content panel-body">
<h3>{% trans "User list"|capfirst %}</h3>
<table class="table table-striped table-with-form-fields">
<tbody>
<thead><tr><th></th><th>{% trans "Who" %}</th><th></th><th></th></tr></thead>
{% for i in users %}
<tr><td><i class="icon-user"></i></td><td>{{i.username}}</td>
<td><a data-group-pk="{{ i.pk }}" href="#" class="real-link groupuser-delete btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
{% endfor %}
<tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}"></td>
</tr>
</tbody>
</table>
<h3>{% trans "Permissions"|capfirst %}</h3>
<form action="{{acl.url}}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields">
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead>
<tbody>
{% for i in acl.users %}
<tr><td><i class="icon-user"></i></td><td>{{i.user}}</td>
<td><select class="form-control" name="perm-u-{{i.user.id}}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select></td>
<td class="user-remove"><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
{% endfor %}
<tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}"></td>
<td><select class="form-control" name="perm-new">
{% for id, name in acl.levels %}
<option value="{{id}}">{{name}}</option>
{% endfor %}
</select></td><td></td>
</tr>
</tbody>
</table>
<textarea class="form-control"></textarea>
<div class="form-actions panel-body">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="{{ STATIC_URL}}dashboard/group-details.js"></script>
{% endblock %}
......@@ -15,16 +15,13 @@
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="no-margin"><i class="icon-desktop"></i> Your nodes</h3>
<h3 class="no-margin"><i class="icon-group"></i> Your groups</h3>
</div>
<div class="panel-body node-list-group-control">
<div class="panel-body group-list-group-control">
<p>
<strong>Group actions</strong>
<button id="node-list-group-select-all" class="btn btn-info btn-xs">Select all</button>
<a class="btn btn-default btn-xs" id="node-list-group-migrate" disabled><i class="icon-truck"></i> Migrate</a>
<a disabled href="#" class="btn btn-default btn-xs"><i class="icon-refresh"></i> Reboot</a>
<a disabled href="#" class="btn btn-default btn-xs"><i class="icon-off"></i> Shutdown</a>
<a id="node-list-group-delete" disabled href="#" class="btn btn-danger btn-xs"><i class="icon-remove"></i> Discard</a>
<button id="group-list-group-select-all" class="btn btn-info btn-xs">Select all</button>
<a id="group-list-group-delete" disabled href="#" class="btn btn-danger btn-xs"><i class="icon-remove"></i> Discard</a>
</p>
</div>
<div id="table_container">
......@@ -41,28 +38,28 @@
max-width: 600px;
}
.node-list-selected, .node-list-selected td {
.group-list-selected, .group-list-selected td {
background-color: #e8e8e8 !important;
}
.node-list-selected:hover, .node-list-selected:hover td {
.group-list-selected:hover, .group-list-selected:hover td {
background-color: #d0d0d0 !important;
}
.node-list-selected td:first-child {
.group-list-selected td:first-child {
font-weight: bold;
}
.node-list-table-thin {
.group-list-table-thin {
width: 10px;
}
.node-list-table-admin {
.group-list-table-admin {
width: 130px;
}
</style>
{% endblock %}
{% block extra_js %}
<script src="{{ STATIC_URL}}dashboard/node-list.js"></script>
<script src="{{ STATIC_URL}}dashboard/group-list.js"></script>
{% endblock %}
<a data-group-pk="{{ record.pk }}" class="btn btn-danger btn-xs real-link group-delete" href="{% url "dashboard.views.delete-group" pk=record.pk %}?next={{ request.path }}"><i class="icon-trash"></i></a>
<a id="group-list-rename-button" class="btn btn-default btn-xs" title data-original-title="Rename">
<i class="icon-pencil"></i>
</a>
<a class="btn btn-info btn-xs group-list-details" href="#"
>Member list</a>
<div id="group-{{ record.pk }}">{{ record.pk }}</div>
{% load i18n %}
<div id="group-list-rename">
<form action="{% url "dashboard.views.group-detail" pk=record.pk %}" method="POST" id="group-list-rename-form">
{% csrf_token %}
<input id="group-list-rename-name" class="form-control input-sm" name="new_name" type="text" value="{{ record.name }}"/>
<button type="submit" class="group-list-rename-submit btn btn-sm">{% trans "Rename" %}</button>
</form>
</div>
<div id="group-list-column-name">
<a class="real-link" href="{% url "dashboard.views.group-detail" pk=record.pk %}">{{ record.name }}</a>
</div>
{% load i18n %}
<div id="group-list-column-users">
<a class="real-link" href="{% url "dashboard.views.group-detail" pk=record.pk %}">{{ record.user_set.count }}</a>
</div>
<tr>
<!--<td><input type="checkbox"/ class="vm-checkbox" id="vm-1825{{ c }}"></td>-->
<td>
<div id="vm-1{{ c }}">1{{ c }}</div>
</td>
<td><a href="" class="real-link">network-devenv</a></td>
<td>running</td>
<td>10 days</td>
<td>1 month</td>
<td>
<a class="btn btn-default btn-xs" title data-original-title="Migrate">
<i class="icon-truck"></i>
</a>
<a class="btn btn-default btn-xs" title data-original-title="Rename">
<i class="icon-pencil"></i>
</a>
<a href="#" class="btn btn-default btn-xs vm-list-connect" data-toggle="popover"
data-content='
Belépés: <input style="width: 300px;" type="text" class="form-control" value="ssh cloud@vm.ik.bme.hu -p22312"/>
Jelszó: <input style="width: 300px;" type="text" class="form-control" value="asdfkicsiasdfkocsi"/>
'>Connect</a>
</td>
<td>
<a class="btn btn-info btn-xs vm-list-details" href="#" data-toggle="popover"
data-content='
<h4>Quick details</h4>
<dl class="dl-horizontal">
<dt>Number of cores:</dt><dd>4</dd>
<dt>Memory:</dt> <dd>512 MB</dd>
<dt>Architecture:</td><dd>x86-64</dd>
</dl>
<dl>
<dt>IPv4 address:</dt><dd>10.9.8.7</dd>
<dt>IPv6 address:</dt><dd> 2001:2001:2001:2001:2001:2001::</dd>
<dt>DNS name:</dt><dd>1825.vm.ik.bme.hu</dd>
</ul>
'>Details</a>
</td>
<td>
<div class="btn-group">
<button type="button" class="btn btn-xs btn-warning dropdown-toggle" data-toggle="dropdown">Action <i class="icon-caret-down"></i></button>
<ul class="dropdown-menu" role="menu">
<li><a href="#"><i class="icon-refresh"></i> Reboot</a></li>
<li><a href="#"><i class="icon-off"></i> Shutdown</a></li>
<li><a href="#"><i class="icon-remove"></i> Discard</a></li>
</ul>
</div>
</td>
</tr>
<div class="panel panel-default">
<div class="panel-heading">
<ul class="list-inline pull-right">
<li><a href="#vm-graph-view" class="btn btn-default btn-xs"><i class="icon-dashboard"></i></a></li>
<li><a href="#vm-list-view" class="btn btn-default btn-xs"><i class="icon-list"></i></a></li>
</ul>
<h3 class="no-margin"><i class="icon-group"></i> Groups
</h3>
</div>
<div class="pull-right toolbar">
<span class="btn btn-default btn-xs infobtn" title="Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. "><i class="icon-info-sign"></i></span>
</div>
<h3 class="no-margin"><i class="icon-group"></i> Groups
</h3>
</div>
<div class="list-group" id="vm-list-view">
{% for i in groups %}
<a href="#" class="list-group-item">
<i class="icon-file"></i> {{ i.name }}<div class="pull-right"><i class="icon-download-alt "></i></div>
</a>
<a href="{% url "dashboard.views.group-detail" pk=i.pk %}" class="list-group-item real-link">
<i class="icon-group"></i> {{ i.name }}
</a>
{% endfor %}
<div href="#" class="list-group-item list-group-footer text-right">
<div class="row">
......
{% load i18n %}
<form id="vm-details-resources-form" method="POST" action="">
{% csrf_token %}
<dl class="dl-horizontal">
<dt>Name:</dt><dd>{{ node.name }}</dd>
<dt>Number of cores:</dt><dd>{{ node.num_cores }}</dd>
<dt>Memory:</dt> <dd>{% widthratio node.ram_size 1048576 1 %} MB</dd>
<dt>Architecture:</td><dd>{{ record.arch }}</dd>
<dt>IPv4 address:</dt><dd>{{ node.host.ipv4 }}</dd>
<dt>IPv6 address:</dt><dd> {{ node.host.ipv6 }}</dd>
<dt>Enabled:</dt><dd>{{ node.enabled }}</dd>
<dt>Host online:</dt><dd> {{ node.online }}</dd>
<dt>Priority:</dt><dd>{{ node.priority }}</dd>
<dt>Host owner:</dt><dd>{{ node.host.owner }}</dd>
<dt>Vlan:</dt><dd>{{ node.host.vlan }}</dd>
<dt>Hostname:</dt><dd>{{ node.host.hostname }}</dd>
<dt>{% trans "Node name" %}:</dt><dd>{{ node.name }}</dd>
<dt>{% trans "CPU cores" %}:</dt><dd>{{ node.num_cores }}</dd>
<dt>{% trans "RAM size" %}:</dt> <dd>{% widthratio node.ram_size 1048576 1 %} MB</dd>
<dt>{% trans "Architecture" %}:</td><dd>{{ node.arch }}</dd>
<dt>{% trans "Host IP" %}:</dt><dd>{{ node.host.ipv4 }}</dd>
<dt>{% trans "Enabled" %}:</dt><dd>{{ node.enabled }}</dd>
<dt>{% trans "Host online" %}:</dt><dd> {{ node.online }}</dd>
<dt>{% trans "Priority" %}:</dt><dd>{{ node.priority }}</dd>
<dt>{% trans "Host owner" %}:</dt><dd>{{ node.host.owner }}</dd>
<dt>{% trans "Vlan" %}:</dt><dd>{{ node.host.vlan }}</dd>
<dt>{% trans "Host name" %}:</dt><dd>{{ node.host.hostname }}</dd>
</dl>
<p class="row">
<div class="col-sm-3">
<label for="cpu-count-slider"><i class="icon-cogs"></i> {% trans "Enabled" %}</label>
</div>
<div class="col-sm-9">
<label > {{ node.enabled }} </label>
</div>
</p>
<p class="row">
<div class="col-sm-3">
<label for="cpu-count-slider"><i class="icon-cogs"></i> {% trans "Priority" %}</label>
</div>
<div class="col-sm-9">
<label > {{ node.priority }} </label>
</div>
</p>
<p class="row">
<div class="col-sm-3">
<label for="cpu-count-slider"><i class="icon-cogs"></i> {% trans "CPU cores" %}</label>
</div>
<div class="col-sm-9">
<label > {{ node.num_cores }} </label>
</div>
</p>
<p class="row">
<div class="col-sm-3">
<label for="cpu-count-slider"><i class="icon-cogs"></i> {% trans "RAM size" %}</label>
</div>
<div class="col-sm-9">