Commit 46557987 authored by Stevan Radakovic's avatar Stevan Radakovic
Browse files

Add cancel command for TestJob viewset in REST API.

Also refactor the cancel code a bit to be shared across the
multiple APIs.
parent d8bf532e
...@@ -170,6 +170,12 @@ class TestSuiteViewSet(NestedViewSetMixin, viewsets.ReadOnlyModelViewSet): ...@@ -170,6 +170,12 @@ class TestSuiteViewSet(NestedViewSetMixin, viewsets.ReadOnlyModelViewSet):
) )
return response return response
@detail_route(methods=["get"], suffix="cancel")
def cancel(self, request, **kwargs):
# django-rest-framework handles django's PermissionDenied error
# automagically
self.get_object().cancel(request.user)
class TestCaseViewSet(NestedViewSetMixin, viewsets.ReadOnlyModelViewSet): class TestCaseViewSet(NestedViewSetMixin, viewsets.ReadOnlyModelViewSet):
queryset = TestCase.objects queryset = TestCase.objects
......
...@@ -197,22 +197,10 @@ class SchedulerAPI(ExposedAPI): ...@@ -197,22 +197,10 @@ class SchedulerAPI(ExposedAPI):
except TestJob.DoesNotExist: except TestJob.DoesNotExist:
raise xmlrpc.client.Fault(404, "Specified job not found.") raise xmlrpc.client.Fault(404, "Specified job not found.")
if job.state in [TestJob.STATE_CANCELING, TestJob.STATE_FINISHED]: try:
# Don't do anything for jobs that ended already job.cancel(self.user)
return True except PermissionDenied:
if not job.can_cancel(self.user):
raise xmlrpc.client.Fault(403, "Permission denied.") raise xmlrpc.client.Fault(403, "Permission denied.")
if job.is_multinode:
multinode_jobs = TestJob.objects.select_for_update().filter(
target_group=job.target_group
)
for multinode_job in multinode_jobs:
multinode_job.go_state_canceling()
multinode_job.save()
else:
job.go_state_canceling()
job.save()
return True return True
def validate_yaml(self, yaml_string): def validate_yaml(self, yaml_string):
......
...@@ -29,6 +29,7 @@ import gzip ...@@ -29,6 +29,7 @@ import gzip
import simplejson import simplejson
import yaml import yaml
from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User, Group, Permission from django.contrib.auth.models import User, Group, Permission
...@@ -36,7 +37,11 @@ from django.contrib.contenttypes.models import ContentType ...@@ -36,7 +37,11 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE from django.contrib.admin.models import LogEntry, ADDITION, CHANGE
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.exceptions import (
ImproperlyConfigured,
PermissionDenied,
ValidationError,
)
from django.urls import reverse from django.urls import reverse
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
...@@ -2016,6 +2021,24 @@ class TestJob(models.Model): ...@@ -2016,6 +2021,24 @@ class TestJob(models.Model):
retval.append({attribute.name: attribute.value}) retval.append({attribute.name: attribute.value})
return retval return retval
@transaction.atomic
def cancel(self, user):
if not self.can_cancel(user):
raise PermissionDenied("Insufficient permissions")
if self.state in [TestJob.STATE_CANCELING, TestJob.STATE_FINISHED]:
# Don't do anything for jobs that ended already
return
if self.is_multinode:
multinode_jobs = TestJob.objects.select_for_update().filter(
target_group=self.target_group
)
for multinode_job in multinode_jobs:
multinode_job.go_state_canceling()
multinode_job.save()
else:
self.go_state_canceling()
self.save()
class Notification(models.Model): class Notification(models.Model):
......
...@@ -1705,25 +1705,16 @@ def job_log_incremental(request, pk): ...@@ -1705,25 +1705,16 @@ def job_log_incremental(request, pk):
return response return response
@transaction.atomic
def job_cancel(request, pk): def job_cancel(request, pk):
with transaction.atomic(): job = get_restricted_job(request.user, pk, request=request, for_update=True)
job = get_restricted_job(request.user, pk, request=request, for_update=True) try:
if job.can_cancel(request.user): job.cancel(request.user)
if job.is_multinode: return redirect(job)
multinode_jobs = TestJob.objects.select_for_update().filter( except PermissionDenied:
target_group=job.target_group return HttpResponseForbidden(
) "you cannot cancel this job", content_type="text/plain"
for multinode_job in multinode_jobs: )
multinode_job.go_state_canceling()
multinode_job.save()
else:
job.go_state_canceling()
job.save()
return redirect(job)
else:
return HttpResponseForbidden(
"you cannot cancel this job", content_type="text/plain"
)
def job_fail(request, pk): def job_fail(request, pk):
......
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 to comment