Skip to content

Instantly share code, notes, and snippets.

@AlanCoding
Last active September 28, 2023 13:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlanCoding/bca128a1b391e28819f4020eb9b6536e to your computer and use it in GitHub Desktop.
Save AlanCoding/bca128a1b391e28819f4020eb9b6536e to your computer and use it in GitHub Desktop.

AWX Resource Graph Visualizer

This is a widget hacky thing for visualizing things in AWX which do not otherwise have visualization.

https://github.com/ansible/awx/compare/devel...AlanCoding:inspector?expand=1

SVG here for the job was produced by

dnf install graphviz
/var/lib/awx/venv/awx/bin/pip install graphviz
awx-manage inspect_job --job 243 --file alan --ext svg

The SVG for the roles was produced by

awx-manage inspect_role --role=1520 --file alan --method downup --ext svg

This is where 1520 is the admin role of the organization from the full house script. More on that in the next section

Creating the Example Data

The job is created by a playbook that generates all forms of status.

https://github.com/ansible/test-playbooks/blob/master/gen_host_status.yml

The exact structure will depend heavily on the inventory you use this against.

The RBAC example is done by running the test_full_house script. After this is ran, search for the organization with "Full house" in its name. Then get the admin role for that organization. Exact instructions for running this from the CLI might be somewhat more complicated than these instructions suggest. You need an awxkit environment.

RBAC Relevance

There is some more information in comments in this issue:

ansible/awx#3600

The core idea is that a visualization in order to allow the proliferation of more permissions types.

