diff --git a/app/models.py b/app/models.py index 2f5597a5e3a309c8a161c1276233f098dfd60308..de1fb6980044ff9490da7f3a33fbae8d30a07b5f 100644 --- a/app/models.py +++ b/app/models.py @@ -967,6 +967,7 @@ class Task(db.Model): command = db.Column(db.Text) status = db.Column(db.Enum(JobStatus, name='job_status')) awx_job_id = db.Column(db.Integer) + exception = db.Column(db.Text) user_id = db.Column(db.Integer, db.ForeignKey('user_account.id'), nullable=False, default=utils.fetch_current_user_id) @@ -992,6 +993,7 @@ class Task(db.Model): 'awx_job_id': self.awx_job_id, 'awx_job_url': self.awx_job_url, 'command': self.command, + 'exception': self.exception, 'user': str(self.user), } diff --git a/app/tasks.py b/app/tasks.py index c558b83016ee529b4d8e34256cffef1fe2da7b69..fba509724fef211573affb64fa4067e5ed844d6e 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -10,6 +10,7 @@ This module implements tasks to run. """ import time +import traceback import tower_cli from flask import current_app from rq import Worker, get_current_job @@ -23,6 +24,19 @@ class TaskWorker(Worker): the task status in the CSEntry database """ + def save_exception(self, job, *exc_info): + """Save the exception to the database + + The exception is only saved if it occured before the AWX job was triggered. + If the AWX job failed, we can refer to the logs on AWX. + """ + task = models.Task.query.get(job.id) + if task.awx_job_id is None: + # No AWX job was triggered. An exception occured before. Save it. + task.exception = self._get_safe_exception_string( + traceback.format_exception(*exc_info)) + db.session.commit() + def update_task_attribute(self, job, name, value): # The task is created after enqueueing the job. # If the job is processed very quickly, the task might @@ -43,6 +57,7 @@ class TaskWorker(Worker): def move_to_failed_queue(self, job, *exc_info): self.update_task_attribute(job, 'ended_at', job.ended_at) self.update_task_attribute(job, 'status', models.JobStatus.FAILED) + self.save_exception(job, *exc_info) super().move_to_failed_queue(job, *exc_info) def handle_job_success(self, job, queue, started_job_registry): diff --git a/app/templates/task/view_task.html b/app/templates/task/view_task.html index 2530f33ad675d0350c666743e563073fdc7251d3..4a5afbb92ac21b5a2d685bb74ea6a2c657bdd942 100644 --- a/app/templates/task/view_task.html +++ b/app/templates/task/view_task.html @@ -28,6 +28,10 @@ {% else %} <dd class="col-sm-9">{{ task.awx_job_id }}</dd> {% endif %} + {% if task.exception %} + <dt class="col-sm-3">Exception</dt> + <dd class="col-sm-9"><pre>{{ task.exception }}</pre></dd> + {% endif %} <dt class="col-sm-3">Command</dt> <dd class="col-sm-9">{{ task.command }}</dd> <dt class="col-sm-3">User</dt> diff --git a/migrations/versions/a9442567c6dc_add_exception_to_task_table.py b/migrations/versions/a9442567c6dc_add_exception_to_task_table.py new file mode 100644 index 0000000000000000000000000000000000000000..c3d83798d56961115b8c50854ffbf85dbe9e2e07 --- /dev/null +++ b/migrations/versions/a9442567c6dc_add_exception_to_task_table.py @@ -0,0 +1,24 @@ +"""add exception to task table + +Revision ID: a9442567c6dc +Revises: c0b8036078e7 +Create Date: 2018-07-03 07:17:03.718695 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a9442567c6dc' +down_revision = 'c0b8036078e7' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('task', sa.Column('exception', sa.Text(), nullable=True)) + + +def downgrade(): + op.drop_column('task', 'exception')