Commit 7ec740a5 by Őry Máté

Merge remote-tracking branch 'origin/master' into feature-userguide

parents 448a2141 fec75c1e
...@@ -463,3 +463,5 @@ STORE_URL = get_env_variable("STORE_URL", "") ...@@ -463,3 +463,5 @@ STORE_URL = get_env_variable("STORE_URL", "")
SESSION_COOKIE_NAME = "csessid%x" % (((getnode() // 139) ^ SESSION_COOKIE_NAME = "csessid%x" % (((getnode() // 139) ^
(getnode() % 983)) & 0xffff) (getnode() % 983)) & 0xffff)
MAX_NODE_RAM = get_env_variable("MAX_NODE_RAM", 1024)
...@@ -70,20 +70,14 @@ SERVER_EMAIL = EMAIL_HOST_USER ...@@ -70,20 +70,14 @@ SERVER_EMAIL = EMAIL_HOST_USER
########## CACHE CONFIGURATION ########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches # See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
try: from urlparse import urlsplit
CACHES = {
'default': { CACHES = {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'default': {
'LOCATION': get_env_variable('DJANGO_MEMCACHED'), 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
} 'LOCATION': urlsplit(get_env_variable('CACHE_URI')).netloc,
}
except ImproperlyConfigured:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': SITE_NAME,
}
} }
}
########## END CACHE CONFIGURATION ########## END CACHE CONFIGURATION
......
...@@ -420,6 +420,7 @@ create_readable = HumanReadableObject.create ...@@ -420,6 +420,7 @@ create_readable = HumanReadableObject.create
class HumanReadableException(HumanReadableObject, Exception): class HumanReadableException(HumanReadableObject, Exception):
"""HumanReadableObject that is an Exception so can used in except clause. """HumanReadableObject that is an Exception so can used in except clause.
""" """
def __init__(self, level=None, *args, **kwargs): def __init__(self, level=None, *args, **kwargs):
super(HumanReadableException, self).__init__(*args, **kwargs) super(HumanReadableException, self).__init__(*args, **kwargs)
if level is not None: if level is not None:
...@@ -450,6 +451,7 @@ def humanize_exception(message, exception=None, level=None, **params): ...@@ -450,6 +451,7 @@ def humanize_exception(message, exception=None, level=None, **params):
... ...
Welcome! Welcome!
""" """
Ex = type("HumanReadable" + type(exception).__name__, Ex = type("HumanReadable" + type(exception).__name__,
(HumanReadableException, type(exception)), (HumanReadableException, type(exception)),
exception.__dict__) exception.__dict__)
......
...@@ -1382,7 +1382,7 @@ ...@@ -1382,7 +1382,7 @@
"pw": "ads", "pw": "ads",
"time_of_suspend": null, "time_of_suspend": null,
"ram_size": 200, "ram_size": 200,
"priority": 4, "priority": 10,
"active_since": null, "active_since": null,
"template": null, "template": null,
"access_method": "nx", "access_method": "nx",
...@@ -1412,7 +1412,7 @@ ...@@ -1412,7 +1412,7 @@
"pw": "ads", "pw": "ads",
"time_of_suspend": null, "time_of_suspend": null,
"ram_size": 200, "ram_size": 200,
"priority": 4, "priority": 10,
"active_since": null, "active_since": null,
"template": null, "template": null,
"access_method": "nx", "access_method": "nx",
...@@ -1518,7 +1518,7 @@ ...@@ -1518,7 +1518,7 @@
"ram_size": 1024, "ram_size": 1024,
"modified": "2014-01-24T00:58:19.654Z", "modified": "2014-01-24T00:58:19.654Z",
"system": "bubuntu", "system": "bubuntu",
"priority": 20, "priority": 10,
"access_method": "ssh", "access_method": "ssh",
"raw_data": "", "raw_data": "",
"arch": "x86_64", "arch": "x86_64",
......
...@@ -186,42 +186,6 @@ html { ...@@ -186,42 +186,6 @@ html {
text-decoration: none !important; text-decoration: none !important;
} }
.slider {
display: inline-block;
}
.slider .track {
height: 20px;
top: 50%;
}
.slider > .dragger, .slider > .dragger:hover {
border-radius: 0px;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
width: 8px;
height: 24px;
margin-top: -12px!important;
text-shadow: 0 1px 0 #fff;
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));
background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
background-repeat: repeat-x;
border-color: #2d6ca2;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
}
.slider > .dragger:hover {
background-color: #3071a9;
background-image: none;
border-color: #2d6ca2;
}
.slider > .highlight-track {
height: 20px;
top: 50%;
}
.slider + .output {
}
.rule-table tr >:nth-child(1) { .rule-table tr >:nth-child(1) {
text-align: right; text-align: right;
} }
...@@ -848,3 +812,54 @@ textarea[name="list-new-namelist"] { ...@@ -848,3 +812,54 @@ textarea[name="list-new-namelist"] {
#show-all-activities-container { #show-all-activities-container {
margin: 20px 0 0 10px; margin: 20px 0 0 10px;
} }
#vm-details-resources-form {
margin-top: 15px;
}
#vm-details-resources-form .row, .resources-sliders .row {
margin-bottom: 15px;
}
#vm-create-disk-add-form {
max-width: 450px;
margin-top: 15px;
}
.vm-create-template {
max-width: 800px;
border: 1px solid black;
border-bottom: none;
}
.vm-create-template-list .vm-create-template:last-child {
border-bottom: 1px solid black;
}
.vm-create-template-summary {
padding: 15px;
cursor: pointer;
}
.vm-create-template:nth-child(odd) .vm-create-template-summary {
background: #F5F5F5;
}
.vm-create-template-list .vm-create-template-summary:hover {
background: #D2D2D2;
}
.vm-create-template-details {
border-top: 1px dashed #D3D3D3;
padding: 15px;
}
.vm-create-template-details ul {
list-style: none;
padding: 0 15px;
}
.vm-create-template-details li {
border-bottom: 1px dotted #aaa;
padding: 5px 0px;
}
...@@ -435,28 +435,63 @@ function compareVmByFav(a, b) { ...@@ -435,28 +435,63 @@ function compareVmByFav(a, b) {
return a.pk < b.pk ? -1 : 1; return a.pk < b.pk ? -1 : 1;
} }
$(document).on('shown.bs.tab', 'a[href="#resources"]', function (e) {
$(".cpu-priority-input").trigger("change");
$(".cpu-count-input, .ram-input").trigger("input");
})
function addSliderMiscs() { function addSliderMiscs() {
$('.vm-slider').each(function() { // set max values based on inputs
$("<span>").addClass("output").html($(this).val()).insertAfter($(this)); var cpu_count_range = "0, " + $(".cpu-count-input").prop("max");
}); var ram_range = "0, " + $(".ram-input").prop("max");
$(".cpu-count-slider").data("slider-range", cpu_count_range);
$('.vm-slider').slider() $(".ram-slider").data("slider-range", ram_range);
.on('slide', function(e) {
$(this).val(e.value); $(".vm-slider").simpleSlider();
$(this).parent('div').nextAll("span").html(e.value) $(".cpu-priority-slider").bind("slider:changed", function (event, data) {
var value = data.value + 0;
$('.cpu-priority-input option[value="' + value + '"]').attr("selected", "selected");
}); });
refreshSliders(); $(".cpu-priority-input").change(function() {
} var val = $(":selected", $(this)).val();
$(".cpu-priority-slider").simpleSlider("setValue", val);
});
$(".cpu-count-slider").bind("slider:changed", function (event, data) {
var value = data.value + 0;
$(".cpu-count-input").val(parseInt(value));
});
// ehhh $(".cpu-count-input").bind("input", function() {
function refreshSliders() { var val = parseInt($(this).val());
$('.vm-slider').each(function() { if(!val) return;
$(this).val($(this).slider().data('slider').getValue()); $(".cpu-count-slider").simpleSlider("setValue", val);
$(this).parent('div').nextAll("span").html($(this).val());
}); });
var ram_fire = false;
$(".ram-slider").bind("slider:changed", function (event, data) {
if(ram_fire) {
ram_fire = false;
return;
}
var value = data.value + 0;
$(".ram-input").val(value);
});
$(".ram-input").bind("input", function() {
var val = $(this).val();
ram_fire = true;
$(".ram-slider").simpleSlider("setValue", parseInt(val));
});
$(".cpu-priority-input").trigger("change");
$(".cpu-count-input, .ram-input").trigger("input");
} }
/* deletes the VM with the pk /* deletes the VM with the pk
* if dir is true, then redirect to the dashboard landing page * if dir is true, then redirect to the dashboard landing page
* else it adds a success message */ * else it adds a success message */
......
jQuery Simple Slider: Unobtrusive Numerical Slider
==================================================
SimpleSlider is a jQuery plugin for turning your text inputs into draggable
numerical sliders.
It has no external dependencies other than jQuery, and you don't need to write
a single line of JavaScript to get it to work.
How to Use
-----------
Include the javascript file in your page:
<script src="simple-slider.js"></script>
Turn your text input into a slider:
<input type="text" data-slider="true">
Documentation, Features and Demos
---------------------------------
Full details and documentation can be found on the project page here:
<http://loopj.com/jquery-simple-slider/>
\ No newline at end of file
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="js/simple-slider.js"></script>
<link href="css/simple-slider.css" rel="stylesheet" type="text/css" />
<link href="css/simple-slider-volume.css" rel="stylesheet" type="text/css" />
<!-- These styles are only used for this page, not required for the slider -->
<style>
body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; }
[class^=slider] { display: inline-block; margin-bottom: 30px; }
.output { color: #888; font-size: 14px; padding-top: 1px; margin-left: 5px; vertical-align: top;}
h1 { font-size: 20px; }
h2 { clear: both; margin: 0; margin-bottom: 5px; font-size: 16px; }
p { font-size: 15px; margin-bottom: 30px; }
h2:first-of-type { margin-top: 0; }
</style>
</head>
<body>
<h1>jQuery Simple Slider Examples</h1>
<p>
Here are a few examples of the functionality available in Simple Slider.
</p>
<h2>Basic Example</h2>
<input type="text" data-slider="true">
<h2>Basic Example (Themed)</h2>
<input type="text" data-slider="true" data-slider-theme="volume">
<h2>Predefined Value</h2>
<input type="text" value="0.2" data-slider="true">
<h2>Steps</h2>
<input type="text" data-slider="true" data-slider-step="0.1">
<h2>Range</h2>
<input type="text" data-slider="true" data-slider-range="10,1000">
<h2>Range &amp; Steps</h2>
<input type="text" data-slider="true" data-slider-range="100,500" data-slider-step="100">
<h2>Range, Steps &amp; Snap</h2>
<input type="text" data-slider="true" data-slider-range="100,500" data-slider-step="100" data-slider-snap="true">
<h2>Predefined List of Values</h2>
<input type="text" data-slider="true" data-slider-values="0,100,500,800,2000">
<h2>Predefined List &amp; Snap</h2>
<input type="text" data-slider="true" data-slider-values="0,100,500,800,2000" data-slider-snap="true">
<h2>Predefined List, Equal Steps &amp; Snap</h2>
<input type="text" data-slider="true" data-slider-values="0,100,500,800,2000" data-slider-equal-steps="true" data-slider-snap="true">
<h2>Highlighted</h2>
<input type="text" data-slider="true" value="0.8" data-slider-highlight="true">
<h2>Highlighted (Themed)</h2>
<input type="text" data-slider="true" value="0.4" data-slider-highlight="true" data-slider-theme="volume">
<script>
$("[data-slider]")
.each(function () {
var input = $(this);
$("<span>")
.addClass("output")
.insertAfter($(this));
})
.bind("slider:ready slider:changed", function (event, data) {
$(this)
.nextAll(".output:first")
.html(data.value.toFixed(3));
});
</script>
</body>
</html>
module.exports = function(grunt) {
grunt.initConfig({
pkg: '<json:package.json>',
meta: {
banner:
'/*\n' +
' * <%= pkg.title || pkg.name %>: <%= pkg.description %>\n' +
' * Version <%= pkg.version %>\n' +
' *\n' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> (<%= pkg.author.url %>)\n' +
' *\n' +
' * Licensed under the <%= pkg.licenses[0].type %> license (<%= pkg.licenses[0].url %>)\n' +
' *\n' +
' */\n'
},
coffee: {
compile: {
files: {
'js/<%= pkg.name %>.js': 'js/*.coffee'
},
options: {
bare: true
}
}
},
watch: {
coffee: {
files: ['js/*.coffee'],
tasks: 'coffee growl:coffee'
}
},
growl: {
coffee: {
title: 'CoffeeScript',
message: 'Compiled successfully'
}
},
min: {
dist: {
src: ['<banner:meta.banner>', 'js/<%= pkg.name %>.js'],
dest: 'js/<%= pkg.name %>.min.js'
}
},
compress: {
zip: {
files: {
"<%= pkg.name %>-<%= pkg.version %>.zip": ["js/**", "demo.html", "README.md"]
}
}
}
});
// Lib tasks.
grunt.loadNpmTasks('grunt-contrib');
grunt.loadNpmTasks('grunt-growl');
// Default task.
grunt.registerTask('build', 'coffee min');
grunt.registerTask('serve', 'server watch:coffee');
grunt.registerTask('default', 'build');
};
\ No newline at end of file
/*
* jQuery Simple Slider: Unobtrusive Numerical Slider
* Version 1.0.0
*
* Copyright (c) 2013 James Smith (http://loopj.com)
*
* Licensed under the MIT license (http://mit-license.org/)
*
*/
var __slice=[].slice,__indexOf=[].indexOf||function(e){for(var t=0,n=this.length;t<n;t++)if(t in this&&this[t]===e)return t;return-1};(function(e,t){var n;return n=function(){function t(t,n){var r,i=this;this.input=t,this.defaultOptions={animate:!0,snapMid:!1,classPrefix:null,classSuffix:null,theme:null,highlight:!1},this.settings=e.extend({},this.defaultOptions,n),this.settings.theme&&(this.settings.classSuffix="-"+this.settings.theme),this.input.hide(),this.slider=e("<div>").addClass("slider"+(this.settings.classSuffix||"")).css({position:"relative",userSelect:"none",boxSizing:"border-box"}).insertBefore(this.input),this.input.attr("id")&&this.slider.attr("id",this.input.attr("id")+"-slider"),this.track=this.createDivElement("track").css({width:"100%"}),this.settings.highlight&&(this.highlightTrack=this.createDivElement("highlight-track").css({width:"0"})),this.dragger=this.createDivElement("dragger"),this.slider.css({minHeight:this.dragger.outerHeight(),marginLeft:this.dragger.outerWidth()/2,marginRight:this.dragger.outerWidth()/2}),this.track.css({marginTop:this.track.outerHeight()/-2}),this.settings.highlight&&this.highlightTrack.css({marginTop:this.track.outerHeight()/-2}),this.dragger.css({marginTop:this.dragger.outerWidth()/-2,marginLeft:this.dragger.outerWidth()/-2}),this.track.mousedown(function(e){return i.trackEvent(e)}),this.settings.highlight&&this.highlightTrack.mousedown(function(e){return i.trackEvent(e)}),this.dragger.mousedown(function(e){if(e.which!==1)return;return i.dragging=!0,i.dragger.addClass("dragging"),i.domDrag(e.pageX,e.pageY),!1}),e("body").mousemove(function(t){if(i.dragging)return i.domDrag(t.pageX,t.pageY),e("body").css({cursor:"pointer"})}).mouseup(function(t){if(i.dragging)return i.dragging=!1,i.dragger.removeClass("dragging"),e("body").css({cursor:"auto"})}),this.pagePos=0,this.input.val()===""?(this.value=this.getRange().min,this.input.val(this.value)):this.value=this.nearestValidValue(this.input.val()),this.setSliderPositionFromValue(this.value),r=this.valueToRatio(this.value),this.input.trigger("slider:ready",{value:this.value,ratio:r,position:r*this.slider.outerWidth(),el:this.slider})}return t.prototype.createDivElement=function(t){var n;return n=e("<div>").addClass(t).css({position:"absolute",top:"50%",userSelect:"none",cursor:"pointer"}).appendTo(this.slider),n},t.prototype.setRatio=function(e){var t;return e=Math.min(1,e),e=Math.max(0,e),t=this.ratioToValue(e),this.setSliderPositionFromValue(t),this.valueChanged(t,e,"setRatio")},t.prototype.setValue=function(e){var t;return e=this.nearestValidValue(e),t=this.valueToRatio(e),this.setSliderPositionFromValue(e),this.valueChanged(e,t,"setValue")},t.prototype.trackEvent=function(e){if(e.which!==1)return;return this.domDrag(e.pageX,e.pageY,!0),this.dragging=!0,!1},t.prototype.domDrag=function(e,t,n){var r,i,s;n==null&&(n=!1),r=e-this.slider.offset().left,r=Math.min(this.slider.outerWidth(),r),r=Math.max(0,r);if(this.pagePos!==r)return this.pagePos=r,i=r/this.slider.outerWidth(),s=this.ratioToValue(i),this.valueChanged(s,i,"domDrag"),this.settings.snap?this.setSliderPositionFromValue(s,n):this.setSliderPosition(r,n)},t.prototype.setSliderPosition=function(e,t){t==null&&(t=!1);if(t&&this.settings.animate){this.dragger.animate({left:e},200);if(this.settings.highlight)return this.highlightTrack.animate({width:e},200)}else{this.dragger.css({left:e});if(this.settings.highlight)return this.highlightTrack.css({width:e})}},t.prototype.setSliderPositionFromValue=function(e,t){var n;return t==null&&(t=!1),n=this.valueToRatio(e),this.setSliderPosition(n*this.slider.outerWidth(),t)},t.prototype.getRange=function(){return this.settings.allowedValues?{min:Math.min.apply(Math,this.settings.allowedValues),max:Math.max.apply(Math,this.settings.allowedValues)}:this.settings.range?{min:parseFloat(this.settings.range[0]),max:parseFloat(this.settings.range[1])}:{min:0,max:1}},t.prototype.nearestValidValue=function(t){var n,r,i,s;return i=this.getRange(),t=Math.min(i.max,t),t=Math.max(i.min,t),this.settings.allowedValues?(n=null,e.each(this.settings.allowedValues,function(){if(n===null||Math.abs(this-t)<Math.abs(n-t))return n=this}),n):this.settings.step?(r=(i.max-i.min)/this.settings.step,s=Math.floor((t-i.min)/this.settings.step),(t-i.min)%this.settings.step>this.settings.step/2&&s<r&&(s+=1),s*this.settings.step+i.min):t},t.prototype.valueToRatio=function(e){var t,n,r,i,s,o,u,a;if(this.settings.equalSteps){a=this.settings.allowedValues;for(i=o=0,u=a.length;o<u;i=++o){t=a[i];if(typeof n=="undefined"||n===null||Math.abs(t-e)<Math.abs(n-e))n=t,r=i}return this.settings.snapMid?(r+.5)/this.settings.allowedValues.length:r/(this.settings.allowedValues.length-1)}return s=this.getRange(),(e-s.min)/(s.max-s.min)},t.prototype.ratioToValue=function(e){var t,n,r,i,s;return this.settings.equalSteps?(s=this.settings.allowedValues.length,i=Math.round(e*s-.5),t=Math.min(i,this.settings.allowedValues.length-1),this.settings.allowedValues[t]):(n=this.getRange(),r=e*(n.max-n.min)+n.min,this.nearestValidValue(r))},t.prototype.valueChanged=function(t,n,r){var i;if(t.toString()===this.value.toString())return;return this.value=t,i={value:t,ratio:n,position:n*this.slider.outerWidth(),trigger:r,el:this.slider},this.input.val(t).trigger(e.Event("change",i)).trigger("slider:changed",i)},t}(),e.extend(e.fn,{simpleSlider:function(){var t,r,i;return i=arguments[0],t=2<=arguments.length?__slice.call(arguments,1):[],r=["setRatio","setValue"],e(this).each(function(){var s,o;return i&&__indexOf.call(r,i)>=0?(s=e(this).data("slider-object"),s[i].apply(s,t)):(o=i,e(this).data("slider-object",new n(e(this),o)))})}}),e(function(){return e("[data-slider]").each(function(){var t,n,r,i;return t=e(this),r={},n=t.data("slider-values"),n&&(r.allowedValues=function(){var e,t,r,s;r=n.split(","),s=[];for(e=0,t=r.length;e<t;e++)i=r[e],s.push(parseFloat(i));return s}()),t.data("slider-range")&&(r.range=t.data("slider-range").split(",")),t.data("slider-step")&&(r.step=t.data("slider-step")),r.snap=t.data("slider-snap"),r.equalSteps=t.data("slider-equal-steps"),t.data("slider-theme")&&(r.theme=t.data("slider-theme")),t.attr("data-slider-highlight")&&(r.highlight=t.data("slider-highlight")),t.data("slider-animate")!=null&&(r.animate=t.data("slider-animate")),t.simpleSlider(r)})})})(this.jQuery||this.Zepto,this);
\ No newline at end of file
{
"name" : "simple-slider",
"title" : "jQuery Simple Slider",
"description" : "Unobtrusive Numerical Slider",
"version" : "1.0.0",
"homepage" : "http://loopj.com/jquery-simple-slider",
"keywords" : [],
"author" : {
"name" : "James Smith",
"email" : "james@loopj.com",
"url" : "http://loopj.com"
},
"repository" : {
"type" : "git",
"url" : "https://github.com/loopj/jquery-simple-slider.git"
},
"bugs" : {
"url" : "https://github.com/loopj/jquery-simple-slider/issues"
},
"licenses": [{
"type": "MIT",
"url" : "http://mit-license.org/"
}],
"devDependencies" : {
"grunt" : "0.3.x",
"grunt-contrib": "0.2.x",
"grunt-growl": "git://github.com/loopj/grunt-growl.git#master"
},
"scripts": {
"test": "grunt"
}
}
\ No newline at end of file
...@@ -53,3 +53,50 @@ ...@@ -53,3 +53,50 @@
border-color: #496805; border-color: #496805;
} }
.slider {
display: inline-block;
}
.slider .track {
height: 20px;
top: 50%;
}
.slider > .dragger, .slider > .dragger:hover {
border-radius: 0px;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
width: 8px;
height: 24px;
margin-top: -12px!important;
text-shadow: 0 1px 0 #fff;
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));
background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
background-repeat: repeat-x;
border-color: #2d6ca2;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
}
.slider > .dragger:hover {
background-color: #3071a9;
background-image: none;
border-color: #2d6ca2;
}
.slider > .highlight-track {
height: 20px;
top: 50%;
}
.slider + .output {
}
.slider > .track, .slider > .highlight-track {
border-radius: 5px;
}
/* review this later */
.slider {
width: 100%;
}
/* /*
jQuery Simple Slider jQuery Simple Slider
Copyright (c) 2012 James Smith (http://loopj.com) Copyright (c) 2012, 2013 James Smith (http://loopj.com)
Copyright (c) 2013 Maarten van Grootel (http://maatenvangrootel.nl)
Copyright (c) 2013 Nathan Hunzaker (http://natehunzaker.com)
Copyright (c) 2013 Erik J. Nedwidek (http://github.com/nedwidek)
Licensed under the MIT license (http://mit-license.org/) Licensed under the MIT license (http://mit-license.org/)
*/ */
...@@ -23,8 +26,12 @@ var __slice = [].slice, ...@@ -23,8 +26,12 @@ var __slice = [].slice,
classPrefix: null, classPrefix: null,
classSuffix: null, classSuffix: null,
theme: null, theme: null,
highlight: false highlight: false,
showScale: false
}; };
if(typeof options == 'undefined') {
options = this.loadDataOptions();
}
this.settings = $.extend({}, this.defaultOptions, options); this.settings = $.extend({}, this.defaultOptions, options);
if (this.settings.theme) { if (this.settings.theme) {
this.settings.classSuffix = "-" + this.settings.theme; this.settings.classSuffix = "-" + this.settings.theme;
...@@ -106,6 +113,20 @@ var __slice = [].slice, ...@@ -106,6 +113,20 @@ var __slice = [].slice,
} }
this.setSliderPositionFromValue(this.value); this.setSliderPositionFromValue(this.value);
ratio = this.valueToRatio(this.value); ratio = this.valueToRatio(this.value);
if (this.settings.showScale) {
this.scale = this.createDivElement("scale");
this.minScale = this.createSpanElement("min-scale", this.scale);
this.maxScale = this.createSpanElement("max-scale", this.scale);
range = this.getRange();
this.minScale.html(range.min);
this.maxScale.html(range.max);
this.scale.css('marginTop', function(index, currentValue) {
return (parseInt(currentValue, 10) + this.previousSibling.offsetHeight / 2) + 'px';
});
}
this.input.trigger("slider:ready", { this.input.trigger("slider:ready", {
value: this.value, value: this.value,
ratio: ratio, ratio: ratio,
...@@ -114,6 +135,44 @@ var __slice = [].slice, ...@@ -114,6 +135,44 @@ var __slice = [].slice,
}); });
} }
SimpleSlider.prototype.loadDataOptions = function() {
var options = {};
allowedValues = this.input.data("slider-values");
if (allowedValues) {
options.allowedValues = (function() {
var _i, _len, _ref, _results;
_ref = allowedValues.split(",");
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
x = _ref[_i];
_results.push(parseFloat(x));
}
return _results;
})();
}
if (this.input.data("slider-range")) {
options.range = this.input.data("slider-range").split(",");
}
if (this.input.data("slider-step")) {
options.step = this.input.data("slider-step");
}
options.snap = this.input.data("slider-snap");
options.equalSteps = this.input.data("slider-equal-steps");
if (this.input.data("slider-theme")) {
options.theme = this.input.data("slider-theme");
}
if (this.input.attr("data-slider-highlight")) {
options.highlight = this.input.data("slider-highlight");
}
if (this.input.data("slider-animate") != null) {
options.animate = this.input.data("slider-animate");
}
if (this.input.data("slider-showscale") != null) {
options.showScale = this.input.data("slider-showscale");
}
return options;
}
SimpleSlider.prototype.createDivElement = function(classname) { SimpleSlider.prototype.createDivElement = function(classname) {
var item; var item;
item = $("<div>").addClass(classname).css({ item = $("<div>").addClass(classname).css({
...@@ -125,6 +184,12 @@ var __slice = [].slice, ...@@ -125,6 +184,12 @@ var __slice = [].slice,
return item; return item;
}; };
SimpleSlider.prototype.createSpanElement = function(classname, parent) {
var item;
item = $("<span>").addClass(classname).appendTo(parent);
return item;
};
SimpleSlider.prototype.setRatio = function(ratio) { SimpleSlider.prototype.setRatio = function(ratio) {
var value; var value;
ratio = Math.min(1, ratio); ratio = Math.min(1, ratio);
...@@ -322,42 +387,14 @@ var __slice = [].slice, ...@@ -322,42 +387,14 @@ var __slice = [].slice,
}); });
} }
}); });
/*
return $(function() { return $(function() {
return $("[data-slider]").each(function() { return $("[data-slider]").each(function() {
var $el, allowedValues, settings, x; var $el, allowedValues, settings, x;
$el = $(this); $el = $(this);
settings = {}; return $el.simpleSlider();
allowedValues = $el.data("slider-values");
if (allowedValues) {
settings.allowedValues = (function() {
var _i, _len, _ref, _results;
_ref = allowedValues.split(",");
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
x = _ref[_i];
_results.push(parseFloat(x));
}
return _results;
})();
}
if ($el.data("slider-range")) {
settings.range = $el.data("slider-range").split(",");
}
if ($el.data("slider-step")) {
settings.step = $el.data("slider-step");
}
settings.snap = $el.data("slider-snap");
settings.equalSteps = $el.data("slider-equal-steps");
if ($el.data("slider-theme")) {
settings.theme = $el.data("slider-theme");
}
if ($el.attr("data-slider-highlight")) {
settings.highlight = $el.data("slider-highlight");
}
if ($el.data("slider-animate") != null) {
settings.animate = $el.data("slider-animate");
}
return $el.simpleSlider(settings);
}); });
}); });
*/
})(this.jQuery || this.Zepto, this); })(this.jQuery || this.Zepto, this);
/*
* jQuery Simple Slider: Unobtrusive Numerical Slider
* Version 1.0.0
*
* Copyright (c) 2013 James Smith (http://loopj.com)
*
* Licensed under the MIT license (http://mit-license.org/)
*
*/
var __slice=[].slice,__indexOf=[].indexOf||function(c){for(var b=0,a=this.length;b<a;b++){if(b in this&&this[b]===c){return b}}return -1};(function(c,a){var b;b=(function(){function d(e,f){var g,h=this;this.input=e;this.defaultOptions={animate:true,snapMid:false,classPrefix:null,classSuffix:null,theme:null,highlight:false,showScale:false};if(typeof f=="undefined"){f=this.loadDataOptions()}this.settings=c.extend({},this.defaultOptions,f);if(this.settings.theme){this.settings.classSuffix="-"+this.settings.theme}this.input.hide();this.slider=c("<div>").addClass("slider"+(this.settings.classSuffix||"")).css({position:"relative",userSelect:"none",boxSizing:"border-box"}).insertBefore(this.input);if(this.input.attr("id")){this.slider.attr("id",this.input.attr("id")+"-slider")}this.track=this.createDivElement("track").css({width:"100%"});if(this.settings.highlight){this.highlightTrack=this.createDivElement("highlight-track").css({width:"0"})}this.dragger=this.createDivElement("dragger");this.slider.css({minHeight:this.dragger.outerHeight(),marginLeft:this.dragger.outerWidth()/2,marginRight:this.dragger.outerWidth()/2});this.track.css({marginTop:this.track.outerHeight()/-2});if(this.settings.highlight){this.highlightTrack.css({marginTop:this.track.outerHeight()/-2})}this.dragger.css({marginTop:this.dragger.outerWidth()/-2,marginLeft:this.dragger.outerWidth()/-2});this.track.mousedown(function(i){return h.trackEvent(i)});if(this.settings.highlight){this.highlightTrack.mousedown(function(i){return h.trackEvent(i)})}this.dragger.mousedown(function(i){if(i.which!==1){return}h.dragging=true;h.dragger.addClass("dragging");h.domDrag(i.pageX,i.pageY);return false});c("body").mousemove(function(i){if(h.dragging){h.domDrag(i.pageX,i.pageY);return c("body").css({cursor:"pointer"})}}).mouseup(function(i){if(h.dragging){h.dragging=false;h.dragger.removeClass("dragging");return c("body").css({cursor:"auto"})}});this.pagePos=0;if(this.input.val()===""){this.value=this.getRange().min;this.input.val(this.value)}else{this.value=this.nearestValidValue(this.input.val())}this.setSliderPositionFromValue(this.value);g=this.valueToRatio(this.value);if(this.settings.showScale){this.scale=this.createDivElement("scale");this.minScale=this.createSpanElement("min-scale",this.scale);this.maxScale=this.createSpanElement("max-scale",this.scale);range=this.getRange();this.minScale.html(range.min);this.maxScale.html(range.max);this.scale.css("marginTop",function(i,j){return(parseInt(j,10)+this.previousSibling.offsetHeight/2)+"px"})}this.input.trigger("slider:ready",{value:this.value,ratio:g,position:g*this.slider.outerWidth(),el:this.slider})}d.prototype.loadDataOptions=function(){var e={};allowedValues=this.input.data("slider-values");if(allowedValues){e.allowedValues=(function(){var i,g,h,f;h=allowedValues.split(",");f=[];for(i=0,g=h.length;i<g;i++){x=h[i];f.push(parseFloat(x))}return f})()}if(this.input.data("slider-range")){e.range=this.input.data("slider-range").split(",")}if(this.input.data("slider-step")){e.step=this.input.data("slider-step")}e.snap=this.input.data("slider-snap");e.equalSteps=this.input.data("slider-equal-steps");if(this.input.data("slider-theme")){e.theme=this.input.data("slider-theme")}if(this.input.attr("data-slider-highlight")){e.highlight=this.input.data("slider-highlight")}if(this.input.data("slider-animate")!=null){e.animate=this.input.data("slider-animate")}if(this.input.data("slider-showscale")!=null){e.showScale=this.input.data("slider-showscale")}return e};d.prototype.createDivElement=function(f){var e;e=c("<div>").addClass(f).css({position:"absolute",top:"50%",userSelect:"none",cursor:"pointer"}).appendTo(this.slider);return e};d.prototype.createSpanElement=function(g,e){var f;f=c("<span>").addClass(g).appendTo(e);return f};d.prototype.setRatio=function(e){var f;e=Math.min(1,e);e=Math.max(0,e);f=this.ratioToValue(e);this.setSliderPositionFromValue(f);return this.valueChanged(f,e,"setRatio")};d.prototype.setValue=function(f){var e;f=this.nearestValidValue(f);e=this.valueToRatio(f);this.setSliderPositionFromValue(f);return this.valueChanged(f,e,"setValue")};d.prototype.trackEvent=function(f){if(f.which!==1){return}this.domDrag(f.pageX,f.pageY,true);this.dragging=true;return false};d.prototype.domDrag=function(i,g,e){var f,h,j;if(e==null){e=false}f=i-this.slider.offset().left;f=Math.min(this.slider.outerWidth(),f);f=Math.max(0,f);if(this.pagePos!==f){this.pagePos=f;h=f/this.slider.outerWidth();j=this.ratioToValue(h);this.valueChanged(j,h,"domDrag");if(this.settings.snap){return this.setSliderPositionFromValue(j,e)}else{return this.setSliderPosition(f,e)}}};d.prototype.setSliderPosition=function(e,f){if(f==null){f=false}if(f&&this.settings.animate){this.dragger.animate({left:e},200);if(this.settings.highlight){return this.highlightTrack.animate({width:e},200)}}else{this.dragger.css({left:e});if(this.settings.highlight){return this.highlightTrack.css({width:e})}}};d.prototype.setSliderPositionFromValue=function(g,e){var f;if(e==null){e=false}f=this.valueToRatio(g);return this.setSliderPosition(f*this.slider.outerWidth(),e)};d.prototype.getRange=function(){if(this.settings.allowedValues){return{min:Math.min.apply(Math,this.settings.allowedValues),max:Math.max.apply(Math,this.settings.allowedValues)}}else{if(this.settings.range){return{min:parseFloat(this.settings.range[0]),max:parseFloat(this.settings.range[1])}}else{return{min:0,max:1}}}};d.prototype.nearestValidValue=function(i){var h,g,f,e;f=this.getRange();i=Math.min(f.max,i);i=Math.max(f.min,i);if(this.settings.allowedValues){h=null;c.each(this.settings.allowedValues,function(){if(h===null||Math.abs(this-i)<Math.abs(h-i)){return h=this}});return h}else{if(this.settings.step){g=(f.max-f.min)/this.settings.step;e=Math.floor((i-f.min)/this.settings.step);if((i-f.min)%this.settings.step>this.settings.step/2&&e<g){e+=1}return e*this.settings.step+f.min}else{return i}}};d.prototype.valueToRatio=function(l){var f,e,j,m,i,g,k,h;if(this.settings.equalSteps){h=this.settings.allowedValues;for(m=g=0,k=h.length;g<k;m=++g){f=h[m];if(!(typeof e!=="undefined"&&e!==null)||Math.abs(f-l)<Math.abs(e-l)){e=f;j=m}}if(this.settings.snapMid){return(j+0.5)/this.settings.allowedValues.length}else{return j/(this.settings.allowedValues.length-1)}}else{i=this.getRange();return(l-i.min)/(i.max-i.min)}};d.prototype.ratioToValue=function(h){var e,g,j,i,f;if(this.settings.equalSteps){f=this.settings.allowedValues.length;i=Math.round(h*f-0.5);e=Math.min(i,this.settings.allowedValues.length-1);return this.settings.allowedValues[e]}else{g=this.getRange();j=h*(g.max-g.min)+g.min;return this.nearestValidValue(j)}};d.prototype.valueChanged=function(h,f,e){var g;if(h.toString()===this.value.toString()){return}this.value=h;g={value:h,ratio:f,position:f*this.slider.outerWidth(),trigger:e,el:this.slider};return this.input.val(h).trigger(c.Event("change",g)).trigger("slider:changed",g)};return d})();c.extend(c.fn,{simpleSlider:function(){var e,d,f;f=arguments[0],e=2<=arguments.length?__slice.call(arguments,1):[];d=["setRatio","setValue"];return c(this).each(function(){var h,g;if(f&&__indexOf.call(d,f)>=0){h=c(this).data("slider-object");return h[f].apply(h,e)}else{g=f;return c(this).data("slider-object",new b(c(this),g))}})}});return c(function(){return c("[data-slider]").each(function(){var e,g,f,d;e=c(this);return e.simpleSlider()})})})(this.jQuery||this.Zepto,this);
...@@ -2,13 +2,17 @@ var vlans = []; ...@@ -2,13 +2,17 @@ var vlans = [];
var disks = []; var disks = [];
$(function() { $(function() {
vmCustomizeLoaded(); if($(".vm-create-template-list").length) {
vmCreateLoaded();
} else {
vmCustomizeLoaded();
}
}); });
function vmCreateLoaded() { function vmCreateLoaded() {
$(".vm-create-template-details").hide(); $(".vm-create-template-details").hide();
$(".vm-create-template-summary").click(function() { $(".vm-create-template-summary").unbind("click").click(function() {
$(this).next(".vm-create-template-details").slideToggle(); $(this).next(".vm-create-template-details").slideToggle();
}); });
...@@ -64,9 +68,18 @@ function vmCreateLoaded() { ...@@ -64,9 +68,18 @@ function vmCreateLoaded() {
return false; return false;
}); });
$('.progress-bar').each(function() {
var min = $(this).attr('aria-valuemin');
var max = $(this).attr('aria-valuemax');
var now = $(this).attr('aria-valuenow');
var siz = (now-min)*100/(max-min);
$(this).css('width', siz+'%');
});
} }
function vmCustomizeLoaded() { function vmCustomizeLoaded() {
$("[title]").tooltip();
/* network thingies */ /* network thingies */
/* add network */ /* add network */
...@@ -92,7 +105,7 @@ function vmCustomizeLoaded() { ...@@ -92,7 +105,7 @@ function vmCustomizeLoaded() {
/* add dummy text if no more networks are available */ /* add dummy text if no more networks are available */
if($('#vm-create-network-add-select option').length < 1) { if($('#vm-create-network-add-select option').length < 1) {
$('#vm-create-network-add-button').attr('disabled', true); $('#vm-create-network-add-button').attr('disabled', true);
$('#vm-create-network-add-select').html('<option value="-1">No more networks!</option>'); $('#vm-create-network-add-select').html('<option value="-1">' + gettext("No more networks.") + '</option>');
} }
return false; return false;
...@@ -124,7 +137,7 @@ function vmCustomizeLoaded() { ...@@ -124,7 +137,7 @@ function vmCustomizeLoaded() {
/* remove the selection from the multiple select */ /* remove the selection from the multiple select */
$('#vm-create-network-add-vlan option[value="' + vlan_pk + '"]').prop('selected', false); $('#vm-create-network-add-vlan option[value="' + vlan_pk + '"]').prop('selected', false);
if ($('#vm-create-network-list').children('span').length < 1) { if ($('#vm-create-network-list').children('span').length < 1) {
$('#vm-create-network-list').append('Not added to any network!'); $('#vm-create-network-list').append(gettext("Not added to any network"));
} }
}); });
return false; return false;
...@@ -155,7 +168,7 @@ function vmCustomizeLoaded() { ...@@ -155,7 +168,7 @@ function vmCustomizeLoaded() {
// if all networks are added add a dummy and disable the add button // if all networks are added add a dummy and disable the add button
if($("#vm-create-network-add-select option").length < 1) { if($("#vm-create-network-add-select option").length < 1) {
$("#vm-create-network-add-select").html('<option value="-1">No more networks!</option>'); $("#vm-create-network-add-select").html('<option value="-1">' + gettext("No more networks.") + '</option>');
$('#vm-create-network-add-button').attr('disabled', true); $('#vm-create-network-add-button').attr('disabled', true);
} }
...@@ -170,54 +183,6 @@ function vmCustomizeLoaded() { ...@@ -170,54 +183,6 @@ function vmCustomizeLoaded() {
/* ----- end of networks thingies ----- */ /* ----- end of networks thingies ----- */
/* add disk */
$('#vm-create-disk-add-button').click(function() {
var disk_pk = $('#vm-create-disk-add-select :selected').val();
var name = $('#vm-create-disk-add-select :selected').text();
if ($('#vm-create-disk-list').children('span').length < 1) {
$('#vm-create-disk-list').html('');
}
$('#vm-create-disk-list').append(
vmCreateDiskLabel(disk_pk, name)
);
/* select the disk from the multiple select */
$('#vm-create-disk-add-form option[value="' + disk_pk + '"]').prop('selected', true);
$('option:selected', $('#vm-create-disk-add-select')).remove();
/* add dummy text if no more disks are available */
if($('#vm-create-disk-add-select option').length < 1) {
$('#vm-create-disk-add-button').attr('disabled', true);
$('#vm-create-disk-add-select').html('<option value="-1">We are out of &lt;options&gt; hehe</option>');
}
return false;
});
/* remove disk */
// event for disk remove button (icon, X)
$('body').on('click', '.vm-create-remove-disk', function() {
var disk_pk = ($(this).parent('span').prop('id')).replace('disk-', '')
$(this).parent('span').fadeOut(500, function() {
/* remove the disk label */
$(this).remove();
var disk_name = $(this).text();
/* remove the selection from the multiple select */
$('#vm-create-disk-add-form option[value="' + disk_pk + '"]').prop('selected', false);
if ($('#vm-create-disk-list').children('span').length < 1) {
$('#vm-create-disk-list').append('No disks are added!');
}
});
return false;
});
/* copy disks from hidden select */ /* copy disks from hidden select */
$('#vm-create-disk-add-form option').each(function() { $('#vm-create-disk-add-form option').each(function() {
var text = $(this).text(); var text = $(this).text();
...@@ -244,6 +209,14 @@ function vmCustomizeLoaded() { ...@@ -244,6 +209,14 @@ function vmCustomizeLoaded() {
/* start vm button clicks */ /* start vm button clicks */
$('#vm-create-customized-start').click(function() { $('#vm-create-customized-start').click(function() {
var error = false;
$(".cpu-count-input, .ram-input, #id_name, #id_amount ").each(function() {
if(!$(this)[0].checkValidity()) {
error = true;
}
});
if(error) return true;
$.ajax({ $.ajax({
url: '/dashboard/vm/create/', url: '/dashboard/vm/create/',
headers: {"X-CSRFToken": getCookie('csrftoken')}, headers: {"X-CSRFToken": getCookie('csrftoken')},
...@@ -284,6 +257,7 @@ function vmCustomizeLoaded() { ...@@ -284,6 +257,7 @@ function vmCustomizeLoaded() {
/* for no js stuff */ /* for no js stuff */
$('.no-js-hidden').show(); $('.no-js-hidden').show();
$('.js-hidden').hide(); $('.js-hidden').hide();
} }
...@@ -294,5 +268,5 @@ function vmCreateNetworkLabel(pk, name, managed) { ...@@ -294,5 +268,5 @@ function vmCreateNetworkLabel(pk, name, managed) {
function vmCreateDiskLabel(pk, name) { function vmCreateDiskLabel(pk, name) {
var style = "float: left; margin: 5px 5px 5px 0;"; var style = "float: left; margin: 5px 5px 5px 0;";
return '<span id="disk-' + pk + '" class="label label-primary" style="' + style + '"><i class="fa fa-file"></i> ' + name + ' <a href="#" class="hover-black vm-create-remove-disk"><i class="fa fa-times-circle"></i></a></span> '; return '<span id="disk-' + pk + '" class="label label-primary" style="' + style + '"><i class="fa fa-file"></i> ' + name + '</span> ';
} }
var show_all = false; var show_all = false;
var in_progress = false; var in_progress = false;
var activity_hash = 5;
$(function() { $(function() {
/* do we need to check for new activities */ /* do we need to check for new activities */
...@@ -27,6 +28,15 @@ $(function() { ...@@ -27,6 +28,15 @@ $(function() {
/* save resources */ /* save resources */
$('#vm-details-resources-save').click(function() { $('#vm-details-resources-save').click(function() {
var error = false;
$(".cpu-count-input, .ram-input").each(function() {
if(!$(this)[0].checkValidity()) {
error = true;
}
});
if(error) return true;
$('i.fa-floppy-o', this).removeClass("fa-floppy-o").addClass("fa-refresh fa-spin"); $('i.fa-floppy-o', this).removeClass("fa-floppy-o").addClass("fa-refresh fa-spin");
var vm = $(this).data("vm"); var vm = $(this).data("vm");
$.ajax({ $.ajax({
...@@ -34,8 +44,12 @@ $(function() { ...@@ -34,8 +44,12 @@ $(function() {
url: "/dashboard/vm/" + vm + "/op/resources_change/", url: "/dashboard/vm/" + vm + "/op/resources_change/",
data: $('#vm-details-resources-form').serialize(), data: $('#vm-details-resources-form').serialize(),
success: function(data, textStatus, xhr) { success: function(data, textStatus, xhr) {
if(data.success) {
$('a[href="#activity"]').trigger("click");
} else {
addMessage(data.messages.join("<br />"), "danger");
}
$("#vm-details-resources-save i").removeClass('fa-refresh fa-spin').addClass("fa-floppy-o"); $("#vm-details-resources-save i").removeClass('fa-refresh fa-spin').addClass("fa-floppy-o");
$('a[href="#activity"]').trigger("click");
}, },
error: function(xhr, textStatus, error) { error: function(xhr, textStatus, error) {
$("#vm-details-resources-save i").removeClass('fa-refresh fa-spin').addClass("fa-floppy-o"); $("#vm-details-resources-save i").removeClass('fa-refresh fa-spin').addClass("fa-floppy-o");
...@@ -346,17 +360,6 @@ function decideActivityRefresh() { ...@@ -346,17 +360,6 @@ function decideActivityRefresh() {
return check; return check;
} }
/* unescapes html got via the request, also removes whitespaces and replaces all ' with " */
function unescapeHTML(html) {
return html.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&').replace(/&ndash;/g, "–").replace(/\//g, "").replace(/'/g, '"').replace(/&#39;/g, "'").replace(/ /g, '');
}
/* the html page contains some tags that were modified via js (titles for example), we delete these
also some html tags are closed with / */
function changeHTML(html) {
return html.replace(/data-original-title/g, "title").replace(/title=""/g, "").replace(/\//g, '').replace(/ /g, '');
}
function checkNewActivity(runs) { function checkNewActivity(runs) {
var instance = location.href.split('/'); instance = instance[instance.length - 2]; var instance = location.href.split('/'); instance = instance[instance.length - 2];
...@@ -365,19 +368,24 @@ function checkNewActivity(runs) { ...@@ -365,19 +368,24 @@ function checkNewActivity(runs) {
url: '/dashboard/vm/' + instance + '/activity/', url: '/dashboard/vm/' + instance + '/activity/',
data: {'show_all': show_all}, data: {'show_all': show_all},
success: function(data) { success: function(data) {
if(show_all) { /* replace on longer string freezes the spinning stuff */ var new_activity_hash = (data['activities'] + "").hashCode();
if(new_activity_hash != activity_hash) {
$("#activity-refresh").html(data['activities']); $("#activity-refresh").html(data['activities']);
} else {
a = unescapeHTML(data['activities']);
b = changeHTML($("#activity-refresh").html());
if(a != b)
$("#activity-refresh").html(data['activities']);
} }
activity_hash = new_activity_hash;
$("#ops").html(data['ops']); $("#ops").html(data['ops']);
$("#disk-ops").html(data['disk_ops']); $("#disk-ops").html(data['disk_ops']);
$("[title]").tooltip(); $("[title]").tooltip();
$("#vm-details-state i").prop("class", "fa " + data['icon']); /* 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']);
}
$("#vm-details-state span").html(data['human_readable_status'].toUpperCase()); $("#vm-details-state span").html(data['human_readable_status'].toUpperCase());
if(data['status'] == "RUNNING") { if(data['status'] == "RUNNING") {
$("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled"); $("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled");
...@@ -385,12 +393,12 @@ function checkNewActivity(runs) { ...@@ -385,12 +393,12 @@ function checkNewActivity(runs) {
$("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled"); $("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled");
} }
if(data['status'] == "STOPPED") { if(data['status'] == "STOPPED" || data['status'] == "PENDING") {
$(".enabled-when-stopped").prop("disabled", false); $(".change-resources-button").prop("disabled", false);
$(".hide-when-stopped").hide(); $(".change-resources-help").hide();
} else { } else {
$(".enabled-when-stopped").prop("disabled", true); $(".change-resources-button").prop("disabled", true);
$(".hide-when-stopped").show(); $(".change-resources-help").show();
} }
if(runs > 0 && decideActivityRefresh()) { if(runs > 0 && decideActivityRefresh()) {
...@@ -408,3 +416,14 @@ function checkNewActivity(runs) { ...@@ -408,3 +416,14 @@ function checkNewActivity(runs) {
} }
}); });
} }
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;
};
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
</body> </body>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="{{ STATIC_URL }}dashboard/loopj-jquery-simple-slider/js/simple-slider.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="{{ STATIC_URL }}jsi18n/{{ LANGUAGE_CODE }}/djangojs.js"></script> <script src="{{ STATIC_URL }}jsi18n/{{ LANGUAGE_CODE }}/djangojs.js"></script>
{% include 'autocomplete_light/static.html' %} {% include 'autocomplete_light/static.html' %}
......
{% load i18n %}
{% load sizefieldtags %}
{% load crispy_forms_tags %}
<div class="row">
<div class="col-sm-3" style="font-weight: bold;">
<i class="fa fa-trophy"></i> {% trans "CPU priority" %}
</div>
<div class="col-sm-6">
<input type="text" class="vm-slider cpu-priority-slider"
disabled placeholder="{% trans "Enable JS for fancy sliders" %}"
data-slider="true" data-slider-highlight="true" data-slider-range="10, 100"
data-slider-values="0, 10, 30, 80, 100" data-slider-snap="true"/>
</div>
<div class="col-sm-3">
<div class="input-group">
{{ field_priority }}
<span class="input-group-addon input-tags">
<i class="fa fa-question" title="{% trans "The priority of the CPU" %}"></i>
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-3" style="font-weight: bold;">
<i class="fa fa-cogs"></i> {% trans "CPU count" %}
</div>
<div class="col-sm-6">
<input type="text" class="vm-slider cpu-count-slider"
disabled placeholder="{% trans "Enable JS for fancy sliders" %}"
data-slider="true" data-slider-highlight="true" data-slider-range="0, 10"
data-slider-step="true" data-slider-snap="true"/>
</div>
<div class="col-sm-3">
<div class="input-group">
{{ field_num_cores }}
<span class="input-group-addon input-tags">
<i class="fa fa-question" title="{% trans "Number of CPU cores" %}"></i>
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-3" style="font-weight: bold;">
<i class="fa fa-ticket"></i> {% trans "RAM amount" %}
</div>
<div class="col-sm-6">
<input type="text" class="vm-slider ram-slider"
disabled placeholder="{% trans "Enable JS for fancy sliders" %}"
data-slider="true" data-slider-highlight="true" data-slider-range="0, 8192"
data-slider-step="128" data-slider-snap="true"/>
</div>
<div class="col-sm-3">
<div class="input-group">
{{ field_ram_size }}
<span class="input-group-addon input-tags">
MiB
</span>
<span class="input-group-addon input-tags">
<i title="{% trans "RAM size in mebibytes" %}" class="fa fa-question"></i>
</span>
</div>
</div>
</div>
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% if leases < 1 %}
<form action="" method="POST">
{% with form=form %}
{% include "display-form-errors.html" %}
{% endwith %}
{% csrf_token %}
{{ form.name|as_crispy_field }}
<fieldset class="resources-sliders">
<legend>{% trans "Resource configuration" %}</legend>
{% include "dashboard/_resources-sliders.html" with field_priority=form.priority field_num_cores=form.num_cores field_ram_size=form.ram_size %}
{{ form.max_ram_size|as_crispy_field }}
</fieldset>
<fieldset>
<legend>{% trans "Virtual machine settings" %}</legend>
{{ form.arch|as_crispy_field }}
{{ form.access_method|as_crispy_field }}
{{ form.boot_menu|as_crispy_field }}
{{ form.raw_data|as_crispy_field }}
{{ form.req_traits|as_crispy_field }}
{{ form.description|as_crispy_field }}
{{ form.system|as_crispy_field }}
</fieldset>
<fieldset>
<legend>{% trans "External resources" %}</legend>
{{ form.networks|as_crispy_field }}
{{ form.lease|as_crispy_field }}
{% if show_lease_create %}
<div class="alert alert-warning"> <div class="alert alert-warning">
{% trans "You haven't created any leases yet, but you need one to create a template!" %} {% trans "You haven't created any leases yet, but you need one to create a template." %}
<a href="{% url "dashboard.views.lease-create" %}">{% trans "Create a new lease now." %}</a> <a href="{% url "dashboard.views.lease-create" %}">{% trans "Create a new lease now." %}</a>
</div> </div>
{% endif %} {% endif %}
{{ form.tags|as_crispy_field }}
</fieldset>
<input type="submit" value="{% trans "Create new template" %}" class="btn btn-success">
</form>
{% with form=form %}
{% include "display-form-errors.html" %}
{% endwith %}
{% crispy form %}
<style> <style>
fieldset { fieldset {
...@@ -21,8 +53,3 @@ ...@@ -21,8 +53,3 @@
font-weight: bold; font-weight: bold;
} }
</style> </style>
<script>
$(function() {
$("#hint_id_num_cores, #hint_id_priority, #hint_id_ram_size").hide();
});
</script>
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
</li> </li>
</ul> </ul>
<div style="margin-top: 20px; padding: 0 15px; width: 100%"> <div style="margin-top: 20px; padding: 0 15px; width: 100%">
{% if perms.vm_set_resources %} {% if perms.vm.set_resources %}
<a class="btn btn-primary btn-xs customize-vm" data-template-pk="{{ t.pk }}" href="{% url "dashboard.views.vm-create" %}?template={{ t.pk }}"><i class="fa fa-wrench"></i> {% trans "Customize" %}</a> <a class="btn btn-primary btn-xs customize-vm" data-template-pk="{{ t.pk }}" href="{% url "dashboard.views.vm-create" %}?template={{ t.pk }}"><i class="fa fa-wrench"></i> {% trans "Customize" %}</a>
{% endif %} {% endif %}
<form class="pull-right text-right" method="POST" action="{% url "dashboard.views.vm-create" %}"> <form class="pull-right text-right" method="POST" action="{% url "dashboard.views.vm-create" %}">
...@@ -76,48 +76,6 @@ ...@@ -76,48 +76,6 @@
</div> </div>
<style> <style>
.row {
margin-bottom: 15px;
}
.vm-create-template {
max-width: 800px;
border: 1px solid black;
border-bottom: none;
}
.vm-create-template-list .vm-create-template:last-child {
border-bottom: 1px solid black;
}
.vm-create-template-summary {
padding: 15px;
cursor: pointer;
}
.vm-create-template:nth-child(odd) .vm-create-template-summary {
background: #F5F5F5;
}
.vm-create-template-list .vm-create-template-summary:hover {
background: #D2D2D2;
}
.vm-create-template-details {
border-top: 1px dashed #D3D3D3;
padding: 15px;
}
.vm-create-template-details ul {
list-style: none;
padding: 0 15px;
}
.vm-create-template-details li {
border-bottom: 1px dotted #aaa;
padding: 5px 0px;
}
.progress { .progress {
position: relative; position: relative;
width: 200px; width: 200px;
...@@ -139,15 +97,3 @@ ...@@ -139,15 +97,3 @@
font-size: 10px; font-size: 10px;
} }
</style> </style>
{% block "extra-js" %}
<script>
$('.progress-bar').each(function() {
var min = $(this).attr('aria-valuemin');
var max = $(this).attr('aria-valuemax');
var now = $(this).attr('aria-valuenow');
var siz = (now-min)*100/(max-min);
$(this).css('width', siz+'%');
});
</script>
{% endblock %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load i18n %}
{% load sizefieldtags %} {% load sizefieldtags %}
{% crispy vm_create_form %} {% include "display-form-errors.html" with form=vm_create_form %}
<form method="POST">
{% csrf_token %}
<script src="/static/dashboard/vm-create.js"></script> {{ vm_create_form.template }}
{{ vm_create_form.customized }}
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<button id="vm-create-customized-start" class="btn btn-success"
style="float: right; margin-top: 24px;">
<i class="fa fa-play"></i>
{% trans "Start" %}
</button>
<label>{% trans "Name" %}*</label>
{{ vm_create_form.name }}
</div>
</div>
</div>
<div class="row">
<div class="col-sm-10">
<div class="form-group">
<label>{% trans "Amount" %}*</label>
{{ vm_create_form.amount }}
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h2>{% trans "Resources" %}</h2>
</div>
</div>
<!-- resources -->
<div class="resources-sliders" style="max-width: 720px;">
{% include "dashboard/_resources-sliders.html" with field_priority=vm_create_form.cpu_priority field_num_cores=vm_create_form.cpu_count field_ram_size=vm_create_form.ram_size %}
</div>
<div class="row">
<div class="col-sm-4">
<h2>{% trans "Disks" %}</h2>
</div>
<div class="col-sm-8">
<div class="js-hidden">
{{ vm_create_form.disks }}
</div>
<div class="no-js-hidden">
<h3 id="vm-create-disk-list">{% trans "No disks are added." %}</h3>
<div style="clear: both;"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<h2>{% trans "Network" %}</h2>
</div>
<div class="col-sm-8" style="padding-top: 3px;">
<div class="js-hidden" style="padding-top: 15px; max-width: 450px;">
{{ vm_create_form.networks }}
</div>
<div class="no-js-hidden">
<h3 id="vm-create-network-list">
{% trans "Not added to any network." %}
</h3>
<h3 id="vm-create-network-add">
<div class="input-group" style="max-width: 330px;">
<select class="form-control font-awesome-font" id="vm-create-network-add-select">
</select>
<div class="input-group-btn">
<a id="vm-create-network-add-button" class="btn btn-success">
<i class="fa fa-plus-circle"></i>
</a>
</div><!-- .input-group-btn -->
</div><!-- .input-group -->
</h3><!-- #vm-create-network-add -->
</div><!-- .no-js-hidden -->
</div><!-- .col-sm-8 -->
</div><!-- .row -->
</form>
<script>
try {
vmCustomizeLoaded();
} catch(e) {}
</script>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
{% block extra_link %} {% block extra_link %}
<link rel="stylesheet" href="{{ STATIC_URL }}dashboard/bootstrap-slider/slider.css"/> <link rel="stylesheet" href="{{ STATIC_URL }}dashboard/loopj-jquery-simple-slider/css/simple-slider.css"/>
<link href="{{ STATIC_URL }}dashboard/bootstrap-tour.min.css" rel="stylesheet"> <link href="{{ STATIC_URL }}dashboard/bootstrap-tour.min.css" rel="stylesheet">
<link href="{{ STATIC_URL }}dashboard/dashboard.css" rel="stylesheet"> <link href="{{ STATIC_URL }}dashboard/dashboard.css" rel="stylesheet">
{% endblock %} {% endblock %}
...@@ -55,6 +55,5 @@ ...@@ -55,6 +55,5 @@
{% block extra_script %} {% block extra_script %}
<script src="{{ STATIC_URL }}dashboard/js/jquery.knob.js"></script> <script src="{{ STATIC_URL }}dashboard/js/jquery.knob.js"></script>
<script src="{{ STATIC_URL}}dashboard/bootstrap-slider/bootstrap-slider.js"></script>
<script src="{{ STATIC_URL }}dashboard/dashboard.js"></script> <script src="{{ STATIC_URL }}dashboard/dashboard.js"></script>
{% endblock %} {% endblock %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
<style>
.row {
margin-bottom: 15px;
}
</style>
<form method="POST" action="{% url "dashboard.views.group-create" %}"> <form method="POST" action="{% url "dashboard.views.group-create" %}">
{% csrf_token %} {% csrf_token %}
......
...@@ -82,9 +82,9 @@ ...@@ -82,9 +82,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
<tr><td><i class="icon-plus"></i></td> <tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name" <td><input type="text" class="form-control" name="name"
placeholder="{% trans "Name of group or user" %}"></td> placeholder="{% trans "Name of group or user" %}"></td>
<td><select class="form-control" name="perm-new"> <td><select class="form-control" name="level">
{% for id, name in acl.levels %} {% for id, name in acl.levels %}
<option value="{{id}}">{{name}}</option> <option value="{{id}}">{{name}}</option>
{% endfor %} {% endfor %}
......
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
<form method="POST" action="/dashboard/node/create/" id="node-create-form">
{% csrf_token %}
{% crispy formset formset.form.helper %}
</form>
<style> <style>
.row { #node-create-form .row {
margin-bottom: 15px; margin-bottom: 10px;
} }
</style> </style>
<form method="POST" action="/dashboard/node/create/">
{% csrf_token %}
{% crispy formset formset.form.helper %}
</form>
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
{% endblock %} {% endblock %}
{% block extra_js %} {% block extra_js %}
{% if template == "dashboard/_vm-create-1.html" %} {% if template == "dashboard/_vm-create-1.html" or template == "dashboard/_vm-create-2.html" %}
<script src="{{ STATIC_URL }}dashboard/vm-create.js"></script> <script src="{{ STATIC_URL }}dashboard/vm-create.js"></script>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<div class="panel-body"> <div class="panel-body">
<div style="text-align: center; margin: 0 0 20px 0;"> <div style="text-align: center; margin: 0 0 20px 0;">
<div class="label label-info" style="padding: 5px;"> <div class="label label-info" style="padding: 5px;">
{% trans "Curently uploading to" %}: {{ directory }} {% trans "Currently uploading to" %}: {{ directory }}
</div> </div>
</div> </div>
<form method="POST" action="{{ action }}" enctype="multipart/form-data"> <form method="POST" action="{{ action }}" enctype="multipart/form-data">
......
...@@ -15,10 +15,41 @@ ...@@ -15,10 +15,41 @@
<h3 class="no-margin"><i class="fa fa-puzzle-piece"></i> {% trans "Edit template" %}</h3> <h3 class="no-margin"><i class="fa fa-puzzle-piece"></i> {% trans "Edit template" %}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<form action="" method="POST">
{% with form=form %} {% with form=form %}
{% include "display-form-errors.html" %} {% include "display-form-errors.html" %}
{% endwith %} {% endwith %}
{% crispy form %}
{% csrf_token %}
{{ form.name|as_crispy_field }}
<fieldset class="resources-sliders">
<legend>{% trans "Resource configuration" %}</legend>
{% include "dashboard/_resources-sliders.html" with field_priority=form.priority field_num_cores=form.num_cores field_ram_size=form.ram_size %}
{{ form.max_ram_size|as_crispy_field }}
</fieldset>
<fieldset>
<legend>{% trans "Virtual machine settings" %}</legend>
{{ form.arch|as_crispy_field }}
{{ form.access_method|as_crispy_field }}
{{ form.boot_menu|as_crispy_field }}
{{ form.raw_data|as_crispy_field }}
{{ form.req_traits|as_crispy_field }}
{{ form.description|as_crispy_field }}
{{ form.system|as_crispy_field }}
</fieldset>
<fieldset>
<legend>{% trans "External resources" %}</legend>
{{ form.networks|as_crispy_field }}
{{ form.lease|as_crispy_field }}
{{ form.tags|as_crispy_field }}
</fieldset>
<input type="submit" value="{% trans "Save changes" %}" class="btn btn-primary">
</form>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -63,7 +63,11 @@ ...@@ -63,7 +63,11 @@
<div class="col-md-4" id="vm-info-pane"> <div class="col-md-4" id="vm-info-pane">
<div class="big"> <div class="big">
<span id="vm-details-state" class="label label-success"> <span id="vm-details-state" class="label label-success">
<i class="fa {{ instance.get_status_icon }}"></i> <i class="fa
{% if is_new_state %}
fa-spinner fa-spin
{% else %}
{{ instance.get_status_icon }}{% endif %}"></i>
<span>{{ instance.get_status_display|upper }}</span> <span>{{ instance.get_status_display|upper }}</span>
</span> </span>
</div> </div>
...@@ -75,6 +79,8 @@ ...@@ -75,6 +79,8 @@
<dd> <dd>
{% if instance.get_connect_port %} {% if instance.get_connect_port %}
{{ instance.get_connect_host }}:<strong>{{ instance.get_connect_port }}</strong> {{ instance.get_connect_host }}:<strong>{{ instance.get_connect_port }}</strong>
{% elif instance.interface_set.count < 1%}
<strong>{% trans "The VM doesn't have any network interface." %}</strong>
{% else %} {% else %}
<strong>{% trans "The required port for this protocol is not forwarded." %}</strong> <strong>{% trans "The required port for this protocol is not forwarded." %}</strong>
{% endif %} {% endif %}
...@@ -109,8 +115,7 @@ ...@@ -109,8 +115,7 @@
<div class="input-group" id="dashboard-vm-details-connect-command"> <div class="input-group" id="dashboard-vm-details-connect-command">
<span class="input-group-addon input-tags">{% trans "Command" %}</span> <span class="input-group-addon input-tags">{% trans "Command" %}</span>
<input type="text" spellcheck="false" <input type="text" spellcheck="false"
value="{% if instance.get_connect_command %}{{ instance.get_connect_command }}{% else %} value="{% if instance.get_connect_command %}{{ instance.get_connect_command }}{% else %}{% trans "Connection is not possible." %}{% endif %}"
{% trans "Connection is not possible." %}{% endif %}"
id="vm-details-connection-string" class="form-control input-tags" /> id="vm-details-connection-string" class="form-control input-tags" />
<span class="input-group-addon input-tags" id="vm-details-connection-string-copy"> <span class="input-group-addon input-tags" id="vm-details-connection-string-copy">
<i class="fa fa-copy" title="{% trans "Select all" %}"></i> <i class="fa fa-copy" title="{% trans "Select all" %}"></i>
......
...@@ -2,55 +2,26 @@ ...@@ -2,55 +2,26 @@
{% load sizefieldtags %} {% load sizefieldtags %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
<form id="vm-details-resources-form" method="POST" action=""> <form method="POST" action="{{ op.resources_change.get_url }}" id="vm-details-resources-form">
{% csrf_token %} {% csrf_token %}
<p class="row"> {% include "dashboard/_resources-sliders.html" with field_priority=resources_form.priority field_num_cores=resources_form.num_cores field_ram_size=resources_form.ram_size %}
<div class="col-sm-3">
<label for="vm-cpu-priority-slider"><i class="fa fa-trophy"></i> {% trans "CPU priority" %}</label>
</div>
<div class="col-sm-9">
<input name="cpu-priority" type="text" id="vm-cpu-priority-slider" class="vm-slider" value="{{ instance.priority }}" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="{{ instance.priority }}" data-slider-orientation="horizontal" data-slider-handle="square" data-slider-tooltip="hide"/>
</div>
</p>
<p class="row">
<div class="col-sm-3">
<label for="cpu-count-slider"><i class="fa fa-cogs"></i> {% trans "CPU count" %}</label>
</div>
<div class="col-sm-9">
<input name="cpu-count" type="text" id="vm-cpu-count-slider" class="vm-slider" value=" {{ instance.num_cores }}" data-slider-min="0" data-slider-max="8" data-slider-step="1" data-slider-value="{{ instance.num_cores }}" data-slider-orientation="horizontal" data-slider-handle="square" data-slider-tooltip="hide"/>
</div>
</p>
<p class="row">
<div class="col-sm-3">
<label for="ram-slider"><i class="fa fa-ticket"></i> {% trans "RAM amount" %}</label>
</div>
<div class="col-sm-9">
<input name="ram-size" type="text" id="vm-ram-size-slider" class="vm-slider" value="{{ instance.ram_size }}" data-slider-min="128" data-slider-max="4096" data-slider-step="128" data-slider-value="{{ instance.ram_size }}" data-slider-orientation="horizontal" data-slider-handle="square" data-slider-tooltip="hide"/> MiB
</div>
</p>
{% if can_change_resources %} {% if can_change_resources %}
<p class="row"> <button type="submit" class="btn btn-success btn-sm change-resources-button"
<div class="col-sm-12"> id="vm-details-resources-save" data-vm="{{ instance.pk }}"
<button type="submit" class="btn btn-success btn-sm enabled-when-stopped" id="vm-details-resources-save" {% if op.resources_change.disabled %}disabled{% endif %}>
data-vm="{{ instance.pk }}"
{% if not op.resources_change %}disabled{% endif %}>
<i class="fa fa-floppy-o"></i> {% trans "Save resources" %} <i class="fa fa-floppy-o"></i> {% trans "Save resources" %}
</button> </button>
<span class="hide-when-stopped" <span class="change-resources-help"
{% if op.resources_change %}style="display: none;"{% endif %} {% if not op.resources_change.disabled %}style="display: none;"{% endif %}
>{% trans "Stop your VM to change resources." %}</span> >{% trans "Stop your VM to change resources." %}</span>
</div>
</p>
{% endif %} {% endif %}
</form> </form>
<hr /> <hr />
<div class="row" id="vm-details-resources-disk"> <div class="row" id="vm-details-resources-disk">
<div class="col-sm-11"> <div class="col-sm-11">
<h3> <h3>
......
...@@ -282,10 +282,12 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -282,10 +282,12 @@ class VmDetailTest(LoginMixin, TestCase):
c = Client() c = Client()
self.login(c, 'superuser') self.login(c, 'superuser')
kwargs = InstanceTemplate.objects.get(id=1).__dict__.copy() kwargs = InstanceTemplate.objects.get(id=1).__dict__.copy()
kwargs.update(name='t2', lease=1, disks=1, raw_data='tst2') kwargs.update(name='t2', lease=1, disks=1,
raw_data='<devices></devices>')
response = c.post('/dashboard/template/1/', kwargs) response = c.post('/dashboard/template/1/', kwargs)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(InstanceTemplate.objects.get(id=1).raw_data, 'tst2') self.assertEqual(InstanceTemplate.objects.get(id=1).raw_data,
"<devices></devices>")
def test_permitted_lease_delete_w_template_using_it(self): def test_permitted_lease_delete_w_template_using_it(self):
c = Client() c = Client()
...@@ -565,7 +567,7 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -565,7 +567,7 @@ class VmDetailTest(LoginMixin, TestCase):
'amount': 2, 'amount': 2,
'customized': 1, 'customized': 1,
'template': 1, 'template': 1,
'cpu_priority': 1, 'cpu_count': 1, 'ram_size': 1, 'cpu_priority': 10, 'cpu_count': 1, 'ram_size': 128,
'network': [], 'network': [],
}) })
......
# Copyright 2014 Budapest University of Technology and Economics (BME IK) # Copyright 2014 Budapest University of Technology and Economics (BME IK)
# #
# This file is part of CIRCLE Cloud. # This file is part of CIRCLE Cloud.
# #
...@@ -71,7 +70,7 @@ from .forms import ( ...@@ -71,7 +70,7 @@ from .forms import (
VmSaveForm, UserKeyForm, VmRenewForm, VmSaveForm, UserKeyForm, VmRenewForm,
CirclePasswordChangeForm, VmCreateDiskForm, VmDownloadDiskForm, CirclePasswordChangeForm, VmCreateDiskForm, VmDownloadDiskForm,
TraitsForm, RawDataForm, GroupPermissionForm, AclUserAddForm, TraitsForm, RawDataForm, GroupPermissionForm, AclUserAddForm,
VmAddInterfaceForm, VmResourcesForm, VmAddInterfaceForm,
) )
from .tables import ( from .tables import (
...@@ -87,7 +86,7 @@ from storage.models import Disk ...@@ -87,7 +86,7 @@ from storage.models import Disk
from firewall.models import Vlan, Host, Rule from firewall.models import Vlan, Host, Rule
from .models import Favourite, Profile, GroupProfile, FutureMember from .models import Favourite, Profile, GroupProfile, FutureMember
from .store_api import Store, NoStoreException from .store_api import Store, NoStoreException, NotOkException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
saml_available = hasattr(settings, "SAML_CONFIG") saml_available = hasattr(settings, "SAML_CONFIG")
...@@ -311,6 +310,10 @@ class VmDetailView(CheckedDetailView): ...@@ -311,6 +310,10 @@ class VmDetailView(CheckedDetailView):
activities = activities[:10] activities = activities[:10]
context['activities'] = activities context['activities'] = activities
context['show_show_all'] = show_show_all context['show_show_all'] = show_show_all
latest = instance.get_latest_activity_in_progress()
context['is_new_state'] = (latest and
latest.resultant_state is not None and
instance.status != latest.resultant_state)
context['vlans'] = Vlan.get_objects_with_level( context['vlans'] = Vlan.get_objects_with_level(
'user', self.request.user 'user', self.request.user
...@@ -328,6 +331,8 @@ class VmDetailView(CheckedDetailView): ...@@ -328,6 +331,8 @@ class VmDetailView(CheckedDetailView):
context['ipv6_port'] = instance.get_connect_port(use_ipv6=True) context['ipv6_port'] = instance.get_connect_port(use_ipv6=True)
# resources forms # resources forms
context['resources_form'] = VmResourcesForm(instance=instance)
if self.request.user.is_superuser: if self.request.user.is_superuser:
context['traits_form'] = TraitsForm(instance=instance) context['traits_form'] = TraitsForm(instance=instance)
context['raw_data_form'] = RawDataForm(instance=instance) context['raw_data_form'] = RawDataForm(instance=instance)
...@@ -352,8 +357,6 @@ class VmDetailView(CheckedDetailView): ...@@ -352,8 +357,6 @@ class VmDetailView(CheckedDetailView):
return v(request) return v(request)
raise Http404() raise Http404()
raise Http404()
def __set_name(self, request): def __set_name(self, request):
self.object = self.get_object() self.object = self.get_object()
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
...@@ -789,22 +792,35 @@ class VmResourcesChangeView(VmOperationView): ...@@ -789,22 +792,35 @@ class VmResourcesChangeView(VmOperationView):
op = 'resources_change' op = 'resources_change'
icon = "save" icon = "save"
show_in_toolbar = False show_in_toolbar = False
wait_for_result = 0.5
def post(self, request, extra=None, *args, **kwargs): def post(self, request, extra=None, *args, **kwargs):
if extra is None: if extra is None:
extra = {} extra = {}
resources = { instance = get_object_or_404(Instance, pk=kwargs['pk'])
'num_cores': "cpu-count",
'priority': "cpu-priority",
'ram_size': "ram-size",
"max_ram_size": "ram-size", # TODO
}
for k, v in resources.iteritems():
extra[k] = request.POST.get(v)
return super(VmResourcesChangeView, self).post(request, extra, form = VmResourcesForm(request.POST, instance=instance)
*args, **kwargs) if not form.is_valid():
for f in form.errors:
messages.error(request, "<strong>%s</strong>: %s" % (
f, form.errors[f].as_text()
))
if request.is_ajax(): # this is not too nice
store = messages.get_messages(request)
store.used = True
return HttpResponse(
json.dumps({'success': False,
'messages': [unicode(m) for m in store]}),
content_type="application=json"
)
else:
return redirect(instance.get_absolute_url() + "#resources")
else:
extra = form.cleaned_data
extra['max_ram_size'] = extra['ram_size']
return super(VmResourcesChangeView, self).post(request, extra,
*args, **kwargs)
class TokenOperationView(OperationView): class TokenOperationView(OperationView):
...@@ -1359,10 +1375,13 @@ class TemplateCreate(SuccessMessageMixin, CreateView): ...@@ -1359,10 +1375,13 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
context = super(TemplateCreate, self).get_context_data(*args, **kwargs) context = super(TemplateCreate, self).get_context_data(*args, **kwargs)
num_leases = Lease.get_objects_with_level("user",
self.request.user).count()
can_create_leases = self.request.user.has_perm("create_leases")
context.update({ context.update({
'box_title': _("Create a new base VM"), 'box_title': _("Create a new base VM"),
'template': "dashboard/_template-create.html", 'template': "dashboard/_template-create.html",
'leases': Lease.objects.count() 'show_lease_create': num_leases < 1 and can_create_leases
}) })
return context return context
...@@ -2441,6 +2460,9 @@ def vm_activity(request, pk): ...@@ -2441,6 +2460,9 @@ def vm_activity(request, pk):
response['human_readable_status'] = instance.get_status_display() response['human_readable_status'] = instance.get_status_display()
response['status'] = instance.status response['status'] = instance.status
response['icon'] = instance.get_status_icon() response['icon'] = instance.get_status_icon()
latest = instance.get_latest_activity_in_progress()
response['is_new_state'] = (latest and latest.resultant_state is not None
and instance.status != latest.resultant_state)
context = { context = {
'instance': instance, 'instance': instance,
...@@ -3162,6 +3184,10 @@ class StoreList(LoginRequiredMixin, TemplateView): ...@@ -3162,6 +3184,10 @@ class StoreList(LoginRequiredMixin, TemplateView):
except NoStoreException: except NoStoreException:
messages.warning(self.request, _("No store.")) messages.warning(self.request, _("No store."))
return redirect("/") return redirect("/")
except NotOkException:
messages.warning(self.request, _("Store has some problems now."
" Try again later."))
return redirect("/")
def create_up_directory(self, directory): def create_up_directory(self, directory):
path = normpath(join('/', directory, '..')) path = normpath(join('/', directory, '..'))
......
...@@ -98,5 +98,5 @@ def reloadtask(type='Host', timeout=15): ...@@ -98,5 +98,5 @@ def reloadtask(type='Host', timeout=15):
}[type] }[type]
logger.info("Reload %s on next periodic iteration applying change to %s.", logger.info("Reload %s on next periodic iteration applying change to %s.",
", ".join(reload), type) ", ".join(reload), type)
if all(cache.add("%s_lock" % i, True, 30) for i in reload): if all([cache.add("%s_lock" % i, 'true', 30) for i in reload]):
reloadtask_worker.apply_async(queue='localhost.man', countdown=5) reloadtask_worker.apply_async(queue='localhost.man', countdown=5)
...@@ -3273,7 +3273,7 @@ msgid "" ...@@ -3273,7 +3273,7 @@ msgid ""
"[%(filename)s] because it has never beendeployed." "[%(filename)s] because it has never beendeployed."
msgstr "" msgstr ""
"A kér művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) " "A kér művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) "
"[(filename)s] lemezen, mivel nem volt még csatolva." "[%(filename)s] lemezen, mivel nem volt még csatolva."
#: storage/models.py:159 #: storage/models.py:159
#, python-format #, python-format
......
...@@ -33,7 +33,9 @@ from sizefield.models import FileSizeField ...@@ -33,7 +33,9 @@ from sizefield.models import FileSizeField
from .tasks import local_tasks, storage_tasks from .tasks import local_tasks, storage_tasks
from celery.exceptions import TimeoutError from celery.exceptions import TimeoutError
from common.models import WorkerNotFound, HumanReadableException from common.models import (
WorkerNotFound, HumanReadableException, humanize_exception
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -221,7 +223,7 @@ class Disk(TimeStampedModel): ...@@ -221,7 +223,7 @@ class Disk(TimeStampedModel):
return { return {
'qcow2-norm': 'virtio', 'qcow2-norm': 'virtio',
'qcow2-snap': 'virtio', 'qcow2-snap': 'virtio',
'iso': 'scsi', 'iso': 'ide',
'raw-ro': 'virtio', 'raw-ro': 'virtio',
'raw-rw': 'virtio', 'raw-rw': 'virtio',
}[self.type] }[self.type]
...@@ -404,10 +406,11 @@ class Disk(TimeStampedModel): ...@@ -404,10 +406,11 @@ class Disk(TimeStampedModel):
try: try:
result = remote.get(timeout=5) result = remote.get(timeout=5)
break break
except TimeoutError: except TimeoutError as e:
if task is not None and task.is_aborted(): if task is not None and task.is_aborted():
AbortableAsyncResult(remote.id).abort() AbortableAsyncResult(remote.id).abort()
raise Exception("Download aborted by user.") raise humanize_exception(ugettext_noop(
"Operation aborted by user."), e)
disk.size = result['size'] disk.size = result['size']
disk.type = result['type'] disk.type = result['type']
disk.is_ready = True disk.is_ready = True
...@@ -477,11 +480,12 @@ class Disk(TimeStampedModel): ...@@ -477,11 +480,12 @@ class Disk(TimeStampedModel):
try: try:
remote.get(timeout=5) remote.get(timeout=5)
break break
except TimeoutError: except TimeoutError as e:
if task is not None and task.is_aborted(): if task is not None and task.is_aborted():
AbortableAsyncResult(remote.id).abort() AbortableAsyncResult(remote.id).abort()
disk.destroy() disk.destroy()
raise Exception("Save as aborted by use.") raise humanize_exception(ugettext_noop(
"Operation aborted by user."), e)
disk.is_ready = True disk.is_ready = True
disk.save() disk.save()
return disk return disk
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from datetime import timedelta, datetime from datetime import timedelta, datetime
from django.core.validators import MinValueValidator
from django.db.models import Model, CharField, IntegerField, permalink from django.db.models import Model, CharField, IntegerField, permalink
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.timesince import timeuntil from django.utils.timesince import timeuntil
...@@ -37,16 +38,20 @@ class BaseResourceConfigModel(Model): ...@@ -37,16 +38,20 @@ class BaseResourceConfigModel(Model):
""" """
num_cores = IntegerField(verbose_name=_('number of cores'), num_cores = IntegerField(verbose_name=_('number of cores'),
help_text=_('Number of virtual CPU cores ' help_text=_('Number of virtual CPU cores '
'available to the virtual machine.')) 'available to the virtual machine.'),
validators=[MinValueValidator(0)])
ram_size = IntegerField(verbose_name=_('RAM size'), ram_size = IntegerField(verbose_name=_('RAM size'),
help_text=_('Mebibytes of memory.')) help_text=_('Mebibytes of memory.'),
validators=[MinValueValidator(0)])
max_ram_size = IntegerField(verbose_name=_('maximal RAM size'), max_ram_size = IntegerField(verbose_name=_('maximal RAM size'),
help_text=_('Upper memory size limit ' help_text=_('Upper memory size limit '
'for balloning.')) 'for balloning.'),
validators=[MinValueValidator(0)])
arch = CharField(max_length=10, verbose_name=_('architecture'), arch = CharField(max_length=10, verbose_name=_('architecture'),
choices=ARCHITECTURES) choices=ARCHITECTURES)
priority = IntegerField(verbose_name=_('priority'), priority = IntegerField(verbose_name=_('priority'),
help_text=_('CPU priority.')) help_text=_('CPU priority.'),
validators=[MinValueValidator(0)])
class Meta: class Meta:
abstract = True abstract = True
......
...@@ -41,7 +41,9 @@ from model_utils.models import TimeStampedModel, StatusModel ...@@ -41,7 +41,9 @@ from model_utils.models import TimeStampedModel, StatusModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from acl.models import AclBase from acl.models import AclBase
from common.models import create_readable, HumanReadableException from common.models import (
create_readable, HumanReadableException, humanize_exception
)
from common.operations import OperatedMixin from common.operations import OperatedMixin
from ..tasks import vm_tasks, agent_tasks from ..tasks import vm_tasks, agent_tasks
from .activity import (ActivityInProgressError, instance_activity, from .activity import (ActivityInProgressError, instance_activity,
...@@ -877,10 +879,11 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -877,10 +879,11 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
while True: while True:
try: try:
return remote.get(timeout=step) return remote.get(timeout=step)
except TimeoutError: except TimeoutError as e:
if task is not None and task.is_aborted(): if task is not None and task.is_aborted():
AbortableAsyncResult(remote.id).abort() AbortableAsyncResult(remote.id).abort()
raise Exception("Shutdown aborted by user.") raise humanize_exception(ugettext_noop(
"Operation aborted by user."), e)
def suspend_vm(self, timeout=230): def suspend_vm(self, timeout=230):
queue_name = self.get_remote_queue_name('vm', 'slow') queue_name = self.get_remote_queue_name('vm', 'slow')
...@@ -957,7 +960,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -957,7 +960,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
for a in acts: for a in acts:
if (latest == a.activity_code and if (latest == a.activity_code and
merged_acts[-1].result == a.result and merged_acts[-1].result_data == a.result_data and
a.finished and merged_acts[-1].finished and a.finished and merged_acts[-1].finished and
a.user == merged_acts[-1].user and a.user == merged_acts[-1].user and
(merged_acts[-1].finished - a.finished).days < 7 and (merged_acts[-1].finished - a.finished).days < 7 and
...@@ -975,3 +978,10 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -975,3 +978,10 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
return vm_tasks.screenshot.apply_async(args=[self.vm_name], return vm_tasks.screenshot.apply_async(args=[self.vm_name],
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
def get_latest_activity_in_progress(self):
try:
return InstanceActivity.objects.filter(
instance=self, succeeded=None, parent=None).latest("started")
except InstanceActivity.DoesNotExist:
return None
...@@ -913,7 +913,8 @@ class ResourcesOperation(InstanceOperation): ...@@ -913,7 +913,8 @@ class ResourcesOperation(InstanceOperation):
required_perms = ('vm.change_resources', ) required_perms = ('vm.change_resources', )
accept_states = ('STOPPED', 'PENDING', ) accept_states = ('STOPPED', 'PENDING', )
def _operation(self, user, num_cores, ram_size, max_ram_size, priority): def _operation(self, user, activity,
num_cores, ram_size, max_ram_size, priority):
self.instance.num_cores = num_cores self.instance.num_cores = num_cores
self.instance.ram_size = ram_size self.instance.ram_size = ram_size
...@@ -923,6 +924,12 @@ class ResourcesOperation(InstanceOperation): ...@@ -923,6 +924,12 @@ class ResourcesOperation(InstanceOperation):
self.instance.full_clean() self.instance.full_clean()
self.instance.save() self.instance.save()
activity.result = create_readable(ugettext_noop(
"Priority: %(priority)s, Num cores: %(num_cores)s, "
"Ram size: %(ram_size)s"), priority=priority, num_cores=num_cores,
ram_size=ram_size
)
register_operation(ResourcesOperation) register_operation(ResourcesOperation)
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from common.models import create_readable
from manager.mancelery import celery from manager.mancelery import celery
from vm.tasks.agent_tasks import (restart_networking, change_password, from vm.tasks.agent_tasks import (restart_networking, change_password,
set_time, set_hostname, start_access_server, set_time, set_hostname, start_access_server,
...@@ -87,8 +88,9 @@ def agent_started(vm, version=None): ...@@ -87,8 +88,9 @@ def agent_started(vm, version=None):
try: try:
with act.sub_activity( with act.sub_activity(
'update', 'update',
readable_name=ugettext_noop('update to %(version)s'), readable_name=create_readable(
version=settings.AGENT_VERSION ugettext_noop('update to %(version)s'),
version=settings.AGENT_VERSION)
): ):
update.apply_async( update.apply_async(
queue=queue, queue=queue,
......
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