from awxkit.utils import random_title
class TestAlan(object):
def test_alan(self, factories):
"""Make stuff of every type."""
for i in range(300):
ujts = [
factories.project(),
factories.job_template(),
factories.inventory_source(),
factories.workflow_job_template()
]
factories.ad_hoc_command() # special case
for ujt in ujts:
if hasattr(ujt, 'launch'):
ujt.launch()
else:
ujt.update()
def test_org(self, factories):
"""This makes an organization to test deletion warnings with"""
o = factories.organization(name='Delete Meeeee! {}'.format(random_title()))
print('Creating Resources:')
for res in dir(factories):
if res.startswith('__') or res in ('clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem',
'setdefault', 'update', 'values',
'access_token', 'ad_hoc_command', 'credential_type',
'group', 'host', 'instance_group', 'inventory_source', 'job_template',
'organization', 'workflow_job_template_node'):
continue
print(' {}'.format(res))
obj = getattr(factories, res)(organization=o)
if res != 'user':
assert obj.organization == o.id
if res == 'project':
factories.job_template(project=obj)
def test_full_house(self, factories):
"""Makes every object in an organization and delegates all permissions to everything"""
o = factories.organization(name='Full house!!! {}'.format(random_title()))
print('Adding people to organization roles, org name: {}'.format(o.name))
for role in o.get_related('object_roles').results:
u = factories.user()
role_field = role['name']
o.set_object_roles(u, role_field)
if role_field.lower() not in ('admin', 'member'):
team = factories.team()
o.set_object_roles(team, role_field)
to = team.ds.organization
u2 = factories.user()
to.set_object_roles(u2, 'admin')
u3 = factories.user()
team.set_object_roles(u3, 'member')
u4 = factories.user()
team.set_object_roles(u4, 'admin')
print('Creating Resources:')
for res in dir(factories):
if res.startswith('__') or res in ('clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem',
'setdefault', 'update', 'values',
'access_token', 'ad_hoc_command', 'credential_type',
'group', 'host', 'instance_group', 'inventory_source',
'organization', 'workflow_job_template_node'):
continue
print(' {}'.format(res))
if res == 'job_template':
obj = factories.job_template(project=factories.project(organization=o))
else:
obj = getattr(factories, res)(organization=o)
if res not in ('user', 'job_template'):
assert obj.organization == o.id
if res not in ('user',):
if 'object_roles' not in obj.related:
print(' resource {} does not have roles??? okay?'.format(res))
continue
obj_roles = obj.get_related('object_roles').results
for role in obj_roles:
if res == 'credential':
u = factories.user(organization=o)
team = factories.team(organization=o)
else:
u = factories.user()
team = factories.team()
role_field = role['name']
obj.set_object_roles(u, role_field)
obj.set_object_roles(team, role_field)
to = team.ds.organization
u2 = factories.user()
to.set_object_roles(u2, 'admin')
u3 = factories.user()
team.set_object_roles(u3, 'member')
u4 = factories.user()
team.set_object_roles(u4, 'admin')
def test_inventory_org_admin(self, factories):
o = factories.organization(name='Inventory organization {}'.format(random_title()))
inv = factories.inventory(organization=o)
assert inv.organization == o.id
jt = factories.job_template(inventory=inv)
user = factories.user(organization=o, username='has_inventory_admin_{}'.format(random_title(non_ascii=False)))
o.set_object_roles(user, 'admin')
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
<svg width="874pt" height="962pt"
viewBox="0.00 0.00 873.83 962.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 958)">
<title>%3</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-958 869.8328,-958 869.8328,4 -4,4"/>
<!-- playbook_on_start 4 -->
<g id="node1" class="node">
<title>playbook_on_start 4</title>
<ellipse fill="none" stroke="#000000" cx="783.2901" cy="-342" rx="82.5854" ry="18"/>
<text text-anchor="middle" x="783.2901" y="-338.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_start 4</text>
</g>
<!-- playbook_on_task_start 6 -->
<g id="node2" class="node">
<title>playbook_on_task_start 6</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-855" rx="100.9827" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-851.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 6</text>
</g>
<!-- playbook_on_play_start 5 -->
<g id="node3" class="node">
<title>playbook_on_play_start 5</title>
<ellipse fill="none" stroke="#000000" cx="563.3564" cy="-369" rx="101.2821" ry="18"/>
<text text-anchor="middle" x="563.3564" y="-365.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_play_start 5</text>
</g>
<!-- playbook_on_task_start 6&#45;&gt;playbook_on_play_start 5 -->
<g id="edge1" class="edge">
<title>playbook_on_task_start 6&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M337.5101,-837.0063C360.0176,-812.2189 400.075,-765.4565 425.9654,-720 490.4119,-606.8493 537.6807,-457.5308 555.5381,-396.6355"/>
<polygon fill="#000000" stroke="#000000" points="558.9147,-397.5581 558.3397,-386.9789 552.1919,-395.6076 558.9147,-397.5581"/>
</g>
<!-- playbook_on_play_start 5&#45;&gt;playbook_on_start 4 -->
<g id="edge2" class="edge">
<title>playbook_on_play_start 5&#45;&gt;playbook_on_start 4</title>
<path fill="none" stroke="#000000" d="M646.8175,-358.754C664.4928,-356.5841 683.1395,-354.2949 700.7793,-352.1294"/>
<polygon fill="#000000" stroke="#000000" points="701.5351,-355.563 711.034,-350.8705 700.6821,-348.6151 701.5351,-355.563"/>
</g>
<!-- runner_on_ok 10 -->
<g id="node4" class="node">
<title>runner_on_ok 10</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-936" rx="71.4873" ry="18"/>
<text text-anchor="middle" x="89.692" y="-932.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_ok 10</text>
</g>
<!-- runner_on_ok 10&#45;&gt;playbook_on_task_start 6 -->
<g id="edge3" class="edge">
<title>runner_on_ok 10&#45;&gt;playbook_on_task_start 6</title>
<path fill="none" stroke="#000000" d="M137.3765,-922.3469C151.0091,-918.2542 165.84,-913.62 179.3841,-909 209.8205,-898.6179 243.4693,-885.8118 270.2923,-875.2765"/>
<polygon fill="#000000" stroke="#000000" points="271.8296,-878.4326 279.8471,-871.5065 269.2603,-871.9211 271.8296,-878.4326"/>
</g>
<!-- runner_on_ok 11 -->
<g id="node5" class="node">
<title>runner_on_ok 11</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-882" rx="71.4873" ry="18"/>
<text text-anchor="middle" x="89.692" y="-878.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_ok 11</text>
</g>
<!-- runner_on_ok 11&#45;&gt;playbook_on_task_start 6 -->
<g id="edge4" class="edge">
<title>runner_on_ok 11&#45;&gt;playbook_on_task_start 6</title>
<path fill="none" stroke="#000000" d="M154.6483,-874.4071C176.8319,-871.8141 202.1141,-868.8588 226.1437,-866.0499"/>
<polygon fill="#000000" stroke="#000000" points="226.8268,-869.494 236.3528,-864.8566 226.014,-862.5413 226.8268,-869.494"/>
</g>
<!-- playbook_on_task_start 13 -->
<g id="node6" class="node">
<title>playbook_on_task_start 13</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-693" rx="105.0817" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-689.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 13</text>
</g>
<!-- playbook_on_task_start 13&#45;&gt;playbook_on_play_start 5 -->
<g id="edge5" class="edge">
<title>playbook_on_task_start 13&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M335.178,-674.8094C355.6665,-649.0209 394.1375,-600.2628 425.9654,-558 469.3432,-500.4008 518.72,-431.6706 544.8526,-395.0355"/>
<polygon fill="#000000" stroke="#000000" points="547.7215,-397.0406 550.6744,-386.8656 542.0208,-392.9783 547.7215,-397.0406"/>
</g>
<!-- runner_on_ok 12 -->
<g id="node7" class="node">
<title>runner_on_ok 12</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-828" rx="71.4873" ry="18"/>
<text text-anchor="middle" x="89.692" y="-824.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_ok 12</text>
</g>
<!-- runner_on_ok 12&#45;&gt;playbook_on_task_start 6 -->
<g id="edge6" class="edge">
<title>runner_on_ok 12&#45;&gt;playbook_on_task_start 6</title>
<path fill="none" stroke="#000000" d="M154.6483,-835.5929C176.8319,-838.1859 202.1141,-841.1412 226.1437,-843.9501"/>
<polygon fill="#000000" stroke="#000000" points="226.014,-847.4587 236.3528,-845.1434 226.8268,-840.506 226.014,-847.4587"/>
</g>
<!-- runner_on_skipped 17 -->
<g id="node8" class="node">
<title>runner_on_skipped 17</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-774" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-770.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 17</text>
</g>
<!-- runner_on_skipped 17&#45;&gt;playbook_on_task_start 13 -->
<g id="edge7" class="edge">
<title>runner_on_skipped 17&#45;&gt;playbook_on_task_start 13</title>
<path fill="none" stroke="#000000" d="M141.1223,-759.2177C153.695,-755.4117 167.0713,-751.2 179.3841,-747 209.7098,-736.6557 243.2246,-723.9049 269.9994,-713.3915"/>
<polygon fill="#000000" stroke="#000000" points="271.5224,-716.5533 279.5405,-709.6279 268.9537,-710.0415 271.5224,-716.5533"/>
</g>
<!-- runner_on_skipped 18 -->
<g id="node9" class="node">
<title>runner_on_skipped 18</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-720" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-716.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 18</text>
</g>
<!-- runner_on_skipped 18&#45;&gt;playbook_on_task_start 13 -->
<g id="edge8" class="edge">
<title>runner_on_skipped 18&#45;&gt;playbook_on_task_start 13</title>
<path fill="none" stroke="#000000" d="M167.2485,-710.9343C185.2458,-708.8305 204.6256,-706.5652 223.3518,-704.3763"/>
<polygon fill="#000000" stroke="#000000" points="223.8472,-707.8423 233.3732,-703.2048 223.0345,-700.8896 223.8472,-707.8423"/>
</g>
<!-- runner_on_ok 19 -->
<g id="node10" class="node">
<title>runner_on_ok 19</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-666" rx="71.4873" ry="18"/>
<text text-anchor="middle" x="89.692" y="-662.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_ok 19</text>
</g>
<!-- runner_on_ok 19&#45;&gt;playbook_on_task_start 13 -->
<g id="edge9" class="edge">
<title>runner_on_ok 19&#45;&gt;playbook_on_task_start 13</title>
<path fill="none" stroke="#000000" d="M154.6483,-673.5929C175.9192,-676.0793 200.0391,-678.8987 223.1718,-681.6027"/>
<polygon fill="#000000" stroke="#000000" points="223.0255,-685.1093 233.3642,-682.7941 223.8382,-678.1567 223.0255,-685.1093"/>
</g>
<!-- playbook_on_task_start 20 -->
<g id="node11" class="node">
<title>playbook_on_task_start 20</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-531" rx="105.0817" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-527.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 20</text>
</g>
<!-- playbook_on_task_start 20&#45;&gt;playbook_on_play_start 5 -->
<g id="edge10" class="edge">
<title>playbook_on_task_start 20&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M347.1079,-513.3548C391.1457,-483.9577 479.5245,-424.9613 528.5658,-392.2241"/>
<polygon fill="#000000" stroke="#000000" points="530.5819,-395.0866 536.9558,-386.6235 526.6954,-389.2645 530.5819,-395.0866"/>
</g>
<!-- runner_on_skipped 24 -->
<g id="node12" class="node">
<title>runner_on_skipped 24</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-612" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-608.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 24</text>
</g>
<!-- runner_on_skipped 24&#45;&gt;playbook_on_task_start 20 -->
<g id="edge11" class="edge">
<title>runner_on_skipped 24&#45;&gt;playbook_on_task_start 20</title>
<path fill="none" stroke="#000000" d="M141.1223,-597.2177C153.695,-593.4117 167.0713,-589.2 179.3841,-585 209.7098,-574.6557 243.2246,-561.9049 269.9994,-551.3915"/>
<polygon fill="#000000" stroke="#000000" points="271.5224,-554.5533 279.5405,-547.6279 268.9537,-548.0415 271.5224,-554.5533"/>
</g>
<!-- runner_on_failed 25 -->
<g id="node13" class="node">
<title>runner_on_failed 25</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-558" rx="82.5854" ry="18"/>
<text text-anchor="middle" x="89.692" y="-554.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_failed 25</text>
</g>
<!-- runner_on_failed 25&#45;&gt;playbook_on_task_start 20 -->
<g id="edge12" class="edge">
<title>runner_on_failed 25&#45;&gt;playbook_on_task_start 20</title>
<path fill="none" stroke="#000000" d="M162.4594,-549.4941C181.8921,-547.2226 203.2034,-544.7314 223.714,-542.3339"/>
<polygon fill="#000000" stroke="#000000" points="224.1809,-545.8033 233.7069,-541.1658 223.3681,-538.8506 224.1809,-545.8033"/>
</g>
<!-- runner_on_skipped 26 -->
<g id="node14" class="node">
<title>runner_on_skipped 26</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-504" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-500.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 26</text>
</g>
<!-- runner_on_skipped 26&#45;&gt;playbook_on_task_start 20 -->
<g id="edge13" class="edge">
<title>runner_on_skipped 26&#45;&gt;playbook_on_task_start 20</title>
<path fill="none" stroke="#000000" d="M167.2485,-513.0657C185.2458,-515.1695 204.6256,-517.4348 223.3518,-519.6237"/>
<polygon fill="#000000" stroke="#000000" points="223.0345,-523.1104 233.3732,-520.7952 223.8472,-516.1577 223.0345,-523.1104"/>
</g>
<!-- playbook_on_task_start 27 -->
<g id="node15" class="node">
<title>playbook_on_task_start 27</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-396" rx="105.0817" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-392.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 27</text>
</g>
<!-- playbook_on_task_start 27&#45;&gt;playbook_on_play_start 5 -->
<g id="edge14" class="edge">
<title>playbook_on_task_start 27&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M408.9749,-386.176C427.9018,-384.0703 447.9896,-381.8353 467.1969,-379.6984"/>
<polygon fill="#000000" stroke="#000000" points="467.5984,-383.1754 477.15,-378.5911 466.8243,-376.2183 467.5984,-383.1754"/>
</g>
<!-- runner_on_skipped 30 -->
<g id="node16" class="node">
<title>runner_on_skipped 30</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-450" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-446.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 30</text>
</g>
<!-- runner_on_skipped 30&#45;&gt;playbook_on_task_start 27 -->
<g id="edge15" class="edge">
<title>runner_on_skipped 30&#45;&gt;playbook_on_task_start 27</title>
<path fill="none" stroke="#000000" d="M148.2773,-436.3037C178.8006,-429.1679 216.3939,-420.3792 248.6773,-412.8318"/>
<polygon fill="#000000" stroke="#000000" points="249.5715,-416.2172 258.5121,-410.5326 247.9779,-409.401 249.5715,-416.2172"/>
</g>
<!-- playbook_on_task_start 32 -->
<g id="node17" class="node">
<title>playbook_on_task_start 32</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-342" rx="105.0817" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-338.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 32</text>
</g>
<!-- playbook_on_task_start 32&#45;&gt;playbook_on_play_start 5 -->
<g id="edge16" class="edge">
<title>playbook_on_task_start 32&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M408.9749,-351.824C427.9018,-353.9297 447.9896,-356.1647 467.1969,-358.3016"/>
<polygon fill="#000000" stroke="#000000" points="466.8243,-361.7817 477.15,-359.4089 467.5984,-354.8246 466.8243,-361.7817"/>
</g>
<!-- runner_on_skipped 31 -->
<g id="node18" class="node">
<title>runner_on_skipped 31</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-396" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-392.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 31</text>
</g>
<!-- runner_on_skipped 31&#45;&gt;playbook_on_task_start 27 -->
<g id="edge17" class="edge">
<title>runner_on_skipped 31&#45;&gt;playbook_on_task_start 27</title>
<path fill="none" stroke="#000000" d="M179.6574,-396C188.0336,-396 196.5983,-396 205.1632,-396"/>
<polygon fill="#000000" stroke="#000000" points="205.2065,-399.5001 215.2065,-396 205.2064,-392.5001 205.2065,-399.5001"/>
</g>
<!-- runner_on_failed 35 -->
<g id="node19" class="node">
<title>runner_on_failed 35</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-342" rx="82.5854" ry="18"/>
<text text-anchor="middle" x="89.692" y="-338.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_failed 35</text>
</g>
<!-- runner_on_failed 35&#45;&gt;playbook_on_task_start 32 -->
<g id="edge18" class="edge">
<title>runner_on_failed 35&#45;&gt;playbook_on_task_start 32</title>
<path fill="none" stroke="#000000" d="M172.4297,-342C183.0344,-342 194.0408,-342 205.0488,-342"/>
<polygon fill="#000000" stroke="#000000" points="205.1472,-345.5001 215.1472,-342 205.1472,-338.5001 205.1472,-345.5001"/>
</g>
<!-- playbook_on_task_start 37 -->
<g id="node20" class="node">
<title>playbook_on_task_start 37</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-261" rx="105.0817" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-257.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 37</text>
</g>
<!-- playbook_on_task_start 37&#45;&gt;playbook_on_play_start 5 -->
<g id="edge19" class="edge">
<title>playbook_on_task_start 37&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M357.5737,-278.0179C378.167,-287.9832 404.0164,-301.279 425.9654,-315 442.9244,-325.6016 443.9728,-333.2669 461.9654,-342 470.0286,-345.9137 478.7378,-349.3204 487.5468,-352.2703"/>
<polygon fill="#000000" stroke="#000000" points="486.529,-355.6192 497.1188,-355.2945 488.6378,-348.9444 486.529,-355.6192"/>
</g>
<!-- runner_on_skipped 36 -->
<g id="node21" class="node">
<title>runner_on_skipped 36</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-288" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-284.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 36</text>
</g>
<!-- runner_on_skipped 36&#45;&gt;playbook_on_task_start 32 -->
<g id="edge20" class="edge">
<title>runner_on_skipped 36&#45;&gt;playbook_on_task_start 32</title>
<path fill="none" stroke="#000000" d="M148.2773,-301.6963C178.8006,-308.8321 216.3939,-317.6208 248.6773,-325.1682"/>
<polygon fill="#000000" stroke="#000000" points="247.9779,-328.599 258.5121,-327.4674 249.5715,-321.7828 247.9779,-328.599"/>
</g>
<!-- runner_on_ok 39 -->
<g id="node22" class="node">
<title>runner_on_ok 39</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-234" rx="71.4873" ry="18"/>
<text text-anchor="middle" x="89.692" y="-230.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_ok 39</text>
</g>
<!-- runner_on_ok 39&#45;&gt;playbook_on_task_start 37 -->
<g id="edge21" class="edge">
<title>runner_on_ok 39&#45;&gt;playbook_on_task_start 37</title>
<path fill="none" stroke="#000000" d="M154.6483,-241.5929C175.9192,-244.0793 200.0391,-246.8987 223.1718,-249.6027"/>
<polygon fill="#000000" stroke="#000000" points="223.0255,-253.1093 233.3642,-250.7941 223.8382,-246.1567 223.0255,-253.1093"/>
</g>
<!-- playbook_on_task_start 40 -->
<g id="node23" class="node">
<title>playbook_on_task_start 40</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-180" rx="105.0817" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-176.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 40</text>
</g>
<!-- playbook_on_task_start 40&#45;&gt;playbook_on_play_start 5 -->
<g id="edge22" class="edge">
<title>playbook_on_task_start 40&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M369.9898,-195.926C389.7371,-204.4714 411.2468,-216.8528 425.9654,-234 458.9201,-272.3925 425.1053,-307.3397 461.9654,-342 466.3862,-346.1569 471.3733,-349.6892 476.7044,-352.689"/>
<polygon fill="#000000" stroke="#000000" points="475.4449,-355.9702 485.9621,-357.251 478.5391,-349.6912 475.4449,-355.9702"/>
</g>
<!-- runner_on_skipped 43 -->
<g id="node24" class="node">
<title>runner_on_skipped 43</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-180" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-176.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 43</text>
</g>
<!-- runner_on_skipped 43&#45;&gt;playbook_on_task_start 40 -->
<g id="edge23" class="edge">
<title>runner_on_skipped 43&#45;&gt;playbook_on_task_start 40</title>
<path fill="none" stroke="#000000" d="M179.6574,-180C188.0336,-180 196.5983,-180 205.1632,-180"/>
<polygon fill="#000000" stroke="#000000" points="205.2065,-183.5001 215.2065,-180 205.2064,-176.5001 205.2065,-183.5001"/>
</g>
<!-- runner_on_skipped 44 -->
<g id="node25" class="node">
<title>runner_on_skipped 44</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-126" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-122.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 44</text>
</g>
<!-- runner_on_skipped 44&#45;&gt;playbook_on_task_start 40 -->
<g id="edge24" class="edge">
<title>runner_on_skipped 44&#45;&gt;playbook_on_task_start 40</title>
<path fill="none" stroke="#000000" d="M148.2773,-139.6963C178.8006,-146.8321 216.3939,-155.6208 248.6773,-163.1682"/>
<polygon fill="#000000" stroke="#000000" points="247.9779,-166.599 258.5121,-165.4674 249.5715,-159.7828 247.9779,-166.599"/>
</g>
<!-- playbook_on_task_start 45 -->
<g id="node26" class="node">
<title>playbook_on_task_start 45</title>
<ellipse fill="none" stroke="#000000" cx="320.6747" cy="-72" rx="105.0817" ry="18"/>
<text text-anchor="middle" x="320.6747" y="-68.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_task_start 45</text>
</g>
<!-- playbook_on_task_start 45&#45;&gt;playbook_on_play_start 5 -->
<g id="edge25" class="edge">
<title>playbook_on_task_start 45&#45;&gt;playbook_on_play_start 5</title>
<path fill="none" stroke="#000000" d="M354.2558,-89.142C378.0685,-103.0832 408.7415,-125.0103 425.9654,-153 470.78,-225.8262 403.7171,-279.397 461.9654,-342 465.8431,-346.1676 470.2849,-349.72 475.0953,-352.7453"/>
<polygon fill="#000000" stroke="#000000" points="473.6519,-355.9442 484.11,-357.6408 476.9925,-349.7927 473.6519,-355.9442"/>
</g>
<!-- runner_on_skipped 48 -->
<g id="node27" class="node">
<title>runner_on_skipped 48</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-72" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-68.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 48</text>
</g>
<!-- runner_on_skipped 48&#45;&gt;playbook_on_task_start 45 -->
<g id="edge26" class="edge">
<title>runner_on_skipped 48&#45;&gt;playbook_on_task_start 45</title>
<path fill="none" stroke="#000000" d="M179.6574,-72C188.0336,-72 196.5983,-72 205.1632,-72"/>
<polygon fill="#000000" stroke="#000000" points="205.2065,-75.5001 215.2065,-72 205.2064,-68.5001 205.2065,-75.5001"/>
</g>
<!-- playbook_on_stats 50 -->
<g id="node28" class="node">
<title>playbook_on_stats 50</title>
<ellipse fill="none" stroke="#000000" cx="563.3564" cy="-315" rx="87.1846" ry="18"/>
<text text-anchor="middle" x="563.3564" y="-311.3" font-family="Times,serif" font-size="14.00" fill="#000000">playbook_on_stats 50</text>
</g>
<!-- playbook_on_stats 50&#45;&gt;playbook_on_start 4 -->
<g id="edge27" class="edge">
<title>playbook_on_stats 50&#45;&gt;playbook_on_start 4</title>
<path fill="none" stroke="#000000" d="M638.43,-324.2164C658.6417,-326.6976 680.6079,-329.3943 701.1814,-331.92"/>
<polygon fill="#000000" stroke="#000000" points="700.8034,-335.3998 711.1554,-333.1444 701.6564,-328.4519 700.8034,-335.3998"/>
</g>
<!-- runner_on_skipped 49 -->
<g id="node29" class="node">
<title>runner_on_skipped 49</title>
<ellipse fill="none" stroke="#000000" cx="89.692" cy="-18" rx="89.8845" ry="18"/>
<text text-anchor="middle" x="89.692" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">runner_on_skipped 49</text>
</g>
<!-- runner_on_skipped 49&#45;&gt;playbook_on_task_start 45 -->
<g id="edge28" class="edge">
<title>runner_on_skipped 49&#45;&gt;playbook_on_task_start 45</title>
<path fill="none" stroke="#000000" d="M148.2773,-31.6963C178.8006,-38.8321 216.3939,-47.6208 248.6773,-55.1682"/>
<polygon fill="#000000" stroke="#000000" points="247.9779,-58.599 258.5121,-57.4674 249.5715,-51.7828 247.9779,-58.599"/>
</g>
</g>
</svg>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment