Commit 013ed92b by Kálmán Viktor

Merge branch 'feature-resize-help' into 'master'

Disk resize help and request

![Kijelölés_053](https://git.ik.bme.hu/circle/cloud/uploads/04c1c2c4264db0e93ad9243f22c79d8e/Kijelölés_053.png)

See merge request !341
parents 9d2184d8 fb293c07
...@@ -25,7 +25,7 @@ from django.shortcuts import redirect ...@@ -25,7 +25,7 @@ from django.shortcuts import redirect
from circle.settings.base import get_env_variable from circle.settings.base import get_env_variable
from dashboard.views import circle_login, HelpView from dashboard.views import circle_login, HelpView, ResizeHelpView
from dashboard.forms import CirclePasswordResetForm, CircleSetPasswordForm from dashboard.forms import CirclePasswordResetForm, CircleSetPasswordForm
from firewall.views import add_blacklist_item from firewall.views import add_blacklist_item
...@@ -65,6 +65,8 @@ urlpatterns = patterns( ...@@ -65,6 +65,8 @@ urlpatterns = patterns(
url(r'^info/support/$', url(r'^info/support/$',
TemplateView.as_view(template_name="info/support.html"), TemplateView.as_view(template_name="info/support.html"),
name="info.support"), name="info.support"),
url(r'^info/resize-how-to/$', ResizeHelpView.as_view(),
name="info.resize"),
) )
......
...@@ -1488,3 +1488,38 @@ textarea[name="new_members"] { ...@@ -1488,3 +1488,38 @@ textarea[name="new_members"] {
.acl-table td:first-child { .acl-table td:first-child {
text-align: center; text-align: center;
} }
#resize-help {
table {
background-color: #f5f5f5;
}
.panel {
padding: 2px 20px;
background-color: #f5f5f5;
margin: 20px 0px;
}
ol li {
margin-top: 15px;
}
img {
display: block;
margin: 15px 0 5px 0;
}
pre {
margin-top: 5px;
}
hr {
margin: 50px 0;
}
}
#vm-details-resize-how-to {
font-size: 1.5em;
text-align: center;
width: 100%;
}
...@@ -4,24 +4,29 @@ ...@@ -4,24 +4,29 @@
<i class="fa fa-file"></i> <i class="fa fa-file"></i>
{{ d.name }} (#{{ d.id }}) - {{ d.size|filesize }} {{ d.name }} (#{{ d.id }}) - {{ d.size|filesize }}
{% if op.remove_disk %}
<span class="operation-wrapper"> <span class="operation-wrapper pull-right">
<a href="{{ op.remove_disk.get_url }}?disk={{d.pk}}" {% if d.is_resizable %}
class="btn btn-xs btn-{{ op.remove_disk.effect}} pull-right operation disk-remove-btn {% if op.resize_disk %}
{% if op.remove_disk.disabled %}disabled{% endif %}">
<i class="fa fa-{{ op.remove_disk.icon }} fa-fw-12"></i> {% trans "Remove" %}
</a>
</span>
{% endif %}
{% if op.resize_disk %}
<span class="operation-wrapper">
<a href="{{ op.resize_disk.get_url }}?disk={{d.pk}}" <a href="{{ op.resize_disk.get_url }}?disk={{d.pk}}"
class="btn btn-xs btn-{{ op.resize_disk.effect }} pull-right operation disk-resize-btn class="btn btn-xs btn-{{ op.resize_disk.effect }} operation disk-resize-btn
{% if op.resize_disk.disabled %}disabled{% endif %}"> {% if op.resize_disk.disabled %}disabled{% endif %}">
<i class="fa fa-{{ op.resize_disk.icon }} fa-fw-12"></i> {% trans "Resize" %} <i class="fa fa-{{ op.resize_disk.icon }} fa-fw-12"></i> {% trans "Resize" %}
</a> </a>
</span> {% else %}
{% endif %} <a href="{% url "request.views.request-resize" vm_pk=instance.pk disk_pk=d.pk %}" class="btn btn-xs btn-primary operation">
<i class="fa fa-arrows-alt fa-fw-12"></i> {% trans "Request resize" %}
</a>
{% endif %}
{% endif %}
{% if op.remove_disk %}
<a href="{{ op.remove_disk.get_url }}?disk={{d.pk}}"
class="btn btn-xs btn-{{ op.remove_disk.effect}} operation disk-remove-btn
{% if op.remove_disk.disabled %}disabled{% endif %}">
<i class="fa fa-{{ op.remove_disk.icon }} fa-fw-12"></i> {% trans "Remove" %}
</a>
{% endif %}
</span>
<div style="clear: both;"></div> <div style="clear: both;"></div>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
......
...@@ -66,6 +66,17 @@ ...@@ -66,6 +66,17 @@
{% endfor %} {% endfor %}
</div> </div>
<hr />
{% if instance.disks.all %}
<div id="vm-details-resize-how-to">
<i class="fa fa-question"></i>
{% url "info.resize" as resize_url %}
{% blocktrans with url=resize_url %}
If you need help resizing the disks check out our <a href="{{ url }}">resize how-to.</a>
{% endblocktrans %}
</div>
{% endif %}
{% if user.is_superuser %} {% if user.is_superuser %}
<hr/> <hr/>
......
...@@ -136,6 +136,10 @@ class HelpView(TemplateView): ...@@ -136,6 +136,10 @@ class HelpView(TemplateView):
return ctx return ctx
class ResizeHelpView(TemplateView):
template_name = "info/resize.html"
class OpenSearchDescriptionView(TemplateView): class OpenSearchDescriptionView(TemplateView):
template_name = "dashboard/vm-opensearch.xml" template_name = "dashboard/vm-opensearch.xml"
content_type = "application/opensearchdescription+xml" content_type = "application/opensearchdescription+xml"
......
...@@ -22,6 +22,8 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -22,6 +22,8 @@ from django.utils.translation import ugettext_lazy as _
from django.template import RequestContext from django.template import RequestContext
from django.template.loader import render_to_string from django.template.loader import render_to_string
from sizefield.widgets import FileSizeWidget
from sizefield.utils import filesizeformat
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit from crispy_forms.layout import Submit
...@@ -70,34 +72,32 @@ class InitialFromFileMixin(object): ...@@ -70,34 +72,32 @@ class InitialFromFileMixin(object):
RequestContext(request, {}), RequestContext(request, {}),
) )
def clean(self): def clean_message(self):
cleaned_data = super(InitialFromFileMixin, self).clean() message = self.cleaned_data['message']
if cleaned_data['message'].strip() == self.initial['message'].strip(): if message.strip() == self.initial['message'].strip():
raise ValidationError( raise ValidationError(_("Fill in the message."), code="invalid")
_("Fill in the message."), return message.strip()
code="invalid")
return cleaned_data
class TemplateRequestForm(InitialFromFileMixin, Form): class TemplateRequestForm(InitialFromFileMixin, Form):
message = CharField(widget=Textarea, label=_("Message"))
template = ModelChoiceField(TemplateAccessType.objects.all(), template = ModelChoiceField(TemplateAccessType.objects.all(),
label=_("Template share")) label=_("Template share"))
level = ChoiceField(TemplateAccessAction.LEVELS, widget=RadioSelect, level = ChoiceField(TemplateAccessAction.LEVELS, widget=RadioSelect,
initial=TemplateAccessAction.LEVELS.user) initial=TemplateAccessAction.LEVELS.user)
message = CharField(widget=Textarea, label=_("Message"))
initial_template = "request/initials/template.html" initial_template = "request/initials/template.html"
class LeaseRequestForm(InitialFromFileMixin, Form): class LeaseRequestForm(InitialFromFileMixin, Form):
lease = ModelChoiceField(LeaseType.objects.all(), label=_("Lease")) lease = ModelChoiceField(LeaseType.objects.all(), label=_("Lease"))
message = CharField(widget=Textarea) message = CharField(widget=Textarea, label=_("Message"))
initial_template = "request/initials/lease.html" initial_template = "request/initials/lease.html"
class ResourceRequestForm(InitialFromFileMixin, VmResourcesForm): class ResourceRequestForm(InitialFromFileMixin, VmResourcesForm):
message = CharField(widget=Textarea) message = CharField(widget=Textarea, label=_("Message"))
initial_template = "request/initials/resources.html" initial_template = "request/initials/resources.html"
...@@ -110,3 +110,28 @@ class ResourceRequestForm(InitialFromFileMixin, VmResourcesForm): ...@@ -110,3 +110,28 @@ class ResourceRequestForm(InitialFromFileMixin, VmResourcesForm):
raise ValidationError( raise ValidationError(
_("You haven't changed any of the resources."), _("You haven't changed any of the resources."),
code="invalid") code="invalid")
class ResizeRequestForm(InitialFromFileMixin, Form):
message = CharField(widget=Textarea, label=_("Message"))
size = CharField(widget=FileSizeWidget, label=_('Size'),
help_text=_('Size to resize the disk in bytes or with'
' units like MB or GB.'))
initial_template = "request/initials/resize.html"
def __init__(self, *args, **kwargs):
self.disk = kwargs.pop("disk")
super(ResizeRequestForm, self).__init__(*args, **kwargs)
def clean_size(self):
cleaned_data = super(ResizeRequestForm, self).clean()
disk = self.disk
size_in_bytes = cleaned_data.get("size")
if not size_in_bytes.isdigit() and len(size_in_bytes) > 0:
raise ValidationError(_("Invalid format, you can use GB or MB!"))
if int(size_in_bytes) < int(disk.size):
raise ValidationError(_("Disk size must be greater than the actual"
"size (%s).") % filesizeformat(disk.size))
return size_in_bytes
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import sizefield.models
class Migration(migrations.Migration):
dependencies = [
('vm', '0002_interface_model'),
('storage', '0002_disk_bus'),
('request', '0003_auto_20150410_1917'),
]
operations = [
migrations.CreateModel(
name='DiskResizeAction',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('size', sizefield.models.FileSizeField(default=None, null=True)),
('disk', models.ForeignKey(to='storage.Disk')),
('instance', models.ForeignKey(to='vm.Instance')),
],
options={
'abstract': False,
},
),
migrations.AlterField(
model_name='request',
name='type',
field=models.CharField(max_length=10, choices=[(b'resource', 'resource request'), (b'lease', 'lease request'), (b'template', 'template access request'), (b'resize', 'disk resize request')]),
),
]
...@@ -32,10 +32,14 @@ from django.utils.translation import ( ...@@ -32,10 +32,14 @@ from django.utils.translation import (
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import requests import requests
from sizefield.models import FileSizeField
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from model_utils import Choices from model_utils import Choices
from sizefield.utils import filesizeformat
from vm.models import Instance, InstanceTemplate, Lease from vm.models import Instance, InstanceTemplate, Lease
from vm.operations import ResourcesOperation, ResizeDiskOperation
from storage.models import Disk
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -49,6 +53,9 @@ class RequestAction(Model): ...@@ -49,6 +53,9 @@ class RequestAction(Model):
def accept_msg(self): def accept_msg(self):
raise NotImplementedError raise NotImplementedError
def is_acceptable(self):
return True
class Meta: class Meta:
abstract = True abstract = True
...@@ -77,6 +84,7 @@ class Request(TimeStampedModel): ...@@ -77,6 +84,7 @@ class Request(TimeStampedModel):
('resource', _('resource request')), ('resource', _('resource request')),
('lease', _("lease request")), ('lease', _("lease request")),
('template', _("template access request")), ('template', _("template access request")),
('resize', _("disk resize request")),
) )
type = CharField(choices=TYPES, max_length=10) type = CharField(choices=TYPES, max_length=10)
message = TextField(verbose_name=_("Message")) message = TextField(verbose_name=_("Message"))
...@@ -99,7 +107,8 @@ class Request(TimeStampedModel): ...@@ -99,7 +107,8 @@ class Request(TimeStampedModel):
return { return {
'resource': "tasks", 'resource': "tasks",
'lease': "clock-o", 'lease': "clock-o",
'template': "puzzle-piece" 'template': "puzzle-piece",
'resize': "arrows-alt",
}.get(self.type) }.get(self.type)
def get_effect(self): def get_effect(self):
...@@ -143,6 +152,10 @@ class Request(TimeStampedModel): ...@@ -143,6 +152,10 @@ class Request(TimeStampedModel):
decline_msg, url=self.get_absolute_url(), reason=self.reason, decline_msg, url=self.get_absolute_url(), reason=self.reason,
) )
@property
def is_acceptable(self):
return self.action.is_acceptable()
class LeaseType(RequestType): class LeaseType(RequestType):
lease = ForeignKey(Lease, verbose_name=_("Lease")) lease = ForeignKey(Lease, verbose_name=_("Lease"))
...@@ -200,6 +213,9 @@ class ResourceChangeAction(RequestAction): ...@@ -200,6 +213,9 @@ class ResourceChangeAction(RequestAction):
'priority': self.priority, 'priority': self.priority,
} }
def is_acceptable(self):
return self.instance.status in ResourcesOperation.accept_states
class ExtendLeaseAction(RequestAction): class ExtendLeaseAction(RequestAction):
instance = ForeignKey(Instance) instance = ForeignKey(Instance)
...@@ -246,6 +262,30 @@ class TemplateAccessAction(RequestAction): ...@@ -246,6 +262,30 @@ class TemplateAccessAction(RequestAction):
) % ", ".join([x.name for x in self.template_type.templates.all()]) ) % ", ".join([x.name for x in self.template_type.templates.all()])
class DiskResizeAction(RequestAction):
instance = ForeignKey(Instance)
disk = ForeignKey(Disk)
size = FileSizeField(null=True, default=None)
def accept(self, user):
self.instance.resize_disk(disk=self.disk, size=self.size, user=user)
@property
def accept_msg(self):
return _(
'The disk <em class="text-muted">%(disk_name)s (#%(id)d)</em> of '
'<a href="%(url)s">%(vm_name)s</a> got resized. '
'The new size is: %(bytes)d bytes (%(size)s).'
) % {'disk_name': self.disk.name, 'id': self.disk.id,
'url': self.instance.get_absolute_url(),
'vm_name': self.instance.name,
'bytes': self.size, 'size': filesizeformat(self.size),
}
def is_acceptable(self):
return self.instance.status in ResizeDiskOperation.accept_states
def send_notifications(sender, instance, created, **kwargs): def send_notifications(sender, instance, created, **kwargs):
if not created: if not created:
return return
......
{% load i18n %}
{% load crispy_forms_tags %}
{% load sizefieldtags %}
<dl>
<dt>{% trans "Virtual machine" %}</dt>
<dd><a href="{{ vm.get_absolute_url }}">{{ vm.name }}</a></dd>
<dt>{% trans "Disk" %}</dt>
<dd>
{% if request.user.is_superuser %}
<a href="{{ disk.get_absolute_url }}">{{ disk.name }} (#{{ disk.id }})</a>
{% else %}
{{ disk.name }} (#{{ disk.id }})
{% endif %}
- {{ disk.size|filesize }}
</dd>
</dl>
<form action="{% url "request.views.request-resize" vm_pk=vm.pk disk_pk=disk.pk %}" method="POST">
{% include "display-form-errors.html" %}
{% csrf_token %}
{{ form.size|as_crispy_field }}
{{ form.message|as_crispy_field }}
<input type="submit" class="btn btn-primary" id="op-form-send"/>
</form>
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %} {% load render_table from django_tables2 %}
{% load arrowfilter %} {% load arrowfilter %}
{% load sizefieldtags %}
{% block title-page %}{% trans "Request" %}{% endblock %} {% block title-page %}{% trans "Request" %}{% endblock %}
...@@ -65,6 +66,15 @@ ...@@ -65,6 +66,15 @@
<dd>{{ action.get_readable_level }}</dd> <dd>{{ action.get_readable_level }}</dd>
</dl> </dl>
{% elif object.type == "resource" %} {% elif object.type == "resource" %}
{% if not is_acceptable %}
<div class="alert alert-warning">
{% blocktrans %}
To change the resources the virtual machine must be in one of the following states:
STOPPED, PENDING, RUNNING. If the virtual machine is running it will be
automatically stopped when accepting the request.
{% endblocktrans %}
</div>
{% endif %}
<dl> <dl>
<dt>{% trans "VM name" %}</dt> <dt>{% trans "VM name" %}</dt>
<dd><a href="{{ action.instance.get_absolute_url }}">{{ action.instance.name }}</a></dd> <dd><a href="{{ action.instance.get_absolute_url }}">{{ action.instance.name }}</a></dd>
...@@ -74,7 +84,7 @@ ...@@ -74,7 +84,7 @@
{{ action.instance.get_status_display|upper }} {{ action.instance.get_status_display|upper }}
</dd> </dd>
<dt>{% trans "VM description" %}</dt> <dt>{% trans "VM description" %}</dt>
<dd>{{ action.instance.description }}</dd> <dd>{{ action.instance.description|default:"-" }}</dd>
<dt> <dt>
{% trans "Priority" %} {% trans "Priority" %}
<span class="text-muted" style="font-weight: normal;">{% trans "(old values in parentheses)" %}</span> <span class="text-muted" style="font-weight: normal;">{% trans "(old values in parentheses)" %}</span>
...@@ -85,8 +95,39 @@ ...@@ -85,8 +95,39 @@
<dt>{% trans "Ram size" %}</dt> <dt>{% trans "Ram size" %}</dt>
<dd>{{ action.ram_size }} ({{ action.instance.ram_size }}) MiB</dd> <dd>{{ action.ram_size }} ({{ action.instance.ram_size }}) MiB</dd>
</dl> </dl>
{% elif object.type == "resize" %}
{% if not is_acceptable %}
<div class="alert alert-warning">
{% trans "To resize the disk the virtual machine must be in RUNNING state." %}
</div>
{% endif %}
<dl>
<dt>{% trans "VM name" %}</dt>
<dd><a href="{{ action.instance.get_absolute_url }}">{{ action.instance.name }}</a></dd>
<dt>{% trans "Status" %}</dt>
<dd>
<i class="fa {{ action.instance.get_status_icon }}"></i>
{{ action.instance.get_status_display|upper }}
</dd>
<dt>{% trans "VM description" %}</dt>
<dd>{{ action.instance.description|default:"-" }}</dd>
<dt>{% trans "Disk" %}</dt>
<dd>
{% if request.user.is_superuser %}
<a href="{{ action.disk.get_absolute_url }}">
{{ action.disk.name }} (#{{ action.disk.id}})
</a>
{% else %}
{{ action.disk.name }} (#{{ action.disk.id}})</dd>
{% endif %}
</dd>
<dt>{% trans "Current size" %}</dt>
<dd>{{ action.disk.size|filesize}} ({{ action.disk.size }} bytes)</dd>
<dt>{% trans "Requested size" %}</dt>
<dd>{{ action.size|filesize}} ({{ action.size }} bytes)</dd>
</dl>
{% else %} {% else %}
hacks!!! Are you adding a new action type?
{% endif %} {% endif %}
{% if object.status == "PENDING" and request.user.is_superuser %} {% if object.status == "PENDING" and request.user.is_superuser %}
...@@ -103,7 +144,7 @@ ...@@ -103,7 +144,7 @@
{% trans "Decline" %} {% trans "Decline" %}
</button> </button>
</form> </form>
{% if object.type == "resource" and action.instance.status not in accept_states %} {% if not is_acceptable %}
{% trans "You can't accept this request because of the VM's state." %} {% trans "You can't accept this request because of the VM's state." %}
{% else %} {% else %}
<form method="POST"> <form method="POST">
......
{% spaceless %}
{% if LANGUAGE_CODE == "en" %}
Why do you need a bigger disk?
{% else %} {# place your translations here #}
Why do you need a bigger disk?
{% endif %}
{% endspaceless %}
...@@ -23,7 +23,7 @@ from .views import ( ...@@ -23,7 +23,7 @@ from .views import (
LeaseTypeCreate, LeaseTypeDetail, LeaseTypeCreate, LeaseTypeDetail,
TemplateAccessTypeCreate, TemplateAccessTypeDetail, TemplateAccessTypeCreate, TemplateAccessTypeDetail,
TemplateRequestView, LeaseRequestView, ResourceRequestView, TemplateRequestView, LeaseRequestView, ResourceRequestView,
LeaseTypeDelete, TemplateAccessTypeDelete, LeaseTypeDelete, TemplateAccessTypeDelete, ResizeRequestView,
) )
urlpatterns = patterns( urlpatterns = patterns(
...@@ -60,4 +60,6 @@ urlpatterns = patterns( ...@@ -60,4 +60,6 @@ urlpatterns = patterns(
name="request.views.request-lease"), name="request.views.request-lease"),
url(r'resource/(?P<vm_pk>\d+)/$', ResourceRequestView.as_view(), url(r'resource/(?P<vm_pk>\d+)/$', ResourceRequestView.as_view(),
name="request.views.request-resource"), name="request.views.request-resource"),
url(r'resize/(?P<vm_pk>\d+)/(?P<disk_pk>\d+)/$',
ResizeRequestView.as_view(), name="request.views.request-resize"),
) )
...@@ -19,27 +19,29 @@ from __future__ import unicode_literals, absolute_import ...@@ -19,27 +19,29 @@ from __future__ import unicode_literals, absolute_import
from django.views.generic import ( from django.views.generic import (
UpdateView, TemplateView, DetailView, CreateView, FormView, DeleteView, UpdateView, TemplateView, DetailView, CreateView, FormView, DeleteView,
) )
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.http import JsonResponse
from braces.views import SuperuserRequiredMixin, LoginRequiredMixin from braces.views import SuperuserRequiredMixin, LoginRequiredMixin
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from request.models import ( from request.models import (
Request, TemplateAccessType, LeaseType, TemplateAccessAction, Request, TemplateAccessType, LeaseType, TemplateAccessAction,
ExtendLeaseAction, ResourceChangeAction, ExtendLeaseAction, ResourceChangeAction, DiskResizeAction
) )
from storage.models import Disk
from vm.models import Instance from vm.models import Instance
from vm.operations import ResourcesOperation
from request.tables import ( from request.tables import (
RequestTable, TemplateAccessTypeTable, LeaseTypeTable, RequestTable, TemplateAccessTypeTable, LeaseTypeTable,
) )
from request.forms import ( from request.forms import (
LeaseTypeForm, TemplateAccessTypeForm, TemplateRequestForm, LeaseTypeForm, TemplateAccessTypeForm, TemplateRequestForm,
LeaseRequestForm, ResourceRequestForm, LeaseRequestForm, ResourceRequestForm, ResizeRequestForm,
) )
...@@ -93,7 +95,7 @@ class RequestDetail(LoginRequiredMixin, DetailView): ...@@ -93,7 +95,7 @@ class RequestDetail(LoginRequiredMixin, DetailView):
context = super(RequestDetail, self).get_context_data(**kwargs) context = super(RequestDetail, self).get_context_data(**kwargs)
context['action'] = request.action context['action'] = request.action
context['accept_states'] = ResourcesOperation.accept_states context['is_acceptable'] = request.is_acceptable
# workaround for http://git.io/vIIYi # workaround for http://git.io/vIIYi
context['request'] = self.request context['request'] = self.request
...@@ -167,6 +169,7 @@ class RequestTypeList(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -167,6 +169,7 @@ class RequestTypeList(LoginRequiredMixin, SuperuserRequiredMixin,
class TemplateRequestView(LoginRequiredMixin, FormView): class TemplateRequestView(LoginRequiredMixin, FormView):
form_class = TemplateRequestForm form_class = TemplateRequestForm
template_name = "request/request-template.html" template_name = "request/request-template.html"
success_message = _("Request successfully sent.")
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super(TemplateRequestView, self).get_form_kwargs() kwargs = super(TemplateRequestView, self).get_form_kwargs()
...@@ -192,7 +195,8 @@ class TemplateRequestView(LoginRequiredMixin, FormView): ...@@ -192,7 +195,8 @@ class TemplateRequestView(LoginRequiredMixin, FormView):
) )
req.save() req.save()
return redirect("/") messages.success(self.request, self.success_message)
return redirect(reverse("dashboard.index"))
class VmRequestMixin(LoginRequiredMixin, object): class VmRequestMixin(LoginRequiredMixin, object):
...@@ -224,6 +228,7 @@ class LeaseRequestView(VmRequestMixin, FormView): ...@@ -224,6 +228,7 @@ class LeaseRequestView(VmRequestMixin, FormView):
form_class = LeaseRequestForm form_class = LeaseRequestForm
template_name = "request/request-lease.html" template_name = "request/request-lease.html"
user_level = "operator" user_level = "operator"
success_message = _("Request successfully sent.")
def form_valid(self, form): def form_valid(self, form):
data = form.cleaned_data data = form.cleaned_data
...@@ -244,6 +249,7 @@ class LeaseRequestView(VmRequestMixin, FormView): ...@@ -244,6 +249,7 @@ class LeaseRequestView(VmRequestMixin, FormView):
) )
req.save() req.save()
messages.success(self.request, self.success_message)
return redirect(vm.get_absolute_url()) return redirect(vm.get_absolute_url())
...@@ -251,6 +257,7 @@ class ResourceRequestView(VmRequestMixin, FormView): ...@@ -251,6 +257,7 @@ class ResourceRequestView(VmRequestMixin, FormView):
form_class = ResourceRequestForm form_class = ResourceRequestForm
template_name = "request/request-resource.html" template_name = "request/request-resource.html"
user_level = "user" user_level = "user"
success_message = _("Request successfully sent.")
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super(ResourceRequestView, self).get_form_kwargs() kwargs = super(ResourceRequestView, self).get_form_kwargs()
...@@ -287,4 +294,60 @@ class ResourceRequestView(VmRequestMixin, FormView): ...@@ -287,4 +294,60 @@ class ResourceRequestView(VmRequestMixin, FormView):
) )
req.save() req.save()
messages.success(self.request, self.success_message)
return redirect(vm.get_absolute_url())
class ResizeRequestView(VmRequestMixin, FormView):
form_class = ResizeRequestForm
template_name = "request/_request-resize-form.html"
user_level = "owner"
success_message = _("Request successfully sent.")
def get_disk(self, *args, **kwargs):
disk = get_object_or_404(Disk, pk=self.kwargs['disk_pk'])
if disk not in self.get_vm().disks.all():
raise SuspiciousOperation
return disk
def get_form_kwargs(self):
kwargs = super(ResizeRequestView, self).get_form_kwargs()
kwargs['disk'] = self.get_disk()
return kwargs
def get_template_names(self):