daemon is now tasks

This commit is contained in:
Patrick Jentsch 2020-11-13 13:33:32 +01:00
parent 5a06a6b241
commit f3f6612a57
6 changed files with 67 additions and 43 deletions

View File

@ -4,7 +4,6 @@ from flask_login import UserMixin, AnonymousUserMixin
from itsdangerous import BadSignature, TimedJSONWebSignatureSerializer from itsdangerous import BadSignature, TimedJSONWebSignatureSerializer
from time import sleep from time import sleep
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import db, login_manager from . import db, login_manager
import logging import logging
@ -180,7 +179,7 @@ class User(UserMixin, db.Model):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(User, self).__init__(**kwargs) super(User, self).__init__(**kwargs)
if self.role is None: if self.role is None:
if self.email == current_app.config['ADMIN_EMAIL_ADRESS']: if self.email == current_app.config['NOPAQUE_ADMIN']:
self.role = Role.query.filter_by(name='Administrator').first() self.role = Role.query.filter_by(name='Administrator').first()
if self.role is None: if self.role is None:
self.role = Role.query.filter_by(default=True).first() self.role = Role.query.filter_by(default=True).first()
@ -340,8 +339,6 @@ class Job(db.Model):
end_date = db.Column(db.DateTime()) end_date = db.Column(db.DateTime())
mem_mb = db.Column(db.Integer) mem_mb = db.Column(db.Integer)
n_cores = db.Column(db.Integer) n_cores = db.Column(db.Integer)
# This is used for zip creation
secure_filename = db.Column(db.String(32))
service = db.Column(db.String(64)) service = db.Column(db.String(64))
''' '''
' Service specific arguments as string list. ' Service specific arguments as string list.
@ -367,12 +364,6 @@ class Job(db.Model):
''' '''
return '<Job {}>'.format(self.title) return '<Job {}>'.format(self.title)
def create_secure_filename(self):
'''
Takes the job.title string nad cratesa a secure filename from this.
'''
self.secure_filename = secure_filename(self.title)
def delete(self): def delete(self):
''' '''
Delete the job and its inputs and results from the database. Delete the job and its inputs and results from the database.

View File

@ -50,8 +50,6 @@ def service(service):
service=service, service_args=json.dumps(service_args), service=service, service_args=json.dumps(service_args),
service_version=form.version.data, service_version=form.version.data,
status='preparing', title=form.title.data) status='preparing', title=form.title.data)
if job.service != 'corpus_analysis':
job.create_secure_filename()
db.session.add(job) db.session.add(job)
db.session.commit() db.session.commit()
try: try:
@ -66,7 +64,7 @@ def service(service):
else: else:
for file in form.files.data: for file in form.files.data:
filename = secure_filename(file.filename) filename = secure_filename(file.filename)
job_input = JobInput(dir=job.path, filename=filename, job=job) job_input = JobInput(filename=filename, job=job)
file.save(job_input.path) file.save(job_input.path)
db.session.add(job_input) db.session.add(job_input)
job.status = 'submitted' job.status = 'submitted'

View File

@ -21,9 +21,16 @@ def check_corpora():
def check_jobs(): def check_jobs():
print('check_jobs()')
jobs = Job.query.all() jobs = Job.query.all()
print([job.status for job in jobs])
for job in filter(lambda job: job.status == 'submitted', jobs): for job in filter(lambda job: job.status == 'submitted', jobs):
print('pre create_job_service({})'.format(job))
job_utils.create_job_service(job) job_utils.create_job_service(job)
for job in filter(lambda job: job.status in ['queued', 'running'], jobs): for job in filter(lambda job: job.status in ['queued', 'running'], jobs):
print('pre checkout_job_service({})'.format(job))
job_utils.checkout_job_service(job) job_utils.checkout_job_service(job)
for job in filter(lambda job: job.status == 'canceling', jobs):
print('pre remove_job_service({})'.format(job))
job_utils.remove_job_service(job)
db.session.commit() db.session.commit()

View File

