2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2016 Intel Corporation
6 # This script creates a visual representation for a configuration file used by
7 # the DPDK ip_pipeline application.
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
14 from __future__ import print_function
20 # Command to generate the image file
22 DOT_COMMAND = 'dot -Gsize=20,30 -Tpng %s > %s'
25 # Layout of generated DOT file
28 '#\n# Command to generate image file:\n# \t%s\n#\n\n'
30 'digraph g {\n graph [ splines = true rankdir = "LR" ]\n'
32 ' "%s RX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
34 ' "%s TX" [ shape = box style = filled fillcolor = yellowgreen ]\n'
36 ' "%s RX" [ shape = box style = filled fillcolor = orange ]\n'
38 ' "%s TX" [ shape = box style = filled fillcolor = orange ]\n'
40 ' "%s RX" [ shape = box style = filled fillcolor = gold ]\n'
42 ' "%s TX" [ shape = box style = filled fillcolor = gold ]\n'
44 ' "%s" [ shape = box style = filled fillcolor = darkgreen ]\n'
46 ' "%s" [ shape = box style = filled fillcolor = peachpuff ]\n'
48 ' "%s" [ shape = box style = filled fillcolor = royalblue ]\n'
50 ' "%s" -> "%s" [ label = "%s" color = gray ]\n'
54 # Relationships between the graph nodes and the graph edges:
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
71 # Parse the input configuration file to detect the graph nodes and edges
73 def process_config_file(cfgfile):
83 dotfile = cfgfile + '.txt'
84 imgfile = cfgfile + '.png'
87 # Read configuration file
89 lines = open(cfgfile, 'r')
91 # Remove any leading and trailing white space characters
94 # Remove any comment at end of line
95 line, sep, tail = line.partition(';')
97 # Look for next "PIPELINE" section
98 match = re.search(r'\[(PIPELINE\d+)\]', line)
100 pipeline = match.group(1)
103 # Look for next "pktq_in" section entry
104 match = re.search(r'pktq_in\s*=\s*(.+)', line)
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)
115 # Set ID for the current packet queue (graph edge)
117 if match_rxq or match_swq or match_tm or match_source:
119 elif match_kni or match_tap:
122 print('Error: Unrecognized pktq_in element "%s"' % q)
125 # Add current packet queue to the set of graph edges
126 if q_id not in edges:
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'] = []
135 # Add reader for the new edge
136 edges[q_id]['readers'].append(pipeline)
140 link = 'LINK' + str(match_rxq.group(1))
141 edges[q_id]['writers'].append(link + ' RX')
151 link = 'LINK' + str(match_tm.group(1))
157 link = 'LINK' + str(match_kni.group(1))
158 edges[q_id]['writers'].append(q_id)
165 edges[q_id]['writers'].append(q_id)
171 edges[q_id]['writers'].append(q)
177 # Look for next "pktq_out" section entry
178 match = re.search(r'pktq_out\s*=\s*(.+)', line)
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)
188 # Set ID for the current packet queue (graph edge)
190 if match_txq or match_swq or match_tm or match_sink:
192 elif match_kni or match_tap:
195 print('Error: Unrecognized pktq_out element "%s"' % q)
198 # Add current packet queue to the set of graph edges
199 if q_id not in edges:
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'] = []
208 # Add writer for the new edge
209 edges[q_id]['writers'].append(pipeline)
213 link = 'LINK' + str(match_txq.group(1))
214 edges[q_id]['readers'].append(link + ' TX')
224 link = 'LINK' + str(match_tm.group(1))
230 link = 'LINK' + str(match_kni.group(1))
231 edges[q_id]['readers'].append(q_id)
238 edges[q_id]['readers'].append(q_id)
244 edges[q_id]['readers'].append(q)
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)
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)
276 # Write the graph edges to the DOT file
277 for q in sorted(edges.keys()):
279 if 'writers' not in rw:
280 print('Error: "%s" has no writer' % q)
282 if 'readers' not in rw:
283 print('Error: "%s" has no reader' % q)
285 for w in rw['writers']:
286 for r in rw['readers']:
287 file.write(DOT_EDGE_PKTQ % (w, r, rw['label']))
289 file.write(DOT_GRAPH_END)
293 # Execute the DOT command to create the image file
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).')
304 if __name__ == '__main__':
305 parser = argparse.ArgumentParser(description='Create diagram for IP '
306 'pipeline configuration '
312 help='input configuration file (e.g. "ip_pipeline.cfg")',
315 args = parser.parse_args()
317 process_config_file(args.file)