|
| 1 | +import itertools |
| 2 | +from collections import defaultdict |
| 3 | +import datetime |
| 4 | +import sys |
| 5 | + |
| 6 | +from leapp.exceptions import CommandError, LeappError |
| 7 | +from leapp.logger import configure_logger |
| 8 | +from leapp.repository.scan import find_and_scan_repositories |
| 9 | +from leapp.snactor.context import with_snactor_context |
| 10 | +from leapp.utils.clicmd import command, command_arg |
| 11 | +from leapp.utils.repository import find_repository_basedir, requires_repository |
| 12 | + |
| 13 | +_LONG_DESCRIPTION = ''' |
| 14 | +Outputs the graph of dependencies between actors formed by producer/consumer their relations in the DOT langauge. |
| 15 | +The DOT language allows easy visualization of the graph. |
| 16 | +''' |
| 17 | + |
| 18 | + |
| 19 | +@command('show-dependencies', |
| 20 | + help='Print dependency graph of a workflow in the DOT language.', |
| 21 | + description=_LONG_DESCRIPTION) |
| 22 | +@command_arg('workflow_name') |
| 23 | +@requires_repository |
| 24 | +@with_snactor_context |
| 25 | +def cli(params): |
| 26 | + start = datetime.datetime.utcnow() |
| 27 | + configure_logger() |
| 28 | + repository = find_and_scan_repositories(find_repository_basedir('.'), include_locals=True) |
| 29 | + try: |
| 30 | + repository.load() |
| 31 | + except LeappError as exc: |
| 32 | + sys.stderr.write(exc.message) |
| 33 | + sys.stderr.write('\n') |
| 34 | + sys.exit(1) |
| 35 | + |
| 36 | + workflow = repository.lookup_workflow(params.workflow_name) |
| 37 | + if not workflow: |
| 38 | + raise CommandError('Could not find any workflow named "{}"'.format(params.name)) |
| 39 | + |
| 40 | + workflow_instance = workflow() |
| 41 | + |
| 42 | + # Information about message consuments and producers: |
| 43 | + dependency_graph = defaultdict(lambda: {'producers': set(), 'consumers': set()}) |
| 44 | + |
| 45 | + for staged_phase in workflow_instance.phase_actors: |
| 46 | + # staged_phase = ('name' <str>, 'Before' <PhaseActors>, 'Main' <PhaseActors>, 'After' <PhaseActors>) |
| 47 | + before_stage, main_stage, after_stage = staged_phase[1:] |
| 48 | + |
| 49 | + for actor in itertools.chain(before_stage.actors, main_stage.actors, after_stage.actors): |
| 50 | + for consumed_message in actor.consumes: |
| 51 | + dependency_graph[consumed_message.__name__]['consumers'].add(actor.name) |
| 52 | + for produced_message in actor.produces: |
| 53 | + dependency_graph[produced_message.__name__]['producents'].add(actor.name) |
| 54 | + |
| 55 | + # Display the dependency graph in the DOT format. We don't want to add dependencies on another library, |
| 56 | + # so we do the formatting ourselves since its pretty straightforward |
| 57 | + dot_output_lines = ['digraph LeappDependencyGraph {'] |
| 58 | + |
| 59 | + for message, message_info in dependency_graph.items(): |
| 60 | + for producent, consument in itertools.product(message_info['producents'], message_info['consuments']): |
| 61 | + output_line = ' "{0}" -> "{1}" [label="{2}"]'.format(producent, consument, message) |
| 62 | + dot_output_lines.append(output_line) |
| 63 | + |
| 64 | + dot_output_lines.append('}') |
| 65 | + dot_output = '\n'.join(dot_output_lines) + '\n' |
| 66 | + print(dot_output, end='') |
0 commit comments