@ -1,7 +1,8 @@
from datetime import datetime from datetime import datetime
from werkzeug.utils import secure_filename
from . import docker_client from . import docker_client
from .. import db from .. import db, mail
from ..email import create_message, send from ..email import create_message
from ..models import JobResult from ..models import JobResult
import docker import docker
import logging import logging
@ -10,11 +11,12 @@ import os
def create_job_service(job): def create_job_service(job):
print('create_job_service({})'.format(job))
cmd = '{} -i /files -o /files/output'.format(job.service) cmd = '{} -i /files -o /files/output'.format(job.service)
if job.service == 'file-setup': if job.service == 'file-setup':
cmd += ' -f {}'.format(job.secure_filename) cmd += ' -f {}'.format(secure_filename(job.title))
cmd += ' --log-dir /files' cmd += ' --log-dir /files'
cmd += ' --zip [{}]_{}'.format(job.service, job.secure_filename) cmd += ' --zip [{}]_{}'.format(job.service, secure_filename(job.title))
cmd += ' ' + ' '.join(json.loads(job.service_args)) cmd += ' ' + ' '.join(json.loads(job.service_args))
service_kwargs = {'command': cmd, service_kwargs = {'command': cmd,
'constraints': ['node.role==worker'], 'constraints': ['node.role==worker'],
@ -36,15 +38,18 @@ def create_job_service(job):
logging.error('Create "{}" service raised '.format(service_kwargs['name']) # noqa logging.error('Create "{}" service raised '.format(service_kwargs['name']) # noqa
+ '[docker-APIError] The server returned an error. ' + '[docker-APIError] The server returned an error. '
+ 'Details: {}'.format(e)) + 'Details: {}'.format(e))
return
else: else:
job.status = 'queued' job.status = 'queued'
finally:
print('Sending email to {}'.format(job.creator.email))
msg = create_message( msg = create_message(
job.creator.email, job.creator.email,
'Status update for your Job "{}"'.format(job.title), 'Status update for your Job "{}"'.format(job.title),
'tasks/email/notification', 'tasks/email/notification',
job=job job=job
) )
send(msg) mail.send(msg)
def checkout_job_service(job): def checkout_job_service(job):
@ -60,10 +65,12 @@ def checkout_job_service(job):
logging.error('Get "{}" service raised '.format(service_name) logging.error('Get "{}" service raised '.format(service_name)
+ '[docker-APIError] The server returned an error. ' + '[docker-APIError] The server returned an error. '
+ 'Details: {}'.format(e)) + 'Details: {}'.format(e))
return
except docker.errors.InvalidVersion: except docker.errors.InvalidVersion:
logging.error('Get "{}" service raised '.format(service_name) logging.error('Get "{}" service raised '.format(service_name)
+ '[docker-InvalidVersion] One of the arguments is ' + '[docker-InvalidVersion] One of the arguments is '
+ 'not supported with the current API version.') + 'not supported with the current API version.')
return
else: else:
service_tasks = service.tasks() service_tasks = service.tasks()
if not service_tasks: if not service_tasks:
@ -71,31 +78,33 @@ def checkout_job_service(job):
task_state = service_tasks[0].get('Status').get('State') task_state = service_tasks[0].get('Status').get('State')
if job.status == 'queued' and task_state != 'pending': if job.status == 'queued' and task_state != 'pending':
job.status = 'running' job.status = 'running'
elif job.status == 'running' and task_state == 'complete': elif job.status == 'running' and task_state in ['complete', 'failed']: # noqa
service.remove() try:
job.end_date = datetime.utcnow() service.remove()
job.status = task_state except docker.errors.APIError as e:
if task_state == 'complete': logging.error('Remove "{}" service raised '.format(service_name) # noqa
job_results_dir = os.path.join(job.path, 'output') + '[docker-APIError] The server returned an error. ' # noqa
job_results = filter(lambda x: x.endswith('.zip'), + 'Details: {}'.format(e))
os.listdir(job_results_dir)) return
for job_result in job_results: else:
job_result = JobResult(filename=job_result, job=job) job.end_date = datetime.utcnow()
db.session.add(job_result) job.status = task_state
elif job.status == 'running' and task_state == 'failed': if task_state == 'complete':
service.remove() job_results_dir = os.path.join(job.path, 'output')
job.end_date = datetime.utcnow() job_results = filter(lambda x: x.endswith('.zip'),
job.status = task_state os.listdir(job_results_dir))
for job_result in job_results:
job_result = JobResult(filename=job_result, job=job)
db.session.add(job_result)
finally: finally:
# TODO: send email print('Sending email to {}'.format(job.creator.email))
msg = create_message( msg = create_message(
job.creator.email, job.creator.email,
'[nopaque] Status update for your Job "{}"'.format(job.title), 'Status update for your Job "{}"'.format(job.title),
'tasks/email/notification', 'tasks/email/notification',
job=job job=job
) )
send(msg) mail.send(msg)
pass
def remove_job_service(job): def remove_job_service(job):
@ -103,9 +112,28 @@ def remove_job_service(job):
try: try:
service = docker_client.services.get(service_name) service = docker_client.services.get(service_name)
except docker.errors.NotFound: except docker.errors.NotFound:
# TODO: send email
job.status = 'canceled' job.status = 'canceled'
# TODO: handle docker.errors.APIError and docker.errors.InvalidVersion except docker.errors.APIError as e:
logging.error('Get "{}" service raised '.format(service_name)
+ '[docker-APIError] The server returned an error. '
+ 'Details: {}'.format(e))
return
except docker.errors.InvalidVersion:
logging.error('Get "{}" service raised '.format(service_name)
+ '[docker-InvalidVersion] One of the arguments is '
+ 'not supported with the current API version.')
return
else: else:
service.update(mounts=None) try:
service.remove() service.update(mounts=None)
except docker.errors.APIError as e:
logging.error('Update "{}" service raised '.format(service_name) # noqa
+ '[docker-APIError] The server returned an error. ' # noqa
+ 'Details: {}'.format(e))
return
try:
service.remove()
except docker.errors.APIError as e:
logging.error('Remove "{}" service raised '.format(service_name) # noqa
+ '[docker-APIError] The server returned an error. ' # noqa
+ 'Details: {}'.format(e))

View File

@ -3,6 +3,6 @@
<p>The status of your Job "<b>{{ job.title }}</b>" has changed!</p> <p>The status of your Job "<b>{{ job.title }}</b>" has changed!</p>
<p>It is now <b>{{ job.status }}</b>!</p> <p>It is now <b>{{ job.status }}</b>!</p>
<p>You can access your Job here: <a href="{{ url_for('jobs.job', job_id=job.id) }}">{{ url_for('jobs.job', job_id=job.id) }}</a></p> <p>You can access your Job here: </p>
<p>Kind regards!<br>Your nopaque team</p> <p>Kind regards!<br>Your nopaque team</p>

View File

@ -3,7 +3,7 @@ Dear {{ job.creator.username }},
The status of your Job "{{ job.title }}" has changed! The status of your Job "{{ job.title }}" has changed!
It is now {{ job.status }}! It is now {{ job.status }}!
You can access your Job here: {{ url_for('jobs.job', job_id=job.id) }} You can access your Job here:
Kind regards! Kind regards!
Your nopaque team Your nopaque team