ccb73f3b21
... because this tool may not be run with Python 2. This allows us to reduce additional dependencies. Change-Id: I21f16361d8678ea5eb10966fde75228dcec8f861
142 lines
4.6 KiB
Python
142 lines
4.6 KiB
Python
# 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 <projects-file> <output-file> 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())
|