forked from benbc/cloud-formation-viz
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcfviz
executable file
·93 lines (82 loc) · 2.98 KB
/
cfviz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python
import json
from numbers import Number
import sys
from compiler.ast import flatten
def main():
if sys.argv[1:2] != []:
with open(sys.argv[1]) as h:
template = json.load(h)
else:
template = json.load(sys.stdin)
(graph, edges) = extract_graph(template.get('Description', ''), template['Resources'])
graph['edges'].extend(edges)
handle_terminals(template, graph, 'Parameters', 'source')
handle_terminals(template, graph, 'Outputs', 'sink')
graph['subgraphs'].append(handle_psuedo_params(graph['edges']))
render(graph)
def handle_terminals(template, graph, name, rank):
if name in template:
(subgraph, edges) = extract_graph(name, template[name])
subgraph['rank'] = rank
subgraph['style'] = 'filled,rounded'
graph['subgraphs'].append(subgraph)
graph['edges'].extend(edges)
def handle_psuedo_params(edges):
graph = {'name': 'Psuedo Parameters', 'nodes': [], 'edges': [], 'subgraphs': []}
graph['shape'] = 'ellipse'
params = set()
for e in edges:
if e['from'].startswith(u'AWS::'):
params.add(e['from'])
graph['nodes'].extend({'name': n} for n in params)
return graph
def extract_graph(name, elem):
graph = {'name': name, 'nodes': [], 'edges': [], 'subgraphs': []}
edges = []
for item, details in elem.iteritems():
graph['nodes'].append({'name': item})
edges.extend(flatten(find_refs(item, details)))
return (graph, edges)
def find_refs(context, elem):
if isinstance(elem, dict):
refs = []
for k, v in elem.iteritems():
if unicode(k) == unicode('Ref'):
assert isinstance(v, basestring), 'Expected a string: %s' % v
refs.append({'from': v, 'to': context})
elif unicode(k) == unicode('Fn::GetAtt'):
assert isinstance(v, list), 'Expected a list: %s' % v
refs.append({'from': v[0], 'to': context})
else:
refs.extend(find_refs(context, v))
return refs
elif isinstance(elem, list):
return map(lambda e: find_refs(context, e), elem)
elif isinstance(elem, basestring):
return []
elif isinstance(elem, bool):
return []
elif isinstance(elem, Number):
return []
else:
raise AssertionError('Unexpected type: %s' % elem)
def render(graph, subgraph=False):
print '%s "%s" {' % ('subgraph' if subgraph else 'digraph', graph['name'])
print 'labeljust=l;'
print 'node [shape={}];'.format(graph.get('shape', 'box'))
if 'style' in graph:
print 'node [style="%s"]' % graph['style']
if 'rank' in graph:
print 'rank=%s' % graph['rank']
for n in graph['nodes']:
print '"%s"' % n['name']
for s in graph['subgraphs']:
render(s, True)
for e in graph['edges']:
print '"%s" -> "%s";' % (e['from'], e['to'])
print '}'
def debug(*s):
print >>sys.stderr, s
if __name__ == '__main__':
main()