Commit f2577fb3 by Őry Máté

one: enforce pep8 and others

parent 1b2fb7a6
from django.contrib import messages
from django.core.exceptions import ValidationError
from django import contrib from django import contrib
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from one import models from one import models
import string import school.models
def owner_person(obj): def owner_person(obj):
p = obj.owner p = obj.owner
return "%s %s (%s)" % (p.last_name, p.first_name, p.username) return "%s %s (%s)" % (p.last_name, p.first_name, p.username)
owner_person.short_description = _('owner') owner_person.short_description = _('owner')
class PersonInline(contrib.admin.StackedInline): class PersonInline(contrib.admin.StackedInline):
model = models.Person model = school.models.Person
max_num = 1 max_num = 1
can_delete = False can_delete = False
class SshKeyInline(contrib.admin.TabularInline): class SshKeyInline(contrib.admin.TabularInline):
model = models.SshKey model = models.SshKey
extra = 2 extra = 2
class DetailsInline(contrib.admin.StackedInline): class DetailsInline(contrib.admin.StackedInline):
model = models.UserCloudDetails model = models.UserCloudDetails
max_num = 1 max_num = 1
can_delete = False can_delete = False
class MyUserAdmin(contrib.auth.admin.UserAdmin):
list_display = ('username', 'full_name', 'email', 'date_joined', 'instance_count', 'course_groups')
list_filter = ('is_superuser', 'is_active', 'groups', 'person__course_groups', )
class MyUserAdmin(contrib.auth.admin.UserAdmin):
list_display = ('username', 'full_name', 'email',
'date_joined', 'instance_count', 'course_groups')
list_filter = ('is_superuser', 'is_active',
'groups', 'person__course_groups', )
def __init__(self, *args, **kwargs):
super(MyUserAdmin, self).__init__(*args, **kwargs)
add = [PersonInline, SshKeyInline, DetailsInline, ]
try: try:
inlines = inlines + (PersonInline, SshKeyInline, DetailsInline) inlines = self.inlines
except NameError: except NameError:
inlines = (PersonInline, SshKeyInline, DetailsInline) inlines = list()
self.inlines = inlines + add
def instance_count(self, obj): def instance_count(self, obj):
return _("%(sum)d (%(active)d active)") % { 'sum': obj.instance_set.count(), return _("%(sum)d (%(active)d active)") % {
'active' :obj.instance_set.filter(state='ACTIVE').count(), } 'sum': obj.instance_set.count(),
'active': obj.instance_set.filter(state='ACTIVE').count(), }
def course_groups(self, obj): def course_groups(self, obj):
try: try:
...@@ -48,31 +61,35 @@ class MyUserAdmin(contrib.auth.admin.UserAdmin): ...@@ -48,31 +61,35 @@ class MyUserAdmin(contrib.auth.admin.UserAdmin):
ordering = ["-date_joined"] ordering = ["-date_joined"]
contrib.admin.site.unregister(contrib.auth.models.User) contrib.admin.site.unregister(contrib.auth.models.User)
contrib.admin.site.register(contrib.auth.models.User, MyUserAdmin) contrib.admin.site.register(contrib.auth.models.User, MyUserAdmin)
def update_state(modeladmin, request, queryset): def update_state(modeladmin, request, queryset):
for i in queryset.all(): for i in queryset.all():
i.update_state() i.update_state()
update_state.short_description = _('Update status') update_state.short_description = _('Update status')
def submit_vm(modeladmin, request, queryset): def submit_vm(modeladmin, request, queryset):
for i in queryset.all(): for i in queryset.all():
i.submit(request.user) i.submit(request.user)
i.update_state() i.update_state()
submit_vm.short_description = _('Submit VM') submit_vm.short_description = _('Submit VM')
def delete_vm(modeladmin, request, queryset): def delete_vm(modeladmin, request, queryset):
for i in queryset.exclude(state='DONE').all(): for i in queryset.exclude(state='DONE').all():
i.one_delete() i.one_delete()
delete_vm.short_description = _('Delete VM') delete_vm.short_description = _('Delete VM')
def suspend_vm(modeladmin, request, queryset): def suspend_vm(modeladmin, request, queryset):
for i in queryset.filter(state='ACTIVE').all(): for i in queryset.filter(state='ACTIVE').all():
i.stop() i.stop()
suspend_vm.short_description = _('Suspend VM') suspend_vm.short_description = _('Suspend VM')
def resume_vm(modeladmin, request, queryset): def resume_vm(modeladmin, request, queryset):
for i in queryset.filter(state__in=('STOPPED', 'SUSPENDED')).all(): for i in queryset.filter(state__in=('STOPPED', 'SUSPENDED')).all():
i.resume() i.resume()
...@@ -80,21 +97,24 @@ resume_vm.short_description = _('Resume VM') ...@@ -80,21 +97,24 @@ resume_vm.short_description = _('Resume VM')
class TemplateAdmin(contrib.admin.ModelAdmin): class TemplateAdmin(contrib.admin.ModelAdmin):
model=models.Template model = models.Template
list_display = ('name', 'state', owner_person, 'system', 'public') list_display = ('name', 'state', owner_person, 'system', 'public')
list_filter = ('owner', 'public') list_filter = ('owner', 'public')
class InstanceAdmin(contrib.admin.ModelAdmin): class InstanceAdmin(contrib.admin.ModelAdmin):
model=models.Instance model = models.Instance
actions = [update_state, submit_vm, delete_vm, suspend_vm, resume_vm] actions = [update_state, submit_vm, delete_vm, suspend_vm, resume_vm]
list_display = ('id', 'name', owner_person, 'state') list_display = ('id', 'name', owner_person, 'state')
readonly_fields = ('ip', 'active_since', 'pw', 'template') readonly_fields = ('ip', 'active_since', 'pw', 'template')
list_filter = ('owner', 'template', 'state') list_filter = ('owner', 'template', 'state')
def queryset(self, request): def queryset(self, request):
return super(InstanceAdmin, self).queryset(request) return super(InstanceAdmin, self).queryset(request)
class DiskAdmin(contrib.admin.ModelAdmin): class DiskAdmin(contrib.admin.ModelAdmin):
model=models.Disk model = models.Disk
list_display = ('name', 'used_by') list_display = ('name', 'used_by')
def used_by(self, obj): def used_by(self, obj):
...@@ -104,12 +124,14 @@ class DiskAdmin(contrib.admin.ModelAdmin): ...@@ -104,12 +124,14 @@ class DiskAdmin(contrib.admin.ModelAdmin):
return None return None
used_by.verbose_name = _('used by') used_by.verbose_name = _('used by')
class NetworkAdmin(contrib.admin.ModelAdmin): class NetworkAdmin(contrib.admin.ModelAdmin):
model=models.Network model = models.Network
list_display = ('name', 'nat', 'public', 'get_vlan') list_display = ('name', 'nat', 'public', 'get_vlan')
class ShareAdmin(contrib.admin.ModelAdmin): class ShareAdmin(contrib.admin.ModelAdmin):
model=models.Network model = models.Network
list_filter = ('group', 'template', ) list_filter = ('group', 'template', )
list_display = ('name', owner_person, 'template', 'group', ) list_display = ('name', owner_person, 'template', 'group', )
...@@ -119,4 +141,3 @@ contrib.admin.site.register(models.Network, NetworkAdmin) ...@@ -119,4 +141,3 @@ contrib.admin.site.register(models.Network, NetworkAdmin)
contrib.admin.site.register(models.Disk, DiskAdmin) contrib.admin.site.register(models.Disk, DiskAdmin)
contrib.admin.site.register(models.Share, ShareAdmin) contrib.admin.site.register(models.Share, ShareAdmin)
contrib.admin.site.register(models.InstanceType) contrib.admin.site.register(models.InstanceType)
...@@ -2,20 +2,21 @@ ...@@ -2,20 +2,21 @@
from datetime import datetime from datetime import datetime
from datetime import timedelta as td from datetime import timedelta as td
import subprocess, tempfile, os, stat, re, base64, struct, logging import base64
import struct
import logging
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core import signing from django.core import signing
from django.db import models from django.db import models
from django.db import transaction from django.db import transaction
from django.db.models.signals import post_delete, pre_delete from django.db.models.signals import post_save, pre_delete
from django.db.models.signals import post_save from django.template.defaultfilters import escape
from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from firewall.models import Host, Rule, Vlan, Record from firewall.models import Host, Vlan
from school.models import Person, Group from school.models import Group
from store.api import StoreApi from store.api import StoreApi
from .util import keygen from .util import keygen
import django.conf import django.conf
...@@ -25,6 +26,7 @@ CLOUD_URL = django.conf.settings.CLOUD_URL ...@@ -25,6 +26,7 @@ CLOUD_URL = django.conf.settings.CLOUD_URL
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
pwgen = User.objects.make_random_password pwgen = User.objects.make_random_password
def create_user_profile(sender, instance, created, **kwargs): def create_user_profile(sender, instance, created, **kwargs):
"""User creation hook: create cloud details object""" """User creation hook: create cloud details object"""
if created: if created:
...@@ -34,27 +36,33 @@ def create_user_profile(sender, instance, created, **kwargs): ...@@ -34,27 +36,33 @@ def create_user_profile(sender, instance, created, **kwargs):
post_save.connect(create_user_profile, sender=User) post_save.connect(create_user_profile, sender=User)
class UserCloudDetails(models.Model): class UserCloudDetails(models.Model):
"""Cloud related details of a user.""" """Cloud related details of a user."""
user = models.OneToOneField(User, verbose_name=_('user'), user = models.OneToOneField(User, verbose_name=_('user'),
related_name='cloud_details') related_name='cloud_details')
smb_password = models.CharField(max_length=20, smb_password = models.CharField(max_length=20,
verbose_name=_('Samba password'), verbose_name=_('Samba password'),
help_text=_('Generated password for accessing store from ' help_text=_('Generated password for '
'accessing store from '
'Windows.')) 'Windows.'))
ssh_key = models.ForeignKey('SshKey', verbose_name=_('SSH key (public)'), ssh_key = models.ForeignKey('SshKey', verbose_name=_('SSH key (public)'),
null=True, blank=True, related_name='userclouddetails_set', null=True, blank=True,
help_text=_('Generated SSH public key for accessing store from ' related_name='userclouddetails_set',
'Linux.')) help_text=_('Generated SSH public key for '
ssh_private_key = models.TextField(verbose_name=_('SSH key (private)'),
blank=True, help_text=_('Generated SSH private key for '
'accessing store from Linux.')) 'accessing store from Linux.'))
ssh_private_key = models.TextField(verbose_name=_('SSH key (private)'),
blank=True,
help_text=_('Generated SSH private key '
'for accessing store from '
'Linux.'))
share_quota = models.IntegerField(verbose_name=_('share quota'), share_quota = models.IntegerField(verbose_name=_('share quota'),
default=0) default=0)
instance_quota = models.IntegerField(verbose_name=_('instance quota'), instance_quota = models.IntegerField(verbose_name=_('instance quota'),
default=20) default=20)
disk_quota = models.IntegerField(verbose_name=_('disk quota'), disk_quota = models.IntegerField(verbose_name=_('disk quota'),
default=2048, help_text=_('Disk quota in mebibytes.')) default=2048,
help_text=_('Disk quota in mebibytes.'))
def reset_keys(self): def reset_keys(self):
"""Delete old SSH key pair and generate new one.""" """Delete old SSH key pair and generate new one."""
...@@ -79,16 +87,20 @@ class UserCloudDetails(models.Model): ...@@ -79,16 +87,20 @@ class UserCloudDetails(models.Model):
if i.state in ('ACTIVE', 'PENDING', ): if i.state in ('ACTIVE', 'PENDING', ):
c = c + i.template.instance_type.credit c = c + i.template.instance_type.credit
return c return c
def get_instance_pc(self): def get_instance_pc(self):
return 100 * self.get_weighted_instance_count() / self.instance_quota return 100 * self.get_weighted_instance_count() / self.instance_quota
def get_weighted_share_count(self): def get_weighted_share_count(self):
c = 0 c = 0
for i in Share.objects.filter(owner=self.user).all(): for i in Share.objects.filter(owner=self.user).all():
c = c + i.template.instance_type.credit * i.instance_limit c = c + i.template.instance_type.credit * i.instance_limit
return c return c
def get_share_pc(self): def get_share_pc(self):
return 100 * self.get_weighted_share_count() / self.share_quota return 100 * self.get_weighted_share_count() / self.share_quota
def set_quota(sender, instance, created, **kwargs): def set_quota(sender, instance, created, **kwargs):
if not StoreApi.userexist(instance.user.username): if not StoreApi.userexist(instance.user.username):
try: try:
...@@ -108,6 +120,7 @@ def set_quota(sender, instance, created, **kwargs): ...@@ -108,6 +120,7 @@ def set_quota(sender, instance, created, **kwargs):
instance.disk_quota * 1024) instance.disk_quota * 1024)
post_save.connect(set_quota, sender=UserCloudDetails) post_save.connect(set_quota, sender=UserCloudDetails)
def reset_keys(sender, instance, created, **kwargs): def reset_keys(sender, instance, created, **kwargs):
if created: if created:
instance.reset_smb() instance.reset_smb()
...@@ -115,6 +128,7 @@ def reset_keys(sender, instance, created, **kwargs): ...@@ -115,6 +128,7 @@ def reset_keys(sender, instance, created, **kwargs):
post_save.connect(reset_keys, sender=UserCloudDetails) post_save.connect(reset_keys, sender=UserCloudDetails)
class OpenSshKeyValidator(object): class OpenSshKeyValidator(object):
"""Validate OpenSSH keys (length and type).""" """Validate OpenSSH keys (length and type)."""
valid_types = ['ssh-rsa', 'ssh-dsa'] valid_types = ['ssh-rsa', 'ssh-dsa']
...@@ -133,21 +147,23 @@ class OpenSshKeyValidator(object): ...@@ -133,21 +147,23 @@ class OpenSshKeyValidator(object):
data = base64.decodestring(key_string) data = base64.decodestring(key_string)
int_len = 4 int_len = 4
str_len = struct.unpack('>I', data[:int_len])[0] str_len = struct.unpack('>I', data[:int_len])[0]
if not data[int_len:int_len+str_len] == type: if not data[int_len:(int_len + str_len)] == type:
raise raise
except ValidationError: except ValidationError:
raise raise
except: except:
raise ValidationError(_('Invalid OpenSSH public key.')) raise ValidationError(_('Invalid OpenSSH public key.'))
class SshKey(models.Model): class SshKey(models.Model):
"""SSH public key (in OpenSSH format).""" """SSH public key (in OpenSSH format)."""
user = models.ForeignKey(User, related_name='sshkey_set') user = models.ForeignKey(User, related_name='sshkey_set')
key = models.TextField(verbose_name=_('SSH key'), key = models.TextField(verbose_name=_('SSH key'),
help_text=_('<a href="/info/ssh/">SSH public key in OpenSSH ' help_text=_('<a href="/info/ssh/">SSH public key '
'format</a> used for shell and store login ' 'in OpenSSH format</a> used for shell '
'(2048+ bit RSA preferred). Example: ' 'and store login (2048+ bit RSA '
'<code>ssh-rsa AAAAB...QtQ== john</code>.'), 'preferred). Example: <code>ssh-rsa '
'AAAAB...QtQ== john</code>.'),
validators=[OpenSshKeyValidator()]) validators=[OpenSshKeyValidator()])
def __unicode__(self): def __unicode__(self):
...@@ -164,7 +180,7 @@ TYPES = {"LAB": {"verbose_name": _('lab'), "id": "LAB", ...@@ -164,7 +180,7 @@ TYPES = {"LAB": {"verbose_name": _('lab'), "id": "LAB",
"suspend": td(hours=5), "delete": td(days=15), "suspend": td(hours=5), "delete": td(days=15),
"help_text": _('For lab or homework with short lifetime.')}, "help_text": _('For lab or homework with short lifetime.')},
"PROJECT": {"verbose_name": _('project'), "id": "PROJECT", "PROJECT": {"verbose_name": _('project'), "id": "PROJECT",
"suspend": td(weeks=5), "delete": td(days=366/2), "suspend": td(weeks=5), "delete": td(days=366 / 2),
"help_text": _('For project work.')}, "help_text": _('For project work.')},
"SERVER": {"verbose_name": _('server'), "id": "SERVER", "SERVER": {"verbose_name": _('server'), "id": "SERVER",
"suspend": td(days=365), "delete": None, "suspend": td(days=365), "delete": None,
...@@ -174,6 +190,7 @@ DEFAULT_TYPE = TYPES['LAB'] ...@@ -174,6 +190,7 @@ DEFAULT_TYPE = TYPES['LAB']
TYPES_L = sorted(TYPES.values(), key=lambda m: m["suspend"]) TYPES_L = sorted(TYPES.values(), key=lambda m: m["suspend"])
TYPES_C = tuple([(i[0], i[1]["verbose_name"]) for i in TYPES.items()]) TYPES_C = tuple([(i[0], i[1]["verbose_name"]) for i in TYPES.items()])
class Share(models.Model): class Share(models.Model):
name = models.CharField(max_length=100, verbose_name=_('name')) name = models.CharField(max_length=100, verbose_name=_('name'))
description = models.TextField(verbose_name=_('description')) description = models.TextField(verbose_name=_('description'))
...@@ -183,12 +200,15 @@ class Share(models.Model): ...@@ -183,12 +200,15 @@ class Share(models.Model):
verbose_name=_('created at')) verbose_name=_('created at'))
type = models.CharField(choices=TYPES_C, max_length=10) type = models.CharField(choices=TYPES_C, max_length=10)
instance_limit = models.IntegerField(verbose_name=_('instance limit'), instance_limit = models.IntegerField(verbose_name=_('instance limit'),
help_text=_('Maximal count of instances launchable for this ' help_text=_('Maximal count of '
'share.')) 'instances launchable '
'for this share.'))
per_user_limit = models.IntegerField(verbose_name=_('per user limit'), per_user_limit = models.IntegerField(verbose_name=_('per user limit'),
help_text=_('Maximal count of instances launchable by a single ' help_text=_('Maximal count of '
'user.')) 'instances launchable by '
owner = models.ForeignKey(User, null=True, blank=True, related_name='share_set') 'a single user.'))
owner = models.ForeignKey(
User, null=True, blank=True, related_name='share_set')
class Meta: class Meta:
ordering = ['group', 'template', 'owner', ] ordering = ['group', 'template', 'owner', ]
...@@ -233,6 +253,7 @@ class Share(models.Model): ...@@ -233,6 +253,7 @@ class Share(models.Model):
def get_used_quota(self): def get_used_quota(self):
return self.template.get_credits_per_instance() * self.instance_limit return self.template.get_credits_per_instance() * self.instance_limit
class Disk(models.Model): class Disk(models.Model):
"""Virtual disks automatically synchronized with OpenNebula.""" """Virtual disks automatically synchronized with OpenNebula."""
name = models.CharField(max_length=100, unique=True, name = models.CharField(max_length=100, unique=True,
...@@ -270,21 +291,23 @@ class Disk(models.Model): ...@@ -270,21 +291,23 @@ class Disk(models.Model):
if delete: if delete:
Disk.objects.exclude(id__in=l).delete() Disk.objects.exclude(id__in=l).delete()
class Network(models.Model): class Network(models.Model):
"""Virtual networks automatically synchronized with OpenNebula.""" """Virtual networks automatically synchronized with OpenNebula."""
name = models.CharField(max_length=100, unique=True, name = models.CharField(max_length=100, unique=True,
verbose_name=_('name')) verbose_name=_('name'))
nat = models.BooleanField(verbose_name=_('NAT'), nat = models.BooleanField(verbose_name=_('NAT'),
help_text=_('If network address translation is done.')) help_text=_('If network address translation is '
'done.'))
public = models.BooleanField(verbose_name=_('public'), public = models.BooleanField(verbose_name=_('public'),
help_text=_('If internet gateway is available.')) help_text=_('If internet gateway is '
'available.'))
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = _('network') verbose_name = _('network')
verbose_name_plural = _('networks') verbose_name_plural = _('networks')
def __unicode__(self): def __unicode__(self):
return self.name return self.name
...@@ -310,6 +333,7 @@ class Network(models.Model): ...@@ -310,6 +333,7 @@ class Network(models.Model):
pass pass
l.append(id) l.append(id)
Network.objects.exclude(id__in=l).delete() Network.objects.exclude(id__in=l).delete()
def get_vlan(self): def get_vlan(self):
return Vlan.objects.get(vid=self.id) return Vlan.objects.get(vid=self.id)
...@@ -333,15 +357,20 @@ class InstanceType(models.Model): ...@@ -333,15 +357,20 @@ class InstanceType(models.Model):
TEMPLATE_STATES = (('NEW', _('new')), ('SAVING', _('saving')), TEMPLATE_STATES = (('NEW', _('new')), ('SAVING', _('saving')),
('READY', _('ready')), ) ('READY', _('ready')), )
class Template(models.Model): class Template(models.Model):
"""Virtual machine template specifying OS, disk, type and network.""" """Virtual machine template specifying OS, disk, type and network."""
name = models.CharField(max_length=100, unique=True, name = models.CharField(max_length=100, unique=True,
verbose_name=_('name')) verbose_name=_('name'))
access_type = models.CharField(max_length=10, access_type = models.CharField(max_length=10,
choices=[('rdp', 'rdp'), ('nx', 'nx'), ('ssh', 'ssh')], choices=[('rdp', 'rdp'), (
'nx', 'nx'), ('ssh', 'ssh')],
verbose_name=_('access method')) verbose_name=_('access method'))
disk = models.ForeignKey(Disk, verbose_name=_('disk'), related_name='template_set') disk = models.ForeignKey(Disk, verbose_name=_(
instance_type = models.ForeignKey(InstanceType, related_name='template_set', 'disk'), related_name='template_set')
instance_type = models.ForeignKey(
InstanceType, related_name='template_set',
verbose_name=_('instance type')) verbose_name=_('instance type'))
network = models.ForeignKey(Network, verbose_name=_('network'), network = models.ForeignKey(Network, verbose_name=_('network'),
related_name='template_set') related_name='template_set')
...@@ -352,10 +381,12 @@ class Template(models.Model): ...@@ -352,10 +381,12 @@ class Template(models.Model):
state = models.CharField(max_length=10, choices=TEMPLATE_STATES, state = models.CharField(max_length=10, choices=TEMPLATE_STATES,
default='NEW') default='NEW')
public = models.BooleanField(verbose_name=_('public'), default=False, public = models.BooleanField(verbose_name=_('public'), default=False,
help_text=_('If other users can derive templates of this one.')) help_text=_('If other users can derive '
'templates of this one.'))
description = models.TextField(verbose_name=_('description'), blank=True) description = models.TextField(verbose_name=_('description'), blank=True)
system = models.TextField(verbose_name=_('operating system'), blank=True, system = models.TextField(verbose_name=_('operating system'), blank=True,
help_text=(_('Name of operating system in format like "%s".') % help_text=(_('Name of operating system in '
'format like "%s".') %
"Ubuntu 12.04 LTS Desktop amd64")) "Ubuntu 12.04 LTS Desktop amd64"))
class Meta: class Meta:
...@@ -363,7 +394,6 @@ class Template(models.Model): ...@@ -363,7 +394,6 @@ class Template(models.Model):
verbose_name_plural = _('templates') verbose_name_plural = _('templates')
ordering = ['name', ] ordering = ['name', ]
def __unicode__(self): def __unicode__(self):
return self.name return self.name
...@@ -396,20 +426,19 @@ class Template(models.Model): ...@@ -396,20 +426,19 @@ class Template(models.Model):
else: else:
return shares return shares
def get_share_quota_usage_for(self, user=None): def get_share_quota_usage_for(self, user=None, type=None):
shares = self.get_shares_for(user) if type is None:
usage = 0 c = self.get_credits_per_instance()
for share in shares: else:
usage += share.instance_limit * self.get_credits_per_instance() c = type.credit
return usage
def get_share_quota_usage_for_user_with_type(self, type, user=None):
shares = self.get_shares_for(user) shares = self.get_shares_for(user)
usage = 0 usage = 0
for share in shares: for share in shares:
usage += share.instance_limit * type.credit usage += share.instance_limit * c
return usage return usage
class Instance(models.Model): class Instance(models.Model):
"""Virtual machine instance.""" """Virtual machine instance."""
name = models.CharField(max_length=100, name = models.CharField(max_length=100,
...@@ -433,19 +462,24 @@ class Instance(models.Model): ...@@ -433,19 +462,24 @@ class Instance(models.Model):
default='DEPLOYABLE') default='DEPLOYABLE')
active_since = models.DateTimeField(null=True, blank=True, active_since = models.DateTimeField(null=True, blank=True,
verbose_name=_('active since'), verbose_name=_('active since'),
help_text=_('Time stamp of successful boot report.')) help_text=_('Time stamp of successful '
'boot report.'))
firewall_host = models.ForeignKey(Host, blank=True, null=True, firewall_host = models.ForeignKey(Host, blank=True, null=True,
verbose_name=_('host in firewall'), related_name='instance_set') verbose_name=_('host in firewall'),
related_name='instance_set')
pw = models.CharField(max_length=20, verbose_name=_('password'), pw = models.CharField(max_length=20, verbose_name=_('password'),
help_text=_('Original password of instance')) help_text=_('Original password of instance'))
one_id = models.IntegerField(unique=True, blank=True, null=True, one_id = models.IntegerField(unique=True, blank=True, null=True,
verbose_name=_('OpenNebula ID')) verbose_name=_('OpenNebula ID'))
share = models.ForeignKey('Share', blank=True, null=True, share = models.ForeignKey('Share', blank=True, null=True,
verbose_name=_('share'), related_name='instance_set') verbose_name=_('share'),
related_name='instance_set')
time_of_suspend = models.DateTimeField(default=None, time_of_suspend = models.DateTimeField(default=None,
verbose_name=_('time of suspend'), null=True, blank=True) verbose_name=_('time of suspend'),
null=True, blank=True)
time_of_delete = models.DateTimeField(default=None, time_of_delete = models.DateTimeField(default=None,
verbose_name=_('time of delete'), null=True, blank=True) verbose_name=_('time of delete'),
null=True, blank=True)
waiting = models.BooleanField(default=False) waiting = models.BooleanField(default=False)
class Meta: class Meta:
...@@ -458,13 +492,15 @@ class Instance(models.Model): ...@@ -458,13 +492,15 @@ class Instance(models.Model):
@models.permalink @models.permalink
def get_absolute_url(self): def get_absolute_url(self):
return ('one.views.vm_show', None, {'iid':self.id}) return ('one.views.vm_show', None, {'iid': self.id})
def get_port(self, use_ipv6=False): def get_port(self, use_ipv6=False):
"""Get public port number for default access method.""" """Get public port number for default access method."""
proto = self.template.access_type proto = self.template.access_type
if self.nat and not use_ipv6: if self.nat and not use_ipv6:
return {"rdp": 23000, "nx": 22000, "ssh": 22000}[proto] + int(self.ip.split('.')[2]) * 256 + int(self.ip.split('.')[3]) return ({"rdp": 23000, "nx": 22000, "ssh": 22000}[proto] +
int(self.ip.split('.')[2]) * 256 +
int(self.ip.split('.')[3]))
else: else:
return {"rdp": 3389, "nx": 22, "ssh": 22}[proto] return {"rdp": 3389, "nx": 22, "ssh": 22}[proto]
...@@ -510,21 +546,10 @@ class Instance(models.Model): ...@@ -510,21 +546,10 @@ class Instance(models.Model):
pass pass
return age return age
@classmethod @staticmethod
def submit(cls, template, owner, extra="", share=None): def _create_context(pw, hostname, smb_password, ssh_private_key, owner,
"""Submit a new instance to OpenNebula.""" token, extra):
from django.template.defaultfilters import escape """Return XML context configuration with given parameters."""
inst = Instance(pw=pwgen(), template=template, owner=owner,
share=share, state='PENDING')
inst.save()
hostname = u"%d" % (inst.id, )
token = signing.dumps(inst.id, salt='activate')
try:
details = owner.cloud_details
except:
details = UserCloudDetails(user=owner)
details.save()
ctx = u''' ctx = u'''
<SOURCE>web</SOURCE> <SOURCE>web</SOURCE>
<HOSTNAME>%(hostname)s</HOSTNAME> <HOSTNAME>%(hostname)s</HOSTNAME>
...@@ -536,14 +561,64 @@ class Instance(models.Model): ...@@ -536,14 +561,64 @@ class Instance(models.Model):
<SERVER>store.cloud.ik.bme.hu</SERVER> <SERVER>store.cloud.ik.bme.hu</SERVER>
%(extra)s %(extra)s
''' % { ''' % {
"pw": escape(inst.pw), "pw": escape(pw),
"hostname": escape(hostname), "hostname": escape(hostname),
"smbpw": escape(details.smb_password), "smbpw": escape(smb_password),
"sshkey": escape(details.ssh_private_key), "sshkey": escape(ssh_private_key),
"neptun": escape(owner.username), "neptun": escape(owner),
"booturl": "%sb/%s/" % ( CLOUD_URL, token ), "booturl": "%sb/%s/" % (CLOUD_URL, token),
"extra": extra "extra": extra
} }
return ctx
def _create_host(self, hostname, occi_result):
"""Create firewall host for recently submitted Instance."""
host = Host(
vlan=Vlan.objects.get(name=self.template.network.name),
owner=self.owner, hostname=hostname,
mac=occi_result['interfaces'][0]['mac'],
ipv4=occi_result['interfaces'][0]['ip'], ipv6='auto',
)
if self.template.network.nat:
host.pub_ipv4 = Vlan.objects.get(
name=self.template.network.name).snat_ip
host.shared_ip = True
try:
host.save()
except:
for i in Host.objects.filter(ipv4=host.ipv4).all():
logger.warning('Delete orphan fw host (%s) of %s.' % (i, self))
i.delete()
for i in Host.objects.filter(mac=host.mac).all():
logger.warning('Delete orphan fw host (%s) of %s.' % (i, self))
i.delete()
host.save()
host.enable_net()
port = {"rdp": 3389, "nx": 22, "ssh": 22}[self.template.access_type]
host.add_port("tcp", self.get_port(), port)
self.firewall_host = host
self.save()
@classmethod
def submit(cls, template, owner, extra="", share=None):
"""Submit a new instance to OpenNebula."""
inst = Instance(pw=pwgen(), template=template, owner=owner,
share=share, state='PENDING', waiting=True)
inst.save()
hostname = u"%d" % (inst.id, )
token = signing.dumps(inst.id, salt='activate')
try:
details = owner.cloud_details
except:
details = UserCloudDetails(user=owner)
details.save()
ctx = cls._create_context(inst.pw, hostname, details.smb_password,
details.ssh_private_key, owner.username,
token, extra)
try: try:
from .tasks import CreateInstanceTask from .tasks import CreateInstanceTask
x = CreateInstanceTask.delay( x = CreateInstanceTask.delay(
...@@ -566,33 +641,7 @@ class Instance(models.Model): ...@@ -566,33 +641,7 @@ class Instance(models.Model):
'id': inst.one_id}) 'id': inst.one_id})
inst.save() inst.save()
host = Host( inst._create_host(hostname)
vlan=Vlan.objects.get(name=template.network.name),
owner=owner, hostname=hostname,
mac=res['interfaces'][0]['mac'],
ipv4=res['interfaces'][0]['ip'], ipv6='auto',
)
if inst.template.network.nat:
host.pub_ipv4 = Vlan.objects.get(name=template.network.name).snat_ip
host.shared_ip = True
try:
host.save()
except:
for i in Host.objects.filter(ipv4=host.ipv4).all():
logger.warning('Delete orphan fw host (%s) of %s.' % (i, inst))
i.delete()
for i in Host.objects.filter(mac=host.mac).all():
logger.warning('Delete orphan fw host (%s) of %s.' % (i, inst))
i.delete()
host.save()
host.enable_net()
host.add_port("tcp", inst.get_port(), {"rdp": 3389, "nx": 22,
"ssh": 22}[inst.template.access_type])
inst.firewall_host=host
inst.save()
return inst return inst
def one_delete(self): def one_delete(self):
...@@ -678,6 +727,7 @@ class Instance(models.Model): ...@@ -678,6 +727,7 @@ class Instance(models.Model):
self.firewall_host_delete() self.firewall_host_delete()
return True return True
def delete_instance_pre(sender, instance, using, **kwargs): def delete_instance_pre(sender, instance, using, **kwargs):
if instance.state != 'DONE': if instance.state != 'DONE':
instance.one_delete() instance.one_delete()
......
...@@ -37,27 +37,32 @@ class UpdateInstanceStateTask(Task): ...@@ -37,27 +37,32 @@ class UpdateInstanceStateTask(Task):
# ezek csak azert vannak felveve, hogy szepen meg lehessen hivni oket # ezek csak azert vannak felveve, hogy szepen meg lehessen hivni oket
# ezeket a fejgepen futo celery futtatja # ezeket a fejgepen futo celery futtatja
class CreateInstanceTask(Task): class CreateInstanceTask(Task):
def run(self, name, instance_type, disk_id, network_id, ctx): def run(self, name, instance_type, disk_id, network_id, ctx):
pass pass
class DeleteInstanceTask(Task): class DeleteInstanceTask(Task):
def run(self, one_id): def run(self, one_id):
pass pass
class ChangeInstanceStateTask(Task): class ChangeInstanceStateTask(Task):
def run(self, one_id, new_state): def run(self, one_id, new_state):
pass pass
class SaveAsTask(Task): class SaveAsTask(Task):
def run(self, one_id, new_img): def run(self, one_id, new_img):
pass pass
class UpdateDiskTask(Task): class UpdateDiskTask(Task):
def run(self): def run(self):
pass pass
class UpdateNetworkTask(Task): class UpdateNetworkTask(Task):
def run(self): def run(self):
pass pass
def keygen(length=1024): def keygen(length=1024):
import os, base64 import os
import base64
from datetime import date from datetime import date
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
......
...@@ -3,26 +3,21 @@ from datetime import datetime ...@@ -3,26 +3,21 @@ from datetime import datetime
from django.conf import settings from django.conf import settings
from datetime import timedelta as td from datetime import timedelta as td
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied, ValidationError
from django.core import signing, urlresolvers from django.core import signing
from django.core.mail import mail_managers, send_mail from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.db import transaction
from django.forms import ModelForm, Textarea
from django.http import Http404
from django.shortcuts import render, render_to_response, get_object_or_404, redirect
from django.template import RequestContext from django.template import RequestContext
from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _, ugettext
from django.utils.decorators import method_decorator from django.views.decorators.http import require_GET, require_POST
from django.utils.translation import get_language as lang from django.views.decorators.http import require_safe
from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponse
from django.views.decorators.http import * from django.views.generic import View
from django.views.generic import * # from firewall.tasks import *
from firewall.tasks import * from one.models import Instance, UserCloudDetails, Template, Share
from one.models import * from one.models import InstanceType, TYPES_L, TYPES, SshKey
from school.models import * from school.models import Semester, Group
import django.contrib.auth as auth from store.api import StoreApi
import json import json
import logging import logging
import subprocess import subprocess
...@@ -30,14 +25,18 @@ import subprocess ...@@ -30,14 +25,18 @@ import subprocess
store_settings = settings.STORE_SETTINGS store_settings = settings.STORE_SETTINGS
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _list_instances(request): def _list_instances(request):
instances = Instance.objects.exclude(state='DONE').filter(owner=request.user) instances = Instance.objects.exclude(
state='DONE').filter(owner=request.user)
instances = instances.exclude(state='DONE') instances = instances.exclude(state='DONE')
return instances return instances
def info(request): def info(request):
return render_to_response("info.html", RequestContext(request, {})) return render_to_response("info.html", RequestContext(request, {}))
def index(request): def index(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
return redirect(home) return redirect(home)
...@@ -51,7 +50,8 @@ def home(request): ...@@ -51,7 +50,8 @@ def home(request):
instances = _list_instances(request) instances = _list_instances(request)
shares = [s for s in request.user.person_set.all()[0].get_shares()] shares = [s for s in request.user.person_set.all()[0].get_shares()]
for i, s in enumerate(shares): for i, s in enumerate(shares):
s.running_shared = s.instance_set.all().exclude(state="DONE").filter(owner=request.user).count() s.running_shared = s.instance_set.all().exclude(
state="DONE").filter(owner=request.user).count()
shares[i] = s shares[i] = s
try: try:
details = UserCloudDetails.objects.get(user=request.user) details = UserCloudDetails.objects.get(user=request.user)
...@@ -74,6 +74,7 @@ def home(request): ...@@ -74,6 +74,7 @@ def home(request):
'storeserv': store_settings['store_public'], 'storeserv': store_settings['store_public'],
})) }))
@login_required @login_required
def ajax_template_delete(request): def ajax_template_delete(request):
try: try:
...@@ -82,16 +83,21 @@ def ajax_template_delete(request): ...@@ -82,16 +83,21 @@ def ajax_template_delete(request):
return HttpResponse(unicode(_("Invalid template ID.")), status=404) return HttpResponse(unicode(_("Invalid template ID.")), status=404)
template = get_object_or_404(Template, id=template_id) template = get_object_or_404(Template, id=template_id)
if template.running_instances() > 0: if template.running_instances() > 0:
return HttpResponse(unicode(_("There are running instances of this template.")), status=404) return HttpResponse(unicode(_('There are running instances of '
'this template.')), status=404)
elif template.share_set.exists(): elif template.share_set.exists():
return HttpResponse(unicode(_("Template is still shared.")), status=404) return HttpResponse(unicode(_("Template is still shared.")),
status=404)
elif template.owner != request.user: elif template.owner != request.user:
return HttpResponse(unicode(_("You don't have permission to delete this template.")), status=404) return HttpResponse(unicode(_("You don't have permission to delete "
"this template.")), status=404)
else: else:
if template.safe_delete(): if template.safe_delete():
return HttpResponse(unicode(_("Template successfully deleted."))) return HttpResponse(unicode(_("Template successfully deleted.")))
else: else:
return HttpResponse(unicode(_("Unexpected error happened.")), status=404) return HttpResponse(unicode(_("Unexpected error happened.")),
status=404)
def ajax_template_name_unique(request): def ajax_template_name_unique(request):
name = request.GET['name'] name = request.GET['name']
...@@ -100,6 +106,7 @@ def ajax_template_name_unique(request): ...@@ -100,6 +106,7 @@ def ajax_template_name_unique(request):
s = "False" s = "False"
return HttpResponse(s) return HttpResponse(s)
@login_required @login_required
def vm_credentials(request, iid): def vm_credentials(request, iid):
try: try:
...@@ -110,54 +117,70 @@ def vm_credentials(request, iid): ...@@ -110,54 +117,70 @@ def vm_credentials(request, iid):
vm.is_ipv6 = is_ipv6 vm.is_ipv6 = is_ipv6
vm.port_v4 = vm.get_port(use_ipv6=False) vm.port_v4 = vm.get_port(use_ipv6=False)
vm.port_v6 = vm.get_port(use_ipv6=True) vm.port_v6 = vm.get_port(use_ipv6=True)
return render_to_response('vm-credentials.html', RequestContext(request, { 'i' : vm })) return render_to_response('vm-credentials.html',
RequestContext(request, {'i': vm}))
except: except:
return HttpResponse(_("Could not get Virtual Machine credentials."), status=404) return HttpResponse(_("Could not get Virtual Machine credentials."),
status=404)
messages.error(request, _('Failed to power off virtual machine.')) messages.error(request, _('Failed to power off virtual machine.'))
class AjaxTemplateWizard(View): class AjaxTemplateWizard(View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
return render_to_response('new-template-flow-1.html', RequestContext(request, { templates = ([t for t in Template.objects.filter(public=True).all()] +
'templates': [t for t in Template.objects.filter(public=True).all()] + [t for t
[t for t in Template.objects.filter(owner=request.user).all()], in Template.objects.filter(owner=request.user).all()])
})) ctx = RequestContext(request, {'templates': templates})
return render_to_response('new-template-flow-1.html', ctx)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
base = get_object_or_404(Template, id=request.POST['base']) base = get_object_or_404(Template, id=request.POST['base'])
if base.owner != request.user and not base.public and not request.user.is_superuser: if base.owner != request.user and (not base.public and
not request.user.is_superuser):
raise PermissionDenied() raise PermissionDenied()
try: try:
maxshare = Template.objects.order_by('-pk')[0].pk + 1 maxshare = Template.objects.order_by('-pk')[0].pk + 1
except: except:
maxshare = 1 maxshare = 1
return render_to_response('new-template-flow.html', RequestContext(request, { ctx = {'sizes': InstanceType.objects.all(),
'sizes': InstanceType.objects.all(),
'base': base, 'base': base,
'maxshare': maxshare, 'maxshare': maxshare,
})) }
return render_to_response('new-template-flow.html',
RequestContext(request, ctx))
ajax_template_wizard = login_required(AjaxTemplateWizard.as_view()) ajax_template_wizard = login_required(AjaxTemplateWizard.as_view())
class AjaxTemplateEditWizard(View): class AjaxTemplateEditWizard(View):
def get(self, request, id, *args, **kwargs): def get(self, request, id, *args, **kwargs):
template = get_object_or_404(Template, id=id) template = get_object_or_404(Template, id=id)
if template.owner != request.user and not template.public and not request.user.is_superuser: if template.owner != request.user and (not template.public and
not request.user.is_superuser):
raise PermissionDenied() raise PermissionDenied()
return render_to_response('edit-template-flow.html', RequestContext(request, { ctx = {'sizes': InstanceType.objects.all(),
'sizes': InstanceType.objects.all(),
'template': template, 'template': template,
})) }
return render_to_response('edit-template-flow.html',
RequestContext(request, ctx))
def post(self, request, id, *args, **kwargs): def post(self, request, id, *args, **kwargs):
template = get_object_or_404(Template, id=id) template = get_object_or_404(Template, id=id)
user_details = UserCloudDetails.objects.get(user=request.user) user_details = UserCloudDetails.objects.get(user=request.user)
if template.owner != request.user and not template.public and not request.user.is_superuser: if template.owner != request.user and (not template.public and
not request.user.is_superuser):
raise PermissionDenied() raise PermissionDenied()
instance_type = get_object_or_404(InstanceType, id=request.POST['size']) instance_type = get_object_or_404(
current_used_share_quota = user_details.get_weighted_share_count() InstanceType, id=request.POST['size'])
current_used_share_quota_without_template = current_used_share_quota - template.get_share_quota_usage_for(request.user)
new_quota_for_current_template = template.get_share_quota_usage_for_user_with_type(instance_type, request.user) # calculate quota need with changed template type
new_used_share_quota = current_used_share_quota_without_template + new_quota_for_current_template q = user_details.get_weighted_share_count()
allow_type_modify = True if new_used_share_quota <= user_details.share_quota else False q -= template.get_share_quota_usage_for(request.user)
if not allow_type_modify: q += template.get_share_quota_usage_for(request.user,
messages.error(request, _('You do not have enough free share quota.')) type=instance_type)
if q > user_details.share_quota:
messages.error(request, _(
'You do not have enough free share quota.'))
return redirect(home) return redirect(home)
template.instance_type = instance_type template.instance_type = instance_type
template.description = request.POST['description'] template.description = request.POST['description']
...@@ -172,12 +195,15 @@ class AjaxShareWizard(View): ...@@ -172,12 +195,15 @@ class AjaxShareWizard(View):
def get(self, request, id, gid=None, *args, **kwargs): def get(self, request, id, gid=None, *args, **kwargs):
det = UserCloudDetails.objects.get(user=request.user) det = UserCloudDetails.objects.get(user=request.user)
if det.get_weighted_share_count() >= det.share_quota: if det.get_weighted_share_count() >= det.share_quota:
return HttpResponse(unicode(_('You do not have any free share quota.'))) msg = ugettext('You do not have any free share quota.')
return HttpResponse(msg)
types = TYPES_L types = TYPES_L
types[0]['default'] = True types[0]['default'] = True
for i, t in enumerate(types): for i, t in enumerate(types):
t['deletex'] = datetime.now() + td(seconds=1) + t['delete'] if t['delete'] else None t['deletex'] = datetime.now() + td(
t['suspendx'] = datetime.now() + td(seconds=1) + t['suspend'] if t['suspend'] else None seconds=1) + t['delete'] if t['delete'] else None
t['suspendx'] = datetime.now() + td(
seconds=1) + t['suspend'] if t['suspend'] else None
types[i] = t types[i] = t
if gid: if gid:
gid = get_object_or_404(Group, id=gid) gid = get_object_or_404(Group, id=gid)
...@@ -188,10 +214,12 @@ class AjaxShareWizard(View): ...@@ -188,10 +214,12 @@ class AjaxShareWizard(View):
'types': types, 'types': types,
'group': gid, 'group': gid,
})) }))
def post(self, request, id, gid=None, *args, **kwargs): def post(self, request, id, gid=None, *args, **kwargs):
det = UserCloudDetails.objects.get(user=request.user) det = UserCloudDetails.objects.get(user=request.user)
base = get_object_or_404(Template, id=id) base = get_object_or_404(Template, id=id)
if base.owner != request.user and not base.public and not request.user.is_superuser: if base.owner != request.user and (not base.public and
not request.user.is_superuser):
raise PermissionDenied() raise PermissionDenied()
group = None group = None
if gid: if gid:
...@@ -205,35 +233,45 @@ class AjaxShareWizard(View): ...@@ -205,35 +233,45 @@ class AjaxShareWizard(View):
if not stype in TYPES.keys(): if not stype in TYPES.keys():
raise PermissionDenied() raise PermissionDenied()
il = request.POST['instance_limit'] il = request.POST['instance_limit']
if det.get_weighted_share_count() + int(il)*base.instance_type.credit > det.share_quota: if det.share_quota < (det.get_weighted_share_count() +
messages.error(request, _('You do not have enough free share quota.')) int(il) * base.instance_type.credit):
messages.error(request, _(
'You do not have enough free share quota.'))
return redirect('/') return redirect('/')
s = Share.objects.create(name=request.POST['name'], description=request.POST['description'], Share.objects.create(
type=stype, instance_limit=il, per_user_limit=request.POST['per_user_limit'], name=request.POST['name'], description=request.POST['description'],
type=stype, instance_limit=il, per_user_limit=request.POST[
'per_user_limit'],
group=group, template=base, owner=request.user) group=group, template=base, owner=request.user)
messages.success(request, _('Successfully shared %s.') % base) messages.success(request, _('Successfully shared %s.') % base)
return redirect(group) return redirect(group)
ajax_share_wizard = login_required(AjaxShareWizard.as_view()) ajax_share_wizard = login_required(AjaxShareWizard.as_view())
class AjaxShareEditWizard(View): class AjaxShareEditWizard(View):
def get(self, request, id, *args, **kwargs): def get(self, request, id, *args, **kwargs):
det = UserCloudDetails.objects.get(user=request.user) det = UserCloudDetails.objects.get(user=request.user)
if det.get_weighted_share_count() > det.share_quota: if det.get_weighted_share_count() > det.share_quota:
logger.warning('[one] User %s ha more used share quota, than its limit, how is that possible? (%d > %d)', logger.warning(
str(request.user), '[one] User %s ha more used share quota, than its limit, how '
'is that possible? (%d > %d)', str(request.user),
det.get_weighted_share_count(), det.get_weighted_share_count(),
det.share_quota) det.share_quota)
return HttpResponse(unicode(_('You do not have any free share quota.'))) msg = ugettext('You do not have any free share quota.')
return HttpResponse(msg)
types = TYPES_L types = TYPES_L
for i, t in enumerate(types): for i, t in enumerate(types):
t['deletex'] = datetime.now() + td(seconds=1) + t['delete'] if t['delete'] else None t['deletex'] = datetime.now() + td(
t['suspendx'] = datetime.now() + td(seconds=1) + t['suspend'] if t['suspend'] else None seconds=1) + t['delete'] if t['delete'] else None
t['suspendx'] = datetime.now() + td(
seconds=1) + t['suspend'] if t['suspend'] else None
types[i] = t types[i] = t
share = get_object_or_404(Share, id=id) share = get_object_or_404(Share, id=id)
return render_to_response('edit-share.html', RequestContext(request, { return render_to_response('edit-share.html', RequestContext(request, {
'share': share, 'share': share,
'types': types, 'types': types,
})) }))
def post(self, request, id, *args, **kwargs): def post(self, request, id, *args, **kwargs):
det = UserCloudDetails.objects.get(user=request.user) det = UserCloudDetails.objects.get(user=request.user)
share = get_object_or_404(Share, id=id) share = get_object_or_404(Share, id=id)
...@@ -243,13 +281,15 @@ class AjaxShareEditWizard(View): ...@@ -243,13 +281,15 @@ class AjaxShareEditWizard(View):
if not stype in TYPES.keys(): if not stype in TYPES.keys():
raise PermissionDenied() raise PermissionDenied()
instance_limit = int(request.POST['instance_limit']) instance_limit = int(request.POST['instance_limit'])
current_used_share_quota = det.get_weighted_share_count()
current_used_share_quota_without_current_share = current_used_share_quota - share.get_used_quota() # calculate quota need with changed share
new_quota_for_current_share = instance_limit * share.template.get_credits_per_instance() q = det.get_weighted_share_count()
new_used_share_quota = current_used_share_quota_without_current_share + new_quota_for_current_share q -= share.get_used_quota()
allow_stype_modify = True if new_used_share_quota <= det.share_quota else False q += instance_limit * share.template.get_credits_per_instance()
if not allow_stype_modify:
messages.error(request, _('You do not have enough free share quota.')) if det.share_quota < q:
messages.error(request,
_('You do not have enough free share quota.'))
return redirect(share.group) return redirect(share.group)
share.name = request.POST['name'] share.name = request.POST['name']
share.description = request.POST['description'] share.description = request.POST['description']
...@@ -273,9 +313,11 @@ def vm_saveas(request, vmid): ...@@ -273,9 +313,11 @@ def vm_saveas(request, vmid):
messages.success(request, _("Template is being saved...")) messages.success(request, _("Template is being saved..."))
return redirect(inst) return redirect(inst)
def vm_new_ajax(request, template): def vm_new_ajax(request, template):
return vm_new(request, template, redir=False) return vm_new(request, template, redir=False)
def _redirect_or_201(path, redir): def _redirect_or_201(path, redir):
if redir: if redir:
return redirect(path) return redirect(path)
...@@ -284,38 +326,80 @@ def _redirect_or_201(path, redir): ...@@ -284,38 +326,80 @@ def _redirect_or_201(path, redir):
response['Location'] = path response['Location'] = path
return response return response
def _template_for_save(base, request): def _template_for_save(base, request):
if base.owner != request.user and not base.public and not request.user.is_superuser: if base.owner != request.user and (not base.public and
not request.user.is_superuser):
raise PermissionDenied() raise PermissionDenied()
name = request.POST['name'] name = request.POST['name']
t = Template.objects.create(name=name, disk=base.disk, instance_type_id=request.POST['size'], network=base.network, owner=request.user) t = Template.objects.create(name=name, disk=base.disk,
instance_type_id=request.POST['size'],
network=base.network, owner=request.user)
t.access_type = base.access_type t.access_type = base.access_type
t.description = request.POST['description'] t.description = request.POST['description']
t.system = base.system t.system = base.system
t.save() t.save()
return t return t
def _check_quota(request, template, share): def _check_quota(request, template, share):
""" """
Returns if the given request is permitted to run the new vm. Returns if the given request is permitted to run the new vm.
""" """
det = UserCloudDetails.objects.get(user=request.user) user = request.user
if det.get_weighted_instance_count() + template.instance_type.credit > det.instance_quota: det = UserCloudDetails.objects.get(user=user)
messages.error(request, _('You do not have any free quota. You can not launch this until you stop an other instance.')) q = det.get_weighted_instance_count() + template.instance_type.credit
if q > det.instance_quota:
messages.error(request,
_('You do not have any free quota. You can not launch '
'this until you stop an other instance.'))
return False return False
if share: if share:
if share.get_running() + 1 > share.instance_limit: if share.instance_limit < share.get_running() + 1:
messages.error(request, _('The share does not have any free quota. You can not launch this until someone stops an instance.')) msg = _('The share does not have any free quota. You can '
'not launch this until someone stops an instance.')
messages.error(request, msg)
return False return False
elif share.get_running_or_stopped(request.user) + 1 > share.per_user_limit:
messages.error(request, _('You do not have any free quota for this share. You can not launch this until you stop an other instance.')) if share.per_user_limit < share.get_running_or_stopped(user) + 1:
msg = _('You do not have any free quota for this share. You can '
'not launch this until you stop an other instance.')
messages.error(request, msg)
return False return False
if not share.group.members.filter(user=request.user) and not share.group.owners.filter(user=request.user):
messages.error(request, _('You are not a member of the share group.')) if not share.group.members.filter(user=request.user) and \
not share.group.owners.filter(user=request.user):
msg = _('You are not a member of the share group.')
messages.error(request, msg)
return False return False
return True return True
@require_POST
def _check_permission(request, template, share):
"""
Returns if the given request is permitted to try the new vm.
"""
if not share and not template.public and template.owner != request.user:
msg = _('You have no permission to try this instance without a share. '
'Launch a new instance through a share.')
messages.error(request, msg)
return False
else:
return True
def _try_submit(base, request, extra, share):
try:
inst = Instance.submit(
base, request.user, extra=extra, share=share)
except Exception as e:
logger.error('Failed to create virtual machine.' + unicode(e))
messages.error(request, _('Failed to create virtual machine.'))
else:
inst.renew()
return inst
@login_required @login_required
def vm_new(request, template=None, share=None, redir=True): def vm_new(request, template=None, share=None, redir=True):
base = None base = None
...@@ -334,44 +418,14 @@ def vm_new(request, template=None, share=None, redir=True): ...@@ -334,44 +418,14 @@ def vm_new(request, template=None, share=None, redir=True):
except: except:
messages.error(request, _('Can not create template.')) messages.error(request, _('Can not create template.'))
go = False go = False
go = go and _check_quota(request, base, share) go = go and (_check_quota(request, base, share) and
_check_permission(request, base, share))
if not share and not base.public and base.owner != request.user:
messages.error(request, _('You have no permission to try this instance without a share. Launch a new instance through a share.'))
go = False
type = share.type if share else 'LAB'
TYPES[type]['suspend']
time_of_suspend = TYPES[type]['suspend']+datetime.now()
if TYPES[type]['delete']:
time_of_delete = TYPES[type]['delete']+datetime.now()
else:
time_of_delete = None
inst = None
if go: if go:
try: inst = _try_submit(base, request, extra, share)
inst = Instance.submit(base, request.user, extra=extra, share=share) elif extra and base: # clean up new template object
except Exception as e:
logger.error('Failed to create virtual machine.' + unicode(e))
messages.error(request, _('Failed to create virtual machine.'))
inst = None
if inst:
inst.waiting = True
inst.time_of_suspend = time_of_suspend
inst.time_of_delete = time_of_delete
inst.save()
elif extra and base:
base.delete() base.delete()
return _redirect_or_201(inst.get_absolute_url() if inst else '/', redir) return _redirect_or_201(inst.get_absolute_url() if inst else '/', redir)
class VmListView(ListView):
context_object_name = 'instances'
template_name = 'list.html'
def get_queryset(self):
self.profile = request.user
return Instance.objects.filter(owner=self.profile)
vm_list = login_required(VmListView.as_view())
@require_safe @require_safe
@login_required @login_required
...@@ -392,7 +446,7 @@ def vm_show(request, iid): ...@@ -392,7 +446,7 @@ def vm_show(request, iid):
inst.hostname_v6 = inst.get_connect_host(use_ipv6=True) inst.hostname_v6 = inst.get_connect_host(use_ipv6=True)
inst.port_v4 = inst.get_port(use_ipv6=False) inst.port_v4 = inst.get_port(use_ipv6=False)
inst.port_v6 = inst.get_port(use_ipv6=True) inst.port_v6 = inst.get_port(use_ipv6=True)
return render_to_response("show.html", RequestContext(request,{ return render_to_response("show.html", RequestContext(request, {
'uri': inst.get_connect_uri(), 'uri': inst.get_connect_uri(),
'state': inst.state, 'state': inst.state,
'name': inst.name, 'name': inst.name,
...@@ -400,11 +454,12 @@ def vm_show(request, iid): ...@@ -400,11 +454,12 @@ def vm_show(request, iid):
'age': inst.get_age(), 'age': inst.get_age(),
'instances': _list_instances(request), 'instances': _list_instances(request),
'i': inst, 'i': inst,
'booting' : not inst.active_since, 'booting': not inst.active_since,
'ports': ports, 'ports': ports,
'userdetails': details 'userdetails': details
})) }))
@require_safe @require_safe
@login_required @login_required
def vm_ajax_instance_status(request, iid): def vm_ajax_instance_status(request, iid):
...@@ -417,6 +472,7 @@ def vm_ajax_instance_status(request, iid): ...@@ -417,6 +472,7 @@ def vm_ajax_instance_status(request, iid):
'state': inst.template.state 'state': inst.template.state
}})) }}))
@login_required @login_required
def vm_ajax_rename(request, iid): def vm_ajax_rename(request, iid):
inst = get_object_or_404(Instance, id=iid, owner=request.user) inst = get_object_or_404(Instance, id=iid, owner=request.user)
...@@ -424,6 +480,7 @@ def vm_ajax_rename(request, iid): ...@@ -424,6 +480,7 @@ def vm_ajax_rename(request, iid):
inst.save() inst.save()
return HttpResponse(json.dumps({'name': inst.name})) return HttpResponse(json.dumps({'name': inst.name}))
def boot_token(request, token): def boot_token(request, token):
try: try:
id = signing.loads(token, salt='activate') id = signing.loads(token, salt='activate')
...@@ -437,6 +494,7 @@ def boot_token(request, token): ...@@ -437,6 +494,7 @@ def boot_token(request, token):
inst.save() inst.save()
return HttpResponse("KTHXBYE") return HttpResponse("KTHXBYE")
class VmPortAddView(View): class VmPortAddView(View):
def post(self, request, iid, *args, **kwargs): def post(self, request, iid, *args, **kwargs):
try: try:
...@@ -460,6 +518,7 @@ class VmPortAddView(View): ...@@ -460,6 +518,7 @@ class VmPortAddView(View):
vm_port_add = login_required(VmPortAddView.as_view()) vm_port_add = login_required(VmPortAddView.as_view())
@require_safe @require_safe
@login_required @login_required
@require_GET @require_GET
...@@ -473,14 +532,17 @@ def vm_port_del(request, iid, proto, private): ...@@ -473,14 +532,17 @@ def vm_port_del(request, iid, proto, private):
messages.error(request, _(u"Removing port failed.")) messages.error(request, _(u"Removing port failed."))
return redirect('/vm/show/%d/' % int(iid)) return redirect('/vm/show/%d/' % int(iid))
class VmDeleteView(View): class VmDeleteView(View):
def post(self, request, iid, *args, **kwargs): def post(self, request, iid, *args, **kwargs):
try: try:
inst = get_object_or_404(Instance, id=iid, owner=request.user) inst = get_object_or_404(Instance, id=iid, owner=request.user)
if inst.template.state != 'READY' and inst.template.owner == request.user: if inst.template.state != 'READY' and \
inst.template.owner == request.user:
inst.template.delete() inst.template.delete()
inst.one_delete() inst.one_delete()
messages.success(request, _('Virtual machine is successfully deleted.')) messages.success(request, _(
'Virtual machine is successfully deleted.'))
except: except:
messages.error(request, _('Failed to delete virtual machine.')) messages.error(request, _('Failed to delete virtual machine.'))
if request.is_ajax(): if request.is_ajax():
...@@ -490,13 +552,14 @@ class VmDeleteView(View): ...@@ -490,13 +552,14 @@ class VmDeleteView(View):
def get(self, request, iid, *args, **kwargs): def get(self, request, iid, *args, **kwargs):
i = get_object_or_404(Instance, id=iid, owner=request.user) i = get_object_or_404(Instance, id=iid, owner=request.user)
return render_to_response("confirm_delete.html", RequestContext(request, { ctx = RequestContext(request, {'i': i, })
'i': i})) return render_to_response("confirm_delete.html", ctx)
vm_delete = login_required(VmDeleteView.as_view()) vm_delete = login_required(VmDeleteView.as_view())
@login_required @login_required
#@require_POST # @require_POST
def vm_unshare(request, id, *args, **kwargs): def vm_unshare(request, id, *args, **kwargs):
s = get_object_or_404(Share, id=id) s = get_object_or_404(Share, id=id)
g = s.group g = s.group
...@@ -504,7 +567,8 @@ def vm_unshare(request, id, *args, **kwargs): ...@@ -504,7 +567,8 @@ def vm_unshare(request, id, *args, **kwargs):
raise PermissionDenied() raise PermissionDenied()
try: try:
if s.get_running_or_stopped() > 0: if s.get_running_or_stopped() > 0:
messages.error(request, _('There are machines running of this share.')) messages.error(request, _(
'There are machines running of this share.'))
else: else:
s.delete() s.delete()
messages.success(request, _('Share is successfully removed.')) messages.success(request, _('Share is successfully removed.'))
...@@ -512,23 +576,27 @@ def vm_unshare(request, id, *args, **kwargs): ...@@ -512,23 +576,27 @@ def vm_unshare(request, id, *args, **kwargs):
messages.error(request, _('Failed to remove share.')) messages.error(request, _('Failed to remove share.'))
return redirect(g) return redirect(g)
@login_required @login_required
@require_POST @require_POST
def vm_stop(request, iid, *args, **kwargs): def vm_stop(request, iid, *args, **kwargs):
try: try:
get_object_or_404(Instance, id=iid, owner=request.user).stop() get_object_or_404(Instance, id=iid, owner=request.user).stop()
messages.success(request, _('Virtual machine is successfully stopped.')) messages.success(request, _(
'Virtual machine is successfully stopped.'))
except: except:
messages.error(request, _('Failed to stop virtual machine.')) messages.error(request, _('Failed to stop virtual machine.'))
return redirect('/') return redirect('/')
@login_required @login_required
@require_POST @require_POST
def vm_resume(request, iid, *args, **kwargs): def vm_resume(request, iid, *args, **kwargs):
try: try:
obj = get_object_or_404(Instance, id=iid, owner=request.user) obj = get_object_or_404(Instance, id=iid, owner=request.user)
obj.resume() obj.resume()
messages.success(request, _('Virtual machine is successfully resumed.')) messages.success(request, _(
'Virtual machine is successfully resumed.'))
except: except:
messages.error(request, _('Failed to resume virtual machine.')) messages.error(request, _('Failed to resume virtual machine.'))
try: try:
...@@ -537,6 +605,7 @@ def vm_resume(request, iid, *args, **kwargs): ...@@ -537,6 +605,7 @@ def vm_resume(request, iid, *args, **kwargs):
pass pass
return redirect('/') return redirect('/')
@login_required @login_required
@require_POST @require_POST
def vm_renew(request, which, iid, *args, **kwargs): def vm_renew(request, which, iid, *args, **kwargs):
...@@ -550,33 +619,38 @@ def vm_renew(request, which, iid, *args, **kwargs): ...@@ -550,33 +619,38 @@ def vm_renew(request, which, iid, *args, **kwargs):
'vm': vm 'vm': vm
})) }))
@login_required @login_required
@require_POST @require_POST
def vm_power_off(request, iid, *args, **kwargs): def vm_power_off(request, iid, *args, **kwargs):
try: try:
get_object_or_404(Instance, id=iid, owner=request.user).poweroff() get_object_or_404(Instance, id=iid, owner=request.user).poweroff()
messages.success(request, _('Virtual machine is successfully powered off.')) messages.success(request, _(
'Virtual machine is successfully powered off.'))
except: except:
messages.error(request, _('Failed to power off virtual machine.')) messages.error(request, _('Failed to power off virtual machine.'))
return redirect('/') return redirect('/')
@login_required @login_required
@require_POST @require_POST
def vm_restart(request, iid, *args, **kwargs): def vm_restart(request, iid, *args, **kwargs):
try: try:
get_object_or_404(Instance, id=iid, owner=request.user).restart() get_object_or_404(Instance, id=iid, owner=request.user).restart()
messages.success(request, _('Virtual machine is successfully restarted.')) messages.success(request, _(
'Virtual machine is successfully restarted.'))
except: except:
messages.error(request, _('Failed to restart virtual machine.')) messages.error(request, _('Failed to restart virtual machine.'))
return redirect('/') return redirect('/')
@login_required @login_required
@require_POST @require_POST
def key_add(request): def key_add(request):
try: try:
key=SshKey() key = SshKey()
key.key=request.POST['key'] key.key = request.POST['key']
key.user=request.user key.user = request.user
key.full_clean() key.full_clean()
key.save() key.save()
_update_keys(request.user) _update_keys(request.user)
...@@ -590,22 +664,25 @@ def key_add(request): ...@@ -590,22 +664,25 @@ def key_add(request):
messages.success(request, _('Public key successfully added.')) messages.success(request, _('Public key successfully added.'))
return redirect('/') return redirect('/')
@login_required @login_required
@require_POST @require_POST
def key_ajax_delete(request): def key_ajax_delete(request):
try: try:
key=get_object_or_404(SshKey, id=request.POST['id'], user=request.user) key = get_object_or_404(SshKey, id=request.POST[
'id'], user=request.user)
key.delete() key.delete()
_update_keys(request.user) _update_keys(request.user)
except: except:
messages.error(request, _('Failed to delete public key')) messages.error(request, _('Failed to delete public key'))
return HttpResponse('OK') return HttpResponse('OK')
@login_required @login_required
@require_POST @require_POST
def key_ajax_reset(request): def key_ajax_reset(request):
try: try:
det=UserCloudDetails.objects.get(user=request.user) det = UserCloudDetails.objects.get(user=request.user)
det.reset_smb() det.reset_smb()
det.reset_keys() det.reset_keys()
_update_keys(request.user) _update_keys(request.user)
...@@ -613,6 +690,7 @@ def key_ajax_reset(request): ...@@ -613,6 +690,7 @@ def key_ajax_reset(request):
messages.error(request, _('Failed to reset keys')) messages.error(request, _('Failed to reset keys'))
return HttpResponse('OK') return HttpResponse('OK')
def _update_keys(user): def _update_keys(user):
details = user.cloud_details details = user.cloud_details
password = details.smb_password password = details.smb_password
...@@ -622,22 +700,24 @@ def _update_keys(user): ...@@ -622,22 +700,24 @@ def _update_keys(user):
user = user.username user = user.username
StoreApi.updateauthorizationinfo(user, password, key_list) StoreApi.updateauthorizationinfo(user, password, key_list)
def stat(request): def stat(request):
values = subprocess.check_output(['/opt/webadmin/cloud/miscellaneous/stat/stat_wrap.sh']) values = subprocess.check_output(
['/opt/webadmin/cloud/miscellaneous/stat/stat_wrap.sh'])
# values = ''' # values = '''
# {"CPU": {"USED_CPU": 2, "ALLOC_CPU": 0, # {"CPU": {"USED_CPU": 2, "ALLOC_CPU": 0,
# "FREE_CPU": 98}, "MEM": {"FREE_MEM": 1685432, "ALLOC_MEM":0, # "FREE_CPU": 98}, "MEM": {"FREE_MEM": 1685432, "ALLOC_MEM":0,
# "USED_MEM": 366284}}''' # "USED_MEM": 366284}}'''
stat_dict = json.loads(values) stat_dict = json.loads(values)
return HttpResponse(render_to_response("stat.html", RequestContext( return HttpResponse(render_to_response("stat.html", RequestContext(
request, { request, {'STAT': stat_dict, }
'STAT' : stat_dict,
}
))) )))
def sites(request, site): def sites(request, site):
if site in [ "legal", "policy", "help", "support", "changelog", ]: if site in ["legal", "policy", "help", "support", "changelog", ]:
return render_to_response("sites/%s.html" % site, RequestContext(request, {})) ctx = RequestContext(request, {})
return render_to_response("sites/%s.html" % site, ctx)
else: else:
return redirect(home) return redirect(home)
......
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