Commit eaea5499 by Őry Máté

Merge branch 'feature-agent-renew' into 'master'

Feature Agent Renew

* send notification to agent about expiration
* provide machine-readable response for renew
parents 0dba3a9f 735f6921
......@@ -908,7 +908,8 @@ class VmRenewForm(forms.Form):
self.fields['lease'] = forms.ModelChoiceField(queryset=choices,
initial=default,
required=True,
required=False,
empty_label=None,
label=_('Length'))
if len(choices) < 2:
self.fields['lease'].widget = HiddenInput()
......
......@@ -280,6 +280,7 @@ class RenewViewTest(unittest.TestCase):
view = vm_ops['renew']
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"
......@@ -288,7 +289,10 @@ class RenewViewTest(unittest.TestCase):
inst.has_level.return_value = True
go.return_value = inst
go4.return_value = MagicMock()
assert view.as_view()(request, pk=1234).render().status_code == 200
assert view.as_view()(request, pk=1234)
assert not msg.error.called
assert inst.renew.async.called_with(user=request.user, lease=None)
assert inst.renew.async.return_value.get.called
# success would redirect
def test_renew_by_owner_w_param(self):
......
......@@ -52,6 +52,7 @@ from django_tables2 import SingleTableView
from braces.views import (LoginRequiredMixin, SuperuserRequiredMixin,
PermissionRequiredMixin)
from braces.views._access import AccessMixin
from celery.exceptions import TimeoutError
from django_sshkey.models import UserKey
......@@ -69,6 +70,7 @@ from .tables import (
NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable,
GroupListTable, UserKeyListTable
)
from common.models import HumanReadableObject
from vm.models import (
Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface,
InterfaceTemplate, Lease, Node, NodeActivity, Trait,
......@@ -486,6 +488,7 @@ class OperationView(RedirectToLoginMixin, DetailView):
template_name = 'dashboard/operate.html'
show_in_toolbar = True
effect = None
wait_for_result = None
@property
def name(self):
......@@ -547,19 +550,57 @@ class OperationView(RedirectToLoginMixin, DetailView):
self.check_auth()
return super(OperationView, self).get(request, *args, **kwargs)
def get_response_data(self, result, done, extra=None, **kwargs):
"""Return serializable data to return to agents requesting json
response to POST"""
if extra is None:
extra = {}
extra["success"] = not isinstance(result, Exception)
extra["done"] = done
if isinstance(result, HumanReadableObject):
extra["message"] = result.get_user_text()
return extra
def post(self, request, extra=None, *args, **kwargs):
self.check_auth()
self.object = self.get_object()
if extra is None:
extra = {}
result = None
done = False
try:
self.get_op().async(user=request.user, **extra)
task = self.get_op().async(user=request.user, **extra)
except Exception as e:
messages.error(request, _('Could not start operation.'))
logger.exception(e)
result = e
else:
messages.success(request, _('Operation is started.'))
return redirect("%s#activity" % self.object.get_absolute_url())
wait = self.wait_for_result
if wait:
try:
result = task.get(timeout=wait,
interval=min((wait / 5, .5)))
except TimeoutError:
logger.debug("Result didn't arrive in %ss",
self.wait_for_result, exc_info=True)
except Exception as e:
messages.error(request, _('Operation failed.'))
logger.debug("Operation failed.", exc_info=True)
result = e
else:
done = True
messages.success(request, _('Operation succeeded.'))
if result is None and not done:
messages.success(request, _('Operation is started.'))
if "/json" in request.META.get("HTTP_ACCEPT", ""):
data = self.get_response_data(result, done,
post_extra=extra, **kwargs)
return HttpResponse(json.dumps(data),
content_type="application/json")
else:
return redirect("%s#activity" % self.object.get_absolute_url())
@classmethod
def factory(cls, op, icon='cog', effect='info', extra_bases=(), **kwargs):
......@@ -817,6 +858,7 @@ class VmRenewView(FormOperationMixin, TokenOperationView, VmOperationView):
effect = 'info'
show_in_toolbar = False
form_class = VmRenewForm
wait_for_result = 0.5
def get_form_kwargs(self):
choices = Lease.get_objects_with_level("user", self.request.user)
......@@ -829,6 +871,13 @@ class VmRenewView(FormOperationMixin, TokenOperationView, VmOperationView):
val.update({'choices': choices, 'default': default})
return val
def get_response_data(self, result, done, extra=None, **kwargs):
extra = super(VmRenewView, self).get_response_data(result, done,
extra, **kwargs)
extra["new_suspend_time"] = unicode(self.get_op().
instance.time_of_suspend)
return extra
vm_ops = OrderedDict([
('deploy', VmOperationView.factory(
......
......@@ -687,6 +687,11 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
failed.append((u, e))
else:
success.append(u)
if self.status == "RUNNING":
token = VmRenewView.get_token_url(self, self.owner)
queue = self.get_remote_queue_name("agent")
agent_tasks.send_expiration.apply_async(
queue=queue, args=(self.vm_name, token))
return True
def is_expiring(self, threshold=0.1):
......
......@@ -71,3 +71,8 @@ def del_keys(vm, keys):
@celery.task(name='agent.get_keys')
def get_keys(vm):
pass
@celery.task(name='agent.send_expiration')
def send_expiration(vm, url):
pass
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