Skip to content

Commit

Permalink
add example workflow run to docs
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismeyersfsu authored and mabashian committed Nov 27, 2018
1 parent 28a4bbb commit 2eeca3c
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 3 deletions.
6 changes: 4 additions & 2 deletions awx/main/scheduler/dag_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def __len__(self):
def __iter__(self):
return self.nodes.__iter__()

def generate_graphviz_plot(self):
def generate_graphviz_plot(self, file_name="/awx_devel/graph.gv"):
def run_status(obj):
dnr = "RUN"
status = "NA"
Expand All @@ -83,6 +83,8 @@ def run_status(obj):
color = 'green'
elif status == 'failed':
color = 'red'
elif obj.do_not_run is True:
color = 'gray'
doc += "%s [color = %s]\n" % (
run_status(n['node_object']),
color
Expand All @@ -96,7 +98,7 @@ def run_status(obj):
label,
)
doc += "}\n"
gv_file = open('/awx_devel/graph.gv', 'w')
gv_file = open(file_name, 'w')
gv_file.write(doc)
gv_file.close()

Expand Down
46 changes: 46 additions & 0 deletions awx/main/tests/unit/scheduler/test_dag_workflow.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
import uuid
import os

from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_text
Expand Down Expand Up @@ -270,3 +271,48 @@ def test_cancel_still_runs_children(self, workflow_dag_canceled):
g.mark_dnr_nodes()

assert set([nodes[1], nodes[2]]) == set(g.bfs_nodes_to_run())


@pytest.mark.skip(reason="Run manually to re-generate doc images")
class TestDocsExample():
@pytest.fixture
def complex_dag(self, wf_node_generator):
g = WorkflowDAG()
nodes = [wf_node_generator() for i in range(10)]
map(lambda n: g.add_node(n), nodes)

g.add_edge(nodes[0], nodes[1], "failure_nodes")
g.add_edge(nodes[0], nodes[2], "success_nodes")
g.add_edge(nodes[0], nodes[3], "always_nodes")
g.add_edge(nodes[1], nodes[4], "success_nodes")
g.add_edge(nodes[1], nodes[5], "failure_nodes")

g.add_edge(nodes[2], nodes[6], "failure_nodes")
g.add_edge(nodes[3], nodes[6], "success_nodes")
g.add_edge(nodes[4], nodes[6], "always_nodes")

g.add_edge(nodes[6], nodes[7], "always_nodes")
g.add_edge(nodes[6], nodes[8], "success_nodes")
g.add_edge(nodes[6], nodes[9], "failure_nodes")

return (g, nodes)

def test_dnr_step(self, complex_dag):
(g, nodes) = complex_dag
base_dir = '/awx_devel'

g.generate_graphviz_plot(file_name=os.path.join(base_dir, "workflow_step0.gv"))
nodes[0].job = Job(status='successful')
g.mark_dnr_nodes()
g.generate_graphviz_plot(file_name=os.path.join(base_dir, "workflow_step1.gv"))
nodes[2].job = Job(status='successful')
nodes[3].job = Job(status='successful')
g.mark_dnr_nodes()
g.generate_graphviz_plot(file_name=os.path.join(base_dir, "workflow_step2.gv"))
nodes[6].job = Job(status='failed')
g.mark_dnr_nodes()
g.generate_graphviz_plot(file_name=os.path.join(base_dir, "workflow_step3.gv"))
nodes[7].job = Job(status='successful')
nodes[9].job = Job(status='successful')
g.mark_dnr_nodes()
g.generate_graphviz_plot(file_name=os.path.join(base_dir, "workflow_step4.gv"))
Binary file added docs/img/workflow_step0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/workflow_step1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/workflow_step2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/workflow_step3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/workflow_step4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 28 additions & 1 deletion docs/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ As stated, workflow job templates can be created with populated `extra_vars`. Th

Job resources spawned by workflow jobs are needed by workflow to run correctly. Therefore deletion of spawned job resources is blocked while the underlying workflow job is executing.

