Skip to content

Commit

Permalink
contrib: sync-places: add graph sub-command
Browse files Browse the repository at this point in the history
Add a sub-command to sync-places to generate a graphviz graph of the
resources and places in a given environment.

Signed-off-by: Liam Beguin <liambeguin@gmail.com>
  • Loading branch information
liambeguin committed Nov 29, 2023
1 parent c4a0867 commit 791ce4f
Showing 1 changed file with 102 additions and 0 deletions.
102 changes: 102 additions & 0 deletions contrib/sync-places.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import sys
import textwrap
import yaml
import graphviz


def main():
Expand Down Expand Up @@ -145,6 +146,94 @@ async def do_dump(session, args):
with get_file(args.dest, "w", sys.stdout) as f:
yaml.dump(config, f)

async def do_graph(session, args):
'''Generate a graphviz graph of the current configuration.
Graph displays:
- all resources, grouped by groupname and exporter.
- all places, with a list of tags
- solid edges between places and acquired resources
- dotted edges between places and unacquired resources
- edges between resources and places carry the match name if any.
'''
def res_node_attr(name, resource):
return {
'shape': 'plaintext',
'label': f'''<
<table bgcolor="peru">
<tr>
<td border="0" align="left">Resource</td>
</tr>
<tr>
<td port="cls">{resource.cls}</td>
<td port="name" bgcolor="white">{name}</td>
</tr>
</table>>''',
}

def place_node_attr(name, place):
acquired = ''
bgcolor = 'lightblue'
if place.acquired:
bgcolor = 'cornflowerblue'
acquired = f'<td port="user" border="0" align="right"><b>{place.acquired}</b></td>'

tags = '<tr><td border="0" align="left">Tags</td></tr>' if place.tags else ''
for k, v in place.tags.items():
tags += f'<tr><td border="0"></td><td border="0" align="left">{k}={v}</td></tr>'

return {
'shape': 'plaintext',
'label': f'''<
<table bgcolor="{bgcolor}">
<tr>
<td border="0" align="left">Place</td>
{acquired}
</tr>
<tr>
<td port="name" colspan="2" bgcolor="white">{name}</td>
</tr>
{tags}
</table>>''',
}

g = graphviz.Digraph('G')
g.attr(rankdir='LR')

paths = {}
for exporter, groups in session.resources.items():
g_exporter = graphviz.Digraph(f'cluster_{exporter}')
g_exporter.attr(label=exporter)

for group, resources in groups.items():
g_group = graphviz.Digraph(f'cluster_{group}')
g_group.attr(label=group)

for r_name, entry in resources.items():
res_node = f'{exporter}/{group}/{entry.cls}/{r_name}'.replace(':', '_')
paths[res_node] = [exporter, group, entry.cls, r_name]
g_group.node(res_node, **res_node_attr(r_name, entry))

g_exporter.subgraph(g_group)

g.subgraph(g_exporter)

for p_node, place in session.places.items():
g.node(p_node, **place_node_attr(p_node, place))

for m in place.matches:
for node, p in paths.items():
if m.ismatch(p):
g.edge(
f'{node}:name', p_node,
style='solid' if place.acquired else 'dotted',
label=m.rename if m.rename else None,
)

with get_file(args.dest, "w", sys.stdout) as f:
f.write(g.source)


parser = argparse.ArgumentParser(
description="Synchronize Labgrid places",
epilog=textwrap.dedent(
Expand Down Expand Up @@ -209,6 +298,19 @@ async def do_dump(session, args):
)
dump_parser.set_defaults(func=do_dump)

graph_parser = subparsers.add_parser(
"graph",
help="Dump a dot graph of the current configuration",
)
graph_parser.add_argument(
"dest",
metavar="FILE",
nargs="?",
default="-",
help="Destination file. Use '-' for stdout. Default is '%(default)s'",
)
graph_parser.set_defaults(func=do_graph)

args = parser.parse_args()

if args.proxy:
Expand Down

0 comments on commit 791ce4f

Please sign in to comment.