Commit 4ce5d3d5 by Kálmán Viktor

dashboard: add disk to instances

parent 0a3fc812
from datetime import timedelta from datetime import timedelta
import uuid
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import ( from crispy_forms.layout import (
...@@ -10,9 +11,10 @@ from django.forms.widgets import TextInput ...@@ -10,9 +11,10 @@ from django.forms.widgets import TextInput
from django.template import Context from django.template import Context
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from sizefield.widgets import FileSizeWidget
from firewall.models import Vlan, Host from firewall.models import Vlan, Host
from storage.models import Disk from storage.models import Disk, DataStore
from vm.models import InstanceTemplate, Lease, InterfaceTemplate, Node from vm.models import InstanceTemplate, Lease, InterfaceTemplate, Node
...@@ -695,6 +697,45 @@ class LeaseForm(forms.ModelForm): ...@@ -695,6 +697,45 @@ class LeaseForm(forms.ModelForm):
model = Lease model = Lease
class DiskAddForm(forms.Form):
name = forms.CharField()
size = forms.CharField(widget=FileSizeWidget)
def clean_size(self):
size_in_bytes = self.cleaned_data.get("size")
if not size_in_bytes.isdigit():
raise forms.ValidationError(_("Invalid format, you can use "
" GB or MB!"))
return size_in_bytes
def save(self, vm, commit=True):
data = self.cleaned_data
d = Disk(
name=data['name'],
filename=str(uuid.uuid4()),
datastore=DataStore.objects.all()[0],
type="qcow2-norm",
size=data['size'],
dev_num="a",
)
d.save()
vm.disks.add(d)
return d
@property
def helper(self):
helper = FormHelper()
helper.form_show_labels = False
helper.layout = Layout(
Field("name", placeholder=_("Name")),
Field("size", placeholder=_("Disk size (for example: 20GB, "
"1500MB)")),
)
helper.add_input(Submit("submit", "Create new disk",
css_class="btn btn-success"))
return helper
class LinkButton(BaseInput): class LinkButton(BaseInput):
""" """
......
...@@ -145,6 +145,12 @@ $(function() { ...@@ -145,6 +145,12 @@ $(function() {
$('input[name="new_network_managed"]').tooltip(); $('input[name="new_network_managed"]').tooltip();
return false; return false;
}); });
/* add disk button */
$("#vm-details-disk-add").click(function() {
$("#vm-details-disk-add-for-form").html($("#vm-details-disk-add-form").html());
return false;
});
}); });
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
{% for message in messages %} {% for message in messages %}
<div class="alert <div class="alert
{% if message.tags %} alert-{% if message.tags == "error" %}danger{% else %}{{ message.tags }}{% endif %}{% endif %}"> {% if message.tags %} alert-{% if message.tags == "error" %}danger{% else %}{{ message.tags }}{% endif %}{% endif %}">
{{ message }} {{ message|safe }}
</div> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
......
{% load i18n %} {% load i18n %}
{% load sizefieldtags %}
{% load crispy_forms_tags %}
<form id="vm-details-resources-form" method="POST" action=""> <form id="vm-details-resources-form" method="POST" action="">
{% csrf_token %} {% csrf_token %}
<p class="row"> <p class="row">
...@@ -36,8 +39,45 @@ ...@@ -36,8 +39,45 @@
</div> </div>
</p> </p>
</form> </form>
<hr />
<div class="row">
<div class="col-sm-11">
<h3>
{% trans "Disks" %}
<div class="pull-right">
<a href="#" id="vm-details-disk-add" class="btn btn-success btn-xs">
<i class="icon-plus"></i> {% trans "Add new disk" %}
</a>
</div>
</h3>
<div class="row" id="vm-details-disk-add-for-form">
</div>
{% for d in instance.disks.all %}
<h4 class="list-group-item-heading dashboard-vm-details-network-h3">
<i class="icon-file"></i> {{ d.name }} - {{ d.size|filesize }}
</h4>
{% endfor %}
</div>
</div>
<div class="js-hidden row" id="vm-details-disk-add-form">
<div class="col-md-12">
<div>
<hr />
<form method="POST" action="" style="max-width: 300px;">
{% crispy forms.disk_add_form %}
</form>
<hr />
</div>
</div>
</div>
{% block extra_js %} {% block extra_js %}
<style> <style>
label {padding-top: 6px;} label {padding-top: 6px;}
.form-group {margin-bottom: 8px;}
</style> </style>
{% endblock %} {% endblock %}
...@@ -202,3 +202,25 @@ class VmDetailTest(TestCase): ...@@ -202,3 +202,25 @@ class VmDetailTest(TestCase):
# redirect to the login page # redirect to the login page
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(leases, Lease.objects.count()) self.assertEqual(leases, Lease.objects.count())
def test_unpermitted_vm_disk_add(self):
c = Client()
self.login(c, "user2")
inst = Instance.objects.get(pk=1)
inst.set_level(self.u1, 'owner')
disks = inst.disks.count()
response = c.post("/dashboard/vm/1/", {'disk-name': "a",
'disk-size': 1})
self.assertEqual(response.status_code, 403)
self.assertEqual(disks, inst.disks.count())
def test_permitted_vm_disk_add(self):
c = Client()
self.login(c, "user1")
inst = Instance.objects.get(pk=1)
inst.set_level(self.u1, 'owner')
disks = inst.disks.count()
response = c.post("/dashboard/vm/1/", {'disk-name': "a",
'disk-size': 1})
self.assertEqual(response.status_code, 302)
self.assertEqual(disks + 1, inst.disks.count())
...@@ -19,12 +19,15 @@ from django.views.generic import (TemplateView, DetailView, View, DeleteView, ...@@ -19,12 +19,15 @@ 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.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
from .forms import VmCreateForm, TemplateForm, LeaseForm, NodeForm, HostForm from .forms import (
VmCreateForm, TemplateForm, LeaseForm, NodeForm, HostForm, DiskAddForm,
)
from .tables import (VmListTable, NodeListTable, NodeVmListTable, from .tables import (VmListTable, NodeListTable, NodeVmListTable,
TemplateListTable, LeaseListTable, GroupListTable) TemplateListTable, LeaseListTable, GroupListTable)
from vm.models import (Instance, InstanceTemplate, InterfaceTemplate, from vm.models import (Instance, InstanceTemplate, InterfaceTemplate,
...@@ -150,6 +153,9 @@ class VmDetailView(CheckedDetailView): ...@@ -150,6 +153,9 @@ class VmDetailView(CheckedDetailView):
context['vlans'] = Vlan.get_objects_with_level( context['vlans'] = Vlan.get_objects_with_level(
'user', self.request.user).all() 'user', self.request.user).all()
context['acl'] = get_acl_data(instance) context['acl'] = get_acl_data(instance)
context['forms'] = {
'disk_add_form': DiskAddForm(prefix="disk"),
}
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
...@@ -165,6 +171,7 @@ class VmDetailView(CheckedDetailView): ...@@ -165,6 +171,7 @@ 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,
} }
for k, v in options.iteritems(): for k, v in options.iteritems():
...@@ -333,6 +340,24 @@ class VmDetailView(CheckedDetailView): ...@@ -333,6 +340,24 @@ 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}))
class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView): class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
template_name = "dashboard/node-detail.html" template_name = "dashboard/node-detail.html"
......
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