# No shebang line, this module is meant to be imported
#
# Copyright 2014 Ambient Entertainment GmbH & Co. KG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from decimal import Decimal
from datetime import datetime, timedelta
try:
from httplib import (
BAD_REQUEST, NOT_FOUND, SEE_OTHER, INTERNAL_SERVER_ERROR)
except ImportError: # pragma: no cover
from http.client import (
BAD_REQUEST, NOT_FOUND, SEE_OTHER, INTERNAL_SERVER_ERROR)
from flask import render_template, request, redirect, url_for, flash
from sqlalchemy.orm import aliased
from sqlalchemy import func, desc, asc, or_, distinct
from pyfarm.core.logger import getLogger
from pyfarm.core.enums import WorkState, _WorkState, AgentState
from pyfarm.scheduler.tasks import delete_job, stop_task, assign_tasks
from pyfarm.models.job import (
Job, JobDependency, JobTagAssociation, JobNotifiedUser)
from pyfarm.models.tag import Tag, JobTagRequirement
from pyfarm.models.task import Task
from pyfarm.models.agent import Agent
from pyfarm.models.jobqueue import JobQueue
from pyfarm.models.jobtype import JobType, JobTypeVersion
from pyfarm.models.user import User
from pyfarm.master.application import db
logger = getLogger("ui.jobs")
[docs]def jobs():
queued_count_query = db.session.query(
Task.job_id, func.count('*').label('t_queued')).\
filter(Task.state == None).group_by(Task.job_id).subquery()
running_count_query = db.session.query(
Task.job_id, func.count('*').label('t_running')).\
filter(Task.state == WorkState.RUNNING).\
group_by(Task.job_id).subquery()
done_count_query = db.session.query(
Task.job_id, func.count('*').label('t_done')).\
filter(Task.state == WorkState.DONE).\
group_by(Task.job_id).subquery()
failed_count_query = db.session.query(
Task.job_id, func.count('*').label('t_failed')).\
filter(Task.state == WorkState.FAILED).\
group_by(Task.job_id).subquery()
child_count_query = db.session.query(
JobDependency.c.parentid, func.count('*').label('child_count')).\
group_by(JobDependency.c.parentid).subquery()
blocker_count_query = db.session.query(
JobDependency.c.childid, func.count('*').label('blocker_count')).\
join(Job, Job.id == JobDependency.c.parentid).\
filter(or_(Job.state == None, Job.state != WorkState.DONE)).\
group_by(JobDependency.c.childid).subquery()
agent_count_query = db.session.query(
Task.job_id, func.count(distinct(Task.agent_id)).label('agent_count')).\
filter(Task.agent_id != None, or_(Task.state == None,
Task.state == WorkState.RUNNING),
Task.agent.has(Agent.state != AgentState.OFFLINE)).\
group_by(Task.job_id).subquery()
jobs_query = db.session.query(Job,
func.coalesce(
queued_count_query.c.t_queued,
0).label('t_queued'),
func.coalesce(
running_count_query.c.t_running,
0).label('t_running'),
func.coalesce(
done_count_query.c.t_done,
0).label('t_done'),
func.coalesce(
failed_count_query.c.t_failed,
0).label('t_failed'),
User.username,
JobType.name.label('jobtype_name'),
JobType.id.label('jobtype_id'),
JobQueue.fullpath.label('jobqueue_path'),
func.coalesce(
child_count_query.c.child_count,
0).label('child_count'),
func.coalesce(
blocker_count_query.c.blocker_count,
0).label('blocker_count'),
func.coalesce(
agent_count_query.c.agent_count,
0).label('agent_count')).\
join(JobTypeVersion, Job.jobtype_version_id == JobTypeVersion.id).\
join(JobType, JobTypeVersion.jobtype_id == JobType.id).\
outerjoin(JobQueue, Job.job_queue_id == JobQueue.id).\
outerjoin(queued_count_query, Job.id == queued_count_query.c.job_id).\
outerjoin(running_count_query, Job.id == running_count_query.c.job_id).\
outerjoin(done_count_query, Job.id == done_count_query.c.job_id).\
outerjoin(failed_count_query, Job.id == failed_count_query.c.job_id).\
outerjoin(User, Job.user_id == User.id).\
outerjoin(child_count_query, Job.id == child_count_query.c.parentid).\
outerjoin(blocker_count_query, Job.id == blocker_count_query.c.childid).\
outerjoin(agent_count_query, Job.id == agent_count_query.c.job_id)
filters = {}
if "tags" in request.args:
filters["tags"] = request.args.get("tags")
tags = request.args.get("tags").split(" ")
tags = [x for x in tags if not x == ""]
for tag in tags:
jobs_query = jobs_query.filter(Job.tags.any(Tag.tag == tag))
filters["state_paused"] = ("state_paused" in request.args and
request.args["state_paused"].lower() == "true")
filters["state_queued"] = ("state_queued" in request.args and
request.args["state_queued"].lower() == "true")
filters["state_running"] = ("state_running" in request.args and
request.args["state_running"].lower() == "true")
filters["state_done"] = ("state_done" in request.args and
request.args["state_done"].lower() == "true")
filters["state_failed"] = ("state_failed" in request.args and
request.args["state_failed"].lower() == "true")
no_state_filters = True
if (filters["state_paused"] or
filters["state_queued"] or
filters["state_running"] or
filters["state_done"] or
filters["state_failed"]):
no_state_filters = False
wanted_states = []
if filters["state_paused"]:
wanted_states.append(WorkState.PAUSED)
if filters["state_running"]:
wanted_states.append(WorkState.RUNNING)
if filters["state_done"]:
wanted_states.append(WorkState.DONE)
if filters["state_failed"]:
wanted_states.append(WorkState.FAILED)
if filters["state_queued"]:
jobs_query = jobs_query.filter(or_(
Job.state == None,
Job.state.in_(wanted_states)))
else:
jobs_query = jobs_query.filter(Job.state.in_(wanted_states))
if "title" in request.args:
title = request.args.get("title")
filters["title"] = title
if title != "":
jobs_query = jobs_query.filter(
Job.title.ilike("%%%s%%" % title))
filters["hidden_filter"] = ("hidden_filter" in request.args and
request.args["hidden_filter"].lower() == "true")
filters["hidden"] = False
filters["not_hidden"] = True
if filters["hidden_filter"]:
filters["hidden"] = ("hidden" in request.args and
request.args["hidden"].lower() == "true")
filters["not_hidden"] = ("not_hidden" in request.args and
request.args["not_hidden"].lower() == "true")
if not filters["hidden"]:
jobs_query = jobs_query.filter(Job.hidden != True)
if not filters["not_hidden"]:
jobs_query = jobs_query.filter(Job.hidden != False)
filters["blocked_filter"] = ("blocked_filter" in request.args and
request.args["blocked_filter"].lower() ==
"true")
filters["blocked"] = True
filters["not_blocked"] = True
if filters["blocked_filter"]:
filters["blocked"] = ("blocked" in request.args and
request.args["blocked"].lower() == "true")
filters["not_blocked"] = ("not_blocked" in request.args and
request.args["not_blocked"].lower() == "true")
if not filters["blocked"]:
jobs_query = jobs_query.filter(
blocker_count_query.c.blocker_count == None)
if not filters["not_blocked"]:
jobs_query = jobs_query.filter(
blocker_count_query.c.blocker_count != None)
filters["no_user"] = ("no_user" in request.args and
request.args["no_user"].lower() == "true")
if "u" in request.args or filters["no_user"]:
user_ids = request.args.getlist("u")
user_ids = [int(x) for x in user_ids]
if filters["no_user"]:
jobs_query = jobs_query.filter(or_(
Job.user_id.in_(user_ids),
Job.user_id == None))
else:
jobs_query = jobs_query.filter(Job.user_id.in_(user_ids))
filters["u"] = user_ids
filters["no_queue"] = ("no_queue" in request.args and
request.args["no_queue"].lower() == "true")
if "q" in request.args or filters["no_queue"]:
jobqueue_ids = request.args.getlist("q")
jobqueue_ids = [int(x) for x in jobqueue_ids]
if filters["no_queue"]:
jobs_query = jobs_query.filter(or_(
Job.job_queue_id.in_(jobqueue_ids),
Job.job_queue_id == None))
else:
jobs_query = jobs_query.filter(JobQueue.id.in_(jobqueue_ids))
filters["q"] = jobqueue_ids
if "p" in request.args:
priorities = request.args.getlist("p")
priorities = [int(x) for x in priorities]
jobs_query = jobs_query.filter(Job.priority.in_(priorities))
filters["p"] = priorities
if "jt" in request.args:
jobtype_ids = request.args.getlist("jt")
jobtype_ids = [int(x) for x in jobtype_ids]
jobs_query = jobs_query.filter(JobType.id.in_(jobtype_ids))
filters["jt"] = jobtype_ids
order_dir = "desc"
order_by = "time_submitted"
if "order_by" in request.args:
order_by = request.args.get("order_by")
if order_by not in ["title", "state", "time_submitted", "t_queued",
"t_running", "t_failed", "t_done", "username",
"jobtype_name", "agent_count", "priority", "weight",
"jobqueue_path"]:
return (render_template(
"pyfarm/error.html",
error="Unknown order key %r. Options are 'title', 'state', "
"'time_submitted', 't_queued', 't_running', 't_failed', "
"'t_done', 'username', 'agent_count', 'priority', 'weight' "
"or 'jobqueue_path'" % order_by), BAD_REQUEST)
if "order_dir" in request.args:
order_dir = request.args.get("order_dir")
if order_dir not in ["asc", "desc"]:
return (render_template(
"pyfarm/error.html",
error="Unknown order direction %r. Options are 'asc' or 'desc'" %
order_dir),
BAD_REQUEST)
if order_by == "time_submitted" and order_dir == "desc":
jobs_query = jobs_query.order_by(desc(Job.time_submitted))
elif order_by == "time_submitted" and order_dir == "asc":
jobs_query = jobs_query.order_by(asc(Job.time_submitted))
elif order_by == "state" and order_dir == "desc":
jobs_query = jobs_query.order_by(desc(Job.state))
elif order_by == "state" and order_dir == "asc":
jobs_query = jobs_query.order_by(asc(Job.state))
elif order_by == "weight" and order_dir == "asc":
jobs_query = jobs_query.order_by(asc(Job.weight))
elif order_by == "weight" and order_dir == "desc":
jobs_query = jobs_query.order_by(desc(Job.weight))
elif order_by == "priority" and order_dir == "asc":
jobs_query = jobs_query.order_by(asc(Job.priority))
elif order_by == "priority" and order_dir == "desc":
jobs_query = jobs_query.order_by(desc(Job.priority))
else:
jobs_query = jobs_query.order_by("%s %s" % (order_by, order_dir))
jobs_query = jobs_query.order_by(Job.id)
jobs_count = jobs_query.count()
queued_jobs_count = jobs_query.filter(Job.state == None).count()
running_jobs_count = jobs_query.filter(
Job.state == WorkState.RUNNING).count()
failed_jobs_count = jobs_query.filter(Job.state == WorkState.FAILED).count()
done_jobs_count = jobs_query.filter(Job.state == WorkState.DONE).count()
jobs_subquery = jobs_query.subquery()
per_page = int(request.args.get("per_page", 100))
page = int(request.args.get("page", 1))
filters["per_page"] = per_page
filters["page"] = page
num_pages = 1
all_pages = []
if per_page > 0:
jobs_query = jobs_query.offset((page - 1) * per_page).limit(per_page)
num_pages = int(jobs_count / per_page)
if jobs_count % per_page > 0:
num_pages = num_pages + 1
all_pages = range(0, num_pages)
jobs = jobs_query.all()
users_query = User.query.order_by(User.username)
jobtypes_query = JobType.query
tags_by_job_query = db.session.query(JobTagAssociation.c.job_id, Tag.tag).\
join(Tag, JobTagAssociation.c.tag_id==Tag.id).all()
tags_by_job_id = {}
for association in tags_by_job_query:
if association[0] not in tags_by_job_id:
tags_by_job_id[association[0]] = [association[1]]
else:
tags_by_job_id[association[0]] += [association[1]]
jobqueues = JobQueue.query.order_by(JobQueue.fullpath).all()
available_priorities = db.session.query(distinct(Job.priority)).all()
available_priorities = set(x[0] for x in available_priorities)
filters_and_order = filters.copy()
filters_and_order.update({"order_by": order_by, "order_dir": order_dir})
filters_and_order_wo_pagination = filters_and_order.copy()
del filters_and_order_wo_pagination["per_page"]
del filters_and_order_wo_pagination["page"]
return render_template("pyfarm/user_interface/jobs.html",
jobs=jobs, filters=filters, order_by=order_by,
order_dir=order_dir,
order={"order_by": order_by, "order_dir": order_dir},
no_state_filters=no_state_filters, users=users_query,
filters_and_order=filters_and_order,
jobtypes=jobtypes_query,
tags_by_job_id=tags_by_job_id, jobs_count=jobs_count,
all_pages=all_pages, num_pages=num_pages,
filters_and_order_wo_pagination=\
filters_and_order_wo_pagination,
jobqueues=jobqueues,
queued_jobs_count=queued_jobs_count,
running_jobs_count=running_jobs_count,
failed_jobs_count=failed_jobs_count,
done_jobs_count=done_jobs_count,
priorities=available_priorities)
[docs]def single_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
first_task = Task.query.filter_by(job=job).order_by("frame asc").first()
last_task = Task.query.filter_by(job=job).order_by("frame desc").first()
tasks_query = Task.query.filter(Task.job == job)
order_dir = "asc"
order_by = "frame"
if "order_by" in request.args:
order_by = request.args.get("order_by")
if order_by not in ["frame", "state", "runtime", "failures", "progress"]:
return (render_template(
"pyfarm/error.html",
error="Unknown order key %r. Options are 'frame', 'state', "
"'runtime', 'progress', or 'failures''" % order_by),
BAD_REQUEST)
if "order_dir" in request.args:
order_dir = request.args.get("order_dir")
if order_dir not in ["asc", "desc"]:
return (render_template(
"pyfarm/error.html",
error="Unknown order direction %r. Options are 'asc' or 'desc'" %
order_dir),
BAD_REQUEST)
if order_by == "frame" and order_dir == "desc":
tasks_query = tasks_query.order_by(desc(Task.frame)).\
order_by(desc(Task.tile))
elif order_by == "frame" and order_dir == "asc":
tasks_query = tasks_query.order_by(asc(Task.frame)).\
order_by(asc(Task.tile))
elif order_by == "state" and order_dir == "desc":
tasks_query = tasks_query.order_by(desc(Task.state))
elif order_by == "state" and order_dir == "asc":
tasks_query = tasks_query.order_by(asc(Task.state))
# Note: Those time operations do not seem to work in sqlite
elif order_by == "runtime" and order_dir == "asc":
tasks_query = tasks_query.order_by(asc(
func.coalesce(Task.time_finished, datetime.utcnow()) -
Task.time_started))
elif order_by == "runtime" and order_dir == "desc":
tasks_query = tasks_query.order_by(desc(
func.coalesce(Task.time_finished, datetime.utcnow()) -
Task.time_started))
else:
tasks_query = tasks_query.order_by("%s %s" % (order_by, order_dir))
tasks = tasks_query.all()
jobqueues = JobQueue.query.all()
users_query = User.query.filter(User.email != None).order_by(User.username)
latest_jobtype_version = db.session.query(JobTypeVersion.version).filter_by(
jobtype=job.jobtype_version.jobtype).\
order_by(desc(JobTypeVersion.version)).first()
autodelete_time = None
if job.autodelete_time:
autodelete_timedelta = timedelta(seconds=job.autodelete_time)
autodelete_time = {"days": autodelete_timedelta.days}
seconds = autodelete_timedelta.seconds
autodelete_time["hours"], remainder = divmod(seconds, 3600)
autodelete_time["minutes"], autodelete_time["seconds"] =\
divmod(remainder, 60)
return render_template("pyfarm/user_interface/job.html", job=job,
tasks=tasks, first_task=first_task,
last_task=last_task, queues=jobqueues,
users=users_query,
latest_jobtype_version=latest_jobtype_version[0],
now=datetime.utcnow(),
autodelete_time=autodelete_time,
order_by=order_by, order_dir=order_dir)
[docs]def delete_single_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.to_be_deleted = True
db.session.add(job)
child_job_ids = []
for child in job.children:
child.to_be_deleted = True
child_job_ids.append(child.id)
db.session.add(child)
db.session.commit()
for id_ in child_job_ids + [job_id]:
logger.info("Marking job %s for deletion", id_)
delete_job.delay(id_)
flash("Job %s will be deleted." % job.title)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def delete_multiple_jobs():
job_ids = request.form.getlist("job_id")
job_ids_to_delete = []
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.to_be_deleted = True
db.session.add(job)
job_ids_to_delete.append(job.id)
for child in job.children:
child.to_be_deleted = True
job_ids_to_delete.append(child.id)
db.session.add(child)
db.session.commit()
for id_ in job_ids_to_delete:
logger.info("Marking job %s for deletion", id_)
delete_job.delay(id_)
flash("Selected jobs will be deleted.")
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def rerun_single_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.rerun()
db.session.commit()
assign_tasks.delay()
logger.info("Job %s (job id: %s) is being rerun by request from %s",
job.title, job.id, request.remote_addr)
flash("Job %s will be run again." % job.title)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def rerun_multiple_jobs():
job_ids = request.form.getlist("job_id")
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.rerun()
logger.info("Job %s (job id: %s) is being rerun by request from %s",
job.title, job.id, request.remote_addr)
db.session.commit()
assign_tasks.delay()
flash("Selected jobs will be run again.")
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def rerun_failed_in_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.rerun_failed()
db.session.commit()
assign_tasks.delay()
logger.info("Failed tasks from job %s (job id: %s) are being rerun by "
"request from %s",
job.title, job.id, request.remote_addr)
flash("Failed tasks in job %s will be run again." % job.title)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def rerun_failed_in_multiple_jobs():
job_ids = request.form.getlist("job_id")
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
logger.info("Failed tasks from job %s (job id: %s) are being rerun by "
"request from %s",
job.title, job.id, request.remote_addr)
job.rerun_failed()
db.session.commit()
assign_tasks.delay()
flash("Failed tasks in selected jobs will be run again.")
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def pause_single_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.state = WorkState.PAUSED
db.session.add(job)
db.session.commit()
for task in job.tasks:
if task.state == WorkState.RUNNING:
stop_task.delay(task.id)
assign_tasks.delay()
flash("Job %s will be paused." % job.title)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def pause_multiple_jobs():
job_ids = request.form.getlist("job_id")
task_ids_to_stop = []
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
for task in job.tasks:
if task.state == WorkState.RUNNING:
task_ids_to_stop.append(task.id)
job.state = WorkState.PAUSED
db.session.add(job)
db.session.commit()
for task_id in task_ids_to_stop:
stop_task.delay(task_id)
assign_tasks.delay()
flash("Selected jobs will be paused.")
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def unpause_single_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.state = None
job.update_state()
db.session.add(job)
db.session.commit()
assign_tasks.delay()
flash("Job %s is unpaused." % job.title)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def unpause_multiple_jobs():
job_ids = request.form.getlist("job_id")
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.state = None
job.update_state()
db.session.add(job)
db.session.commit()
assign_tasks.delay()
flash("Selected jobs are unpaused")
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def alter_frames_in_single_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
start = Decimal(request.form['start'])
end = Decimal(request.form['end'])
by = Decimal(request.form['by'])
try:
job.alter_frame_range(start, end, by)
except ValueError as e:
return (render_template(
"pyfarm/error.html", error=e), BAD_REQUEST)
db.session.commit()
assign_tasks.delay()
flash("Frame selection for job %s has been changed." % job.title)
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def alter_scheduling_parameters_for_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.priority = int(request.form['priority'])
job.weight = int(request.form['weight'])
if request.form['minimum_agents']:
job.minimum_agents = int(request.form['minimum_agents'])
else:
job.minimum_agents = None
if request.form['maximum_agents']:
job.maximum_agents = int(request.form['maximum_agents'])
else:
job.maximum_agents = None
job.batch = int(request.form['batch'])
job.requeue = int(request.form['requeue'])
if request.form['minimum_ram']:
job.ram = int(request.form['minimum_ram'])
else:
job.ram = None
if request.form['minimum_cpus']:
job.cpus = int(request.form['minimum_cpus'])
else:
job.cpus = None
if request.form['queue']:
queue_id = int(request.form['queue'])
queue = JobQueue.query.filter_by(id=queue_id).first()
if not queue:
return (render_template(
"pyfarm/error.html", error="Queue %s not found" % queue_id),
NOT_FOUND)
job.queue = queue
else:
job.queue = None
db.session.add(job)
db.session.commit()
flash("Scheduling parameters for job %s have been changed." % job.title)
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def move_multiple_jobs():
job_ids = request.form.getlist("job_id")
queue_id = int(request.form['queue'])
queue = JobQueue.query.filter_by(id=queue_id).first()
if not queue:
return (render_template(
"pyfarm/error.html", error="Queue %s not found" % queue_id),
NOT_FOUND)
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.queue = queue
db.session.add(job)
db.session.commit()
flash("Selected jobs have been moved to queue %s" % queue.path())
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def set_prio_weight_on_jobs():
job_ids = request.form.getlist("job_id")
prio = int(request.form["prio"])
weight = int(request.form["weight"])
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.priority = prio
job.weight = weight
db.session.add(job)
db.session.commit()
flash("Priority and weight on selected jobs have been set")
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def add_tag_on_jobs():
job_ids = request.form.getlist("job_id")
tag_name = request.form["tag"].strip()
tag = Tag.query.filter_by(tag=tag_name).first()
if not tag:
tag = Tag(tag=tag_name)
db.session.add(tag)
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
if tag not in job.tags:
job.tags.append(tag)
db.session.add(job)
db.session.commit()
flash("Tag %s has been added to selected jobs." % tag_name)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def remove_tag_from_jobs():
job_ids = request.form.getlist("job_id")
tag_name = request.form["tag"].strip()
tag = Tag.query.filter_by(tag=tag_name).first()
if not tag:
return (render_template(
"pyfarm/error.html",
error="Tag %s not found" % tag_name),
NOT_FOUND)
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
if tag in job.tags:
job.tags.remove(tag)
db.session.add(job)
db.session.commit()
flash("Tag %s has been removed from selected jobs." % tag_name)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def add_tag_requirement_on_jobs():
job_ids = request.form.getlist("job_id")
tag_name = request.form["tag"].strip()
negate = False
if tag_name[0] == "-":
if len(tag_name) < 2:
return (render_template(
"pyfarm/error.html",
error="Tag must be at least one character long"),
NOT_FOUND)
else:
negate = True
tag_name = tag_name[1:]
tag = Tag.query.filter_by(tag=tag_name).first()
if not tag:
tag = Tag(tag=tag_name)
db.session.add(tag)
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
tag_requirement = JobTagRequirement.query.filter_by(
job=job, tag=tag).first()
if not tag_requirement:
tag_requirement = JobTagRequirement(job=job, tag=tag, negate=negate)
db.session.add(tag_requirement)
elif tag_requirement.negate != negate:
tag_requirement.negate = negate
db.session.add(tag_requirement)
db.session.commit()
flash("Tag requirement %s has been added to selected jobs." % tag_name)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def remove_tag_requirement_from_jobs():
job_ids = request.form.getlist("job_id")
tag_name = request.form["tag"].strip()
negate = False
if tag_name[0] == "-":
if len(tag_name) < 2:
return (render_template(
"pyfarm/error.html",
error="Tag must be at least one character long"),
NOT_FOUND)
else:
negate = True
tag_name = tag_name[1:]
tag = Tag.query.filter_by(tag=tag_name).first()
if not tag:
return (render_template(
"pyfarm/error.html",
error="Tag %s not found" % tag_name),
NOT_FOUND)
for job_id in job_ids:
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
tag_requirement = JobTagRequirement.query.filter_by(
job=job, tag=tag, negate=negate).first()
if tag_requirement:
db.session.delete(tag_requirement)
db.session.commit()
flash("Tag requirement %s has been removed from selected jobs." % tag_name)
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("jobs_index_ui"), SEE_OTHER)
[docs]def alter_autodeletion_for_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
days = int(request.form["days"] or 0)
hours = int(request.form["hours"] or 0)
minutes = int(request.form["minutes"] or 0)
seconds = int(request.form["seconds"] or 0)
autodelete_time = timedelta(days=days, hours=hours, minutes=minutes,
seconds=seconds)
if autodelete_time.total_seconds() > 0:
job.autodelete_time = autodelete_time.total_seconds()
else:
job.autodelete_time = None
db.session.add(job)
db.session.commit()
flash("Scheduling parameters for job %s have been changed." % job.title)
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def update_notes_for_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
job.notes = request.form['notes']
db.session.add(job)
db.session.commit()
flash("Free form notes for job %s have been edited." % job.title)
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def add_notified_user_to_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
user_id = request.form['user']
user = User.query.filter_by(id=user_id).first()
if not user:
return (render_template(
"pyfarm/error.html", error="User %s not found" % user_id),
NOT_FOUND)
notified_user = JobNotifiedUser.query.filter_by(user=user, job=job).first()
if not notified_user:
notified_user = JobNotifiedUser(user=user, job=job)
notified_user.on_success = False
notified_user.on_failure = False
notified_user.on_success = (("on_success" in request.form and
request.form["on_success"] == "true") or
notified_user.on_success)
notified_user.on_failure = (("on_failure" in request.form and
request.form["on_failure"] == "true") or
notified_user.on_failure)
notified_user.on_deletion = (("on_deletion" in request.form and
request.form["on_deletion"] == "true") or
notified_user.on_deletion)
db.session.add(notified_user)
db.session.commit()
flash("User %s has been added as notified user to job %s." %
(user.username, job.title))
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def remove_notified_user_from_job(job_id, user_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
user = User.query.filter_by(id=user_id).first()
if not user:
return (render_template(
"pyfarm/error.html", error="User %s not found" % user_id),
NOT_FOUND)
notified_user = JobNotifiedUser.query.filter_by(user=user, job=job).first()
if notified_user:
db.session.delete(notified_user)
db.session.commit()
flash("User %s has been removed from notified users for job %s." %
(user.username, job.title))
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def upgrade_job_to_latest_jobtype_version(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
latest_version = JobTypeVersion.query.filter_by(
jobtype=job.jobtype_version.jobtype).\
order_by(desc(JobTypeVersion.version)).first()
if not latest_version:
return (render_template(
"pyfarm/error.html", error="Jobtype %s has no versions" %
job.jobtype_id), INTERNAL_SERVER_ERROR)
job.jobtype_version = latest_version
db.session.add(job)
db.session.commit()
flash("Job %s has been upgraded to jobtype %s, version %s." %
(job.title, job.jobtype_version.jobtype.name,
job.jobtype_version.version))
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def update_tag_requirements_in_job(job_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
tagnames = request.form["tag_requirements"].split(" ")
tagnames = [x.strip() for x in tagnames if not x == ""]
job.tag_requirements = []
for name in tagnames:
negate = False
if name.startswith("-"):
if len(name) < 2:
return (render_template(
"pyfarm/error.html", error="Tag too short: %s" % name),
NOT_FOUND)
negate = True
name = name[1:]
tag = Tag.query.filter_by(tag=name).first()
if not tag:
tag = Tag(tag=name)
db.session.add(tag)
tag_requirement = JobTagRequirement(job=job, tag=tag, negate=negate)
db.session.add(tag_requirement)
db.session.commit()
flash("Tag Requirements for job %s have been updated." % job.title)
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)
[docs]def rerun_single_task(job_id, task_id):
job = Job.query.filter_by(id=job_id).first()
if not job:
return (render_template(
"pyfarm/error.html", error="Job %s not found" % job_id),
NOT_FOUND)
task = Task.query.filter_by(id=task_id, job=job).first()
if not task:
return (render_template(
"pyfarm/error.html", error="Task %s not found" % task_id),
NOT_FOUND)
if task.state == WorkState.RUNNING:
return (render_template(
"pyfarm/error.html", error="Cannot rerun task while it is "
"still running"),
BAD_REQUEST)
task.state = None
task.agent = None
task.failures = 0
if Job.state != WorkState.RUNNING:
job.state = None
job.completion_notify_sent = False
db.session.add(job)
db.session.add(task)
db.session.commit()
assign_tasks.delay()
flash("Task %s (frame %s) in job %s will be run again." %
(task.id, task.frame, job.title))
if "next" in request.args:
return redirect(request.args.get("next"), SEE_OTHER)
else:
return redirect(url_for("single_job_ui", job_id=job.id), SEE_OTHER)