1+ #!/usr/bin/env python
2+
3+ # aci2dot.py is free software: you can redistribute it and/or modify
4+ # it under the terms of the GNU General Public License as published by
5+ # the Free Software Foundation, either version 3 of the License, or
6+ # (at your option) any later version.
7+
8+ # This program is distributed in the hope that it will be useful,
9+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ # GNU General Public License for more details.
12+
13+ # You should have received a copy of the GNU General Public License
14+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15+
16+ import argparse
17+ import sys
18+ import json
19+ import os
20+
21+ simple = False
22+ show_attributes = True
23+
24+ gformat = '''
25+ graph [
26+ size="8.27";
27+ ratio="1";
28+ nodesep="0.15";
29+ ranksep="0.5";
30+ #splines="false";
31+ rankdir=LR;
32+ bgcolor="transparent";
33+ ];
34+ node [
35+ shape=box;
36+ style="rounded,filled";
37+ fillcolor=AZURE;
38+ fontname=Helvetica;
39+ ]
40+ edge [
41+ #arrowsize=0.5;
42+ ]
43+ '''
44+
45+ def format_attr (policy ,attr ):
46+
47+ if not show_attributes :
48+ return '<<B>{0}</B>>' .format (policy )
49+
50+ attr_table = '<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="1" CELLPADDING="1">'
51+ attr_table = attr_table + '<TR><TD ALIGN="LEFT" COLSPAN="2"><B>{0}</B></TD><TD></TD></TR>' .format (policy )
52+ attr_table = attr_table + '<TR><TD></TD><TD></TD></TR>'
53+
54+ for k , v in attr .items ():
55+ if k not in ['status' ] and v :
56+ attr_table = attr_table + '<TR><TD ALIGN="LEFT">{0}</TD><TD ALIGN="LEFT">: {1}</TD></TR>' .format (k ,(v [:20 ] + '..' ) if len (v ) > 20 else v )
57+
58+ attr_table = attr_table + '</TABLE>>'
59+
60+ return attr_table
61+
62+ def iterd (d , i ):
63+
64+ #print('# Index: {0}, {1}'.format(i, d))
65+ for k , v in d .items ():
66+
67+ if isinstance (v , dict ):
68+ attr = v .get ("attributes" )
69+
70+ # Write Node Properties
71+ if attr :
72+ print ('{0} [label={1};]' .format (k + str (i ), format_attr (k , attr )))
73+ else :
74+ print ('{0} [label="{0}";]' .format (k + str (i )))
75+
76+ children = v .get ("children" )
77+
78+ if children :
79+ i2 = 0
80+ for child in children :
81+ if not simple : i2 = i2 + 1
82+ for childk , childv in child .items ():
83+ print ('{0} -> {1}' .format (k + str (i ), childk + str (i2 )))
84+ iterd (child , i2 )
85+
86+ def write_gformat ():
87+ with open (".aci2dot" , "wt" ) as text_file :
88+ text_file .write (gformat )
89+
90+ def main ():
91+
92+ global simple
93+ global show_attributes
94+ global gformat
95+ parser = argparse .ArgumentParser (description = 'Export DOT formatted Graph from JSON formatted ACI policy export.\n Convert with: ./aci2dot.py policy.json | dot -o policy.svg -Tsvg' )
96+ parser .add_argument ('policy_file' , type = argparse .FileType ('r' ), help = 'JSON ACI Policy Filename' )
97+ parser .add_argument ('--nr' , action = 'store_true' , help = 'Suppress redundant children' )
98+ parser .add_argument ('--na' , action = 'store_true' , help = "Don't show attributes" )
99+ parser .add_argument ('--write' , action = 'store_true' , help = "Write config template to .aci2dot and exit" )
100+ group = parser .add_mutually_exclusive_group ()
101+ group .add_argument ('--stdout' , action = 'store_true' , help = "Write to STDOUT instead of to file" )
102+ choices = ["svg" , "png" , "pdf" ]
103+ group .add_argument ('--dot' , choices = choices , help = "Also write SVG/PNG/PDF. 'dot' needs to be installed." )
104+
105+ args = parser .parse_args ()
106+
107+ #print(args.dot)
108+ #sys.exit()
109+
110+ if args .write :
111+ write_gformat ()
112+ sys .exit ('Config template written to .aci2dot' )
113+
114+ simple = args .nr
115+ show_attributes = not args .na if not args .nr else False
116+ base_name = (os .path .splitext (args .policy_file .name )[0 ])
117+
118+ try :
119+ data = json .load (args .policy_file )
120+ except :
121+ sys .exit ('JSON File can not be read.' )
122+
123+ try :
124+ with open (".aci2dot" , 'r' ) as config_file :
125+ gformat = config_file .read ()
126+ print ("Graph config read from .aci2dot" , file = sys .stderr )
127+ except IOError :
128+ pass
129+
130+ if not args .stdout :
131+ sys .stdout = open ('{0}.dot' .format (base_name ), 'w' )
132+
133+ print ('strict digraph Policy {' )
134+ print (gformat )
135+ iterd (data , 0 )
136+ print ('}' , flush = True )
137+
138+ if args .dot :
139+ os .system ("dot -T{0} -o{1}.{0} {1}.dot" .format (args .dot , base_name ))
140+ #print(("dot -T{0} -o{1}.svg {1}.dot".format(args.dot, base_name)))
141+ print ("{0} exported to {1}.{0}" .format (args .dot , base_name ), file = sys .stderr )
142+
143+ if __name__ == '__main__' :
144+ main ()
0 commit comments