diff --git a/app/models.py b/app/models.py index 965bb638b9dce981caf43d112f1af01c82be4188..4fd09360faffc1bcd298960e04b15628a3442adc 100644 --- a/app/models.py +++ b/app/models.py @@ -327,6 +327,7 @@ class User(db.Model, UserMixin): command=job.get_call_string(), status=JobStatus(job.status), user=self, + depends_on_id=kwargs.get("depends_on", None), ) db.session.add(task) return task @@ -1641,6 +1642,11 @@ class Task(db.Model): nullable=False, default=utils.fetch_current_user_id, ) + depends_on_id = db.Column(postgresql.UUID, db.ForeignKey("task.id")) + + reverse_dependencies = db.relationship( + "Task", backref=db.backref("depends_on", remote_side=[id]) + ) @property def awx_job_url(self): @@ -1672,6 +1678,7 @@ class Task(db.Model): "awx_resource": self.awx_resource, "awx_job_id": self.awx_job_id, "awx_job_url": self.awx_job_url, + "depends_on": self.depends_on_id, "command": self.command, "exception": self.exception, "user": str(self.user), diff --git a/app/static/js/tasks.js b/app/static/js/tasks.js index 5d1730ca798130786f2053bccfe5bec6769f7cbb..80a75d0809e135b300fcad23c0330d5511229218 100644 --- a/app/static/js/tasks.js +++ b/app/static/js/tasks.js @@ -1,5 +1,14 @@ $(document).ready(function() { + function render_task_link(data) { + // render funtion to create link to Task view page + if ( data === null ) { + return data; + } + var url = $SCRIPT_ROOT + "/task/tasks/view/" + data; + return '<a href="' + url + '">' + data + '</a>'; + } + $("#allTasks").on('change', function() { // reload the data from the Ajax source tasks_table.ajax.reload(); @@ -21,12 +30,7 @@ $(document).ready(function() { "columns": [ { data: 'id', render: function(data, type, row) { - // render funtion to create link to Task view page - if ( data === null ) { - return data; - } - var url = $SCRIPT_ROOT + "/task/tasks/view/" + data; - return '<a href="' + url + '">' + data + '</a>'; + return render_task_link(data); } }, { data: 'name' }, @@ -42,6 +46,11 @@ $(document).ready(function() { return '<a href="' + row.awx_job_url + '">' + row.awx_job_id + '</a>'; } }, + { data: 'depends_on', + render: function(data, type, row) { + return render_task_link(data); + } + }, { data: 'command' }, { data: 'user' }, ] diff --git a/app/templates/_helpers.html b/app/templates/_helpers.html index 353685d4d4ae4af70ef5cfeeee4fff497fb8ca64..1bf33fc3a89eac530fc52dd0e0ce6da493b05747 100644 --- a/app/templates/_helpers.html +++ b/app/templates/_helpers.html @@ -97,6 +97,16 @@ {% endfor %} {%- endmacro %} +{% macro link_to_task(task) -%} + <a href="{{ url_for('task.view_task', id_=task.id) }}">{{ task.id }}</a> +{%- endmacro %} + +{% macro link_to_tasks(tasks) -%} + {% for task in tasks %} + {{ link_to_task(task) }} + {% endfor %} +{%- endmacro %} + {% macro render_field(field) -%} {% set field_class = kwargs.pop('class_', '') + ' form-control' %} {% set label_size = kwargs.pop('label_size', '2') %} diff --git a/app/templates/task/tasks.html b/app/templates/task/tasks.html index 2c0c6f865c730b27d34c28f9d3c7e514a07b1c9a..64ea3fd0ac1e9a52f1e82768c1af0354616921ec 100644 --- a/app/templates/task/tasks.html +++ b/app/templates/task/tasks.html @@ -35,6 +35,7 @@ <th>Ended at</th> <th>Status</th> <th>AWX job</th> + <th>Depends on</th> <th>Command</th> <th>User</th> </tr> diff --git a/app/templates/task/view_task.html b/app/templates/task/view_task.html index bfd1a8f7c4c69f58135a46536296316defdc04e6..15ae5fbac0e4ec8164ee4388687d066aa1c21d5b 100644 --- a/app/templates/task/view_task.html +++ b/app/templates/task/view_task.html @@ -1,4 +1,5 @@ {% extends "task/tasks.html" %} +{% from "_helpers.html" import link_to_task, link_to_tasks %} {% block title %}View Task - CSEntry{% endblock %} @@ -30,6 +31,10 @@ {% else %} <dd class="col-sm-9">{{ task.awx_job_id }}</dd> {% endif %} + <dt class="col-sm-3">Depends on</dt> + <dd class="col-sm-9">{{ link_to_task(task.depends_on) }}</dd> + <dt class="col-sm-3">Reverse dependencies</dt> + <dd class="col-sm-9">{{ link_to_tasks(task.reverse_dependencies) }}</dd> {% if task.exception %} <dt class="col-sm-3">Exception</dt> <dd class="col-sm-9"><pre>{{ task.exception }}</pre></dd> diff --git a/app/utils.py b/app/utils.py index a4c2f95dc881ce715a24aec0b3a931f16312a104..6a20b77fd39913a5d9beedb88fa0758d96141f8a 100644 --- a/app/utils.py +++ b/app/utils.py @@ -240,11 +240,16 @@ def trigger_core_services_update(): """ # Start by triggering an inventory update inventory_task = trigger_inventory_update() - # If there is already a core service update in queue, we could probably skip starting a new - # one but we would have to be sure that the inventory sync it depends on didn't already - # start - # Without this optimization, we might trigger a job that won't change anything. - # If jobs can run in parallel this shouldn't be an issue. + # If there is already a core service update depending on this inventory update, + # there is no need to trigger a new one + for reverse_dependency in inventory_task.reverse_dependencies: + if reverse_dependency.name == "trigger_core_services_update": + current_app.logger.info( + f'Already one "trigger_core_services_update" {reverse_dependency} ' + f"depending on the inventory update {inventory_task}. " + "No need to trigger a new one." + ) + return None job_template = current_app.config["AWX_CORE_SERVICES_UPDATE"] resource = current_app.config.get("AWX_CORE_SERVICES_UPDATE_RESOURCE", "job") kwargs = { diff --git a/migrations/versions/cb777d44627f_add_depends_on_field_on_task.py b/migrations/versions/cb777d44627f_add_depends_on_field_on_task.py new file mode 100644 index 0000000000000000000000000000000000000000..e234cbc00c58e5c94ba8dc0d04a12b4d96241fe1 --- /dev/null +++ b/migrations/versions/cb777d44627f_add_depends_on_field_on_task.py @@ -0,0 +1,28 @@ +"""Add depends_on field on Task + +Revision ID: cb777d44627f +Revises: 166572b78449 +Create Date: 2019-03-27 20:51:05.385857 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "cb777d44627f" +down_revision = "166572b78449" +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column("task", sa.Column("depends_on_id", postgresql.UUID(), nullable=True)) + op.create_foreign_key( + op.f("fk_task_depends_on_id_task"), "task", "task", ["depends_on_id"], ["id"] + ) + + +def downgrade(): + op.drop_constraint(op.f("fk_task_depends_on_id_task"), "task", type_="foreignkey") + op.drop_column("task", "depends_on_id")