Commit df4f36a3 by Kálmán Viktor

dashboard: rework disk adding

parent 2fc3ff12
...@@ -17,7 +17,9 @@ from sizefield.widgets import FileSizeWidget ...@@ -17,7 +17,9 @@ from sizefield.widgets import FileSizeWidget
from firewall.models import Vlan, Host from firewall.models import Vlan, Host
from storage.models import Disk, DataStore from storage.models import Disk, DataStore
from vm.models import InstanceTemplate, Lease, InterfaceTemplate, Node from vm.models import (
InstanceTemplate, Lease, InterfaceTemplate, Node, Instance
)
VLANS = Vlan.objects.all() VLANS = Vlan.objects.all()
DISKS = Disk.objects.exclude(type="qcow2-snap") DISKS = Disk.objects.exclude(type="qcow2-snap")
...@@ -725,27 +727,59 @@ class LeaseForm(forms.ModelForm): ...@@ -725,27 +727,59 @@ class LeaseForm(forms.ModelForm):
class DiskAddForm(forms.Form): class DiskAddForm(forms.Form):
name = forms.CharField() name = forms.CharField()
size = forms.CharField(widget=FileSizeWidget) size = forms.CharField(widget=FileSizeWidget, required=False)
url = forms.CharField(required=False)
add_to = forms.CharField()
object_pk = forms.CharField()
def __init__(self, *args, **kwargs):
self.add_to = kwargs.pop("add_to")
self.object_pk = kwargs.pop("object_pk")
super(DiskAddForm, self).__init__(*args, **kwargs)
self.initial['add_to'] = self.add_to
self.initial['object_pk'] = self.object_pk
def clean_size(self): def clean_size(self):
size_in_bytes = self.cleaned_data.get("size") size_in_bytes = self.cleaned_data.get("size")
if not size_in_bytes.isdigit(): if not size_in_bytes.isdigit() and len(size_in_bytes) > 0:
raise forms.ValidationError(_("Invalid format, you can use " raise forms.ValidationError(_("Invalid format, you can use "
" GB or MB!")) " GB or MB!"))
return size_in_bytes return size_in_bytes
def save(self, vm, commit=True): def clean(self):
cleaned_data = self.cleaned_data
size = cleaned_data.get("size")
url = cleaned_data.get("url")
if not size and not url:
msg = _("You have to either specify size or URL")
self._errors[_("Global")] = self.error_class([msg])
return cleaned_data
def save(self, commit=True):
data = self.cleaned_data data = self.cleaned_data
d = Disk(
name=data['name'], if data['size']:
filename=str(uuid.uuid4()), d = Disk(
datastore=DataStore.objects.all()[0], name=data['name'],
type="qcow2-norm", filename=str(uuid.uuid4()),
size=data['size'], datastore=DataStore.objects.all()[0],
dev_num="a", type="qcow2-norm",
) size=data['size'],
d.save() dev_num="a",
vm.disks.add(d) )
d.save()
else:
# TODO
d = None
if self.add_to == "template":
vm_or_temp = InstanceTemplate.objects.get(pk=self.object_pk)
else:
vm_or_temp = Instance.objects.get(pk=self.object_pk)
vm_or_temp.disks.add(d)
return d return d
@property @property
...@@ -753,11 +787,23 @@ class DiskAddForm(forms.Form): ...@@ -753,11 +787,23 @@ class DiskAddForm(forms.Form):
helper = FormHelper() helper = FormHelper()
helper.form_show_labels = False helper.form_show_labels = False
helper.layout = Layout( helper.layout = Layout(
Field("add_to", type="hidden"),
Field("object_pk", type="hidden"),
Field("name", placeholder=_("Name")), Field("name", placeholder=_("Name")),
Field("size", placeholder=_("Disk size (for example: 20GB, " Field("size", placeholder=_("Disk size (for example: 20GB, "
"1500MB)")), "1500MB)")),
Field("url", placeholder=_("URL to an ISO image")),
AnyTag(
"div",
HTML(
_("Either specify the size for an empty disk or a URL "
"to an ISO image!")
),
css_class="alert alert-info",
style="padding: 5px; text-align: justify;",
),
) )
helper.add_input(Submit("submit", "Create new disk", helper.add_input(Submit("submit", _("Add"),
css_class="btn btn-success")) css_class="btn btn-success"))
return helper return helper
......
...@@ -64,8 +64,19 @@ ...@@ -64,8 +64,19 @@
</form> </form>
</div> </div>
</div> </div>
</div>
</div> <div class="panel panel-default">
<div class="panel-heading">
<h3 class="no-margin"><i class="icon-file"></i> {% trans "Create new disk" %}</h3>
</div>
<div class="panel-body">
<form action="{% url "dashboard.views.disk-add" %}" method="POST">
{% crispy disk_add_form %}
</form>
</div>
</div>
</div><!-- .col-md-4 -->
</div><!-- .row -->
<style> <style>
......
...@@ -52,9 +52,12 @@ ...@@ -52,9 +52,12 @@
</a> </a>
</div> </div>
</h3> </h3>
<div class="row" id="vm-details-disk-add-for-form">
</div> <div class="row" id="vm-details-disk-add-for-form"></div>
{% if not instance.disks.all %}
{% trans "No disks are added!" %}
{% endif %}
{% for d in instance.disks.all %} {% for d in instance.disks.all %}
<h4 class="list-group-item-heading dashboard-vm-details-network-h3"> <h4 class="list-group-item-heading dashboard-vm-details-network-h3">
<i class="icon-file"></i> {{ d.name }} (#{{ d.id }}) - {{ d.size|filesize }} <i class="icon-file"></i> {{ d.name }} (#{{ d.id }}) - {{ d.size|filesize }}
...@@ -67,7 +70,7 @@ ...@@ -67,7 +70,7 @@
<div class="col-md-12"> <div class="col-md-12">
<div> <div>
<hr /> <hr />
<form method="POST" action="" style="max-width: 300px;"> <form method="POST" action="{% url "dashboard.views.disk-add" %}" style="max-width: 350px;">
{% crispy forms.disk_add_form %} {% crispy forms.disk_add_form %}
</form> </form>
<hr /> <hr />
......
...@@ -9,7 +9,7 @@ from .views import ( ...@@ -9,7 +9,7 @@ from .views import (
FavouriteView, NodeStatus, GroupList, TemplateDelete, LeaseDelete, FavouriteView, NodeStatus, GroupList, TemplateDelete, LeaseDelete,
VmGraphView, TemplateAclUpdateView, GroupDetailView, GroupDelete, VmGraphView, TemplateAclUpdateView, GroupDetailView, GroupDelete,
GroupAclUpdateView, GroupUserDelete, NotificationView, NodeGraphView, GroupAclUpdateView, GroupUserDelete, NotificationView, NodeGraphView,
VmMigrateView, VmMigrateView, DiskAddView
) )
urlpatterns = patterns( urlpatterns = patterns(
...@@ -87,4 +87,7 @@ urlpatterns = patterns( ...@@ -87,4 +87,7 @@ urlpatterns = patterns(
url(r'^notifications/$', NotificationView.as_view(), url(r'^notifications/$', NotificationView.as_view(),
name="dashboard.views.notifications"), name="dashboard.views.notifications"),
url(r'^disk/add$', DiskAddView.as_view(),
name="dashboard.views.disk-add"),
) )
...@@ -20,7 +20,7 @@ from django.views.generic import (TemplateView, DetailView, View, DeleteView, ...@@ -20,7 +20,7 @@ from django.views.generic import (TemplateView, DetailView, View, DeleteView,
UpdateView, CreateView) UpdateView, CreateView)
from django.contrib import messages from django.contrib import messages
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.template.defaultfilters import title from django.template.defaultfilters import title as title_filter
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
...@@ -180,7 +180,9 @@ class VmDetailView(CheckedDetailView): ...@@ -180,7 +180,9 @@ class VmDetailView(CheckedDetailView):
).all() ).all()
context['acl'] = get_vm_acl_data(instance) context['acl'] = get_vm_acl_data(instance)
context['forms'] = { context['forms'] = {
'disk_add_form': DiskAddForm(prefix="disk"), 'disk_add_form': DiskAddForm(
add_to="instance", object_pk=self.get_object().pk,
prefix="disk"),
} }
context['os_type_icon'] = instance.os_type.replace("unknown", context['os_type_icon'] = instance.os_type.replace("unknown",
"question") "question")
...@@ -199,7 +201,6 @@ class VmDetailView(CheckedDetailView): ...@@ -199,7 +201,6 @@ class VmDetailView(CheckedDetailView):
'port': self.__add_port, 'port': self.__add_port,
'new_network_vlan': self.__new_network, 'new_network_vlan': self.__new_network,
'save_as': self.__save_as, 'save_as': self.__save_as,
'disk-name': self.__add_disk,
'shut_down': self.__shut_down, 'shut_down': self.__shut_down,
'sleep': self.__sleep, 'sleep': self.__sleep,
'wake_up': self.__wake_up, 'wake_up': self.__wake_up,
...@@ -374,24 +375,6 @@ class VmDetailView(CheckedDetailView): ...@@ -374,24 +375,6 @@ class VmDetailView(CheckedDetailView):
return redirect(reverse_lazy("dashboard.views.template-detail", return redirect(reverse_lazy("dashboard.views.template-detail",
kwargs={'pk': template.pk})) kwargs={'pk': template.pk}))
def __add_disk(self, request):
self.object = self.get_object()
if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied()
form = DiskAddForm(request.POST, prefix="disk")
if form.is_valid():
messages.success(request, _("New disk successfully created!"))
form.save(self.object)
else:
error = "<br /> ".join(["<strong>%s</strong>: %s" %
(title(i[0]), i[1][0])
for i in form.errors.items()])
messages.error(request, error)
return redirect("%s#resources" % reverse_lazy(
"dashboard.views.detail", kwargs={'pk': self.object.pk}))
def __shut_down(self, request): def __shut_down(self, request):
self.object = self.get_object() self.object = self.get_object()
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
...@@ -749,6 +732,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -749,6 +732,11 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(TemplateDetail, self).get_context_data(**kwargs) context = super(TemplateDetail, self).get_context_data(**kwargs)
context['acl'] = get_vm_acl_data(self.get_object()) context['acl'] = get_vm_acl_data(self.get_object())
context['disk_add_form'] = DiskAddForm(
add_to="template",
object_pk=self.get_object().pk,
prefix="disk",
)
return context return context
def get_success_url(self): def get_success_url(self):
...@@ -1718,3 +1706,31 @@ class VmMigrateView(SuperuserRequiredMixin, TemplateView): ...@@ -1718,3 +1706,31 @@ class VmMigrateView(SuperuserRequiredMixin, TemplateView):
messages.error(self.request, _("You didn't select a node!")) messages.error(self.request, _("You didn't select a node!"))
return redirect("%s#activity" % vm.get_absolute_url()) return redirect("%s#activity" % vm.get_absolute_url())
class DiskAddView(SuperuserRequiredMixin, TemplateView):
def post(self, *args, **kwargs):
add_to = self.request.POST.get("disk-add_to")
object_pk = self.request.POST.get("disk-object_pk")
form = DiskAddForm(
self.request.POST,
add_to=add_to, object_pk=object_pk,
prefix="disk"
)
if form.is_valid():
messages.success(self.request, _("Disk successfully added!"))
form.save()
else:
error = "<br /> ".join(["<strong>%s</strong>: %s" %
(title_filter(i[0]), i[1][0])
for i in form.errors.items()])
messages.error(self.request, error)
if add_to == "template":
r = InstanceTemplate.objects.get(pk=object_pk).get_absolute_url()
else:
r = Instance.objects.get(pk=object_pk).get_absolute_url()
r = "%s#resources" % r
return redirect(r)
...@@ -152,6 +152,10 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -152,6 +152,10 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
if is_new: if is_new:
self.set_level(self.owner, 'owner') self.set_level(self.owner, 'owner')
@permalink
def get_absolute_url(self):
return ('dashboard.views.template-detail', None, {'pk': self.pk})
class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel): class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel):
......
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