Imported Upstream version 16.07.2
[deb_dpdk.git] / examples / ip_pipeline / config / diagram-generator.py
1 #!/usr/bin/env python
2
3 #   BSD LICENSE
4 #
5 #   Copyright(c) 2016 Intel Corporation. All rights reserved.
6 #   All rights reserved.
7 #
8 #   Redistribution and use in source and binary forms, with or without
9 #   modification, are permitted provided that the following conditions
10 #   are met:
11 #
12 #     * Redistributions of source code must retain the above copyright
13 #       notice, this list of conditions and the following disclaimer.
14 #     * Redistributions in binary form must reproduce the above copyright
15 #       notice, this list of conditions and the following disclaimer in
16 #       the documentation and/or other materials provided with the
17 #       distribution.
18 #     * Neither the name of Intel Corporation nor the names of its
19 #       contributors may be used to endorse or promote products derived
20 #       from this software without specific prior written permission.
21 #
22 #   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 #   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 #   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 #   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 #   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 #   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 #   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 #   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 #   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34 #
35 # This script creates a visual representation for a configuration file used by
36 # the DPDK ip_pipeline application.
37 #
38 # The input configuration file is translated to an output file in DOT syntax,
39 # which is then used to create the image file using graphviz (www.graphviz.org).
40 #
41
42 from __future__ import print_function
43 import argparse
44 import re
45 import os
46
47 #
48 # Command to generate the image file
49 #
50 DOT_COMMAND = 'dot -Gsize=20,30 -Tpng %s > %s'
51
52 #
53 # Layout of generated DOT file
54 #
55 DOT_INTRO = \
56     '#\n# Command to generate image file:\n# \t%s\n#\n\n'
57 DOT_GRAPH_BEGIN = \
58     'digraph g {\n  graph [ splines = true rankdir = "LR" ]\n'
59 DOT_NODE_LINK_RX = \
60     '  "%s RX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
61 DOT_NODE_LINK_TX = \
62     '  "%s TX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
63 DOT_NODE_KNI_RX = \
64     '  "%s RX" [ shape = box style = filled fillcolor = orange ]\n'
65 DOT_NODE_KNI_TX = \
66     '  "%s TX" [ shape = box style = filled fillcolor = orange ]\n'
67 DOT_NODE_TAP_RX = \
68     '  "%s RX" [ shape = box style = filled fillcolor = gold ]\n'
69 DOT_NODE_TAP_TX = \
70     '  "%s TX" [ shape = box style = filled fillcolor = gold ]\n'
71 DOT_NODE_SOURCE = \
72     '  "%s" [ shape = box style = filled fillcolor = darkgreen ]\n'
73 DOT_NODE_SINK = \
74     '  "%s" [ shape = box style = filled fillcolor = peachpuff ]\n'
75 DOT_NODE_PIPELINE = \
76     '  "%s" [ shape = box style = filled fillcolor = royalblue ]\n'
77 DOT_EDGE_PKTQ = \
78     '  "%s" -> "%s" [ label = "%s" color = gray ]\n'
79 DOT_GRAPH_END = \
80     '}\n'
81
82 # Relationships between the graph nodes and the graph edges:
83 #
84 # Edge ID | Edge Label | Writer Node | Reader Node   | Dependencies
85 # --------+------------+-------------+---------------+--------------
86 # RXQx.y  | RXQx.y     | LINKx       | PIPELINEz     | LINKx
87 # TXQx.y  | TXQx.y     | PIPELINEz   | LINKx         | LINKx
88 # SWQx    | SWQx       | PIPELINEy   | PIPELINEz     | -
89 # TMx     | TMx        | PIPELINEy   | PIPELINEz     | LINKx
90 # KNIx RX | KNIx       | KNIx RX     | PIPELINEy     | KNIx, LINKx
91 # KNIx TX | KNIx       | PIPELINEy   | KNIx TX       | KNIx, LINKx
92 # TAPx RX | TAPx       | TAPx RX     | PIPELINEy     | TAPx
93 # TAPx TX | TAPx       | PIPELINEy   | TAPx TX       | TAPx
94 # SOURCEx | SOURCEx    | SOURCEx     | PIPELINEy     | SOURCEx
95 # SINKx   | SINKx      | PIPELINEy   | SINKx         | SINKx
96
97 #
98 # Parse the input configuration file to detect the graph nodes and edges
99 #
100 def process_config_file(cfgfile):
101     edges = {}
102     links = set()
103     knis = set()
104     taps = set()
105     sources = set()
106     sinks = set()
107     pipelines = set()
108     pipeline = ''
109
110     dotfile = cfgfile + '.txt'
111     imgfile = cfgfile + '.png'
112
113     #
114     # Read configuration file
115     #
116     lines = open(cfgfile, 'r')
117     for line in lines:
118         # Remove any leading and trailing white space characters
119         line = line.strip()
120
121         # Remove any comment at end of line
122         line, sep, tail = line.partition(';')
123
124         # Look for next "PIPELINE" section
125         match = re.search(r'\[(PIPELINE\d+)\]', line)
126         if match:
127             pipeline = match.group(1)
128             continue
129
130         # Look for next "pktq_in" section entry
131         match = re.search(r'pktq_in\s*=\s*(.+)', line)
132         if match:
133             pipelines.add(pipeline)
134             for q in re.findall('\S+', match.group(1)):
135                 match_rxq = re.search(r'^RXQ(\d+)\.\d+$', q)
136                 match_swq = re.search(r'^SWQ\d+$', q)
137                 match_tm = re.search(r'^TM(\d+)$', q)
138                 match_kni = re.search(r'^KNI(\d+)$', q)
139                 match_tap = re.search(r'^TAP\d+$', q)
140                 match_source = re.search(r'^SOURCE\d+$', q)
141
142                 # Set ID for the current packet queue (graph edge)
143                 q_id = ''
144                 if match_rxq or match_swq or match_tm or match_source:
145                     q_id = q
146                 elif match_kni or match_tap:
147                     q_id = q + ' RX'
148                 else:
149                     print('Error: Unrecognized pktq_in element "%s"' % q)
150                     return
151
152                 # Add current packet queue to the set of graph edges
153                 if q_id not in edges:
154                     edges[q_id] = {}
155                 if 'label' not in edges[q_id]:
156                     edges[q_id]['label'] = q
157                 if 'readers' not in edges[q_id]:
158                     edges[q_id]['readers'] = []
159                 if 'writers' not in edges[q_id]:
160                     edges[q_id]['writers'] = []
161
162                 # Add reader for the new edge
163                 edges[q_id]['readers'].append(pipeline)
164
165                 # Check for RXQ
166                 if match_rxq:
167                     link = 'LINK' + str(match_rxq.group(1))
168                     edges[q_id]['writers'].append(link + ' RX')
169                     links.add(link)
170                     continue
171
172                 # Check for SWQ
173                 if match_swq:
174                     continue
175
176                 # Check for TM
177                 if match_tm:
178                     link = 'LINK' + str(match_tm.group(1))
179                     links.add(link)
180                     continue
181
182                 # Check for KNI
183                 if match_kni:
184                     link = 'LINK' + str(match_kni.group(1))
185                     edges[q_id]['writers'].append(q_id)
186                     knis.add(q)
187                     links.add(link)
188                     continue
189
190                 # Check for TAP
191                 if match_tap:
192                     edges[q_id]['writers'].append(q_id)
193                     taps.add(q)
194                     continue
195
196                 # Check for SOURCE
197                 if match_source:
198                     edges[q_id]['writers'].append(q)
199                     sources.add(q)
200                     continue
201
202                 continue
203
204         # Look for next "pktq_out" section entry
205         match = re.search(r'pktq_out\s*=\s*(.+)', line)
206         if match:
207             for q in re.findall('\S+', match.group(1)):
208                 match_txq = re.search(r'^TXQ(\d+)\.\d+$', q)
209                 match_swq = re.search(r'^SWQ\d+$', q)
210                 match_tm = re.search(r'^TM(\d+)$', q)
211                 match_kni = re.search(r'^KNI(\d+)$', q)
212                 match_tap = re.search(r'^TAP(\d+)$', q)
213                 match_sink = re.search(r'^SINK(\d+)$', q)
214
215                 # Set ID for the current packet queue (graph edge)
216                 q_id = ''
217                 if match_txq or match_swq or match_tm or match_sink:
218                     q_id = q
219                 elif match_kni or match_tap:
220                     q_id = q + ' TX'
221                 else:
222                     print('Error: Unrecognized pktq_out element "%s"' % q)
223                     return
224
225                 # Add current packet queue to the set of graph edges
226                 if q_id not in edges:
227                     edges[q_id] = {}
228                 if 'label' not in edges[q_id]:
229                     edges[q_id]['label'] = q
230                 if 'readers' not in edges[q_id]:
231                     edges[q_id]['readers'] = []
232                 if 'writers' not in edges[q_id]:
233                     edges[q_id]['writers'] = []
234
235                 # Add writer for the new edge
236                 edges[q_id]['writers'].append(pipeline)
237
238                 # Check for TXQ
239                 if match_txq:
240                     link = 'LINK' + str(match_txq.group(1))
241                     edges[q_id]['readers'].append(link + ' TX')
242                     links.add(link)
243                     continue
244
245                 # Check for SWQ
246                 if match_swq:
247                     continue
248
249                 # Check for TM
250                 if match_tm:
251                     link = 'LINK' + str(match_tm.group(1))
252                     links.add(link)
253                     continue
254
255                 # Check for KNI
256                 if match_kni:
257                     link = 'LINK' + str(match_kni.group(1))
258                     edges[q_id]['readers'].append(q_id)
259                     knis.add(q)
260                     links.add(link)
261                     continue
262
263                 # Check for TAP
264                 if match_tap:
265                     edges[q_id]['readers'].append(q_id)
266                     taps.add(q)
267                     continue
268
269                 # Check for SINK
270                 if match_sink:
271                     edges[q_id]['readers'].append(q)
272                     sinks.add(q)
273                     continue
274
275                 continue
276
277     #
278     # Write DOT file
279     #
280     print('Creating DOT file "%s" ...' % dotfile)
281     dot_cmd = DOT_COMMAND % (dotfile, imgfile)
282     file = open(dotfile, 'w')
283     file.write(DOT_INTRO % dot_cmd)
284     file.write(DOT_GRAPH_BEGIN)
285
286     # Write the graph nodes to the DOT file
287     for l in sorted(links):
288         file.write(DOT_NODE_LINK_RX % l)
289         file.write(DOT_NODE_LINK_TX % l)
290     for k in sorted(knis):
291         file.write(DOT_NODE_KNI_RX % k)
292         file.write(DOT_NODE_KNI_TX % k)
293     for t in sorted(taps):
294         file.write(DOT_NODE_TAP_RX % t)
295         file.write(DOT_NODE_TAP_TX % t)
296     for s in sorted(sources):
297         file.write(DOT_NODE_SOURCE % s)
298     for s in sorted(sinks):
299         file.write(DOT_NODE_SINK % s)
300     for p in sorted(pipelines):
301         file.write(DOT_NODE_PIPELINE % p)
302
303     # Write the graph edges to the DOT file
304     for q in sorted(edges.keys()):
305         rw = edges[q]
306         if 'writers' not in rw:
307             print('Error: "%s" has no writer' % q)
308             return
309         if 'readers' not in rw:
310             print('Error: "%s" has no reader' % q)
311             return
312         for w in rw['writers']:
313             for r in rw['readers']:
314                 file.write(DOT_EDGE_PKTQ % (w, r, rw['label']))
315
316     file.write(DOT_GRAPH_END)
317     file.close()
318
319     #
320     # Execute the DOT command to create the image file
321     #
322     print('Creating image file "%s" ...' % imgfile)
323     if os.system('which dot > /dev/null'):
324         print('Error: Unable to locate "dot" executable.' \
325             'Please install the "graphviz" package (www.graphviz.org).')
326         return
327
328     os.system(dot_cmd)
329
330
331 if __name__ == '__main__':
332     parser = argparse.ArgumentParser(description=\
333         'Create diagram for IP pipeline configuration file.')
334
335     parser.add_argument(
336         '-f',
337         '--file',
338         help='input configuration file (e.g. "ip_pipeline.cfg")',
339         required=True)
340
341     args = parser.parse_args()
342
343     process_config_file(args.file)