5 # Copyright(c) 2016 Intel Corporation. All rights reserved.
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
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
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.
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.
35 # This script creates a visual representation for a configuration file used by
36 # the DPDK ip_pipeline application.
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
43 from __future__ import print_function
49 # Command to generate the image file
51 DOT_COMMAND = 'dot -Gsize=20,30 -Tpng %s > %s'
54 # Layout of generated DOT file
57 '#\n# Command to generate image file:\n# \t%s\n#\n\n'
59 'digraph g {\n graph [ splines = true rankdir = "LR" ]\n'
61 ' "%s RX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
63 ' "%s TX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
65 ' "%s RX" [ shape = box style = filled fillcolor = orange ]\n'
67 ' "%s TX" [ shape = box style = filled fillcolor = orange ]\n'
69 ' "%s RX" [ shape = box style = filled fillcolor = gold ]\n'
71 ' "%s TX" [ shape = box style = filled fillcolor = gold ]\n'
73 ' "%s" [ shape = box style = filled fillcolor = darkgreen ]\n'
75 ' "%s" [ shape = box style = filled fillcolor = peachpuff ]\n'
77 ' "%s" [ shape = box style = filled fillcolor = royalblue ]\n'
79 ' "%s" -> "%s" [ label = "%s" color = gray ]\n'
83 # Relationships between the graph nodes and the graph edges:
85 # Edge ID | Edge Label | Writer Node | Reader Node | Dependencies
86 # --------+------------+-------------+---------------+--------------
87 # RXQx.y | RXQx.y | LINKx | PIPELINEz | LINKx
88 # TXQx.y | TXQx.y | PIPELINEz | LINKx | LINKx
89 # SWQx | SWQx | PIPELINEy | PIPELINEz | -
90 # TMx | TMx | PIPELINEy | PIPELINEz | LINKx
91 # KNIx RX | KNIx | KNIx RX | PIPELINEy | KNIx, LINKx
92 # KNIx TX | KNIx | PIPELINEy | KNIx TX | KNIx, LINKx
93 # TAPx RX | TAPx | TAPx RX | PIPELINEy | TAPx
94 # TAPx TX | TAPx | PIPELINEy | TAPx TX | TAPx
95 # SOURCEx | SOURCEx | SOURCEx | PIPELINEy | SOURCEx
96 # SINKx | SINKx | PIPELINEy | SINKx | SINKx
100 # Parse the input configuration file to detect the graph nodes and edges
102 def process_config_file(cfgfile):
112 dotfile = cfgfile + '.txt'
113 imgfile = cfgfile + '.png'
116 # Read configuration file
118 lines = open(cfgfile, 'r')
120 # Remove any leading and trailing white space characters
123 # Remove any comment at end of line
124 line, sep, tail = line.partition(';')
126 # Look for next "PIPELINE" section
127 match = re.search(r'\[(PIPELINE\d+)\]', line)
129 pipeline = match.group(1)
132 # Look for next "pktq_in" section entry
133 match = re.search(r'pktq_in\s*=\s*(.+)', line)
135 pipelines.add(pipeline)
136 for q in re.findall('\S+', match.group(1)):
137 match_rxq = re.search(r'^RXQ(\d+)\.\d+$', q)
138 match_swq = re.search(r'^SWQ\d+$', q)
139 match_tm = re.search(r'^TM(\d+)$', q)
140 match_kni = re.search(r'^KNI(\d+)$', q)
141 match_tap = re.search(r'^TAP\d+$', q)
142 match_source = re.search(r'^SOURCE\d+$', q)
144 # Set ID for the current packet queue (graph edge)
146 if match_rxq or match_swq or match_tm or match_source:
148 elif match_kni or match_tap:
151 print('Error: Unrecognized pktq_in element "%s"' % q)
154 # Add current packet queue to the set of graph edges
155 if q_id not in edges:
157 if 'label' not in edges[q_id]:
158 edges[q_id]['label'] = q
159 if 'readers' not in edges[q_id]:
160 edges[q_id]['readers'] = []
161 if 'writers' not in edges[q_id]:
162 edges[q_id]['writers'] = []
164 # Add reader for the new edge
165 edges[q_id]['readers'].append(pipeline)
169 link = 'LINK' + str(match_rxq.group(1))
170 edges[q_id]['writers'].append(link + ' RX')
180 link = 'LINK' + str(match_tm.group(1))
186 link = 'LINK' + str(match_kni.group(1))
187 edges[q_id]['writers'].append(q_id)
194 edges[q_id]['writers'].append(q_id)
200 edges[q_id]['writers'].append(q)
206 # Look for next "pktq_out" section entry
207 match = re.search(r'pktq_out\s*=\s*(.+)', line)
209 for q in re.findall('\S+', match.group(1)):
210 match_txq = re.search(r'^TXQ(\d+)\.\d+$', q)
211 match_swq = re.search(r'^SWQ\d+$', q)
212 match_tm = re.search(r'^TM(\d+)$', q)
213 match_kni = re.search(r'^KNI(\d+)$', q)
214 match_tap = re.search(r'^TAP(\d+)$', q)
215 match_sink = re.search(r'^SINK(\d+)$', q)
217 # Set ID for the current packet queue (graph edge)
219 if match_txq or match_swq or match_tm or match_sink:
221 elif match_kni or match_tap:
224 print('Error: Unrecognized pktq_out element "%s"' % q)
227 # Add current packet queue to the set of graph edges
228 if q_id not in edges:
230 if 'label' not in edges[q_id]:
231 edges[q_id]['label'] = q
232 if 'readers' not in edges[q_id]:
233 edges[q_id]['readers'] = []
234 if 'writers' not in edges[q_id]:
235 edges[q_id]['writers'] = []
237 # Add writer for the new edge
238 edges[q_id]['writers'].append(pipeline)
242 link = 'LINK' + str(match_txq.group(1))
243 edges[q_id]['readers'].append(link + ' TX')
253 link = 'LINK' + str(match_tm.group(1))
259 link = 'LINK' + str(match_kni.group(1))
260 edges[q_id]['readers'].append(q_id)
267 edges[q_id]['readers'].append(q_id)
273 edges[q_id]['readers'].append(q)
282 print('Creating DOT file "%s" ...' % dotfile)
283 dot_cmd = DOT_COMMAND % (dotfile, imgfile)
284 file = open(dotfile, 'w')
285 file.write(DOT_INTRO % dot_cmd)
286 file.write(DOT_GRAPH_BEGIN)
288 # Write the graph nodes to the DOT file
289 for l in sorted(links):
290 file.write(DOT_NODE_LINK_RX % l)
291 file.write(DOT_NODE_LINK_TX % l)
292 for k in sorted(knis):
293 file.write(DOT_NODE_KNI_RX % k)
294 file.write(DOT_NODE_KNI_TX % k)
295 for t in sorted(taps):
296 file.write(DOT_NODE_TAP_RX % t)
297 file.write(DOT_NODE_TAP_TX % t)
298 for s in sorted(sources):
299 file.write(DOT_NODE_SOURCE % s)
300 for s in sorted(sinks):
301 file.write(DOT_NODE_SINK % s)
302 for p in sorted(pipelines):
303 file.write(DOT_NODE_PIPELINE % p)
305 # Write the graph edges to the DOT file
306 for q in sorted(edges.keys()):
308 if 'writers' not in rw:
309 print('Error: "%s" has no writer' % q)
311 if 'readers' not in rw:
312 print('Error: "%s" has no reader' % q)
314 for w in rw['writers']:
315 for r in rw['readers']:
316 file.write(DOT_EDGE_PKTQ % (w, r, rw['label']))
318 file.write(DOT_GRAPH_END)
322 # Execute the DOT command to create the image file
324 print('Creating image file "%s" ...' % imgfile)
325 if os.system('which dot > /dev/null'):
326 print('Error: Unable to locate "dot" executable.'
327 'Please install the "graphviz" package (www.graphviz.org).')
333 if __name__ == '__main__':
334 parser = argparse.ArgumentParser(description='Create diagram for IP '
335 'pipeline configuration '
341 help='input configuration file (e.g. "ip_pipeline.cfg")',
344 args = parser.parse_args()
346 process_config_file(args.file)