From 0b053edee9793cef21c62b3ccb379eb304a22ccd Mon Sep 17 00:00:00 2001 From: Maciej Tronowski Date: Tue, 28 Apr 2015 11:02:02 +0200 Subject: [PATCH] canceling jobs and tasks --- qcg/models.py | 8 ++++++++ qcg/service.py | 18 +++++++++++++++++- qcg/templates/qcg/job.html | 9 +++++++++ qcg/templates/qcg/task.html | 9 +++++++++ qcg/urls.py | 3 +++ qcg/views.py | 29 ++++++++++++++++++++++++++++- 6 files changed, 74 insertions(+), 2 deletions(-) diff --git a/qcg/models.py b/qcg/models.py index f452874..fa5065b 100644 --- a/qcg/models.py +++ b/qcg/models.py @@ -78,6 +78,10 @@ class Job(models.Model): return attrs + @property + def terminated(self): + return self.get_status_display() in [JobStatus.FINISHED, JobStatus.FAILED, JobStatus.CANCELED] + class Task(models.Model): STATUS_CHOICES = list(enumerate(field for field in dir(TaskStatus) if not field.startswith('__'))) @@ -159,6 +163,10 @@ class Task(models.Model): def short_host_names(self): return {alloc.host_name.split('.', 1)[0] for alloc in self.allocations.all()} + @property + def terminated(self): + return self.get_status_display() in [TaskStatus.FINISHED, TaskStatus.FAILED, TaskStatus.CANCELED] + class Allocation(models.Model): STATUS_CHOICES = list(enumerate(field for field in dir(AllocationType) if not field.startswith('__'))) diff --git a/qcg/service.py b/qcg/service.py index f950aac..c87f40a 100644 --- a/qcg/service.py +++ b/qcg/service.py @@ -85,7 +85,7 @@ def update_user_data(user, proxy): @transaction.atomic def update_job(job, proxy): - if job.get_status_display() in [JobStatus.FINISHED, JobStatus.FAILED, JobStatus.CANCELED]: + if job.terminated: return ts = time.time() @@ -164,3 +164,19 @@ def submit_job(params, proxy): job = desc.submit() return job.job_id + + +def cancel(obj, proxy): + ts = time.time() + QCG.start() + + qcg_obj = obj.qcg_job if isinstance(obj, Job) else obj.qcg_task + qcg_obj.credential = Credential(proxy) + + jts = time.time() + qcg_obj.cancel() + elapsed_cancel = time.time() - jts + + elapsed = time.time() - ts + elapsed_py = elapsed - elapsed_cancel + logger.info('(%.3f) OBJ = %s (%.3f), TIME = %.3f', elapsed, obj, elapsed_cancel, elapsed_py) diff --git a/qcg/templates/qcg/job.html b/qcg/templates/qcg/job.html index 114a329..0189ef2 100644 --- a/qcg/templates/qcg/job.html +++ b/qcg/templates/qcg/job.html @@ -7,6 +7,15 @@
  • {{ job.job_id }}
  • +
    + {% if not job.terminated %} +
    + {% csrf_token %} + +
    + {% endif %} +
    +

    {% block title %}Job {{ job.job_id }}{% endblock %}

    diff --git a/qcg/templates/qcg/task.html b/qcg/templates/qcg/task.html index 396b29e..8615cb8 100644 --- a/qcg/templates/qcg/task.html +++ b/qcg/templates/qcg/task.html @@ -24,6 +24,15 @@
  • {{ task.task_id }}
  • +
    + {% if not task.terminated %} +
    + {% csrf_token %} + +
    + {% endif %} +
    +

    {% block title %}Task {{ task.task_id }}{% endblock %}

    diff --git a/qcg/urls.py b/qcg/urls.py index 798950b..1353522 100644 --- a/qcg/urls.py +++ b/qcg/urls.py @@ -14,6 +14,9 @@ urlpatterns = patterns('', url(r'^$', views.index, name='index'), url(r'^jobs/$', views.jobs_list, name='jobs'), url(r'^job/submit/$', views.job_submit, name='job_submit'), + url(r'^job/cancel/(?P[\w]+)/$', views.job_cancel, name='job_cancel'), + url(r'^task/cancel(?P[\w]+)/(?P[\w]+)/$', views.task_cancel, name='task_cancel'), + url(r'^job/(?P[\w]+)/?$', views.job_details, name='job'), url(r'^job/(?P[\w]+)/(?P[\w]+)/?$', views.task_details, name='task'), diff --git a/qcg/views.py b/qcg/views.py index 8905339..89ddfa4 100644 --- a/qcg/views.py +++ b/qcg/views.py @@ -13,15 +13,17 @@ from django.shortcuts import render, get_object_or_404, redirect from django.utils.html import format_html from django.utils.http import urlencode from django.utils.timezone import UTC +from django.views.decorators.http import require_POST from django_openid_auth.views import make_consumer from openid.extensions import ax +from pyqcg.utils.qcg_types import PyqcgException from filex.forms import HostPathNameForm, RenameForm, ArchiveForm, HostPathForm from filex.ftp import FTPOperation, FTPError from filex.views import make_url from qcg.forms import FiltersForm, ColumnsForm, JobDescriptionForm, EnvFormSet from qcg.utils import paginator_context -from qcg.service import update_user_data, submit_job, update_job +from qcg.service import update_user_data, submit_job, update_job, cancel def index(request): @@ -188,6 +190,31 @@ def job_submit(request): return render(request, 'qcg/job_submit.html', {'form': form, 'env_formset': env_formset, 'errors': errors}) +@require_POST +@login_required +def job_cancel(request, job_id): + return obj_cancel(request, get_object_or_404(request.user.jobs, job_id=job_id)) + + +@require_POST +@login_required +def task_cancel(request, job_id, task_id): + return obj_cancel(request, get_object_or_404(request.user.tasks, job__job_id=job_id, task_id=task_id)) + + +def obj_cancel(request, obj): + try: + cancel(obj, request.session['proxy']) + except PyqcgException as e: + messages.error(request, format_html(' {}', + e.message)) + else: + messages.success(request, format_html(' ' + 'Zadanie anulowano.')) + + return redirect(obj) + + @login_required def gridftp(request): return render(request, 'qcg/gridftp.html', -- 1.7.9.5