Commit 2353aacc by Bach Dániel

Merge branch 'feature-operation-form'

Add generic way of asking operations' parameters.

Conflicts:
	circle/dashboard/views.py
parents 02832e16 be0eafdf
...@@ -47,6 +47,11 @@ from vm.models import ( ...@@ -47,6 +47,11 @@ from vm.models import (
from .models import Profile, GroupProfile from .models import Profile, GroupProfile
class VmSaveForm(forms.Form):
name = forms.CharField(max_length=100, label=_('Name'),
help_text=_('Human readable name of template.'))
class VmCustomizeForm(forms.Form): class VmCustomizeForm(forms.Form):
name = forms.CharField() name = forms.CharField()
cpu_priority = forms.IntegerField() cpu_priority = forms.IntegerField()
......
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %}
{% block question %} {% block question %}
<p> <p>
...@@ -10,7 +11,11 @@ Do you want to do the following operation on {{obj}}: ...@@ -10,7 +11,11 @@ Do you want to do the following operation on {{obj}}:
<p class="text-info">{{op.name}}: {{op.description}}</p> <p class="text-info">{{op.name}}: {{op.description}}</p>
{% endblock %} {% endblock %}
<form method="POST" action="{{url}}">{% csrf_token %} <form method="POST" action="{{url}}">{% csrf_token %}
{% block formfields %}{% endblock %} {% block formfields %}
{% if form %}
{% crispy form %}
{% endif %}
{% endblock %}
<div class="pull-right"> <div class="pull-right">
<a class="btn btn-default" href="{{object.get_absolute_url}}" <a class="btn btn-default" href="{{object.get_absolute_url}}"
data-dismiss="modal">{% trans "Cancel" %}</a> data-dismiss="modal">{% trans "Cancel" %}</a>
......
...@@ -203,7 +203,7 @@ class VmOperationViewTestCase(unittest.TestCase): ...@@ -203,7 +203,7 @@ class VmOperationViewTestCase(unittest.TestCase):
inst.has_level.return_value = True inst.has_level.return_value = True
go.return_value = inst go.return_value = inst
go4.return_value = MagicMock() go4.return_value = MagicMock()
assert view.as_view()(request, pk=1234)['location'] assert view.as_view()(request, pk=1234)
assert not msg.error.called assert not msg.error.called
def test_save_as_w_name(self): def test_save_as_w_name(self):
...@@ -223,24 +223,6 @@ class VmOperationViewTestCase(unittest.TestCase): ...@@ -223,24 +223,6 @@ class VmOperationViewTestCase(unittest.TestCase):
assert view.as_view()(request, pk=1234)['location'] assert view.as_view()(request, pk=1234)['location']
assert not msg.error.called assert not msg.error.called
def test_save_as_failed(self):
request = FakeRequestFactory(POST={})
view = vm_ops['save_as_template']
with patch.object(view, 'get_object') as go, \
patch('dashboard.views.messages') as msg, \
patch('dashboard.views.get_object_or_404') as go4:
inst = MagicMock(spec=Instance)
inst._meta.object_name = "Instance"
inst.save_as_template = Instance._ops['save_as_template'](inst)
inst.save_as_template.async = MagicMock()
inst.save_as_template.async.side_effect = Exception
inst.has_level.return_value = True
go.return_value = inst
go4.return_value = MagicMock()
assert view.as_view()(request, pk=1234)['location']
assert msg.error.called
def test_save_as_template(self): def test_save_as_template(self):
request = FakeRequestFactory() request = FakeRequestFactory()
view = vm_ops['save_as_template'] view = vm_ops['save_as_template']
...@@ -254,7 +236,6 @@ class VmOperationViewTestCase(unittest.TestCase): ...@@ -254,7 +236,6 @@ class VmOperationViewTestCase(unittest.TestCase):
go.return_value = inst go.return_value = inst
rend = view.as_view()(request, pk=1234).render() rend = view.as_view()(request, pk=1234).render()
self.assertEquals(rend.status_code, 200) self.assertEquals(rend.status_code, 200)
assert 'foo v1' in rend.content
def FakeRequestFactory(*args, **kwargs): def FakeRequestFactory(*args, **kwargs):
......
...@@ -60,7 +60,7 @@ from .forms import ( ...@@ -60,7 +60,7 @@ from .forms import (
CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, MyProfileForm, CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, MyProfileForm,
NodeForm, TemplateForm, TraitForm, VmCustomizeForm, GroupCreateForm, NodeForm, TemplateForm, TraitForm, VmCustomizeForm, GroupCreateForm,
UserCreationForm, GroupProfileUpdateForm, UnsubscribeForm, UserCreationForm, GroupProfileUpdateForm, UnsubscribeForm,
CirclePasswordChangeForm CirclePasswordChangeForm, VmSaveForm,
) )
from .tables import ( from .tables import (
...@@ -258,11 +258,13 @@ class VmDetailView(CheckedDetailView): ...@@ -258,11 +258,13 @@ class VmDetailView(CheckedDetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(VmDetailView, self).get_context_data(**kwargs) context = super(VmDetailView, self).get_context_data(**kwargs)
instance = context['instance'] instance = context['instance']
ops = get_operations(instance, self.request.user)
context.update({ context.update({
'graphite_enabled': VmGraphView.get_graphite_url() is not None, 'graphite_enabled': VmGraphView.get_graphite_url() is not None,
'vnc_url': reverse_lazy("dashboard.views.detail-vnc", 'vnc_url': reverse_lazy("dashboard.views.detail-vnc",
kwargs={'pk': self.object.pk}), kwargs={'pk': self.object.pk}),
'ops': get_operations(instance, self.request.user), 'ops': ops,
'op': {i.op: i for i in ops},
}) })
# activity data # activity data
...@@ -501,6 +503,7 @@ class VmDetailView(CheckedDetailView): ...@@ -501,6 +503,7 @@ class VmDetailView(CheckedDetailView):
class OperationView(DetailView): class OperationView(DetailView):
template_name = 'dashboard/operate.html' template_name = 'dashboard/operate.html'
show_in_toolbar = True
@property @property
def name(self): def name(self):
...@@ -575,6 +578,30 @@ class VmOperationView(OperationView): ...@@ -575,6 +578,30 @@ class VmOperationView(OperationView):
context_object_name = 'instance' # much simpler to mock object context_object_name = 'instance' # much simpler to mock object
class FormOperationMixin(object):
form_class = None
def get_context_data(self, **kwargs):
ctx = super(FormOperationMixin, self).get_context_data(**kwargs)
if self.request.method == 'POST':
ctx['form'] = self.form_class(self.request.POST)
else:
ctx['form'] = self.form_class()
return ctx
def post(self, request, extra=None, *args, **kwargs):
if extra is None:
extra = {}
form = self.form_class(self.request.POST)
if form.is_valid():
extra.update(form.cleaned_data)
return super(FormOperationMixin, self).post(
request, extra, *args, **kwargs)
else:
return self.get(request)
class VmMigrateView(VmOperationView): class VmMigrateView(VmOperationView):
op = 'migrate' op = 'migrate'
...@@ -597,25 +624,11 @@ class VmMigrateView(VmOperationView): ...@@ -597,25 +624,11 @@ class VmMigrateView(VmOperationView):
return super(VmMigrateView, self).post(request, extra, *args, **kwargs) return super(VmMigrateView, self).post(request, extra, *args, **kwargs)
class VmSaveView(VmOperationView): class VmSaveView(FormOperationMixin, VmOperationView):
op = 'save_as_template' op = 'save_as_template'
icon = 'save' icon = 'save'
template_name = 'dashboard/_vm-save.html' form_class = VmSaveForm
def get_context_data(self, **kwargs):
ctx = super(VmSaveView, self).get_context_data(**kwargs)
ctx['name'] = self.get_op()._rename(self.object.name)
return ctx
def post(self, request, extra=None, *args, **kwargs):
if extra is None:
extra = {}
name = self.request.POST.get("name")
if name:
extra["name"] = name
return super(VmSaveView, self).post(request, extra, *args, **kwargs)
vm_ops = { vm_ops = {
'reset': VmOperationView.factory(op='reset', icon='bolt'), 'reset': VmOperationView.factory(op='reset', icon='bolt'),
...@@ -638,8 +651,9 @@ def get_operations(instance, user): ...@@ -638,8 +651,9 @@ def get_operations(instance, user):
op = v.get_op_by_object(instance) op = v.get_op_by_object(instance)
op.check_auth(user) op.check_auth(user)
op.check_precond() op.check_precond()
except: except Exception as e:
pass # unavailable logger.debug('Not showing operation %s for %s: %s',
k, instance, unicode(e))
else: else:
ops.append(v.bind_to_object(instance)) ops.append(v.bind_to_object(instance))
return ops return ops
...@@ -2628,7 +2642,7 @@ class UnsubscribeFormView(SuccessMessageMixin, UpdateView): ...@@ -2628,7 +2642,7 @@ class UnsubscribeFormView(SuccessMessageMixin, UpdateView):
def get_object(self, queryset=None): def get_object(self, queryset=None):
key = self.kwargs['token'] key = self.kwargs['token']
try: try:
pk = signing.loads(key, salt=self.get_salt(), max_age=48*3600) pk = signing.loads(key, salt=self.get_salt(), max_age=48 * 3600)
except signing.SignatureExpired: except signing.SignatureExpired:
raise raise
except (signing.BadSignature, ValueError, TypeError) as e: except (signing.BadSignature, ValueError, TypeError) as e:
......
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