# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # # See the License for the specific language governing permissions and # limitations under the License. """ A tool that will translate a projects.yaml file into a visualized graph. Usage: universe_dot.py just-project? ... To run make sure pydot (or pydot2) is installed, as well as pyyaml. After these are installed run this like: $ python tools/universe_dot.py reference/projects.yaml \ ironic_universe.dot ironic; $ neato -Tsvg ironic_universe.dot > ironic_universe.svg Then open ironic_universe.svg in your favorite web browser. Or to create the whole openstack universe. $ python tools/universe_dot.py reference/projects.yaml os_universe.dot $ neato -Tsvg os_universe.dot > os_universe.svg Then open os_universe.svg in your favorite web browser. """ import sys import time import pydot import yaml if len(sys.argv) == 1: print(__doc__.strip()) sys.exit(1) projects_file = sys.argv[1] output_dot_file = sys.argv[2] just_projects = set(sys.argv[3:]) print("Reading projects from '%s'" % projects_file) with open(projects_file, 'rb') as fh: projects = yaml.safe_load(fh) project_names = sorted(projects.keys()) if just_projects: print("Restricting to just %s projects" % list(just_projects)) for project_name in list(project_names): if project_name not in just_projects: project_names.remove(project_name) project_names = tuple(project_names) graph_nodes = {} start = time.time() print("Constructing nodes.") print("Please wait...") deliverable_node_attrs = { 'fontsize': '11', } project_node_attrs = { 'fontsize': '11', 'shape': 'diamond', } node_count = 0 edges_needed = 0 for project_name in project_names: node = pydot.Node(name=project_name, **project_node_attrs) node_count += 1 graph_nodes[project_name] = node deliverables = projects[project_name].get('deliverables', []) deliverable_names = sorted(deliverables.keys()) for deliverable_name in deliverable_names: edges_needed += 1 node_path = "%s/%s" % (project_name, deliverable_name) if deliverable_name == project_name: # Avoid creating a self-link, since its actually not, avoid # this by creating a node with a hidden name, and a label that # is its actual name... node_deliverable_node_attrs = dict(deliverable_node_attrs) node_deliverable_node_attrs['name'] = '__%s' % deliverable_name node_deliverable_node_attrs['label'] = deliverable_name else: node_deliverable_node_attrs = dict(deliverable_node_attrs) node_deliverable_node_attrs['name'] = deliverable_name node = pydot.Node(**node_deliverable_node_attrs) graph_nodes[node_path] = node node_count += 1 print("Inserting %s nodes and %s edges into" " a new graph." % (node_count, edges_needed)) print("Please wait...") if just_projects: nice_names = [name.title() for name in sorted(just_projects)] graph_name = '%s Universe' % (" and ".join(nice_names)) else: graph_name = 'OpenStack Universe' graph_kwargs = { 'rankdir': 'LR', 'nodesep': '0.25', 'overlap': 'false', 'ranksep': '0.5', 'splines': 'true', 'ordering': 'in', 'graph_name': graph_name, } graph = pydot.Graph(**graph_kwargs) for project_name in project_names: print(" Inserting nodes for '%s'" % project_name) node = graph_nodes[project_name] graph.add_node(node) deliverables = projects[project_name].get('deliverables', []) deliverable_names = sorted(deliverables.keys()) for deliverable_name in deliverable_names: print(" Inserting node for '%s'" % deliverable_name) node_path = "%s/%s" % (project_name, deliverable_name) deliverable_node = graph_nodes[node_path] graph.add_node(deliverable_node) print(" Inserting edge for '%s' -> '%s'" % (project_name, deliverable_name)) graph.add_edge(pydot.Edge(node, deliverable_node, style='dotted')) end = time.time() print("Finished in %0.2f seconds" % (end - start)) print("Writing graph to '%s'" % output_dot_file) with open(output_dot_file, "w") as fh: fh.write(graph.to_string())