Commit a65d9643 by Bach Dániel

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

parents 3d1583e0 3122ce02
......@@ -213,6 +213,7 @@ PIPELINE_JS = {
"dashboard/vm-common.js",
"dashboard/vm-create.js",
"dashboard/vm-list.js",
"dashboard/help.js",
"js/host.js",
"js/network.js",
"js/switch-port.js",
......
......@@ -21,7 +21,8 @@ from logging import getLogger
from django.core.exceptions import PermissionDenied, ImproperlyConfigured
from django.utils.translation import ugettext_noop
from .models import activity_context, has_suffix, humanize_exception
from .models import (activity_context, has_suffix, humanize_exception,
HumanReadableObject)
logger = getLogger(__name__)
......@@ -110,8 +111,12 @@ class Operation(object):
arguments.update(auxargs)
with activity_context(allargs['activity'], on_abort=self.on_abort,
on_commit=self.on_commit):
return self._operation(**arguments)
on_commit=self.on_commit) as act:
retval = self._operation(**arguments)
if (act.result is None and isinstance(
retval, (basestring, int, HumanReadableObject))):
act.result = retval
return retval
def _operation(self, **kwargs):
"""This method is the operation's particular implementation.
......
......@@ -17,6 +17,7 @@
from __future__ import absolute_import
from datetime import timedelta
from itertools import chain
from hashlib import md5
from logging import getLogger
......@@ -31,6 +32,7 @@ from django.db.models import (
)
from django.db.models.signals import post_save, pre_delete, post_delete
from django.templatetags.static import static
from django.utils import timezone
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
from django_sshkey.models import UserKey
......@@ -132,6 +134,17 @@ class Notification(TimeStampedModel):
def message(self, value):
self.message_data = None if value is None else value.to_dict()
@property
def has_valid_renew_url(self):
params = self.message_data['params']
return ('token' in params and 'suspend' in params and
self.modified > timezone.now() - timedelta(days=3))
@property
def renew_url(self):
return (settings.DJANGO_URL.rstrip("/") +
str(self.message_data['params'].get('token')))
class ConnectCommand(Model):
user = ForeignKey(User, related_name='command_set')
......
......@@ -1320,11 +1320,151 @@ textarea[name="new_members"] {
}
}
/* help page */
.help-tabs li {
width: 50%;
text-align: center;
}
// note: padding-margin hack to skip banner on anchor
#help-tab-content {
h1, h2, h3, h4, h5 {
font-weight: bold;
padding-top: 55px; margin-top: -55px;
}
blockquote {
background-color:#dfe4e4;
}
}
#wrapper {
position: fixed;
padding-left: 0px;
background: #252525;
left: 0px;
top: 46px;
height: 100%;
width: 210px;
transition: all .4s ease 0s;
/* Firefox */
height: -moz-calc(100% - 130px);
/* WebKit */
height: -webkit-calc(100% - 120px);
/* Opera */
height: -o-calc(100% - 110px);
/* Standard */
height: calc(100% - 130px);
}
#sidebar-wrapper {
margin-left: 0px;
left: 0px;
top: inherit;
bottom: 30px;
width: inherit;
position: inherit;
z-index: 10000;
transition: all .4s ease 0s;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
#sidebar-wrapper ul {
display: block;
float: left;
list-style: none;
margin: 0;
padding: 0px;
}
#sidebar-wrapper a {
font-family: "proxima-nova", 'Helvetica Neue', 'Helvetica', 'sans-serif';
color: #ddd;
display: block;
float: left;
width: 200px;
text-decoration: none;
background: #252525;
border-top: 1px solid #333;
border-bottom: 1px solid #222;
-webkit-transition: background .5s;
-moz-transition: background .5s;
-o-transition: background .5s;
-ms-transition: background .5s;
transition: background .5s;
}
#sidebar-wrapper ul a{
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
font-size: larger;
}
#sidebar-wrapper li a{
padding-top: 6px;
padding-bottom: 6px;
padding-left: 20px;
padding-right: 6px;
font-size: smaller;
}
#sidebar-wrapper ul ul a{
padding-top: 10px;
padding-bottom: 10px;
padding-left: 16px;
font-size: larger;
}
#sidebar-wrapper li li a{
padding-top: 6px;
padding-bottom: 6px;
padding-left: 24px;
padding-right: 6px;
font-size: smaller;
}
#sidebar-wrapper li a:hover, #sidebar-wrapper ul a:hover{
color: #fff;
background: rgba(200,200,255,0.15);
text-decoration: none;
}
#page-content {
margin-left: 200px;
}
@media only screen and (max-width: 600px) {
#wrapper {
-webkit-transform: translateX(-250px);
-moz-transform: translateX(-250px);
-o-transform: translateX(-250px);
-ms-transform: translateX(-250px);
transform: translateX(-250px);
}
#page-content {
margin-left: 0px;
-webkit-transition: background .5s;
-moz-transition: background .5s;
-o-transition: background .5s;
-ms-transition: background .5s;
transition: background .5s;
}
}
.overview_href {
cursor: pointer;
}
#request-buttons {
form {
display: inline;
}
textarea {
resize: none;
min-height: 80px;
......
$(function() {
$('.crosslink').click(function(e) {
e.preventDefault();
var menu = $(this).data("menu");
$(menu).click();
window.location = this.href;
});
var hash = window.location.hash;
if(hash) {
var pane = $(hash).closest(".tab-pane").prop("class");
if (pane) {
if (pane.indexOf("overview") != -1) {
$("#overview_menu").click();
} else {
$("#faq_menu").click();
}
$("html, body").animate({scrollTop: $(hash).offset().top}, 500);
window.location.hash = hash;
}
}
});
<img src="{{ STATIC_URL}}dashboard/img/logo.png" alt="circle logo" style="height: 25px;"/>
<img src="{{ STATIC_URL}}local-logo.png" alt="local logo" style="padding-left: 2px; height: 25px;"/>
{% load staticfiles %}
<img src="{% static "dashboard/img/logo.png" %}" alt="circle logo" style="height: 25px;"/>
<img src="{% static "local-logo.png" %}" alt="local logo" style="padding-left: 2px; height: 25px;"/>
......@@ -4,7 +4,9 @@
{% blocktrans count n=messages|length %}You have a new notification:{% plural %}You have {{n}} new notifications:{% endblocktrans %}
{% for msg in messages %} * {{msg.subject}}
{% for msg in messages %} * {{msg.subject}}{% if msg.has_valid_renew_url %}
{% trans "You can renew it without logging in:" %}
{{ msg.renew_url }}{% endif %}
{% endfor %}
{% blocktrans with url=url count n=messages|length %}See it in detail on <{{url}}>.{% plural %} See them in detail on <{{url}}>.{% endblocktrans %}
......
{% load i18n %}
{% load staticfiles %}
{% if not perms.vm.vm_access_console %}
{% if not perms.vm.access_console %}
<div class="alert alert-warning">
{% trans "You are not authorized to access the VNC console." %}
</div>
......
......@@ -148,7 +148,11 @@
</div>
<div class="alert alert-info">
You can filter the list by certain attributes (owner, name, status, tags) in the following way: "owner:John Doe name:my little server". If you don't specify any attribute names the filtering will be done by name.
{% blocktrans %}
You can filter the list by certain attributes (owner, name, status, tags)
in the following way: "owner:John Doe name:my little server !name:test".
If you don't specify any attribute names the filtering will be done by name.
{% endblocktrans %}
</div>
<div class="alert alert-info">
......
......@@ -5,5 +5,8 @@
<div><strong>{{ field.label }}</strong>: {{ field.errors|striptags }}</div>
{% endif %}
{% endfor %}
{% for error in form.non_field_errors %}
<div>{{ error|striptags }}</div>
{% endfor %}
</div>
{% endif %}
......@@ -28,6 +28,7 @@ from braces.views import LoginRequiredMixin
from dashboard.models import GroupProfile
from vm.models import Instance, Node, InstanceTemplate
from dashboard.views.vm import vm_ops
from ..store_api import Store
......@@ -127,7 +128,10 @@ class HelpView(TemplateView):
def get_context_data(self, *args, **kwargs):
ctx = super(HelpView, self).get_context_data(*args, **kwargs)
operations = [(o, Instance._ops[o.op])
for o in vm_ops.values() if o.show_in_toolbar]
ctx.update({"saml": hasattr(settings, "SAML_CONFIG"),
"operations": operations,
"store": settings.STORE_URL})
return ctx
......
......@@ -237,7 +237,8 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView):
self.create_fake_get()
try:
qs = qs.filter(**self.get_queryset_filters()).distinct()
filters, excludes = self.get_queryset_filters()
qs = qs.filter(**filters).exclude(**excludes).distinct()
except ValueError:
messages.error(self.request, _("Error during filtering."))
......
......@@ -79,14 +79,26 @@ class FilterMixin(object):
def get_queryset_filters(self):
filters = {}
for item in self.allowed_filters:
if item in self.request.GET:
filters[self.allowed_filters[item]] = (
self.request.GET[item].split(",")
if self.allowed_filters[item].endswith("__in") else
self.request.GET[item])
excludes = {}
for key, value in self.request.GET.items():
if not key:
continue
exclude = key.startswith('!')
key = key.lstrip('!')
if key not in self.allowed_filters:
continue
filter_field = self.allowed_filters[key]
value = (value.split(",")
if filter_field.endswith("__in") else
value)
if exclude:
excludes[filter_field] = value
else:
filters[filter_field] = value
return filters
return filters, excludes
def get_queryset(self):
return super(FilterMixin,
......@@ -118,6 +130,9 @@ class FilterMixin(object):
>>> o = f._parse_get({'s': "name:hello ws node:node 3 oh"}).items()
>>> sorted(o) # doctest: +ELLIPSIS
[(u'name', u'hello ws'), (u'node', u'node 3 oh'), (...)]
>>> o = f._parse_get({'s': "!hello:szia"}).items()
>>> sorted(o) # doctest: +ELLIPSIS
[(u'!hello', u'szia'), (...)]
"""
s = GET_dict.get("s")
fake = GET_dict.copy()
......
......@@ -1002,7 +1002,8 @@ class VmList(LoginRequiredMixin, FilterMixin, ListView):
in [i.name for i in Instance._meta.fields] + ["pk"]):
queryset = queryset.order_by(sort)
return queryset.filter(**self.get_queryset_filters()).prefetch_related(
filters, excludes = self.get_queryset_filters()
return queryset.filter(**filters).exclude(**excludes).prefetch_related(
"owner", "node", "owner__profile", "interface_set", "lease",
"interface_set__host").distinct()
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -16,7 +16,7 @@
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from django.forms import (
ModelForm, ModelChoiceField, ChoiceField, Form, CharField, RadioSelect,
Textarea,
Textarea, ValidationError
)
from django.utils.translation import ugettext_lazy as _
from django.template import RequestContext
......@@ -70,6 +70,14 @@ class InitialFromFileMixin(object):
RequestContext(request, {}),
)
def clean(self):
cleaned_data = super(InitialFromFileMixin, self).clean()
if cleaned_data['message'].strip() == self.initial['message'].strip():
raise ValidationError(
_("Fill in the message."),
code="invalid")
return cleaned_data
class TemplateRequestForm(InitialFromFileMixin, Form):
template = ModelChoiceField(TemplateAccessType.objects.all(),
......@@ -92,3 +100,13 @@ class ResourceRequestForm(InitialFromFileMixin, VmResourcesForm):
message = CharField(widget=Textarea)
initial_template = "request/initials/resources.html"
def clean(self):
cleaned_data = super(ResourceRequestForm, self).clean()
inst = self.instance
if (cleaned_data['ram_size'] == inst.ram_size and
cleaned_data['num_cores'] == inst.num_cores and
int(cleaned_data['priority']) == inst.priority):
raise ValidationError(
_("You haven't changed any of the resources."),
code="invalid")
{% load i18n %}
<br/>
<h2 id="faq" >{% trans "FAQ" %}</h2>
<br/>
<h3 id="how-can-i-create-and-share-a-template">{% trans "How can I create and share a template?" %}</h3>
<blockquote>
<ol>
<li>{% trans "Start a virtual machine." %}</li>
<li>{% trans "Customize this machine - install and remove softwares, etc." %}</li>
<li>{% trans 'Click the “Save as Template” button.' %}</li>
<li>{% trans 'On the dashboard select this template to go to the "Edit template page".' %}</li>
<li>
{% trans 'Use the "Manage access" box to add a user or user group with "user" access level.' %}
(<a class="crosslink" data-menu="#overview_menu" href="#how-can-i-create-groups">
{% trans "You can easily create groups if you need to" %}</a>)
</li>
</ol>
</blockquote>
<h3 id="how-can-i-create-a-vm-and-give-to-another-user">{% trans "How can I create a VM and give to another user?" %}</h3>
<blockquote>
<ol>
<li>{% trans "Start a virtual machine." %}</li>
<li>{% trans "On the machine's Access panel you can grant access for users and groups to the VM." %}</li>
</ol>
</blockquote>
<h3 id="how-can-i-portforward">{% trans "How can I open ports?" %}</h3>
<blockquote>
<ol>
<li>{% trans "On the VM’s detail page click on the Network panel." %}</li>
<li>{% trans "On the prefered interface type the port number, select type and click ‘Add’." %}</li>
<li>{% trans "You have to open this port in the firewall too. Opening port 80 examples: " %}<br>
<ul>
<li>Ubuntu, Debian: <code>sudo ufw allow 80</code></li>
<li>
CentOS, RHEL: {% trans "append to" %} <code>/etc/sysconfig/iptables</code>:<br/>
<code>iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT</code>
{% trans "and restart the service:" %} <code>sudo service iptables restart</code>
</li>
</ul>
</li>
<li>{% trans "Now you can connect to the machine with the generated port number." %}</li>
</ol>
</blockquote>
<h3 id="my-machines-lease-is-short-how-can-i-extend-it">{% trans "My machine's lease is too short. How can I extend it?" %}</h3>
<blockquote>
<p>
{% blocktrans %}You can send a request to the administrators. On the VM's home panel click on the ‘renew’ button and ‘send request’.
Please explain why you need a longer lease and choose the most suitable one.{% endblocktrans %}
</p>
</blockquote>
<h3 id="how-can-i-have-more-cpumemory">{% trans "How can I have more CPU/memory?" %}</h3>
<blockquote>
<p>
{% blocktrans %}
You can send a request to the administrators.
On the VM's resources panel click <strong>Request&nbsp;more&nbsp;resources</strong>, modify the values, explain your request and finally hit save.
{% endblocktrans %}
</p>
</blockquote>
<h3 id="how-can-i-get-access-to-a-template">{% trans "How can I get access to a template?" %}</h3>
<blockquote>
<p>
{% blocktrans %}
When you want to create a VM below the template list there is an option to send a request.
Select which template you want, explain why you need it and then submit the form.
{% endblocktrans %}
</p>
</blockquote>
{% load i18n %}
<ul><a href="#faq">{% trans "FAQ" %}</a>
<li><a href="#how-can-i-create-and-share-a-template">{% trans "How can I create and share a template?" %}</a></li>
<li><a href="#how-can-i-create-a-vm-and-give-to-another-user">{% trans "How can I create a VM and give to another user?" %}</a></li>
<li><a href="#how-can-i-portforward">{% trans "How can I portforward?" %}</a></li>
<li><a href="#my-machines-lease-is-short-how-can-i-extend-it">{% trans "My machine’s lease is short. How can I extend it?" %}</a></li>
<li><a href="#how-can-i-have-more-cpumemory">{% trans "How can I have more CPU/memory?" %}</a></li>
<li><a href="#how-can-i-get-access-to-a-template">{% trans "How can I get acces to a template?" %}</a></li>
</li>
</ul>
{% load i18n %}
<ul>
<a href="#overview">{% trans "Overview" %}</a>
<li><ul><a href="#introduction">{% trans "Introduction" %}</a></ul></li>
<li>
<ul><a href="#dashboard">{% trans "Dashboard" %}</a>
<li><a href="#virtual-machines-box">{% trans "Virtual Machines box" %}</a>
<ul>
<li><a href="#how-can-i-create-a-vm">{% trans "How can I create a VM?" %}</a></li>
<li><a href="#how-can-i-mark-frequently-used-vms">{% trans "How can I mark frequently used VMs?" %}</a></li>
<li><a href="#how-can-i-search-for-vms">{% trans "How can I search for VMs?" %}</a></li>
</ul>
</li>
<li><a href="#templates-box">{% trans "Templates box" %}</a></li>
</ul>
</li>
<li>
<ul><a href="#virtual-machines">{% trans "Virtual Machines" %}</a>
<li>
<ul><a href="#details">{% trans "Details" %}</a>
<li><a href="#how-can-i-connect-to-the-virtual-machine">{% trans "How can I connect to the virtual machine?" %}</a></li>
<li><a href="#how-can-i-change-the-vms-password">{% trans "How can I change the VM’s password?" %}</a></li>
</ul>
</li>
<li>
<ul><a href="#operations">{% trans "Operations" %}</a>
<li><a href="#what-kind-of-operations-are-allowed-to-do-with-my-vm">{% trans "What kind of operations are allowed to do with my VM?" %}</a></li>
<li>
</ul>
</li>
<li>
<ul><a href="#home">{% trans "Home" %}</a>
<li><a href="#expiration">{% trans "Expiration" %}</a></li>
<li><a href="#how-can-i-expand-the-vms-expiration-date">{% trans "How can I expand the VM’s expiration date?" %}</a></li>
<li><a href="#file-management">{% trans "File management" %}</a></li>
<li><a href="#how-can-i-share-previously-uploaded-files-with-the-vm">{% trans "How can I share previously uploaded files with the VM?" %}</a></li>
</ul>
</li>
<li><ul><a href="#resources">{% trans "Resources" %}</a></ul></li>
<li><a href="#console">{% trans "Console" %}</a></li>
<li><a href="#access">{% trans "Access" %}</a><ul>
<li><a href="#how-can-i-give-access-to-others">{% trans "How can I give access to others?" %}</a></li>
<li><a href="#what-kind-of-permissions-are-available">{% trans "What kind of permissions are available?" %}</a></li>
<li>
<ul><a href="#network">{% trans "Network" %}</a>
<li><a href="#how-can-i-add-a-network-interface">{% trans "How can I add a network interface?" %}</a></li>
</ul>
</li>
<li><a href="#activity">{% trans "Activity" %}</a></li>
<li><a href="#multiple-vm-operations">{% trans "Multiple VM operations" %}</a>
<li><a href="#how-can-i-show-shared-or-destroyed-vms">{% trans "How can I show shared or destroyed VMs?" %}</a></li>
</ul>
</li>
</ul>
</li>
<li>
<ul><a href="#templates">{% trans "Templates" %}</a>
<li><a href="#how-can-i-create-templates">{% trans "How can I create templates?" %}</a></li>
<li><a href="#what-kind-of-options-are-customizable-in-the-template">{% trans "What kind of options are customizable in the template?" %}</a></li>
<li><a href="#how-can-i-change-the-expiration-of-the-templates-vms">{% trans "How can I change the expiration of the tempalte's VMs" %}</a></li>
<li><a href="#how-can-i-give-the-template-to-other-user">{% trans "How can I give the template to other user" %}</a></li>
<li><a href="#how-can-i-give-access-to-users-or-groups-to-the-template">{% trans "How can I give access to users or groups to the template?"%}</a></li>
</ul>
</li>
<li>
<ul><a href="#groups">{% trans "Groups" %}</a>
<li><a href="#how-can-i-create-groups">{% trans "How can I create groups?" %}</a></li>
<li><a href="#how-can-i-manage-the-users-in-a-group">{% trans "How can I manage the users in a group?" %}</a></li>
<li><a href="#how-can-i-manage-privileges-with-the-group">{% trans "How can I manage privileges with the group?" %}</a></li>
</ul>
</li>
<li>
<ul><a href="#files">{% trans "Files" %}</a>
<li><a href="#how-can-i-share-my-files-with-a-vm">{% trans "How can I share my files with a VM?" %}</a></li>
</ul>
</li>
<li>
<ul><a href="#profile">{% trans "Profile" %}</a>
<li><a href="#how-can-i-change-my-password">{% trans "How can I change my password?" %}</a></li>
<li><a href="#how-can-i-store-public-keys-on-the-vms">{% trans "How can I store public keys on the VMs?" %}</a></li>
<li><a href="#how-can-i-change-connection-template">{% trans "How can I change connection template" %}</a></li>
</ul>
</li>
</ul>
......@@ -28,6 +28,7 @@ import time
from urlparse import urlsplit
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _, ugettext_noop
from django.conf import settings
......@@ -794,7 +795,10 @@ class SaveAsTemplateOperation(InstanceOperation):
tmpl.delete()
raise
else:
return tmpl
return create_readable(
ugettext_noop("New template: %(template)s"),
template=reverse('dashboard.views.template-detail',
kwargs={'pk': tmpl.pk}))
@register_operation
......@@ -986,7 +990,7 @@ class RenewOperation(InstanceOperation):
if save:
self.instance.lease = lease
self.instance.save()
activity.result = create_readable(ugettext_noop(
return create_readable(ugettext_noop(
"Renewed to suspend at %(suspend)s and destroy at %(delete)s."),
suspend=suspend, delete=delete)
......@@ -1357,7 +1361,7 @@ class ResourcesOperation(InstanceOperation):
self.instance.full_clean()
self.instance.save()
activity.result = create_readable(ugettext_noop(
return 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
......
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