Commit 1a9d84cd by Kálmán Viktor

Merge branch 'master' into issue-364

Conflicts:
	circle/dashboard/static/dashboard/vm-common.js
	circle/dashboard/templates/dashboard/store/index-files.html
parents 29adb58a e70e03b7
......@@ -198,6 +198,7 @@ PIPELINE_JS = {
"jquery-knob/dist/jquery.knob.min.js",
"jquery-simple-slider/js/simple-slider.js",
"dashboard/dashboard.js",
"dashboard/activity.js",
"dashboard/group-details.js",
"dashboard/group-list.js",
"dashboard/js/stupidtable.min.js", # no bower file
......
......@@ -282,6 +282,8 @@ def register_operation(op_cls, op_id=None, target_cls=None):
"in the 'target_cls' parameter to this "
"call.")
assert not hasattr(target_cls, op_id), (
"target class already has an attribute with this id")
if not issubclass(target_cls, OperatedMixin):
raise TypeError("%r is not a subclass of %r" %
(target_cls.__name__, OperatedMixin.__name__))
......
......@@ -898,7 +898,7 @@ class VmDownloadDiskForm(OperationForm):
def clean(self):
cleaned_data = super(VmDownloadDiskForm, self).clean()
if not cleaned_data['name']:
if cleaned_data['url']:
if cleaned_data.get('url'):
cleaned_data['name'] = urlparse(
cleaned_data['url']).path.split('/')[-1]
if not cleaned_data['name']:
......@@ -908,6 +908,36 @@ class VmDownloadDiskForm(OperationForm):
return cleaned_data
class VmRemoveInterfaceForm(OperationForm):
def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices')
self.interface = kwargs.pop('default')
super(VmRemoveInterfaceForm, self).__init__(*args, **kwargs)
self.fields.insert(0, 'interface', forms.ModelChoiceField(
queryset=choices, initial=self.interface, required=True,
empty_label=None, label=_('Interface')))
if self.interface:
self.fields['interface'].widget = HiddenInput()
@property
def helper(self):
helper = super(VmRemoveInterfaceForm, self).helper
if self.interface:
helper.layout = Layout(
AnyTag(
"div",
HTML(format_html(
_("<label>Vlan:</label> {0}"),
self.interface.vlan)),
css_class="form-group",
),
Field("interface"),
)
return helper
class VmAddInterfaceForm(OperationForm):
def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices')
......
/* for functions in both vm list and vm detail */
$(function() {
var in_progress = false;
var activity_hash = 5;
var show_all = false;
var reload_vm_detail = false;
/* do we need to check for new activities */
if(decideActivityRefresh()) {
if(!in_progress) {
checkNewActivity(1);
in_progress = true;
}
}
/* vm operations */
$('a[href="#activity"]').click(function(){
$('a[href="#activity"] i').addClass('fa-spin');
if(!in_progress) {
checkNewActivity(1);
in_progress = true;
}
});
$("#activity-refresh").on("click", "#show-all-activities", function() {
$(this).find("i").addClass("fa-spinner fa-spin");
show_all = !show_all;
$('a[href="#activity"]').trigger("click");
return false;
});
/* 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) {
var icon = $(this).children("i").addClass('fa-spinner fa-spin');
......@@ -23,11 +48,9 @@ $(function() {
});
/* if the operation fails show the modal again */
$("body").on("click", "#op-form-send", function() {
$("body").on("click", "#confirmation-modal #op-form-send", function() {
var url = $(this).closest("form").prop("action");
$(this).find("i").prop("class", "fa fa-fw fa-spinner fa-spin");
$.ajax({
url: url,
headers: {"X-CSRFToken": getCookie('csrftoken')},
......@@ -79,4 +102,91 @@ $(function() {
return false;
});
function decideActivityRefresh() {
var check = false;
/* if something is still spinning */
if($('.timeline .activity i').hasClass('fa-spin'))
check = true;
return check;
}
function checkNewActivity(runs) {
$.ajax({
type: 'GET',
url: $('a[href="#activity"]').attr('data-activity-url'),
data: {'show_all': show_all},
success: function(data) {
var new_activity_hash = (data.activities + "").hashCode();
if(new_activity_hash != activity_hash) {
$("#activity-refresh").html(data.activities);
}
activity_hash = new_activity_hash;
$("#ops").html(data.ops);
$("#disk-ops").html(data.disk_ops);
$("[title]").tooltip();
/* changing the status text */
var icon = $("#vm-details-state i");
if(data.is_new_state) {
if(!icon.hasClass("fa-spin"))
icon.prop("class", "fa fa-spinner fa-spin");
} else {
icon.prop("class", "fa " + data.icon);
}
var vm_state = $("#vm-details-state");
if (vm_state.length) {
vm_state.data("status", data['status']);
$("#vm-details-state span").html(data['human_readable_status'].toUpperCase());
}
if(data['status'] == "RUNNING") {
if(data['connect_uri']) {
$("#dashboard-vm-details-connect-button").removeClass('disabled');
}
$("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled");
} else {
if(data['connect_uri']) {
$("#dashboard-vm-details-connect-button").addClass('disabled');
}
$("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled");
}
if(data.status == "STOPPED" || data.status == "PENDING") {
$(".change-resources-button").prop("disabled", false);
$(".change-resources-help").hide();
} else {
$(".change-resources-button").prop("disabled", true);
$(".change-resources-help").show();
}
if(runs > 0 && decideActivityRefresh()) {
setTimeout(
function() {checkNewActivity(runs + 1);},
1000 + Math.exp(runs * 0.05)
);
} else {
in_progress = false;
if(reload_vm_detail) location.reload();
}
$('a[href="#activity"] i').removeClass('fa-spin');
},
error: function() {
in_progress = false;
}
});
}
});
String.prototype.hashCode = function() {
var hash = 0, i, chr, len;
if (this.length == 0) return hash;
for (i = 0, len = this.length; i < len; i++) {
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
$(function () {
$(".not-tab-pane").removeClass("not-tab-pane").addClass("tab-pane");
$('.vm-create').click(function(e) {
var template = $(this).data("template");
$.ajax({
type: 'GET',
url: '/dashboard/vm/create/' + (typeof template === "undefined" ? '' : '?template=' + template),
url: $(this).attr('href'),
success: function(data) {
$('body').append(data);
vmCreateLoaded();
addSliderMiscs();
$('#create-modal').modal('show');
$('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove();
var modal = $('#confirmation-modal');
modal.modal('show');
modal.on('hidden.bs.modal', function() {
modal.remove();
});
}
});
return false;
});
$('.node-create').click(function(e) {
$('.group-create, .node-create, .tx-tpl-ownership, .group-delete, .node-delete, .disk-remove, .template-delete, .delete-from-group, .lease-delete').click(function(e) {
$.ajax({
type: 'GET',
url: '/dashboard/node/create/',
url: $(this).prop('href'),
success: function(data) {
$('body').append(data);
nodeCreateLoaded();
addSliderMiscs();
$('#create-modal').modal('show');
$('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove();
});
}
});
return false;
});
$('.group-create').click(function(e) {
$.ajax({
type: 'GET',
url: '/dashboard/group/create/',
success: function(data) {
$('body').append(data);
addSliderMiscs();
$('#create-modal').modal('show');
$('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove();
});
}
});
return false;
});
$('.tx-tpl-ownership').click(function(e) {
$.ajax({
type: 'GET',
url: $('.tx-tpl-ownership').attr('href'),
success: function(data) {
$('body').append(data);
$('#confirmation-modal').modal('show');
$('#confirmation-modal').on('hidden.bs.modal', function() {
$('#confirmation-modal').remove();
var modal = $('#confirmation-modal');
modal.modal('show');
modal.on('hidden.bs.modal', function() {
modal.remove();
});
},
error: function(xhr, textStatus, error) {
if(xhr.status === 403) {
addMessage(gettext("Only the owners can delete the selected object."), "warning");
} else {
addMessage(gettext("An error occurred. (") + xhr.status + ")", 'danger')
}
}
});
return false;
......@@ -68,12 +46,13 @@ $(function () {
$('.template-choose').click(function(e) {
$.ajax({
type: 'GET',
url: '/dashboard/template/choose/',
url: $(this).prop('href'),
success: function(data) {
$('body').append(data);
$('#create-modal').modal('show');
$('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove();
var modal = $('#confirmation-modal');
modal.modal('show');
modal.on('hidden.bs.modal', function() {
modal.remove();
});
// check if user selected anything
$("#template-choose-next-button").click(function() {
......@@ -99,6 +78,7 @@ $(function () {
e.stopImmediatePropagation();
return false;
});
$('[href=#index-list-view]').click(function (e) {
var box = $(this).data('index-box');
$('#' + box + '-graph-view').hide();
......@@ -108,9 +88,10 @@ $(function () {
e.stopImmediatePropagation();
return false;
});
$('body [title]:not(.title-favourite)').tooltip();
$('body .title-favourite').tooltip({'placement': 'right'});
$('body :input[title]').tooltip({trigger: 'focus', placement: 'auto right'});
$('body [title]').tooltip();
$(".knob").knob();
$('[data-toggle="pill"]').click(function() {
......@@ -165,74 +146,6 @@ $(function () {
addSliderMiscs();
/* for VM removes buttons */
$('.vm-delete').click(function() {
var vm_pk = $(this).data('vm-pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(deleteObject,
{ 'url': '/dashboard/vm/delete/' + vm_pk + '/',
'data': [],
'pk': vm_pk,
'type': "vm",
'redirect': dir});
return false;
});
/* for disk remove buttons */
$('.disk-remove').click(function() {
var disk_pk = $(this).data('disk-pk');
addModalConfirmation(deleteObject,
{ 'url': '/dashboard/disk/' + disk_pk + '/remove/',
'data': [],
'pk': disk_pk,
'type': "disk",
});
return false;
});
/* for Node removes buttons */
$('.node-delete').click(function() {
var node_pk = $(this).data('node-pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(deleteObject,
{ 'url': '/dashboard/node/delete/' + node_pk + '/',
'data': [],
'pk': node_pk,
'type': "node",
'redirect': dir});
return false;
});
/* for Node flush buttons */
$('.node-flush').click(function() {
var node_pk = $(this).data('node-pk');
var postto = $(this).attr('href');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(function(){},
{ 'url': postto,
'data': [],
'pk': node_pk,
'type': "node",
'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(deleteObject,
{ 'url': '/dashboard/group/delete/' + group_pk + '/',
'data': [],
'type': "group",
'pk': group_pk,
'redirect': dir});
return false;
});
/* search for vms */
var my_vms = [];
$("#dashboard-vm-search-input").keyup(function(e) {
......@@ -243,7 +156,7 @@ $(function () {
for(var i in result) {
my_vms.push({
'pk': result[i].pk,
'name': result[i].name.toLowerCase(),
'name': result[i].name,
'state': result[i].state,
'fav': result[i].fav,
'host': result[i].host,
......@@ -260,7 +173,7 @@ $(function () {
var search_result = [];
var html = '';
for(var i in my_vms) {
if(my_vms[i].name.indexOf(input) != -1 || my_vms[i].host.indexOf(input) != -1) {
if(my_vms[i].name.toLowerCase().indexOf(input) != -1 || my_vms[i].host.indexOf(input) != -1) {
search_result.push(my_vms[i]);
}
}
......@@ -294,7 +207,7 @@ $(function () {
$.get("/dashboard/node/list/", function(result) {
for(var i in result) {
my_nodes.push({
'name': result[i].name.toLowerCase(),
'name': result[i].name,
'icon': result[i].icon,
'status': result[i].status,
'label': result[i].label,
......@@ -309,7 +222,7 @@ $(function () {
var search_result = [];
var html = '';
for(var i in my_nodes) {
if(my_nodes[i].name.indexOf(input) != -1) {
if(my_nodes[i].name.toLowerCase().indexOf(input) != -1) {
search_result.push(my_nodes[i]);
}
}
......@@ -351,7 +264,7 @@ $(function () {
for(var i in result) {
my_groups.push({
'url': result[i].url,
'name': result[i].name.toLowerCase(),
'name': result[i].name,
});
}
});
......@@ -362,14 +275,14 @@ $(function () {
var search_result = [];
var html = '';
for(var i in my_groups) {
if(my_groups[i].name.indexOf(input) != -1) {
if(my_groups[i].name.toLowerCase().indexOf(input) != -1) {
search_result.push(my_groups[i]);
}
}
for(i=0; i<5 && i<search_result.length; i++)
html += generateGroupHTML(search_result[i].url, search_result[i].name, search_result.length < 5);
if(search_result.length === 0)
html += '<div class="list-group-item list-group-item-last">No result</div>';
html += '<div class="list-group-item list-group-item-last">' + gettext("No result") + '</div>';
$("#dashboard-group-list").html(html);
// if there is only one result and ENTER is pressed redirect
......@@ -556,62 +469,6 @@ function setDefaultSliderValues() {
}
/* deletes the VM with the pk
* if dir is true, then redirect to the dashboard landing page
* else it adds a success message */
function deleteObject(data) {
$.ajax({
type: 'POST',
data: {'redirect': data.redirect},
url: data.url,
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(re, textStatus, xhr) {
if(!data.redirect) {
selected = [];
addMessage(re.message, 'success');
if(data.type === "disk") {
// no need to remove them from DOM
$('a[data-disk-pk="' + data.pk + '"]').parent("li").fadeOut();
$('a[data-disk-pk="' + data.pk + '"]').parent("h4").fadeOut();
}
else {
$('a[data-'+data.type+'-pk="' + data.pk + '"]').closest('tr').fadeOut(function() {
$(this).remove();
});
}
} else {
window.location.replace('/dashboard');
}
},
error: function(xhr, textStatus, error) {
addMessage('Uh oh :(', 'danger');
}
});
}
function massDeleteVm(data) {
f = function() {
selected = [];
// reset group buttons
$('.vm-list-group-control a').attr('disabled', true);
$(this).remove();
};
$.ajax({
traditional: true,
url: data.url,
headers: {"X-CSRFToken": getCookie('csrftoken')},
type: 'POST',
data: {'vms': data.data.v},
success: function(re, textStatus, xhr) {
for(var i=0; i< data.data.v.length; i++)
$('.vm-list-table tbody tr[data-vm-pk="' + data.data.v[i] + '"]').fadeOut(500, f);
addMessage(re.message, 'success');
},
error: function(xhr, textStatus, error) {
// TODO this
}
});
}
function addMessage(text, type) {
......@@ -699,6 +556,7 @@ function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
......
......@@ -90,8 +90,11 @@ html {
}
.list-group .list-group-footer {
padding-top: 5px;
padding-bottom: 5px;
height: 41px;
}
.list-group-footer .text-right {
padding-top: 4px;
}
.big {
......@@ -199,7 +202,7 @@ html {
}
.dashboard-index .panel {
height: 300px;
height: 294px;
}
#vm-details-rename, #vm-details-h1-name, #vm-details-rename ,
......@@ -586,8 +589,8 @@ footer a, footer a:hover, footer a:visited {
}
#dashboard-vm-list, #dashboard-node-list, #dashboard-group-list,
#dashboard-template-list {
min-height: 204px;
#dashboard-template-list, #dashboard-files-toplist {
min-height: 200px;
}
#group-detail-user-table td:first-child, #group-detail-user-table th:last-child,
......@@ -749,10 +752,6 @@ textarea[name="new_members"] {
margin-top: 8px;
}
#dashboard-files-toplist {
min-height: 204px;
}
#dashboard-files-toplist div.list-group-item {
color: #555;
}
......@@ -957,6 +956,11 @@ textarea[name="new_members"] {
#vm-list-search, #vm-mass-ops {
margin-top: 8px;
}
.list-group-item {
border-bottom: 0px !important;
}
.list-group-item-last {
border-bottom: 1px solid #ddd !important;
}
......
......@@ -28,39 +28,3 @@
$(".group-details-help-button").click(function() {
$(".group-details-help").stop().slideToggle();
});
/* for Node removes buttons */
$('.delete-from-group').click(function() {
var href = $(this).attr('href');
var tr = $(this).closest('tr');
var group = $(this).data('group_pk');
var member = $(this).data('member_pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(removeMember,
{ 'url': href,
'data': [],
'tr': tr,
'group_pk': group,
'member_pk': member,
'type': "user",
'redirect': dir});
return false;
});
function removeMember(data) {
$.ajax({
type: 'POST',
url: data.url,
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(re, textStatus, xhr) {
data.tr.fadeOut(function() {
$(this).remove();});
},
error: function(xhr, textStatus, error) {
addMessage('Uh oh :(', 'danger');
}
});
}
$(function() {
/* rename */
$("#group-list-rename-button, .group-details-rename-button").click(function() {
$("#group-list-column-name", $(this).closest("tr")).hide();
$(".group-list-column-name", $(this).closest("tr")).hide();
$("#group-list-rename", $(this).closest("tr")).css('display', 'inline');
$("#group-list-rename").find("input").select();
});
......@@ -10,7 +10,7 @@ $(function() {
$('.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(" ", "") + '/';
var url = row.find(".group-list-column-name a").prop("href");
$.ajax({
method: 'POST',
url: url,
......@@ -18,7 +18,7 @@ $(function() {
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(data, textStatus, xhr) {
$("#group-list-column-name", row).html(
$(".group-list-column-name", row).html(
$("<a/>", {
'class': "real-link",
href: "/dashboard/group/" + data.group_pk + "/",
......
$(function() {
nodeCreateLoaded();
});
function nodeCreateLoaded() {
/* no js compatibility */
$('.no-js-hidden').show();
$('.js-hidden').hide();
}
......@@ -30,20 +30,6 @@ $(function() {
$(".node-details-help").stop().slideToggle();
});
/* for Node removes buttons */
$('.node-enable').click(function() {
var node_pk = $(this).data('node-pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(changeNodeStatus,
{ 'url': '/dashboard/node/status/' + node_pk + '/',
'data': [],
'pk': node_pk,
'type': "node",
'redirect': dir});
return false;
});
// remove trait
$('.node-details-remove-trait').click(function() {
var to_remove = $(this).data("trait-pk");
......@@ -69,22 +55,3 @@ $(function() {
});
});
function changeNodeStatus(data) {
$.ajax({
type: 'POST',
url: data.url,
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(re, textStatus, xhr) {
if(!data.redirect) {
selected = [];
addMessage(re.message, 'success');
} else {
window.location.replace('/dashboard');
}
},
error: function(xhr, textStatus, error) {
addMessage('Uh oh :(', 'danger');
}
});
}