-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathaws-cfviz
executable file
·113 lines (96 loc) · 4.07 KB
/
aws-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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python2
# original from https://github.com/benbc/cloud-formation-viz/blob/2e4fbcce9863db458c4a1fe043aa2ad3200f8c19/cfviz
# The MIT Licence (MIT)
# Copyright (c) 2015 Ben Butler-Cole
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import json
from numbers import Number
import sys
from compiler.ast import flatten
def main():
template = json.load(sys.stdin)
(graph, edges) = extract_graph(template.get('Description', ''), template['Resources'])
graph['edges'].extend(edges)
handle_terminals(template, graph, 'Parameters', 'same')
handle_terminals(template, graph, 'Outputs', 'same')
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()