New upstream version 18.02
[deb_dpdk.git] / examples / ip_pipeline / config / diagram-generator.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2016 Intel Corporation
4
5 #
6 # This script creates a visual representation for a configuration file used by
7 # the DPDK ip_pipeline application.
8 #
9 # The input configuration file is translated to an output file in DOT syntax,
10 # which is then used to create the image file using graphviz
11 # (www.graphviz.org).
12 #
13
14 from __future__ import print_function
15 import argparse
16 import re
17 import os
18
19 #
20 # Command to generate the image file
21 #
22 DOT_COMMAND = 'dot -Gsize=20,30 -Tpng %s > %s'
23
24 #
25 # Layout of generated DOT file
26 #
27 DOT_INTRO = \
28     '#\n# Command to generate image file:\n# \t%s\n#\n\n'
29 DOT_GRAPH_BEGIN = \
30     'digraph g {\n  graph [ splines = true rankdir = "LR" ]\n'
31 DOT_NODE_LINK_RX = \
32     '  "%s RX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
33 DOT_NODE_LINK_TX = \
34     '  "%s TX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
35 DOT_NODE_KNI_RX = \
36     '  "%s RX" [ shape = box style = filled fillcolor = orange ]\n'
37 DOT_NODE_KNI_TX = \
38     '  "%s TX" [ shape = box style = filled fillcolor = orange ]\n'
39 DOT_NODE_TAP_RX = \
40     '  "%s RX" [ shape = box style = filled fillcolor = gold ]\n'
41 DOT_NODE_TAP_TX = \
42     '  "%s TX" [ shape = box style = filled fillcolor = gold ]\n'
43 DOT_NODE_SOURCE = \
44     '  "%s" [ shape = box style = filled fillcolor = darkgreen ]\n'
45 DOT_NODE_SINK = \
46     '  "%s" [ shape = box style = filled fillcolor = peachpuff ]\n'
47 DOT_NODE_PIPELINE = \
48     '  "%s" [ shape = box style = filled fillcolor = royalblue ]\n'
49 DOT_EDGE_PKTQ = \
50     '  "%s" -> "%s" [ label = "%s" color = gray ]\n'
51 DOT_GRAPH_END = \
52     '}\n'
53
54 # Relationships between the graph nodes and the graph edges:
55 #
56 # Edge ID | Edge Label | Writer Node | Reader Node   | Dependencies
57 # --------+------------+-------------+---------------+--------------
58 # RXQx.y  | RXQx.y     | LINKx       | PIPELINEz     | LINKx
59 # TXQx.y  | TXQx.y     | PIPELINEz   | LINKx         | LINKx
60 # SWQx    | SWQx       | PIPELINEy   | PIPELINEz     | -
61 # TMx     | TMx        | PIPELINEy   | PIPELINEz     | LINKx
62 # KNIx RX | KNIx       | KNIx RX     | PIPELINEy     | KNIx, LINKx
63 # KNIx TX | KNIx       | PIPELINEy   | KNIx TX       | KNIx, LINKx
64 # TAPx RX | TAPx       | TAPx RX     | PIPELINEy     | TAPx
65 # TAPx TX | TAPx       | PIPELINEy   | TAPx TX       | TAPx
66 # SOURCEx | SOURCEx    | SOURCEx     | PIPELINEy     | SOURCEx
67 # SINKx   | SINKx      | PIPELINEy   | SINKx         | SINKx
68
69
70 #
71 # Parse the input configuration file to detect the graph nodes and edges
72 #
73 def process_config_file(cfgfile):
74     edges = {}
75     links = set()
76     knis = set()
77     taps = set()
78     sources = set()
79     sinks = set()
80     pipelines = set()
81     pipeline = ''
82
83     dotfile = cfgfile + '.txt'
84     imgfile = cfgfile + '.png'
85
86     #
87     # Read configuration file
88     #
89     lines = open(cfgfile, 'r')
90     for line in lines:
91         # Remove any leading and trailing white space characters
92         line = line.strip()
93
94         # Remove any comment at end of line
95         line, sep, tail = line.partition(';')
96
97         # Look for next "PIPELINE" section
98         match = re.search(r'\[(PIPELINE\d+)\]', line)
99         if match:
100             pipeline = match.group(1)
101             continue
102
103         # Look for next "pktq_in" section entry
104         match = re.search(r'pktq_in\s*=\s*(.+)', line)
105         if match:
106             pipelines.add(pipeline)
107             for q in re.findall('\S+', match.group(1)):
108                 match_rxq = re.search(r'^RXQ(\d+)\.\d+$', q)
109                 match_swq = re.search(r'^SWQ\d+$', q)
110                 match_tm = re.search(r'^TM(\d+)$', q)
111                 match_kni = re.search(r'^KNI(\d+)$', q)
112                 match_tap = re.search(r'^TAP\d+$', q)
113                 match_source = re.search(r'^SOURCE\d+$', q)
114
115                 # Set ID for the current packet queue (graph edge)
116                 q_id = ''
117                 if match_rxq or match_swq or match_tm or match_source:
118                     q_id = q
119                 elif match_kni or match_tap:
120                     q_id = q + ' RX'
121                 else:
122                     print('Error: Unrecognized pktq_in element "%s"' % q)
123                     return
124
125                 # Add current packet queue to the set of graph edges
126                 if q_id not in edges:
127                     edges[q_id] = {}
128                 if 'label' not in edges[q_id]:
129                     edges[q_id]['label'] = q
130                 if 'readers' not in edges[q_id]:
131                     edges[q_id]['readers'] = []
132                 if 'writers' not in edges[q_id]:
133                     edges[q_id]['writers'] = []
134
135                 # Add reader for the new edge
136                 edges[q_id]['readers'].append(pipeline)
137
138                 # Check for RXQ
139                 if match_rxq:
140                     link = 'LINK' + str(match_rxq.group(1))
141                     edges[q_id]['writers'].append(link + ' RX')
142                     links.add(link)
143                     continue
144
145                 # Check for SWQ
146                 if match_swq:
147                     continue
148
149                 # Check for TM
150                 if match_tm:
151                     link = 'LINK' + str(match_tm.group(1))
152                     links.add(link)
153                     continue
154
155                 # Check for KNI
156                 if match_kni:
157                     link = 'LINK' + str(match_kni.group(1))
158                     edges[q_id]['writers'].append(q_id)
159                     knis.add(q)
160                     links.add(link)
161                     continue
162
163                 # Check for TAP
164                 if match_tap:
165                     edges[q_id]['writers'].append(q_id)
166                     taps.add(q)
167                     continue
168
169                 # Check for SOURCE
170                 if match_source:
171                     edges[q_id]['writers'].append(q)
172                     sources.add(q)
173                     continue
174
175                 continue
176
177         # Look for next "pktq_out" section entry
178         match = re.search(r'pktq_out\s*=\s*(.+)', line)
179         if match:
180             for q in re.findall('\S+', match.group(1)):
181                 match_txq = re.search(r'^TXQ(\d+)\.\d+$', q)
182                 match_swq = re.search(r'^SWQ\d+$', q)
183                 match_tm = re.search(r'^TM(\d+)$', q)
184                 match_kni = re.search(r'^KNI(\d+)$', q)
185                 match_tap = re.search(r'^TAP(\d+)$', q)
186                 match_sink = re.search(r'^SINK(\d+)$', q)
187
188                 # Set ID for the current packet queue (graph edge)
189                 q_id = ''
190                 if match_txq or match_swq or match_tm or match_sink:
191                     q_id = q
192                 elif match_kni or match_tap:
193                     q_id = q + ' TX'
194                 else:
195                     print('Error: Unrecognized pktq_out element "%s"' % q)
196                     return
197
198                 # Add current packet queue to the set of graph edges
199                 if q_id not in edges:
200                     edges[q_id] = {}
201                 if 'label' not in edges[q_id]:
202                     edges[q_id]['label'] = q
203                 if 'readers' not in edges[q_id]:
204                     edges[q_id]['readers'] = []
205                 if 'writers' not in edges[q_id]:
206                     edges[q_id]['writers'] = []
207
208                 # Add writer for the new edge
209                 edges[q_id]['writers'].append(pipeline)
210
211                 # Check for TXQ
212                 if match_txq:
213                     link = 'LINK' + str(match_txq.group(1))
214                     edges[q_id]['readers'].append(link + ' TX')
215                     links.add(link)
216                     continue
217
218                 # Check for SWQ
219                 if match_swq:
220                     continue
221
222                 # Check for TM
223                 if match_tm:
224                     link = 'LINK' + str(match_tm.group(1))
225                     links.add(link)
226                     continue
227
228                 # Check for KNI
229                 if match_kni:
230                     link = 'LINK' + str(match_kni.group(1))
231                     edges[q_id]['readers'].append(q_id)
232                     knis.add(q)
233                     links.add(link)
234                     continue
235
236                 # Check for TAP
237                 if match_tap:
238                     edges[q_id]['readers'].append(q_id)
239                     taps.add(q)
240                     continue
241
242                 # Check for SINK
243                 if match_sink:
244                     edges[q_id]['readers'].append(q)
245                     sinks.add(q)
246                     continue
247
248                 continue
249
250     #
251     # Write DOT file
252     #
253     print('Creating DOT file "%s" ...' % dotfile)
254     dot_cmd = DOT_COMMAND % (dotfile, imgfile)
255     file = open(dotfile, 'w')
256     file.write(DOT_INTRO % dot_cmd)
257     file.write(DOT_GRAPH_BEGIN)
258
259     # Write the graph nodes to the DOT file
260     for l in sorted(links):
261         file.write(DOT_NODE_LINK_RX % l)
262         file.write(DOT_NODE_LINK_TX % l)
263     for k in sorted(knis):
264         file.write(DOT_NODE_KNI_RX % k)
265         file.write(DOT_NODE_KNI_TX % k)
266     for t in sorted(taps):
267         file.write(DOT_NODE_TAP_RX % t)
268         file.write(DOT_NODE_TAP_TX % t)
269     for s in sorted(sources):
270         file.write(DOT_NODE_SOURCE % s)
271     for s in sorted(sinks):
272         file.write(DOT_NODE_SINK % s)
273     for p in sorted(pipelines):
274         file.write(DOT_NODE_PIPELINE % p)
275
276     # Write the graph edges to the DOT file
277     for q in sorted(edges.keys()):
278         rw = edges[q]
279         if 'writers' not in rw:
280             print('Error: "%s" has no writer' % q)
281             return
282         if 'readers' not in rw:
283             print('Error: "%s" has no reader' % q)
284             return
285         for w in rw['writers']:
286             for r in rw['readers']:
287                 file.write(DOT_EDGE_PKTQ % (w, r, rw['label']))
288
289     file.write(DOT_GRAPH_END)
290     file.close()
291
292     #
293     # Execute the DOT command to create the image file
294     #
295     print('Creating image file "%s" ...' % imgfile)
296     if os.system('which dot > /dev/null'):
297         print('Error: Unable to locate "dot" executable.'
298               'Please install the "graphviz" package (www.graphviz.org).')
299         return
300
301     os.system(dot_cmd)
302
303
304 if __name__ == '__main__':
305     parser = argparse.ArgumentParser(description='Create diagram for IP '
306                                                  'pipeline configuration '
307                                                  'file.')
308
309     parser.add_argument(
310         '-f',
311         '--file',
312         help='input configuration file (e.g. "ip_pipeline.cfg")',
313         required=True)
314
315     args = parser.parse_args()
316
317     process_config_file(args.file)