Other than success and failure, a workflow spawned job resource can also end with status 'error' and 'canceled'. When a workflow spawned job resource errors, it is treated the same as failure. Canceling a workflow spawned job resource is also treated as a failure. If the unified job template of the node is null (which could be a result of deleting the unified job template or copying a workflow when the user lacks necessary permissions to use the resource), then the branch should stop executing in this case as well.
Other than success and failure, a workflow spawned job resource can also end with status 'error' and 'canceled'. When a workflow spawned job resource errors or is canceled, it is treated the same as failure. If the unified job template of the node is null (which could be a result of deleting the unified job template or copying a workflow when the user lacks necessary permissions to use the resource), then the node will be treated as 'failed' and the failure paths will continue to execute.

A workflow job itself can also be canceled. In this case all its spawned job resources will be canceled if cancelable and following paths stop executing.

Expand Down Expand Up @@ -92,6 +92,33 @@ Workflow jobs cannot be copied directly, instead a workflow job is implicitly co
### Artifacts
Artifact support starts in Ansible and is carried through in Tower. The `set_stats` module is invoked by users, in a playbook, to register facts. Facts are passed in via `data:` argument. Note that the default `set_stats` parameters are the correct ones to work with Tower (i.e. `per_host: no`). Now that facts are registered, we will describe how facts are used. In Ansible, registered facts are "returned" to the callback plugin(s) via the `playbook_on_stats` event. Ansible users can configure whether or not they want the facts displayed through the global `show_custom_stats` configuration. Note that the `show_custom_stats` does not effect the artifacting feature of Tower. This only controls the displaying of `set_stats` fact data in Ansible output (also the output in Ansible playbooks ran in Tower). Tower uses a custom callback plugin that gathers the fact data set via `set_stats` in the `playbook_on_stats` handler and "ships" it back to Tower, saves it in the database, and makes it available on the job endpoint via the variable `artifacts`. The semantics and usage of `artifacts` throughout a workflow is described elsewhere in this document.

### Workflow Run Example
To best understand the nuances of workflow run logic we will look at an example workflow run as it progresses through the 'running' state. In the workflow examples below nodes are labeled `<do_not_run, job_status, node_id>` where `do_not_run` can be `RUN` or `DNR` where `DNR` means 'do not run the node' and `RUN` which means will run the node. Nodes start out with `do_not_run = False` depicted as `RUN` in the pictures below. When nodes are known to not run they will be marked `DNR` and the state will not change. `job_status` is the job's status associated with the node. `node_id` is the unique id for the workflow job node.

<p align="center">
<img src="img/workflow_step0.png">
Workflow before running has started.
</p>
<p align="center">
<img src="img/workflow_step1.png">
Root nodes are selected to run. A root node is a node with no incoming nodes. Node 0 is selected to run and results in a status of 'successful'. Nodes 1, 4, and 5 are marked 'DNR' because they are in the failure path. Node 6 is not marked 'DNR' because nodes 2 and 3 may run and result and node 6 running. The same reasoning is why nodes 7, 8, 9 are not marked 'DNR'.
</p>
<p align="center">
<img src="img/workflow_step2.png">
Nodes 2 and 3 are selected to run and their job results are both 'successful'. Node 6 is not marked 'DNR' because node 3 will trigger node 6.
</p>
<p align="center">
<img src="img/workflow_step3.png">
Node 6 is selected to run and the job results in 'failed'. Node 8 is marked 'DNR' because of the success path. Nodes 7 and 8 will be ran in the next cycle.
</p>
<p align="center">
<img src="img/workflow_step4.png">
Node 7 and 8 are selected to run and their job results are both 'successful'.
</p>

The resulting state of the workflow job run above would be 'successful'. Although individual nodes fail, the overall workflow job status is 'successful' because all individual node failures have error handling paths ('failed_nodes' or 'always_nodes').


## Test Coverage
### CRUD-related
* Verify that CRUD operations on all workflow resources are working properly. Note workflow job nodes cannot be created or deleted independently, but verifications are needed to make sure when a workflow job is deleted, all its related workflow job nodes are deleted.
Expand Down

0 comments on commit 2eeca3c

Please sign in to comment.