Commit 4565cf03 by Czémán Arnold

Merge branch 'issue_392' into 'master'

Rename operation

See merge request !389
parents 0623def3 e0f32040
...@@ -1538,6 +1538,10 @@ class VmResourcesForm(forms.ModelForm): ...@@ -1538,6 +1538,10 @@ class VmResourcesForm(forms.ModelForm):
fields = ('num_cores', 'priority', 'ram_size', ) fields = ('num_cores', 'priority', 'ram_size', )
class VmRenameForm(forms.Form):
new_name = forms.CharField()
vm_search_choices = ( vm_search_choices = (
("owned", _("owned")), ("owned", _("owned")),
("shared", _("shared")), ("shared", _("shared")),
......
...@@ -111,9 +111,10 @@ $(function() { ...@@ -111,9 +111,10 @@ $(function() {
/* rename ajax */ /* rename ajax */
$('.vm-details-rename-submit').click(function() { $('.vm-details-rename-submit').click(function() {
var name = $(this).parent("span").prev("input").val(); var name = $(this).parent("span").prev("input").val();
var url = $("#vm-details-rename-form").attr("action");
$.ajax({ $.ajax({
method: 'POST', method: 'POST',
url: location.href, url: url,
data: {'new_name': name}, data: {'new_name': name},
headers: {"X-CSRFToken": getCookie('csrftoken')}, headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(data, textStatus, xhr) { success: function(data, textStatus, xhr) {
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
</div> </div>
<h1> <h1>
<div id="vm-details-rename" class="vm-details-home-rename-form-div"> <div id="vm-details-rename" class="vm-details-home-rename-form-div">
<form action="" method="POST" id="vm-details-rename-form"> <form action="{{ op.rename.get_url }}" method="POST" id="vm-details-rename-form">
{% csrf_token %} {% csrf_token %}
<div class="input-group vm-details-home-name"> <div class="input-group vm-details-home-name">
<input id="vm-details-rename-name" class="form-control input-sm" name="new_name" type="text" value="{{ instance.name }}"/> <input id="vm-details-rename-name" class="form-control input-sm" name="new_name" type="text" value="{{ instance.name }}"/>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<small class="vm-details-home-edit-name">{{ instance.name }}</small> <small class="vm-details-home-edit-name">{{ instance.name }}</small>
</div> </div>
<div class="js-hidden vm-details-home-rename-form-div" id="vm-details-home-rename"> <div class="js-hidden vm-details-home-rename-form-div" id="vm-details-home-rename">
<form method="POST"> <form action="{{ op.rename.get_url }}" method="POST">
{% csrf_token %} {% csrf_token %}
<div class="input-group"> <div class="input-group">
<input type="text" name="new_name" value="{{ instance.name }}" class="form-control input-sm"/> <input type="text" name="new_name" value="{{ instance.name }}" class="form-control input-sm"/>
......
...@@ -30,7 +30,7 @@ from dashboard.views import VmAddInterfaceView ...@@ -30,7 +30,7 @@ from dashboard.views import VmAddInterfaceView
from vm.models import Instance, InstanceTemplate, Lease, Node, Trait from vm.models import Instance, InstanceTemplate, Lease, Node, Trait
from vm.operations import (WakeUpOperation, AddInterfaceOperation, from vm.operations import (WakeUpOperation, AddInterfaceOperation,
AddPortOperation, RemoveInterfaceOperation, AddPortOperation, RemoveInterfaceOperation,
DeployOperation) DeployOperation, RenameOperation)
from ..models import Profile from ..models import Profile
from firewall.models import Vlan, Host, VlanGroup from firewall.models import Vlan, Host, VlanGroup
from mock import Mock, patch from mock import Mock, patch
...@@ -437,30 +437,42 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase): ...@@ -437,30 +437,42 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase):
def test_unpermitted_set_name(self): def test_unpermitted_set_name(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(RenameOperation, 'async') as mock_method:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
mock_method.side_effect = inst.rename
inst.set_level(self.u2, 'user') inst.set_level(self.u2, 'user')
old_name = inst.name old_name = inst.name
response = c.post("/dashboard/vm/1/", {'new_name': 'test1235'}) response = c.post("/dashboard/vm/1/op/rename/",
{'new_name': 'test1235'})
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
assert not mock_method.called
self.assertEqual(Instance.objects.get(pk=1).name, old_name) self.assertEqual(Instance.objects.get(pk=1).name, old_name)
def test_permitted_set_name(self): def test_permitted_set_name(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(RenameOperation, 'async') as mock_method:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
mock_method.side_effect = inst.rename
inst.set_level(self.u2, 'owner') inst.set_level(self.u2, 'owner')
response = c.post("/dashboard/vm/1/", {'new_name': 'test1234'}) response = c.post("/dashboard/vm/1/op/rename/",
{'new_name': 'test1234'})
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
assert mock_method.called
self.assertEqual(Instance.objects.get(pk=1).name, 'test1234') self.assertEqual(Instance.objects.get(pk=1).name, 'test1234')
def test_permitted_set_name_w_ajax(self): def test_permitted_set_name_w_ajax(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
with patch.object(RenameOperation, 'async') as mock_method:
inst.set_level(self.u2, 'owner') inst.set_level(self.u2, 'owner')
response = c.post("/dashboard/vm/1/", {'new_name': 'test123'}, mock_method.side_effect = inst.rename
response = c.post("/dashboard/vm/1/op/rename/",
{'new_name': 'test123'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest') HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
assert mock_method.called
self.assertEqual(Instance.objects.get(pk=1).name, 'test123') self.assertEqual(Instance.objects.get(pk=1).name, 'test123')
def test_permitted_wake_up_wrong_state(self): def test_permitted_wake_up_wrong_state(self):
......
...@@ -68,6 +68,7 @@ from ..forms import ( ...@@ -68,6 +68,7 @@ from ..forms import (
VmMigrateForm, VmDeployForm, VmMigrateForm, VmDeployForm,
VmPortRemoveForm, VmPortAddForm, VmPortRemoveForm, VmPortAddForm,
VmRemoveInterfaceForm, VmRemoveInterfaceForm,
VmRenameForm,
) )
from request.models import TemplateAccessType, LeaseType from request.models import TemplateAccessType, LeaseType
from request.forms import LeaseRequestForm, TemplateRequestForm from request.forms import LeaseRequestForm, TemplateRequestForm
...@@ -199,7 +200,6 @@ class VmDetailView(GraphMixin, CheckedDetailView): ...@@ -199,7 +200,6 @@ class VmDetailView(GraphMixin, CheckedDetailView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
options = { options = {
'new_name': self.__set_name,
'new_description': self.__set_description, 'new_description': self.__set_description,
'new_tag': self.__add_tag, 'new_tag': self.__add_tag,
'to_remove': self.__remove_tag, 'to_remove': self.__remove_tag,
...@@ -210,29 +210,6 @@ class VmDetailView(GraphMixin, CheckedDetailView): ...@@ -210,29 +210,6 @@ class VmDetailView(GraphMixin, CheckedDetailView):
return v(request) return v(request)
raise Http404() raise Http404()
def __set_name(self, request):
self.object = self.get_object()
if not self.object.has_level(request.user, "operator"):
raise PermissionDenied()
new_name = request.POST.get("new_name")
Instance.objects.filter(pk=self.object.pk).update(
**{'name': new_name})
success_message = _("VM successfully renamed.")
if request.is_ajax():
response = {
'message': success_message,
'new_name': new_name,
'vm_pk': self.object.pk
}
return HttpResponse(
json.dumps(response),
content_type="application/json"
)
else:
messages.success(request, success_message)
return redirect(self.object.get_absolute_url())
def __set_description(self, request): def __set_description(self, request):
self.object = self.get_object() self.object = self.get_object()
if not self.object.has_level(request.user, "operator"): if not self.object.has_level(request.user, "operator"):
...@@ -743,6 +720,31 @@ class VmDeployView(FormOperationMixin, VmOperationView): ...@@ -743,6 +720,31 @@ class VmDeployView(FormOperationMixin, VmOperationView):
return kwargs return kwargs
class VmRenameView(FormOperationMixin, VmOperationView):
op = 'rename'
icon = 'pencil'
effect = 'success'
show_in_toolbar = False
form_class = VmRenameForm
def post(self, request, extra=None, *args, **kwargs):
if extra is None:
extra = {}
form = self.form_class(self.request.POST, **self.get_form_kwargs())
if form.is_valid():
extra.update(form.cleaned_data)
resp = super(FormOperationMixin, self).post(
request, extra, *args, **kwargs)
success_message = _('VM successfully renamed.')
if request.is_ajax():
return JsonResponse({'new_name': extra['new_name']})
else:
messages.success(request, success_message)
return resp
else:
return self.get(request)
vm_ops = OrderedDict([ vm_ops = OrderedDict([
('deploy', VmDeployView), ('deploy', VmDeployView),
('wake_up', VmOperationView.factory( ('wake_up', VmOperationView.factory(
...@@ -792,6 +794,7 @@ vm_ops = OrderedDict([ ...@@ -792,6 +794,7 @@ vm_ops = OrderedDict([
op='install_keys', icon='key', effect='info', op='install_keys', icon='key', effect='info',
show_in_toolbar=False, show_in_toolbar=False,
)), )),
('rename', VmRenameView),
]) ])
......
...@@ -1403,6 +1403,27 @@ class ResourcesOperation(InstanceOperation): ...@@ -1403,6 +1403,27 @@ class ResourcesOperation(InstanceOperation):
@register_operation @register_operation
class RenameOperation(InstanceOperation):
id = "rename"
name = _("rename")
description = _("Change the name of virtual machine.")
acl_level = "operator"
required_perms = ()
def _operation(self, user, activity, new_name):
old_name = self.instance.name
self.instance.name = new_name
self.instance.full_clean()
self.instance.save()
return create_readable(ugettext_noop(
"Changed name from '%(old_name)s' to '%(new_name)s'."),
old_name=old_name, new_name=new_name
)
@register_operation
class PasswordResetOperation(RemoteAgentOperation): class PasswordResetOperation(RemoteAgentOperation):
id = 'password_reset' id = 'password_reset'
name = _("password reset") name = _("password reset")
......
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