Skip to content
Snippets Groups Projects
Commit ace6a579 authored by Sybren A. Stüvel's avatar Sybren A. Stüvel
Browse files

Server: allow re-queueing of individual tasks.

This is quite useful if one task failed (for example when a worker times
out) and you want to re-queue it without cancelling any currently active
tasks.
parent 6e063175
Branches
Tags
No related merge requests found
...@@ -20,6 +20,8 @@ COLOR_FOR_TASK_STATUS = { ...@@ -20,6 +20,8 @@ COLOR_FOR_TASK_STATUS = {
'completed': '#bbe151', 'completed': '#bbe151',
} }
REQUEABLE_TASK_STATES = {'completed', 'canceled', 'failed'}
@attr.s @attr.s
class TaskManager(object): class TaskManager(object):
...@@ -90,6 +92,15 @@ class TaskManager(object): ...@@ -90,6 +92,15 @@ class TaskManager(object):
return tasks return tasks
def web_set_task_status(self, task_id, new_status):
"""Web-level call to updates the task status."""
from .sdk import Task
api = pillar_api()
task = Task({'_id': task_id})
task.patch({'op': 'set-task-status',
'status': new_status}, api=api)
def setup_app(app): def setup_app(app):
from . import eve_hooks, patch from . import eve_hooks, patch
......
...@@ -12,6 +12,9 @@ from flamenco import current_flamenco, ROLES_REQUIRED_TO_VIEW_ITEMS, ROLES_REQUI ...@@ -12,6 +12,9 @@ from flamenco import current_flamenco, ROLES_REQUIRED_TO_VIEW_ITEMS, ROLES_REQUI
TASK_LOG_PAGE_SIZE = 10 TASK_LOG_PAGE_SIZE = 10
# The task statuses that can be set from the web-interface.
ALLOWED_TASK_STATUSES_FROM_WEB = {'cancel-requested', 'queued'}
perjob_blueprint = Blueprint('flamenco.tasks.perjob', __name__, perjob_blueprint = Blueprint('flamenco.tasks.perjob', __name__,
url_prefix='/<project_url>/jobs/<job_id>') url_prefix='/<project_url>/jobs/<job_id>')
perproject_blueprint = Blueprint('flamenco.tasks.perproject', __name__, perproject_blueprint = Blueprint('flamenco.tasks.perproject', __name__,
...@@ -60,11 +63,34 @@ def view_task(project, flamenco_props, task_id): ...@@ -60,11 +63,34 @@ def view_task(project, flamenco_props, task_id):
raise wz_exceptions.Forbidden() raise wz_exceptions.Forbidden()
task = Task.find(task_id, api=api) task = Task.find(task_id, api=api)
from . import REQUEABLE_TASK_STATES
write_access = current_flamenco.current_user_is_flamenco_admin()
return render_template('flamenco/tasks/view_task_embed.html', return render_template('flamenco/tasks/view_task_embed.html',
task=task, task=task,
project=project, project=project,
flamenco_props=flamenco_props.to_dict(), flamenco_props=flamenco_props.to_dict(),
flamenco_context=request.args.get('context')) flamenco_context=request.args.get('context'),
can_requeue_task=write_access and task['status'] in REQUEABLE_TASK_STATES)
@perproject_blueprint.route('/<task_id>/set-status', methods=['POST'])
@flask_login.login_required
@flamenco_project_view(extension_props=False)
def set_task_status(project, task_id):
from flask_login import current_user
new_status = request.form['status']
if new_status not in ALLOWED_TASK_STATUSES_FROM_WEB:
log.warning('User %s tried to set status of task %s to disallowed status "%s"; denied.',
current_user.objectid, task_id, new_status)
raise wz_exceptions.UnprocessableEntity('Status "%s" not allowed' % new_status)
log.info('User %s set status of task %s to "%s"', current_user.objectid, task_id, new_status)
current_flamenco.task_manager.web_set_task_status(task_id, new_status)
return '', 204
@perproject_blueprint.route('/<task_id>/log') @perproject_blueprint.route('/<task_id>/log')
......
from pillarsdk.resource import List from pillarsdk.resource import List
from pillarsdk.resource import Find from pillarsdk.resource import Find
from pillarsdk.resource import Patch
class Task(List, Find): class Task(List, Find, Patch):
"""Task class wrapping the REST nodes endpoint """Task class wrapping the REST nodes endpoint
""" """
path = 'flamenco/tasks' path = 'flamenco/tasks'
......
...@@ -157,7 +157,7 @@ $(function() { ...@@ -157,7 +157,7 @@ $(function() {
*/ */
function setJobStatus(job_id, new_status) { function setJobStatus(job_id, new_status) {
if (typeof job_id === 'undefined' || typeof new_status === 'undefined') { if (typeof job_id === 'undefined' || typeof new_status === 'undefined') {
if (console) console.log("cancelJob(" + job_id + ", " + new_status + ") called"); if (console) console.log("setJobStatus(" + job_id + ", " + new_status + ") called");
return; return;
} }
...@@ -186,3 +186,39 @@ function setJobStatus(job_id, new_status) { ...@@ -186,3 +186,39 @@ function setJobStatus(job_id, new_status) {
$('#job-action-panel .action-result-panel').html(show_html); $('#job-action-panel .action-result-panel').html(show_html);
}); });
} }
/**
* Request cancellation or re-queueing of the given task ID.
*/
function setTaskStatus(task_id, new_status) {
if (typeof task_id === 'undefined' || typeof new_status === 'undefined') {
if (console) console.log("setTaskStatus(" + task_id + ", " + new_status + ") called");
return;
}
project_url = ProjectUtils.projectUrl();
return $.post('/flamenco/' + project_url + '/tasks/' + task_id + '/set-status', {status: new_status})
.done(function(data) {
if(console) console.log('Job set-status request OK');
// Reload the entire page, since both the view-embed and the task list need refreshing.
location.reload(true);
})
.fail(function(xhr) {
if (console) {
console.log('Error setting task status');
console.log('XHR:', xhr);
}
statusBarSet('error', 'Error requesting task status change', 'pi-error');
var show_html;
if (xhr.status) {
show_html = xhr.responseText;
} else {
show_html = $('<p>').addClass('text-danger').text(
'Setting task status failed. There possibly was an error connecting to the server. ' +
'Please check your network connection and try again.');
}
$('#task-action-panel .action-result-panel').html(show_html);
});
}
...@@ -28,17 +28,17 @@ ...@@ -28,17 +28,17 @@
.table.item-properties .table.item-properties
.table-body .table-body
.table-row .table-row
.table-cell Belongs to job .table-cell Belongs to task
.table-cell .table-cell
a( a(
href="{{ url_for('flamenco.jobs.perproject.view_job', project_url=project.url, job_id=task.job) }}" href="{{ url_for('flamenco.tasks.perproject.view_task', project_url=project.url, task_id=task._id) }}"
data-job-id="{{ task.job }}", data-task-id="{{ task._id }}",
class="job-link" class="task-link"
) )
| {{ task.job }} | {{ task.task }}
.table-row .table-row
.table-cell Job Type .table-cell task Type
.table-cell {{ task.job_type | undertitle }} .table-cell {{ task.task_type | undertitle }}
.table-row.properties-status.js-help( .table-row.properties-status.js-help(
data-url="{{ url_for('flamenco.help', project_url=project.url) }}") data-url="{{ url_for('flamenco.help', project_url=project.url) }}")
.table-cell Status .table-cell Status
...@@ -57,6 +57,14 @@ ...@@ -57,6 +57,14 @@
.table-cell Worker .table-cell Worker
.table-cell {{ task.worker }} .table-cell {{ task.worker }}
#task-action-panel
| {% if can_requeue_task %}
button.btn.btn-success.requeue-task(onclick="setTaskStatus('{{ task._id }}', 'queued')")
i.pi-refresh
| Re-queue task
| {% endif %}
.action-result-panel
#item-view-feed #item-view-feed
#activities #activities
#comments-embed #comments-embed
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment