Commit 002facdf by Szeberényi Imre

Merge branch 'template_instance_limit' into 'master'

Template instance limit

See merge request !418
parents 9e18bdbf 729d6f82
Pipeline #1361 passed with stage
in 0 seconds
...@@ -1336,6 +1336,9 @@ class UserEditForm(forms.ModelForm): ...@@ -1336,6 +1336,9 @@ class UserEditForm(forms.ModelForm):
instance_limit = forms.IntegerField( instance_limit = forms.IntegerField(
label=_('Instance limit'), label=_('Instance limit'),
min_value=0, widget=NumberInput) min_value=0, widget=NumberInput)
template_instance_limit = forms.IntegerField(
label=_('Template instance limit'),
min_value=0, widget=NumberInput)
two_factor_secret = forms.CharField( two_factor_secret = forms.CharField(
label=_('Two-factor authentication secret'), label=_('Two-factor authentication secret'),
help_text=_("Remove the secret key to disable two-factor " help_text=_("Remove the secret key to disable two-factor "
...@@ -1345,18 +1348,23 @@ class UserEditForm(forms.ModelForm): ...@@ -1345,18 +1348,23 @@ class UserEditForm(forms.ModelForm):
super(UserEditForm, self).__init__(*args, **kwargs) super(UserEditForm, self).__init__(*args, **kwargs)
self.fields["instance_limit"].initial = ( self.fields["instance_limit"].initial = (
self.instance.profile.instance_limit) self.instance.profile.instance_limit)
self.fields["template_instance_limit"].initial = (
self.instance.profile.template_instance_limit)
self.fields["two_factor_secret"].initial = ( self.fields["two_factor_secret"].initial = (
self.instance.profile.two_factor_secret) self.instance.profile.two_factor_secret)
class Meta: class Meta:
model = User model = User
fields = ('email', 'first_name', 'last_name', 'instance_limit', fields = ('email', 'first_name', 'last_name',
'is_active', "two_factor_secret",) 'instance_limit', 'template_instance_limit',
'is_active', 'two_factor_secret',)
def save(self, commit=True): def save(self, commit=True):
user = super(UserEditForm, self).save() user = super(UserEditForm, self).save()
user.profile.instance_limit = ( user.profile.instance_limit = (
self.cleaned_data['instance_limit'] or None) self.cleaned_data['instance_limit'] or None)
user.profile.template_instance_limit = (
self.cleaned_data['template_instance_limit'] or None)
user.profile.two_factor_secret = ( user.profile.two_factor_secret = (
self.cleaned_data['two_factor_secret'] or None) self.cleaned_data['two_factor_secret'] or None)
user.profile.save() user.profile.save()
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.25 on 2020-11-06 13:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dashboard', '0007_groupprofile_disk_quota'),
]
operations = [
migrations.AddField(
model_name='profile',
name='template_instance_limit',
field=models.IntegerField(default=1),
),
]
...@@ -178,6 +178,7 @@ class Profile(Model): ...@@ -178,6 +178,7 @@ class Profile(Model):
unique=True, blank=True, null=True, max_length=64, unique=True, blank=True, null=True, max_length=64,
help_text=_('Unique identifier of the person, e.g. a student number.')) help_text=_('Unique identifier of the person, e.g. a student number.'))
instance_limit = IntegerField(default=5) instance_limit = IntegerField(default=5)
template_instance_limit = IntegerField(default=1)
use_gravatar = BooleanField( use_gravatar = BooleanField(
verbose_name=_("Use Gravatar"), default=True, verbose_name=_("Use Gravatar"), default=True,
help_text=_("Whether to use email address as Gravatar profile image")) help_text=_("Whether to use email address as Gravatar profile image"))
......
...@@ -534,7 +534,7 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase): ...@@ -534,7 +534,7 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase):
with patch.object(DeployOperation, 'async') as async: with patch.object(DeployOperation, 'async') as async:
response = c.post("/dashboard/vm/create/", { response = c.post("/dashboard/vm/create/", {
'name': 'vm', 'name': 'vm',
'amount': 2, 'amount': 1,
'customized': 1, 'customized': 1,
'template': 1, 'template': 1,
'cpu_priority': 10, 'cpu_count': 1, 'ram_size': 128, 'cpu_priority': 10, 'cpu_count': 1, 'ram_size': 128,
...@@ -543,7 +543,7 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase): ...@@ -543,7 +543,7 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase):
assert async.called assert async.called
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(instance_count + 2, Instance.objects.all().count()) self.assertEqual(instance_count + 1, Instance.objects.all().count())
def test_unpermitted_description_update(self): def test_unpermitted_description_update(self):
c = Client() c = Client()
......
...@@ -460,8 +460,7 @@ class VmMigrateView(FormOperationMixin, VmOperationView): ...@@ -460,8 +460,7 @@ class VmMigrateView(FormOperationMixin, VmOperationView):
if isinstance(inst, Instance): if isinstance(inst, Instance):
nodes_w_traits = [ nodes_w_traits = [
n.pk for n in Node.objects.filter(enabled=True) n.pk for n in Node.objects.filter(enabled=True)
if n.online and if n.online and has_traits(inst.req_traits.all(), n)
has_traits(inst.req_traits.all(), n)
] ]
ctx['nodes_w_traits'] = nodes_w_traits ctx['nodes_w_traits'] = nodes_w_traits
...@@ -1166,24 +1165,28 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1166,24 +1165,28 @@ class VmCreate(LoginRequiredMixin, TemplateView):
# limit chekcs # limit chekcs
try: try:
limit = user.profile.instance_limit instance_limit = user.profile.instance_limit
template_instance_limit = user.profile.template_instance_limit
except Exception as e: except Exception as e:
logger.debug('No profile or instance limit: %s', e) logger.debug('No profile or instance limit: %s', e)
else: else:
try: try:
amount = int(request.POST.get("amount", 1)) amount = int(request.POST.get("amount", 1))
except: except:
amount = limit # TODO this should definitely use a Form # TODO this should definitely use a Form
current = Instance.active.filter(owner=user).count() amount = instance_limit
logger.debug('current use: %d, limit: %d', current, limit) instances = Instance.active.filter(owner=user).count()
if current + amount > limit: template_instances = template.get_user_instances(user).count()
messages.error(request,
_('Instance limit (%d) exceeded.') % limit) logger.debug('current instance use: %d, limit: %d',
if request.is_ajax(): instances, instance_limit)
return HttpResponse(json.dumps({'redirect': '/'}), logger.debug('current template instance use: %d, limit: %d',
content_type="application/json") template_instances, template_instance_limit)
else:
return redirect('/') if instances + amount > instance_limit:
return self._limit_exceeded(instance_limit, request)
if template_instances + amount > template_instance_limit:
return self._limit_exceeded(template_instance_limit, request)
create_func = (self.__create_normal if create_func = (self.__create_normal if
request.POST.get("customized") is None else request.POST.get("customized") is None else
...@@ -1191,6 +1194,16 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1191,6 +1194,16 @@ class VmCreate(LoginRequiredMixin, TemplateView):
return create_func(request, template, *args, **kwargs) return create_func(request, template, *args, **kwargs)
def _limit_exceeded(self, limit, request):
messages.error(request,
_('Instance limit (%d) exceeded.')
% limit)
if request.is_ajax():
return HttpResponse(json.dumps({'redirect': '/'}),
content_type="application/json")
else:
return redirect('/')
@require_GET @require_GET
def get_vm_screenshot(request, pk): def get_vm_screenshot(request, pk):
......
...@@ -200,6 +200,9 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -200,6 +200,9 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
def get_running_instances(self): def get_running_instances(self):
return Instance.active.filter(template=self, status="RUNNING") return Instance.active.filter(template=self, status="RUNNING")
def get_user_instances(self, user):
return Instance.active.filter(template=self, owner=user)
@property @property
def metric_prefix(self): def metric_prefix(self):
return 'template.%d' % self.pk return 'template.%d' % self.pk
......
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