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 (
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):
name = forms.CharField()
cpu_priority = forms.IntegerField()
......
{% load i18n %}
{% load crispy_forms_tags %}
{% block question %}
<p>
......@@ -10,7 +11,11 @@ Do you want to do the following operation on {{obj}}:
<p class="text-info">{{op.name}}: {{op.description}}</p>
{% endblock %}
<form method="POST" action="{{url}}">{% csrf_token %}
{% block formfields %}{% endblock %}
{% block formfields %}
{% if form %}
{% crispy form %}
{% endif %}
{% endblock %}
<div class="pull-right">
<a class="btn btn-default" href="{{object.get_absolute_url}}"
data-dismiss="modal">{% trans "Cancel" %}</a>
......
......@@ -203,7 +203,7 @@ class VmOperationViewTestCase(unittest.TestCase):
inst.has_level.return_value = True
go.return_value = inst
go4.return_value = MagicMock()
assert view.as_view()(request, pk=1234)['location']
assert view.as_view()(request, pk=1234)
assert not msg.error.called
def test_save_as_w_name(self):
......@@ -223,24 +223,6 @@ class VmOperationViewTestCase(unittest.TestCase):
assert view.as_view()(request, pk=1234)['location']
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):
request = FakeRequestFactory()
view = vm_ops['save_as_template']
......@@ -254,7 +236,6 @@ class VmOperationViewTestCase(unittest.TestCase):
go.return_value = inst
rend = view.as_view()(request, pk=1234).render()
self.assertEquals(rend.status_code, 200)
assert 'foo v1' in rend.content
def FakeRequestFactory(*args, **kwargs):
......
......@@ -60,7 +60,7 @@ from .forms import (
CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, MyProfileForm,
NodeForm, TemplateForm, TraitForm, VmCustomizeForm, GroupCreateForm,
UserCreationForm, GroupProfileUpdateForm, UnsubscribeForm,
CirclePasswordChangeForm
CirclePasswordChangeForm, VmSaveForm,
)
from .tables import (
......@@ -258,11 +258,13 @@ class VmDetailView(CheckedDetailView):
def get_context_data(self, **kwargs):
context = super(VmDetailView, self).get_context_data(**kwargs)
instance = context['instance']
ops = get_operations(instance, self.request.user)
context.update({
'graphite_enabled': VmGraphView.get_graphite_url() is not None,
'vnc_url': reverse_lazy("dashboard.views.detail-vnc",
kwargs={'pk': self.object.pk}),
'ops': get_operations(instance, self.request.user),
'ops': ops,
'op': {i.op: i for i in ops},
})
# activity data
......@@ -501,6 +503,7 @@ class VmDetailView(CheckedDetailView):
class OperationView(DetailView):
template_name = 'dashboard/operate.html'
show_in_toolbar = True
@property
def name(self):
......@@ -575,6 +578,30 @@ class VmOperationView(OperationView):
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):
op = 'migrate'
......@@ -597,25 +624,11 @@ class VmMigrateView(VmOperationView):
return super(VmMigrateView, self).post(request, extra, *args, **kwargs)
class VmSaveView(VmOperationView):
class VmSaveView(FormOperationMixin, VmOperationView):
op = 'save_as_template'
icon = 'save'
template_name = 'dashboard/_vm-save.html'
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)
form_class = VmSaveForm
vm_ops = {
'reset': VmOperationView.factory(op='reset', icon='bolt'),
......@@ -638,8 +651,9 @@ def get_operations(instance, user):
op = v.get_op_by_object(instance)
op.check_auth(user)
op.check_precond()
except:
pass # unavailable
except Exception as e:
logger.debug('Not showing operation %s for %s: %s',
k, instance, unicode(e))
else:
ops.append(v.bind_to_object(instance))
return ops
......@@ -2628,7 +2642,7 @@ class UnsubscribeFormView(SuccessMessageMixin, UpdateView):
def get_object(self, queryset=None):
key = self.kwargs['token']
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:
raise
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