Commit 07fb9e65 by Őry Máté

Merge branch 'master' into feature-activity-state

parents 71932249 f17bddde
...@@ -382,3 +382,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE': ...@@ -382,3 +382,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
'DJANGO_SAML_ATTRIBUTE_MAPPING', 'DJANGO_SAML_ATTRIBUTE_MAPPING',
'{"mail": ["email"], "sn": ["last_name"], ' '{"mail": ["email"], "sn": ["last_name"], '
'"uid": ["username"], "cn": ["first_name"]}')) '"uid": ["username"], "cn": ["first_name"]}'))
SAML_CREATE_UNKNOWN_USER = True
if get_env_variable('DJANGO_SAML_ORG_ID_ATTRIBUTE', False) != False:
SAML_ORG_ID_ATTRIBUTE = get_env_variable(
'DJANGO_SAML_ORG_ID_ATTRIBUTE')
...@@ -1467,7 +1467,8 @@ ...@@ -1467,7 +1467,8 @@
"raw_data": "", "raw_data": "",
"arch": "x86_64", "arch": "x86_64",
"max_ram_size": 1024, "max_ram_size": 1024,
"lease": 1 "lease": 1,
"owner": 1
} }
} }
] ]
from datetime import timedelta from datetime import timedelta
import uuid import uuid
from django.contrib.auth.models import User
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import ( from crispy_forms.layout import (
Layout, Div, BaseInput, Field, HTML, Submit, Fieldset, TEMPLATE_PACK Layout, Div, BaseInput, Field, HTML, Submit, Fieldset, TEMPLATE_PACK
...@@ -430,8 +432,16 @@ class TemplateForm(forms.ModelForm): ...@@ -430,8 +432,16 @@ class TemplateForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
parent = kwargs.pop("parent", None) parent = kwargs.pop("parent", None)
self.user = kwargs.pop("user", None)
super(TemplateForm, self).__init__(*args, **kwargs) super(TemplateForm, self).__init__(*args, **kwargs)
self.fields['disks'] = forms.ModelMultipleChoiceField(queryset=DISKS) self.fields['disks'] = forms.ModelMultipleChoiceField(
queryset=Disk.get_objects_with_level(
'user', self.user).exclude(type="qcow2-snap")
)
data = self.data.copy()
data['owner'] = self.user.pk
self.data = data
if parent is not None: if parent is not None:
template = InstanceTemplate.objects.get(pk=parent) template = InstanceTemplate.objects.get(pk=parent)
...@@ -458,6 +468,11 @@ class TemplateForm(forms.ModelForm): ...@@ -458,6 +468,11 @@ class TemplateForm(forms.ModelForm):
self.instance.ram_size = 512 self.instance.ram_size = 512
self.instance.num_cores = 2 self.instance.num_cores = 2
def clean_owner(self):
if self.instance.pk is not None:
return User.objects.get(pk=self.instance.owner.pk)
return self.user
def save(self, commit=True): def save(self, commit=True):
data = self.cleaned_data data = self.cleaned_data
self.instance.max_ram_size = data.get('ram_size') self.instance.max_ram_size = data.get('ram_size')
......
from logging import getLogger
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import Model, ForeignKey from django.contrib.auth.signals import user_logged_in
from django.db.models import (
Model, ForeignKey, OneToOneField, CharField, IntegerField
)
from django.utils.translation import ugettext_lazy as _
from vm.models import Instance from vm.models import Instance
logger = getLogger(__name__)
class Favourite(Model): class Favourite(Model):
instance = ForeignKey(Instance) instance = ForeignKey(Instance)
user = ForeignKey(User) user = ForeignKey(User)
class Profile(Model):
user = OneToOneField(User)
preferred_language = CharField(verbose_name=_('preferred language'),
choices=settings.LANGUAGES,
max_length=32,
default=settings.LANGUAGE_CODE, blank=False)
org_id = CharField( # may be populated from eduPersonOrgId field
unique=True, blank=True, null=True, max_length=64,
help_text=_('Unique identifier of the person, e.g. a student number.'))
instance_limit = IntegerField(default=5)
def create_profile(sender, user, request, **kwargs):
if not user.pk:
return False
profile, created = Profile.objects.get_or_create(user=user)
return created
user_logged_in.connect(create_profile)
if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
logger.debug("Register save_org_id to djangosaml2 pre_user_save")
from djangosaml2.signals import pre_user_save
def save_org_id(sender, **kwargs):
logger.debug("save_org_id called by %s", sender.username)
attributes = kwargs.pop('attributes')
atr = settings.SAML_ORG_ID_ATTRIBUTE
try:
value = attributes[atr][0]
except Exception as e:
value = None
logger.info("save_org_id couldn't find attribute. %s", unicode(e))
if sender.pk is None:
sender.save()
logger.debug("save_org_id saved user %s", unicode(sender))
profile, created = Profile.objects.get_or_create(user=sender)
if created or profile.org_id != value:
logger.info("org_id of %s added to user %s's profile",
value, sender.username)
profile.org_id = value
profile.save()
else:
logger.debug("org_id of %s already added to user %s's profile",
value, sender.username)
return False
pre_user_save.connect(save_org_id)
else:
logger.debug("Do not register save_org_id to djangosaml2 pre_user_save")
$(function() {
if($('.timeline .activity:first i:first').hasClass('icon-spin'))
checkNewActivity();
/* save resources */
$('#vm-details-resources-save').click(function() {
$('i.icon-save', this).removeClass("icon-save").addClass("icon-refresh icon-spin");
$.ajax({
type: 'POST',
url: location.href,
data: $('#vm-details-resources-form').serialize(),
success: function(data, textStatus, xhr) {
addMessage(data['message'], 'success');
$("#vm-details-resources-save i").removeClass('icon-refresh icon-spin').addClass("icon-save");
},
error: function(xhr, textStatus, error) {
$("#vm-details-resources-save i").removeClass('icon-refresh icon-spin').addClass("icon-save");
addMessage("Eww, something is wrong", 'danger');
if (xhr.status == 500) {
// alert("uhuhuhuhuhuh");
} else {
// alert("unknown error");
}
}
});
return false;
});
/* rename */
$("#vm-details-h1-name, .vm-details-rename-button").click(function() {
$("#vm-details-h1-name").hide();
$("#vm-details-rename").css('display', 'inline');
});
/* rename ajax */
$('#vm-details-rename-submit').click(function() {
var name = $('#vm-details-rename-name').val();
$.ajax({
method: 'POST',
url: location.href,
data: {'new_name': name},
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(data, textStatus, xhr) {
$("#vm-details-h1-name").html(data['new_name']).show();
$('#vm-details-rename').hide();
// addMessage(data['message'], "success");
},
error: function(xhr, textStatus, error) {
addMessage("uhoh", "danger");
}
});
return false;
});
/* remove tag */
$('.vm-details-remove-tag').click(function() {
var to_remove = $.trim($(this).parent('div').text());
var clicked = $(this);
$.ajax({
type: 'POST',
url: location.href,
headers: {"X-CSRFToken": getCookie('csrftoken')},
data: {'to_remove': to_remove},
success: function(re) {
if(re['message'].toLowerCase() == "success") {
$(clicked).closest(".label").fadeOut(500, function() {
$(this).remove();
});
}
},
error: function() {
addMessage(re['message'], 'danger');
}
});
return false;
});
});
function checkNewActivity() {
var latest = $('.activity:first').data('activity-id');
var latest_sub = $('div[data-activity-id="' + latest + '"] .sub-timeline .sub-activity:first').data('activity-id');
var instance = location.href.split('/'); instance = instance[instance.length - 2];
$.ajax({
type: 'POST',
url: '/dashboard/vm/' + instance + '/activity/',
headers: {"X-CSRFToken": getCookie('csrftoken')},
data: {'latest': latest, 'latest_sub': latest_sub},
success: function(data) {
if(data['new_sub_activities'].length > 0) {
d = data['new_sub_activities'];
html = ""
for(var i=0; i<d.length; i++) {
html += '<div data-activity-id="' + d[i].id + '" class="sub-activity">' + d[i].name + ' - ';
if(d[i].finished != null) {
html += d[i].finished
} else {
html += '<i class="icon-refresh icon-spin" class="sub-activity-loading-icon"></i>';
}
html += '</div>';
}
$('div[data-activity-id="' + latest_sub + '"] .sub-activity .sub-activity-loading-icon').remove();
$('div[data-activity-id="' + latest + '"] .sub-timeline').prepend(html);
}
if(data['is_parent_finished']) {
var c = "icon-plus"
$('div[data-activity-id="' + latest + '"] .icon-refresh.icon-spin:first').removeClass('icon-refresh').removeClass('icon-spin').addClass(c);
}
if(data['latest_sub_finished'] != null) {
s = $('div[data-activity-id="' + latest_sub + '"]')
$('.icon-refresh.icon-spin', s).remove();
$(s).append(data['latest_sub_finished']);
}
if(data['is_parent_finished'])
return;
else
setTimeout(checkNewActivity, 1000);
},
error: function() {
}
});
}
...@@ -126,7 +126,7 @@ $(function() { ...@@ -126,7 +126,7 @@ $(function() {
// addMessage(data['message'], "success"); // addMessage(data['message'], "success");
}, },
error: function(xhr, textStatus, error) { error: function(xhr, textStatus, error) {
addMessage("uhoh", "danger"); addMessage("Error during renaming!", "danger");
} }
}); });
return false; return false;
...@@ -191,7 +191,7 @@ $(function() { ...@@ -191,7 +191,7 @@ $(function() {
onsuccess(params); onsuccess(params);
}, },
error: function(xhr, textStatus, error) { error: function(xhr, textStatus, error) {
addMessage("uhoh", "danger"); addMessage("Error!", "danger");
} }
}); });
return false; return false;
......
...@@ -202,10 +202,12 @@ function vmCreateLoaded() { ...@@ -202,10 +202,12 @@ function vmCreateLoaded() {
} }
}, },
error: function(xhr, textStatus, error) { error: function(xhr, textStatus, error) {
var r = $('#create-modal'); r.next('div').remove(); r.remove();
if (xhr.status == 500) { if (xhr.status == 500) {
alert("uhuhuhuhuhuh"); addMessage("500 Internal Server Error", "danger");
} else { } else {
alert("unknown error"); addMessage(xhr.status + " Unknown Error", "danger");
} }
} }
}); });
......
...@@ -15,11 +15,10 @@ $(function() { ...@@ -15,11 +15,10 @@ $(function() {
}, },
error: function(xhr, textStatus, error) { error: function(xhr, textStatus, error) {
$("#vm-details-resources-save i").removeClass('icon-refresh icon-spin').addClass("icon-save"); $("#vm-details-resources-save i").removeClass('icon-refresh icon-spin').addClass("icon-save");
addMessage("Eww, something is wrong", 'danger');
if (xhr.status == 500) { if (xhr.status == 500) {
// alert("uhuhuhuhuhuh"); addMessage("500 Internal Server Error", "danger");
} else { } else {
// alert("unknown error"); addMessage(xhr.status + " Unknown Error", "danger");
} }
} }
}); });
...@@ -47,7 +46,7 @@ $(function() { ...@@ -47,7 +46,7 @@ $(function() {
// addMessage(data['message'], "success"); // addMessage(data['message'], "success");
}, },
error: function(xhr, textStatus, error) { error: function(xhr, textStatus, error) {
addMessage("uhoh", "danger"); addMessage("Error during renaming!", "danger");
} }
}); });
return false; return false;
......
...@@ -118,7 +118,7 @@ $(function() { ...@@ -118,7 +118,7 @@ $(function() {
// addMessage(data['message'], "success"); // addMessage(data['message'], "success");
}, },
error: function(xhr, textStatus, error) { error: function(xhr, textStatus, error) {
addMessage("uhoh", "danger"); addMessage("Error during renaming!", "danger");
} }
}); });
return false; return false;
......
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-8">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.views.template-list" %}">Back</a> <a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.views.template-list" %}">Back</a>
<h3 class="no-margin"><i class="icon-desktop"></i> Edit template</h3> <h3 class="no-margin"><i class="icon-desktop"></i> {% trans "Edit template" %}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
{% with form=form %} {% with form=form %}
...@@ -18,8 +18,56 @@ ...@@ -18,8 +18,56 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="no-margin"><i class="icon-group"></i> {% trans "Manage access" %}</h3>
</div>
<div class="panel-body">
<form action="{% url "dashboard.views.template-acl" pk=object.pk %}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields">
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead>
<tbody>
{% for i in acl.users %}
<tr><td><i class="icon-user"></i></td><td>{{i.user}}</td>
<td><select class="form-control" name="perm-u-{{i.user.id}}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select></td>
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
{% endfor %}
{% for i in acl.groups %}
<tr><td><i class="icon-group"></i></td><td>{{i.group}}</td>
<td><select class="form-control" name="perm-g-{{i.group.id}}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select></td>
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
{% endfor %}
<tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}"></td>
<td><select class="form-control" name="perm-new">
{% for id, name in acl.levels %}
<option value="{{id}}">{{name}}</option>
{% endfor %}
</select></td><td></td>
</tr>
</tbody>
</table>
<div class="form-actions">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div>
</form>
</div>
</div>
</div>
</div> </div>
<style> <style>
fieldset { fieldset {
margin-top: 40px; margin-top: 40px;
......
...@@ -13,7 +13,11 @@ ...@@ -13,7 +13,11 @@
<a title="Start" href="#" class="btn btn-default btn-xs"><i class="icon-play"></i></a> <a title="Start" href="#" class="btn btn-default btn-xs"><i class="icon-play"></i></a>
<a title="Wake up" href="#" class="btn btn-default btn-xs"><i class="icon-sun"></i></a> <a title="Wake up" href="#" class="btn btn-default btn-xs"><i class="icon-sun"></i></a>
{% endif %} {% endif %}
<a title="Shut down" href="#" class="btn btn-default btn-xs"><i class="icon-off"></i></a> <form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="shut_down" value="dummy"/>
<button title="Shut down" class="btn btn-default btn-xs" type="submit"><i class="icon-off"></i></button>
</form>
<a title="Migrate" href="#" class="btn btn-default btn-xs"><i class="icon-truck"></i></a> <a title="Migrate" href="#" class="btn btn-default btn-xs"><i class="icon-truck"></i></a>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}"> <form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %} {% csrf_token %}
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<i class="{% if not a.finished %} icon-refresh icon-spin {% else %}icon-plus{% endif %}"></i> <i class="{% if not a.finished %} icon-refresh icon-spin {% else %}icon-plus{% endif %}"></i>
</span> </span>
<strong>{{ a.get_readable_name }}</strong> <strong>{{ a.get_readable_name }}</strong>
{{ a.started|date:"Y-m-d. H:i" }}, {{ a.user }} {{ a.started|date:"Y-m-d H:i" }}, {{ a.user }}
{% if a.children.count > 0 %} {% if a.children.count > 0 %}
<div class="sub-timeline"> <div class="sub-timeline">
{% for s in a.children.all %} {% for s in a.children.all %}
...@@ -32,12 +32,10 @@ ...@@ -32,12 +32,10 @@
{% endif %} {% endif %}
</div> </div>
{% endfor %} {% endfor %}
<div><span class="timeline-icon timeline-warning"><i class="icon-remove"></i></span> <strong>Removing</strong> 2013-11-21 15:32</div> <div>
<div><span class="timeline-icon timeline-warning"><i class="icon-pause"></i></span> <strong>Suspending</strong> 2013-09-21 15:32</div> <span class="timeline-icon"><i class="icon-plus"></i></span> <strong>{% trans "Created" %}</strong>
<div><span class="timeline-icon"><i class="icon-ellipsis-vertical" ></i></span> <strong>(now)</strong></div> {{ instance.created|date:"Y-m-d H:i" }}, {{ instance.owner }}
<div><span class="timeline-icon"><i class="icon-truck"></i></span> <strong>Migrated to mega5</strong> 2013-04-21 15:32, ABC123</div> </div>
<div><span class="timeline-icon"><i class="icon-refresh"></i></span> <strong>Forced reboot</strong> 2013-04-21 15:32, ABC123</div>
<div><span class="timeline-icon"><i class="icon-plus"></i></span> <strong>Created</strong> 2013-04-21 15:32, ABC123</div>
</div> </div>
{% block extra_js %} {% block extra_js %}
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
<div class="col-md-4"> <div class="col-md-4">
<dl> <dl>
<dt>System:</dt> <dt>System:</dt>
<dd><i class="icon-linux"></i> Uhu Binux Optikai Rendszer</dd> <dd><i class="icon-{{ os_type_icon }}"></i> {{ instance.system }}</dd>
<dt>Description:</dt> <dt style="margin-top: 5px;">Description:</dt>
<dd><small>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc est libero, hendrerit at posuere sed, molestie congue quam. </small></dd> <dd><small>{{ instance.description }}</small></dd>
</dl> </dl>
<div style="font-weight: bold;">{% trans "Tags" %}</div> <div style="font-weight: bold;">{% trans "Tags" %}</div>
......
...@@ -7,7 +7,7 @@ from .views import ( ...@@ -7,7 +7,7 @@ from .views import (
TransferOwnershipView, TransferOwnershipConfirmView, NodeDelete, TransferOwnershipView, TransferOwnershipConfirmView, NodeDelete,
TemplateList, LeaseDetail, NodeCreate, LeaseCreate, TemplateCreate, TemplateList, LeaseDetail, NodeCreate, LeaseCreate, TemplateCreate,
FavouriteView, NodeStatus, GroupList, TemplateDelete, LeaseDelete, FavouriteView, NodeStatus, GroupList, TemplateDelete, LeaseDelete,
VmGraphView, VmGraphView, TemplateAclUpdateView
) )
urlpatterns = patterns( urlpatterns = patterns(
...@@ -19,14 +19,18 @@ urlpatterns = patterns( ...@@ -19,14 +19,18 @@ urlpatterns = patterns(
name="dashboard.views.lease-create"), name="dashboard.views.lease-create"),
url(r'^lease/delete/(?P<pk>\d+)/$', LeaseDelete.as_view(), url(r'^lease/delete/(?P<pk>\d+)/$', LeaseDelete.as_view(),
name="dashboard.views.lease-delete"), name="dashboard.views.lease-delete"),
url(r'^template/create/$', TemplateCreate.as_view(), url(r'^template/create/$', TemplateCreate.as_view(),
name="dashboard.views.template-create"), name="dashboard.views.template-create"),
url(r'template/(?P<pk>\d+)/acl/$', TemplateAclUpdateView.as_view(),
name='dashboard.views.template-acl'),
url(r'^template/(?P<pk>\d+)/$', TemplateDetail.as_view(), url(r'^template/(?P<pk>\d+)/$', TemplateDetail.as_view(),
name='dashboard.views.template-detail'), name='dashboard.views.template-detail'),
url(r"^template/list/$", TemplateList.as_view(), url(r"^template/list/$", TemplateList.as_view(),
name="dashboard.views.template-list"), name="dashboard.views.template-list"),
url(r"^template/delete/(?P<pk>\d+)/$", TemplateDelete.as_view(), url(r"^template/delete/(?P<pk>\d+)/$", TemplateDelete.as_view(),
name="dashboard.views.template-delete"), name="dashboard.views.template-delete"),
url(r'^vm/(?P<pk>\d+)/remove_port/(?P<rule>\d+)/$', PortDelete.as_view(), url(r'^vm/(?P<pk>\d+)/remove_port/(?P<rule>\d+)/$', PortDelete.as_view(),
name='dashboard.views.remove-port'), name='dashboard.views.remove-port'),
url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(), url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(),
...@@ -43,6 +47,7 @@ urlpatterns = patterns( ...@@ -43,6 +47,7 @@ urlpatterns = patterns(
url(r'^vm/mass-delete/', VmMassDelete.as_view(), url(r'^vm/mass-delete/', VmMassDelete.as_view(),
name='dashboard.view.mass-delete-vm'), name='dashboard.view.mass-delete-vm'),
url(r'^vm/(?P<pk>\d+)/activity/$', vm_activity), url(r'^vm/(?P<pk>\d+)/activity/$', vm_activity),
url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'), url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'),
url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(), url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(),
name='dashboard.views.node-detail'), name='dashboard.views.node-detail'),
...@@ -54,8 +59,10 @@ urlpatterns = patterns( ...@@ -54,8 +59,10 @@ urlpatterns = patterns(
name="dashboard.views.status-node"), name="dashboard.views.status-node"),
url(r'^node/create/$', NodeCreate.as_view(), url(r'^node/create/$', NodeCreate.as_view(),
name='dashboard.views.node-create'), name='dashboard.views.node-create'),
url(r'^favourite/$', FavouriteView.as_view(), url(r'^favourite/$', FavouriteView.as_view(),
name='dashboard.views.favourite'), name='dashboard.views.favourite'),
url(r'^group/list/$', GroupList.as_view(), url(r'^group/list/$', GroupList.as_view(),
name='dashboard.views.group-list'), name='dashboard.views.group-list'),
url((r'^vm/(?P<pk>\d+)/graph/(?P<metric>cpu|memory|network)/' url((r'^vm/(?P<pk>\d+)/graph/(?P<metric>cpu|memory|network)/'
......
...@@ -157,6 +157,8 @@ class VmDetailView(CheckedDetailView): ...@@ -157,6 +157,8 @@ class VmDetailView(CheckedDetailView):
context['forms'] = { context['forms'] = {
'disk_add_form': DiskAddForm(prefix="disk"), 'disk_add_form': DiskAddForm(prefix="disk"),
} }
context['os_type_icon'] = instance.os_type.replace("unknown",
"question")
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
...@@ -173,6 +175,7 @@ class VmDetailView(CheckedDetailView): ...@@ -173,6 +175,7 @@ class VmDetailView(CheckedDetailView):
'new_network_vlan': self.__new_network, 'new_network_vlan': self.__new_network,
'save_as': self.__save_as, 'save_as': self.__save_as,
'disk-name': self.__add_disk, 'disk-name': self.__add_disk,
'shut_down': self.__shut_down,
} }
for k, v in options.iteritems(): for k, v in options.iteritems():
...@@ -359,6 +362,15 @@ class VmDetailView(CheckedDetailView): ...@@ -359,6 +362,15 @@ class VmDetailView(CheckedDetailView):
return redirect("%s#resources" % reverse_lazy( return redirect("%s#resources" % reverse_lazy(
"dashboard.views.detail", kwargs={'pk': self.object.pk})) "dashboard.views.detail", kwargs={'pk': self.object.pk}))
def __shut_down(self, request):
self.object = self.get_object()
if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied()
self.object.shutdown_async(request.user)
return redirect("%s#activity" % reverse_lazy(
"dashboard.views.detail", kwargs={'pk': self.object.pk}))
class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView): class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
template_name = "dashboard/node-detail.html" template_name = "dashboard/node-detail.html"
...@@ -446,7 +458,7 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): ...@@ -446,7 +458,7 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin):
if m: if m:
typ, id = m.groups() typ, id = m.groups()
entity = {'u': User, 'g': Group}[typ].objects.get(id=id) entity = {'u': User, 'g': Group}[typ].objects.get(id=id)
if instance.owner == entity: if getattr(instance, "owner", None) == entity:
logger.info("Tried to set owner's acl level for %s by %s.", logger.info("Tried to set owner's acl level for %s by %s.",
unicode(instance), unicode(request.user)) unicode(instance), unicode(request.user))
continue continue
...@@ -476,6 +488,29 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): ...@@ -476,6 +488,29 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin):
value, unicode(request.user)) value, unicode(request.user))
class TemplateAclUpdateView(AclUpdateView):
model = InstanceTemplate
def post(self, request, *args, **kwargs):
template = self.get_object()
if not (template.has_level(request.user, "owner") or
getattr(template, 'owner', None) == request.user):
logger.warning('Tried to set permissions of %s by non-owner %s.',
unicode(template), unicode(request.user))
raise PermissionDenied()
self.set_levels(request, template)
self.add_levels(request, template)
post_for_disk = request.POST.copy()
post_for_disk['perm-new'] = 'user'
request.POST = post_for_disk
for d in template.disks.all():
self.add_levels(request, d)
return redirect(reverse("dashboard.views.template-detail",
kwargs=self.kwargs))
class TemplateCreate(SuccessMessageMixin, CreateView): class TemplateCreate(SuccessMessageMixin, CreateView):
model = InstanceTemplate model = InstanceTemplate
form_class = TemplateForm form_class = TemplateForm
...@@ -485,27 +520,28 @@ class TemplateCreate(SuccessMessageMixin, CreateView): ...@@ -485,27 +520,28 @@ class TemplateCreate(SuccessMessageMixin, CreateView):
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
if not self.request.user.has_perm('vm.create_template'): if not self.request.user.has_perm('vm.create_template'):
raise PermissionDenied() raise PermissionDenied()
form = self.form_class()
form.fields['disks'].queryset = Disk.get_objects_with_level(
'user', self.request.user).exclude(type="qcow2-snap")
self.parent = self.request.GET.get("parent") self.parent = self.request.GET.get("parent")
return super(TemplateCreate, self).get(*args, **kwargs) return super(TemplateCreate, self).get(*args, **kwargs)
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super(TemplateCreate, self).get_form_kwargs() kwargs = super(TemplateCreate, self).get_form_kwargs()
kwargs['parent'] = getattr(self, "parent", None) kwargs['parent'] = getattr(self, "parent", None)
kwargs['user'] = self.request.user
return kwargs return kwargs
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
if not self.request.user.has_perm('vm.create_template'): if not self.request.user.has_perm('vm.create_template'):
raise PermissionDenied() raise PermissionDenied()
form = self.form_class(request.POST)
form = self.form_class(request.POST, user=request.user)
if not form.is_valid(): if not form.is_valid():
return self.get(request, form, *args, **kwargs) return self.get(request, form, *args, **kwargs)
post = form.cleaned_data post = form.cleaned_data
for disk in post['disks']: for disk in post['disks']:
if not disk.has_level(request.user, 'user'): if not disk.has_level(request.user, 'user'):
raise PermissionDenied() raise PermissionDenied()
return super(TemplateCreate, self).post(self, request, args, kwargs) return super(TemplateCreate, self).post(self, request, args, kwargs)
def get_success_url(self): def get_success_url(self):
...@@ -545,6 +581,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -545,6 +581,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
else: else:
return super(TemplateDetail, self).get(request, *args, **kwargs) return super(TemplateDetail, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(TemplateDetail, self).get_context_data(**kwargs)
context['acl'] = get_acl_data(self.get_object())
return context
def get_success_url(self): def get_success_url(self):
return reverse_lazy("dashboard.views.template-detail", return reverse_lazy("dashboard.views.template-detail",
kwargs=self.kwargs) kwargs=self.kwargs)
...@@ -558,6 +599,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -558,6 +599,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
raise PermissionDenied() raise PermissionDenied()
return super(TemplateDetail, self).post(self, request, args, kwargs) return super(TemplateDetail, self).post(self, request, args, kwargs)
def get_form_kwargs(self):
kwargs = super(TemplateDetail, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
class TemplateList(LoginRequiredMixin, SingleTableView): class TemplateList(LoginRequiredMixin, SingleTableView):
template_name = "dashboard/template-list.html" template_name = "dashboard/template-list.html"
...@@ -684,7 +730,6 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -684,7 +730,6 @@ class VmCreate(LoginRequiredMixin, TemplateView):
}) })
return self.render_to_response(context) return self.render_to_response(context)
# TODO handle not ajax posts
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
form = self.form_class(request.POST) form = self.form_class(request.POST)
if not form.is_valid(): if not form.is_valid():
...@@ -692,6 +737,22 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -692,6 +737,22 @@ class VmCreate(LoginRequiredMixin, TemplateView):
post = form.cleaned_data post = form.cleaned_data
user = request.user user = request.user
try:
limit = user.profile.instance_limit
except Exception as e:
logger.debug('No profile or instance limit: %s', e)
else:
current = Instance.active.filter(owner=user).count()
logger.debug('current use: %d, limit: %d', current, limit)
if limit < current:
messages.error(request,
_('Instance limit (%d) exceeded.') % limit)
if request.is_ajax():
return HttpResponse(json.dumps({'redirect': '/'}),
content_type="application/json")
else:
return redirect('/')
template = post['template'] template = post['template']
if not template.has_level(request.user, 'user'): if not template.has_level(request.user, 'user'):
raise PermissionDenied() raise PermissionDenied()
...@@ -1241,19 +1302,21 @@ class VmGraphView(LoginRequiredMixin, View): ...@@ -1241,19 +1302,21 @@ class VmGraphView(LoginRequiredMixin, View):
if not instance.has_level(request.user, 'user'): if not instance.has_level(request.user, 'user'):
raise PermissionDenied() raise PermissionDenied()
prefix = 'vm.%s' % instance.vm_name targets = {
if metric == 'cpu': 'cpu': ('cactiStyle(alias(derivative(%s.cpu.usage),'
target = ('cactiStyle(alias(derivative(%s.cpu.usage),' '"cpu usage (%%)"))'),
'"cpu usage (%%)"))') % prefix 'memory': ('cactiStyle(alias(%s.memory.usage,'
elif metric == 'memory': '"memory usage (%%)"))'),
target = ('cactiStyle(alias(%s.memory.usage,' 'network': ('cactiStyle(aliasByMetric('
'"memory usage (%%)"))') % prefix 'derivative(%s.network.bytes_*)))'),
elif metric == 'network': }
target = ('cactiStyle(aliasByMetric('
'derivative(%s.network.bytes_*)))') % prefix if metric not in targets.keys():
else:
raise SuspiciousOperation() raise SuspiciousOperation()
prefix = 'vm.%s' % instance.vm_name
target = targets[metric] % prefix
title = '%s (%s) - %s' % (instance.name, instance.vm_name, metric) title = '%s (%s) - %s' % (instance.name, instance.vm_name, metric)
params = urlencode({'target': target, params = urlencode({'target': target,
......
...@@ -119,6 +119,7 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -119,6 +119,7 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
disks = ManyToManyField(Disk, verbose_name=_('disks'), disks = ManyToManyField(Disk, verbose_name=_('disks'),
related_name='template_set', related_name='template_set',
help_text=_('Disks which are to be mounted.')) help_text=_('Disks which are to be mounted.'))
owner = ForeignKey(User)
class Meta: class Meta:
app_label = 'vm' app_label = 'vm'
...@@ -148,6 +149,12 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -148,6 +149,12 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
else: else:
return 'linux' return 'linux'
def save(self, *args, **kwargs):
is_new = getattr(self, "pk", None) is None
super(InstanceTemplate, self).save(*args, **kwargs)
if is_new:
self.set_level(self.owner, 'owner')
class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel): class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel):
...@@ -419,6 +426,24 @@ class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -419,6 +426,24 @@ class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel):
else: else:
return timedelta() # zero return timedelta() # zero
@property
def os_type(self):
"""Get the type of the instance's operating system.
"""
if self.template is None:
return "unknown"
else:
return self.template.os_type
@property
def system(self):
"""Get the instance's operating system.
"""
if self.template is None:
return _("Unknown")
else:
return self.template.system
def get_age(self): def get_age(self):
"""Deprecated. Use uptime instead. """Deprecated. Use uptime instead.
......
...@@ -117,8 +117,9 @@ class Node(TimeStampedModel): ...@@ -117,8 +117,9 @@ class Node(TimeStampedModel):
try: try:
handler = GraphiteHandler() handler = GraphiteHandler()
except: except:
collected["cpu.usage"] = None response = self.remote_query(vm_tasks.get_node_metrics, 30)
collected["memory.usage"] = self.ram_size collected['cpu.usage'] = response['cpu.usage']
collected['memory.usage'] = response['memory.usage']
return collected return collected
query = Query() query = Query()
query.set_target(self.host.hostname + ".circle") query.set_target(self.host.hostname + ".circle")
......
...@@ -79,3 +79,8 @@ def get_core_num(params): ...@@ -79,3 +79,8 @@ def get_core_num(params):
@celery.task(name='vmdriver.get_ram_size') @celery.task(name='vmdriver.get_ram_size')
def get_ram_size(params): def get_ram_size(params):
pass pass
@celery.task(name='vmdriver.get_node_metrics')
def get_node_metrics(params):
pass
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